Dart DocumentationthreeShape

Shape class

@author zz85 / http://www.lab4games.net/zz85/blog Defines a 2d shape plane using paths.

class Shape extends Path {

 List holes;

	Shape([List points]) : holes = [], super(points);

 // Convenience method to return ExtrudeGeometry
 extrude( {amount: 100,
           bevelThickness: 6.0,
           bevelSize: null,
           bevelSegments: 3,
           bevelEnabled: true,
           curveSegments: 12,
           steps: 1,
           bendPath,
           extrudePath,
           material,
           extrudeMaterial} ) {

   if (bevelSize == null) bevelSize = bevelThickness - 2.0;

   return new ExtrudeGeometry( [this], amount: amount,
                                       bevelThickness: bevelThickness,
                                       bevelSize: bevelSize,
                                       bevelSegments: bevelSegments,
                                       bevelEnabled: bevelEnabled,
                                       curveSegments: curveSegments,
                                       steps: steps,
                                       bendPath: bendPath,
                                       extrudePath: extrudePath,
                                       material: material,
                                       extrudeMaterial: extrudeMaterial );
 }


 // Get points of holes
 getPointsHoles( divisions ) {

 	var i, il = holes.length;
 	var holesPts = new List(il);

 	for ( i = 0; i < il; i ++ ) {

 		holesPts[ i ] = holes[ i ].getTransformedPoints( divisions, bends: _bends );

 	}

 	return holesPts;

 }

 // Get points of holes (spaced by regular distance)
 getSpacedPointsHoles ( divisions ) {

 	var i, il = holes.length;
 	var holesPts = new List(il);

 	for ( i = 0; i < il; i ++ ) {

 		holesPts[ i ] = holes[ i ].getTransformedSpacedPoints( divisions, _bends );

 	}

 	return holesPts;

 }


 // Get points of shape and holes (keypoints based on segments parameter)
 extractAllPoints( divisions ) {

 	return {

 		"shape": getTransformedPoints( divisions ),
 		"holes": getPointsHoles( divisions )

 	};

 }

 extractPoints( [num divisions] ) {

 	if (useSpacedPoints) {
 		return extractAllSpacedPoints(divisions);
 	}

 	return extractAllPoints(divisions);

 }

 //
 // THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) {
 //
 // 	return {
 //
 // 		shape: this.transform( bend, divisions ),
 // 		holes: this.getPointsHoles( divisions, bend )
 //
 // 	};
 //
 // };

 // Get points of shape and holes (spaced by regular distance)
 extractAllSpacedPoints( [num divisions] ) {
 	return {
 		"shape": getTransformedSpacedPoints( divisions ),
 		"holes": getSpacedPointsHoles( divisions )

 	};
 }

}

Extends

Curve > CurvePath > Path > Shape

Constructors

new Shape([List points]) #

Creates a new Object instance.

Object instances have no meaningful state, and are only useful through their identity. An Object instance is equal to itself only.

docs inherited from Object
Shape([List points]) : holes = [], super(points);

Properties

List<PathAction> actions #

inherited from Path
List<PathAction> actions

bool autoClose #

inherited from CurvePath
bool autoClose

List cacheArcLengths #

inherited from Curve
List cacheArcLengths = null

List cacheLengths #

inherited from CurvePath
List cacheLengths = null

List curves #

inherited from CurvePath
List curves

List holes #

List holes

final num length #

inherited from CurvePath
num get length => getCurveLengths().last;

bool needsUpdate #

inherited from Curve
bool needsUpdate = false

var useSpacedPoints #

inherited from Path
var useSpacedPoints = false

Methods

dynamic absarc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) #

inherited from Path
absarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {

 absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise);

}

dynamic absellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise) #

inherited from Path
absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) {

 var args = [aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise];

 var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise );
 curves.add( curve );

 var lastPoint = curve.getPoint(aClockwise ? 1 : 0);
 args.add(lastPoint.x);
 args.add(lastPoint.y);

 addAction(PathAction.ELLIPSE, args);

}

dynamic add(curve) #

