source: other-projects/playing-in-the-street/summer-2013/trunk/Playing-in-the-Street-WPF/Content/Web/mrdoob-three.js-4862f5f/src/extras/core/Path.js@ 28897

Last change on this file since 28897 was 28897, checked in by davidb, 10 years ago

GUI front-end to server base plus web page content

File size: 11.3 KB
Line 
1/**
2 * @author zz85 / http://www.lab4games.net/zz85/blog
3 * Creates free form 2d path using series of points, lines or curves.
4 *
5 **/
6
7THREE.Path = function ( points ) {
8
9 THREE.CurvePath.call(this);
10
11 this.actions = [];
12
13 if ( points ) {
14
15 this.fromPoints( points );
16
17 }
18
19};
20
21THREE.Path.prototype = Object.create( THREE.CurvePath.prototype );
22
23THREE.PathActions = {
24
25 MOVE_TO: 'moveTo',
26 LINE_TO: 'lineTo',
27 QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve
28 BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve
29 CSPLINE_THRU: 'splineThru', // Catmull-rom spline
30 ARC: 'arc', // Circle
31 ELLIPSE: 'ellipse'
32};
33
34// TODO Clean up PATH API
35
36// Create path using straight lines to connect all points
37// - vectors: array of Vector2
38
39THREE.Path.prototype.fromPoints = function ( vectors ) {
40
41 this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y );
42
43 for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) {
44
45 this.lineTo( vectors[ v ].x, vectors[ v ].y );
46
47 };
48
49};
50
51// startPath() endPath()?
52
53THREE.Path.prototype.moveTo = function ( x, y ) {
54
55 var args = Array.prototype.slice.call( arguments );
56 this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } );
57
58};
59
60THREE.Path.prototype.lineTo = function ( x, y ) {
61
62 var args = Array.prototype.slice.call( arguments );
63
64 var lastargs = this.actions[ this.actions.length - 1 ].args;
65
66 var x0 = lastargs[ lastargs.length - 2 ];
67 var y0 = lastargs[ lastargs.length - 1 ];
68
69 var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) );
70 this.curves.push( curve );
71
72 this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } );
73
74};
75
76THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) {
77
78 var args = Array.prototype.slice.call( arguments );
79
80 var lastargs = this.actions[ this.actions.length - 1 ].args;
81
82 var x0 = lastargs[ lastargs.length - 2 ];
83 var y0 = lastargs[ lastargs.length - 1 ];
84
85 var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ),
86 new THREE.Vector2( aCPx, aCPy ),
87 new THREE.Vector2( aX, aY ) );
88 this.curves.push( curve );
89
90 this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } );
91
92};
93
94THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y,
95 aCP2x, aCP2y,
96 aX, aY ) {
97
98 var args = Array.prototype.slice.call( arguments );
99
100 var lastargs = this.actions[ this.actions.length - 1 ].args;
101
102 var x0 = lastargs[ lastargs.length - 2 ];
103 var y0 = lastargs[ lastargs.length - 1 ];
104
105 var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ),
106 new THREE.Vector2( aCP1x, aCP1y ),
107 new THREE.Vector2( aCP2x, aCP2y ),
108 new THREE.Vector2( aX, aY ) );
109 this.curves.push( curve );
110
111 this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } );
112
113};
114
115THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) {
116
117 var args = Array.prototype.slice.call( arguments );
118 var lastargs = this.actions[ this.actions.length - 1 ].args;
119
120 var x0 = lastargs[ lastargs.length - 2 ];
121 var y0 = lastargs[ lastargs.length - 1 ];
122//---
123 var npts = [ new THREE.Vector2( x0, y0 ) ];
124 Array.prototype.push.apply( npts, pts );
125
126 var curve = new THREE.SplineCurve( npts );
127 this.curves.push( curve );
128
129 this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } );
130
131};
132
133// FUTURE: Change the API or follow canvas API?
134
135THREE.Path.prototype.arc = function ( aX, aY, aRadius,
136 aStartAngle, aEndAngle, aClockwise ) {
137
138 var lastargs = this.actions[ this.actions.length - 1].args;
139 var x0 = lastargs[ lastargs.length - 2 ];
140 var y0 = lastargs[ lastargs.length - 1 ];
141
142 this.absarc(aX + x0, aY + y0, aRadius,
143 aStartAngle, aEndAngle, aClockwise );
144
145 };
146
147 THREE.Path.prototype.absarc = function ( aX, aY, aRadius,
148 aStartAngle, aEndAngle, aClockwise ) {
149 this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise);
150 };
151
152THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius,
153 aStartAngle, aEndAngle, aClockwise ) {
154
155 var lastargs = this.actions[ this.actions.length - 1].args;
156 var x0 = lastargs[ lastargs.length - 2 ];
157 var y0 = lastargs[ lastargs.length - 1 ];
158
159 this.absellipse(aX + x0, aY + y0, xRadius, yRadius,
160 aStartAngle, aEndAngle, aClockwise );
161
162 };
163
164
165THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius,
166 aStartAngle, aEndAngle, aClockwise ) {
167
168 var args = Array.prototype.slice.call( arguments );
169 var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius,
170 aStartAngle, aEndAngle, aClockwise );
171 this.curves.push( curve );
172
173 var lastPoint = curve.getPoint(1);
174 args.push(lastPoint.x);
175 args.push(lastPoint.y);
176
177 this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } );
178
179 };
180
181THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) {
182
183 if ( ! divisions ) divisions = 40;
184
185 var points = [];
186
187 for ( var i = 0; i < divisions; i ++ ) {
188
189 points.push( this.getPoint( i / divisions ) );
190
191 //if( !this.getPoint( i / divisions ) ) throw "DIE";
192
193 }
194
195 // if ( closedPath ) {
196 //
197 // points.push( points[ 0 ] );
198 //
199 // }
200
201 return points;
202
203};
204
205/* Return an array of vectors based on contour of the path */
206
207THREE.Path.prototype.getPoints = function( divisions, closedPath ) {
208
209 if (this.useSpacedPoints) {
210 console.log('tata');
211 return this.getSpacedPoints( divisions, closedPath );
212 }
213
214 divisions = divisions || 12;
215
216 var points = [];
217
218 var i, il, item, action, args;
219 var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0,
220 laste, j,
221 t, tx, ty;
222
223 for ( i = 0, il = this.actions.length; i < il; i ++ ) {
224
225 item = this.actions[ i ];
226
227 action = item.action;
228 args = item.args;
229
230 switch( action ) {
231
232 case THREE.PathActions.MOVE_TO:
233
234 points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
235
236 break;
237
238 case THREE.PathActions.LINE_TO:
239
240 points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
241
242 break;
243
244 case THREE.PathActions.QUADRATIC_CURVE_TO:
245
246 cpx = args[ 2 ];
247 cpy = args[ 3 ];
248
249 cpx1 = args[ 0 ];
250 cpy1 = args[ 1 ];
251
252 if ( points.length > 0 ) {
253
254 laste = points[ points.length - 1 ];
255
256 cpx0 = laste.x;
257 cpy0 = laste.y;
258
259 } else {
260
261 laste = this.actions[ i - 1 ].args;
262
263 cpx0 = laste[ laste.length - 2 ];
264 cpy0 = laste[ laste.length - 1 ];
265
266 }
267
268 for ( j = 1; j <= divisions; j ++ ) {
269
270 t = j / divisions;
271
272 tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
273 ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
274
275 points.push( new THREE.Vector2( tx, ty ) );
276
277 }
278
279 break;
280
281 case THREE.PathActions.BEZIER_CURVE_TO:
282
283 cpx = args[ 4 ];
284 cpy = args[ 5 ];
285
286 cpx1 = args[ 0 ];
287 cpy1 = args[ 1 ];
288
289 cpx2 = args[ 2 ];
290 cpy2 = args[ 3 ];
291
292 if ( points.length > 0 ) {
293
294 laste = points[ points.length - 1 ];
295
296 cpx0 = laste.x;
297 cpy0 = laste.y;
298
299 } else {
300
301 laste = this.actions[ i - 1 ].args;
302
303 cpx0 = laste[ laste.length - 2 ];
304 cpy0 = laste[ laste.length - 1 ];
305
306 }
307
308
309 for ( j = 1; j <= divisions; j ++ ) {
310
311 t = j / divisions;
312
313 tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
314 ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
315
316 points.push( new THREE.Vector2( tx, ty ) );
317
318 }
319
320 break;
321
322 case THREE.PathActions.CSPLINE_THRU:
323
324 laste = this.actions[ i - 1 ].args;
325
326 var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] );
327 var spts = [ last ];
328
329 var n = divisions * args[ 0 ].length;
330
331 spts = spts.concat( args[ 0 ] );
332
333 var spline = new THREE.SplineCurve( spts );
334
335 for ( j = 1; j <= n; j ++ ) {
336
337 points.push( spline.getPointAt( j / n ) ) ;
338
339 }
340
341 break;
342
343 case THREE.PathActions.ARC:
344
345 var aX = args[ 0 ], aY = args[ 1 ],
346 aRadius = args[ 2 ],
347 aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
348 aClockwise = !!args[ 5 ];
349
350 var deltaAngle = aEndAngle - aStartAngle;
351 var angle;
352 var tdivisions = divisions * 2;
353
354 for ( j = 1; j <= tdivisions; j ++ ) {
355
356 t = j / tdivisions;
357
358 if ( ! aClockwise ) {
359
360 t = 1 - t;
361
362 }
363
364 angle = aStartAngle + t * deltaAngle;
365
366 tx = aX + aRadius * Math.cos( angle );
367 ty = aY + aRadius * Math.sin( angle );
368
369 //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
370
371 points.push( new THREE.Vector2( tx, ty ) );
372
373 }
374
375 //console.log(points);
376
377 break;
378
379 case THREE.PathActions.ELLIPSE:
380
381 var aX = args[ 0 ], aY = args[ 1 ],
382 xRadius = args[ 2 ],
383 yRadius = args[ 3 ],
384 aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
385 aClockwise = !!args[ 6 ];
386
387
388 var deltaAngle = aEndAngle - aStartAngle;
389 var angle;
390 var tdivisions = divisions * 2;
391
392 for ( j = 1; j <= tdivisions; j ++ ) {
393
394 t = j / tdivisions;
395
396 if ( ! aClockwise ) {
397
398 t = 1 - t;
399
400 }
401
402 angle = aStartAngle + t * deltaAngle;
403
404 tx = aX + xRadius * Math.cos( angle );
405 ty = aY + yRadius * Math.sin( angle );
406
407 //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
408
409 points.push( new THREE.Vector2( tx, ty ) );
410
411 }
412
413 //console.log(points);
414
415 break;
416
417 } // end switch
418
419 }
420
421
422
423 // Normalize to remove the closing point by default.
424 var lastPoint = points[ points.length - 1];
425 var EPSILON = 0.0000000001;
426 if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON &&
427 Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON)
428 points.splice( points.length - 1, 1);
429 if ( closedPath ) {
430
431 points.push( points[ 0 ] );
432
433 }
434
435 return points;
436
437};
438
439// Breaks path into shapes
440
441THREE.Path.prototype.toShapes = function( isCCW ) {
442
443 var i, il, item, action, args;
444
445 var subPaths = [], lastPath = new THREE.Path();
446
447 for ( i = 0, il = this.actions.length; i < il; i ++ ) {
448
449 item = this.actions[ i ];
450
451 args = item.args;
452 action = item.action;
453
454 if ( action == THREE.PathActions.MOVE_TO ) {
455
456 if ( lastPath.actions.length != 0 ) {
457
458 subPaths.push( lastPath );
459 lastPath = new THREE.Path();
460
461 }
462
463 }
464
465 lastPath[ action ].apply( lastPath, args );
466
467 }
468
469 if ( lastPath.actions.length != 0 ) {
470
471 subPaths.push( lastPath );
472
473 }
474
475 // console.log(subPaths);
476
477 if ( subPaths.length == 0 ) return [];
478
479 var solid, tmpPath, tmpShape, shapes = [];
480
481 if ( subPaths.length == 1) {
482
483 tmpPath = subPaths[0];
484 tmpShape = new THREE.Shape();
485 tmpShape.actions = tmpPath.actions;
486 tmpShape.curves = tmpPath.curves;
487 shapes.push( tmpShape );
488 return shapes;
489
490 }
491
492 var holesFirst = !THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() );
493 holesFirst = isCCW ? !holesFirst : holesFirst;
494
495 // console.log("Holes first", holesFirst);
496
497 if ( holesFirst ) {
498
499 tmpShape = new THREE.Shape();
500
501 for ( i = 0, il = subPaths.length; i < il; i ++ ) {
502
503 tmpPath = subPaths[ i ];
504 solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() );
505 solid = isCCW ? !solid : solid;
506
507 if ( solid ) {
508
509 tmpShape.actions = tmpPath.actions;
510 tmpShape.curves = tmpPath.curves;
511
512 shapes.push( tmpShape );
513 tmpShape = new THREE.Shape();
514
515 //console.log('cw', i);
516
517 } else {
518
519 tmpShape.holes.push( tmpPath );
520
521 //console.log('ccw', i);
522
523 }
524
525 }
526
527 } else {
528
529 // Shapes first
530 tmpShape = undefined;
531
532 for ( i = 0, il = subPaths.length; i < il; i ++ ) {
533
534 tmpPath = subPaths[ i ];
535 solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() );
536 solid = isCCW ? !solid : solid;
537
538 if ( solid ) {
539
540 if ( tmpShape ) shapes.push( tmpShape );
541
542 tmpShape = new THREE.Shape();
543 tmpShape.actions = tmpPath.actions;
544 tmpShape.curves = tmpPath.curves;
545
546 } else {
547
548 tmpShape.holes.push( tmpPath );
549
550 }
551
552 }
553
554 shapes.push( tmpShape );
555
556 }
557
558 //console.log("shape", shapes);
559
560 return shapes;
561
562};
Note: See TracBrowser for help on using the repository browser.