ShapePath.js 5.9 KB

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