Path.js 11.3 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
THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y,
Z
zz85 已提交
95 96
											   aCP2x, aCP2y,
											   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
	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 );
Z
zz85 已提交
144

145 146 147 148 149
 };

 THREE.Path.prototype.absarc = function ( aX, aY, aRadius,
									  aStartAngle, aEndAngle, aClockwise ) {
	this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise);
150
 };
Z
zz85 已提交
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
 };
Z
zz85 已提交
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
									aStartAngle, aEndAngle, aClockwise );
	this.curves.push( curve );

E
Emery 已提交
173
	var lastPoint = curve.getPoint(1);
174 175 176
	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

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

Z
zz85 已提交
277
			}
278

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


	// 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 &&
Z
zz85 已提交
427
			 Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON)
428
		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

Z
zz85 已提交
469
	if ( lastPath.actions.length != 0 ) {
470

Z
zz85 已提交
471
		subPaths.push( lastPath );
472

Z
zz85 已提交
473
	}
474

Z
zz85 已提交
475
	// console.log(subPaths);
476

Z
zz85 已提交
477
	if ( subPaths.length == 0 ) return [];
478

Z
zz85 已提交
479
	var solid, tmpPath, tmpShape, shapes = [];
Z
zz85 已提交
480

Z
zz85 已提交
481
	if ( subPaths.length == 1) {
Z
zz85 已提交
482

Z
zz85 已提交
483 484 485 486 487 488
		tmpPath = subPaths[0];
		tmpShape = new THREE.Shape();
		tmpShape.actions = tmpPath.actions;
		tmpShape.curves = tmpPath.curves;
		shapes.push( tmpShape );
		return shapes;
489

Z
zz85 已提交
490
	}
491

Z
zz85 已提交
492 493
	var holesFirst = !THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() );
	holesFirst = isCCW ? !holesFirst : holesFirst;
494

Z
zz85 已提交
495
	// console.log("Holes first", holesFirst);
496

Z
zz85 已提交
497
	if ( holesFirst ) {
498

Z
zz85 已提交
499
		tmpShape = new THREE.Shape();
500

Z
zz85 已提交
501
		for ( i = 0, il = subPaths.length; i < il; i ++ ) {
502

Z
zz85 已提交
503 504 505
			tmpPath = subPaths[ i ];
			solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() );
			solid = isCCW ? !solid : solid;
506

Z
zz85 已提交
507
			if ( solid ) {
508

Z
zz85 已提交
509 510
				tmpShape.actions = tmpPath.actions;
				tmpShape.curves = tmpPath.curves;
511

Z
zz85 已提交
512 513
				shapes.push( tmpShape );
				tmpShape = new THREE.Shape();
514

Z
zz85 已提交
515
				//console.log('cw', i);
516

Z
zz85 已提交
517
			} else {
518

Z
zz85 已提交
519
				tmpShape.holes.push( tmpPath );
520

Z
zz85 已提交
521
				//console.log('ccw', i);
522

Z
zz85 已提交
523
			}
524

Z
zz85 已提交
525
		}
526

Z
zz85 已提交
527
	} else {
528

Z
zz85 已提交
529 530
		// Shapes first
		tmpShape = undefined;
531

Z
zz85 已提交
532
		for ( i = 0, il = subPaths.length; i < il; i ++ ) {
533

Z
zz85 已提交
534 535 536
			tmpPath = subPaths[ i ];
			solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() );
			solid = isCCW ? !solid : solid;
537

Z
zz85 已提交
538
			if ( solid ) {
539

Z
zz85 已提交
540
				if ( tmpShape ) shapes.push( tmpShape );
541

Z
zz85 已提交
542 543 544
				tmpShape = new THREE.Shape();
				tmpShape.actions = tmpPath.actions;
				tmpShape.curves = tmpPath.curves;
545

Z
zz85 已提交
546
			} else {
547

Z
zz85 已提交
548
				tmpShape.holes.push( tmpPath );
549

Z
zz85 已提交
550
			}
551

Z
zz85 已提交
552 553 554 555 556
		}

		shapes.push( tmpShape );

	}
557

Z
zz85 已提交
558
	//console.log("shape", shapes);
559

Z
zz85 已提交
560
	return shapes;
561

Z
zz85 已提交
562
};