Curve.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  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. *
  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. // In 2D space, there are actually 2 normal vectors,
  144. // and in 3D space, infinte
  145. // TODO this should be depreciated.
  146. THREE.Curve.prototype.getNormalVector = function( t ) {
  147. var vec = this.getTangent( t );
  148. return new THREE.Vector2( -vec.y , vec.x );
  149. };
  150. // Returns a unit vector tangent at t
  151. // In case any sub curve does not implement its tangent / normal finding,
  152. // we get 2 points with a small delta and find a gradient of the 2 points
  153. // which seems to make a reasonable approximation
  154. THREE.Curve.prototype.getTangent = function( t ) {
  155. var delta = 0.0001;
  156. var t1 = t - delta;
  157. var t2 = t + delta;
  158. // Capping in case of danger
  159. if ( t1 < 0 ) t1 = 0;
  160. if ( t2 > 1 ) t2 = 1;
  161. var pt1 = this.getPoint( t1 );
  162. var pt2 = this.getPoint( t2 );
  163. var vec = pt2.clone().subSelf(pt1);
  164. return vec.normalize();
  165. };
  166. THREE.Curve.prototype.getTangentAt = function ( u ) {
  167. var t = this.getUtoTmapping( u );
  168. return this.getTangent( t );
  169. };
  170. /**************************************************************
  171. * Line
  172. **************************************************************/
  173. THREE.LineCurve = function ( v1, v2 ) {
  174. this.v1 = v1;
  175. this.v2 = v2;
  176. };
  177. THREE.LineCurve.prototype = new THREE.Curve();
  178. THREE.LineCurve.prototype.constructor = THREE.LineCurve;
  179. THREE.LineCurve.prototype.getPoint = function ( t ) {
  180. var point = this.v2.clone().subSelf(this.v1);
  181. point.multiplyScalar( t ).addSelf( this.v1 );
  182. return point;
  183. };
  184. // Line curve is linear, so we can overwrite default getPointAt
  185. THREE.LineCurve.prototype.getPointAt = function ( u ) {
  186. return this.getPoint( u );
  187. };
  188. THREE.LineCurve.prototype.getTangent = function( t ) {
  189. var tangent = this.v2.clone().subSelf(this.v1);
  190. return tangent.normalize();
  191. };
  192. /**************************************************************
  193. * Quadratic Bezier curve
  194. **************************************************************/
  195. THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) {
  196. this.v0 = v0;
  197. this.v1 = v1;
  198. this.v2 = v2;
  199. };
  200. THREE.QuadraticBezierCurve.prototype = new THREE.Curve();
  201. THREE.QuadraticBezierCurve.prototype.constructor = THREE.QuadraticBezierCurve;
  202. THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) {
  203. var tx, ty;
  204. tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
  205. ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
  206. return new THREE.Vector2( tx, ty );
  207. };
  208. THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {
  209. var tx, ty;
  210. tx = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x );
  211. ty = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y );
  212. // returns unit vector
  213. var tangent = new THREE.Vector2( tx, ty );
  214. tangent.normalize();
  215. return tangent;
  216. };
  217. /**************************************************************
  218. * Cubic Bezier curve
  219. **************************************************************/
  220. THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) {
  221. this.v0 = v0;
  222. this.v1 = v1;
  223. this.v2 = v2;
  224. this.v3 = v3;
  225. };
  226. THREE.CubicBezierCurve.prototype = new THREE.Curve();
  227. THREE.CubicBezierCurve.prototype.constructor = THREE.CubicBezierCurve;
  228. THREE.CubicBezierCurve.prototype.getPoint = function ( t ) {
  229. var tx, ty;
  230. tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
  231. ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
  232. return new THREE.Vector2( tx, ty );
  233. };
  234. THREE.CubicBezierCurve.prototype.getTangent = function( t ) {
  235. var tx, ty;
  236. tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
  237. ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
  238. var tangent = new THREE.Vector2( tx, ty );
  239. tangent.normalize();
  240. return tangent;
  241. };
  242. /**************************************************************
  243. * Spline curve
  244. **************************************************************/
  245. THREE.SplineCurve = function ( points /* array of Vector2 */ ) {
  246. this.points = (points == undefined) ? [] : points;
  247. };
  248. THREE.SplineCurve.prototype = new THREE.Curve();
  249. THREE.SplineCurve.prototype.constructor = THREE.SplineCurve;
  250. THREE.SplineCurve.prototype.getPoint = function ( t ) {
  251. var v = new THREE.Vector2();
  252. var c = [];
  253. var points = this.points, point, intPoint, weight;
  254. point = ( points.length - 1 ) * t;
  255. intPoint = Math.floor( point );
  256. weight = point - intPoint;
  257. c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
  258. c[ 1 ] = intPoint;
  259. c[ 2 ] = intPoint > points.length - 2 ? points.length -1 : intPoint + 1;
  260. c[ 3 ] = intPoint > points.length - 3 ? points.length -1 : intPoint + 2;
  261. v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight );
  262. v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight );
  263. return v;
  264. };
  265. /**************************************************************
  266. * Ellipse curve
  267. **************************************************************/
  268. THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius,
  269. aStartAngle, aEndAngle,
  270. aClockwise ) {
  271. this.aX = aX;
  272. this.aY = aY;
  273. this.xRadius = xRadius;
  274. this.yRadius = yRadius;
  275. this.aStartAngle = aStartAngle;
  276. this.aEndAngle = aEndAngle;
  277. this.aClockwise = aClockwise;
  278. };
  279. THREE.EllipseCurve.prototype = new THREE.Curve();
  280. THREE.EllipseCurve.prototype.constructor = THREE.EllipseCurve;
  281. THREE.EllipseCurve.prototype.getPoint = function ( t ) {
  282. var deltaAngle = this.aEndAngle - this.aStartAngle;
  283. if ( !this.aClockwise ) {
  284. t = 1 - t;
  285. }
  286. var angle = this.aStartAngle + t * deltaAngle;
  287. var tx = this.aX + this.xRadius * Math.cos( angle );
  288. var ty = this.aY + this.yRadius * Math.sin( angle );
  289. return new THREE.Vector2( tx, ty );
  290. };
  291. /**************************************************************
  292. * Arc curve
  293. **************************************************************/
  294. THREE.ArcCurve = function ( aX, aY, aRadius,
  295. aStartAngle, aEndAngle,
  296. aClockwise ) {
  297. THREE.EllipseCurve.call(this, aX, aY, aRadius, aRadius,
  298. aStartAngle, aEndAngle, aClockwise);
  299. };
  300. THREE.ArcCurve.prototype = new THREE.EllipseCurve();
  301. THREE.ArcCurve.prototype.constructor = THREE.ArcCurve;
  302. /**************************************************************
  303. * Utils
  304. **************************************************************/
  305. THREE.Curve.Utils = {
  306. tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
  307. return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
  308. },
  309. // Puay Bing, thanks for helping with this derivative!
  310. tangentCubicBezier: function (t, p0, p1, p2, p3 ) {
  311. return -3 * p0 * (1 - t) * (1 - t) +
  312. 3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) +
  313. 6 * t * p2 * (1-t) - 3 * t * t * p2 +
  314. 3 * t * t * p3;
  315. },
  316. tangentSpline: function ( t, p0, p1, p2, p3 ) {
  317. // To check if my formulas are correct
  318. var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1
  319. var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t
  320. var h01 = -6 * t * t + 6 * t; // − 2t3 + 3t2
  321. var h11 = 3 * t * t - 2 * t; // t3 − t2
  322. return h00 + h10 + h01 + h11;
  323. },
  324. // Catmull-Rom
  325. interpolate: function( p0, p1, p2, p3, t ) {
  326. var v0 = ( p2 - p0 ) * 0.5;
  327. var v1 = ( p3 - p1 ) * 0.5;
  328. var t2 = t * t;
  329. var t3 = t * t2;
  330. return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
  331. }
  332. };
  333. // TODO: Transformation for Curves?
  334. /**************************************************************
  335. * 3D Curves
  336. **************************************************************/
  337. // A Factory method for creating new curve subclasses
  338. THREE.Curve.create = function( constructor, getPointFunc ) {
  339. var subClass = constructor;
  340. subClass.prototype = new THREE.Curve();
  341. subClass.prototype.constructor = constructor;
  342. subClass.prototype.getPoint = getPointFunc;
  343. return subClass;
  344. };
  345. /**************************************************************
  346. * Line3D
  347. **************************************************************/
  348. THREE.LineCurve3 = THREE.Curve.create(
  349. function ( v1, v2 ) {
  350. this.v1 = v1;
  351. this.v2 = v2;
  352. },
  353. function ( t ) {
  354. var r = new THREE.Vector3();
  355. r.sub( this.v2, this.v1 ); // diff
  356. r.multiplyScalar( t );
  357. r.addSelf( this.v1 );
  358. return r;
  359. }
  360. );
  361. /**************************************************************
  362. * Quadratic Bezier 3D curve
  363. **************************************************************/
  364. THREE.QuadraticBezierCurve3 = THREE.Curve.create(
  365. function ( v0, v1, v2 ) {
  366. this.v0 = v0;
  367. this.v1 = v1;
  368. this.v2 = v2;
  369. },
  370. function ( t ) {
  371. var tx, ty, tz;
  372. tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
  373. ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
  374. tz = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z );
  375. return new THREE.Vector3( tx, ty, tz );
  376. }
  377. );
  378. /**************************************************************
  379. * Cubic Bezier 3D curve
  380. **************************************************************/
  381. THREE.CubicBezierCurve3 = THREE.Curve.create(
  382. function ( v0, v1, v2, v3 ) {
  383. this.v0 = v0;
  384. this.v1 = v1;
  385. this.v2 = v2;
  386. this.v3 = v3;
  387. },
  388. function ( t ) {
  389. var tx, ty, tz;
  390. tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
  391. ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
  392. tz = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z );
  393. return new THREE.Vector3( tx, ty, tz );
  394. }
  395. );
  396. /**************************************************************
  397. * Spline 3D curve
  398. **************************************************************/
  399. THREE.SplineCurve3 = THREE.Curve.create(
  400. function ( points /* array of Vector3 */) {
  401. this.points = (points == undefined) ? [] : points;
  402. },
  403. function ( t ) {
  404. var v = new THREE.Vector3();
  405. var c = [];
  406. var points = this.points, point, intPoint, weight;
  407. point = ( points.length - 1 ) * t;
  408. intPoint = Math.floor( point );
  409. weight = point - intPoint;
  410. c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
  411. c[ 1 ] = intPoint;
  412. c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1;
  413. c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2;
  414. var pt0 = points[ c[0] ],
  415. pt1 = points[ c[1] ],
  416. pt2 = points[ c[2] ],
  417. pt3 = points[ c[3] ];
  418. v.x = THREE.Curve.Utils.interpolate(pt0.x, pt1.x, pt2.x, pt3.x, weight);
  419. v.y = THREE.Curve.Utils.interpolate(pt0.y, pt1.y, pt2.y, pt3.y, weight);
  420. v.z = THREE.Curve.Utils.interpolate(pt0.z, pt1.z, pt2.z, pt3.z, weight);
  421. return v;
  422. }
  423. );
  424. // THREE.SplineCurve3.prototype.getTangent = function(t) {
  425. // var v = new THREE.Vector3();
  426. // var c = [];
  427. // var points = this.points, point, intPoint, weight;
  428. // point = ( points.length - 1 ) * t;
  429. // intPoint = Math.floor( point );
  430. // weight = point - intPoint;
  431. // c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
  432. // c[ 1 ] = intPoint;
  433. // c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1;
  434. // c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2;
  435. // var pt0 = points[ c[0] ],
  436. // pt1 = points[ c[1] ],
  437. // pt2 = points[ c[2] ],
  438. // pt3 = points[ c[3] ];
  439. // // t = weight;
  440. // v.x = THREE.Curve.Utils.tangentSpline( t, pt0.x, pt1.x, pt2.x, pt3.x );
  441. // v.y = THREE.Curve.Utils.tangentSpline( t, pt0.y, pt1.y, pt2.y, pt3.y );
  442. // v.z = THREE.Curve.Utils.tangentSpline( t, pt0.z, pt1.z, pt2.z, pt3.z );
  443. // return v;
  444. // }
  445. /**************************************************************
  446. * Closed Spline 3D curve
  447. **************************************************************/
  448. THREE.ClosedSplineCurve3 = THREE.Curve.create(
  449. function ( points /* array of Vector3 */) {
  450. this.points = (points == undefined) ? [] : points;
  451. },
  452. function ( t ) {
  453. var v = new THREE.Vector3();
  454. var c = [];
  455. var points = this.points, point, intPoint, weight;
  456. point = ( points.length - 0 ) * t;
  457. // This needs to be from 0-length +1
  458. intPoint = Math.floor( point );
  459. weight = point - intPoint;
  460. intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;
  461. c[ 0 ] = ( intPoint - 1 ) % points.length;
  462. c[ 1 ] = ( intPoint ) % points.length;
  463. c[ 2 ] = ( intPoint + 1 ) % points.length;
  464. c[ 3 ] = ( intPoint + 2 ) % points.length;
  465. v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight );
  466. v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight );
  467. v.z = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].z, points[ c[ 1 ] ].z, points[ c[ 2 ] ].z, points[ c[ 3 ] ].z, weight );
  468. return v;
  469. }
  470. );