123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- /**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- * Extensible curve object
- *
- * Some common of Curve methods
- * .getPoint(t), getTangent(t)
- * .getPointAt(u), getTagentAt(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.SplineCurve3
- * THREE.ClosedSplineCurve3
- *
- * A series of curves can be represented as a THREE.CurvePath
- *
- **/
- /**************************************************************
- * Abstract Curve base class
- **************************************************************/
- THREE.Curve = function () {
- };
- // Virtual base class method to overwrite and implement in subclasses
- // - t [0 .. 1]
- THREE.Curve.prototype.getPoint = function ( t ) {
- console.log( "Warning, getPoint() not implemented!" );
- return null;
- };
- // Get point at relative position in curve according to arc length
- // - u [0 .. 1]
- THREE.Curve.prototype.getPointAt = function ( u ) {
- var t = this.getUtoTmapping( u );
- return this.getPoint( t );
- };
- // Get sequence of points using getPoint( t )
- THREE.Curve.prototype.getPoints = function ( divisions ) {
- if ( ! divisions ) divisions = 5;
- var d, pts = [];
- for ( d = 0; d <= divisions; d ++ ) {
- pts.push( this.getPoint( d / divisions ) );
- }
- return pts;
- };
- // Get sequence of points using getPointAt( u )
- THREE.Curve.prototype.getSpacedPoints = function ( divisions ) {
- if ( ! divisions ) divisions = 5;
- var d, pts = [];
- for ( d = 0; d <= divisions; d ++ ) {
- pts.push( this.getPointAt( d / divisions ) );
- }
- return pts;
- };
- // Get total curve arc length
- THREE.Curve.prototype.getLength = function () {
- var lengths = this.getLengths();
- return lengths[ lengths.length - 1 ];
- };
- // Get list of cumulative segment lengths
- THREE.Curve.prototype.getLengths = function ( divisions ) {
- if ( ! divisions ) 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.
- };
- THREE.Curve.prototype.updateArcLengths = function() {
- this.needsUpdate = true;
- this.getLengths();
- };
- // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance
- THREE.Curve.prototype.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;
- continue;
- } else if ( comparison > 0 ) {
- high = i - 1;
- continue;
- } 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 interpolatation 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
- THREE.Curve.prototype.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();
- };
- THREE.Curve.prototype.getTangentAt = function ( u ) {
- var t = this.getUtoTmapping( u );
- return this.getTangent( t );
- };
- /**************************************************************
- * Utils
- **************************************************************/
- THREE.Curve.Utils = {
- tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
- return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
- },
- // Puay Bing, thanks for helping with this derivative!
- tangentCubicBezier: function (t, p0, p1, p2, p3 ) {
- return - 3 * p0 * (1 - t) * (1 - t) +
- 3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) +
- 6 * t * p2 * (1-t) - 3 * t * t * p2 +
- 3 * t * t * p3;
- },
- tangentSpline: function ( t, p0, p1, p2, p3 ) {
- // To check if my formulas are correct
- var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1
- var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t
- var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2
- var h11 = 3 * t * t - 2 * t; // t3 − t2
- return h00 + h10 + h01 + h11;
- },
- // Catmull-Rom
- interpolate: function( p0, p1, p2, p3, t ) {
- 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;
- }
- };
- // TODO: Transformation for Curves?
- /**************************************************************
- * 3D Curves
- **************************************************************/
- // A Factory method for creating new curve subclasses
- THREE.Curve.create = function ( constructor, getPointFunc ) {
- constructor.prototype = Object.create( THREE.Curve.prototype );
- constructor.prototype.getPoint = getPointFunc;
- return constructor;
- };
|