Path.js 11.8 KB
Newer Older
Z
zz85 已提交
1 2
/**
 * @author zz85 / http://www.lab4games.net/zz85/blog
Z
zz85 已提交
3 4
 * Creates free form 2d path using series of points, lines or curves.
 *
Z
zz85 已提交
5 6
 **/

7 8
THREE.Path = function ( points ) {

9
	THREE.CurvePath.call(this);
10 11 12 13 14 15 16

	this.actions = [];

	if ( points ) {

		this.fromPoints( points );

Z
zz85 已提交
17
	}
18

Z
zz85 已提交
19 20
};

21
THREE.Path.prototype = Object.create( THREE.CurvePath.prototype );
22

23 24
THREE.PathActions = {

Z
zz85 已提交
25 26
	MOVE_TO: 'moveTo',
	LINE_TO: 'lineTo',
Z
zz85 已提交
27 28 29
	QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve
	BEZIER_CURVE_TO: 'bezierCurveTo', 		// Bezier cubic curve
	CSPLINE_THRU: 'splineThru',				// Catmull-rom spline
30 31
	ARC: 'arc',								// Circle
	ELLIPSE: 'ellipse'
Z
zz85 已提交
32 33
};

34
// TODO Clean up PATH API
35

36 37
// Create path using straight lines to connect all points
// - vectors: array of Vector2
38

39
THREE.Path.prototype.fromPoints = function ( vectors ) {
40 41 42

	this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y );

Z
zz85 已提交
43
	for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) {
44 45 46

		this.lineTo( vectors[ v ].x, vectors[ v ].y );

Z
zz85 已提交
47
	};
48

Z
zz85 已提交
49 50
};

Z
zz85 已提交
51 52
// startPath() endPath()?

53
THREE.Path.prototype.moveTo = function ( x, y ) {
54 55 56 57

	var args = Array.prototype.slice.call( arguments );
	this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } );

Z
zz85 已提交
58 59
};

60
THREE.Path.prototype.lineTo = function ( x, y ) {
61 62

	var args = Array.prototype.slice.call( arguments );
63

Z
curves  
zz85 已提交
64
	var lastargs = this.actions[ this.actions.length - 1 ].args;
65

Z
curves  
zz85 已提交
66 67
	var x0 = lastargs[ lastargs.length - 2 ];
	var y0 = lastargs[ lastargs.length - 1 ];
68

69
	var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) );
Z
zz85 已提交
70
	this.curves.push( curve );
71

72
	this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } );
73

Z
zz85 已提交
74 75
};

76 77 78
THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) {

	var args = Array.prototype.slice.call( arguments );
79

Z
curves  
zz85 已提交
80
	var lastargs = this.actions[ this.actions.length - 1 ].args;
81

Z
curves  
zz85 已提交
82 83
	var x0 = lastargs[ lastargs.length - 2 ];
	var y0 = lastargs[ lastargs.length - 1 ];
84

85 86 87
	var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ),
												new THREE.Vector2( aCPx, aCPy ),
												new THREE.Vector2( aX, aY ) );
Z
zz85 已提交
88
	this.curves.push( curve );
89

90
	this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } );
91

Z
zz85 已提交
92 93
};

94 95
THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y,
                                               aCP2x, aCP2y,
96
                                               aX, aY ) {
97 98

	var args = Array.prototype.slice.call( arguments );
99

Z
curves  
zz85 已提交
100
	var lastargs = this.actions[ this.actions.length - 1 ].args;
101

Z
curves  
zz85 已提交
102 103 104
	var x0 = lastargs[ lastargs.length - 2 ];
	var y0 = lastargs[ lastargs.length - 1 ];

105 106 107 108
	var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ),
											new THREE.Vector2( aCP1x, aCP1y ),
											new THREE.Vector2( aCP2x, aCP2y ),
											new THREE.Vector2( aX, aY ) );
Z
zz85 已提交
109
	this.curves.push( curve );
110

111
	this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } );
112

Z
zz85 已提交
113 114
};

Z
zz85 已提交
115
THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) {
116

Z
zz85 已提交
117
	var args = Array.prototype.slice.call( arguments );
Z
curves  
zz85 已提交
118
	var lastargs = this.actions[ this.actions.length - 1 ].args;
119

Z
curves  
zz85 已提交
120 121
	var x0 = lastargs[ lastargs.length - 2 ];
	var y0 = lastargs[ lastargs.length - 1 ];
Z
zz85 已提交
122
//---
123
	var npts = [ new THREE.Vector2( x0, y0 ) ];
124
	Array.prototype.push.apply( npts, pts );
125

Z
zz85 已提交
126 127
	var curve = new THREE.SplineCurve( npts );
	this.curves.push( curve );
128

129
	this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } );
