In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

2.3 lines: non-intersecting smoothed

adding a cubic path smoothing to the previous method gives interesting results.
Get Adobe Flash player
by nicoptere 27 Dec 2010
/**
 * Copyright nicoptere ( http://wonderfl.net/user/nicoptere )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/1ynN
 */

package 
{
	import com.bit101.components.HUISlider;
	import com.bit101.components.PushButton;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;

	/**
	 * @author Nicolas Barradeau
	 * http://en.nicoptere.net
	 */
	public class ProximityIntersectDistributionSmooth extends Sprite 
	{
		
		private var points:Vector.<Point> = Vector.<Point>([ new Point(297.48,180.02),new Point(302.56,145.32),new Point(283.75,88.31),new Point(237.16,66.59),new Point(198.93,78.66),new Point(184.90,103.70),new Point(193.26,129.34),new Point(201.62,152.56),new Point(195.35,169.76),new Point(179.52,176.70),new Point(158.32,165.23),new Point(148.46,132.35),new Point(175.64,75.64),new Point(249.40,50.90),new Point(334.51,84.99),new Point(354.52,140.20),new Point(345.56,182.73),new Point(310.92,233.71),new Point(261.05,299.77),new Point(250.00,352.87),new Point(238.35,352.87),new Point(247.31,294.04),new Point(275.68,228.88),new Point(246.12,450.90),new Point(224.02,441.86),new Point(214.76,419.53),new Point(224.02,397.21),new Point(246.12,387.86),new Point(268.52,397.21),new Point(277.47,419.53),new Point(268.22,441.86)]);
		private var pairs:Vector.<Point> = new Vector.<Point>();
		
		private var distanceSlider:HUISlider;
		private var minDist:Number;
		
		public function ProximityIntersectDistributionSmooth() 
		{
			var randomizeBtn:PushButton = new PushButton( this, 200, 10, 'randomize', onRandomizeButtonClick );
			
			distanceSlider = new HUISlider( this, 10, 10, 'distance', onDistanceChange );
			distanceSlider.maximum = 200;
			distanceSlider.value = 80;
			onDistanceChange( null );
			
		}
		
		private function onDistanceChange( e:Event ):void 
		{
			minDist = distanceSlider.value * distanceSlider.value;
			redraw();
		}
		
		private function redraw():void 
		{
			graphics.clear();
			graphics.lineStyle( 0 );
			
			pairs.length = 0;
			var addPair:Boolean;
			
			var i:int;
			var p:Point, pp:Point;
			for each( p in points )
			{
				for each( pp in points )
				{
					if ( p === pp ) continue;
					if ( ( ( p.x - pp.x ) * ( p.x - pp.x ) + ( p.y - pp.y ) * ( p.y - pp.y ) ) < minDist )
					{
						addPair = true;
						intersection : for ( i = 0; i < pairs.length; i += 2 )
						{
							if ( lineIntersectLine( pairs[ i ], pairs[ i + 1 ], p, pp ) != null )
							{
								addPair = false;
								break intersection;
							}
						}
						if ( addPair ) pairs.push( p, pp );
					}
				}
			}
			
			//drawing the valid pairs
			if ( pairs.length == 0 ) return;
			
			//computes a cubic path throught the valid lines
			pairs = computePath( pairs ); 
			
			graphics.moveTo( pairs[ 0 ].x, pairs[ 0 ].y );
			for ( i = 0; i < pairs.length; i++ )
			{
				graphics.lineTo( pairs[ i ].x, pairs[ i ].y );
			}
			
		}
		
		
		/**
		 * original function by Keith Hair:
		 * http://keith-hair.net/blog/2008/08/04/find-intersection-point-of-two-lines-in-as3/
		 * 
		 * extended by Hristo Dachev
		 * http://blog.controul.com/2009/05/line-segment-intersection/
		 * 
		 * @param	A line 1 point 1
		 * @param	B line 1 point 2
		 * @param	E line 2 point 1
		 * @param	F line 2 point 2
		 * @param	ABasSeg should the test consider line 1 as a segment  
		 * @param	EFasSeg should the test consider line 2 as a segment
		 * @return
		 */
		
		public function lineIntersectLine (	A : Point, B : Point,
											E : Point, F : Point,
											ABasSeg : Boolean = true, EFasSeg : Boolean = true ) : Point
		{
			
			var a1:Number = B.y - A.y;
			var a2:Number = F.y - E.y;
			var b1:Number = A.x - B.x;
			var b2:Number = E.x - F.x;
		 
			var denom:Number = a1 * b2 - a2 * b1;
			if (denom == 0) 
			{
				return null;
			}
			
			var c1:Number = B.x * A.y - A.x * B.y;
			var c2:Number = F.x * E.y - E.x * F.y;
			
			var ip:Point = new Point(	(b1 * c2 - b2 * c1) / denom,
										(a2 * c1 - a1 * c2) / denom		);
			
			//---------------------------------------------------
			//Do checks to see if intersection to endpoints
			//distance is longer than actual pairs.
			//Return null if it is with any.
			//---------------------------------------------------
			if ( A.x == B.x )
				ip.x = A.x;
			else if ( E.x == F.x )
				ip.x = E.x;
			if ( A.y == B.y )
				ip.y = A.y;
			else if ( E.y == F.y )
				ip.y = E.y;

			//	Constrain to segment.

			if ( ABasSeg )
			{
				if ( ( A.x < B.x ) ? ip.x < A.x || ip.x > B.x : ip.x > A.x || ip.x < B.x )
					return null;
				if ( ( A.y < B.y ) ? ip.y < A.y || ip.y > B.y : ip.y > A.y || ip.y < B.y )
					return null;
			}
			if ( EFasSeg )
			{
				if ( ( E.x < F.x ) ? ip.x < E.x || ip.x > F.x : ip.x > E.x || ip.x < F.x )
					return null;
				if ( ( E.y < F.y ) ? ip.y < E.y || ip.y > F.y : ip.y > E.y || ip.y < F.y )
					return null;
			}
			return ip;
		}
		
		
		/**
		 * cubic path computation
		 * @param	handles 	a set of points
		 * @param	precision 	the number of subdivisions
		 * @param	loop		specifies if it should loop
		 * @return
		 */
		private function computePath( handles:Vector.<Point>, precision:Number = 0.1, loop:Boolean = false ):Vector.<Point>
		{
		
		
			//output values
			var tmp:Vector.<Point> = new Vector.<Point>();
		
			var p0:Point = new Point(0,0);
			var p1:Point = new Point(0,0);
			var p2:Point = new Point(0,0);
				
			var i:int = 0;
			var t:Number;
			var t2:Number;
			var t3:Number;
			var t4:Number;
			var X:Number;
			var Y:Number;
			var j:Number;
			
			while( i < handles.length )
			{
		
				//p0
				if( i == 0 )
				{
		
					if ( loop == true ){
						
						p0.x = (handles[handles.length-1].x+handles[i].x)/2;
						p0.y = (handles[handles.length-1].y+handles[i].y)/2;
						
					}else{
						
						p0.x = handles[ i ].x;
						p0.y = handles[ i ].y;
					}
					
				}
				else
				{
		
					p0.x = ( handles[ i - 1 ].x + handles[ i ].x ) / 2;
					p0.y = ( handles[ i - 1 ].y + handles[ i ].y ) / 2;
		
				}
				//p1
				p1.x = handles[ i ].x;
				p1.y = handles[ i ].y;
		
				//p2	
				if( i == handles.length - 1 )
				{
		
					if (loop == true){
						
						p2.x=(handles[i].x+handles[0].x)/2;
						p2.y=(handles[i].y+handles[0].y)/2;
						
					}else{
						
						p2.x = handles[ i ].x;
						p2.y = handles[ i ].y;
					}
					
				}
				else
				{
		
					p2.x = ( handles[ i + 1 ].x + handles[ i ].x ) / 2;
					p2.y = ( handles[ i + 1 ].y + handles[ i ].y ) / 2;
		
				}
				
				j = 0;
				while( j < 1 )
				{
				
					t  = 1 - j;
					t2 = t * t;
					t3 = 2 * j * t;
					t4 = j * j;
					
					X = t2 * p0.x + t3 * p1.x + t4 * p2.x;
					Y = t2 * p0.y + t3 * p1.y + t4 * p2.y;
		
					tmp.push( new Point( X, Y ) );
					j += precision;
					
				}
				i++;
			}
			
			return tmp;
		
		}
		
		private function onRandomizeButtonClick( e:Event ):void 
		{
			points.sort( randomize );
			redraw();
		}
		
        private function randomize( a:Point, b:Point ):Number
        {
            return ( Math.random() > .5 ) ? -1 : 1;
        }
	}
}