Path.js 14.5 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 ) {

G
gero3 已提交
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.assign( Object.create( THREE.CurvePath.prototype ), {
22

23
	constructor: THREE.Path,
24

25
	// TODO Clean up PATH API
26

27 28
	// Create path using straight lines to connect all points
	// - vectors: array of Vector2
29

30
	fromPoints: function ( vectors ) {
31

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

34
		for ( var i = 1, l = vectors.length; i < l; i ++ ) {
35

36
			this.lineTo( vectors[ i ].x, vectors[ i ].y );
37

38
		}
Z
zz85 已提交
39

40
	},
Z
zz85 已提交
41

42
	moveTo: function ( x, y ) {
43

44
		this.actions.push( { action: 'moveTo', args: [ x, y ] } );
45

46
	},
Z
zz85 已提交
47

48
	lineTo: function ( x, y ) {
49

50
		var lastargs = this.actions[ this.actions.length - 1 ].args;
51

52 53
		var x0 = lastargs[ lastargs.length - 2 ];
		var y0 = lastargs[ lastargs.length - 1 ];
54

55 56
		var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) );
		this.curves.push( curve );
57

58
		this.actions.push( { action: 'lineTo', args: [ x, y ] } );
59

60
	},
Z
zz85 已提交
61

62
	quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) {
63

64
		var lastargs = this.actions[ this.actions.length - 1 ].args;
65

66 67
		var x0 = lastargs[ lastargs.length - 2 ];
		var y0 = lastargs[ lastargs.length - 1 ];
68

69 70 71 72 73
		var curve = new THREE.QuadraticBezierCurve(
			new THREE.Vector2( x0, y0 ),
			new THREE.Vector2( aCPx, aCPy ),
			new THREE.Vector2( aX, aY )
		);
M
Mr.doob 已提交
74

75
		this.curves.push( curve );
76

77
		this.actions.push( { action: 'quadraticCurveTo', args: [ aCPx, aCPy, aX, aY ] } );
78

79
	},
Z
zz85 已提交
80

81
	bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {
82

83
		var lastargs = this.actions[ this.actions.length - 1 ].args;
84

85 86
		var x0 = lastargs[ lastargs.length - 2 ];
		var y0 = lastargs[ lastargs.length - 1 ];
Z
curves  
zz85 已提交
87

88 89 90 91 92 93
		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 )
		);
M
Mr.doob 已提交
94

95
		this.curves.push( curve );
96

97
		this.actions.push( { action: 'bezierCurveTo', args: [ aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ] } );
98

99
	},
Z
zz85 已提交
100

101
	splineThru: function ( pts /*Array of Vector*/ ) {
102

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

105
		var lastargs = this.actions[ this.actions.length - 1 ].args;
106

107 108
		var x0 = lastargs[ lastargs.length - 2 ];
		var y0 = lastargs[ lastargs.length - 1 ];
M
Mr.doob 已提交
109

110 111
		var npts = [ new THREE.Vector2( x0, y0 ) ];
		Array.prototype.push.apply( npts, pts );
112

113 114
		var curve = new THREE.SplineCurve( npts );
		this.curves.push( curve );
115

116 117 118 119
		var lastPoint = pts[ pts.length - 1 ];
		args.push( lastPoint.x );
		args.push( lastPoint.y );

120
		this.actions.push( { action: 'splineThru', args: args } );
121

122
	},
Z
zz85 已提交
123

124
	arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
125

126 127 128
		var lastargs = this.actions[ this.actions.length - 1 ].args;
		var x0 = lastargs[ lastargs.length - 2 ];
		var y0 = lastargs[ lastargs.length - 1 ];
129

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

133
	},
Z
zz85 已提交
134

135
	absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
136

137
		this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
G
gero3 已提交
138

139
	},
G
gero3 已提交
140

141
	ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
Z
zz85 已提交
142

143 144 145
		var lastargs = this.actions[ this.actions.length - 1 ].args;
		var x0 = lastargs[ lastargs.length - 2 ];
		var y0 = lastargs[ lastargs.length - 1 ];
A
alteredq 已提交
146

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

149
	},
150

