MinimumDiameter.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import Coordinate from '../geom/Coordinate'
  2. import Polygon from '../geom/Polygon'
  3. import Double from '../../../../java/lang/Double'
  4. import LineSegment from '../geom/LineSegment'
  5. import ConvexHull from './ConvexHull'
  6. export default class MinimumDiameter {
  7. constructor() {
  8. MinimumDiameter.constructor_.apply(this, arguments)
  9. }
  10. static constructor_() {
  11. this._inputGeom = null
  12. this._isConvex = null
  13. this._convexHullPts = null
  14. this._minBaseSeg = new LineSegment()
  15. this._minWidthPt = null
  16. this._minPtIndex = null
  17. this._minWidth = 0.0
  18. if (arguments.length === 1) {
  19. const inputGeom = arguments[0]
  20. MinimumDiameter.constructor_.call(this, inputGeom, false)
  21. } else if (arguments.length === 2) {
  22. const inputGeom = arguments[0], isConvex = arguments[1]
  23. this._inputGeom = inputGeom
  24. this._isConvex = isConvex
  25. }
  26. }
  27. static nextIndex(pts, index) {
  28. index++
  29. if (index >= pts.length) index = 0
  30. return index
  31. }
  32. static computeC(a, b, p) {
  33. return a * p.y - b * p.x
  34. }
  35. static getMinimumDiameter(geom) {
  36. return new MinimumDiameter(geom).getDiameter()
  37. }
  38. static getMinimumRectangle(geom) {
  39. return new MinimumDiameter(geom).getMinimumRectangle()
  40. }
  41. static computeSegmentForLine(a, b, c) {
  42. let p0 = null
  43. let p1 = null
  44. if (Math.abs(b) > Math.abs(a)) {
  45. p0 = new Coordinate(0.0, c / b)
  46. p1 = new Coordinate(1.0, c / b - a / b)
  47. } else {
  48. p0 = new Coordinate(c / a, 0.0)
  49. p1 = new Coordinate(c / a - b / a, 1.0)
  50. }
  51. return new LineSegment(p0, p1)
  52. }
  53. getWidthCoordinate() {
  54. this.computeMinimumDiameter()
  55. return this._minWidthPt
  56. }
  57. getSupportingSegment() {
  58. this.computeMinimumDiameter()
  59. return this._inputGeom.getFactory().createLineString([this._minBaseSeg.p0, this._minBaseSeg.p1])
  60. }
  61. getDiameter() {
  62. this.computeMinimumDiameter()
  63. if (this._minWidthPt === null) return this._inputGeom.getFactory().createLineString()
  64. const basePt = this._minBaseSeg.project(this._minWidthPt)
  65. return this._inputGeom.getFactory().createLineString([basePt, this._minWidthPt])
  66. }
  67. computeWidthConvex(convexGeom) {
  68. if (convexGeom instanceof Polygon) this._convexHullPts = convexGeom.getExteriorRing().getCoordinates(); else this._convexHullPts = convexGeom.getCoordinates()
  69. if (this._convexHullPts.length === 0) {
  70. this._minWidth = 0.0
  71. this._minWidthPt = null
  72. this._minBaseSeg = null
  73. } else if (this._convexHullPts.length === 1) {
  74. this._minWidth = 0.0
  75. this._minWidthPt = this._convexHullPts[0]
  76. this._minBaseSeg.p0 = this._convexHullPts[0]
  77. this._minBaseSeg.p1 = this._convexHullPts[0]
  78. } else if (this._convexHullPts.length === 2 || this._convexHullPts.length === 3) {
  79. this._minWidth = 0.0
  80. this._minWidthPt = this._convexHullPts[0]
  81. this._minBaseSeg.p0 = this._convexHullPts[0]
  82. this._minBaseSeg.p1 = this._convexHullPts[1]
  83. } else {
  84. this.computeConvexRingMinDiameter(this._convexHullPts)
  85. }
  86. }
  87. computeConvexRingMinDiameter(pts) {
  88. this._minWidth = Double.MAX_VALUE
  89. let currMaxIndex = 1
  90. const seg = new LineSegment()
  91. for (let i = 0; i < pts.length - 1; i++) {
  92. seg.p0 = pts[i]
  93. seg.p1 = pts[i + 1]
  94. currMaxIndex = this.findMaxPerpDistance(pts, seg, currMaxIndex)
  95. }
  96. }
  97. computeMinimumDiameter() {
  98. if (this._minWidthPt !== null) return null
  99. if (this._isConvex) {
  100. this.computeWidthConvex(this._inputGeom)
  101. } else {
  102. const convexGeom = new ConvexHull(this._inputGeom).getConvexHull()
  103. this.computeWidthConvex(convexGeom)
  104. }
  105. }
  106. getLength() {
  107. this.computeMinimumDiameter()
  108. return this._minWidth
  109. }
  110. findMaxPerpDistance(pts, seg, startIndex) {
  111. let maxPerpDistance = seg.distancePerpendicular(pts[startIndex])
  112. let nextPerpDistance = maxPerpDistance
  113. let maxIndex = startIndex
  114. let nextIndex = maxIndex
  115. while (nextPerpDistance >= maxPerpDistance) {
  116. maxPerpDistance = nextPerpDistance
  117. maxIndex = nextIndex
  118. nextIndex = MinimumDiameter.nextIndex(pts, maxIndex)
  119. nextPerpDistance = seg.distancePerpendicular(pts[nextIndex])
  120. }
  121. if (maxPerpDistance < this._minWidth) {
  122. this._minPtIndex = maxIndex
  123. this._minWidth = maxPerpDistance
  124. this._minWidthPt = pts[this._minPtIndex]
  125. this._minBaseSeg = new LineSegment(seg)
  126. }
  127. return maxIndex
  128. }
  129. getMinimumRectangle() {
  130. this.computeMinimumDiameter()
  131. if (this._minWidth === 0.0) {
  132. if (this._minBaseSeg.p0.equals2D(this._minBaseSeg.p1))
  133. return this._inputGeom.getFactory().createPoint(this._minBaseSeg.p0)
  134. return this._minBaseSeg.toGeometry(this._inputGeom.getFactory())
  135. }
  136. const dx = this._minBaseSeg.p1.x - this._minBaseSeg.p0.x
  137. const dy = this._minBaseSeg.p1.y - this._minBaseSeg.p0.y
  138. let minPara = Double.MAX_VALUE
  139. let maxPara = -Double.MAX_VALUE
  140. let minPerp = Double.MAX_VALUE
  141. let maxPerp = -Double.MAX_VALUE
  142. for (let i = 0; i < this._convexHullPts.length; i++) {
  143. const paraC = MinimumDiameter.computeC(dx, dy, this._convexHullPts[i])
  144. if (paraC > maxPara) maxPara = paraC
  145. if (paraC < minPara) minPara = paraC
  146. const perpC = MinimumDiameter.computeC(-dy, dx, this._convexHullPts[i])
  147. if (perpC > maxPerp) maxPerp = perpC
  148. if (perpC < minPerp) minPerp = perpC
  149. }
  150. const maxPerpLine = MinimumDiameter.computeSegmentForLine(-dx, -dy, maxPerp)
  151. const minPerpLine = MinimumDiameter.computeSegmentForLine(-dx, -dy, minPerp)
  152. const maxParaLine = MinimumDiameter.computeSegmentForLine(-dy, dx, maxPara)
  153. const minParaLine = MinimumDiameter.computeSegmentForLine(-dy, dx, minPara)
  154. const p0 = maxParaLine.lineIntersection(maxPerpLine)
  155. const p1 = minParaLine.lineIntersection(maxPerpLine)
  156. const p2 = minParaLine.lineIntersection(minPerpLine)
  157. const p3 = maxParaLine.lineIntersection(minPerpLine)
  158. const shell = this._inputGeom.getFactory().createLinearRing([p0, p1, p2, p3, p0])
  159. return this._inputGeom.getFactory().createPolygon(shell)
  160. }
  161. }