GeometryUtils.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. /**
  2. * @author mrdoob / http://mrdoob.com/
  3. * @author alteredq / http://alteredqualia.com/
  4. */
  5. import {
  6. Mesh,
  7. Vector3
  8. } from "../../../build/three.module.js";
  9. var GeometryUtils = {
  10. // Merge two geometries or geometry and geometry from object (using object's transform)
  11. merge: function ( geometry1, geometry2, materialIndexOffset ) {
  12. console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' );
  13. var matrix;
  14. if ( geometry2 instanceof Mesh ) {
  15. geometry2.matrixAutoUpdate && geometry2.updateMatrix();
  16. matrix = geometry2.matrix;
  17. geometry2 = geometry2.geometry;
  18. }
  19. geometry1.merge( geometry2, matrix, materialIndexOffset );
  20. },
  21. // Get random point in triangle (via barycentric coordinates)
  22. // (uniform distribution)
  23. // http://www.cgafaq.info/wiki/Random_Point_In_Triangle
  24. randomPointInTriangle: function () {
  25. var vector = new Vector3();
  26. return function ( vectorA, vectorB, vectorC ) {
  27. var point = new Vector3();
  28. var a = Math.random();
  29. var b = Math.random();
  30. if ( ( a + b ) > 1 ) {
  31. a = 1 - a;
  32. b = 1 - b;
  33. }
  34. var c = 1 - a - b;
  35. point.copy( vectorA );
  36. point.multiplyScalar( a );
  37. vector.copy( vectorB );
  38. vector.multiplyScalar( b );
  39. point.add( vector );
  40. vector.copy( vectorC );
  41. vector.multiplyScalar( c );
  42. point.add( vector );
  43. return point;
  44. };
  45. }(),
  46. // Get random point in face (triangle)
  47. // (uniform distribution)
  48. randomPointInFace: function ( face, geometry ) {
  49. var vA, vB, vC;
  50. vA = geometry.vertices[ face.a ];
  51. vB = geometry.vertices[ face.b ];
  52. vC = geometry.vertices[ face.c ];
  53. return GeometryUtils.randomPointInTriangle( vA, vB, vC );
  54. },
  55. // Get uniformly distributed random points in mesh
  56. // - create array with cumulative sums of face areas
  57. // - pick random number from 0 to total area
  58. // - find corresponding place in area array by binary search
  59. // - get random point in face
  60. randomPointsInGeometry: function ( geometry, n ) {
  61. var face, i,
  62. faces = geometry.faces,
  63. vertices = geometry.vertices,
  64. il = faces.length,
  65. totalArea = 0,
  66. cumulativeAreas = [],
  67. vA, vB, vC;
  68. // precompute face areas
  69. for ( i = 0; i < il; i ++ ) {
  70. face = faces[ i ];
  71. vA = vertices[ face.a ];
  72. vB = vertices[ face.b ];
  73. vC = vertices[ face.c ];
  74. face._area = GeometryUtils.triangleArea( vA, vB, vC );
  75. totalArea += face._area;
  76. cumulativeAreas[ i ] = totalArea;
  77. }
  78. // binary search cumulative areas array
  79. function binarySearchIndices( value ) {
  80. function binarySearch( start, end ) {
  81. // return closest larger index
  82. // if exact number is not found
  83. if ( end < start )
  84. return start;
  85. var mid = start + Math.floor( ( end - start ) / 2 );
  86. if ( cumulativeAreas[ mid ] > value ) {
  87. return binarySearch( start, mid - 1 );
  88. } else if ( cumulativeAreas[ mid ] < value ) {
  89. return binarySearch( mid + 1, end );
  90. } else {
  91. return mid;
  92. }
  93. }
  94. var result = binarySearch( 0, cumulativeAreas.length - 1 );
  95. return result;
  96. }
  97. // pick random face weighted by face area
  98. var r, index,
  99. result = [];
  100. var stats = {};
  101. for ( i = 0; i < n; i ++ ) {
  102. r = Math.random() * totalArea;
  103. index = binarySearchIndices( r );
  104. result[ i ] = GeometryUtils.randomPointInFace( faces[ index ], geometry );
  105. if ( ! stats[ index ] ) {
  106. stats[ index ] = 1;
  107. } else {
  108. stats[ index ] += 1;
  109. }
  110. }
  111. return result;
  112. },
  113. randomPointsInBufferGeometry: function ( geometry, n ) {
  114. var i,
  115. vertices = geometry.attributes.position.array,
  116. totalArea = 0,
  117. cumulativeAreas = [],
  118. vA, vB, vC;
  119. // precompute face areas
  120. vA = new Vector3();
  121. vB = new Vector3();
  122. vC = new Vector3();
  123. // geometry._areas = [];
  124. var il = vertices.length / 9;
  125. for ( i = 0; i < il; i ++ ) {
  126. vA.set( vertices[ i * 9 + 0 ], vertices[ i * 9 + 1 ], vertices[ i * 9 + 2 ] );
  127. vB.set( vertices[ i * 9 + 3 ], vertices[ i * 9 + 4 ], vertices[ i * 9 + 5 ] );
  128. vC.set( vertices[ i * 9 + 6 ], vertices[ i * 9 + 7 ], vertices[ i * 9 + 8 ] );
  129. totalArea += GeometryUtils.triangleArea( vA, vB, vC );
  130. cumulativeAreas.push( totalArea );
  131. }
  132. // binary search cumulative areas array
  133. function binarySearchIndices( value ) {
  134. function binarySearch( start, end ) {
  135. // return closest larger index
  136. // if exact number is not found
  137. if ( end < start )
  138. return start;
  139. var mid = start + Math.floor( ( end - start ) / 2 );
  140. if ( cumulativeAreas[ mid ] > value ) {
  141. return binarySearch( start, mid - 1 );
  142. } else if ( cumulativeAreas[ mid ] < value ) {
  143. return binarySearch( mid + 1, end );
  144. } else {
  145. return mid;
  146. }
  147. }
  148. var result = binarySearch( 0, cumulativeAreas.length - 1 );
  149. return result;
  150. }
  151. // pick random face weighted by face area
  152. var r, index,
  153. result = [];
  154. for ( i = 0; i < n; i ++ ) {
  155. r = Math.random() * totalArea;
  156. index = binarySearchIndices( r );
  157. // result[ i ] = GeometryUtils.randomPointInFace( faces[ index ], geometry, true );
  158. vA.set( vertices[ index * 9 + 0 ], vertices[ index * 9 + 1 ], vertices[ index * 9 + 2 ] );
  159. vB.set( vertices[ index * 9 + 3 ], vertices[ index * 9 + 4 ], vertices[ index * 9 + 5 ] );
  160. vC.set( vertices[ index * 9 + 6 ], vertices[ index * 9 + 7 ], vertices[ index * 9 + 8 ] );
  161. result[ i ] = GeometryUtils.randomPointInTriangle( vA, vB, vC );
  162. }
  163. return result;
  164. },
  165. // Get triangle area (half of parallelogram)
  166. // http://mathworld.wolfram.com/TriangleArea.html
  167. triangleArea: function () {
  168. var vector1 = new Vector3();
  169. var vector2 = new Vector3();
  170. return function ( vectorA, vectorB, vectorC ) {
  171. vector1.subVectors( vectorB, vectorA );
  172. vector2.subVectors( vectorC, vectorA );
  173. vector1.cross( vector2 );
  174. return 0.5 * vector1.length();
  175. };
  176. }(),
  177. center: function ( geometry ) {
  178. console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' );
  179. return geometry.center();
  180. },
  181. /**
  182. * Generates 2D-Coordinates in a very fast way.
  183. *
  184. * @author Dylan Grafmyre
  185. *
  186. * Based on work by:
  187. * @author Thomas Diewald
  188. * @link http://www.openprocessing.org/sketch/15493
  189. *
  190. * @param center Center of Hilbert curve.
  191. * @param size Total width of Hilbert curve.
  192. * @param iterations Number of subdivisions.
  193. * @param v0 Corner index -X, -Z.
  194. * @param v1 Corner index -X, +Z.
  195. * @param v2 Corner index +X, +Z.
  196. * @param v3 Corner index +X, -Z.
  197. */
  198. hilbert2D: function ( center, size, iterations, v0, v1, v2, v3 ) {
  199. // Default Vars
  200. var center = center !== undefined ? center : new Vector3( 0, 0, 0 ),
  201. size = size !== undefined ? size : 10,
  202. half = size / 2,
  203. iterations = iterations !== undefined ? iterations : 1,
  204. v0 = v0 !== undefined ? v0 : 0,
  205. v1 = v1 !== undefined ? v1 : 1,
  206. v2 = v2 !== undefined ? v2 : 2,
  207. v3 = v3 !== undefined ? v3 : 3
  208. ;
  209. var vec_s = [
  210. new Vector3( center.x - half, center.y, center.z - half ),
  211. new Vector3( center.x - half, center.y, center.z + half ),
  212. new Vector3( center.x + half, center.y, center.z + half ),
  213. new Vector3( center.x + half, center.y, center.z - half )
  214. ];
  215. var vec = [
  216. vec_s[ v0 ],
  217. vec_s[ v1 ],
  218. vec_s[ v2 ],
  219. vec_s[ v3 ]
  220. ];
  221. // Recurse iterations
  222. if ( 0 <= -- iterations ) {
  223. var tmp = [];
  224. Array.prototype.push.apply( tmp, GeometryUtils.hilbert2D( vec[ 0 ], half, iterations, v0, v3, v2, v1 ) );
  225. Array.prototype.push.apply( tmp, GeometryUtils.hilbert2D( vec[ 1 ], half, iterations, v0, v1, v2, v3 ) );
  226. Array.prototype.push.apply( tmp, GeometryUtils.hilbert2D( vec[ 2 ], half, iterations, v0, v1, v2, v3 ) );
  227. Array.prototype.push.apply( tmp, GeometryUtils.hilbert2D( vec[ 3 ], half, iterations, v2, v1, v0, v3 ) );
  228. // Return recursive call
  229. return tmp;
  230. }
  231. // Return complete Hilbert Curve.
  232. return vec;
  233. },
  234. /**
  235. * Generates 3D-Coordinates in a very fast way.
  236. *
  237. * @author Dylan Grafmyre
  238. *
  239. * Based on work by:
  240. * @author Thomas Diewald
  241. * @link http://www.openprocessing.org/visuals/?visualID=15599
  242. *
  243. * @param center Center of Hilbert curve.
  244. * @param size Total width of Hilbert curve.
  245. * @param iterations Number of subdivisions.
  246. * @param v0 Corner index -X, +Y, -Z.
  247. * @param v1 Corner index -X, +Y, +Z.
  248. * @param v2 Corner index -X, -Y, +Z.
  249. * @param v3 Corner index -X, -Y, -Z.
  250. * @param v4 Corner index +X, -Y, -Z.
  251. * @param v5 Corner index +X, -Y, +Z.
  252. * @param v6 Corner index +X, +Y, +Z.
  253. * @param v7 Corner index +X, +Y, -Z.
  254. */
  255. hilbert3D: function ( center, size, iterations, v0, v1, v2, v3, v4, v5, v6, v7 ) {
  256. // Default Vars
  257. var center = center !== undefined ? center : new Vector3( 0, 0, 0 ),
  258. size = size !== undefined ? size : 10,
  259. half = size / 2,
  260. iterations = iterations !== undefined ? iterations : 1,
  261. v0 = v0 !== undefined ? v0 : 0,
  262. v1 = v1 !== undefined ? v1 : 1,
  263. v2 = v2 !== undefined ? v2 : 2,
  264. v3 = v3 !== undefined ? v3 : 3,
  265. v4 = v4 !== undefined ? v4 : 4,
  266. v5 = v5 !== undefined ? v5 : 5,
  267. v6 = v6 !== undefined ? v6 : 6,
  268. v7 = v7 !== undefined ? v7 : 7
  269. ;
  270. var vec_s = [
  271. new Vector3( center.x - half, center.y + half, center.z - half ),
  272. new Vector3( center.x - half, center.y + half, center.z + half ),
  273. new Vector3( center.x - half, center.y - half, center.z + half ),
  274. new Vector3( center.x - half, center.y - half, center.z - half ),
  275. new Vector3( center.x + half, center.y - half, center.z - half ),
  276. new Vector3( center.x + half, center.y - half, center.z + half ),
  277. new Vector3( center.x + half, center.y + half, center.z + half ),
  278. new Vector3( center.x + half, center.y + half, center.z - half )
  279. ];
  280. var vec = [
  281. vec_s[ v0 ],
  282. vec_s[ v1 ],
  283. vec_s[ v2 ],
  284. vec_s[ v3 ],
  285. vec_s[ v4 ],
  286. vec_s[ v5 ],
  287. vec_s[ v6 ],
  288. vec_s[ v7 ]
  289. ];
  290. // Recurse iterations
  291. if ( -- iterations >= 0 ) {
  292. var tmp = [];
  293. Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 0 ], half, iterations, v0, v3, v4, v7, v6, v5, v2, v1 ) );
  294. Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 1 ], half, iterations, v0, v7, v6, v1, v2, v5, v4, v3 ) );
  295. Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 2 ], half, iterations, v0, v7, v6, v1, v2, v5, v4, v3 ) );
  296. Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 3 ], half, iterations, v2, v3, v0, v1, v6, v7, v4, v5 ) );
  297. Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 4 ], half, iterations, v2, v3, v0, v1, v6, v7, v4, v5 ) );
  298. Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 5 ], half, iterations, v4, v3, v2, v5, v6, v1, v0, v7 ) );
  299. Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 6 ], half, iterations, v4, v3, v2, v5, v6, v1, v0, v7 ) );
  300. Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 7 ], half, iterations, v6, v5, v2, v1, v0, v3, v4, v7 ) );
  301. // Return recursive call
  302. return tmp;
  303. }
  304. // Return complete Hilbert Curve.
  305. return vec;
  306. }
  307. };
  308. export { GeometryUtils };