151
	absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
Z
zz85 已提交
152

153 154 155 156 157 158 159
		var args = [
			aX, aY,
			xRadius, yRadius,
			aStartAngle, aEndAngle,
			aClockwise,
			aRotation || 0 // aRotation is optional.
		];
160

161 162
		var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );
		this.curves.push( curve );
163

164 165 166
		var lastPoint = curve.getPoint( 1 );
		args.push( lastPoint.x );
		args.push( lastPoint.y );
M
Mr.doob 已提交
167

168
		this.actions.push( { action: 'ellipse', args: args } );
169

170
	},
171

172
	getSpacedPoints: function ( divisions ) {
173

174
		if ( ! divisions ) divisions = 40;
175

176
		var points = [];
177

178
		for ( var i = 0; i < divisions; i ++ ) {
179

180
			points.push( this.getPoint( i / divisions ) );
181

182
			//if ( !this.getPoint( i / divisions ) ) throw "DIE";
183

184
		}
185

186
		if ( this.autoClose ) {
187

188
			points.push( points[ 0 ] );
Z
zz85 已提交
189

190
		}
W
WestLangley 已提交
191

192
		return points;
W
WestLangley 已提交
193

194
	},
195

196
	getPoints: function ( divisions ) {
197

198
		divisions = divisions || 12;
Z
zz85 已提交
199

200 201
		var b2 = THREE.ShapeUtils.b2;
		var b3 = THREE.ShapeUtils.b3;
202

203
		var points = [];
204

205 206
		var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0,
			laste, tx, ty;
Z
zz85 已提交
207

208
		for ( var i = 0, l = this.actions.length; i < l; i ++ ) {
M
Mr.doob 已提交
209

210
			var item = this.actions[ i ];
211

212 213
			var action = item.action;
			var args = item.args;
214

215
			switch ( action ) {
216

217
			case 'moveTo':
218

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

221
				break;
222

223
			case 'lineTo':
224

225
				points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
Z
zz85 已提交
226

227
				break;
Z
zz85 已提交
228

229
			case 'quadraticCurveTo':
230

231 232
				cpx  = args[ 2 ];
				cpy  = args[ 3 ];
233

234 235
				cpx1 = args[ 0 ];
				cpy1 = args[ 1 ];
Z
zz85 已提交
236

237
				if ( points.length > 0 ) {
238

239
					laste = points[ points.length - 1 ];
240

241 242
					cpx0 = laste.x;
					cpy0 = laste.y;
243

244
				} else {
245

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

248 249
					cpx0 = laste[ laste.length - 2 ];
					cpy0 = laste[ laste.length - 1 ];
250

251
				}
Z
zz85 已提交
252

253
				for ( var j = 1; j <= divisions; j ++ ) {
254

255
					var t = j / divisions;
Z
zz85 已提交
256

257 258
					tx = b2( t, cpx0, cpx1, cpx );
					ty = b2( t, cpy0, cpy1, cpy );
Z
zz85 已提交
259

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

262
				}
263

264
				break;
265

266
			case 'bezierCurveTo':
267

268 269
				cpx  = args[ 4 ];
				cpy  = args[ 5 ];
270

271 272
				cpx1 = args[ 0 ];
				cpy1 = args[ 1 ];
Z
zz85 已提交
273

274 275
				cpx2 = args[ 2 ];
				cpy2 = args[ 3 ];
Z
zz85 已提交
276

277
				if ( points.length > 0 ) {
Z
zz85 已提交
278

279
					laste = points[ points.length - 1 ];
Z
zz85 已提交
280

281 282
					cpx0 = laste.x;
					cpy0 = laste.y;
Z
zz85 已提交
283

284
				} else {
Z
zz85 已提交
285

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

288 289
					cpx0 = laste[ laste.length - 2 ];
					cpy0 = laste[ laste.length - 1 ];
Z
zz85 已提交
290

291
				}
Z
zz85 已提交
292 293


294
				for ( var j = 1; j <= divisions; j ++ ) {
Z
zz85 已提交
295

296
					var t = j / divisions;
Z
zz85 已提交
297

298 299
					tx = b3( t, cpx0, cpx1, cpx2, cpx );
					ty = b3( t, cpy0, cpy1, cpy2, cpy );
Z
zz85 已提交
300

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

303
				}
Z
zz85 已提交
304

305
				break;
Z
zz85 已提交
306

307
			case 'splineThru':
Z
zz85 已提交
308

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

311 312
				var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] );
				var spts = [ last ];
Z
zz85 已提交
313

314
				var n = divisions * args[ 0 ].length;
315

316
				spts = spts.concat( args[ 0 ] );
317

318
				var spline = new THREE.SplineCurve( spts );
319

320
				for ( var j = 1; j <= n; j ++ ) {
321

322
					points.push( spline.getPointAt( j / n ) );
323

324
				}
325

326
				break;
327

328
			case 'arc':
329

330 331 332 333
				var aX = args[ 0 ], aY = args[ 1 ],
					aRadius = args[ 2 ],
					aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
					aClockwise = !! args[ 5 ];
334

335 336 337
				var deltaAngle = aEndAngle - aStartAngle;
				var angle;
				var tdivisions = divisions * 2;
Z
zz85 已提交
338

339
				for ( var j = 1; j <= tdivisions; j ++ ) {
Z
zz85 已提交
340

341
					var t = j / tdivisions;
Z
zz85 已提交
342

343
					if ( ! aClockwise ) {
344

345
						t = 1 - t;
346

347
					}
348

349
					angle = aStartAngle + t * deltaAngle;
350

351 352
					tx = aX + aRadius * Math.cos( angle );
					ty = aY + aRadius * Math.sin( angle );
353

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

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

358
				}
359

360
				//console.log(points);
361

362
				break;
363

364
			case 'ellipse':
365

366 367 368 369 370 371
				var aX = args[ 0 ], aY = args[ 1 ],
					xRadius = args[ 2 ],
					yRadius = args[ 3 ],
					aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
					aClockwise = !! args[ 6 ],
					aRotation = args[ 7 ];
Z
zz85 已提交
372

F
Fabian Lange 已提交
373

374 375 376
				var deltaAngle = aEndAngle - aStartAngle;
				var angle;
				var tdivisions = divisions * 2;
377

378 379
				var cos, sin;
				if ( aRotation !== 0 ) {
380

381 382
					cos = Math.cos( aRotation );
					sin = Math.sin( aRotation );
383

384
				}
385

386
				for ( var j = 1; j <= tdivisions; j ++ ) {
M
Mr.doob 已提交
387

388
					var t = j / tdivisions;
389

390
					if ( ! aClockwise ) {
391

392
						t = 1 - t;
393

394
					}
395

396
					angle = aStartAngle + t * deltaAngle;
397

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

401
					if ( aRotation !== 0 ) {
402

403
						var x = tx, y = ty;
404

405 406 407
						// Rotate the point about the center of the ellipse.
						tx = ( x - aX ) * cos - ( y - aY ) * sin + aX;
						ty = ( x - aX ) * sin + ( y - aY ) * cos + aY;
408

409
					}
410

411
					//console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
N
neko 已提交
412

413
					points.push( new THREE.Vector2( tx, ty ) );
414 415 416

				}

417
				//console.log(points);
418

419
				break;
Z
zz85 已提交
420

421
			} // end switch
