ConvexGeometry.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /**
  2. * @author qiao / https://github.com/qiao
  3. * @fileoverview This is a convex hull generator using the incremental method.
  4. * The complexity is O(n^2) where n is the number of vertices.
  5. * O(nlogn) algorithms do exist, but they are much more complicated.
  6. *
  7. * Benchmark:
  8. *
  9. * Platform: CPU: P7350 @2.00GHz Engine: V8
  10. *
  11. * Num Vertices Time(ms)
  12. *
  13. * 10 1
  14. * 20 3
  15. * 30 19
  16. * 40 48
  17. * 50 107
  18. */
  19. THREE.ConvexGeometry = function( vertices ) {
  20. THREE.Geometry.call( this );
  21. var faces = [ [ 0, 1, 2 ], [ 0, 2, 1 ] ];
  22. for ( var i = 3; i < vertices.length; i++ ) {
  23. addPoint( i );
  24. }
  25. function addPoint( vertexId ) {
  26. var vertex = vertices[ vertexId ].clone();
  27. var mag = vertex.length();
  28. vertex.x += mag * randomOffset();
  29. vertex.y += mag * randomOffset();
  30. vertex.z += mag * randomOffset();
  31. var hole = [];
  32. for ( var f = 0; f < faces.length; ) {
  33. var face = faces[ f ];
  34. // for each face, if the vertex can see it,
  35. // then we try to add the face's edges into the hole.
  36. if ( visible( face, vertex ) ) {
  37. for ( var e = 0; e < 3; e++ ) {
  38. var edge = [ face[ e ], face[ ( e + 1 ) % 3 ] ];
  39. var boundary = true;
  40. // remove duplicated edges.
  41. for ( var h = 0; h < hole.length; h++ ) {
  42. if ( equalEdge( hole[ h ], edge ) ) {
  43. hole[ h ] = hole[ hole.length - 1 ];
  44. hole.pop();
  45. boundary = false;
  46. break;
  47. }
  48. }
  49. if ( boundary ) {
  50. hole.push( edge );
  51. }
  52. }
  53. // remove faces[ f ]
  54. faces[ f ] = faces[ faces.length - 1 ];
  55. faces.pop();
  56. } else { // not visible
  57. f++;
  58. }
  59. }
  60. // construct the new faces formed by the edges of the hole and the vertex
  61. for ( var h = 0; h < hole.length; h++ ) {
  62. faces.push( [
  63. hole[ h ][ 0 ],
  64. hole[ h ][ 1 ],
  65. vertexId
  66. ] );
  67. }
  68. }
  69. /**
  70. * Whether the face is visible from the vertex
  71. */
  72. function visible( face, vertex ) {
  73. var va = vertices[ face[ 0 ] ];
  74. var vb = vertices[ face[ 1 ] ];
  75. var vc = vertices[ face[ 2 ] ];
  76. var n = normal( va, vb, vc );
  77. // distance from face to origin
  78. var dist = n.dot( va );
  79. return n.dot( vertex ) >= dist;
  80. }
  81. /**
  82. * Face normal
  83. */
  84. function normal( va, vb, vc ) {
  85. var cb = new THREE.Vector3();
  86. var ab = new THREE.Vector3();
  87. cb.sub( vc, vb );
  88. ab.sub( va, vb );
  89. cb.crossSelf( ab );
  90. if ( !cb.isZero() ) {
  91. cb.normalize();
  92. }
  93. return cb;
  94. }
  95. /**
  96. * Detect whether two edges are equal.
  97. * Note that when constructing the convex hull, two same edges can only
  98. * be of the negative direction.
  99. */
  100. function equalEdge( ea, eb ) {
  101. return ea[ 0 ] === eb[ 1 ] && ea[ 1 ] === eb[ 0 ];
  102. }
  103. /**
  104. * Create a random offset between -1e-6 and 1e-6.
  105. */
  106. function randomOffset() {
  107. return ( Math.random() - 0.5 ) * 2 * 1e-6;
  108. }
  109. /**
  110. * XXX: Not sure if this is the correct approach. Need someone to review.
  111. */
  112. function vertexUv( vertex ) {
  113. var mag = vertex.length();
  114. return new THREE.UV( vertex.x / mag, vertex.y / mag );
  115. }
  116. // Push vertices into `this.vertices`, skipping those inside the hull
  117. var id = 0;
  118. var newId = new Array( vertices.length ); // map from old vertex id to new id
  119. for ( var i = 0; i < faces.length; i++ ) {
  120. var face = faces[ i ];
  121. for ( var j = 0; j < 3; j++ ) {
  122. if ( newId[ face[ j ] ] === undefined ) {
  123. newId[ face[ j ] ] = id++;
  124. this.vertices.push( vertices[ face[ j ] ] );
  125. }
  126. face[ j ] = newId[ face[ j ] ];
  127. }
  128. }
  129. // Convert faces into instances of THREE.Face3
  130. for ( var i = 0; i < faces.length; i++ ) {
  131. this.faces.push( new THREE.Face3(
  132. faces[ i ][ 0 ],
  133. faces[ i ][ 1 ],
  134. faces[ i ][ 2 ]
  135. ) );
  136. }
  137. // Compute UVs
  138. for ( var i = 0; i < this.faces.length; i++ ) {
  139. var face = this.faces[ i ];
  140. this.faceVertexUvs[ 0 ].push( [
  141. vertexUv( this.vertices[ face.a ] ),
  142. vertexUv( this.vertices[ face.b ] ),
  143. vertexUv( this.vertices[ face.c ])
  144. ] );
  145. }
  146. this.computeCentroids();
  147. this.computeFaceNormals();
  148. this.computeVertexNormals();
  149. };
  150. THREE.ConvexGeometry.prototype = Object.create( THREE.Geometry.prototype );