SolidGeometry.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /*
  2. * SolidGeometry.js
  3. *
  4. * @author realor
  5. */
  6. import * as THREE from '../lib/three.module.js'
  7. import { GeometryUtils } from '../utils/GeometryUtils.js'
  8. class SolidGeometry extends THREE.BufferGeometry {
  9. constructor() {
  10. super()
  11. this.type = 'SolidGeometry'
  12. this.vertices = [] // THREE.Vector3
  13. this.faces = [] // Face
  14. this.isManifold = false
  15. this.smoothAngle = 0
  16. }
  17. addFace(...vertices) {
  18. if (vertices.length < 3) {
  19. throw 'Invalid face: ' + vertices.length + ' vertices'
  20. }
  21. const face = new Face(this)
  22. const outerLoop = face.outerLoop
  23. for (let v of vertices) {
  24. outerLoop._addVertex(v)
  25. }
  26. face.updateNormal()
  27. this.faces.push(face)
  28. return face
  29. }
  30. updateFaceNormals() {
  31. for (let face of this.faces) {
  32. face.updateNormal()
  33. }
  34. }
  35. updateBuffers() {
  36. const positions = []
  37. const normals = []
  38. const vertices = this.vertices
  39. if (this.smoothAngle > 0) {
  40. const cosAngle = Math.cos(THREE.MathUtils.degToRad(this.smoothAngle))
  41. const vertexMap = new Map()
  42. const processLoopEdges = loop => {
  43. const face = loop.face
  44. for (let v of loop.indices) {
  45. let vertexFaceMap = vertexMap.get(v)
  46. if (vertexFaceMap === undefined) {
  47. vertexFaceMap = new Map()
  48. vertexMap.set(v, vertexFaceMap)
  49. }
  50. let normals = null
  51. for (let otherFace of vertexFaceMap.keys()) {
  52. if (otherFace.normal.dot(face.normal) > cosAngle) {
  53. normals = vertexFaceMap.get(otherFace)
  54. break
  55. }
  56. }
  57. if (normals === null) normals = []
  58. normals.push(face.normal)
  59. vertexFaceMap.set(face, normals)
  60. }
  61. }
  62. for (let face of this.faces) {
  63. if (face.normal === null) face.updateNormal()
  64. processLoopEdges(face.outerLoop)
  65. for (let hole of face.holes) {
  66. processLoopEdges(hole)
  67. }
  68. }
  69. for (let vertexFaceMap of vertexMap.values()) {
  70. for (let face of vertexFaceMap.keys()) {
  71. let smoothNormal = null
  72. let normals = vertexFaceMap.get(face)
  73. if (normals.length === 1) {
  74. smoothNormal = normals[0]
  75. } else {
  76. smoothNormal = new THREE.Vector3()
  77. for (let normal of normals) {
  78. smoothNormal.add(normal)
  79. }
  80. smoothNormal.normalize()
  81. }
  82. vertexFaceMap.set(face, smoothNormal)
  83. }
  84. }
  85. for (let face of this.faces) {
  86. let triangles = face.getTriangles()
  87. for (let triangle of triangles) {
  88. for (let i = 0; i < 3; i++) {
  89. let v = triangle[i]
  90. let vertex = vertices[v]
  91. let normal = vertexMap.get(v).get(face)
  92. positions.push(vertex.x, vertex.y, vertex.z)
  93. normals.push(normal.x, normal.y, normal.z)
  94. }
  95. }
  96. }
  97. } else {
  98. for (let face of this.faces) {
  99. if (face.normal === null) face.updateNormal()
  100. let normal = face.normal
  101. let triangles = face.getTriangles()
  102. for (let triangle of triangles) {
  103. for (let i = 0; i < 3; i++) {
  104. let vertex = vertices[triangle[i]]
  105. positions.push(vertex.x, vertex.y, vertex.z)
  106. normals.push(normal.x, normal.y, normal.z)
  107. }
  108. }
  109. }
  110. }
  111. this.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3))
  112. this.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3))
  113. this.setIndex(null)
  114. return this
  115. }
  116. applyMatrix4(matrix) {
  117. for (let vertex of this.vertices) {
  118. vertex.applyMatrix4(matrix)
  119. }
  120. this.updateFaceNormals()
  121. this.updateBuffers()
  122. if (this.boundingBox !== null) {
  123. this.computeBoundingBox()
  124. }
  125. if (this.boundingSphere !== null) {
  126. this.computeBoundingSphere()
  127. }
  128. return this
  129. }
  130. copy(geometry) {
  131. if (geometry instanceof SolidGeometry) {
  132. this.vertices = []
  133. for (let v = 0; v < geometry.vertices.length; v++) {
  134. let vertex = geometry.vertices[v].clone()
  135. this.vertices.push(vertex)
  136. }
  137. this.faces = []
  138. for (let f = 0; f < geometry.faces.length; f++) {
  139. let face = geometry.faces[f]
  140. let newFace = new Face(this)
  141. newFace.indices = [...face.indices]
  142. newFace.normal = face.normal ? face.normal.clone() : null
  143. for (let hole of face.holes) {
  144. let newHole = new Loop(newFace)
  145. newHole.indices = [...hole.indices]
  146. newFace.holes.push(newHole)
  147. }
  148. this.faces.push(newFace)
  149. }
  150. this.isManifold = geometry.isManifold
  151. this.smoothAngle = geometry.smoothAngle
  152. } else if (geometry instanceof THREE.BufferGeometry) {
  153. this.vertices = GeometryUtils.getBufferGeometryVertices(geometry)
  154. const addFace = (va, vb, vc) => {
  155. this.addFace(va, vb, vc)
  156. }
  157. GeometryUtils.getBufferGeometryFaces(geometry, addFace)
  158. this.isManifold = false
  159. this.smoothAngle = 0
  160. }
  161. return this
  162. }
  163. clone() {
  164. const geometry = new SolidGeometry()
  165. geometry.copy(this)
  166. geometry.updateBuffers()
  167. return geometry
  168. }
  169. getEdgesGeometry() {
  170. const edges = new Set()
  171. let edgePositions = []
  172. for (let face of this.faces) {
  173. face.outerLoop.forEachEdge((v1, v2, position1, position2) => {
  174. let key = Math.min(v1, v2) + '/' + Math.max(v1, v2)
  175. if (!edges.has(key)) {
  176. edgePositions.push(position1, position2)
  177. edges.add(key)
  178. }
  179. })
  180. for (let hole of face.holes) {
  181. hole.forEachEdge((v1, v2, position1, position2) => {
  182. let key = Math.min(v1, v2) + '/' + Math.max(v1, v2)
  183. if (!edges.has(key)) {
  184. edgePositions.push(position1, position2)
  185. edges.add(key)
  186. }
  187. })
  188. }
  189. }
  190. let edgesGeometry = new THREE.BufferGeometry()
  191. edgesGeometry.setFromPoints(edgePositions)
  192. return edgesGeometry
  193. }
  194. getTriangleCount() {
  195. let triangleCount = 0
  196. for (let face of this.faces) {
  197. triangleCount += face.getTriangles().length
  198. }
  199. return triangleCount
  200. }
  201. getTrianglesGeometry() {
  202. const edges = new Set()
  203. const vertices = this.vertices
  204. let edgePositions = []
  205. for (let face of this.faces) {
  206. let triangles = face.getTriangles()
  207. for (let triangle of triangles) {
  208. for (let i = 0; i < 3; i++) {
  209. let v1 = triangle[i]
  210. let v2 = triangle[(i + 1) % 3]
  211. let key = Math.min(v1, v2) + '/' + Math.max(v1, v2)
  212. if (!edges.has(key)) {
  213. edgePositions.push(vertices[v1], vertices[v2])
  214. edges.add(key)
  215. }
  216. }
  217. }
  218. }
  219. let trianglesGeometry = new THREE.BufferGeometry()
  220. trianglesGeometry.setFromPoints(edgePositions)
  221. return trianglesGeometry
  222. }
  223. }
  224. /* Face */
  225. class Face {
  226. constructor(
  227. geometry // SolidGeometry
  228. ) {
  229. this.geometry = geometry
  230. this.outerLoop = new Loop(this)
  231. this.holes = [] // array of Loop
  232. this.normal = null
  233. this.triangles = null
  234. }
  235. getVertex(pos) {
  236. return this.outerLoop.getVertex(pos)
  237. }
  238. getVertexCount() {
  239. return this.outerLoop.indices.length
  240. }
  241. getVertices() {
  242. return this.outerLoop.getVertices()
  243. }
  244. get indices() {
  245. return this.outerLoop.indices
  246. }
  247. set indices(indices) {
  248. this.outerLoop.indices = indices
  249. }
  250. addHole(...vertices) {
  251. if (vertices.length < 3) {
  252. throw 'Invalid hole: ' + vertices.length + ' vertices'
  253. }
  254. const hole = new Loop(this)
  255. for (let v of vertices) {
  256. hole._addVertex(v)
  257. }
  258. this.holes.push(hole)
  259. this.triangles = null
  260. return hole
  261. }
  262. getHole(index) {
  263. return this.holes[index]
  264. }
  265. get holeCount() {
  266. return this.holes.length
  267. }
  268. updateNormal() {
  269. const indices = this.indices
  270. if (indices.length >= 3) {
  271. let normal = new THREE.Vector3()
  272. let vertexCount = indices.length
  273. let pi, pj
  274. for (let i = 0; i < vertexCount; i++) {
  275. let j = (i + 1) % vertexCount
  276. pi = this.getVertex(i)
  277. pj = this.getVertex(j)
  278. normal.x += (pi.y - pj.y) * (pi.z + pj.z)
  279. normal.y += (pi.z - pj.z) * (pi.x + pj.x)
  280. normal.z += (pi.x - pj.x) * (pi.y + pj.y)
  281. }
  282. normal.normalize()
  283. this.normal = normal
  284. }
  285. }
  286. getTriangles() {
  287. if (this.triangles === null) {
  288. this.updateTriangles()
  289. }
  290. return this.triangles
  291. }
  292. isConvex() {
  293. let v12 = new THREE.Vector3()
  294. let v13 = new THREE.Vector3()
  295. if (this.normal === null) {
  296. this.updateNormal()
  297. }
  298. const indices = this.outerLoop.indices
  299. const vertices = this.geometry.vertices
  300. if (indices.length === 3) return true
  301. for (let i = 0; i < indices.length; i++) {
  302. let v1 = indices[i]
  303. let v2 = indices[(i + 1) % indices.length]
  304. let v3 = indices[(i + 2) % indices.length]
  305. let vertex1 = vertices[v1]
  306. let vertex2 = vertices[v2]
  307. let vertex3 = vertices[v3]
  308. v12.subVectors(vertex2, vertex1)
  309. v13.subVectors(vertex3, vertex1)
  310. if (v12.cross(v13).dot(this.normal) < 0) return false
  311. }
  312. return true
  313. }
  314. getArea() {
  315. let area = 0
  316. const triangle = new THREE.Triangle()
  317. const vertices = this.geometry.vertices
  318. let triangles = this.getTriangles()
  319. for (let tri of triangles) {
  320. let vertex0 = vertices[tri[0]]
  321. let vertex1 = vertices[tri[1]]
  322. let vertex2 = vertices[tri[2]]
  323. triangle.a.copy(vertex0)
  324. triangle.b.copy(vertex1)
  325. triangle.c.copy(vertex2)
  326. area += triangle.getArea()
  327. }
  328. return area
  329. }
  330. updateTriangles() {
  331. if (this.indices.length === 3 && this.holes.length === 0) {
  332. let a = this.indices[0]
  333. let b = this.indices[1]
  334. let c = this.indices[2]
  335. this.triangles = [[a, b, c]]
  336. } else {
  337. const vertices = this.geometry.vertices
  338. const faceIndices = []
  339. const outerVertices = this.indices.map(v => vertices[v])
  340. faceIndices.push(...this.indices)
  341. const innerVertices = []
  342. for (let hole of this.holes) {
  343. innerVertices.push(hole.indices.map(v => vertices[v]))
  344. faceIndices.push(...hole.indices)
  345. }
  346. this.triangles = GeometryUtils.triangulateFace(outerVertices, innerVertices, this.normal)
  347. for (let triangle of this.triangles) {
  348. for (let i = 0; i < 3; i++) {
  349. triangle[i] = faceIndices[triangle[i]]
  350. }
  351. }
  352. }
  353. }
  354. }
  355. /* Loop */
  356. class Loop {
  357. constructor(face) {
  358. this.face = face
  359. this.indices = []
  360. }
  361. getVertex(pos) {
  362. const vertices = this.face.geometry.vertices
  363. let index = this.indices[pos]
  364. return vertices[index]
  365. }
  366. getVertexCount() {
  367. return this.indices.length
  368. }
  369. getVertices() {
  370. let vertices = []
  371. for (let pos = 0; pos < this.indices.length; pos++) {
  372. vertices.push(this.getVertex(pos))
  373. }
  374. return vertices
  375. }
  376. forEachEdge(
  377. fn // fn(v1, v2, position1, position2)
  378. ) {
  379. let vertices = this.face.geometry.vertices
  380. for (let i = 0; i < this.indices.length; i++) {
  381. let v1 = this.indices[i]
  382. let v2 = this.indices[(i + 1) % this.indices.length]
  383. let position1 = vertices[v1]
  384. let position2 = vertices[v2]
  385. fn(v1, v2, position1, position2)
  386. }
  387. }
  388. _addVertex(vertex) {
  389. const vertices = this.face.geometry.vertices
  390. let vertexCount = vertices.length
  391. if (typeof vertex === 'number') {
  392. if (vertex >= 0 && vertex < vertexCount) {
  393. this.indices.push(vertex)
  394. } else console.warn('Invalid vertex index: ' + vertex)
  395. } else if (vertex instanceof THREE.Vector3) {
  396. vertices.push(vertex)
  397. this.indices.push(vertexCount)
  398. }
  399. this.face.triangles = null
  400. return vertexCount
  401. }
  402. }
  403. export { SolidGeometry, Face, Loop }