2
0

CurvePath.js 4.5 KB

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