inherited from CurvePath
add( curve ) => curves.add( curve );

dynamic addAction(String action, [args]) #

inherited from Path
addAction(String action, [var args]) => actions.add(new PathAction(action, args));

dynamic addWrapPath(bendpath) #

inherited from CurvePath

Bend / Wrap Helper Methods

addWrapPath( bendpath ) => _bends.add( bendpath );

dynamic arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) #

inherited from Path
arc( aX, aY, aRadius,
								aStartAngle, aEndAngle, aClockwise ) {

 var lastargs = actions[ actions.length - 1].args;
 var x0 = lastargs[ lastargs.length - 2 ];
 var y0 = lastargs[ lastargs.length - 1 ];

 absarc(aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise );

}

dynamic bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) #

inherited from Path
bezierCurveTo( aCP1x, aCP1y,
              aCP2x, aCP2y,
              aX, aY ) {

	var args = [aCP1x, aCP1y,
              aCP2x, aCP2y,
              aX, aY];

	var lastargs = actions.last.args;

	var x0 = lastargs[ lastargs.length - 2 ].toDouble();
	var y0 = lastargs[ lastargs.length - 1 ].toDouble();

	var curve = new CubicBezierCurve( new Vector2( x0, y0 ),
											new Vector2( aCP1x.toDouble(), aCP1y.toDouble() ),
											new Vector2( aCP2x.toDouble(), aCP2y.toDouble() ),
											new Vector2( aX.toDouble(), aY.toDouble() ) );
	curves.add( curve );

	addAction( PathAction.BEZIER_CURVE_TO, args );

}

dynamic checkConnection() #

inherited from CurvePath
checkConnection() {
		// TODO
		// If the ending of curve is not connected to the starting
		// or the next curve, then, this is not a real path
	}

dynamic closePath() #

inherited from CurvePath
closePath() {
		// TODO Test
		// and verify for vector3 (needs to implement equals)
		// Add a line curve if start and end of lines are not connected
		var startPoint = curves[0].getPoint(0);
		var endPoint = curves[curves.length-1].getPoint(1);

		if (!startPoint.equals(endPoint)) {
			this.curves.add( new LineCurve(endPoint, startPoint) );
		}

	}

dynamic createGeometry(points) #

inherited from CurvePath
createGeometry( points ) {

		var geometry = new Geometry();

		for ( var i = 0; i < points.length; i ++ ) {
		  var z = (points[i] is Vector3) ? points[ i ].z : 0.0;
			geometry.vertices.add( new Vector3( points[ i ].x, points[ i ].y, z) );
		}

		return geometry;
	}

dynamic createPointsGeometry({divisions}) #

inherited from CurvePath

Create Geometries Helpers

Generate geometry from path points (for Line or ParticleSystem objects)

createPointsGeometry( {divisions} ) {
		var pts = this.getPoints( divisions, true );
		return this.createGeometry( pts );
	}

dynamic createSpacedPointsGeometry([divisions]) #

inherited from CurvePath
createSpacedPointsGeometry( [divisions] ) {
		var pts = this.getSpacedPoints( divisions, true );
		return this.createGeometry( pts );
	}

dynamic ellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise) #

inherited from Path
ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) {

 var lastargs = actions.last.args;
 var x0 = lastargs[ lastargs.length - 2 ];
 var y0 = lastargs[ lastargs.length - 1 ];

 absellipse(aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise );

}

dynamic extractAllPoints(divisions) #

extractAllPoints( divisions ) {

	return {

		"shape": getTransformedPoints( divisions ),
		"holes": getPointsHoles( divisions )

	};

}

dynamic extractAllSpacedPoints([num divisions]) #

extractAllSpacedPoints( [num divisions] ) {
	return {
		"shape": getTransformedSpacedPoints( divisions ),
		"holes": getSpacedPointsHoles( divisions )

	};
}

dynamic extractPoints([num divisions]) #

extractPoints( [num divisions] ) {

	if (useSpacedPoints) {
		return extractAllSpacedPoints(divisions);
	}

	return extractAllPoints(divisions);

}