130

131
};
Z
zz85 已提交
132

Z
zz85 已提交
133
// FUTURE: Change the API or follow canvas API?
134

135
THREE.Path.prototype.arc = function ( aX, aY, aRadius,
136
									  aStartAngle, aEndAngle, aClockwise ) {
137

138 139 140 141 142 143 144 145 146 147 148 149
	var lastargs = this.actions[ this.actions.length - 1].args;
	var x0 = lastargs[ lastargs.length - 2 ];
	var y0 = lastargs[ lastargs.length - 1 ];

	this.absarc(aX + x0, aY + y0, aRadius,
		aStartAngle, aEndAngle, aClockwise );
	
 };

 THREE.Path.prototype.absarc = function ( aX, aY, aRadius,
									  aStartAngle, aEndAngle, aClockwise ) {
	this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise);
150 151
 };
 
152
THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius,
153
									  aStartAngle, aEndAngle, aClockwise ) {
A
alteredq 已提交
154

155 156 157 158 159 160 161
	var lastargs = this.actions[ this.actions.length - 1].args;
	var x0 = lastargs[ lastargs.length - 2 ];
	var y0 = lastargs[ lastargs.length - 1 ];

	this.absellipse(aX + x0, aY + y0, xRadius, yRadius,
		aStartAngle, aEndAngle, aClockwise );

162 163 164 165 166 167 168 169
 };
 

THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius,
									  aStartAngle, aEndAngle, aClockwise ) {

	var args = Array.prototype.slice.call( arguments );
	var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius,
170 171 172 173 174 175 176
									aStartAngle, aEndAngle, aClockwise );
	this.curves.push( curve );

	var lastPoint = curve.getPoint(aClockwise ? 1 : 0);
	args.push(lastPoint.x);
	args.push(lastPoint.y);

177
	this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } );
178 179 180

 };

181
THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) {
182

Z
zz85 已提交
183
	if ( ! divisions ) divisions = 40;
184

185 186
	var points = [];

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

Z
zz85 已提交
193
	}
Z
zz85 已提交
194

195
	// if ( closedPath ) {
196
	//
197
	// 	points.push( points[ 0 ] );
198
	//
199
	// }
200 201

	return points;
202 203

};
Z
zz85 已提交
204

Z
zz85 已提交
205
/* Return an array of vectors based on contour of the path */
206

