QuadEdgeSubdivision.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. import QuadEdge from './QuadEdge'
  2. import CoordinateList from '../../geom/CoordinateList'
  3. import HashSet from '../../../../../java/util/HashSet'
  4. import WKTWriter from '../../io/WKTWriter'
  5. import GeometryFactory from '../../geom/GeometryFactory'
  6. import Coordinate from '../../geom/Coordinate'
  7. import IllegalArgumentException from '../../../../../java/lang/IllegalArgumentException'
  8. import Stack from '../../../../../java/util/Stack'
  9. import LastFoundQuadEdgeLocator from './LastFoundQuadEdgeLocator'
  10. import LocateFailureException from './LocateFailureException'
  11. import Vertex from './Vertex'
  12. import System from '../../../../../java/lang/System'
  13. import LineSegment from '../../geom/LineSegment'
  14. import ArrayList from '../../../../../java/util/ArrayList'
  15. import Envelope from '../../geom/Envelope'
  16. import Triangle from '../../geom/Triangle'
  17. import TriangleVisitor from './TriangleVisitor'
  18. export default class QuadEdgeSubdivision {
  19. constructor() {
  20. QuadEdgeSubdivision.constructor_.apply(this, arguments)
  21. }
  22. static constructor_() {
  23. this._visitedKey = 0
  24. this._quadEdges = new ArrayList()
  25. this._startingEdge = null
  26. this._tolerance = null
  27. this._edgeCoincidenceTolerance = null
  28. this._frameVertex = new Array(3).fill(null)
  29. this._frameEnv = null
  30. this._locator = null
  31. this._seg = new LineSegment()
  32. this._triEdges = new Array(3).fill(null)
  33. const env = arguments[0], tolerance = arguments[1]
  34. this._tolerance = tolerance
  35. this._edgeCoincidenceTolerance = tolerance / QuadEdgeSubdivision.EDGE_COINCIDENCE_TOL_FACTOR
  36. this.createFrame(env)
  37. this._startingEdge = this.initSubdiv()
  38. this._locator = new LastFoundQuadEdgeLocator(this)
  39. }
  40. static getTriangleEdges(startQE, triEdge) {
  41. triEdge[0] = startQE
  42. triEdge[1] = triEdge[0].lNext()
  43. triEdge[2] = triEdge[1].lNext()
  44. if (triEdge[2].lNext() !== triEdge[0]) throw new IllegalArgumentException('Edges do not form a triangle')
  45. }
  46. getTriangleVertices(includeFrame) {
  47. const visitor = new TriangleVertexListVisitor()
  48. this.visitTriangles(visitor, includeFrame)
  49. return visitor.getTriangleVertices()
  50. }
  51. isFrameVertex(v) {
  52. if (v.equals(this._frameVertex[0])) return true
  53. if (v.equals(this._frameVertex[1])) return true
  54. if (v.equals(this._frameVertex[2])) return true
  55. return false
  56. }
  57. isVertexOfEdge(e, v) {
  58. if (v.equals(e.orig(), this._tolerance) || v.equals(e.dest(), this._tolerance))
  59. return true
  60. return false
  61. }
  62. connect(a, b) {
  63. const q = QuadEdge.connect(a, b)
  64. this._quadEdges.add(q)
  65. return q
  66. }
  67. getVoronoiCellPolygon(qe, geomFact) {
  68. const cellPts = new ArrayList()
  69. const startQE = qe
  70. do {
  71. const cc = qe.rot().orig().getCoordinate()
  72. cellPts.add(cc)
  73. qe = qe.oPrev()
  74. } while (qe !== startQE)
  75. const coordList = new CoordinateList()
  76. coordList.addAll(cellPts, false)
  77. coordList.closeRing()
  78. if (coordList.size() < 4) {
  79. System.out.println(coordList)
  80. coordList.add(coordList.get(coordList.size() - 1), true)
  81. }
  82. const pts = coordList.toCoordinateArray()
  83. const cellPoly = geomFact.createPolygon(geomFact.createLinearRing(pts))
  84. const v = startQE.orig()
  85. cellPoly.setUserData(v.getCoordinate())
  86. return cellPoly
  87. }
  88. setLocator(locator) {
  89. this._locator = locator
  90. }
  91. initSubdiv() {
  92. const ea = this.makeEdge(this._frameVertex[0], this._frameVertex[1])
  93. const eb = this.makeEdge(this._frameVertex[1], this._frameVertex[2])
  94. QuadEdge.splice(ea.sym(), eb)
  95. const ec = this.makeEdge(this._frameVertex[2], this._frameVertex[0])
  96. QuadEdge.splice(eb.sym(), ec)
  97. QuadEdge.splice(ec.sym(), ea)
  98. return ea
  99. }
  100. isFrameBorderEdge(e) {
  101. const leftTri = new Array(3).fill(null)
  102. QuadEdgeSubdivision.getTriangleEdges(e, leftTri)
  103. const rightTri = new Array(3).fill(null)
  104. QuadEdgeSubdivision.getTriangleEdges(e.sym(), rightTri)
  105. const vLeftTriOther = e.lNext().dest()
  106. if (this.isFrameVertex(vLeftTriOther)) return true
  107. const vRightTriOther = e.sym().lNext().dest()
  108. if (this.isFrameVertex(vRightTriOther)) return true
  109. return false
  110. }
  111. makeEdge(o, d) {
  112. const q = QuadEdge.makeEdge(o, d)
  113. this._quadEdges.add(q)
  114. return q
  115. }
  116. visitTriangles(triVisitor, includeFrame) {
  117. this._visitedKey++
  118. const edgeStack = new Stack()
  119. edgeStack.push(this._startingEdge)
  120. const visitedEdges = new HashSet()
  121. while (!edgeStack.empty()) {
  122. const edge = edgeStack.pop()
  123. if (!visitedEdges.contains(edge)) {
  124. const triEdges = this.fetchTriangleToVisit(edge, edgeStack, includeFrame, visitedEdges)
  125. if (triEdges !== null) triVisitor.visit(triEdges)
  126. }
  127. }
  128. }
  129. isFrameEdge(e) {
  130. if (this.isFrameVertex(e.orig()) || this.isFrameVertex(e.dest())) return true
  131. return false
  132. }
  133. isOnEdge(e, p) {
  134. this._seg.setCoordinates(e.orig().getCoordinate(), e.dest().getCoordinate())
  135. const dist = this._seg.distance(p)
  136. return dist < this._edgeCoincidenceTolerance
  137. }
  138. getEnvelope() {
  139. return new Envelope(this._frameEnv)
  140. }
  141. createFrame(env) {
  142. const deltaX = env.getWidth()
  143. const deltaY = env.getHeight()
  144. let offset = 0.0
  145. if (deltaX > deltaY)
  146. offset = deltaX * 10.0
  147. else
  148. offset = deltaY * 10.0
  149. this._frameVertex[0] = new Vertex((env.getMaxX() + env.getMinX()) / 2.0, env.getMaxY() + offset)
  150. this._frameVertex[1] = new Vertex(env.getMinX() - offset, env.getMinY() - offset)
  151. this._frameVertex[2] = new Vertex(env.getMaxX() + offset, env.getMinY() - offset)
  152. this._frameEnv = new Envelope(this._frameVertex[0].getCoordinate(), this._frameVertex[1].getCoordinate())
  153. this._frameEnv.expandToInclude(this._frameVertex[2].getCoordinate())
  154. }
  155. getTriangleCoordinates(includeFrame) {
  156. const visitor = new TriangleCoordinatesVisitor()
  157. this.visitTriangles(visitor, includeFrame)
  158. return visitor.getTriangles()
  159. }
  160. getVertices(includeFrame) {
  161. const vertices = new HashSet()
  162. for (let i = this._quadEdges.iterator(); i.hasNext(); ) {
  163. const qe = i.next()
  164. const v = qe.orig()
  165. if (includeFrame || !this.isFrameVertex(v)) vertices.add(v)
  166. const vd = qe.dest()
  167. if (includeFrame || !this.isFrameVertex(vd)) vertices.add(vd)
  168. }
  169. return vertices
  170. }
  171. fetchTriangleToVisit(edge, edgeStack, includeFrame, visitedEdges) {
  172. let curr = edge
  173. let edgeCount = 0
  174. let isFrame = false
  175. do {
  176. this._triEdges[edgeCount] = curr
  177. if (this.isFrameEdge(curr)) isFrame = true
  178. const sym = curr.sym()
  179. if (!visitedEdges.contains(sym)) edgeStack.push(sym)
  180. visitedEdges.add(curr)
  181. edgeCount++
  182. curr = curr.lNext()
  183. } while (curr !== edge)
  184. if (isFrame && !includeFrame) return null
  185. return this._triEdges
  186. }
  187. getEdges() {
  188. if (arguments.length === 0) {
  189. return this._quadEdges
  190. } else if (arguments.length === 1) {
  191. const geomFact = arguments[0]
  192. const quadEdges = this.getPrimaryEdges(false)
  193. const edges = new Array(quadEdges.size()).fill(null)
  194. let i = 0
  195. for (let it = quadEdges.iterator(); it.hasNext(); ) {
  196. const qe = it.next()
  197. edges[i++] = geomFact.createLineString([qe.orig().getCoordinate(), qe.dest().getCoordinate()])
  198. }
  199. return geomFact.createMultiLineString(edges)
  200. }
  201. }
  202. getVertexUniqueEdges(includeFrame) {
  203. const edges = new ArrayList()
  204. const visitedVertices = new HashSet()
  205. for (let i = this._quadEdges.iterator(); i.hasNext(); ) {
  206. const qe = i.next()
  207. const v = qe.orig()
  208. if (!visitedVertices.contains(v)) {
  209. visitedVertices.add(v)
  210. if (includeFrame || !this.isFrameVertex(v))
  211. edges.add(qe)
  212. }
  213. const qd = qe.sym()
  214. const vd = qd.orig()
  215. if (!visitedVertices.contains(vd)) {
  216. visitedVertices.add(vd)
  217. if (includeFrame || !this.isFrameVertex(vd))
  218. edges.add(qd)
  219. }
  220. }
  221. return edges
  222. }
  223. getTriangleEdges(includeFrame) {
  224. const visitor = new TriangleEdgesListVisitor()
  225. this.visitTriangles(visitor, includeFrame)
  226. return visitor.getTriangleEdges()
  227. }
  228. getPrimaryEdges(includeFrame) {
  229. this._visitedKey++
  230. const edges = new ArrayList()
  231. const edgeStack = new Stack()
  232. edgeStack.push(this._startingEdge)
  233. const visitedEdges = new HashSet()
  234. while (!edgeStack.empty()) {
  235. const edge = edgeStack.pop()
  236. if (!visitedEdges.contains(edge)) {
  237. const priQE = edge.getPrimary()
  238. if (includeFrame || !this.isFrameEdge(priQE)) edges.add(priQE)
  239. edgeStack.push(edge.oNext())
  240. edgeStack.push(edge.sym().oNext())
  241. visitedEdges.add(edge)
  242. visitedEdges.add(edge.sym())
  243. }
  244. }
  245. return edges
  246. }
  247. delete(e) {
  248. QuadEdge.splice(e, e.oPrev())
  249. QuadEdge.splice(e.sym(), e.sym().oPrev())
  250. const eSym = e.sym()
  251. const eRot = e.rot()
  252. const eRotSym = e.rot().sym()
  253. this._quadEdges.remove(e)
  254. this._quadEdges.remove(eSym)
  255. this._quadEdges.remove(eRot)
  256. this._quadEdges.remove(eRotSym)
  257. e.delete()
  258. eSym.delete()
  259. eRot.delete()
  260. eRotSym.delete()
  261. }
  262. locateFromEdge(v, startEdge) {
  263. let iter = 0
  264. const maxIter = this._quadEdges.size()
  265. let e = startEdge
  266. while (true) {
  267. iter++
  268. if (iter > maxIter)
  269. throw new LocateFailureException(e.toLineSegment())
  270. if (v.equals(e.orig()) || v.equals(e.dest()))
  271. break
  272. else if (v.rightOf(e))
  273. e = e.sym()
  274. else if (!v.rightOf(e.oNext()))
  275. e = e.oNext()
  276. else if (!v.rightOf(e.dPrev()))
  277. e = e.dPrev()
  278. else
  279. break
  280. }
  281. return e
  282. }
  283. getTolerance() {
  284. return this._tolerance
  285. }
  286. getVoronoiCellPolygons(geomFact) {
  287. this.visitTriangles(new TriangleCircumcentreVisitor(), true)
  288. const cells = new ArrayList()
  289. const edges = this.getVertexUniqueEdges(false)
  290. for (let i = edges.iterator(); i.hasNext(); ) {
  291. const qe = i.next()
  292. cells.add(this.getVoronoiCellPolygon(qe, geomFact))
  293. }
  294. return cells
  295. }
  296. getVoronoiDiagram(geomFact) {
  297. const vorCells = this.getVoronoiCellPolygons(geomFact)
  298. return geomFact.createGeometryCollection(GeometryFactory.toGeometryArray(vorCells))
  299. }
  300. getTriangles(geomFact) {
  301. const triPtsList = this.getTriangleCoordinates(false)
  302. const tris = new Array(triPtsList.size()).fill(null)
  303. let i = 0
  304. for (let it = triPtsList.iterator(); it.hasNext(); ) {
  305. const triPt = it.next()
  306. tris[i++] = geomFact.createPolygon(geomFact.createLinearRing(triPt))
  307. }
  308. return geomFact.createGeometryCollection(tris)
  309. }
  310. insertSite(v) {
  311. let e = this.locate(v)
  312. if (v.equals(e.orig(), this._tolerance) || v.equals(e.dest(), this._tolerance))
  313. return e
  314. let base = this.makeEdge(e.orig(), v)
  315. QuadEdge.splice(base, e)
  316. const startEdge = base
  317. do {
  318. base = this.connect(e, base.sym())
  319. e = base.oPrev()
  320. } while (e.lNext() !== startEdge)
  321. return startEdge
  322. }
  323. locate() {
  324. if (arguments.length === 1) {
  325. if (arguments[0] instanceof Vertex) {
  326. const v = arguments[0]
  327. return this._locator.locate(v)
  328. } else if (arguments[0] instanceof Coordinate) {
  329. const p = arguments[0]
  330. return this._locator.locate(new Vertex(p))
  331. }
  332. } else if (arguments.length === 2) {
  333. const p0 = arguments[0], p1 = arguments[1]
  334. const e = this._locator.locate(new Vertex(p0))
  335. if (e === null) return null
  336. let base = e
  337. if (e.dest().getCoordinate().equals2D(p0)) base = e.sym()
  338. let locEdge = base
  339. do {
  340. if (locEdge.dest().getCoordinate().equals2D(p1)) return locEdge
  341. locEdge = locEdge.oNext()
  342. } while (locEdge !== base)
  343. return null
  344. }
  345. }
  346. }
  347. class TriangleCircumcentreVisitor {
  348. visit(triEdges) {
  349. const a = triEdges[0].orig().getCoordinate()
  350. const b = triEdges[1].orig().getCoordinate()
  351. const c = triEdges[2].orig().getCoordinate()
  352. const cc = Triangle.circumcentreDD(a, b, c)
  353. const ccVertex = new Vertex(cc)
  354. for (let i = 0; i < 3; i++)
  355. triEdges[i].rot().setOrig(ccVertex)
  356. }
  357. get interfaces_() {
  358. return [TriangleVisitor]
  359. }
  360. }
  361. class TriangleEdgesListVisitor {
  362. constructor() {
  363. TriangleEdgesListVisitor.constructor_.apply(this, arguments)
  364. }
  365. static constructor_() {
  366. this._triList = new ArrayList()
  367. }
  368. getTriangleEdges() {
  369. return this._triList
  370. }
  371. visit(triEdges) {
  372. this._triList.add(triEdges)
  373. }
  374. get interfaces_() {
  375. return [TriangleVisitor]
  376. }
  377. }
  378. class TriangleVertexListVisitor {
  379. constructor() {
  380. TriangleVertexListVisitor.constructor_.apply(this, arguments)
  381. }
  382. static constructor_() {
  383. this._triList = new ArrayList()
  384. }
  385. visit(triEdges) {
  386. this._triList.add([triEdges[0].orig(), triEdges[1].orig(), triEdges[2].orig()])
  387. }
  388. getTriangleVertices() {
  389. return this._triList
  390. }
  391. get interfaces_() {
  392. return [TriangleVisitor]
  393. }
  394. }
  395. class TriangleCoordinatesVisitor {
  396. constructor() {
  397. TriangleCoordinatesVisitor.constructor_.apply(this, arguments)
  398. }
  399. static constructor_() {
  400. this._coordList = new CoordinateList()
  401. this._triCoords = new ArrayList()
  402. }
  403. checkTriangleSize(pts) {
  404. let loc = ''
  405. if (pts.length >= 2) loc = WKTWriter.toLineString(pts[0], pts[1]); else
  406. if (pts.length >= 1) loc = WKTWriter.toPoint(pts[0])
  407. }
  408. visit(triEdges) {
  409. this._coordList.clear()
  410. for (let i = 0; i < 3; i++) {
  411. const v = triEdges[i].orig()
  412. this._coordList.add(v.getCoordinate())
  413. }
  414. if (this._coordList.size() > 0) {
  415. this._coordList.closeRing()
  416. const pts = this._coordList.toCoordinateArray()
  417. if (pts.length !== 4)
  418. return null
  419. this._triCoords.add(pts)
  420. }
  421. }
  422. getTriangles() {
  423. return this._triCoords
  424. }
  425. get interfaces_() {
  426. return [TriangleVisitor]
  427. }
  428. }
  429. QuadEdgeSubdivision.TriangleCircumcentreVisitor = TriangleCircumcentreVisitor
  430. QuadEdgeSubdivision.TriangleEdgesListVisitor = TriangleEdgesListVisitor
  431. QuadEdgeSubdivision.TriangleVertexListVisitor = TriangleVertexListVisitor
  432. QuadEdgeSubdivision.TriangleCoordinatesVisitor = TriangleCoordinatesVisitor
  433. QuadEdgeSubdivision.EDGE_COINCIDENCE_TOL_FACTOR = 1000