Curve.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  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 file contains following classes:
  13. *
  14. * -- 2d classes --
  15. * THREE.Curve
  16. * THREE.LineCurve
  17. * THREE.QuadraticBezierCurve
  18. * THREE.CubicBezierCurve
  19. * THREE.SplineCurve
  20. * THREE.ArcCurve
  21. * THREE.EllipseCurve
  22. *
  23. * -- 3d classes --
  24. * THREE.LineCurve3
  25. * THREE.QuadraticBezierCurve3
  26. * THREE.CubicBezierCurve3
  27. * THREE.SplineCurve3
  28. * THREE.ClosedSplineCurve3
  29. *
  30. * A series of curves can be represented as a THREE.CurvePath
  31. *
  32. **/
  33. /**************************************************************
  34. * Abstract Curve base class
  35. **************************************************************/
  36. THREE.Curve = function () {
  37. };
  38. // Virtual base class method to overwrite and implement in subclasses
  39. // - t [0 .. 1]
  40. THREE.Curve.prototype.getPoint = function ( t ) {
  41. console.log( "Warning, getPoint() not implemented!" );
  42. return null;
  43. };
  44. // Get point at relative position in curve according to arc length
  45. // - u [0 .. 1]
  46. THREE.Curve.prototype.getPointAt = function ( u ) {
  47. var t = this.getUtoTmapping( u );
  48. return this.getPoint( t );
  49. };
  50. // Get sequence of points using getPoint( t )
  51. THREE.Curve.prototype.getPoints = function ( divisions ) {
  52. if ( !divisions ) divisions = 5;
  53. var d, pts = [];
  54. for ( d = 0; d <= divisions; d ++ ) {
  55. pts.push( this.getPoint( d / divisions ) );
  56. }
  57. return pts;
  58. };
  59. // Get sequence of points using getPointAt( u )
  60. THREE.Curve.prototype.getSpacedPoints = function ( divisions ) {
  61. if ( !divisions ) divisions = 5;
  62. var d, pts = [];
  63. for ( d = 0; d <= divisions; d ++ ) {
  64. pts.push( this.getPointAt( d / divisions ) );
  65. }
  66. return pts;
  67. };
  68. // Get total curve arc length
  69. THREE.Curve.prototype.getLength = function () {
  70. var lengths = this.getLengths();
  71. return lengths[ lengths.length - 1 ];
  72. };
  73. // Get list of cumulative segment lengths
  74. THREE.Curve.prototype.getLengths = function ( divisions ) {
  75. if ( !divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200;
  76. if ( this.cacheArcLengths
  77. && ( this.cacheArcLengths.length == divisions + 1 )
  78. && !this.needsUpdate) {
  79. //console.log( "cached", this.cacheArcLengths );
  80. return this.cacheArcLengths;
  81. }
  82. this.needsUpdate = false;
  83. var cache = [];
  84. var current, last = this.getPoint( 0 );
  85. var p, sum = 0;
  86. cache.push( 0 );
  87. for ( p = 1; p <= divisions; p ++ ) {
  88. current = this.getPoint ( p / divisions );
  89. sum += current.distanceTo( last );
  90. cache.push( sum );
  91. last = current;
  92. }
  93. this.cacheArcLengths = cache;
  94. return cache; // { sums: cache, sum:sum }; Sum is in the last element.
  95. };
  96. THREE.Curve.prototype.updateArcLengths = function() {
  97. this.needsUpdate = true;
  98. this.getLengths();
  99. };
  100. // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance
  101. THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) {
  102. var arcLengths = this.getLengths();
  103. var i = 0, il = arcLengths.length;
  104. var targetArcLength; // The targeted u distance value to get
  105. if ( distance ) {
  106. targetArcLength = distance;
  107. } else {
  108. targetArcLength = u * arcLengths[ il - 1 ];
  109. }
  110. //var time = Date.now();
  111. // binary search for the index with largest value smaller than target u distance
  112. var low = 0, high = il - 1, comparison;
  113. while ( low <= high ) {
  114. 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
  115. comparison = arcLengths[ i ] - targetArcLength;
  116. if ( comparison < 0 ) {
  117. low = i + 1;
  118. continue;
  119. } else if ( comparison > 0 ) {
  120. high = i - 1;
  121. continue;
  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 interpolatation 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. THREE.Curve.prototype.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. THREE.Curve.prototype.getTangentAt = function ( u ) {
  161. var t = this.getUtoTmapping( u );
  162. return this.getTangent( t );
  163. };
  164. /**************************************************************
  165. * Line
  166. **************************************************************/
  167. THREE.LineCurve = function ( v1, v2 ) {
  168. this.v1 = v1;
  169. this.v2 = v2;
  170. };
  171. THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype );
  172. THREE.LineCurve.prototype.getPoint = function ( t ) {
  173. var point = this.v2.clone().sub(this.v1);
  174. point.multiplyScalar( t ).add( this.v1 );
  175. return point;
  176. };
  177. // Line curve is linear, so we can overwrite default getPointAt
  178. THREE.LineCurve.prototype.getPointAt = function ( u ) {
  179. return this.getPoint( u );
  180. };
  181. THREE.LineCurve.prototype.getTangent = function( t ) {
  182. var tangent = this.v2.clone().sub(this.v1);
  183. return tangent.normalize();
  184. };
  185. /**************************************************************
  186. * Quadratic Bezier curve
  187. **************************************************************/
  188. THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) {
  189. this.v0 = v0;
  190. this.v1 = v1;
  191. this.v2 = v2;
  192. };
  193. THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype );
  194. THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) {
  195. var tx, ty;
  196. tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
  197. ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
  198. return new THREE.Vector2( tx, ty );
  199. };
  200. THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {
  201. var tx, ty;
  202. tx = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x );
  203. ty = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y );
  204. // returns unit vector
  205. var tangent = new THREE.Vector2( tx, ty );
  206. tangent.normalize();
  207. return tangent;
  208. };
  209. /**************************************************************
  210. * Cubic Bezier curve
  211. **************************************************************/
  212. THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) {
  213. this.v0 = v0;
  214. this.v1 = v1;
  215. this.v2 = v2;
  216. this.v3 = v3;
  217. };
  218. THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype );
  219. THREE.CubicBezierCurve.prototype.getPoint = function ( t ) {
  220. var tx, ty;
  221. tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
  222. ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
  223. return new THREE.Vector2( tx, ty );
  224. };
  225. THREE.CubicBezierCurve.prototype.getTangent = function( t ) {
  226. var tx, ty;
  227. tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
  228. ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
  229. var tangent = new THREE.Vector2( tx, ty );
  230. tangent.normalize();
  231. return tangent;
  232. };
  233. /**************************************************************
  234. * Spline curve
  235. **************************************************************/
  236. THREE.SplineCurve = function ( points /* array of Vector2 */ ) {
  237. this.points = (points == undefined) ? [] : points;
  238. };
  239. THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype );
  240. THREE.SplineCurve.prototype.getPoint = function ( t ) {
  241. var v = new THREE.Vector2();
  242. var c = [];
  243. var points = this.points, point, intPoint, weight;
  244. point = ( points.length - 1 ) * t;
  245. intPoint = Math.floor( point );
  246. weight = point - intPoint;
  247. c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
  248. c[ 1 ] = intPoint;
  249. c[ 2 ] = intPoint > points.length - 2 ? points.length -1 : intPoint + 1;
  250. c[ 3 ] = intPoint > points.length - 3 ? points.length -1 : intPoint + 2;
  251. v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight );
  252. v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight );
  253. return v;
  254. };
  255. /**************************************************************
  256. * Ellipse curve
  257. **************************************************************/
  258. THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius,
  259. aStartAngle, aEndAngle,
  260. aClockwise ) {
  261. this.aX = aX;
  262. this.aY = aY;
  263. this.xRadius = xRadius;
  264. this.yRadius = yRadius;
  265. this.aStartAngle = aStartAngle;
  266. this.aEndAngle = aEndAngle;
  267. this.aClockwise = aClockwise;
  268. };
  269. THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype );
  270. THREE.EllipseCurve.prototype.getPoint = function ( t ) {
  271. var deltaAngle = this.aEndAngle - this.aStartAngle;
  272. if ( !this.aClockwise ) {
  273. t = 1 - t;
  274. }
  275. var angle = this.aStartAngle + t * deltaAngle;
  276. var tx = this.aX + this.xRadius * Math.cos( angle );
  277. var ty = this.aY + this.yRadius * Math.sin( angle );
  278. return new THREE.Vector2( tx, ty );
  279. };
  280. /**************************************************************
  281. * Arc curve
  282. **************************************************************/
  283. THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
  284. THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
  285. };
  286. THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype );
  287. /**************************************************************
  288. * Utils
  289. **************************************************************/
  290. THREE.Curve.Utils = {
  291. tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
  292. return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
  293. },
  294. // Puay Bing, thanks for helping with this derivative!
  295. tangentCubicBezier: function (t, p0, p1, p2, p3 ) {
  296. return -3 * p0 * (1 - t) * (1 - t) +
  297. 3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) +
  298. 6 * t * p2 * (1-t) - 3 * t * t * p2 +
  299. 3 * t * t * p3;
  300. },
  301. tangentSpline: function ( t, p0, p1, p2, p3 ) {
  302. // To check if my formulas are correct
  303. var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1
  304. var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t
  305. var h01 = -6 * t * t + 6 * t; // − 2t3 + 3t2
  306. var h11 = 3 * t * t - 2 * t; // t3 − t2
  307. return h00 + h10 + h01 + h11;
  308. },
  309. // Catmull-Rom
  310. interpolate: function( p0, p1, p2, p3, t ) {
  311. var v0 = ( p2 - p0 ) * 0.5;
  312. var v1 = ( p3 - p1 ) * 0.5;
  313. var t2 = t * t;
  314. var t3 = t * t2;
  315. return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
  316. }
  317. };
  318. // TODO: Transformation for Curves?
  319. /**************************************************************
  320. * 3D Curves
  321. **************************************************************/
  322. // A Factory method for creating new curve subclasses
  323. THREE.Curve.create = function ( constructor, getPointFunc ) {
  324. constructor.prototype = Object.create( THREE.Curve.prototype );
  325. constructor.prototype.getPoint = getPointFunc;
  326. return constructor;
  327. };
  328. /**************************************************************
  329. * Line3D
  330. **************************************************************/
  331. THREE.LineCurve3 = THREE.Curve.create(
  332. function ( v1, v2 ) {
  333. this.v1 = v1;
  334. this.v2 = v2;
  335. },
  336. function ( t ) {
  337. var r = new THREE.Vector3();
  338. r.subVectors( this.v2, this.v1 ); // diff
  339. r.multiplyScalar( t );
  340. r.add( this.v1 );
  341. return r;
  342. }
  343. );
  344. /**************************************************************
  345. * Quadratic Bezier 3D curve
  346. **************************************************************/
  347. THREE.QuadraticBezierCurve3 = THREE.Curve.create(
  348. function ( v0, v1, v2 ) {
  349. this.v0 = v0;
  350. this.v1 = v1;
  351. this.v2 = v2;
  352. },
  353. function ( t ) {
  354. var tx, ty, tz;
  355. tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
  356. ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
  357. tz = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z );
  358. return new THREE.Vector3( tx, ty, tz );
  359. }
  360. );
  361. /**************************************************************
  362. * Cubic Bezier 3D curve
  363. **************************************************************/
  364. THREE.CubicBezierCurve3 = THREE.Curve.create(
  365. function ( v0, v1, v2, v3 ) {
  366. this.v0 = v0;
  367. this.v1 = v1;
  368. this.v2 = v2;
  369. this.v3 = v3;
  370. },
  371. function ( t ) {
  372. var tx, ty, tz;
  373. tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
  374. ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
  375. tz = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z );
  376. return new THREE.Vector3( tx, ty, tz );
  377. }
  378. );
  379. /**************************************************************
  380. * Spline 3D curve
  381. **************************************************************/
  382. THREE.SplineCurve3 = THREE.Curve.create(
  383. function ( points /* array of Vector3 */) {
  384. this.points = (points == undefined) ? [] : points;
  385. },
  386. function ( t ) {
  387. var v = new THREE.Vector3();
  388. var c = [];
  389. var points = this.points, point, intPoint, weight;
  390. point = ( points.length - 1 ) * t;
  391. intPoint = Math.floor( point );
  392. weight = point - intPoint;
  393. c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
  394. c[ 1 ] = intPoint;
  395. c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1;
  396. c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2;
  397. var pt0 = points[ c[0] ],
  398. pt1 = points[ c[1] ],
  399. pt2 = points[ c[2] ],
  400. pt3 = points[ c[3] ];
  401. v.x = THREE.Curve.Utils.interpolate(pt0.x, pt1.x, pt2.x, pt3.x, weight);
  402. v.y = THREE.Curve.Utils.interpolate(pt0.y, pt1.y, pt2.y, pt3.y, weight);
  403. v.z = THREE.Curve.Utils.interpolate(pt0.z, pt1.z, pt2.z, pt3.z, weight);
  404. return v;
  405. }
  406. );
  407. // THREE.SplineCurve3.prototype.getTangent = function(t) {
  408. // var v = new THREE.Vector3();
  409. // var c = [];
  410. // var points = this.points, point, intPoint, weight;
  411. // point = ( points.length - 1 ) * t;
  412. // intPoint = Math.floor( point );
  413. // weight = point - intPoint;
  414. // c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
  415. // c[ 1 ] = intPoint;
  416. // c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1;
  417. // c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2;
  418. // var pt0 = points[ c[0] ],
  419. // pt1 = points[ c[1] ],
  420. // pt2 = points[ c[2] ],
  421. // pt3 = points[ c[3] ];
  422. // // t = weight;
  423. // v.x = THREE.Curve.Utils.tangentSpline( t, pt0.x, pt1.x, pt2.x, pt3.x );
  424. // v.y = THREE.Curve.Utils.tangentSpline( t, pt0.y, pt1.y, pt2.y, pt3.y );
  425. // v.z = THREE.Curve.Utils.tangentSpline( t, pt0.z, pt1.z, pt2.z, pt3.z );
  426. // return v;
  427. // }
  428. /**************************************************************
  429. * Closed Spline 3D curve
  430. **************************************************************/
  431. THREE.ClosedSplineCurve3 = THREE.Curve.create(
  432. function ( points /* array of Vector3 */) {
  433. this.points = (points == undefined) ? [] : points;
  434. },
  435. function ( t ) {
  436. var v = new THREE.Vector3();
  437. var c = [];
  438. var points = this.points, point, intPoint, weight;
  439. point = ( points.length - 0 ) * t;
  440. // This needs to be from 0-length +1
  441. intPoint = Math.floor( point );
  442. weight = point - intPoint;
  443. intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;
  444. c[ 0 ] = ( intPoint - 1 ) % points.length;
  445. c[ 1 ] = ( intPoint ) % points.length;
  446. c[ 2 ] = ( intPoint + 1 ) % points.length;
  447. c[ 3 ] = ( intPoint + 2 ) % points.length;
  448. v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight );
  449. v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight );
  450. v.z = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].z, points[ c[ 1 ] ].z, points[ c[ 2 ] ].z, points[ c[ 3 ] ].z, weight );
  451. return v;
  452. }
  453. );
粤ICP备19079148号