initial commit
This commit is contained in:
425
lib/extras/core/Curve.js
Normal file
425
lib/extras/core/Curve.js
Normal file
@ -0,0 +1,425 @@
|
||||
import { _Math } from '../../math/Math.js';
|
||||
import { Vector3 } from '../../math/Vector3.js';
|
||||
import { Matrix4 } from '../../math/Matrix4.js';
|
||||
|
||||
/**
|
||||
* @author zz85 / http://www.lab4games.net/zz85/blog
|
||||
* Extensible curve object
|
||||
*
|
||||
* Some common of curve methods:
|
||||
* .getPoint( t, optionalTarget ), .getTangent( t )
|
||||
* .getPointAt( u, optionalTarget ), .getTangentAt( u )
|
||||
* .getPoints(), .getSpacedPoints()
|
||||
* .getLength()
|
||||
* .updateArcLengths()
|
||||
*
|
||||
* This following curves inherit from THREE.Curve:
|
||||
*
|
||||
* -- 2D curves --
|
||||
* THREE.ArcCurve
|
||||
* THREE.CubicBezierCurve
|
||||
* THREE.EllipseCurve
|
||||
* THREE.LineCurve
|
||||
* THREE.QuadraticBezierCurve
|
||||
* THREE.SplineCurve
|
||||
*
|
||||
* -- 3D curves --
|
||||
* THREE.CatmullRomCurve3
|
||||
* THREE.CubicBezierCurve3
|
||||
* THREE.LineCurve3
|
||||
* THREE.QuadraticBezierCurve3
|
||||
*
|
||||
* A series of curves can be represented as a THREE.CurvePath.
|
||||
*
|
||||
**/
|
||||
|
||||
/**************************************************************
|
||||
* Abstract Curve base class
|
||||
**************************************************************/
|
||||
|
||||
function Curve() {
|
||||
|
||||
this.type = 'Curve';
|
||||
|
||||
this.arcLengthDivisions = 200;
|
||||
|
||||
}
|
||||
|
||||
Object.assign( Curve.prototype, {
|
||||
|
||||
// Virtual base class method to overwrite and implement in subclasses
|
||||
// - t [0 .. 1]
|
||||
|
||||
getPoint: function ( /* t, optionalTarget */ ) {
|
||||
|
||||
console.warn( 'THREE.Curve: .getPoint() not implemented.' );
|
||||
return null;
|
||||
|
||||
},
|
||||
|
||||
// Get point at relative position in curve according to arc length
|
||||
// - u [0 .. 1]
|
||||
|
||||
getPointAt: function ( u, optionalTarget ) {
|
||||
|
||||
var t = this.getUtoTmapping( u );
|
||||
return this.getPoint( t, optionalTarget );
|
||||
|
||||
},
|
||||
|
||||
// Get sequence of points using getPoint( t )
|
||||
|
||||
getPoints: function ( divisions ) {
|
||||
|
||||
if ( divisions === undefined ) divisions = 5;
|
||||
|
||||
var points = [];
|
||||
|
||||
for ( var d = 0; d <= divisions; d ++ ) {
|
||||
|
||||
points.push( this.getPoint( d / divisions ) );
|
||||
|
||||
}
|
||||
|
||||
return points;
|
||||
|
||||
},
|
||||
|
||||
// Get sequence of points using getPointAt( u )
|
||||
|
||||
getSpacedPoints: function ( divisions ) {
|
||||
|
||||
if ( divisions === undefined ) divisions = 5;
|
||||
|
||||
var points = [];
|
||||
|
||||
for ( var d = 0; d <= divisions; d ++ ) {
|
||||
|
||||
points.push( this.getPointAt( d / divisions ) );
|
||||
|
||||
}
|
||||
|
||||
return points;
|
||||
|
||||
},
|
||||
|
||||
// Get total curve arc length
|
||||
|
||||
getLength: function () {
|
||||
|
||||
var lengths = this.getLengths();
|
||||
return lengths[ lengths.length - 1 ];
|
||||
|
||||
},
|
||||
|
||||
// Get list of cumulative segment lengths
|
||||
|
||||
getLengths: function ( divisions ) {
|
||||
|
||||
if ( divisions === undefined ) divisions = this.arcLengthDivisions;
|
||||
|
||||
if ( this.cacheArcLengths &&
|
||||
( this.cacheArcLengths.length === divisions + 1 ) &&
|
||||
! this.needsUpdate ) {
|
||||
|
||||
return this.cacheArcLengths;
|
||||
|
||||
}
|
||||
|
||||
this.needsUpdate = false;
|
||||
|
||||
var cache = [];
|
||||
var current, last = this.getPoint( 0 );
|
||||
var p, sum = 0;
|
||||
|
||||
cache.push( 0 );
|
||||
|
||||
for ( p = 1; p <= divisions; p ++ ) {
|
||||
|
||||
current = this.getPoint( p / divisions );
|
||||
sum += current.distanceTo( last );
|
||||
cache.push( sum );
|
||||
last = current;
|
||||
|
||||
}
|
||||
|
||||
this.cacheArcLengths = cache;
|
||||
|
||||
return cache; // { sums: cache, sum: sum }; Sum is in the last element.
|
||||
|
||||
},
|
||||
|
||||
updateArcLengths: function () {
|
||||
|
||||
this.needsUpdate = true;
|
||||
this.getLengths();
|
||||
|
||||
},
|
||||
|
||||
// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant
|
||||
|
||||
getUtoTmapping: function ( u, distance ) {
|
||||
|
||||
var arcLengths = this.getLengths();
|
||||
|
||||
var i = 0, il = arcLengths.length;
|
||||
|
||||
var targetArcLength; // The targeted u distance value to get
|
||||
|
||||
if ( distance ) {
|
||||
|
||||
targetArcLength = distance;
|
||||
|
||||
} else {
|
||||
|
||||
targetArcLength = u * arcLengths[ il - 1 ];
|
||||
|
||||
}
|
||||
|
||||
// binary search for the index with largest value smaller than target u distance
|
||||
|
||||
var low = 0, high = il - 1, comparison;
|
||||
|
||||
while ( low <= high ) {
|
||||
|
||||
i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
|
||||
|
||||
comparison = arcLengths[ i ] - targetArcLength;
|
||||
|
||||
if ( comparison < 0 ) {
|
||||
|
||||
low = i + 1;
|
||||
|
||||
} else if ( comparison > 0 ) {
|
||||
|
||||
high = i - 1;
|
||||
|
||||
} else {
|
||||
|
||||
high = i;
|
||||
break;
|
||||
|
||||
// DONE
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
i = high;
|
||||
|
||||
if ( arcLengths[ i ] === targetArcLength ) {
|
||||
|
||||
return i / ( il - 1 );
|
||||
|
||||
}
|
||||
|
||||
// we could get finer grain at lengths, or use simple interpolation between two points
|
||||
|
||||
var lengthBefore = arcLengths[ i ];
|
||||
var lengthAfter = arcLengths[ i + 1 ];
|
||||
|
||||
var segmentLength = lengthAfter - lengthBefore;
|
||||
|
||||
// determine where we are between the 'before' and 'after' points
|
||||
|
||||
var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
|
||||
|
||||
// add that fractional amount to t
|
||||
|
||||
var t = ( i + segmentFraction ) / ( il - 1 );
|
||||
|
||||
return t;
|
||||
|
||||
},
|
||||
|
||||
// Returns a unit vector tangent at t
|
||||
// In case any sub curve does not implement its tangent derivation,
|
||||
// 2 points a small delta apart will be used to find its gradient
|
||||
// which seems to give a reasonable approximation
|
||||
|
||||
getTangent: function ( t ) {
|
||||
|
||||
var delta = 0.0001;
|
||||
var t1 = t - delta;
|
||||
var t2 = t + delta;
|
||||
|
||||
// Capping in case of danger
|
||||
|
||||
if ( t1 < 0 ) t1 = 0;
|
||||
if ( t2 > 1 ) t2 = 1;
|
||||
|
||||
var pt1 = this.getPoint( t1 );
|
||||
var pt2 = this.getPoint( t2 );
|
||||
|
||||
var vec = pt2.clone().sub( pt1 );
|
||||
return vec.normalize();
|
||||
|
||||
},
|
||||
|
||||
getTangentAt: function ( u ) {
|
||||
|
||||
var t = this.getUtoTmapping( u );
|
||||
return this.getTangent( t );
|
||||
|
||||
},
|
||||
|
||||
computeFrenetFrames: function ( segments, closed ) {
|
||||
|
||||
// see http://www.cs.indiana.edu/pub/techreports/TR425.pdf
|
||||
|
||||
var normal = new Vector3();
|
||||
|
||||
var tangents = [];
|
||||
var normals = [];
|
||||
var binormals = [];
|
||||
|
||||
var vec = new Vector3();
|
||||
var mat = new Matrix4();
|
||||
|
||||
var i, u, theta;
|
||||
|
||||
// compute the tangent vectors for each segment on the curve
|
||||
|
||||
for ( i = 0; i <= segments; i ++ ) {
|
||||
|
||||
u = i / segments;
|
||||
|
||||
tangents[ i ] = this.getTangentAt( u );
|
||||
tangents[ i ].normalize();
|
||||
|
||||
}
|
||||
|
||||
// select an initial normal vector perpendicular to the first tangent vector,
|
||||
// and in the direction of the minimum tangent xyz component
|
||||
|
||||
normals[ 0 ] = new Vector3();
|
||||
binormals[ 0 ] = new Vector3();
|
||||
var min = Number.MAX_VALUE;
|
||||
var tx = Math.abs( tangents[ 0 ].x );
|
||||
var ty = Math.abs( tangents[ 0 ].y );
|
||||
var tz = Math.abs( tangents[ 0 ].z );
|
||||
|
||||
if ( tx <= min ) {
|
||||
|
||||
min = tx;
|
||||
normal.set( 1, 0, 0 );
|
||||
|
||||
}
|
||||
|
||||
if ( ty <= min ) {
|
||||
|
||||
min = ty;
|
||||
normal.set( 0, 1, 0 );
|
||||
|
||||
}
|
||||
|
||||
if ( tz <= min ) {
|
||||
|
||||
normal.set( 0, 0, 1 );
|
||||
|
||||
}
|
||||
|
||||
vec.crossVectors( tangents[ 0 ], normal ).normalize();
|
||||
|
||||
normals[ 0 ].crossVectors( tangents[ 0 ], vec );
|
||||
binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );
|
||||
|
||||
|
||||
// compute the slowly-varying normal and binormal vectors for each segment on the curve
|
||||
|
||||
for ( i = 1; i <= segments; i ++ ) {
|
||||
|
||||
normals[ i ] = normals[ i - 1 ].clone();
|
||||
|
||||
binormals[ i ] = binormals[ i - 1 ].clone();
|
||||
|
||||
vec.crossVectors( tangents[ i - 1 ], tangents[ i ] );
|
||||
|
||||
if ( vec.length() > Number.EPSILON ) {
|
||||
|
||||
vec.normalize();
|
||||
|
||||
theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors
|
||||
|
||||
normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
|
||||
|
||||
}
|
||||
|
||||
binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
|
||||
|
||||
}
|
||||
|
||||
// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
|
||||
|
||||
if ( closed === true ) {
|
||||
|
||||
theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) );
|
||||
theta /= segments;
|
||||
|
||||
if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) {
|
||||
|
||||
theta = - theta;
|
||||
|
||||
}
|
||||
|
||||
for ( i = 1; i <= segments; i ++ ) {
|
||||
|
||||
// twist a little...
|
||||
normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
|
||||
binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
tangents: tangents,
|
||||
normals: normals,
|
||||
binormals: binormals
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
clone: function () {
|
||||
|
||||
return new this.constructor().copy( this );
|
||||
|
||||
},
|
||||
|
||||
copy: function ( source ) {
|
||||
|
||||
this.arcLengthDivisions = source.arcLengthDivisions;
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
|
||||
var data = {
|
||||
metadata: {
|
||||
version: 4.5,
|
||||
type: 'Curve',
|
||||
generator: 'Curve.toJSON'
|
||||
}
|
||||
};
|
||||
|
||||
data.arcLengthDivisions = this.arcLengthDivisions;
|
||||
data.type = this.type;
|
||||
|
||||
return data;
|
||||
|
||||
},
|
||||
|
||||
fromJSON: function ( json ) {
|
||||
|
||||
this.arcLengthDivisions = json.arcLengthDivisions;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
|
||||
export { Curve };
|
261
lib/extras/core/CurvePath.js
Normal file
261
lib/extras/core/CurvePath.js
Normal file
@ -0,0 +1,261 @@
|
||||
import { Curve } from './Curve.js';
|
||||
import * as Curves from '../curves/Curves.js';
|
||||
|
||||
/**
|
||||
* @author zz85 / http://www.lab4games.net/zz85/blog
|
||||
*
|
||||
**/
|
||||
|
||||
/**************************************************************
|
||||
* Curved Path - a curve path is simply a array of connected
|
||||
* curves, but retains the api of a curve
|
||||
**************************************************************/
|
||||
|
||||
function CurvePath() {
|
||||
|
||||
Curve.call( this );
|
||||
|
||||
this.type = 'CurvePath';
|
||||
|
||||
this.curves = [];
|
||||
this.autoClose = false; // Automatically closes the path
|
||||
|
||||
}
|
||||
|
||||
CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), {
|
||||
|
||||
constructor: CurvePath,
|
||||
|
||||
add: function ( curve ) {
|
||||
|
||||
this.curves.push( curve );
|
||||
|
||||
},
|
||||
|
||||
closePath: function () {
|
||||
|
||||
// Add a line curve if start and end of lines are not connected
|
||||
var startPoint = this.curves[ 0 ].getPoint( 0 );
|
||||
var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 );
|
||||
|
||||
if ( ! startPoint.equals( endPoint ) ) {
|
||||
|
||||
this.curves.push( new Curves[ 'LineCurve' ]( endPoint, startPoint ) );
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
// To get accurate point with reference to
|
||||
// entire path distance at time t,
|
||||
// following has to be done:
|
||||
|
||||
// 1. Length of each sub path have to be known
|
||||
// 2. Locate and identify type of curve
|
||||
// 3. Get t for the curve
|
||||
// 4. Return curve.getPointAt(t')
|
||||
|
||||
getPoint: function ( t ) {
|
||||
|
||||
var d = t * this.getLength();
|
||||
var curveLengths = this.getCurveLengths();
|
||||
var i = 0;
|
||||
|
||||
// To think about boundaries points.
|
||||
|
||||
while ( i < curveLengths.length ) {
|
||||
|
||||
if ( curveLengths[ i ] >= d ) {
|
||||
|
||||
var diff = curveLengths[ i ] - d;
|
||||
var curve = this.curves[ i ];
|
||||
|
||||
var segmentLength = curve.getLength();
|
||||
var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength;
|
||||
|
||||
return curve.getPointAt( u );
|
||||
|
||||
}
|
||||
|
||||
i ++;
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
// loop where sum != 0, sum > d , sum+1 <d
|
||||
|
||||
},
|
||||
|
||||
// We cannot use the default THREE.Curve getPoint() with getLength() because in
|
||||
// THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath
|
||||
// getPoint() depends on getLength
|
||||
|
||||
getLength: function () {
|
||||
|
||||
var lens = this.getCurveLengths();
|
||||
return lens[ lens.length - 1 ];
|
||||
|
||||
},
|
||||
|
||||
// cacheLengths must be recalculated.
|
||||
updateArcLengths: function () {
|
||||
|
||||
this.needsUpdate = true;
|
||||
this.cacheLengths = null;
|
||||
this.getCurveLengths();
|
||||
|
||||
},
|
||||
|
||||
// Compute lengths and cache them
|
||||
// We cannot overwrite getLengths() because UtoT mapping uses it.
|
||||
|
||||
getCurveLengths: function () {
|
||||
|
||||
// We use cache values if curves and cache array are same length
|
||||
|
||||
if ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) {
|
||||
|
||||
return this.cacheLengths;
|
||||
|
||||
}
|
||||
|
||||
// Get length of sub-curve
|
||||
// Push sums into cached array
|
||||
|
||||
var lengths = [], sums = 0;
|
||||
|
||||
for ( var i = 0, l = this.curves.length; i < l; i ++ ) {
|
||||
|
||||
sums += this.curves[ i ].getLength();
|
||||
lengths.push( sums );
|
||||
|
||||
}
|
||||
|
||||
this.cacheLengths = lengths;
|
||||
|
||||
return lengths;
|
||||
|
||||
},
|
||||
|
||||
getSpacedPoints: function ( divisions ) {
|
||||
|
||||
if ( divisions === undefined ) divisions = 40;
|
||||
|
||||
var points = [];
|
||||
|
||||
for ( var i = 0; i <= divisions; i ++ ) {
|
||||
|
||||
points.push( this.getPoint( i / divisions ) );
|
||||
|
||||
}
|
||||
|
||||
if ( this.autoClose ) {
|
||||
|
||||
points.push( points[ 0 ] );
|
||||
|
||||
}
|
||||
|
||||
return points;
|
||||
|
||||
},
|
||||
|
||||
getPoints: function ( divisions ) {
|
||||
|
||||
divisions = divisions || 12;
|
||||
|
||||
var points = [], last;
|
||||
|
||||
for ( var i = 0, curves = this.curves; i < curves.length; i ++ ) {
|
||||
|
||||
var curve = curves[ i ];
|
||||
var resolution = ( curve && curve.isEllipseCurve ) ? divisions * 2
|
||||
: ( curve && ( curve.isLineCurve || curve.isLineCurve3 ) ) ? 1
|
||||
: ( curve && curve.isSplineCurve ) ? divisions * curve.points.length
|
||||
: divisions;
|
||||
|
||||
var pts = curve.getPoints( resolution );
|
||||
|
||||
for ( var j = 0; j < pts.length; j ++ ) {
|
||||
|
||||
var point = pts[ j ];
|
||||
|
||||
if ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates
|
||||
|
||||
points.push( point );
|
||||
last = point;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( this.autoClose && points.length > 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) {
|
||||
|
||||
points.push( points[ 0 ] );
|
||||
|
||||
}
|
||||
|
||||
return points;
|
||||
|
||||
},
|
||||
|
||||
copy: function ( source ) {
|
||||
|
||||
Curve.prototype.copy.call( this, source );
|
||||
|
||||
this.curves = [];
|
||||
|
||||
for ( var i = 0, l = source.curves.length; i < l; i ++ ) {
|
||||
|
||||
var curve = source.curves[ i ];
|
||||
|
||||
this.curves.push( curve.clone() );
|
||||
|
||||
}
|
||||
|
||||
this.autoClose = source.autoClose;
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
|
||||
var data = Curve.prototype.toJSON.call( this );
|
||||
|
||||
data.autoClose = this.autoClose;
|
||||
data.curves = [];
|
||||
|
||||
for ( var i = 0, l = this.curves.length; i < l; i ++ ) {
|
||||
|
||||
var curve = this.curves[ i ];
|
||||
data.curves.push( curve.toJSON() );
|
||||
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
},
|
||||
|
||||
fromJSON: function ( json ) {
|
||||
|
||||
Curve.prototype.fromJSON.call( this, json );
|
||||
|
||||
this.autoClose = json.autoClose;
|
||||
this.curves = [];
|
||||
|
||||
for ( var i = 0, l = json.curves.length; i < l; i ++ ) {
|
||||
|
||||
var curve = json.curves[ i ];
|
||||
this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) );
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
|
||||
export { CurvePath };
|
145
lib/extras/core/Font.js
Normal file
145
lib/extras/core/Font.js
Normal file
@ -0,0 +1,145 @@
|
||||
/**
|
||||
* @author zz85 / http://www.lab4games.net/zz85/blog
|
||||
* @author mrdoob / http://mrdoob.com/
|
||||
*/
|
||||
|
||||
import { ShapePath } from './ShapePath.js';
|
||||
|
||||
|
||||
function Font( data ) {
|
||||
|
||||
this.type = 'Font';
|
||||
|
||||
this.data = data;
|
||||
|
||||
}
|
||||
|
||||
Object.assign( Font.prototype, {
|
||||
|
||||
isFont: true,
|
||||
|
||||
generateShapes: function ( text, size ) {
|
||||
|
||||
if ( size === undefined ) size = 100;
|
||||
|
||||
var shapes = [];
|
||||
var paths = createPaths( text, size, this.data );
|
||||
|
||||
for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
|
||||
|
||||
Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
|
||||
|
||||
}
|
||||
|
||||
return shapes;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
function createPaths( text, size, data ) {
|
||||
|
||||
var chars = Array.from ? Array.from( text ) : String( text ).split( '' ); // see #13988
|
||||
var scale = size / data.resolution;
|
||||
var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
|
||||
|
||||
var paths = [];
|
||||
|
||||
var offsetX = 0, offsetY = 0;
|
||||
|
||||
for ( var i = 0; i < chars.length; i ++ ) {
|
||||
|
||||
var char = chars[ i ];
|
||||
|
||||
if ( char === '\n' ) {
|
||||
|
||||
offsetX = 0;
|
||||
offsetY -= line_height;
|
||||
|
||||
} else {
|
||||
|
||||
var ret = createPath( char, scale, offsetX, offsetY, data );
|
||||
offsetX += ret.offsetX;
|
||||
paths.push( ret.path );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return paths;
|
||||
|
||||
}
|
||||
|
||||
function createPath( char, scale, offsetX, offsetY, data ) {
|
||||
|
||||
var glyph = data.glyphs[ char ] || data.glyphs[ '?' ];
|
||||
|
||||
if ( ! glyph ) return;
|
||||
|
||||
var path = new ShapePath();
|
||||
|
||||
var x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2;
|
||||
|
||||
if ( glyph.o ) {
|
||||
|
||||
var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
|
||||
|
||||
for ( var i = 0, l = outline.length; i < l; ) {
|
||||
|
||||
var action = outline[ i ++ ];
|
||||
|
||||
switch ( action ) {
|
||||
|
||||
case 'm': // moveTo
|
||||
|
||||
x = outline[ i ++ ] * scale + offsetX;
|
||||
y = outline[ i ++ ] * scale + offsetY;
|
||||
|
||||
path.moveTo( x, y );
|
||||
|
||||
break;
|
||||
|
||||
case 'l': // lineTo
|
||||
|
||||
x = outline[ i ++ ] * scale + offsetX;
|
||||
y = outline[ i ++ ] * scale + offsetY;
|
||||
|
||||
path.lineTo( x, y );
|
||||
|
||||
break;
|
||||
|
||||
case 'q': // quadraticCurveTo
|
||||
|
||||
cpx = outline[ i ++ ] * scale + offsetX;
|
||||
cpy = outline[ i ++ ] * scale + offsetY;
|
||||
cpx1 = outline[ i ++ ] * scale + offsetX;
|
||||
cpy1 = outline[ i ++ ] * scale + offsetY;
|
||||
|
||||
path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
|
||||
|
||||
break;
|
||||
|
||||
case 'b': // bezierCurveTo
|
||||
|
||||
cpx = outline[ i ++ ] * scale + offsetX;
|
||||
cpy = outline[ i ++ ] * scale + offsetY;
|
||||
cpx1 = outline[ i ++ ] * scale + offsetX;
|
||||
cpy1 = outline[ i ++ ] * scale + offsetY;
|
||||
cpx2 = outline[ i ++ ] * scale + offsetX;
|
||||
cpy2 = outline[ i ++ ] * scale + offsetY;
|
||||
|
||||
path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return { offsetX: glyph.ha * scale, path: path };
|
||||
|
||||
}
|
||||
|
||||
export { Font };
|
81
lib/extras/core/Interpolations.js
Normal file
81
lib/extras/core/Interpolations.js
Normal file
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @author zz85 / http://www.lab4games.net/zz85/blog
|
||||
*
|
||||
* Bezier Curves formulas obtained from
|
||||
* http://en.wikipedia.org/wiki/Bézier_curve
|
||||
*/
|
||||
|
||||
function CatmullRom( t, p0, p1, p2, p3 ) {
|
||||
|
||||
var v0 = ( p2 - p0 ) * 0.5;
|
||||
var v1 = ( p3 - p1 ) * 0.5;
|
||||
var t2 = t * t;
|
||||
var t3 = t * t2;
|
||||
return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
function QuadraticBezierP0( t, p ) {
|
||||
|
||||
var k = 1 - t;
|
||||
return k * k * p;
|
||||
|
||||
}
|
||||
|
||||
function QuadraticBezierP1( t, p ) {
|
||||
|
||||
return 2 * ( 1 - t ) * t * p;
|
||||
|
||||
}
|
||||
|
||||
function QuadraticBezierP2( t, p ) {
|
||||
|
||||
return t * t * p;
|
||||
|
||||
}
|
||||
|
||||
function QuadraticBezier( t, p0, p1, p2 ) {
|
||||
|
||||
return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) +
|
||||
QuadraticBezierP2( t, p2 );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
function CubicBezierP0( t, p ) {
|
||||
|
||||
var k = 1 - t;
|
||||
return k * k * k * p;
|
||||
|
||||
}
|
||||
|
||||
function CubicBezierP1( t, p ) {
|
||||
|
||||
var k = 1 - t;
|
||||
return 3 * k * k * t * p;
|
||||
|
||||
}
|
||||
|
||||
function CubicBezierP2( t, p ) {
|
||||
|
||||
return 3 * ( 1 - t ) * t * t * p;
|
||||
|
||||
}
|
||||
|
||||
function CubicBezierP3( t, p ) {
|
||||
|
||||
return t * t * t * p;
|
||||
|
||||
}
|
||||
|
||||
function CubicBezier( t, p0, p1, p2, p3 ) {
|
||||
|
||||
return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) +
|
||||
CubicBezierP3( t, p3 );
|
||||
|
||||
}
|
||||
|
||||
export { CatmullRom, QuadraticBezier, CubicBezier };
|
183
lib/extras/core/Path.js
Normal file
183
lib/extras/core/Path.js
Normal file
@ -0,0 +1,183 @@
|
||||
import { Vector2 } from '../../math/Vector2.js';
|
||||
import { CurvePath } from './CurvePath.js';
|
||||
import { EllipseCurve } from '../curves/EllipseCurve.js';
|
||||
import { SplineCurve } from '../curves/SplineCurve.js';
|
||||
import { CubicBezierCurve } from '../curves/CubicBezierCurve.js';
|
||||
import { QuadraticBezierCurve } from '../curves/QuadraticBezierCurve.js';
|
||||
import { LineCurve } from '../curves/LineCurve.js';
|
||||
|
||||
/**
|
||||
* @author zz85 / http://www.lab4games.net/zz85/blog
|
||||
* Creates free form 2d path using series of points, lines or curves.
|
||||
**/
|
||||
|
||||
function Path( points ) {
|
||||
|
||||
CurvePath.call( this );
|
||||
|
||||
this.type = 'Path';
|
||||
|
||||
this.currentPoint = new Vector2();
|
||||
|
||||
if ( points ) {
|
||||
|
||||
this.setFromPoints( points );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Path.prototype = Object.assign( Object.create( CurvePath.prototype ), {
|
||||
|
||||
constructor: Path,
|
||||
|
||||
setFromPoints: function ( points ) {
|
||||
|
||||
this.moveTo( points[ 0 ].x, points[ 0 ].y );
|
||||
|
||||
for ( var i = 1, l = points.length; i < l; i ++ ) {
|
||||
|
||||
this.lineTo( points[ i ].x, points[ i ].y );
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
moveTo: function ( x, y ) {
|
||||
|
||||
this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying?
|
||||
|
||||
},
|
||||
|
||||
lineTo: function ( x, y ) {
|
||||
|
||||
var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) );
|
||||
this.curves.push( curve );
|
||||
|
||||
this.currentPoint.set( x, y );
|
||||
|
||||
},
|
||||
|
||||
quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) {
|
||||
|
||||
var curve = new QuadraticBezierCurve(
|
||||
this.currentPoint.clone(),
|
||||
new Vector2( aCPx, aCPy ),
|
||||
new Vector2( aX, aY )
|
||||
);
|
||||
|
||||
this.curves.push( curve );
|
||||
|
||||
this.currentPoint.set( aX, aY );
|
||||
|
||||
},
|
||||
|
||||
bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {
|
||||
|
||||
var curve = new CubicBezierCurve(
|
||||
this.currentPoint.clone(),
|
||||
new Vector2( aCP1x, aCP1y ),
|
||||
new Vector2( aCP2x, aCP2y ),
|
||||
new Vector2( aX, aY )
|
||||
);
|
||||
|
||||
this.curves.push( curve );
|
||||
|
||||
this.currentPoint.set( aX, aY );
|
||||
|
||||
},
|
||||
|
||||
splineThru: function ( pts /*Array of Vector*/ ) {
|
||||
|
||||
var npts = [ this.currentPoint.clone() ].concat( pts );
|
||||
|
||||
var curve = new SplineCurve( npts );
|
||||
this.curves.push( curve );
|
||||
|
||||
this.currentPoint.copy( pts[ pts.length - 1 ] );
|
||||
|
||||
},
|
||||
|
||||
arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
|
||||
|
||||
var x0 = this.currentPoint.x;
|
||||
var y0 = this.currentPoint.y;
|
||||
|
||||
this.absarc( aX + x0, aY + y0, aRadius,
|
||||
aStartAngle, aEndAngle, aClockwise );
|
||||
|
||||
},
|
||||
|
||||
absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
|
||||
|
||||
this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
|
||||
|
||||
},
|
||||
|
||||
ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
|
||||
|
||||
var x0 = this.currentPoint.x;
|
||||
var y0 = this.currentPoint.y;
|
||||
|
||||
this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );
|
||||
|
||||
},
|
||||
|
||||
absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
|
||||
|
||||
var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );
|
||||
|
||||
if ( this.curves.length > 0 ) {
|
||||
|
||||
// if a previous curve is present, attempt to join
|
||||
var firstPoint = curve.getPoint( 0 );
|
||||
|
||||
if ( ! firstPoint.equals( this.currentPoint ) ) {
|
||||
|
||||
this.lineTo( firstPoint.x, firstPoint.y );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.curves.push( curve );
|
||||
|
||||
var lastPoint = curve.getPoint( 1 );
|
||||
this.currentPoint.copy( lastPoint );
|
||||
|
||||
},
|
||||
|
||||
copy: function ( source ) {
|
||||
|
||||
CurvePath.prototype.copy.call( this, source );
|
||||
|
||||
this.currentPoint.copy( source.currentPoint );
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
|
||||
var data = CurvePath.prototype.toJSON.call( this );
|
||||
|
||||
data.currentPoint = this.currentPoint.toArray();
|
||||
|
||||
return data;
|
||||
|
||||
},
|
||||
|
||||
fromJSON: function ( json ) {
|
||||
|
||||
CurvePath.prototype.fromJSON.call( this, json );
|
||||
|
||||
this.currentPoint.fromArray( json.currentPoint );
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
|
||||
export { Path };
|
115
lib/extras/core/Shape.js
Normal file
115
lib/extras/core/Shape.js
Normal file
@ -0,0 +1,115 @@
|
||||
import { Path } from './Path.js';
|
||||
import { _Math } from '../../math/Math.js';
|
||||
|
||||
/**
|
||||
* @author zz85 / http://www.lab4games.net/zz85/blog
|
||||
* Defines a 2d shape plane using paths.
|
||||
**/
|
||||
|
||||
// STEP 1 Create a path.
|
||||
// STEP 2 Turn path into shape.
|
||||
// STEP 3 ExtrudeGeometry takes in Shape/Shapes
|
||||
// STEP 3a - Extract points from each shape, turn to vertices
|
||||
// STEP 3b - Triangulate each shape, add faces.
|
||||
|
||||
function Shape( points ) {
|
||||
|
||||
Path.call( this, points );
|
||||
|
||||
this.uuid = _Math.generateUUID();
|
||||
|
||||
this.type = 'Shape';
|
||||
|
||||
this.holes = [];
|
||||
|
||||
}
|
||||
|
||||
Shape.prototype = Object.assign( Object.create( Path.prototype ), {
|
||||
|
||||
constructor: Shape,
|
||||
|
||||
getPointsHoles: function ( divisions ) {
|
||||
|
||||
var holesPts = [];
|
||||
|
||||
for ( var i = 0, l = this.holes.length; i < l; i ++ ) {
|
||||
|
||||
holesPts[ i ] = this.holes[ i ].getPoints( divisions );
|
||||
|
||||
}
|
||||
|
||||
return holesPts;
|
||||
|
||||
},
|
||||
|
||||
// get points of shape and holes (keypoints based on segments parameter)
|
||||
|
||||
extractPoints: function ( divisions ) {
|
||||
|
||||
return {
|
||||
|
||||
shape: this.getPoints( divisions ),
|
||||
holes: this.getPointsHoles( divisions )
|
||||
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
copy: function ( source ) {
|
||||
|
||||
Path.prototype.copy.call( this, source );
|
||||
|
||||
this.holes = [];
|
||||
|
||||
for ( var i = 0, l = source.holes.length; i < l; i ++ ) {
|
||||
|
||||
var hole = source.holes[ i ];
|
||||
|
||||
this.holes.push( hole.clone() );
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
toJSON: function () {
|
||||
|
||||
var data = Path.prototype.toJSON.call( this );
|
||||
|
||||
data.uuid = this.uuid;
|
||||
data.holes = [];
|
||||
|
||||
for ( var i = 0, l = this.holes.length; i < l; i ++ ) {
|
||||
|
||||
var hole = this.holes[ i ];
|
||||
data.holes.push( hole.toJSON() );
|
||||
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
},
|
||||
|
||||
fromJSON: function ( json ) {
|
||||
|
||||
Path.prototype.fromJSON.call( this, json );
|
||||
|
||||
this.uuid = json.uuid;
|
||||
this.holes = [];
|
||||
|
||||
for ( var i = 0, l = json.holes.length; i < l; i ++ ) {
|
||||
|
||||
var hole = json.holes[ i ];
|
||||
this.holes.push( new Path().fromJSON( hole ) );
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
|
||||
export { Shape };
|
286
lib/extras/core/ShapePath.js
Normal file
286
lib/extras/core/ShapePath.js
Normal file
@ -0,0 +1,286 @@
|
||||
/**
|
||||
* @author zz85 / http://www.lab4games.net/zz85/blog
|
||||
* minimal class for proxing functions to Path. Replaces old "extractSubpaths()"
|
||||
**/
|
||||
|
||||
import { Color } from '../../math/Color.js';
|
||||
import { Path } from './Path.js';
|
||||
import { Shape } from './Shape.js';
|
||||
import { ShapeUtils } from '../ShapeUtils.js';
|
||||
|
||||
function ShapePath() {
|
||||
|
||||
this.type = 'ShapePath';
|
||||
|
||||
this.color = new Color();
|
||||
|
||||
this.subPaths = [];
|
||||
this.currentPath = null;
|
||||
|
||||
}
|
||||
|
||||
Object.assign( ShapePath.prototype, {
|
||||
|
||||
moveTo: function ( x, y ) {
|
||||
|
||||
this.currentPath = new Path();
|
||||
this.subPaths.push( this.currentPath );
|
||||
this.currentPath.moveTo( x, y );
|
||||
|
||||
},
|
||||
|
||||
lineTo: function ( x, y ) {
|
||||
|
||||
this.currentPath.lineTo( x, y );
|
||||
|
||||
},
|
||||
|
||||
quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) {
|
||||
|
||||
this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY );
|
||||
|
||||
},
|
||||
|
||||
bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {
|
||||
|
||||
this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY );
|
||||
|
||||
},
|
||||
|
||||
splineThru: function ( pts ) {
|
||||
|
||||
this.currentPath.splineThru( pts );
|
||||
|
||||
},
|
||||
|
||||
toShapes: function ( isCCW, noHoles ) {
|
||||
|
||||
function toShapesNoHoles( inSubpaths ) {
|
||||
|
||||
var shapes = [];
|
||||
|
||||
for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) {
|
||||
|
||||
var tmpPath = inSubpaths[ i ];
|
||||
|
||||
var tmpShape = new Shape();
|
||||
tmpShape.curves = tmpPath.curves;
|
||||
|
||||
shapes.push( tmpShape );
|
||||
|
||||
}
|
||||
|
||||
return shapes;
|
||||
|
||||
}
|
||||
|
||||
function isPointInsidePolygon( inPt, inPolygon ) {
|
||||
|
||||
var polyLen = inPolygon.length;
|
||||
|
||||
// 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 ++ ) {
|
||||
|
||||
var edgeLowPt = inPolygon[ p ];
|
||||
var edgeHighPt = inPolygon[ q ];
|
||||
|
||||
var edgeDx = edgeHighPt.x - edgeLowPt.x;
|
||||
var edgeDy = edgeHighPt.y - edgeLowPt.y;
|
||||
|
||||
if ( Math.abs( edgeDy ) > Number.EPSILON ) {
|
||||
|
||||
// not parallel
|
||||
if ( edgeDy < 0 ) {
|
||||
|
||||
edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx;
|
||||
edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;
|
||||
|
||||
}
|
||||
if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue;
|
||||
|
||||
if ( inPt.y === edgeLowPt.y ) {
|
||||
|
||||
if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ?
|
||||
// continue; // no intersection or edgeLowPt => doesn't count !!!
|
||||
|
||||
} else {
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return inside;
|
||||
|
||||
}
|
||||
|
||||
var isClockWise = ShapeUtils.isClockWise;
|
||||
|
||||
var subPaths = this.subPaths;
|
||||
if ( subPaths.length === 0 ) return [];
|
||||
|
||||
if ( noHoles === true ) return toShapesNoHoles( subPaths );
|
||||
|
||||
|
||||
var solid, tmpPath, tmpShape, shapes = [];
|
||||
|
||||
if ( subPaths.length === 1 ) {
|
||||
|
||||
tmpPath = subPaths[ 0 ];
|
||||
tmpShape = new Shape();
|
||||
tmpShape.curves = tmpPath.curves;
|
||||
shapes.push( tmpShape );
|
||||
return shapes;
|
||||
|
||||
}
|
||||
|
||||
var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() );
|
||||
holesFirst = isCCW ? ! holesFirst : holesFirst;
|
||||
|
||||
// console.log("Holes first", holesFirst);
|
||||
|
||||
var betterShapeHoles = [];
|
||||
var newShapes = [];
|
||||
var newShapeHoles = [];
|
||||
var mainIdx = 0;
|
||||
var tmpPoints;
|
||||
|
||||
newShapes[ mainIdx ] = undefined;
|
||||
newShapeHoles[ mainIdx ] = [];
|
||||
|
||||
for ( var i = 0, l = subPaths.length; i < l; i ++ ) {
|
||||
|
||||
tmpPath = subPaths[ i ];
|
||||
tmpPoints = tmpPath.getPoints();
|
||||
solid = isClockWise( tmpPoints );
|
||||
solid = isCCW ? ! solid : solid;
|
||||
|
||||
if ( solid ) {
|
||||
|
||||
if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++;
|
||||
|
||||
newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints };
|
||||
newShapes[ mainIdx ].s.curves = tmpPath.curves;
|
||||
|
||||
if ( holesFirst ) mainIdx ++;
|
||||
newShapeHoles[ mainIdx ] = [];
|
||||
|
||||
//console.log('cw', i);
|
||||
|
||||
} else {
|
||||
|
||||
newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } );
|
||||
|
||||
//console.log('ccw', i);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// only Holes? -> probably all Shapes with wrong orientation
|
||||
if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths );
|
||||
|
||||
|
||||
if ( newShapes.length > 1 ) {
|
||||
|
||||
var ambiguous = false;
|
||||
var toChange = [];
|
||||
|
||||
for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
|
||||
|
||||
betterShapeHoles[ sIdx ] = [];
|
||||
|
||||
}
|
||||
|
||||
for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
|
||||
|
||||
var sho = newShapeHoles[ sIdx ];
|
||||
|
||||
for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) {
|
||||
|
||||
var ho = sho[ hIdx ];
|
||||
var hole_unassigned = true;
|
||||
|
||||
for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {
|
||||
|
||||
if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) {
|
||||
|
||||
if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );
|
||||
if ( hole_unassigned ) {
|
||||
|
||||
hole_unassigned = false;
|
||||
betterShapeHoles[ s2Idx ].push( ho );
|
||||
|
||||
} else {
|
||||
|
||||
ambiguous = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if ( hole_unassigned ) {
|
||||
|
||||
betterShapeHoles[ sIdx ].push( ho );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// console.log("ambiguous: ", ambiguous);
|
||||
if ( toChange.length > 0 ) {
|
||||
|
||||
// console.log("to change: ", toChange);
|
||||
if ( ! ambiguous ) newShapeHoles = betterShapeHoles;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var tmpHoles;
|
||||
|
||||
for ( var i = 0, il = newShapes.length; i < il; i ++ ) {
|
||||
|
||||
tmpShape = newShapes[ i ].s;
|
||||
shapes.push( tmpShape );
|
||||
tmpHoles = newShapeHoles[ i ];
|
||||
|
||||
for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) {
|
||||
|
||||
tmpShape.holes.push( tmpHoles[ j ].h );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//console.log("shape", shapes);
|
||||
|
||||
return shapes;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
|
||||
export { ShapePath };
|
Reference in New Issue
Block a user