LineSegment.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. import Coordinate from './Coordinate'
  2. import Double from '../../../../java/lang/Double'
  3. import Orientation from '../algorithm/Orientation'
  4. import Intersection from '../algorithm/Intersection'
  5. import Comparable from '../../../../java/lang/Comparable'
  6. import RobustLineIntersector from '../algorithm/RobustLineIntersector'
  7. import Serializable from '../../../../java/io/Serializable'
  8. import Distance from '../algorithm/Distance'
  9. export default class LineSegment {
  10. constructor() {
  11. LineSegment.constructor_.apply(this, arguments)
  12. }
  13. static constructor_() {
  14. this.p0 = null
  15. this.p1 = null
  16. if (arguments.length === 0) {
  17. LineSegment.constructor_.call(this, new Coordinate(), new Coordinate())
  18. } else if (arguments.length === 1) {
  19. const ls = arguments[0]
  20. LineSegment.constructor_.call(this, ls.p0, ls.p1)
  21. } else if (arguments.length === 2) {
  22. const p0 = arguments[0], p1 = arguments[1]
  23. this.p0 = p0
  24. this.p1 = p1
  25. } else if (arguments.length === 4) {
  26. const x0 = arguments[0], y0 = arguments[1], x1 = arguments[2], y1 = arguments[3]
  27. LineSegment.constructor_.call(this, new Coordinate(x0, y0), new Coordinate(x1, y1))
  28. }
  29. }
  30. static midPoint(p0, p1) {
  31. return new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2)
  32. }
  33. minX() {
  34. return Math.min(this.p0.x, this.p1.x)
  35. }
  36. orientationIndex() {
  37. if (arguments[0] instanceof LineSegment) {
  38. const seg = arguments[0]
  39. const orient0 = Orientation.index(this.p0, this.p1, seg.p0)
  40. const orient1 = Orientation.index(this.p0, this.p1, seg.p1)
  41. if (orient0 >= 0 && orient1 >= 0) return Math.max(orient0, orient1)
  42. if (orient0 <= 0 && orient1 <= 0) return Math.max(orient0, orient1)
  43. return 0
  44. } else if (arguments[0] instanceof Coordinate) {
  45. const p = arguments[0]
  46. return Orientation.index(this.p0, this.p1, p)
  47. }
  48. }
  49. toGeometry(geomFactory) {
  50. return geomFactory.createLineString([this.p0, this.p1])
  51. }
  52. isVertical() {
  53. return this.p0.x === this.p1.x
  54. }
  55. equals(o) {
  56. if (!(o instanceof LineSegment))
  57. return false
  58. const other = o
  59. return this.p0.equals(other.p0) && this.p1.equals(other.p1)
  60. }
  61. intersection(line) {
  62. const li = new RobustLineIntersector()
  63. li.computeIntersection(this.p0, this.p1, line.p0, line.p1)
  64. if (li.hasIntersection()) return li.getIntersection(0)
  65. return null
  66. }
  67. project() {
  68. if (arguments[0] instanceof Coordinate) {
  69. const p = arguments[0]
  70. if (p.equals(this.p0) || p.equals(this.p1)) return new Coordinate(p)
  71. const r = this.projectionFactor(p)
  72. const coord = new Coordinate()
  73. coord.x = this.p0.x + r * (this.p1.x - this.p0.x)
  74. coord.y = this.p0.y + r * (this.p1.y - this.p0.y)
  75. return coord
  76. } else if (arguments[0] instanceof LineSegment) {
  77. const seg = arguments[0]
  78. const pf0 = this.projectionFactor(seg.p0)
  79. const pf1 = this.projectionFactor(seg.p1)
  80. if (pf0 >= 1.0 && pf1 >= 1.0) return null
  81. if (pf0 <= 0.0 && pf1 <= 0.0) return null
  82. let newp0 = this.project(seg.p0)
  83. if (pf0 < 0.0) newp0 = this.p0
  84. if (pf0 > 1.0) newp0 = this.p1
  85. let newp1 = this.project(seg.p1)
  86. if (pf1 < 0.0) newp1 = this.p0
  87. if (pf1 > 1.0) newp1 = this.p1
  88. return new LineSegment(newp0, newp1)
  89. }
  90. }
  91. normalize() {
  92. if (this.p1.compareTo(this.p0) < 0) this.reverse()
  93. }
  94. angle() {
  95. return Math.atan2(this.p1.y - this.p0.y, this.p1.x - this.p0.x)
  96. }
  97. getCoordinate(i) {
  98. if (i === 0) return this.p0
  99. return this.p1
  100. }
  101. distancePerpendicular(p) {
  102. return Distance.pointToLinePerpendicular(p, this.p0, this.p1)
  103. }
  104. minY() {
  105. return Math.min(this.p0.y, this.p1.y)
  106. }
  107. midPoint() {
  108. return LineSegment.midPoint(this.p0, this.p1)
  109. }
  110. projectionFactor(p) {
  111. if (p.equals(this.p0)) return 0.0
  112. if (p.equals(this.p1)) return 1.0
  113. const dx = this.p1.x - this.p0.x
  114. const dy = this.p1.y - this.p0.y
  115. const len = dx * dx + dy * dy
  116. if (len <= 0.0) return Double.NaN
  117. const r = ((p.x - this.p0.x) * dx + (p.y - this.p0.y) * dy) / len
  118. return r
  119. }
  120. closestPoints(line) {
  121. const intPt = this.intersection(line)
  122. if (intPt !== null)
  123. return [intPt, intPt]
  124. const closestPt = new Array(2).fill(null)
  125. let minDistance = Double.MAX_VALUE
  126. let dist = null
  127. const close00 = this.closestPoint(line.p0)
  128. minDistance = close00.distance(line.p0)
  129. closestPt[0] = close00
  130. closestPt[1] = line.p0
  131. const close01 = this.closestPoint(line.p1)
  132. dist = close01.distance(line.p1)
  133. if (dist < minDistance) {
  134. minDistance = dist
  135. closestPt[0] = close01
  136. closestPt[1] = line.p1
  137. }
  138. const close10 = line.closestPoint(this.p0)
  139. dist = close10.distance(this.p0)
  140. if (dist < minDistance) {
  141. minDistance = dist
  142. closestPt[0] = this.p0
  143. closestPt[1] = close10
  144. }
  145. const close11 = line.closestPoint(this.p1)
  146. dist = close11.distance(this.p1)
  147. if (dist < minDistance) {
  148. minDistance = dist
  149. closestPt[0] = this.p1
  150. closestPt[1] = close11
  151. }
  152. return closestPt
  153. }
  154. closestPoint(p) {
  155. const factor = this.projectionFactor(p)
  156. if (factor > 0 && factor < 1)
  157. return this.project(p)
  158. const dist0 = this.p0.distance(p)
  159. const dist1 = this.p1.distance(p)
  160. if (dist0 < dist1) return this.p0
  161. return this.p1
  162. }
  163. maxX() {
  164. return Math.max(this.p0.x, this.p1.x)
  165. }
  166. getLength() {
  167. return this.p0.distance(this.p1)
  168. }
  169. compareTo(o) {
  170. const other = o
  171. const comp0 = this.p0.compareTo(other.p0)
  172. if (comp0 !== 0) return comp0
  173. return this.p1.compareTo(other.p1)
  174. }
  175. reverse() {
  176. const temp = this.p0
  177. this.p0 = this.p1
  178. this.p1 = temp
  179. }
  180. equalsTopo(other) {
  181. return this.p0.equals(other.p0) && this.p1.equals(other.p1) || this.p0.equals(other.p1) && this.p1.equals(other.p0)
  182. }
  183. lineIntersection(line) {
  184. const intPt = Intersection.intersection(this.p0, this.p1, line.p0, line.p1)
  185. return intPt
  186. }
  187. maxY() {
  188. return Math.max(this.p0.y, this.p1.y)
  189. }
  190. pointAlongOffset(segmentLengthFraction, offsetDistance) {
  191. const segx = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x)
  192. const segy = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y)
  193. const dx = this.p1.x - this.p0.x
  194. const dy = this.p1.y - this.p0.y
  195. const len = Math.sqrt(dx * dx + dy * dy)
  196. let ux = 0.0
  197. let uy = 0.0
  198. if (offsetDistance !== 0.0) {
  199. if (len <= 0.0) throw new IllegalStateException('Cannot compute offset from zero-length line segment')
  200. ux = offsetDistance * dx / len
  201. uy = offsetDistance * dy / len
  202. }
  203. const offsetx = segx - uy
  204. const offsety = segy + ux
  205. const coord = new Coordinate(offsetx, offsety)
  206. return coord
  207. }
  208. setCoordinates() {
  209. if (arguments.length === 1) {
  210. const ls = arguments[0]
  211. this.setCoordinates(ls.p0, ls.p1)
  212. } else if (arguments.length === 2) {
  213. const p0 = arguments[0], p1 = arguments[1]
  214. this.p0.x = p0.x
  215. this.p0.y = p0.y
  216. this.p1.x = p1.x
  217. this.p1.y = p1.y
  218. }
  219. }
  220. segmentFraction(inputPt) {
  221. let segFrac = this.projectionFactor(inputPt)
  222. if (segFrac < 0.0) segFrac = 0.0; else if (segFrac > 1.0 || Double.isNaN(segFrac)) segFrac = 1.0
  223. return segFrac
  224. }
  225. toString() {
  226. return 'LINESTRING( ' + this.p0.x + ' ' + this.p0.y + ', ' + this.p1.x + ' ' + this.p1.y + ')'
  227. }
  228. isHorizontal() {
  229. return this.p0.y === this.p1.y
  230. }
  231. reflect(p) {
  232. const A = this.p1.getY() - this.p0.getY()
  233. const B = this.p0.getX() - this.p1.getX()
  234. const C = this.p0.getY() * (this.p1.getX() - this.p0.getX()) - this.p0.getX() * (this.p1.getY() - this.p0.getY())
  235. const A2plusB2 = A * A + B * B
  236. const A2subB2 = A * A - B * B
  237. const x = p.getX()
  238. const y = p.getY()
  239. const rx = (-A2subB2 * x - 2 * A * B * y - 2 * A * C) / A2plusB2
  240. const ry = (A2subB2 * y - 2 * A * B * x - 2 * B * C) / A2plusB2
  241. return new Coordinate(rx, ry)
  242. }
  243. distance() {
  244. if (arguments[0] instanceof LineSegment) {
  245. const ls = arguments[0]
  246. return Distance.segmentToSegment(this.p0, this.p1, ls.p0, ls.p1)
  247. } else if (arguments[0] instanceof Coordinate) {
  248. const p = arguments[0]
  249. return Distance.pointToSegment(p, this.p0, this.p1)
  250. }
  251. }
  252. pointAlong(segmentLengthFraction) {
  253. const coord = new Coordinate()
  254. coord.x = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x)
  255. coord.y = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y)
  256. return coord
  257. }
  258. hashCode() {
  259. let bits0 = Double.doubleToLongBits(this.p0.x)
  260. bits0 ^= Double.doubleToLongBits(this.p0.y) * 31
  261. const hash0 = Math.trunc(bits0) ^ Math.trunc(bits0 >> 32)
  262. let bits1 = Double.doubleToLongBits(this.p1.x)
  263. bits1 ^= Double.doubleToLongBits(this.p1.y) * 31
  264. const hash1 = Math.trunc(bits1) ^ Math.trunc(bits1 >> 32)
  265. return hash0 ^ hash1
  266. }
  267. get interfaces_() {
  268. return [Comparable, Serializable]
  269. }
  270. }