CurvePath.js 4.6 KB

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