dynamic extrude({amount: 100, bevelThickness: 6.0, bevelSize: null, bevelSegments: 3, bevelEnabled: true, curveSegments: 12, steps: 1, bendPath, extrudePath, material, extrudeMaterial}) #

extrude( {amount: 100,
         bevelThickness: 6.0,
         bevelSize: null,
         bevelSegments: 3,
         bevelEnabled: true,
         curveSegments: 12,
         steps: 1,
         bendPath,
         extrudePath,
         material,
         extrudeMaterial} ) {

 if (bevelSize == null) bevelSize = bevelThickness - 2.0;

 return new ExtrudeGeometry( [this], amount: amount,
                                     bevelThickness: bevelThickness,
                                     bevelSize: bevelSize,
                                     bevelSegments: bevelSegments,
                                     bevelEnabled: bevelEnabled,
                                     curveSegments: curveSegments,
                                     steps: steps,
                                     bendPath: bendPath,
                                     extrudePath: extrudePath,
                                     material: material,
                                     extrudeMaterial: extrudeMaterial );
}

dynamic getBoundingBox() #

inherited from CurvePath
getBoundingBox() {

		var points = getPoints();

		var maxX, maxY, maxZ;
		var minX, minY, minZ;

		maxX = maxY = double.NEGATIVE_INFINITY;
		minX = minY = double.INFINITY;

		var p, i, sum;

		var v3 = points[0] is Vector3;

		sum = (v3) ? new Vector3.zero() : new Vector2.zero();

		int il = points.length;
		for ( i = 0; i < il; i ++ ) {

			p = points[ i ];

			if ( p.x > maxX ) { maxX = p.x;
			} else if ( p.x < minX ) minX = p.x;

			if ( p.y > maxY ) { maxY = p.y;
			} else if ( p.y < minY ) minY = p.y;

			if (v3) {
			  p = p as Vector3;
	      if ( p.z > maxZ ) { maxZ = p.z;
	      } else if ( p.z < minZ ) minZ = p.z;

	      (sum as Vector3).add( p );
	    } else {
	      (sum as Vector2).add( p );
	    }



		}

		var ret = {

			"minX": minX,
			"minY": minY,
			"maxX": maxX,
			"maxY": maxY,
			"centroid": (sum as dynamic).scale( 1.0 / il )

		};

		if (v3) {

	    ret["maxZ"] = maxZ;
	    ret["minZ"] = minZ;

	  }

	  return ret;
	}

List<num> getCurveLengths() #

inherited from CurvePath
List<num> getCurveLengths() {

		// We use cache values if curves and cache array are same length

		if ( this.cacheLengths != null && this.cacheLengths.length == this.curves.length ) {

			return this.cacheLengths;

		}

		// Get length of subsurve
		// Push sums into cached array

		var lengths = [], sums = 0;
		var i, il = this.curves.length;

		for ( i = 0; i < il; i ++ ) {

			sums += this.curves[ i ].length;
			lengths.add( sums );

		}

		this.cacheLengths = lengths;

		return lengths;

	}

List getLengths({num divisions: null}) #

inherited from Curve
List getLengths( {num divisions: null} ) {

		if (divisions == null) divisions = (_arcLengthDivisions != null) ? (_arcLengthDivisions): 200;

		if ( cacheArcLengths != null
			&& ( cacheArcLengths.length == divisions + 1 )
			&& !needsUpdate) {

			//console.log( "cached", this.cacheArcLengths );
			return cacheArcLengths;
		}

		needsUpdate = false;

		var cache = [];
		var current;
		var last = getPoint( 0.0 );
		var sum = 0;

		cache.add( 0 );

		for ( var p = 1; p <= divisions; p ++ ) {

			current = getPoint ( p / divisions );

			var distance;

			// TODO(nelsonsilva) - Must move distanceTo to IVector interface os create a new IHasDistance
			if (current is Vector3) {
			  distance = (current as Vector3).absoluteError( last as Vector3 );
			} else {
      distance = (current as Vector2).absoluteError( last as Vector2);
    }

			sum += distance;
			cache.add( sum );
			last = current;

		}

		cacheArcLengths = cache;

		return cache; // { sums: cache, sum:sum }; Sum is in the last element.
	}

