2.3 lines: non-intersecting smoothed
adding a cubic path smoothing to the previous method gives interesting results.
/**
* 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;
}
}
}