A
alteredq 已提交
207
THREE.Path.prototype.getPoints = function( divisions, closedPath ) {
208

209 210 211 212 213
	if (this.useSpacedPoints) {
		console.log('tata');
		return this.getSpacedPoints( divisions, closedPath );
	}

Z
zz85 已提交
214 215
	divisions = divisions || 12;

216 217 218 219 220 221 222
	var points = [];

	var i, il, item, action, args;
	var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0,
		laste, j,
		t, tx, ty;

223
	for ( i = 0, il = this.actions.length; i < il; i ++ ) {
224 225 226 227 228 229 230 231 232 233

		item = this.actions[ i ];

		action = item.action;
		args = item.args;

		switch( action ) {

		case THREE.PathActions.MOVE_TO:

234
			points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
Z
zz85 已提交
235 236 237

			break;

238 239 240 241
		case THREE.PathActions.LINE_TO:

			points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );

Z
zz85 已提交
242 243
			break;

244 245 246 247 248 249 250 251 252 253 254
		case THREE.PathActions.QUADRATIC_CURVE_TO:

			cpx  = args[ 2 ];
			cpy  = args[ 3 ];

			cpx1 = args[ 0 ];
			cpy1 = args[ 1 ];

			if ( points.length > 0 ) {

				laste = points[ points.length - 1 ];
Z
zz85 已提交
255 256 257

				cpx0 = laste.x;
				cpy0 = laste.y;
258

Z
zz85 已提交
259 260
			} else {

261 262 263 264
				laste = this.actions[ i - 1 ].args;

				cpx0 = laste[ laste.length - 2 ];
				cpy0 = laste[ laste.length - 1 ];
Z
zz85 已提交
265 266 267

			}

268 269 270 271
			for ( j = 1; j <= divisions; j ++ ) {

				t = j / divisions;

272 273
				tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
				ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
274 275 276 277 278

				points.push( new THREE.Vector2( tx, ty ) );

		  	}

Z
zz85 已提交
279 280
			break;

281
		case THREE.PathActions.BEZIER_CURVE_TO:
Z
zz85 已提交
282

283 284
			cpx  = args[ 4 ];
			cpy  = args[ 5 ];
Z
zz85 已提交
285

286 287
			cpx1 = args[ 0 ];
			cpy1 = args[ 1 ];
Z
zz85 已提交
288

289 290
			cpx2 = args[ 2 ];
			cpy2 = args[ 3 ];
Z
zz85 已提交
291

292
			if ( points.length > 0 ) {
Z
zz85 已提交
293

294
				laste = points[ points.length - 1 ];
Z
zz85 已提交
295

296 297
				cpx0 = laste.x;
				cpy0 = laste.y;
Z
zz85 已提交
298

299
			} else {
Z
zz85 已提交
300

301
				laste = this.actions[ i - 1 ].args;
Z
zz85 已提交
302

303 304
				cpx0 = laste[ laste.length - 2 ];
				cpy0 = laste[ laste.length - 1 ];
Z
zz85 已提交
305

306
			}
Z
zz85 已提交
307 308


309
			for ( j = 1; j <= divisions; j ++ ) {
Z
zz85 已提交
310

311
				t = j / divisions;
Z
zz85 已提交
312

313 314
				tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
				ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
Z
zz85 已提交
315

316
				points.push( new THREE.Vector2( tx, ty ) );
Z
zz85 已提交
317

318
			}
Z
zz85 已提交
319

320
			break;
Z
zz85 已提交
321

Z
zz85 已提交
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

Z
zz85 已提交
329
			var n = divisions * args[ 0 ].length;
330

331
			spts = spts.concat( args[ 0 ] );
332

333
			var spline = new THREE.SplineCurve( spts );
334

Z
zz85 已提交
335
			for ( j = 1; j <= n; j ++ ) {
336

Z
zz85 已提交
337
				points.push( spline.getPointAt( j / n ) ) ;
338

339
			}
340

Z
zz85 已提交
341
			break;
Z
zz85 已提交
342

Z
zz85 已提交
343
		case THREE.PathActions.ARC:
Z
zz85 已提交
344

345 346 347 348
			var aX = args[ 0 ], aY = args[ 1 ],
				aRadius = args[ 2 ],
				aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
				aClockwise = !!args[ 5 ];
Z
zz85 已提交
349

Z
zz85 已提交
350 351
			var deltaAngle = aEndAngle - aStartAngle;
			var angle;
Z
zz85 已提交
352
			var tdivisions = divisions * 2;
353

Z
zz85 已提交
354
			for ( j = 1; j <= tdivisions; j ++ ) {
355

356
				t = j / tdivisions;
357

Z
zz85 已提交
358
				if ( ! aClockwise ) {
359

Z
zz85 已提交
360
					t = 1 - t;
361

Z
zz85 已提交
362
				}
363

Z
zz85 已提交
364
				angle = aStartAngle + t * deltaAngle;
365

366 367
				tx = aX + aRadius * Math.cos( angle );
				ty = aY + aRadius * Math.sin( angle );
368

Z
zz85 已提交
369
				//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
370

Z
zz85 已提交
371
				points.push( new THREE.Vector2( tx, ty ) );
372

Z
zz85 已提交
373
			}
374

Z
zz85 已提交
375
			//console.log(points);
Z
zz85 已提交
376 377

		  break;
378 379 380 381 382
		  
		case THREE.PathActions.ELLIPSE:

			var aX = args[ 0 ], aY = args[ 1 ],
				xRadius = args[ 2 ],
383
				yRadius = args[ 3 ],
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
				aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
				aClockwise = !!args[ 6 ];


			var deltaAngle = aEndAngle - aStartAngle;
			var angle;
			var tdivisions = divisions * 2;

			for ( j = 1; j <= tdivisions; j ++ ) {

				t = j / tdivisions;

				if ( ! aClockwise ) {

					t = 1 - t;

				}

				angle = aStartAngle + t * deltaAngle;

				tx = aX + xRadius * Math.cos( angle );
				ty = aY + yRadius * Math.sin( angle );

				//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);

				points.push( new THREE.Vector2( tx, ty ) );

			}

			//console.log(points);

		  break;
Z
zz85 已提交
416 417

		} // end switch
Z
zz85 已提交
418

419 420
	}

421 422 423 424 425 426 427 428


	// Normalize to remove the closing point by default.
	var lastPoint = points[ points.length - 1];
	var EPSILON = 0.0000000001;
	if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON &&
             Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON)
		points.splice( points.length - 1, 1);
A
alteredq 已提交
429 430 431 432 433 434
	if ( closedPath ) {

		points.push( points[ 0 ] );

	}

435
	return points;
Z
zz85 已提交
436

437
};
Z
zz85 已提交
438

439
// Breaks path into shapes
440