dynamic getPoint(num t) #

inherited from CurvePath
getPoint( num t ) {

		var d = t * this.length;
		var curveLengths = this.getCurveLengths();
		var i = 0, diff;
		Curve curve;

		// To think about boundaries points.

		while ( i < curveLengths.length ) {

			if ( curveLengths[ i ] >= d ) {

				diff = curveLengths[ i ] - d;
				curve = this.curves[ i ];

				var u = 1 - diff / curve.length;

				return curve.getPointAt( u );

			}

			i ++;

		}

		return null;

		// loop where sum != 0, sum > d , sum+1 <d

	}

V getPointAt(u) #

inherited from Curve
V getPointAt( u ) {
		var t = getUtoTmapping( u );
		return getPoint( t );
	}

dynamic getPoints([int divisions = null, closedPath = false]) #

inherited from Path
getPoints( [int divisions = null, closedPath = false] ) {

	if (useSpacedPoints) {
		return getSpacedPoints( divisions, closedPath );
	}

	if (divisions == null) divisions =  12;

	List<Vector2> points = [];

	var i, il, item, action, args;
	var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0,
		laste, j,
		t, tx, ty;

	for ( i = 0; i < actions.length; i ++ ) {

		item = actions[ i ];

		action = item.action;
		args = item.args;

		switch( action ) {

		case PathAction.MOVE_TO:

			points.add( new Vector2( args[ 0 ], args[ 1 ] ) );

			break;

		case PathAction.LINE_TO:

			points.add( new Vector2( args[ 0 ], args[ 1 ] ) );

			break;

		case PathAction.QUADRATIC_CURVE_TO:

			cpx  = args[ 2 ];
			cpy  = args[ 3 ];

			cpx1 = args[ 0 ];
			cpy1 = args[ 1 ];

			if ( points.length > 0 ) {

				laste = points[ points.length - 1 ];

				cpx0 = laste.x;
				cpy0 = laste.y;

			} else {

				laste = actions[ i - 1 ].args;

				cpx0 = laste[ laste.length - 2 ];
				cpy0 = laste[ laste.length - 1 ];

			}

			for ( j = 1; j <= divisions; j ++ ) {

				t = j / divisions;

				tx = ShapeUtils.b2( t, cpx0, cpx1, cpx );
				ty = ShapeUtils.b2( t, cpy0, cpy1, cpy );

				points.add( new Vector2( tx, ty ) );

		  	}

			break;

		case PathAction.BEZIER_CURVE_TO:

			cpx  = args[ 4 ];
			cpy  = args[ 5 ];

			cpx1 = args[ 0 ];
			cpy1 = args[ 1 ];

			cpx2 = args[ 2 ];
			cpy2 = args[ 3 ];

			if ( points.length > 0 ) {

				laste = points[ points.length - 1 ];

				cpx0 = laste.x;
				cpy0 = laste.y;

			} else {

				laste = actions[ i - 1 ].args;

				cpx0 = laste[ laste.length - 2 ];
				cpy0 = laste[ laste.length - 1 ];

			}


			for ( j = 1; j <= divisions; j ++ ) {

				t = j / divisions;

				tx = ShapeUtils.b3( t, cpx0, cpx1, cpx2, cpx );
				ty = ShapeUtils.b3( t, cpy0, cpy1, cpy2, cpy );

				points.add( new Vector2( tx, ty ) );

			}

			break;

		case PathAction.CSPLINE_THRU:

			laste = actions[ i - 1 ].args;

			var last = new Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] );
			var spts = [ last ];

			var n = divisions * args[ 0 ].length;

			spts.addAll( args[ 0 ] );

			var spline = new SplineCurve( spts );

			for ( j = 1; j <= n; j ++ ) {

				points.add( spline.getPointAt( j / n ) ) ;

			}

			break;

		case PathAction.ARC:

			laste = actions[ i - 1 ].args;

			var aX = args[ 0 ], aY = args[ 1 ],
				aRadius = args[ 2 ],
				aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
				aClockwise = !!args[ 5 ];


			var deltaAngle = aEndAngle - aStartAngle;
			var angle;
			var tdivisions = divisions * 2;

			for ( j = 1; j <= tdivisions; j ++ ) {

				t = j / tdivisions;

				if ( ! aClockwise ) {

					t = 1 - t;

				}

				angle = aStartAngle + t * deltaAngle;

				tx = aX + aRadius * Math.cos( angle );
				ty = aY + aRadius * Math.sin( angle );

				//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);

				points.add( new Vector2( tx, ty ) );

			}

			//console.log(points);

		  break;

		case PathAction.ELLIPSE:

     var aX = args[ 0 ], aY = args[ 1 ],
     xRadius = args[ 2 ],
     yRadius = args[ 3 ],
     aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
     aClockwise = !!args[ 6 ];


   var deltaAngle = aEndAngle - aStartAngle;
   var angle;
   var tdivisions = divisions * 2;

   for ( j = 1; j <= tdivisions; j ++ ) {

     t = j / tdivisions;

     if ( ! aClockwise ) {

       t = 1 - t;

     }

     angle = aStartAngle + t * deltaAngle;

     tx = aX + xRadius * Math.cos( angle );
     ty = aY + yRadius * Math.sin( angle );

     //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);

     points.add( new Vector2( tx, ty ) );

   }

   //console.log(points);

   break;
		} // end switch

	}



	// Normalize to remove the closing point by default.
	var lastPoint = points[ points.length - 1];
	var EPSILON = 0.0000000001;
	if ( (lastPoint.x - points[ 0 ].x).abs() < EPSILON &&
       (lastPoint.y - points[ 0 ].y).abs() < EPSILON) {
	  points.removeLast();
	}
	if ( closedPath ) {
		points.add( points[ 0 ] );
	}

	return points;

}

