Shape.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. /**
  2. * @author zz85 / http://www.lab4games.net/zz85/blog
  3. * Defines a 2d shape plane using paths.
  4. **/
  5. // STEP 1 Create a path.
  6. // STEP 2 Turn path into shape.
  7. // STEP 3 ExtrudeGeometry takes in Shape/Shapes
  8. // STEP 3a - Extract points from each shape, turn to vertices
  9. // STEP 3b - Triangulate each shape, add faces.
  10. THREE.Shape = function ( ) {
  11. THREE.Path.apply( this, arguments );
  12. this.holes = [];
  13. };
  14. THREE.Shape.prototype = new THREE.Path();
  15. THREE.Shape.prototype.constructor = THREE.Path;
  16. // Convenience method to return ExtrudeGeometry
  17. THREE.Shape.prototype.extrude = function ( options ) {
  18. var extruded = new THREE.ExtrudeGeometry( this, options );
  19. return extruded;
  20. };
  21. // Get points of holes
  22. THREE.Shape.prototype.getPointsHoles = function ( divisions ) {
  23. var i, il = this.holes.length, holesPts = [];
  24. for ( i = 0; i < il; i ++ ) {
  25. holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends );
  26. }
  27. return holesPts;
  28. };
  29. // Get points of holes (spaced by regular distance)
  30. THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) {
  31. var i, il = this.holes.length, holesPts = [];
  32. for ( i = 0; i < il; i ++ ) {
  33. holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends );
  34. }
  35. return holesPts;
  36. };
  37. // Get points of shape and holes (keypoints based on segments parameter)
  38. THREE.Shape.prototype.extractAllPoints = function ( divisions ) {
  39. return {
  40. shape: this.getTransformedPoints( divisions ),
  41. holes: this.getPointsHoles( divisions )
  42. };
  43. };
  44. THREE.Shape.prototype.extractPoints = function ( divisions ) {
  45. if (this.useSpacedPoints) {
  46. return this.extractAllSpacedPoints(divisions);
  47. }
  48. return this.extractAllPoints(divisions);
  49. };
  50. //
  51. // THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) {
  52. //
  53. // return {
  54. //
  55. // shape: this.transform( bend, divisions ),
  56. // holes: this.getPointsHoles( divisions, bend )
  57. //
  58. // };
  59. //
  60. // };
  61. // Get points of shape and holes (spaced by regular distance)
  62. THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) {
  63. return {
  64. shape: this.getTransformedSpacedPoints( divisions ),
  65. holes: this.getSpacedPointsHoles( divisions )
  66. };
  67. };
  68. /**************************************************************
  69. * Utils
  70. **************************************************************/
  71. THREE.Shape.Utils = {
  72. /*
  73. contour - array of vector2 for contour
  74. holes - array of array of vector2
  75. */
  76. removeHoles: function ( contour, holes ) {
  77. var shape = contour.concat(); // work on this shape
  78. var allpoints = shape.concat();
  79. /* For each isolated shape, find the closest points and break to the hole to allow triangulation */
  80. var prevShapeVert, nextShapeVert,
  81. prevHoleVert, nextHoleVert,
  82. holeIndex, shapeIndex,
  83. shapeId, shapeGroup,
  84. h, h2,
  85. hole, shortest, d,
  86. p, pts1, pts2,
  87. tmpShape1, tmpShape2,
  88. tmpHole1, tmpHole2,
  89. verts = [];
  90. for ( h = 0; h < holes.length; h ++ ) {
  91. hole = holes[ h ];
  92. /*
  93. shapeholes[ h ].concat(); // preserves original
  94. holes.push( hole );
  95. */
  96. Array.prototype.push.apply( allpoints, hole );
  97. shortest = Number.POSITIVE_INFINITY;
  98. // Find the shortest pair of pts between shape and hole
  99. // Note: Actually, I'm not sure now if we could optimize this to be faster than O(m*n)
  100. // Using distanceToSquared() intead of distanceTo() should speed a little
  101. // since running square roots operations are reduced.
  102. for ( h2 = 0; h2 < hole.length; h2 ++ ) {
  103. pts1 = hole[ h2 ];
  104. var dist = [];
  105. for ( p = 0; p < shape.length; p++ ) {
  106. pts2 = shape[ p ];
  107. d = pts1.distanceToSquared( pts2 );
  108. dist.push( d );
  109. if ( d < shortest ) {
  110. shortest = d;
  111. holeIndex = h2;
  112. shapeIndex = p;
  113. }
  114. }
  115. }
  116. //console.log("shortest", shortest, dist);
  117. prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
  118. prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
  119. var areaapts = [
  120. hole[ holeIndex ],
  121. shape[ shapeIndex ],
  122. shape[ prevShapeVert ]
  123. ];
  124. var areaa = THREE.FontUtils.Triangulate.area( areaapts );
  125. var areabpts = [
  126. hole[ holeIndex ],
  127. hole[ prevHoleVert ],
  128. shape[ shapeIndex ]
  129. ];
  130. var areab = THREE.FontUtils.Triangulate.area( areabpts );
  131. var shapeOffset = 1;
  132. var holeOffset = -1;
  133. var oldShapeIndex = shapeIndex, oldHoleIndex = holeIndex;
  134. shapeIndex += shapeOffset;
  135. holeIndex += holeOffset;
  136. if ( shapeIndex < 0 ) { shapeIndex += shape.length; }
  137. shapeIndex %= shape.length;
  138. if ( holeIndex < 0 ) { holeIndex += hole.length; }
  139. holeIndex %= hole.length;
  140. prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
  141. prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
  142. areaapts = [
  143. hole[ holeIndex ],
  144. shape[ shapeIndex ],
  145. shape[ prevShapeVert ]
  146. ];
  147. var areaa2 = THREE.FontUtils.Triangulate.area( areaapts );
  148. areabpts = [
  149. hole[ holeIndex ],
  150. hole[ prevHoleVert ],
  151. shape[ shapeIndex ]
  152. ];
  153. var areab2 = THREE.FontUtils.Triangulate.area( areabpts );
  154. //console.log(areaa,areab ,areaa2,areab2, ( areaa + areab ), ( areaa2 + areab2 ));
  155. if ( ( areaa + areab ) > ( areaa2 + areab2 ) ) {
  156. // In case areas are not correct.
  157. //console.log("USE THIS");
  158. shapeIndex = oldShapeIndex;
  159. holeIndex = oldHoleIndex ;
  160. if ( shapeIndex < 0 ) { shapeIndex += shape.length; }
  161. shapeIndex %= shape.length;
  162. if ( holeIndex < 0 ) { holeIndex += hole.length; }
  163. holeIndex %= hole.length;
  164. prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
  165. prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
  166. } else {
  167. //console.log("USE THAT ")
  168. }
  169. tmpShape1 = shape.slice( 0, shapeIndex );
  170. tmpShape2 = shape.slice( shapeIndex );
  171. tmpHole1 = hole.slice( holeIndex );
  172. tmpHole2 = hole.slice( 0, holeIndex );
  173. // Should check orders here again?
  174. var trianglea = [
  175. hole[ holeIndex ],
  176. shape[ shapeIndex ],
  177. shape[ prevShapeVert ]
  178. ];
  179. var triangleb = [
  180. hole[ holeIndex ] ,
  181. hole[ prevHoleVert ],
  182. shape[ shapeIndex ]
  183. ];
  184. verts.push( trianglea );
  185. verts.push( triangleb );
  186. shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
  187. }
  188. return {
  189. shape:shape, /* shape with no holes */
  190. isolatedPts: verts, /* isolated faces */
  191. allpoints: allpoints
  192. }
  193. },
  194. triangulateShape: function ( contour, holes ) {
  195. var shapeWithoutHoles = THREE.Shape.Utils.removeHoles( contour, holes );
  196. var shape = shapeWithoutHoles.shape,
  197. allpoints = shapeWithoutHoles.allpoints,
  198. isolatedPts = shapeWithoutHoles.isolatedPts;
  199. var triangles = THREE.FontUtils.Triangulate( shape, false ); // True returns indices for points of spooled shape
  200. // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first.
  201. //console.log( "triangles",triangles, triangles.length );
  202. //console.log( "allpoints",allpoints, allpoints.length );
  203. var i, il, f, face,
  204. key, index,
  205. allPointsMap = {},
  206. isolatedPointsMap = {};
  207. // prepare all points map
  208. for ( i = 0, il = allpoints.length; i < il; i ++ ) {
  209. key = allpoints[ i ].x + ":" + allpoints[ i ].y;
  210. if ( allPointsMap[ key ] !== undefined ) {
  211. console.log( "Duplicate point", key );
  212. }
  213. allPointsMap[ key ] = i;
  214. }
  215. // check all face vertices against all points map
  216. for ( i = 0, il = triangles.length; i < il; i ++ ) {
  217. face = triangles[ i ];
  218. for ( f = 0; f < 3; f ++ ) {
  219. key = face[ f ].x + ":" + face[ f ].y;
  220. index = allPointsMap[ key ];
  221. if ( index !== undefined ) {
  222. face[ f ] = index;
  223. }
  224. }
  225. }
  226. // check isolated points vertices against all points map
  227. for ( i = 0, il = isolatedPts.length; i < il; i ++ ) {
  228. face = isolatedPts[ i ];
  229. for ( f = 0; f < 3; f ++ ) {
  230. key = face[ f ].x + ":" + face[ f ].y;
  231. index = allPointsMap[ key ];
  232. if ( index !== undefined ) {
  233. face[ f ] = index;
  234. }
  235. }
  236. }
  237. return triangles.concat( isolatedPts );
  238. }, // end triangulate shapes
  239. /*
  240. triangulate2 : function( pts, holes ) {
  241. // For use with Poly2Tri.js
  242. var allpts = pts.concat();
  243. var shape = [];
  244. for (var p in pts) {
  245. shape.push(new js.poly2tri.Point(pts[p].x, pts[p].y));
  246. }
  247. var swctx = new js.poly2tri.SweepContext(shape);
  248. for (var h in holes) {
  249. var aHole = holes[h];
  250. var newHole = []
  251. for (i in aHole) {
  252. newHole.push(new js.poly2tri.Point(aHole[i].x, aHole[i].y));
  253. allpts.push(aHole[i]);
  254. }
  255. swctx.AddHole(newHole);
  256. }
  257. var find;
  258. var findIndexForPt = function (pt) {
  259. find = new THREE.Vector2(pt.x, pt.y);
  260. var p;
  261. for (p=0, pl = allpts.length; p<pl; p++) {
  262. if (allpts[p].equals(find)) return p;
  263. }
  264. return -1;
  265. };
  266. // triangulate
  267. js.poly2tri.sweep.Triangulate(swctx);
  268. var triangles = swctx.GetTriangles();
  269. var tr ;
  270. var facesPts = [];
  271. for (var t in triangles) {
  272. tr = triangles[t];
  273. facesPts.push([
  274. findIndexForPt(tr.GetPoint(0)),
  275. findIndexForPt(tr.GetPoint(1)),
  276. findIndexForPt(tr.GetPoint(2))
  277. ]);
  278. }
  279. // console.log(facesPts);
  280. // console.log("triangles", triangles.length, triangles);
  281. // Returns array of faces with 3 element each
  282. return facesPts;
  283. },
  284. */
  285. isClockWise: function ( pts ) {
  286. return THREE.FontUtils.Triangulate.area( pts ) < 0;
  287. },
  288. // Bezier Curves formulas obtained from
  289. // http://en.wikipedia.org/wiki/B%C3%A9zier_curve
  290. // Quad Bezier Functions
  291. b2p0: function ( t, p ) {
  292. var k = 1 - t;
  293. return k * k * p;
  294. },
  295. b2p1: function ( t, p ) {
  296. return 2 * ( 1 - t ) * t * p;
  297. },
  298. b2p2: function ( t, p ) {
  299. return t * t * p;
  300. },
  301. b2: function ( t, p0, p1, p2 ) {
  302. return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 );
  303. },
  304. // Cubic Bezier Functions
  305. b3p0: function ( t, p ) {
  306. var k = 1 - t;
  307. return k * k * k * p;
  308. },
  309. b3p1: function ( t, p ) {
  310. var k = 1 - t;
  311. return 3 * k * k * t * p;
  312. },
  313. b3p2: function ( t, p ) {
  314. var k = 1 - t;
  315. return 3 * k * t * t * p;
  316. },
  317. b3p3: function ( t, p ) {
  318. return t * t * t * p;
  319. },
  320. b3: function ( t, p0, p1, p2, p3 ) {
  321. return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 );
  322. }
  323. };
粤ICP备19079148号