C
Cheng Chih-chung 已提交
441
THREE.Path.prototype.toShapes = function( isCCW ) {
442

443 444 445 446 447 448 449 450 451 452
	var i, il, item, action, args;

	var subPaths = [], lastPath = new THREE.Path();

	for ( i = 0, il = this.actions.length; i < il; i ++ ) {

		item = this.actions[ i ];

		args = item.args;
		action = item.action;
453 454 455 456 457 458

		if ( action == THREE.PathActions.MOVE_TO ) {

			if ( lastPath.actions.length != 0 ) {

				subPaths.push( lastPath );
459
				lastPath = new THREE.Path();
460

461
			}
462

463
		}
464 465 466

		lastPath[ action ].apply( lastPath, args );

467
	}
468

C
Cheng Chih-chung 已提交
469
    if ( lastPath.actions.length != 0 ) {
470

C
Cheng Chih-chung 已提交
471
        subPaths.push( lastPath );
472

C
Cheng Chih-chung 已提交
473
    }
474

C
Cheng Chih-chung 已提交
475
    // console.log(subPaths);
476

C
Cheng Chih-chung 已提交
477
    if ( subPaths.length == 0 ) return [];
478

C
Cheng Chih-chung 已提交
479
    var solid, tmpPath, tmpShape, shapes = [];
Z
zz85 已提交
480

C
Cheng Chih-chung 已提交
481 482 483 484 485 486 487 488
    if ( subPaths.length == 1) {
        tmpPath = subPaths[0];
        tmpShape = new THREE.Shape();
        tmpShape.actions = tmpPath.actions;
        tmpShape.curves = tmpPath.curves;
        shapes.push( tmpShape );
        return shapes;
    };
Z
zz85 已提交
489

C
Cheng Chih-chung 已提交
490 491
    var holesFirst = !THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() );
    holesFirst = isCCW ? !holesFirst : holesFirst;
492

C
Cheng Chih-chung 已提交
493
    // console.log("Holes first", holesFirst);
494

C
Cheng Chih-chung 已提交
495
    if ( holesFirst ) {
496

C
Cheng Chih-chung 已提交
497
        tmpShape = new THREE.Shape();
498

C
Cheng Chih-chung 已提交
499
        for ( i = 0, il = subPaths.length; i < il; i ++ ) {
500

C
Cheng Chih-chung 已提交
501 502 503
            tmpPath = subPaths[ i ];
            solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() );
            solid = isCCW ? !solid : solid;
504

C
Cheng Chih-chung 已提交
505
            if ( solid ) {
506

C
Cheng Chih-chung 已提交
507 508
                tmpShape.actions = tmpPath.actions;
                tmpShape.curves = tmpPath.curves;
509

C
Cheng Chih-chung 已提交
510 511
                shapes.push( tmpShape );
                tmpShape = new THREE.Shape();
512

C
Cheng Chih-chung 已提交
513
                //console.log('cw', i);
514

C
Cheng Chih-chung 已提交
515
            } else {
516

C
Cheng Chih-chung 已提交
517
                tmpShape.holes.push( tmpPath );
518

C
Cheng Chih-chung 已提交
519
                //console.log('ccw', i);
520

C
Cheng Chih-chung 已提交
521
            }
522

C
Cheng Chih-chung 已提交
523
        }
524

C
Cheng Chih-chung 已提交
525
    } else {
526

C
Cheng Chih-chung 已提交
527 528
        // Shapes first
        tmpShape = undefined;
529

C
Cheng Chih-chung 已提交
530
        for ( i = 0, il = subPaths.length; i < il; i ++ ) {
531

C
Cheng Chih-chung 已提交
532 533 534
            tmpPath = subPaths[ i ];
            solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() );
            solid = isCCW ? !solid : solid;
535

C
Cheng Chih-chung 已提交
536
            if ( solid ) {
537

C
Cheng Chih-chung 已提交
538
                if ( tmpShape ) shapes.push( tmpShape );
539

C
Cheng Chih-chung 已提交
540 541 542
                tmpShape = new THREE.Shape();
                tmpShape.actions = tmpPath.actions;
                tmpShape.curves = tmpPath.curves;
543

C
Cheng Chih-chung 已提交
544
            } else {
545

C
Cheng Chih-chung 已提交
546
                tmpShape.holes.push( tmpPath );
547

C
Cheng Chih-chung 已提交
548
            }
549

C
Cheng Chih-chung 已提交
550
        }
551

C
Cheng Chih-chung 已提交
552
        shapes.push( tmpShape );
553

C
Cheng Chih-chung 已提交
554
    }
555

C
Cheng Chih-chung 已提交
556
    //console.log("shape", shapes);
557

C
Cheng Chih-chung 已提交
558
    return shapes;
559

Z
zz85 已提交
560
};