dynamic getPointsHoles(divisions) #

getPointsHoles( divisions ) {

	var i, il = holes.length;
	var holesPts = new List(il);

	for ( i = 0; i < il; i ++ ) {

		holesPts[ i ] = holes[ i ].getTransformedPoints( divisions, bends: _bends );

	}

	return holesPts;

}

dynamic getSpacedPoints([int divisions = 5, bool closedPath = false]) #

inherited from Path
getSpacedPoints( [int divisions = 5, bool closedPath = false] ) {

	if ( divisions == null ) divisions = 40;

	var points = [];

	for ( var i = 0; i < divisions; i ++ ) {

		points.add( this.getPoint( i / divisions ) );

		//if( !this.getPoint( i / divisions ) ) throw "DIE";

	}

	// if ( closedPath ) {
	//
	// 	points.push( points[ 0 ] );
	//
	// }

	return points;

}

dynamic getSpacedPointsHoles(divisions) #

getSpacedPointsHoles ( divisions ) {

	var i, il = holes.length;
	var holesPts = new List(il);

	for ( i = 0; i < il; i ++ ) {

		holesPts[ i ] = holes[ i ].getTransformedSpacedPoints( divisions, _bends );

	}

	return holesPts;

}

V getTangent(t) #

inherited from Curve
V getTangent( t ) {

		var delta = 0.0001;
		var t1 = t - delta;
		var t2 = t + delta;

		// Capping in case of danger

		if ( t1 < 0 ) t1 = 0;
		if ( t2 > 1 ) t2 = 1;

		var pt1 = getPoint( t1 );
		var pt2 = getPoint( t2 );

		var vec = pt2 - pt1;
		return vec.normalize();
	}

V getTangentAt(u) #

inherited from Curve
V getTangentAt( u ) {
		var t = getUtoTmapping( u );
		return getTangent( t );
	}

dynamic getTransformedPoints(segments, {List bends: null}) #