Z
zz85 已提交
422

423
		}
424

425 426


427 428 429 430 431
		// Normalize to remove the closing point by default.
		var lastPoint = points[ points.length - 1 ];
		if ( Math.abs( lastPoint.x - points[ 0 ].x ) < Number.EPSILON &&
				 Math.abs( lastPoint.y - points[ 0 ].y ) < Number.EPSILON )
			points.splice( points.length - 1, 1 );
W
WestLangley 已提交
432

433
		if ( this.autoClose ) {
A
alteredq 已提交
434

435
			points.push( points[ 0 ] );
A
alteredq 已提交
436

437
		}
A
alteredq 已提交
438

439
		return points;
Z
zz85 已提交
440

441
	},
Z
zz85 已提交
442

443
	toShapes: function ( isCCW, noHoles ) {
444

445
		function extractSubpaths( inActions ) {
446

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

449
			for ( var i = 0, l = inActions.length; i < l; i ++ ) {
450

451
				var item = inActions[ i ];
452

453 454
				var args = item.args;
				var action = item.action;
455

456
				if ( action === 'moveTo' ) {
457

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

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

463
					}
464 465 466

				}

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

469 470
			}

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

473
				subPaths.push( lastPath );
474

475 476 477
			}

			// console.log(subPaths);
478

