Curve.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. import { _Math } from '../../math/Math';
  2. import { Vector3 } from '../../math/Vector3';
  3. import { Matrix4 } from '../../math/Matrix4';
  4. /**
  5. * @author zz85 / http://www.lab4games.net/zz85/blog
  6. * Extensible curve object
  7. *
  8. * Some common of Curve methods
  9. * .getPoint(t), getTangent(t)
  10. * .getPointAt(u), getTangentAt(u)
  11. * .getPoints(), .getSpacedPoints()
  12. * .getLength()
  13. * .updateArcLengths()
  14. *
  15. * This following classes subclasses THREE.Curve:
  16. *
  17. * -- 2d classes --
  18. * THREE.LineCurve
  19. * THREE.QuadraticBezierCurve
  20. * THREE.CubicBezierCurve
  21. * THREE.SplineCurve
  22. * THREE.ArcCurve
  23. * THREE.EllipseCurve
  24. *
  25. * -- 3d classes --
  26. * THREE.LineCurve3
  27. * THREE.QuadraticBezierCurve3
  28. * THREE.CubicBezierCurve3
  29. * THREE.CatmullRomCurve3
  30. *
  31. * A series of curves can be represented as a THREE.CurvePath
  32. *
  33. **/
  34. /**************************************************************
  35. * Abstract Curve base class
  36. **************************************************************/
  37. function Curve() {}
  38. Curve.prototype = {
  39. constructor: Curve,
  40. // Virtual base class method to overwrite and implement in subclasses
  41. // - t [0 .. 1]
  42. getPoint: function ( t ) {
  43. console.warn( "THREE.Curve: Warning, getPoint() not implemented!" );
  44. return null;
  45. },
  46. // Get point at relative position in curve according to arc length
  47. // - u [0 .. 1]
  48. getPointAt: function ( u ) {
  49. var t = this.getUtoTmapping( u );
  50. return this.getPoint( t );
  51. },
  52. // Get sequence of points using getPoint( t )
  53. getPoints: function ( divisions ) {
  54. if ( ! divisions ) divisions = 5;
  55. var points = [];
  56. for ( var d = 0; d <= divisions; d ++ ) {
  57. points.push( this.getPoint( d / divisions ) );
  58. }
  59. return points;
  60. },
  61. // Get sequence of points using getPointAt( u )
  62. getSpacedPoints: function ( divisions ) {
  63. if ( ! divisions ) divisions = 5;
  64. var points = [];
  65. for ( var d = 0; d <= divisions; d ++ ) {
  66. points.push( this.getPointAt( d / divisions ) );
  67. }
  68. return points;
  69. },
  70. // Get total curve arc length
  71. getLength: function () {
  72. var lengths = this.getLengths();
  73. return lengths[ lengths.length - 1 ];
  74. },
  75. // Get list of cumulative segment lengths
  76. getLengths: function ( divisions ) {
  77. if ( ! divisions ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200;
  78. if ( this.cacheArcLengths
  79. && ( this.cacheArcLengths.length === divisions + 1 )
  80. && ! this.needsUpdate ) {
  81. //console.log( "cached", this.cacheArcLengths );
  82. return this.cacheArcLengths;
  83. }
  84. this.needsUpdate = false;
  85. var cache = [];
  86. var current, last = this.getPoint( 0 );
  87. var p, sum = 0;
  88. cache.push( 0 );
  89. for ( p = 1; p <= divisions; p ++ ) {
  90. current = this.getPoint ( p / divisions );
  91. sum += current.distanceTo( last );
  92. cache.push( sum );
  93. last = current;
  94. }
  95. this.cacheArcLengths = cache;
  96. return cache; // { sums: cache, sum:sum }; Sum is in the last element.
  97. },
  98. updateArcLengths: function() {
  99. this.needsUpdate = true;
  100. this.getLengths();
  101. },
  102. // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant
  103. getUtoTmapping: function ( u, distance ) {
  104. var arcLengths = this.getLengths();
  105. var i = 0, il = arcLengths.length;
  106. var targetArcLength; // The targeted u distance value to get
  107. if ( distance ) {
  108. targetArcLength = distance;
  109. } else {
  110. targetArcLength = u * arcLengths[ il - 1 ];
  111. }
  112. //var time = Date.now();
  113. // binary search for the index with largest value smaller than target u distance
  114. var low = 0, high = il - 1, comparison;
  115. while ( low <= high ) {
  116. 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
  117. comparison = arcLengths[ i ] - targetArcLength;
  118. if ( comparison < 0 ) {
  119. low = i + 1;
  120. } else if ( comparison > 0 ) {
  121. high = i - 1;
  122. } else {
  123. high = i;
  124. break;
  125. // DONE
  126. }
  127. }
  128. i = high;
  129. //console.log('b' , i, low, high, Date.now()- time);
  130. if ( arcLengths[ i ] === targetArcLength ) {
  131. var t = i / ( il - 1 );
  132. return t;
  133. }
  134. // we could get finer grain at lengths, or use simple interpolation between two points
  135. var lengthBefore = arcLengths[ i ];
  136. var lengthAfter = arcLengths[ i + 1 ];
  137. var segmentLength = lengthAfter - lengthBefore;
  138. // determine where we are between the 'before' and 'after' points
  139. var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
  140. // add that fractional amount to t
  141. var t = ( i + segmentFraction ) / ( il - 1 );
  142. return t;
  143. },
  144. // Returns a unit vector tangent at t
  145. // In case any sub curve does not implement its tangent derivation,
  146. // 2 points a small delta apart will be used to find its gradient
  147. // which seems to give a reasonable approximation
  148. getTangent: function( t ) {
  149. var delta = 0.0001;
  150. var t1 = t - delta;
  151. var t2 = t + delta;
  152. // Capping in case of danger
  153. if ( t1 < 0 ) t1 = 0;
  154. if ( t2 > 1 ) t2 = 1;
  155. var pt1 = this.getPoint( t1 );
  156. var pt2 = this.getPoint( t2 );
  157. var vec = pt2.clone().sub( pt1 );
  158. return vec.normalize();
  159. },
  160. getTangentAt: function ( u ) {
  161. var t = this.getUtoTmapping( u );
  162. return this.getTangent( t );
  163. },
  164. computeFrenetFrames: function ( segments, closed ) {
  165. // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf
  166. var normal = new Vector3();
  167. var tangents = [];
  168. var normals = [];
  169. var binormals = [];
  170. var vec = new Vector3();
  171. var mat = new Matrix4();
  172. var i, u, theta;
  173. // compute the tangent vectors for each segment on the curve
  174. for ( i = 0; i <= segments; i ++ ) {
  175. u = i / segments;
  176. tangents[ i ] = this.getTangentAt( u );
  177. tangents[ i ].normalize();
  178. }
  179. // select an initial normal vector perpendicular to the first tangent vector,
  180. // and in the direction of the minimum tangent xyz component
  181. normals[ 0 ] = new Vector3();
  182. binormals[ 0 ] = new Vector3();
  183. var min = Number.MAX_VALUE;
  184. var tx = Math.abs( tangents[ 0 ].x );
  185. var ty = Math.abs( tangents[ 0 ].y );
  186. var tz = Math.abs( tangents[ 0 ].z );
  187. if ( tx <= min ) {
  188. min = tx;
  189. normal.set( 1, 0, 0 );
  190. }
  191. if ( ty <= min ) {
  192. min = ty;
  193. normal.set( 0, 1, 0 );
  194. }
  195. if ( tz <= min ) {
  196. normal.set( 0, 0, 1 );
  197. }
  198. vec.crossVectors( tangents[ 0 ], normal ).normalize();
  199. normals[ 0 ].crossVectors( tangents[ 0 ], vec );
  200. binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );
  201. // compute the slowly-varying normal and binormal vectors for each segment on the curve
  202. for ( i = 1; i <= segments; i ++ ) {
  203. normals[ i ] = normals[ i - 1 ].clone();
  204. binormals[ i ] = binormals[ i - 1 ].clone();
  205. vec.crossVectors( tangents[ i - 1 ], tangents[ i ] );
  206. if ( vec.length() > Number.EPSILON ) {
  207. vec.normalize();
  208. theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors
  209. normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
  210. }
  211. binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
  212. }
  213. // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
  214. if ( closed === true ) {
  215. theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) );
  216. theta /= segments;
  217. if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) {
  218. theta = - theta;
  219. }
  220. for ( i = 1; i <= segments; i ++ ) {
  221. // twist a little...
  222. normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
  223. binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
  224. }
  225. }
  226. return {
  227. tangents: tangents,
  228. normals: normals,
  229. binormals: binormals
  230. };
  231. }
  232. };
  233. // TODO: Transformation for Curves?
  234. /**************************************************************
  235. * 3D Curves
  236. **************************************************************/
  237. // A Factory method for creating new curve subclasses
  238. Curve.create = function ( constructor, getPointFunc ) {
  239. constructor.prototype = Object.create( Curve.prototype );
  240. constructor.prototype.constructor = constructor;
  241. constructor.prototype.getPoint = getPointFunc;
  242. return constructor;
  243. };
  244. export { Curve };
粤ICP备19079148号