inherited from CurvePath
getTransformedPoints( segments, {List bends: null} ) {

		var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints
		var i, il;

		if (bends == null) {
			bends = _bends;
		}

		for ( i = 0; i < bends.length; i ++ ) {
			oldPts = this.getWrapPoints( oldPts, bends[ i ] );
		}

		return oldPts;

	}

dynamic getTransformedSpacedPoints([num segments, List bends = null]) #

inherited from CurvePath
getTransformedSpacedPoints( [num segments, List bends = null] ) {

		var oldPts = getSpacedPoints( segments );

		var i, il;

		if (bends == null) {
			bends = _bends;
		}

		for ( i = 0; i < bends.length; i ++ ) {
			oldPts = this.getWrapPoints( oldPts, bends[ i ] );
		}

		return oldPts;
	}

List<V> getUPoints([List uList, closedPath = false]) #

inherited from Curve
List<V> getUPoints( [List uList , closedPath = false] ) {
 var pts = [];

 for ( var u in uList ) {
   pts.add( this.getPointAt( u ) );
 }

 return pts;
}

dynamic getUtoTmapping(u, {distance: null}) #

inherited from Curve
getUtoTmapping( u, {distance: null} ) {

		var arcLengths = getLengths();

		int i = 0, il = arcLengths.length;

		var targetArcLength; // The targeted u distance value to get

		if (distance != null) {
			targetArcLength = distance;
		} else {
			targetArcLength = u * arcLengths[ il - 1 ];
		}

		//var time = Date.now();

		// binary search for the index with largest value smaller than target u distance

		var low = 0, high = il - 1, comparison;

		while ( low <= high ) {

			i = ( low + ( high - low ) / 2 ).floor().toInt(); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats

			comparison = arcLengths[ i ] - targetArcLength;

			if ( comparison < 0 ) {

				low = i + 1;
				continue;

			} else if ( comparison > 0 ) {

				high = i - 1;
				continue;

			} else {

				high = i;
				break;

				// DONE

			}

		}

		i = high;

		//console.log('b' , i, low, high, Date.now()- time);

		if ( arcLengths[ i ] == targetArcLength ) {

			var t = i / ( il - 1 );
			return t;

		}

		// we could get finer grain at lengths, or use simple interpolatation between two points

		var lengthBefore = arcLengths[ i ];
	    var lengthAfter = arcLengths[ i + 1 ];

	    var segmentLength = lengthAfter - lengthBefore;

	    // determine where we are between the 'before' and 'after' points

	    var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;

	    // add that fractional amount to t

	    var t = ( i + segmentFraction ) / ( il -1 );

		return t;
	}

dynamic getWrapPoints(oldPts, path) #

inherited from CurvePath
getWrapPoints( oldPts, path ) {

		var bounds = getBoundingBox();

		var i, il, p, oldX, oldY, xNorm;

		for ( i = 0; i < oldPts.length; i ++ ) {

			p = oldPts[ i ];

			oldX = p.x;
			oldY = p.y;

			xNorm = oldX / bounds.maxX;

			// If using actual distance, for length > path, requires line extrusions
			//xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance

			xNorm = path.getUtoTmapping( xNorm, oldX );

			// check for out of bounds?

			var pathPt = path.getPoint( xNorm );
			var normal = path.getNormalVector( xNorm ).scale( oldY );

			p.x = pathPt.x + normal.x;
			p.y = pathPt.y + normal.y;

		}

		return oldPts;

	}

dynamic lineTo(x, y) #

inherited from Path
lineTo( x, y ) {

	var args = [x, y];

	var lastargs = actions.last.args;

	var x0 = lastargs[ lastargs.length - 2 ];
	var y0 = lastargs[ lastargs.length - 1 ];

	var curve = new LineCurve( new Vector2( x0, y0 ), new Vector2( x, y ) );
	curves.add( curve );

	addAction( PathAction.LINE_TO, args);
}

dynamic moveTo(x, y) #

inherited from Path
moveTo( x, y ) => addAction( PathAction.MOVE_TO, [x, y] );

dynamic quadraticCurveTo(aCPx, aCPy, aX, aY) #

