From 78e2be1aed73f727720634f4558500f6a2a0c56b Mon Sep 17 00:00:00 2001 From: Juergen Ahting Date: Wed, 29 Jan 2014 08:48:13 +0100 Subject: [PATCH] THREE.Path.toShapes is now independent of the order of specification of holes and shapes. It moves holes to those shapes that contain them if unambiguous. Otherwise it respects specification order. --- src/extras/core/Path.js | 149 +++++++++++++++++++++++++++++----------- 1 file changed, 108 insertions(+), 41 deletions(-) diff --git a/src/extras/core/Path.js b/src/extras/core/Path.js index f2e49733b2..b8a4c19e81 100644 --- a/src/extras/core/Path.js +++ b/src/extras/core/Path.js @@ -440,6 +440,51 @@ THREE.Path.prototype.getPoints = function( divisions, closedPath ) { THREE.Path.prototype.toShapes = function( isCCW ) { + function isPointInsidePolygon( inPt, inPolygon ) { + var EPSILON = 0.0000000001; + + 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) > 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 colinear + if ( inPt.y != edgeLowPt.y ) continue; // parallel + // egde 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 i, il, item, action, args; var subPaths = [], lastPath = new THREE.Path(); @@ -493,66 +538,88 @@ THREE.Path.prototype.toShapes = function( isCCW ) { holesFirst = isCCW ? !holesFirst : holesFirst; // console.log("Holes first", holesFirst); + + var betterShapeHoles = []; + var newShapes = []; + var newShapeHoles = []; + var mainIdx = 0; + var tmpPoints; - if ( holesFirst ) { + newShapes[mainIdx] = undefined; + newShapeHoles[mainIdx] = []; - tmpShape = new THREE.Shape(); - - for ( i = 0, il = subPaths.length; i < il; i ++ ) { + for ( i = 0, il = subPaths.length; i < il; i ++ ) { - tmpPath = subPaths[ i ]; - solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() ); - solid = isCCW ? !solid : solid; + tmpPath = subPaths[ i ]; + tmpPoints = tmpPath.getPoints(); + solid = THREE.Shape.Utils.isClockWise( tmpPoints ); + solid = isCCW ? !solid : solid; - if ( solid ) { + if ( solid ) { - tmpShape.actions = tmpPath.actions; - tmpShape.curves = tmpPath.curves; + if ( (! holesFirst ) && ( newShapes[mainIdx] ) ) mainIdx++; - shapes.push( tmpShape ); - tmpShape = new THREE.Shape(); + newShapes[mainIdx] = { s: new THREE.Shape(), p: tmpPoints }; + newShapes[mainIdx].s.actions = tmpPath.actions; + newShapes[mainIdx].s.curves = tmpPath.curves; + + if ( holesFirst ) mainIdx++; + newShapeHoles[mainIdx] = []; - //console.log('cw', i); + //console.log('cw', i); - } else { + } else { - tmpShape.holes.push( tmpPath ); + newShapeHoles[mainIdx].push( { h: tmpPath, p: tmpPoints[0] } ); - //console.log('ccw', i); - - } + //console.log('ccw', i); } - } else { - - // Shapes first - tmpShape = undefined; - - for ( i = 0, il = subPaths.length; i < il; i ++ ) { - - tmpPath = subPaths[ i ]; - solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() ); - solid = isCCW ? !solid : solid; - - if ( solid ) { - - if ( tmpShape ) shapes.push( tmpShape ); - - tmpShape = new THREE.Shape(); - tmpShape.actions = tmpPath.actions; - tmpShape.curves = tmpPath.curves; - - } else { + } - tmpShape.holes.push( tmpPath ); + if ( newShapes.length > 1 ) { + var ambigious = 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 sh = newShapes[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 { + ambigious = true; + } + } + } + if ( hole_unassigned ) { betterShapeHoles[sIdx].push( ho ); } } - } + // console.log("ambigious: ", ambigious); + if ( toChange.length > 0 ) { + // console.log("to change: ", toChange); + if (! ambigious) newShapeHoles = betterShapeHoles; + } + } + var tmpHoles, j, jl; + for ( i = 0, il = newShapes.length; i < il; i ++ ) { + tmpShape = newShapes[i].s; shapes.push( tmpShape ); - + tmpHoles = newShapeHoles[i]; + for ( j = 0, jl = tmpHoles.length; j < jl; j ++ ) { + tmpShape.holes.push( tmpHoles[j].h ); + } } //console.log("shape", shapes); -- GitLab