479
			return	subPaths;
480 481 482

		}

483
		function toShapesNoHoles( inSubpaths ) {
484

485
			var shapes = [];
G
gero3 已提交
486

487
			for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) {
488

489
				var tmpPath = inSubpaths[ i ];
490

491 492 493
				var tmpShape = new THREE.Shape();
				tmpShape.actions = tmpPath.actions;
				tmpShape.curves = tmpPath.curves;
494

495
				shapes.push( tmpShape );
496

497
			}
498

499
			//console.log("shape", shapes);
500

501
			return shapes;
G
gero3 已提交
502

503 504
		}

505
		function isPointInsidePolygon( inPt, inPolygon ) {
506

507
			var polyLen = inPolygon.length;
G
gero3 已提交
508

509 510 511 512 513 514
			// inPt on polygon contour => immediate success    or
			// toggling of inside/outside at every single! intersection point of an edge
			//  with the horizontal line through inPt, left of inPt
			//  not counting lowerY endpoints of edges and whole edges on that line
			var inside = false;
			for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {
515

516 517
				var edgeLowPt  = inPolygon[ p ];
				var edgeHighPt = inPolygon[ q ];
G
gero3 已提交
518

519 520
				var edgeDx = edgeHighPt.x - edgeLowPt.x;
				var edgeDy = edgeHighPt.y - edgeLowPt.y;
521

522
				if ( Math.abs( edgeDy ) > Number.EPSILON ) {
G
gero3 已提交
523

524 525
					// not parallel
					if ( edgeDy < 0 ) {
526

527 528
						edgeLowPt  = inPolygon[ q ]; edgeDx = - edgeDx;
						edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;
529

530 531
					}
					if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) 		continue;
G
gero3 已提交
532

533
					if ( inPt.y === edgeLowPt.y ) {
G
gero3 已提交
534

535 536
						if ( inPt.x === edgeLowPt.x )		return	true;		// inPt is on contour ?
						// continue;				// no intersection or edgeLowPt => doesn't count !!!
G
gero3 已提交
537

538
					} else {
539

540 541 542 543
						var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y );
						if ( perpEdge === 0 )				return	true;		// inPt is on contour ?
						if ( perpEdge < 0 ) 				continue;
						inside = ! inside;		// true intersection left of inPt
G
gero3 已提交
544

545
					}
G
gero3 已提交
546

547
				} else {
G
gero3 已提交
548

549 550 551 552 553 554
					// parallel or collinear
					if ( inPt.y !== edgeLowPt.y ) 		continue;			// parallel
					// edge lies on the same horizontal line as inPt
					if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||
						 ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) )		return	true;	// inPt: Point on contour !
					// continue;
G
gero3 已提交
555

556
				}
G
gero3 已提交
557

558
			}
G
gero3 已提交
559

560 561
			return	inside;

562 563
		}

564
		var isClockWise = THREE.ShapeUtils.isClockWise;
G
gero3 已提交
565

566 567
		var subPaths = extractSubpaths( this.actions );
		if ( subPaths.length === 0 ) return [];
568

569
		if ( noHoles === true )	return	toShapesNoHoles( subPaths );
570 571


572
		var solid, tmpPath, tmpShape, shapes = [];
573

574
		if ( subPaths.length === 1 ) {
575

576 577 578 579 580 581
			tmpPath = subPaths[ 0 ];
			tmpShape = new THREE.Shape();
			tmpShape.actions = tmpPath.actions;
			tmpShape.curves = tmpPath.curves;
			shapes.push( tmpShape );
			return shapes;
Z
zz85 已提交
582

583
		}
584

585 586
		var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() );
		holesFirst = isCCW ? ! holesFirst : holesFirst;
