|
@@ -1,15 +1,13 @@
|
|
-import { PathPrototype } from './PathPrototype';
|
|
|
|
-import { Shape } from './Shape';
|
|
|
|
-import { ShapeUtils } from '../ShapeUtils';
|
|
|
|
-import { Vector2 } from '../../math/Vector2';
|
|
|
|
-import { CurvePath } from './CurvePath';
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* @author zz85 / http://www.lab4games.net/zz85/blog
|
|
* @author zz85 / http://www.lab4games.net/zz85/blog
|
|
* Creates free form 2d path using series of points, lines or curves.
|
|
* Creates free form 2d path using series of points, lines or curves.
|
|
- *
|
|
|
|
**/
|
|
**/
|
|
|
|
|
|
|
|
+import { PathPrototype } from './PathPrototype';
|
|
|
|
+import { Vector2 } from '../../math/Vector2';
|
|
|
|
+import { CurvePath } from './CurvePath';
|
|
|
|
+
|
|
|
|
+
|
|
function Path( points ) {
|
|
function Path( points ) {
|
|
|
|
|
|
CurvePath.call( this );
|
|
CurvePath.call( this );
|
|
@@ -26,259 +24,4 @@ function Path( points ) {
|
|
Path.prototype = PathPrototype;
|
|
Path.prototype = PathPrototype;
|
|
PathPrototype.constructor = Path;
|
|
PathPrototype.constructor = Path;
|
|
|
|
|
|
-
|
|
|
|
-// minimal class for proxing functions to Path. Replaces old "extractSubpaths()"
|
|
|
|
-function ShapePath() {
|
|
|
|
- this.subPaths = [];
|
|
|
|
- this.currentPath = null;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-ShapePath.prototype = {
|
|
|
|
- moveTo: function ( x, y ) {
|
|
|
|
- this.currentPath = new Path();
|
|
|
|
- this.subPaths.push(this.currentPath);
|
|
|
|
- this.currentPath.moveTo( x, y );
|
|
|
|
- },
|
|
|
|
- lineTo: function ( x, y ) {
|
|
|
|
- this.currentPath.lineTo( x, y );
|
|
|
|
- },
|
|
|
|
- quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) {
|
|
|
|
- this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY );
|
|
|
|
- },
|
|
|
|
- bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {
|
|
|
|
- this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY );
|
|
|
|
- },
|
|
|
|
- splineThru: function ( pts ) {
|
|
|
|
- this.currentPath.splineThru( pts );
|
|
|
|
- },
|
|
|
|
-
|
|
|
|
- toShapes: function ( isCCW, noHoles ) {
|
|
|
|
-
|
|
|
|
- function toShapesNoHoles( inSubpaths ) {
|
|
|
|
-
|
|
|
|
- var shapes = [];
|
|
|
|
-
|
|
|
|
- for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) {
|
|
|
|
-
|
|
|
|
- var tmpPath = inSubpaths[ i ];
|
|
|
|
-
|
|
|
|
- var tmpShape = new Shape();
|
|
|
|
- tmpShape.curves = tmpPath.curves;
|
|
|
|
-
|
|
|
|
- shapes.push( tmpShape );
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return shapes;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function isPointInsidePolygon( inPt, inPolygon ) {
|
|
|
|
-
|
|
|
|
- var polyLen = inPolygon.length;
|
|
|
|
-
|
|
|
|
- // inPt on polygon contour => immediate success or
|
|
|
|
- // toggling of inside/outside at every single! intersection point of an edge
|
|
|
|
- // with the horizontal line through inPt, left of inPt
|
|
|
|
- // not counting lowerY endpoints of edges and whole edges on that line
|
|
|
|
- var inside = false;
|
|
|
|
- for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {
|
|
|
|
-
|
|
|
|
- var edgeLowPt = inPolygon[ p ];
|
|
|
|
- var edgeHighPt = inPolygon[ q ];
|
|
|
|
-
|
|
|
|
- var edgeDx = edgeHighPt.x - edgeLowPt.x;
|
|
|
|
- var edgeDy = edgeHighPt.y - edgeLowPt.y;
|
|
|
|
-
|
|
|
|
- if ( Math.abs( edgeDy ) > Number.EPSILON ) {
|
|
|
|
-
|
|
|
|
- // not parallel
|
|
|
|
- if ( edgeDy < 0 ) {
|
|
|
|
-
|
|
|
|
- edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx;
|
|
|
|
- edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
- if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue;
|
|
|
|
-
|
|
|
|
- if ( inPt.y === edgeLowPt.y ) {
|
|
|
|
-
|
|
|
|
- if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ?
|
|
|
|
- // continue; // no intersection or edgeLowPt => doesn't count !!!
|
|
|
|
-
|
|
|
|
- } else {
|
|
|
|
-
|
|
|
|
- var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y );
|
|
|
|
- if ( perpEdge === 0 ) return true; // inPt is on contour ?
|
|
|
|
- if ( perpEdge < 0 ) continue;
|
|
|
|
- inside = ! inside; // true intersection left of inPt
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- } else {
|
|
|
|
-
|
|
|
|
- // parallel or collinear
|
|
|
|
- if ( inPt.y !== edgeLowPt.y ) continue; // parallel
|
|
|
|
- // edge lies on the same horizontal line as inPt
|
|
|
|
- if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||
|
|
|
|
- ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour !
|
|
|
|
- // continue;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return inside;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var isClockWise = ShapeUtils.isClockWise;
|
|
|
|
-
|
|
|
|
- var subPaths = this.subPaths;
|
|
|
|
- if ( subPaths.length === 0 ) return [];
|
|
|
|
-
|
|
|
|
- if ( noHoles === true ) return toShapesNoHoles( subPaths );
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- var solid, tmpPath, tmpShape, shapes = [];
|
|
|
|
-
|
|
|
|
- if ( subPaths.length === 1 ) {
|
|
|
|
-
|
|
|
|
- tmpPath = subPaths[ 0 ];
|
|
|
|
- tmpShape = new Shape();
|
|
|
|
- tmpShape.curves = tmpPath.curves;
|
|
|
|
- shapes.push( tmpShape );
|
|
|
|
- return shapes;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() );
|
|
|
|
- holesFirst = isCCW ? ! holesFirst : holesFirst;
|
|
|
|
-
|
|
|
|
- // console.log("Holes first", holesFirst);
|
|
|
|
-
|
|
|
|
- var betterShapeHoles = [];
|
|
|
|
- var newShapes = [];
|
|
|
|
- var newShapeHoles = [];
|
|
|
|
- var mainIdx = 0;
|
|
|
|
- var tmpPoints;
|
|
|
|
-
|
|
|
|
- newShapes[ mainIdx ] = undefined;
|
|
|
|
- newShapeHoles[ mainIdx ] = [];
|
|
|
|
-
|
|
|
|
- for ( var i = 0, l = subPaths.length; i < l; i ++ ) {
|
|
|
|
-
|
|
|
|
- tmpPath = subPaths[ i ];
|
|
|
|
- tmpPoints = tmpPath.getPoints();
|
|
|
|
- solid = isClockWise( tmpPoints );
|
|
|
|
- solid = isCCW ? ! solid : solid;
|
|
|
|
-
|
|
|
|
- if ( solid ) {
|
|
|
|
-
|
|
|
|
- if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++;
|
|
|
|
-
|
|
|
|
- newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints };
|
|
|
|
- newShapes[ mainIdx ].s.curves = tmpPath.curves;
|
|
|
|
-
|
|
|
|
- if ( holesFirst ) mainIdx ++;
|
|
|
|
- newShapeHoles[ mainIdx ] = [];
|
|
|
|
-
|
|
|
|
- //console.log('cw', i);
|
|
|
|
-
|
|
|
|
- } else {
|
|
|
|
-
|
|
|
|
- newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } );
|
|
|
|
-
|
|
|
|
- //console.log('ccw', i);
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // only Holes? -> probably all Shapes with wrong orientation
|
|
|
|
- if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths );
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- if ( newShapes.length > 1 ) {
|
|
|
|
-
|
|
|
|
- var ambiguous = false;
|
|
|
|
- var toChange = [];
|
|
|
|
-
|
|
|
|
- for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
|
|
|
|
-
|
|
|
|
- betterShapeHoles[ sIdx ] = [];
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
|
|
|
|
-
|
|
|
|
- var sho = newShapeHoles[ sIdx ];
|
|
|
|
-
|
|
|
|
- for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) {
|
|
|
|
-
|
|
|
|
- var ho = sho[ hIdx ];
|
|
|
|
- var hole_unassigned = true;
|
|
|
|
-
|
|
|
|
- for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {
|
|
|
|
-
|
|
|
|
- if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) {
|
|
|
|
-
|
|
|
|
- if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );
|
|
|
|
- if ( hole_unassigned ) {
|
|
|
|
-
|
|
|
|
- hole_unassigned = false;
|
|
|
|
- betterShapeHoles[ s2Idx ].push( ho );
|
|
|
|
-
|
|
|
|
- } else {
|
|
|
|
-
|
|
|
|
- ambiguous = true;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
- if ( hole_unassigned ) {
|
|
|
|
-
|
|
|
|
- betterShapeHoles[ sIdx ].push( ho );
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
- // console.log("ambiguous: ", ambiguous);
|
|
|
|
- if ( toChange.length > 0 ) {
|
|
|
|
-
|
|
|
|
- // console.log("to change: ", toChange);
|
|
|
|
- if ( ! ambiguous ) newShapeHoles = betterShapeHoles;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var tmpHoles;
|
|
|
|
-
|
|
|
|
- for ( var i = 0, il = newShapes.length; i < il; i ++ ) {
|
|
|
|
-
|
|
|
|
- tmpShape = newShapes[ i ].s;
|
|
|
|
- shapes.push( tmpShape );
|
|
|
|
- tmpHoles = newShapeHoles[ i ];
|
|
|
|
-
|
|
|
|
- for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) {
|
|
|
|
-
|
|
|
|
- tmpShape.holes.push( tmpHoles[ j ].h );
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- //console.log("shape", shapes);
|
|
|
|
-
|
|
|
|
- return shapes;
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-export { ShapePath, Path };
|
|
|
|
|
|
+export { Path };
|