ShapePath.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. import { Path } from './Path.js';
  2. import { Shape } from './Shape.js';
  3. import { ShapeUtils } from '../ShapeUtils.js';
  4. /**
  5. * @author zz85 / http://www.lab4games.net/zz85/blog
  6. * minimal class for proxing functions to Path. Replaces old "extractSubpaths()"
  7. **/
  8. function ShapePath() {
  9. this.type = 'ShapePath';
  10. this.color = new THREE.Color();
  11. this.subPaths = [];
  12. this.currentPath = null;
  13. }
  14. Object.assign( ShapePath.prototype, {
  15. moveTo: function ( x, y ) {
  16. this.currentPath = new Path();
  17. this.subPaths.push( this.currentPath );
  18. this.currentPath.moveTo( x, y );
  19. },
  20. lineTo: function ( x, y ) {
  21. this.currentPath.lineTo( x, y );
  22. },
  23. quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) {
  24. this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY );
  25. },
  26. bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {
  27. this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY );
  28. },
  29. splineThru: function ( pts ) {
  30. this.currentPath.splineThru( pts );
  31. },
  32. toShapes: function ( isCCW, noHoles ) {
  33. function toShapesNoHoles( inSubpaths ) {
  34. var shapes = [];
  35. for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) {
  36. var tmpPath = inSubpaths[ i ];
  37. var tmpShape = new Shape();
  38. tmpShape.curves = tmpPath.curves;
  39. shapes.push( tmpShape );
  40. }
  41. return shapes;
  42. }
  43. function isPointInsidePolygon( inPt, inPolygon ) {
  44. var polyLen = inPolygon.length;
  45. // inPt on polygon contour => immediate success or
  46. // toggling of inside/outside at every single! intersection point of an edge
  47. // with the horizontal line through inPt, left of inPt
  48. // not counting lowerY endpoints of edges and whole edges on that line
  49. var inside = false;
  50. for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {
  51. var edgeLowPt = inPolygon[ p ];
  52. var edgeHighPt = inPolygon[ q ];
  53. var edgeDx = edgeHighPt.x - edgeLowPt.x;
  54. var edgeDy = edgeHighPt.y - edgeLowPt.y;
  55. if ( Math.abs( edgeDy ) > Number.EPSILON ) {
  56. // not parallel
  57. if ( edgeDy < 0 ) {
  58. edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx;
  59. edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;
  60. }
  61. if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue;
  62. if ( inPt.y === edgeLowPt.y ) {
  63. if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ?
  64. // continue; // no intersection or edgeLowPt => doesn't count !!!
  65. } else {
  66. var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y );
  67. if ( perpEdge === 0 ) return true; // inPt is on contour ?
  68. if ( perpEdge < 0 ) continue;
  69. inside = ! inside; // true intersection left of inPt
  70. }
  71. } else {
  72. // parallel or collinear
  73. if ( inPt.y !== edgeLowPt.y ) continue; // parallel
  74. // edge lies on the same horizontal line as inPt
  75. if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||
  76. ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour !
  77. // continue;
  78. }
  79. }
  80. return inside;
  81. }
  82. var isClockWise = ShapeUtils.isClockWise;
  83. var subPaths = this.subPaths;
  84. if ( subPaths.length === 0 ) return [];
  85. if ( noHoles === true ) return toShapesNoHoles( subPaths );
  86. var solid, tmpPath, tmpShape, shapes = [];
  87. if ( subPaths.length === 1 ) {
  88. tmpPath = subPaths[ 0 ];
  89. tmpShape = new Shape();
  90. tmpShape.curves = tmpPath.curves;
  91. shapes.push( tmpShape );
  92. return shapes;
  93. }
  94. var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() );
  95. holesFirst = isCCW ? ! holesFirst : holesFirst;
  96. // console.log("Holes first", holesFirst);
  97. var betterShapeHoles = [];
  98. var newShapes = [];
  99. var newShapeHoles = [];
  100. var mainIdx = 0;
  101. var tmpPoints;
  102. newShapes[ mainIdx ] = undefined;
  103. newShapeHoles[ mainIdx ] = [];
  104. for ( var i = 0, l = subPaths.length; i < l; i ++ ) {
  105. tmpPath = subPaths[ i ];
  106. tmpPoints = tmpPath.getPoints();
  107. solid = isClockWise( tmpPoints );
  108. solid = isCCW ? ! solid : solid;
  109. if ( solid ) {
  110. if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++;
  111. newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints };
  112. newShapes[ mainIdx ].s.curves = tmpPath.curves;
  113. if ( holesFirst ) mainIdx ++;
  114. newShapeHoles[ mainIdx ] = [];
  115. //console.log('cw', i);
  116. } else {
  117. newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } );
  118. //console.log('ccw', i);
  119. }
  120. }
  121. // only Holes? -> probably all Shapes with wrong orientation
  122. if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths );
  123. if ( newShapes.length > 1 ) {
  124. var ambiguous = false;
  125. var toChange = [];
  126. for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
  127. betterShapeHoles[ sIdx ] = [];
  128. }
  129. for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
  130. var sho = newShapeHoles[ sIdx ];
  131. for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) {
  132. var ho = sho[ hIdx ];
  133. var hole_unassigned = true;
  134. for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {
  135. if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) {
  136. if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );
  137. if ( hole_unassigned ) {
  138. hole_unassigned = false;
  139. betterShapeHoles[ s2Idx ].push( ho );
  140. } else {
  141. ambiguous = true;
  142. }
  143. }
  144. }
  145. if ( hole_unassigned ) {
  146. betterShapeHoles[ sIdx ].push( ho );
  147. }
  148. }
  149. }
  150. // console.log("ambiguous: ", ambiguous);
  151. if ( toChange.length > 0 ) {
  152. // console.log("to change: ", toChange);
  153. if ( ! ambiguous ) newShapeHoles = betterShapeHoles;
  154. }
  155. }
  156. var tmpHoles;
  157. for ( var i = 0, il = newShapes.length; i < il; i ++ ) {
  158. tmpShape = newShapes[ i ].s;
  159. shapes.push( tmpShape );
  160. tmpHoles = newShapeHoles[ i ];
  161. for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) {
  162. tmpShape.holes.push( tmpHoles[ j ].h );
  163. }
  164. }
  165. //console.log("shape", shapes);
  166. return shapes;
  167. }
  168. } );
  169. export { ShapePath };
粤ICP备19079148号