PlanarPolygon3D.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import Location from '../../geom/Location'
  2. import hasInterface from '../../../../../hasInterface'
  3. import Coordinate from '../../geom/Coordinate'
  4. import AxisPlaneCoordinateSequence from './AxisPlaneCoordinateSequence'
  5. import Vector3D from '../../math/Vector3D'
  6. import CoordinateSequence from '../../geom/CoordinateSequence'
  7. import Plane3D from '../../math/Plane3D'
  8. import RayCrossingCounter from '../../algorithm/RayCrossingCounter'
  9. export default class PlanarPolygon3D {
  10. constructor() {
  11. PlanarPolygon3D.constructor_.apply(this, arguments)
  12. }
  13. static constructor_() {
  14. this._plane = null
  15. this._poly = null
  16. this._facingPlane = -1
  17. const poly = arguments[0]
  18. this._poly = poly
  19. this._plane = this.findBestFitPlane(poly)
  20. this._facingPlane = this._plane.closestAxisPlane()
  21. }
  22. static project() {
  23. if (hasInterface(arguments[0], CoordinateSequence) && Number.isInteger(arguments[1])) {
  24. const seq = arguments[0], facingPlane = arguments[1]
  25. switch (facingPlane) {
  26. case Plane3D.XY_PLANE:
  27. return AxisPlaneCoordinateSequence.projectToXY(seq)
  28. case Plane3D.XZ_PLANE:
  29. return AxisPlaneCoordinateSequence.projectToXZ(seq)
  30. default:
  31. return AxisPlaneCoordinateSequence.projectToYZ(seq)
  32. }
  33. } else if (arguments[0] instanceof Coordinate && Number.isInteger(arguments[1])) {
  34. const p = arguments[0], facingPlane = arguments[1]
  35. switch (facingPlane) {
  36. case Plane3D.XY_PLANE:
  37. return new Coordinate(p.x, p.y)
  38. case Plane3D.XZ_PLANE:
  39. return new Coordinate(p.x, p.getZ())
  40. default:
  41. return new Coordinate(p.y, p.getZ())
  42. }
  43. }
  44. }
  45. intersects() {
  46. if (arguments.length === 1) {
  47. const intPt = arguments[0]
  48. if (Location.EXTERIOR === this.locate(intPt, this._poly.getExteriorRing())) return false
  49. for (let i = 0; i < this._poly.getNumInteriorRing(); i++)
  50. if (Location.INTERIOR === this.locate(intPt, this._poly.getInteriorRingN(i))) return false
  51. return true
  52. } else if (arguments.length === 2) {
  53. const pt = arguments[0], ring = arguments[1]
  54. const seq = ring.getCoordinateSequence()
  55. const seqProj = PlanarPolygon3D.project(seq, this._facingPlane)
  56. const ptProj = PlanarPolygon3D.project(pt, this._facingPlane)
  57. return Location.EXTERIOR !== RayCrossingCounter.locatePointInRing(ptProj, seqProj)
  58. }
  59. }
  60. averagePoint(seq) {
  61. const a = new Coordinate(0, 0, 0)
  62. const n = seq.size()
  63. for (let i = 0; i < n; i++) {
  64. a.x += seq.getOrdinate(i, CoordinateSequence.X)
  65. a.y += seq.getOrdinate(i, CoordinateSequence.Y)
  66. a.setZ(a.getZ() + seq.getOrdinate(i, CoordinateSequence.Z))
  67. }
  68. a.x /= n
  69. a.y /= n
  70. a.setZ(a.getZ() / n)
  71. return a
  72. }
  73. getPolygon() {
  74. return this._poly
  75. }
  76. getPlane() {
  77. return this._plane
  78. }
  79. findBestFitPlane(poly) {
  80. const seq = poly.getExteriorRing().getCoordinateSequence()
  81. const basePt = this.averagePoint(seq)
  82. const normal = this.averageNormal(seq)
  83. return new Plane3D(normal, basePt)
  84. }
  85. averageNormal(seq) {
  86. const n = seq.size()
  87. const sum = new Coordinate(0, 0, 0)
  88. const p1 = new Coordinate(0, 0, 0)
  89. const p2 = new Coordinate(0, 0, 0)
  90. for (let i = 0; i < n - 1; i++) {
  91. seq.getCoordinate(i, p1)
  92. seq.getCoordinate(i + 1, p2)
  93. sum.x += (p1.y - p2.y) * (p1.getZ() + p2.getZ())
  94. sum.y += (p1.getZ() - p2.getZ()) * (p1.x + p2.x)
  95. sum.setZ(sum.getZ() + (p1.x - p2.x) * (p1.y + p2.y))
  96. }
  97. sum.x /= n
  98. sum.y /= n
  99. sum.setZ(sum.getZ() / n)
  100. const norm = Vector3D.create(sum).normalize()
  101. return norm
  102. }
  103. locate(pt, ring) {
  104. const seq = ring.getCoordinateSequence()
  105. const seqProj = PlanarPolygon3D.project(seq, this._facingPlane)
  106. const ptProj = PlanarPolygon3D.project(pt, this._facingPlane)
  107. return RayCrossingCounter.locatePointInRing(ptProj, seqProj)
  108. }
  109. }