1 | /**
|
---|
2 | * @author zz85 / http://www.lab4games.net/zz85/blog
|
---|
3 | * Defines a 2d shape plane using paths.
|
---|
4 | **/
|
---|
5 |
|
---|
6 | // STEP 1 Create a path.
|
---|
7 | // STEP 2 Turn path into shape.
|
---|
8 | // STEP 3 ExtrudeGeometry takes in Shape/Shapes
|
---|
9 | // STEP 3a - Extract points from each shape, turn to vertices
|
---|
10 | // STEP 3b - Triangulate each shape, add faces.
|
---|
11 |
|
---|
12 | THREE.Shape = function () {
|
---|
13 |
|
---|
14 | THREE.Path.apply( this, arguments );
|
---|
15 | this.holes = [];
|
---|
16 |
|
---|
17 | };
|
---|
18 |
|
---|
19 | THREE.Shape.prototype = Object.create( THREE.Path.prototype );
|
---|
20 |
|
---|
21 | // Convenience method to return ExtrudeGeometry
|
---|
22 |
|
---|
23 | THREE.Shape.prototype.extrude = function ( options ) {
|
---|
24 |
|
---|
25 | var extruded = new THREE.ExtrudeGeometry( this, options );
|
---|
26 | return extruded;
|
---|
27 |
|
---|
28 | };
|
---|
29 |
|
---|
30 | // Convenience method to return ShapeGeometry
|
---|
31 |
|
---|
32 | THREE.Shape.prototype.makeGeometry = function ( options ) {
|
---|
33 |
|
---|
34 | var geometry = new THREE.ShapeGeometry( this, options );
|
---|
35 | return geometry;
|
---|
36 |
|
---|
37 | };
|
---|
38 |
|
---|
39 | // Get points of holes
|
---|
40 |
|
---|
41 | THREE.Shape.prototype.getPointsHoles = function ( divisions ) {
|
---|
42 |
|
---|
43 | var i, il = this.holes.length, holesPts = [];
|
---|
44 |
|
---|
45 | for ( i = 0; i < il; i ++ ) {
|
---|
46 |
|
---|
47 | holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends );
|
---|
48 |
|
---|
49 | }
|
---|
50 |
|
---|
51 | return holesPts;
|
---|
52 |
|
---|
53 | };
|
---|
54 |
|
---|
55 | // Get points of holes (spaced by regular distance)
|
---|
56 |
|
---|
57 | THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) {
|
---|
58 |
|
---|
59 | var i, il = this.holes.length, holesPts = [];
|
---|
60 |
|
---|
61 | for ( i = 0; i < il; i ++ ) {
|
---|
62 |
|
---|
63 | holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends );
|
---|
64 |
|
---|
65 | }
|
---|
66 |
|
---|
67 | return holesPts;
|
---|
68 |
|
---|
69 | };
|
---|
70 |
|
---|
71 |
|
---|
72 | // Get points of shape and holes (keypoints based on segments parameter)
|
---|
73 |
|
---|
74 | THREE.Shape.prototype.extractAllPoints = function ( divisions ) {
|
---|
75 |
|
---|
76 | return {
|
---|
77 |
|
---|
78 | shape: this.getTransformedPoints( divisions ),
|
---|
79 | holes: this.getPointsHoles( divisions )
|
---|
80 |
|
---|
81 | };
|
---|
82 |
|
---|
83 | };
|
---|
84 |
|
---|
85 | THREE.Shape.prototype.extractPoints = function ( divisions ) {
|
---|
86 |
|
---|
87 | if (this.useSpacedPoints) {
|
---|
88 | return this.extractAllSpacedPoints(divisions);
|
---|
89 | }
|
---|
90 |
|
---|
91 | return this.extractAllPoints(divisions);
|
---|
92 |
|
---|
93 | };
|
---|
94 |
|
---|
95 | //
|
---|
96 | // THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) {
|
---|
97 | //
|
---|
98 | // return {
|
---|
99 | //
|
---|
100 | // shape: this.transform( bend, divisions ),
|
---|
101 | // holes: this.getPointsHoles( divisions, bend )
|
---|
102 | //
|
---|
103 | // };
|
---|
104 | //
|
---|
105 | // };
|
---|
106 |
|
---|
107 | // Get points of shape and holes (spaced by regular distance)
|
---|
108 |
|
---|
109 | THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) {
|
---|
110 |
|
---|
111 | return {
|
---|
112 |
|
---|
113 | shape: this.getTransformedSpacedPoints( divisions ),
|
---|
114 | holes: this.getSpacedPointsHoles( divisions )
|
---|
115 |
|
---|
116 | };
|
---|
117 |
|
---|
118 | };
|
---|
119 |
|
---|
120 | /**************************************************************
|
---|
121 | * Utils
|
---|
122 | **************************************************************/
|
---|
123 |
|
---|
124 | THREE.Shape.Utils = {
|
---|
125 |
|
---|
126 | /*
|
---|
127 | contour - array of vector2 for contour
|
---|
128 | holes - array of array of vector2
|
---|
129 | */
|
---|
130 |
|
---|
131 | removeHoles: function ( contour, holes ) {
|
---|
132 |
|
---|
133 | var shape = contour.concat(); // work on this shape
|
---|
134 | var allpoints = shape.concat();
|
---|
135 |
|
---|
136 | /* For each isolated shape, find the closest points and break to the hole to allow triangulation */
|
---|
137 |
|
---|
138 |
|
---|
139 | var prevShapeVert, nextShapeVert,
|
---|
140 | prevHoleVert, nextHoleVert,
|
---|
141 | holeIndex, shapeIndex,
|
---|
142 | shapeId, shapeGroup,
|
---|
143 | h, h2,
|
---|
144 | hole, shortest, d,
|
---|
145 | p, pts1, pts2,
|
---|
146 | tmpShape1, tmpShape2,
|
---|
147 | tmpHole1, tmpHole2,
|
---|
148 | verts = [];
|
---|
149 |
|
---|
150 | for ( h = 0; h < holes.length; h ++ ) {
|
---|
151 |
|
---|
152 | hole = holes[ h ];
|
---|
153 |
|
---|
154 | /*
|
---|
155 | shapeholes[ h ].concat(); // preserves original
|
---|
156 | holes.push( hole );
|
---|
157 | */
|
---|
158 |
|
---|
159 | Array.prototype.push.apply( allpoints, hole );
|
---|
160 |
|
---|
161 | shortest = Number.POSITIVE_INFINITY;
|
---|
162 |
|
---|
163 |
|
---|
164 | // Find the shortest pair of pts between shape and hole
|
---|
165 |
|
---|
166 | // Note: Actually, I'm not sure now if we could optimize this to be faster than O(m*n)
|
---|
167 | // Using distanceToSquared() intead of distanceTo() should speed a little
|
---|
168 | // since running square roots operations are reduced.
|
---|
169 |
|
---|
170 | for ( h2 = 0; h2 < hole.length; h2 ++ ) {
|
---|
171 |
|
---|
172 | pts1 = hole[ h2 ];
|
---|
173 | var dist = [];
|
---|
174 |
|
---|
175 | for ( p = 0; p < shape.length; p++ ) {
|
---|
176 |
|
---|
177 | pts2 = shape[ p ];
|
---|
178 | d = pts1.distanceToSquared( pts2 );
|
---|
179 | dist.push( d );
|
---|
180 |
|
---|
181 | if ( d < shortest ) {
|
---|
182 |
|
---|
183 | shortest = d;
|
---|
184 | holeIndex = h2;
|
---|
185 | shapeIndex = p;
|
---|
186 |
|
---|
187 | }
|
---|
188 |
|
---|
189 | }
|
---|
190 |
|
---|
191 | }
|
---|
192 |
|
---|
193 | //console.log("shortest", shortest, dist);
|
---|
194 |
|
---|
195 | prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
|
---|
196 | prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
|
---|
197 |
|
---|
198 | var areaapts = [
|
---|
199 |
|
---|
200 | hole[ holeIndex ],
|
---|
201 | shape[ shapeIndex ],
|
---|
202 | shape[ prevShapeVert ]
|
---|
203 |
|
---|
204 | ];
|
---|
205 |
|
---|
206 | var areaa = THREE.FontUtils.Triangulate.area( areaapts );
|
---|
207 |
|
---|
208 | var areabpts = [
|
---|
209 |
|
---|
210 | hole[ holeIndex ],
|
---|
211 | hole[ prevHoleVert ],
|
---|
212 | shape[ shapeIndex ]
|
---|
213 |
|
---|
214 | ];
|
---|
215 |
|
---|
216 | var areab = THREE.FontUtils.Triangulate.area( areabpts );
|
---|
217 |
|
---|
218 | var shapeOffset = 1;
|
---|
219 | var holeOffset = -1;
|
---|
220 |
|
---|
221 | var oldShapeIndex = shapeIndex, oldHoleIndex = holeIndex;
|
---|
222 | shapeIndex += shapeOffset;
|
---|
223 | holeIndex += holeOffset;
|
---|
224 |
|
---|
225 | if ( shapeIndex < 0 ) { shapeIndex += shape.length; }
|
---|
226 | shapeIndex %= shape.length;
|
---|
227 |
|
---|
228 | if ( holeIndex < 0 ) { holeIndex += hole.length; }
|
---|
229 | holeIndex %= hole.length;
|
---|
230 |
|
---|
231 | prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
|
---|
232 | prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
|
---|
233 |
|
---|
234 | areaapts = [
|
---|
235 |
|
---|
236 | hole[ holeIndex ],
|
---|
237 | shape[ shapeIndex ],
|
---|
238 | shape[ prevShapeVert ]
|
---|
239 |
|
---|
240 | ];
|
---|
241 |
|
---|
242 | var areaa2 = THREE.FontUtils.Triangulate.area( areaapts );
|
---|
243 |
|
---|
244 | areabpts = [
|
---|
245 |
|
---|
246 | hole[ holeIndex ],
|
---|
247 | hole[ prevHoleVert ],
|
---|
248 | shape[ shapeIndex ]
|
---|
249 |
|
---|
250 | ];
|
---|
251 |
|
---|
252 | var areab2 = THREE.FontUtils.Triangulate.area( areabpts );
|
---|
253 | //console.log(areaa,areab ,areaa2,areab2, ( areaa + areab ), ( areaa2 + areab2 ));
|
---|
254 |
|
---|
255 | if ( ( areaa + areab ) > ( areaa2 + areab2 ) ) {
|
---|
256 |
|
---|
257 | // In case areas are not correct.
|
---|
258 | //console.log("USE THIS");
|
---|
259 |
|
---|
260 | shapeIndex = oldShapeIndex;
|
---|
261 | holeIndex = oldHoleIndex ;
|
---|
262 |
|
---|
263 | if ( shapeIndex < 0 ) { shapeIndex += shape.length; }
|
---|
264 | shapeIndex %= shape.length;
|
---|
265 |
|
---|
266 | if ( holeIndex < 0 ) { holeIndex += hole.length; }
|
---|
267 | holeIndex %= hole.length;
|
---|
268 |
|
---|
269 | prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
|
---|
270 | prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
|
---|
271 |
|
---|
272 | } else {
|
---|
273 |
|
---|
274 | //console.log("USE THAT ")
|
---|
275 |
|
---|
276 | }
|
---|
277 |
|
---|
278 | tmpShape1 = shape.slice( 0, shapeIndex );
|
---|
279 | tmpShape2 = shape.slice( shapeIndex );
|
---|
280 | tmpHole1 = hole.slice( holeIndex );
|
---|
281 | tmpHole2 = hole.slice( 0, holeIndex );
|
---|
282 |
|
---|
283 | // Should check orders here again?
|
---|
284 |
|
---|
285 | var trianglea = [
|
---|
286 |
|
---|
287 | hole[ holeIndex ],
|
---|
288 | shape[ shapeIndex ],
|
---|
289 | shape[ prevShapeVert ]
|
---|
290 |
|
---|
291 | ];
|
---|
292 |
|
---|
293 | var triangleb = [
|
---|
294 |
|
---|
295 | hole[ holeIndex ] ,
|
---|
296 | hole[ prevHoleVert ],
|
---|
297 | shape[ shapeIndex ]
|
---|
298 |
|
---|
299 | ];
|
---|
300 |
|
---|
301 | verts.push( trianglea );
|
---|
302 | verts.push( triangleb );
|
---|
303 |
|
---|
304 | shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
|
---|
305 |
|
---|
306 | }
|
---|
307 |
|
---|
308 | return {
|
---|
309 |
|
---|
310 | shape:shape, /* shape with no holes */
|
---|
311 | isolatedPts: verts, /* isolated faces */
|
---|
312 | allpoints: allpoints
|
---|
313 |
|
---|
314 | }
|
---|
315 |
|
---|
316 |
|
---|
317 | },
|
---|
318 |
|
---|
319 | triangulateShape: function ( contour, holes ) {
|
---|
320 |
|
---|
321 | var shapeWithoutHoles = THREE.Shape.Utils.removeHoles( contour, holes );
|
---|
322 |
|
---|
323 | var shape = shapeWithoutHoles.shape,
|
---|
324 | allpoints = shapeWithoutHoles.allpoints,
|
---|
325 | isolatedPts = shapeWithoutHoles.isolatedPts;
|
---|
326 |
|
---|
327 | var triangles = THREE.FontUtils.Triangulate( shape, false ); // True returns indices for points of spooled shape
|
---|
328 |
|
---|
329 | // 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.
|
---|
330 |
|
---|
331 | //console.log( "triangles",triangles, triangles.length );
|
---|
332 | //console.log( "allpoints",allpoints, allpoints.length );
|
---|
333 |
|
---|
334 | var i, il, f, face,
|
---|
335 | key, index,
|
---|
336 | allPointsMap = {},
|
---|
337 | isolatedPointsMap = {};
|
---|
338 |
|
---|
339 | // prepare all points map
|
---|
340 |
|
---|
341 | for ( i = 0, il = allpoints.length; i < il; i ++ ) {
|
---|
342 |
|
---|
343 | key = allpoints[ i ].x + ":" + allpoints[ i ].y;
|
---|
344 |
|
---|
345 | if ( allPointsMap[ key ] !== undefined ) {
|
---|
346 |
|
---|
347 | console.log( "Duplicate point", key );
|
---|
348 |
|
---|
349 | }
|
---|
350 |
|
---|
351 | allPointsMap[ key ] = i;
|
---|
352 |
|
---|
353 | }
|
---|
354 |
|
---|
355 | // check all face vertices against all points map
|
---|
356 |
|
---|
357 | for ( i = 0, il = triangles.length; i < il; i ++ ) {
|
---|
358 |
|
---|
359 | face = triangles[ i ];
|
---|
360 |
|
---|
361 | for ( f = 0; f < 3; f ++ ) {
|
---|
362 |
|
---|
363 | key = face[ f ].x + ":" + face[ f ].y;
|
---|
364 |
|
---|
365 | index = allPointsMap[ key ];
|
---|
366 |
|
---|
367 | if ( index !== undefined ) {
|
---|
368 |
|
---|
369 | face[ f ] = index;
|
---|
370 |
|
---|
371 | }
|
---|
372 |
|
---|
373 | }
|
---|
374 |
|
---|
375 | }
|
---|
376 |
|
---|
377 | // check isolated points vertices against all points map
|
---|
378 |
|
---|
379 | for ( i = 0, il = isolatedPts.length; i < il; i ++ ) {
|
---|
380 |
|
---|
381 | face = isolatedPts[ i ];
|
---|
382 |
|
---|
383 | for ( f = 0; f < 3; f ++ ) {
|
---|
384 |
|
---|
385 | key = face[ f ].x + ":" + face[ f ].y;
|
---|
386 |
|
---|
387 | index = allPointsMap[ key ];
|
---|
388 |
|
---|
389 | if ( index !== undefined ) {
|
---|
390 |
|
---|
391 | face[ f ] = index;
|
---|
392 |
|
---|
393 | }
|
---|
394 |
|
---|
395 | }
|
---|
396 |
|
---|
397 | }
|
---|
398 |
|
---|
399 | return triangles.concat( isolatedPts );
|
---|
400 |
|
---|
401 | }, // end triangulate shapes
|
---|
402 |
|
---|
403 | /*
|
---|
404 | triangulate2 : function( pts, holes ) {
|
---|
405 |
|
---|
406 | // For use with Poly2Tri.js
|
---|
407 |
|
---|
408 | var allpts = pts.concat();
|
---|
409 | var shape = [];
|
---|
410 | for (var p in pts) {
|
---|
411 | shape.push(new js.poly2tri.Point(pts[p].x, pts[p].y));
|
---|
412 | }
|
---|
413 |
|
---|
414 | var swctx = new js.poly2tri.SweepContext(shape);
|
---|
415 |
|
---|
416 | for (var h in holes) {
|
---|
417 | var aHole = holes[h];
|
---|
418 | var newHole = []
|
---|
419 | for (i in aHole) {
|
---|
420 | newHole.push(new js.poly2tri.Point(aHole[i].x, aHole[i].y));
|
---|
421 | allpts.push(aHole[i]);
|
---|
422 | }
|
---|
423 | swctx.AddHole(newHole);
|
---|
424 | }
|
---|
425 |
|
---|
426 | var find;
|
---|
427 | var findIndexForPt = function (pt) {
|
---|
428 | find = new THREE.Vector2(pt.x, pt.y);
|
---|
429 | var p;
|
---|
430 | for (p=0, pl = allpts.length; p<pl; p++) {
|
---|
431 | if (allpts[p].equals(find)) return p;
|
---|
432 | }
|
---|
433 | return -1;
|
---|
434 | };
|
---|
435 |
|
---|
436 | // triangulate
|
---|
437 | js.poly2tri.sweep.Triangulate(swctx);
|
---|
438 |
|
---|
439 | var triangles = swctx.GetTriangles();
|
---|
440 | var tr ;
|
---|
441 | var facesPts = [];
|
---|
442 | for (var t in triangles) {
|
---|
443 | tr = triangles[t];
|
---|
444 | facesPts.push([
|
---|
445 | findIndexForPt(tr.GetPoint(0)),
|
---|
446 | findIndexForPt(tr.GetPoint(1)),
|
---|
447 | findIndexForPt(tr.GetPoint(2))
|
---|
448 | ]);
|
---|
449 | }
|
---|
450 |
|
---|
451 |
|
---|
452 | // console.log(facesPts);
|
---|
453 | // console.log("triangles", triangles.length, triangles);
|
---|
454 |
|
---|
455 | // Returns array of faces with 3 element each
|
---|
456 | return facesPts;
|
---|
457 | },
|
---|
458 | */
|
---|
459 |
|
---|
460 | isClockWise: function ( pts ) {
|
---|
461 |
|
---|
462 | return THREE.FontUtils.Triangulate.area( pts ) < 0;
|
---|
463 |
|
---|
464 | },
|
---|
465 |
|
---|
466 | // Bezier Curves formulas obtained from
|
---|
467 | // http://en.wikipedia.org/wiki/B%C3%A9zier_curve
|
---|
468 |
|
---|
469 | // Quad Bezier Functions
|
---|
470 |
|
---|
471 | b2p0: function ( t, p ) {
|
---|
472 |
|
---|
473 | var k = 1 - t;
|
---|
474 | return k * k * p;
|
---|
475 |
|
---|
476 | },
|
---|
477 |
|
---|
478 | b2p1: function ( t, p ) {
|
---|
479 |
|
---|
480 | return 2 * ( 1 - t ) * t * p;
|
---|
481 |
|
---|
482 | },
|
---|
483 |
|
---|
484 | b2p2: function ( t, p ) {
|
---|
485 |
|
---|
486 | return t * t * p;
|
---|
487 |
|
---|
488 | },
|
---|
489 |
|
---|
490 | b2: function ( t, p0, p1, p2 ) {
|
---|
491 |
|
---|
492 | return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 );
|
---|
493 |
|
---|
494 | },
|
---|
495 |
|
---|
496 | // Cubic Bezier Functions
|
---|
497 |
|
---|
498 | b3p0: function ( t, p ) {
|
---|
499 |
|
---|
500 | var k = 1 - t;
|
---|
501 | return k * k * k * p;
|
---|
502 |
|
---|
503 | },
|
---|
504 |
|
---|
505 | b3p1: function ( t, p ) {
|
---|
506 |
|
---|
507 | var k = 1 - t;
|
---|
508 | return 3 * k * k * t * p;
|
---|
509 |
|
---|
510 | },
|
---|
511 |
|
---|
512 | b3p2: function ( t, p ) {
|
---|
513 |
|
---|
514 | var k = 1 - t;
|
---|
515 | return 3 * k * t * t * p;
|
---|
516 |
|
---|
517 | },
|
---|
518 |
|
---|
519 | b3p3: function ( t, p ) {
|
---|
520 |
|
---|
521 | return t * t * t * p;
|
---|
522 |
|
---|
523 | },
|
---|
524 |
|
---|
525 | b3: function ( t, p0, p1, p2, p3 ) {
|
---|
526 |
|
---|
527 | return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 );
|
---|
528 |
|
---|
529 | }
|
---|
530 |
|
---|
531 | };
|
---|
532 |
|
---|