587

588
		// console.log("Holes first", holesFirst);
589

590 591 592 593 594
		var betterShapeHoles = [];
		var newShapes = [];
		var newShapeHoles = [];
		var mainIdx = 0;
		var tmpPoints;
F
Fabian Lange 已提交
595

596 597
		newShapes[ mainIdx ] = undefined;
		newShapeHoles[ mainIdx ] = [];
598

599
		for ( var i = 0, l = subPaths.length; i < l; i ++ ) {
600

601 602 603 604
			tmpPath = subPaths[ i ];
			tmpPoints = tmpPath.getPoints();
			solid = isClockWise( tmpPoints );
			solid = isCCW ? ! solid : solid;
605

606
			if ( solid ) {
607

608
				if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) )	mainIdx ++;
609

610 611 612
				newShapes[ mainIdx ] = { s: new THREE.Shape(), p: tmpPoints };
				newShapes[ mainIdx ].s.actions = tmpPath.actions;
				newShapes[ mainIdx ].s.curves = tmpPath.curves;
613

614 615
				if ( holesFirst )	mainIdx ++;
				newShapeHoles[ mainIdx ] = [];
F
Fabian Lange 已提交
616

617
				//console.log('cw', i);
618

619
			} else {
620

621
				newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } );
622

623
				//console.log('ccw', i);
624

625
			}
626

Z
zz85 已提交
627
		}
628

629 630
		// only Holes? -> probably all Shapes with wrong orientation
		if ( ! newShapes[ 0 ] )	return	toShapesNoHoles( subPaths );
631

632

633
		if ( newShapes.length > 1 ) {
634

635 636
			var ambiguous = false;
			var toChange = [];
G
gero3 已提交
637

638
			for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
639

640
				betterShapeHoles[ sIdx ] = [];
G
gero3 已提交
641

642
			}
G
gero3 已提交
643

644
			for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
M
Mr.doob 已提交
645

646
				var sho = newShapeHoles[ sIdx ];
G
gero3 已提交
647

648
				for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) {
M
Mr.doob 已提交
649

650 651
					var ho = sho[ hIdx ];
					var hole_unassigned = true;
G
gero3 已提交
652

653
					for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {
M
Mr.doob 已提交
654

655
						if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) {
G
gero3 已提交
656

657 658
							if ( sIdx !== s2Idx )	toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );
							if ( hole_unassigned ) {
G
gero3 已提交
659

660 661
								hole_unassigned = false;
								betterShapeHoles[ s2Idx ].push( ho );
G
gero3 已提交
662

663
							} else {
G
gero3 已提交
664

665
								ambiguous = true;
G
gero3 已提交
666

667
							}
G
gero3 已提交
668

669
						}
G
gero3 已提交
670

671
					}
672
					if ( hole_unassigned ) {
G
gero3 已提交
673

674
						betterShapeHoles[ sIdx ].push( ho );
G
gero3 已提交
675

676
					}
G
gero3 已提交
677 678 679

				}

Z
zz85 已提交
680
			}
681 682
			// console.log("ambiguous: ", ambiguous);
			if ( toChange.length > 0 ) {
G
gero3 已提交
683

684 685
				// console.log("to change: ", toChange);
				if ( ! ambiguous )	newShapeHoles = betterShapeHoles;
G
gero3 已提交
686

687
			}
G
gero3 已提交
688

689
		}
G
gero3 已提交
690

691
		var tmpHoles;
Z
zz85 已提交
692

693
		for ( var i = 0, il = newShapes.length; i < il; i ++ ) {
M
Mr.doob 已提交
694

695 696 697
			tmpShape = newShapes[ i ].s;
			shapes.push( tmpShape );
			tmpHoles = newShapeHoles[ i ];
G
gero3 已提交
698

699
			for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) {
M
Mr.doob 已提交
700

701
				tmpShape.holes.push( tmpHoles[ j ].h );
G
gero3 已提交
702

703
			}
G
gero3 已提交
704

705
		}
G
gero3 已提交
706

707
		//console.log("shape", shapes);
708

709
		return shapes;
710

711
	}
712

713
} );