SolidOptimizer.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. /*
  2. * SolidOptimizer.js
  3. *
  4. * @author realor
  5. */
  6. import * as THREE from '../lib/three.module.js'
  7. import { SolidGeometry } from '../core/SolidGeometry.js'
  8. import { GeometryUtils } from '../utils/GeometryUtils.js'
  9. class SolidOptimizer {
  10. constructor(geometry) {
  11. this.inputGeometry = geometry
  12. this.outputGeometry = new SolidGeometry()
  13. this.vertices = []
  14. this.borderEdges = []
  15. this.positionMap = new Map()
  16. this.edgeMap = new Map()
  17. this.vertexMap = new Map()
  18. this.vectorMap = new Map()
  19. this.polygons = new Set()
  20. this.statistics = { edges3Faces: 0, unmergedFaces: 0 }
  21. this.vertexFactor = 10000
  22. this.vectorFactor = 1000
  23. this.normalLimit = 0.999
  24. this.vertexDistance = 0.0001
  25. this.edgeDistance = 0.0001
  26. this.minFaceArea = 0.00000001
  27. this._vector = new THREE.Vector3()
  28. }
  29. optimize() {
  30. this.createPolygonsAndEdges()
  31. this.findBorderEdges()
  32. this.splitEdges()
  33. this.updateManifold()
  34. this.mergePolygons()
  35. this.createVertexMap()
  36. this.createFaces()
  37. this.outputGeometry.smoothAngle = this.inputGeometry.smoothAngle
  38. return this.outputGeometry
  39. }
  40. createPolygonsAndEdges() {
  41. const faces = this.inputGeometry.faces
  42. const positionMap = this.positionMap
  43. const edgeMap = this.edgeMap
  44. const polygons = this.polygons
  45. for (let f = 0; f < faces.length; f++) {
  46. let face = faces[f]
  47. if (face.getArea() > this.minFaceArea) {
  48. let faceEdges = []
  49. this.processLoop(face.outerLoop, faceEdges)
  50. for (let hole of face.holes) {
  51. this.processLoop(hole, faceEdges)
  52. }
  53. if (faceEdges.length >= 3) {
  54. let polygon = new Polygon(f, face)
  55. polygons.add(polygon)
  56. for (let edge of faceEdges) {
  57. let edge2 = edgeMap.get(edge.key)
  58. if (edge2 === undefined) {
  59. // new edge
  60. edge.polygon1 = polygon
  61. edgeMap.set(edge.key, edge)
  62. } // edge already exists
  63. else {
  64. if (edge2.polygon2 === null) {
  65. if (edge2.polygon1 === polygon) {
  66. // unnecessary edge
  67. edgeMap.delete(edge.key)
  68. } else {
  69. edge2.polygon2 = polygon
  70. }
  71. } else if (edge2.polygon2 === polygon) {
  72. edge2.polygon2 = null
  73. } else {
  74. this.statistics.edges3Faces++
  75. }
  76. }
  77. }
  78. }
  79. }
  80. }
  81. }
  82. processLoop(loop, faceEdges) {
  83. const inputVertices = this.inputGeometry.vertices
  84. const indices = loop.indices
  85. let v1 = null
  86. for (let i = 0; i <= indices.length; i++) {
  87. let vertex = inputVertices[indices[i % indices.length]]
  88. let v2 = this.findVertex(vertex)
  89. if (v1 !== null && v1 !== v2) {
  90. faceEdges.push(new Edge(v1, v2))
  91. }
  92. v1 = v2
  93. }
  94. }
  95. findVertex(vertex) {
  96. const factor = this.vertexFactor
  97. const positionMap = this.positionMap
  98. const vertices = this.vertices
  99. let key = Math.round(vertex.x * factor) + '_' + Math.round(vertex.y * factor) + '_' + Math.round(vertex.z * factor)
  100. let v2 = positionMap[key]
  101. if (v2 === undefined) {
  102. v2 = this.findVertexHard(vertex)
  103. positionMap[key] = v2
  104. }
  105. return v2
  106. }
  107. findVertexHard(vertex) {
  108. const vertices = this.vertices
  109. for (let i = 0; i < vertices.length; i++) {
  110. if (vertex.distanceTo(vertices[i]) < this.vertexDistance) return i
  111. }
  112. let v = vertices.length
  113. vertices.push(vertex)
  114. return v
  115. }
  116. findBorderEdges() {
  117. const edgeMap = this.edgeMap
  118. const borderEdges = (this.borderEdges = [])
  119. for (let edge of edgeMap.values()) {
  120. if (edge.polygon2 === null) {
  121. borderEdges.push(edge)
  122. }
  123. }
  124. }
  125. updateManifold() {
  126. const edgeMap = this.edgeMap
  127. this.outputGeometry.isManifold = true
  128. for (let edge of edgeMap.values()) {
  129. if (edge.polygon2 === null) {
  130. this.outputGeometry.isManifold = false
  131. break
  132. }
  133. }
  134. }
  135. createVectorMap() {
  136. const vertices = this.vertices
  137. const vectorMap = this.vectorMap
  138. const borderEdges = this.borderEdges
  139. for (let edge of borderEdges) {
  140. let point1 = vertices[edge.v1]
  141. let point2 = vertices[edge.v2]
  142. let key = this.getVectorKey(point1, point2)
  143. let vertexSet = vectorMap.get(key)
  144. if (vertexSet === undefined) {
  145. vertexSet = new Set()
  146. vectorMap.set(key, vertexSet)
  147. }
  148. vertexSet.add(edge.v1)
  149. vertexSet.add(edge.v2)
  150. }
  151. }
  152. splitEdges() {
  153. const borderEdges = this.borderEdges
  154. if (borderEdges.length > 0) {
  155. this.createVectorMap()
  156. const vertices = this.vertices
  157. const edgeMap = this.edgeMap
  158. const edgeDistance = this.edgeDistance
  159. const vectorMap = this.vectorMap
  160. while (borderEdges.length > 0) {
  161. let edge = borderEdges.pop()
  162. if (edge.polygon2 === null) {
  163. // may be already paired
  164. let v1 = edge.v1
  165. let v2 = edge.v2
  166. let point1 = vertices[v1]
  167. let point2 = vertices[v2]
  168. let key = this.getVectorKey(point1, point2)
  169. let vertexSet = vectorMap.get(key)
  170. if (vertexSet) {
  171. for (let v3 of vertexSet.values()) {
  172. let point3 = vertices[v3]
  173. if (v1 !== v3 && v2 !== v3 && GeometryUtils.isPointOnSegment(point3, point1, point2, edgeDistance)) {
  174. edgeMap.delete(edge.key)
  175. let edge13 = edgeMap.get(Edge.keyOf(v1, v3))
  176. if (edge13 === undefined) {
  177. // new edge
  178. edge13 = new Edge(v1, v3)
  179. edgeMap.set(edge13.key, edge13)
  180. edge13.polygon1 = edge.polygon1
  181. borderEdges.push(edge13)
  182. } else if (edge13.polygon2 === null) {
  183. // matching edge found
  184. edge13.polygon2 = edge.polygon1
  185. }
  186. let edge32 = edgeMap.get(Edge.keyOf(v3, v2))
  187. if (edge32 === undefined) {
  188. // new edge
  189. edge32 = new Edge(v3, v2)
  190. edgeMap.set(edge32.key, edge32)
  191. edge32.polygon1 = edge.polygon1
  192. borderEdges.push(edge32)
  193. } else if (edge32.polygon2 === null) {
  194. // matching edge found
  195. edge32.polygon2 = edge.polygon1
  196. }
  197. vertexSet.delete(v3)
  198. break
  199. }
  200. }
  201. }
  202. }
  203. }
  204. }
  205. }
  206. mergePolygons() {
  207. const edgeMap = this.edgeMap
  208. const polygons = this.polygons
  209. const limit = this.normalLimit
  210. const vertices = this.vertices
  211. for (let edge of edgeMap.values()) {
  212. edge.polygon1.edges.push(edge)
  213. if (edge.polygon2) edge.polygon2.edges.push(edge)
  214. }
  215. for (let edge of edgeMap.values()) {
  216. if (edge.polygon2 !== null && edge.polygon1 !== edge.polygon2) {
  217. if (Math.abs(edge.polygon1.normal.dot(edge.polygon2.normal)) > limit) {
  218. let oldPolygon = edge.polygon2
  219. edge.polygon1.merge(edge.polygon2)
  220. polygons.delete(oldPolygon)
  221. }
  222. }
  223. }
  224. }
  225. createVertexMap() {
  226. const edgeMap = this.edgeMap
  227. const vertexMap = this.vertexMap
  228. const vertices = this.vertices
  229. const addVertex = (edge, v) => {
  230. let vertex = vertexMap.get(v)
  231. if (vertex === undefined) {
  232. vertex = new Vertex(vertices[v])
  233. vertexMap.set(v, vertex)
  234. }
  235. vertex.polygons.add(edge.polygon1)
  236. if (edge.polygon2 === null) {
  237. vertex.onBorder = true
  238. } else {
  239. vertex.polygons.add(edge.polygon2)
  240. }
  241. }
  242. for (let edge of edgeMap.values()) {
  243. let v1 = edge.v1
  244. let v2 = edge.v2
  245. addVertex(edge, v1)
  246. addVertex(edge, v2)
  247. }
  248. }
  249. createFaces() {
  250. const polygons = this.polygons
  251. for (let polygon of polygons) {
  252. this.createFace(polygon)
  253. }
  254. this.outputGeometry.updateBuffers()
  255. }
  256. createFace(polygon) {
  257. const outputGeometry = this.outputGeometry
  258. const vertices = outputGeometry.vertices
  259. const vertexMap = this.vertexMap
  260. const edgeMap = this.edgeMap
  261. const segments = new Map()
  262. let rings = []
  263. // create segments Map
  264. for (let edge of polygon.edges) {
  265. if (edge.polygon2 === null || edge.polygon1 !== edge.polygon2) {
  266. let v1 = edge.v1
  267. let v2 = edge.v2
  268. let next1 = segments.get(v1)
  269. if (next1 === undefined) {
  270. next1 = []
  271. segments.set(v1, next1)
  272. }
  273. next1.push(v2)
  274. let next2 = segments.get(v2)
  275. if (next2 === undefined) {
  276. next2 = []
  277. segments.set(v2, next2)
  278. }
  279. next2.push(v1)
  280. } else edgeMap.delete(edge.key)
  281. }
  282. // find rings
  283. while (segments.size > 0) {
  284. let ring = []
  285. let v0 = segments.keys().next().value
  286. let v1 = null
  287. let v2 = v0
  288. let next = undefined
  289. do {
  290. let vertex = vertexMap.get(v2)
  291. if (vertex && (vertex.polygons.size >= 3 || vertex.onBorder)) {
  292. if (vertex.newIndex === undefined) {
  293. vertex.newIndex = vertices.length
  294. vertices.push(vertex.position)
  295. }
  296. ring.push(vertex.newIndex)
  297. }
  298. next = segments.get(v2)
  299. if (next) {
  300. segments.delete(v2)
  301. let vn = next[0] === v1 ? next[1] : next[0]
  302. v1 = v2
  303. v2 = vn
  304. }
  305. } while (next !== undefined && v2 !== v0)
  306. if (next === undefined || ring.length < 3) {
  307. // invalid ring
  308. rings = []
  309. break
  310. } else {
  311. // have a valid ring
  312. rings.push(ring)
  313. }
  314. }
  315. if (rings.length > 0) {
  316. // create faces from rings
  317. let o = rings.length === 1 ? 0 : this.findOuterRing(rings, vertices, polygon.normal)
  318. let outerRing = rings[o]
  319. for (let ring of rings) {
  320. if (ring === outerRing) {
  321. this.orientRing(ring, vertices, polygon.normal, false)
  322. } else {
  323. this.orientRing(ring, vertices, polygon.normal, true)
  324. }
  325. }
  326. let face = outputGeometry.addFace(...outerRing)
  327. face.normal = polygon.normal
  328. for (let r = 0; r < rings.length; r++) {
  329. if (r !== o) {
  330. face.addHole(...rings[r])
  331. }
  332. }
  333. } else {
  334. // invalid rings, add original faces
  335. const inputVertices = this.inputGeometry.vertices
  336. for (let originalFace of polygon.faces) {
  337. let points = originalFace.indices.map(v => inputVertices[v])
  338. let face = outputGeometry.addFace(...points)
  339. face.normal = polygon.normal
  340. for (let hole of face.holes) {
  341. points = hole.indices.map(v => inputVertices[v])
  342. face.addHole(...points)
  343. }
  344. }
  345. this.statistics.unmergedFaces++
  346. outputGeometry.isManifold = false
  347. }
  348. }
  349. findOuterRing(rings, vertices, normal) {
  350. let max = -Infinity
  351. let rMax = 0
  352. let dim = 'x'
  353. if (Math.abs(normal.x) > 0.9) {
  354. dim = 'y'
  355. if (Math.abs(normal.y) > 0.9) {
  356. dim = 'z'
  357. }
  358. }
  359. for (let r = 0; r < rings.length; r++) {
  360. let ring = rings[r]
  361. for (let v of ring) {
  362. let vertex = vertices[v]
  363. if (vertex[dim] > max) {
  364. max = vertex[dim]
  365. rMax = r
  366. }
  367. }
  368. }
  369. return rMax
  370. }
  371. orientRing(ring, vertices, normal, isHole) {
  372. let ringNormal = this._vector
  373. GeometryUtils.calculateNormal(ring, v => vertices[v], ringNormal)
  374. let dot = ringNormal.dot(normal)
  375. if ((!isHole && dot < 0) || (isHole && dot > 0)) {
  376. ring.reverse()
  377. }
  378. }
  379. getVectorKey(vertex1, vertex2) {
  380. const factor = this.vectorFactor
  381. const vector = this._vector
  382. vector
  383. .copy(vertex2)
  384. .sub(vertex1)
  385. .normalize()
  386. let x = Math.round(vector.x * factor)
  387. let y = Math.round(vector.y * factor)
  388. let z = Math.round(vector.z * factor)
  389. let invert = false
  390. if (x === 0) {
  391. if (y === 0) {
  392. if (z < 0) {
  393. invert = true
  394. }
  395. } else if (y < 0) {
  396. invert = true
  397. }
  398. } else if (x < 0) {
  399. invert = true
  400. }
  401. if (invert) {
  402. x = -x
  403. y = -y
  404. z = -z
  405. }
  406. return x + ',' + y + ',' + z
  407. }
  408. }
  409. class Vertex {
  410. constructor(position) {
  411. this.position = position
  412. this.polygons = new Set()
  413. this.newIndex = undefined
  414. this.onBorder = false
  415. }
  416. }
  417. class Edge {
  418. constructor(v1, v2) {
  419. this.v1 = Math.min(v1, v2)
  420. this.v2 = Math.max(v1, v2)
  421. this.polygon1 = null
  422. this.polygon2 = null
  423. }
  424. get key() {
  425. return Edge.keyOf(this.v1, this.v2)
  426. }
  427. static keyOf(v1, v2) {
  428. return Math.min(v1, v2) + '=>' + Math.max(v1, v2)
  429. }
  430. }
  431. class Polygon {
  432. constructor(id, face) {
  433. this.id = id
  434. this.normal = face.normal.clone()
  435. this.faces = [face]
  436. this.edges = []
  437. }
  438. merge(polygon) {
  439. for (let edge of polygon.edges) {
  440. if (edge.polygon1 === polygon) edge.polygon1 = this
  441. else if (edge.polygon2 === polygon) edge.polygon2 = this
  442. }
  443. this.edges.push(...polygon.edges)
  444. this.faces.push(...polygon.faces)
  445. polygon.edges = []
  446. polygon.faces = []
  447. }
  448. }
  449. export { SolidOptimizer }