Curve.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /**
  2. * @author zz85 / http://www.lab4games.net/zz85/blog
  3. * Extensible curve object
  4. *
  5. * Some common of Curve methods
  6. * .getPoint(t), getTangent(t)
  7. * .getPointAt(u), getTagentAt(u)
  8. * .getPoints(), .getSpacedPoints()
  9. * .getLength()
  10. * .updateArcLengths()
  11. *
  12. * This following classes subclasses THREE.Curve:
  13. *
  14. * -- 2d classes --
  15. * THREE.LineCurve
  16. * THREE.QuadraticBezierCurve
  17. * THREE.CubicBezierCurve
  18. * THREE.SplineCurve
  19. * THREE.ArcCurve
  20. * THREE.EllipseCurve
  21. *
  22. * -- 3d classes --
  23. * THREE.LineCurve3
  24. * THREE.QuadraticBezierCurve3
  25. * THREE.CubicBezierCurve3
  26. * THREE.SplineCurve3
  27. * THREE.ClosedSplineCurve3
  28. *
  29. * A series of curves can be represented as a THREE.CurvePath
  30. *
  31. **/
  32. /**************************************************************
  33. * Abstract Curve base class
  34. **************************************************************/
  35. THREE.Curve = function () {
  36. };
  37. // Virtual base class method to overwrite and implement in subclasses
  38. // - t [0 .. 1]
  39. THREE.Curve.prototype.getPoint = function ( t ) {
  40. console.log( "Warning, getPoint() not implemented!" );
  41. return null;
  42. };
  43. // Get point at relative position in curve according to arc length
  44. // - u [0 .. 1]
  45. THREE.Curve.prototype.getPointAt = function ( u ) {
  46. var t = this.getUtoTmapping( u );
  47. return this.getPoint( t );
  48. };
  49. // Get sequence of points using getPoint( t )
  50. THREE.Curve.prototype.getPoints = function ( divisions ) {
  51. if ( ! divisions ) divisions = 5;
  52. var d, pts = [];
  53. for ( d = 0; d <= divisions; d ++ ) {
  54. pts.push( this.getPoint( d / divisions ) );
  55. }
  56. return pts;
  57. };
  58. // Get sequence of points using getPointAt( u )
  59. THREE.Curve.prototype.getSpacedPoints = function ( divisions ) {
  60. if ( ! divisions ) divisions = 5;
  61. var d, pts = [];
  62. for ( d = 0; d <= divisions; d ++ ) {
  63. pts.push( this.getPointAt( d / divisions ) );
  64. }
  65. return pts;
  66. };
  67. // Get total curve arc length
  68. THREE.Curve.prototype.getLength = function () {
  69. var lengths = this.getLengths();
  70. return lengths[ lengths.length - 1 ];
  71. };
  72. // Get list of cumulative segment lengths
  73. THREE.Curve.prototype.getLengths = function ( divisions ) {
  74. if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200;
  75. if ( this.cacheArcLengths
  76. && ( this.cacheArcLengths.length == divisions + 1 )
  77. && ! this.needsUpdate) {
  78. //console.log( "cached", this.cacheArcLengths );
  79. return this.cacheArcLengths;
  80. }
  81. this.needsUpdate = false;
  82. var cache = [];
  83. var current, last = this.getPoint( 0 );
  84. var p, sum = 0;
  85. cache.push( 0 );
  86. for ( p = 1; p <= divisions; p ++ ) {
  87. current = this.getPoint ( p / divisions );
  88. sum += current.distanceTo( last );
  89. cache.push( sum );
  90. last = current;
  91. }
  92. this.cacheArcLengths = cache;
  93. return cache; // { sums: cache, sum:sum }; Sum is in the last element.
  94. };
  95. THREE.Curve.prototype.updateArcLengths = function() {
  96. this.needsUpdate = true;
  97. this.getLengths();
  98. };
  99. // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance
  100. THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) {
  101. var arcLengths = this.getLengths();
  102. var i = 0, il = arcLengths.length;
  103. var targetArcLength; // The targeted u distance value to get
  104. if ( distance ) {
  105. targetArcLength = distance;
  106. } else {
  107. targetArcLength = u * arcLengths[ il - 1 ];
  108. }
  109. //var time = Date.now();
  110. // binary search for the index with largest value smaller than target u distance
  111. var low = 0, high = il - 1, comparison;
  112. while ( low <= high ) {
  113. 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
  114. comparison = arcLengths[ i ] - targetArcLength;
  115. if ( comparison < 0 ) {
  116. low = i + 1;
  117. continue;
  118. } else if ( comparison > 0 ) {
  119. high = i - 1;
  120. continue;
  121. } else {
  122. high = i;
  123. break;
  124. // DONE
  125. }
  126. }
  127. i = high;
  128. //console.log('b' , i, low, high, Date.now()- time);
  129. if ( arcLengths[ i ] == targetArcLength ) {
  130. var t = i / ( il - 1 );
  131. return t;
  132. }
  133. // we could get finer grain at lengths, or use simple interpolatation between two points
  134. var lengthBefore = arcLengths[ i ];
  135. var lengthAfter = arcLengths[ i + 1 ];
  136. var segmentLength = lengthAfter - lengthBefore;
  137. // determine where we are between the 'before' and 'after' points
  138. var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
  139. // add that fractional amount to t
  140. var t = ( i + segmentFraction ) / ( il -1 );
  141. return t;
  142. };
  143. // Returns a unit vector tangent at t
  144. // In case any sub curve does not implement its tangent derivation,
  145. // 2 points a small delta apart will be used to find its gradient
  146. // which seems to give a reasonable approximation
  147. THREE.Curve.prototype.getTangent = function( t ) {
  148. var delta = 0.0001;
  149. var t1 = t - delta;
  150. var t2 = t + delta;
  151. // Capping in case of danger
  152. if ( t1 < 0 ) t1 = 0;
  153. if ( t2 > 1 ) t2 = 1;
  154. var pt1 = this.getPoint( t1 );
  155. var pt2 = this.getPoint( t2 );
  156. var vec = pt2.clone().sub(pt1);
  157. return vec.normalize();
  158. };
  159. THREE.Curve.prototype.getTangentAt = function ( u ) {
  160. var t = this.getUtoTmapping( u );
  161. return this.getTangent( t );
  162. };
  163. /**************************************************************
  164. * Utils
  165. **************************************************************/
  166. THREE.Curve.Utils = {
  167. tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
  168. return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
  169. },
  170. // Puay Bing, thanks for helping with this derivative!
  171. tangentCubicBezier: function (t, p0, p1, p2, p3 ) {
  172. return - 3 * p0 * (1 - t) * (1 - t) +
  173. 3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) +
  174. 6 * t * p2 * (1-t) - 3 * t * t * p2 +
  175. 3 * t * t * p3;
  176. },
  177. tangentSpline: function ( t, p0, p1, p2, p3 ) {
  178. // To check if my formulas are correct
  179. var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1
  180. var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t
  181. var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2
  182. var h11 = 3 * t * t - 2 * t; // t3 − t2
  183. return h00 + h10 + h01 + h11;
  184. },
  185. // Catmull-Rom
  186. interpolate: function( p0, p1, p2, p3, t ) {
  187. var v0 = ( p2 - p0 ) * 0.5;
  188. var v1 = ( p3 - p1 ) * 0.5;
  189. var t2 = t * t;
  190. var t3 = t * t2;
  191. return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
  192. }
  193. };
  194. // TODO: Transformation for Curves?
  195. /**************************************************************
  196. * 3D Curves
  197. **************************************************************/
  198. // A Factory method for creating new curve subclasses
  199. THREE.Curve.create = function ( constructor, getPointFunc ) {
  200. constructor.prototype = Object.create( THREE.Curve.prototype );
  201. constructor.prototype.getPoint = getPointFunc;
  202. return constructor;
  203. };