123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- import { _Math } from '../../math/Math';
- import { Vector3 } from '../../math/Vector3';
- import { Matrix4 } from '../../math/Matrix4';
- /**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- * Extensible curve object
- *
- * Some common of Curve methods
- * .getPoint(t), getTangent(t)
- * .getPointAt(u), getTangentAt(u)
- * .getPoints(), .getSpacedPoints()
- * .getLength()
- * .updateArcLengths()
- *
- * This following classes subclasses THREE.Curve:
- *
- * -- 2d classes --
- * THREE.LineCurve
- * THREE.QuadraticBezierCurve
- * THREE.CubicBezierCurve
- * THREE.SplineCurve
- * THREE.ArcCurve
- * THREE.EllipseCurve
- *
- * -- 3d classes --
- * THREE.LineCurve3
- * THREE.QuadraticBezierCurve3
- * THREE.CubicBezierCurve3
- * THREE.CatmullRomCurve3
- *
- * A series of curves can be represented as a THREE.CurvePath
- *
- **/
- /**************************************************************
- * Abstract Curve base class
- **************************************************************/
- function Curve() {}
- Object.assign( Curve.prototype, {
- // Virtual base class method to overwrite and implement in subclasses
- // - t [0 .. 1]
- getPoint: function ( t ) {
- console.warn( "THREE.Curve: Warning, getPoint() not implemented!" );
- return null;
- },
- // Get point at relative position in curve according to arc length
- // - u [0 .. 1]
- getPointAt: function ( u ) {
- var t = this.getUtoTmapping( u );
- return this.getPoint( t );
- },
- // 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 ) ? ( this.__arcLengthDivisions ) : 200;
- if ( this.cacheArcLengths
- && ( this.cacheArcLengths.length === divisions + 1 )
- && ! this.needsUpdate ) {
- //console.log( "cached", this.cacheArcLengths );
- 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 ];
- }
- //var time = Date.now();
- // 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;
- //console.log('b' , i, low, high, Date.now()- time);
- if ( arcLengths[ i ] === targetArcLength ) {
- var t = i / ( il - 1 );
- return t;
- }
- // 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
- };
- }
- } );
- export { Curve };
|