Line.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. import { Sphere } from '../math/Sphere.js';
  2. import { Ray } from '../math/Ray.js';
  3. import { Matrix4 } from '../math/Matrix4.js';
  4. import { Object3D } from '../core/Object3D.js';
  5. import { Vector3 } from '../math/Vector3.js';
  6. import { LineBasicMaterial } from '../materials/LineBasicMaterial.js';
  7. import { BufferGeometry } from '../core/BufferGeometry.js';
  8. import { Float32BufferAttribute } from '../core/BufferAttribute.js';
  9. import { warn } from '../utils.js';
  10. const _vStart = /*@__PURE__*/ new Vector3();
  11. const _vEnd = /*@__PURE__*/ new Vector3();
  12. const _inverseMatrix = /*@__PURE__*/ new Matrix4();
  13. const _ray = /*@__PURE__*/ new Ray();
  14. const _sphere = /*@__PURE__*/ new Sphere();
  15. const _intersectPointOnRay = /*@__PURE__*/ new Vector3();
  16. const _intersectPointOnSegment = /*@__PURE__*/ new Vector3();
  17. /**
  18. * A continuous line. The line are rendered by connecting consecutive
  19. * vertices with straight lines.
  20. *
  21. * ```js
  22. * const material = new THREE.LineBasicMaterial( { color: 0x0000ff } );
  23. *
  24. * const points = [];
  25. * points.push( new THREE.Vector3( - 10, 0, 0 ) );
  26. * points.push( new THREE.Vector3( 0, 10, 0 ) );
  27. * points.push( new THREE.Vector3( 10, 0, 0 ) );
  28. *
  29. * const geometry = new THREE.BufferGeometry().setFromPoints( points );
  30. *
  31. * const line = new THREE.Line( geometry, material );
  32. * scene.add( line );
  33. * ```
  34. *
  35. * @augments Object3D
  36. */
  37. class Line extends Object3D {
  38. /**
  39. * Constructs a new line.
  40. *
  41. * @param {BufferGeometry} [geometry] - The line geometry.
  42. * @param {Material|Array<Material>} [material] - The line material.
  43. */
  44. constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) {
  45. super();
  46. /**
  47. * This flag can be used for type testing.
  48. *
  49. * @type {boolean}
  50. * @readonly
  51. * @default true
  52. */
  53. this.isLine = true;
  54. this.type = 'Line';
  55. /**
  56. * The line geometry.
  57. *
  58. * @type {BufferGeometry}
  59. */
  60. this.geometry = geometry;
  61. /**
  62. * The line material.
  63. *
  64. * @type {Material|Array<Material>}
  65. * @default LineBasicMaterial
  66. */
  67. this.material = material;
  68. /**
  69. * A dictionary representing the morph targets in the geometry. The key is the
  70. * morph targets name, the value its attribute index. This member is `undefined`
  71. * by default and only set when morph targets are detected in the geometry.
  72. *
  73. * @type {Object<String,number>|undefined}
  74. * @default undefined
  75. */
  76. this.morphTargetDictionary = undefined;
  77. /**
  78. * An array of weights typically in the range `[0,1]` that specify how much of the morph
  79. * is applied. This member is `undefined` by default and only set when morph targets are
  80. * detected in the geometry.
  81. *
  82. * @type {Array<number>|undefined}
  83. * @default undefined
  84. */
  85. this.morphTargetInfluences = undefined;
  86. this.updateMorphTargets();
  87. }
  88. copy( source, recursive ) {
  89. super.copy( source, recursive );
  90. this.material = Array.isArray( source.material ) ? source.material.slice() : source.material;
  91. this.geometry = source.geometry;
  92. return this;
  93. }
  94. /**
  95. * Computes an array of distance values which are necessary for rendering dashed lines.
  96. * For each vertex in the geometry, the method calculates the cumulative length from the
  97. * current point to the very beginning of the line.
  98. *
  99. * @return {Line} A reference to this line.
  100. */
  101. computeLineDistances() {
  102. const geometry = this.geometry;
  103. // we assume non-indexed geometry
  104. if ( geometry.index === null ) {
  105. const positionAttribute = geometry.attributes.position;
  106. const lineDistances = [ 0 ];
  107. for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) {
  108. _vStart.fromBufferAttribute( positionAttribute, i - 1 );
  109. _vEnd.fromBufferAttribute( positionAttribute, i );
  110. lineDistances[ i ] = lineDistances[ i - 1 ];
  111. lineDistances[ i ] += _vStart.distanceTo( _vEnd );
  112. }
  113. geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) );
  114. } else {
  115. warn( 'Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );
  116. }
  117. return this;
  118. }
  119. /**
  120. * Computes intersection points between a casted ray and this line.
  121. *
  122. * @param {Raycaster} raycaster - The raycaster.
  123. * @param {Array<Object>} intersects - The target array that holds the intersection points.
  124. */
  125. raycast( raycaster, intersects ) {
  126. const geometry = this.geometry;
  127. const matrixWorld = this.matrixWorld;
  128. const threshold = raycaster.params.Line.threshold;
  129. const drawRange = geometry.drawRange;
  130. // Checking boundingSphere distance to ray
  131. if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
  132. _sphere.copy( geometry.boundingSphere );
  133. _sphere.applyMatrix4( matrixWorld );
  134. _sphere.radius += threshold;
  135. if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return;
  136. //
  137. _inverseMatrix.copy( matrixWorld ).invert();
  138. _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix );
  139. const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );
  140. const localThresholdSq = localThreshold * localThreshold;
  141. const step = this.isLineSegments ? 2 : 1;
  142. const index = geometry.index;
  143. const attributes = geometry.attributes;
  144. const positionAttribute = attributes.position;
  145. if ( index !== null ) {
  146. const start = Math.max( 0, drawRange.start );
  147. const end = Math.min( index.count, ( drawRange.start + drawRange.count ) );
  148. for ( let i = start, l = end - 1; i < l; i += step ) {
  149. const a = index.getX( i );
  150. const b = index.getX( i + 1 );
  151. const intersect = checkIntersection( this, raycaster, _ray, localThresholdSq, a, b, i );
  152. if ( intersect ) {
  153. intersects.push( intersect );
  154. }
  155. }
  156. if ( this.isLineLoop ) {
  157. const a = index.getX( end - 1 );
  158. const b = index.getX( start );
  159. const intersect = checkIntersection( this, raycaster, _ray, localThresholdSq, a, b, end - 1 );
  160. if ( intersect ) {
  161. intersects.push( intersect );
  162. }
  163. }
  164. } else {
  165. const start = Math.max( 0, drawRange.start );
  166. const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) );
  167. for ( let i = start, l = end - 1; i < l; i += step ) {
  168. const intersect = checkIntersection( this, raycaster, _ray, localThresholdSq, i, i + 1, i );
  169. if ( intersect ) {
  170. intersects.push( intersect );
  171. }
  172. }
  173. if ( this.isLineLoop ) {
  174. const intersect = checkIntersection( this, raycaster, _ray, localThresholdSq, end - 1, start, end - 1 );
  175. if ( intersect ) {
  176. intersects.push( intersect );
  177. }
  178. }
  179. }
  180. }
  181. /**
  182. * Sets the values of {@link Line#morphTargetDictionary} and {@link Line#morphTargetInfluences}
  183. * to make sure existing morph targets can influence this 3D object.
  184. */
  185. updateMorphTargets() {
  186. const geometry = this.geometry;
  187. const morphAttributes = geometry.morphAttributes;
  188. const keys = Object.keys( morphAttributes );
  189. if ( keys.length > 0 ) {
  190. const morphAttribute = morphAttributes[ keys[ 0 ] ];
  191. if ( morphAttribute !== undefined ) {
  192. this.morphTargetInfluences = [];
  193. this.morphTargetDictionary = {};
  194. for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {
  195. const name = morphAttribute[ m ].name || String( m );
  196. this.morphTargetInfluences.push( 0 );
  197. this.morphTargetDictionary[ name ] = m;
  198. }
  199. }
  200. }
  201. }
  202. }
  203. function checkIntersection( object, raycaster, ray, thresholdSq, a, b, i ) {
  204. const positionAttribute = object.geometry.attributes.position;
  205. _vStart.fromBufferAttribute( positionAttribute, a );
  206. _vEnd.fromBufferAttribute( positionAttribute, b );
  207. const distSq = ray.distanceSqToSegment( _vStart, _vEnd, _intersectPointOnRay, _intersectPointOnSegment );
  208. if ( distSq > thresholdSq ) return;
  209. _intersectPointOnRay.applyMatrix4( object.matrixWorld ); // Move back to world space for distance calculation
  210. const distance = raycaster.ray.origin.distanceTo( _intersectPointOnRay );
  211. if ( distance < raycaster.near || distance > raycaster.far ) return;
  212. return {
  213. distance: distance,
  214. // What do we want? intersection point on the ray or on the segment??
  215. // point: raycaster.ray.at( distance ),
  216. point: _intersectPointOnSegment.clone().applyMatrix4( object.matrixWorld ),
  217. index: i,
  218. face: null,
  219. faceIndex: null,
  220. barycoord: null,
  221. object: object
  222. };
  223. }
  224. export { Line };
粤ICP备19079148号