CurvePath.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import { Curve } from './Curve';
  2. import { Vector3 } from '../../math/Vector3';
  3. import { Geometry } from '../../core/Geometry';
  4. import { LineCurve } from '../curves/LineCurve';
  5. /**
  6. * @author zz85 / http://www.lab4games.net/zz85/blog
  7. *
  8. **/
  9. /**************************************************************
  10. * Curved Path - a curve path is simply a array of connected
  11. * curves, but retains the api of a curve
  12. **************************************************************/
  13. function CurvePath() {
  14. this.curves = [];
  15. this.autoClose = false; // Automatically closes the path
  16. };
  17. CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), {
  18. constructor: CurvePath,
  19. add: function ( curve ) {
  20. this.curves.push( curve );
  21. },
  22. closePath: function () {
  23. // Add a line curve if start and end of lines are not connected
  24. var startPoint = this.curves[ 0 ].getPoint( 0 );
  25. var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 );
  26. if ( ! startPoint.equals( endPoint ) ) {
  27. this.curves.push( new LineCurve( endPoint, startPoint ) );
  28. }
  29. },
  30. // To get accurate point with reference to
  31. // entire path distance at time t,
  32. // following has to be done:
  33. // 1. Length of each sub path have to be known
  34. // 2. Locate and identify type of curve
  35. // 3. Get t for the curve
  36. // 4. Return curve.getPointAt(t')
  37. getPoint: function ( t ) {
  38. var d = t * this.getLength();
  39. var curveLengths = this.getCurveLengths();
  40. var i = 0;
  41. // To think about boundaries points.
  42. while ( i < curveLengths.length ) {
  43. if ( curveLengths[ i ] >= d ) {
  44. var diff = curveLengths[ i ] - d;
  45. var curve = this.curves[ i ];
  46. var segmentLength = curve.getLength();
  47. var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength;
  48. return curve.getPointAt( u );
  49. }
  50. i ++;
  51. }
  52. return null;
  53. // loop where sum != 0, sum > d , sum+1 <d
  54. },
  55. // We cannot use the default THREE.Curve getPoint() with getLength() because in
  56. // THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath
  57. // getPoint() depends on getLength
  58. getLength: function () {
  59. var lens = this.getCurveLengths();
  60. return lens[ lens.length - 1 ];
  61. },
  62. // cacheLengths must be recalculated.
  63. updateArcLengths: function () {
  64. this.needsUpdate = true;
  65. this.cacheLengths = null;
  66. this.getLengths();
  67. },
  68. // Compute lengths and cache them
  69. // We cannot overwrite getLengths() because UtoT mapping uses it.
  70. getCurveLengths: function () {
  71. // We use cache values if curves and cache array are same length
  72. if ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) {
  73. return this.cacheLengths;
  74. }
  75. // Get length of sub-curve
  76. // Push sums into cached array
  77. var lengths = [], sums = 0;
  78. for ( var i = 0, l = this.curves.length; i < l; i ++ ) {
  79. sums += this.curves[ i ].getLength();
  80. lengths.push( sums );
  81. }
  82. this.cacheLengths = lengths;
  83. return lengths;
  84. },
  85. getSpacedPoints: function ( divisions ) {
  86. if ( ! divisions ) divisions = 40;
  87. var points = [];
  88. for ( var i = 0; i <= divisions; i ++ ) {
  89. points.push( this.getPoint( i / divisions ) );
  90. }
  91. if ( this.autoClose ) {
  92. points.push( points[ 0 ] );
  93. }
  94. return points;
  95. },
  96. getPoints: function ( divisions ) {
  97. divisions = divisions || 12;
  98. var points = [], last;
  99. for ( var i = 0, curves = this.curves; i < curves.length; i ++ ) {
  100. var curve = curves[ i ];
  101. var resolution = (curve && curve.isEllipseCurve) ? divisions * 2
  102. : (curve && curve.isLineCurve) ? 1
  103. : (curve && curve.isSplineCurve) ? divisions * curve.points.length
  104. : divisions;
  105. var pts = curve.getPoints( resolution );
  106. for ( var j = 0; j < pts.length; j++ ) {
  107. var point = pts[ j ];
  108. if ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates
  109. points.push( point );
  110. last = point;
  111. }
  112. }
  113. if ( this.autoClose && points.length > 1 && !points[ points.length - 1 ].equals( points[ 0 ] ) ) {
  114. points.push( points[ 0 ] );
  115. }
  116. return points;
  117. },
  118. /**************************************************************
  119. * Create Geometries Helpers
  120. **************************************************************/
  121. /// Generate geometry from path points (for Line or Points objects)
  122. createPointsGeometry: function ( divisions ) {
  123. var pts = this.getPoints( divisions );
  124. return this.createGeometry( pts );
  125. },
  126. // Generate geometry from equidistant sampling along the path
  127. createSpacedPointsGeometry: function ( divisions ) {
  128. var pts = this.getSpacedPoints( divisions );
  129. return this.createGeometry( pts );
  130. },
  131. createGeometry: function ( points ) {
  132. var geometry = new Geometry();
  133. for ( var i = 0, l = points.length; i < l; i ++ ) {
  134. var point = points[ i ];
  135. geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) );
  136. }
  137. return geometry;
  138. }
  139. } );
  140. export { CurvePath };