CatmullRomCurve3.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /**
  2. * @author zz85 https://github.com/zz85
  3. *
  4. * Centripetal CatmullRom Curve - which is useful for avoiding
  5. * cusps and self-intersections in non-uniform catmull rom curves.
  6. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
  7. *
  8. * curve.type accepts centripetal(default), chordal and catmullrom
  9. * curve.tension is used for catmullrom which defaults to 0.5
  10. */
  11. THREE.CatmullRomCurve3 = ( function() {
  12. var
  13. tmp = new THREE.Vector3(),
  14. px = new CubicPoly(),
  15. py = new CubicPoly(),
  16. pz = new CubicPoly();
  17. /*
  18. Based on an optimized c++ solution in
  19. - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/
  20. - http://ideone.com/NoEbVM
  21. This CubicPoly class could be used for reusing some variables and calculations,
  22. but for three.js curve use, it could be possible inlined and flatten into a single function call
  23. which can be placed in CurveUtils.
  24. */
  25. function CubicPoly() {
  26. }
  27. /*
  28. * Compute coefficients for a cubic polynomial
  29. * p(s) = c0 + c1*s + c2*s^2 + c3*s^3
  30. * such that
  31. * p(0) = x0, p(1) = x1
  32. * and
  33. * p'(0) = t0, p'(1) = t1.
  34. */
  35. CubicPoly.prototype.init = function( x0, x1, t0, t1 ) {
  36. this.c0 = x0;
  37. this.c1 = t0;
  38. this.c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1;
  39. this.c3 = 2 * x0 - 2 * x1 + t0 + t1;
  40. };
  41. CubicPoly.prototype.initNonuniformCatmullRom = function( x0, x1, x2, x3, dt0, dt1, dt2 ) {
  42. // compute tangents when parameterized in [t1,t2]
  43. var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1;
  44. var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2;
  45. // rescale tangents for parametrization in [0,1]
  46. t1 *= dt1;
  47. t2 *= dt1;
  48. // initCubicPoly
  49. this.init( x1, x2, t1, t2 );
  50. };
  51. // standard Catmull-Rom spline: interpolate between x1 and x2 with previous/following points x1/x4
  52. CubicPoly.prototype.initCatmullRom = function( x0, x1, x2, x3, tension ) {
  53. this.init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) );
  54. };
  55. CubicPoly.prototype.calc = function( t ) {
  56. var t2 = t * t;
  57. var t3 = t2 * t;
  58. return this.c0 + this.c1 * t + this.c2 * t2 + this.c3 * t3;
  59. };
  60. // Subclass Three.js curve
  61. return THREE.Curve.create(
  62. function ( p /* array of Vector3 */ ) {
  63. this.points = p || [];
  64. },
  65. function ( t ) {
  66. var points = this.points,
  67. point, intPoint, weight, l;
  68. l = points.length;
  69. if ( l < 2 ) console.log( 'duh, you need at least 2 points' );
  70. point = ( l - 1 ) * t;
  71. intPoint = Math.floor( point );
  72. weight = point - intPoint;
  73. if ( weight === 0 && intPoint === l - 1 ) {
  74. intPoint = l - 2;
  75. weight = 1;
  76. }
  77. var p0, p1, p2, p3;
  78. if ( intPoint === 0 ) {
  79. // extrapolate first point
  80. tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] );
  81. p0 = tmp;
  82. } else {
  83. p0 = points[ intPoint - 1 ];
  84. }
  85. p1 = points[ intPoint ];
  86. p2 = points[ intPoint + 1 ];
  87. if ( intPoint + 2 < l ) {
  88. p3 = points[ intPoint + 2 ]
  89. } else {
  90. // extrapolate last point
  91. tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 2 ] );
  92. p3 = tmp;
  93. }
  94. if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) {
  95. // init Centripetal / Chordal Catmull-Rom
  96. var pow = this.type === 'chordal' ? 0.5 : 0.25;
  97. var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );
  98. var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );
  99. var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );
  100. // safety check for repeated points
  101. if ( dt1 < 1e-4 ) dt1 = 1.0;
  102. if ( dt0 < 1e-4 ) dt0 = dt1;
  103. if ( dt2 < 1e-4 ) dt2 = dt1;
  104. px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 );
  105. py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );
  106. pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );
  107. } else if ( this.type === 'catmullrom' ) {
  108. var tension = this.tension !== undefined ? this.tension : 0.5;
  109. px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension );
  110. py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, tension );
  111. pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, tension );
  112. }
  113. var v = new THREE.Vector3(
  114. px.calc( weight ),
  115. py.calc( weight ),
  116. pz.calc( weight )
  117. );
  118. return v;
  119. }
  120. );
  121. } )();