inherited from Path
quadraticCurveTo( aCPx, aCPy, aX, aY ) {

	var args = [aCPx, aCPy, aX, aY];

	var lastargs = actions.last.args;

	var x0 = lastargs[ lastargs.length - 2 ].toDouble();
	var y0 = lastargs[ lastargs.length - 1 ].toDouble();

	var curve = new QuadraticBezierCurve( new Vector2( x0, y0 ),
												new Vector2( aCPx.toDouble(), aCPy.toDouble() ),
												new Vector2( aX.toDouble(), aY.toDouble() ) );
	curves.add( curve );

	addAction( PathAction.QUADRATIC_CURVE_TO, args );
}

dynamic splineThru(List<Vector2> pts) #

inherited from Path
splineThru( List<Vector2> pts) {

	var args = [pts];
	var lastargs = actions.last.args;

	var x0 = lastargs[ lastargs.length - 2 ];
	var y0 = lastargs[ lastargs.length - 1 ];
//---
	var npts = [ new Vector2( x0, y0 ) ];
	npts.addAll(pts); //Array.prototype.push.apply( npts, pts );

	var curve = new SplineCurve( npts );
	curves.add( curve );

	addAction( PathAction.CSPLINE_THRU, args);

}

dynamic toShapes() #

inherited from Path
toShapes() {

	var i, il, item, action, args;

	List<Path> subPaths = [];
	var lastPath = new Path();

	for ( i = 0; i < actions.length; i ++ ) {

		item = actions[ i ];

		args = item.args;
		action = item.action;

		if ( action == PathAction.MOVE_TO ) {

			if ( lastPath.actions.length != 0 ) {

				subPaths.add( lastPath );
				lastPath = new Path();

			}

		}

		lastPath._applyAction( action, args);

	}

	if ( lastPath.actions.length != 0 ) {

		subPaths.add( lastPath );

	}

	// console.log(subPaths);

	if ( subPaths.length == 0 ) return [];

	var tmpPath;
	Shape tmpShape;
	List<Shape> shapes = [];

	var holesFirst = !ShapeUtils.isClockWise( subPaths[ 0 ].getPoints() );
	// console.log("Holes first", holesFirst);

	if ( subPaths.length == 1) {
		tmpPath = subPaths[0];
		tmpShape = new Shape();
		tmpShape.actions = tmpPath.actions;
		tmpShape.curves = tmpPath.curves;
		shapes.add( tmpShape );
		return shapes;
	};

	if ( holesFirst ) {

		tmpShape = new Shape();

		for ( i = 0; i < subPaths.length; i ++ ) {

			tmpPath = subPaths[ i ];

			if ( ShapeUtils.isClockWise( tmpPath.getPoints() ) ) {

				tmpShape.actions = tmpPath.actions;
				tmpShape.curves = tmpPath.curves;

				shapes.add( tmpShape );
				tmpShape = new Shape();

				//console.log('cw', i);

			} else {

				tmpShape.holes.add( tmpPath );

				//console.log('ccw', i);

			}

		}

	} else {

		// Shapes first

		for ( i = 0; i < subPaths.length; i ++ ) {

			tmpPath = subPaths[ i ];

			if ( ShapeUtils.isClockWise( tmpPath.getPoints() ) ) {


				if (tmpShape != null) shapes.add( tmpShape );

				tmpShape = new Shape();
				tmpShape.actions = tmpPath.actions;
				tmpShape.curves = tmpPath.curves;

			} else {

				tmpShape.holes.add( tmpPath );

			}

		}

		shapes.add( tmpShape );

	}

	//console.log("shape", shapes);

	return shapes;

}

dynamic transform(path, segments) #

inherited from Path
transform ( path, segments ) {

	var bounds = getBoundingBox();
	var oldPts = getPoints( segments ); // getPoints getSpacedPoints

	//console.log( path.cacheArcLengths() );
	//path.getLengths(400);
	//segments = 40;

	return getWrapPoints( oldPts, path );

}

dynamic updateArcLengths() #

inherited from Curve
updateArcLengths() {
		needsUpdate = true;
		getLengths();
	}