Vector2D.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import Coordinate from '../geom/Coordinate'
  2. import CGAlgorithmsDD from '../algorithm/CGAlgorithmsDD'
  3. import Angle from '../algorithm/Angle'
  4. import Assert from '../util/Assert'
  5. export default class Vector2D {
  6. constructor() {
  7. Vector2D.constructor_.apply(this, arguments)
  8. }
  9. static constructor_() {
  10. this._x = null
  11. this._y = null
  12. if (arguments.length === 0) {
  13. Vector2D.constructor_.call(this, 0.0, 0.0)
  14. } else if (arguments.length === 1) {
  15. if (arguments[0] instanceof Vector2D) {
  16. const v = arguments[0]
  17. this._x = v._x
  18. this._y = v._y
  19. } else if (arguments[0] instanceof Coordinate) {
  20. const v = arguments[0]
  21. this._x = v.x
  22. this._y = v.y
  23. }
  24. } else if (arguments.length === 2) {
  25. if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
  26. const x = arguments[0], y = arguments[1]
  27. this._x = x
  28. this._y = y
  29. } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
  30. const from = arguments[0], to = arguments[1]
  31. this._x = to.x - from.x
  32. this._y = to.y - from.y
  33. }
  34. }
  35. }
  36. static create() {
  37. if (arguments.length === 1) {
  38. if (arguments[0] instanceof Vector2D) {
  39. const v = arguments[0]
  40. return new Vector2D(v)
  41. } else if (arguments[0] instanceof Coordinate) {
  42. const coord = arguments[0]
  43. return new Vector2D(coord)
  44. }
  45. } else if (arguments.length === 2) {
  46. if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
  47. const x = arguments[0], y = arguments[1]
  48. return new Vector2D(x, y)
  49. } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
  50. const from = arguments[0], to = arguments[1]
  51. return new Vector2D(from, to)
  52. }
  53. }
  54. }
  55. dot(v) {
  56. return this._x * v._x + this._y * v._y
  57. }
  58. isParallel(v) {
  59. return 0.0 === CGAlgorithmsDD.signOfDet2x2(this._x, this._y, v._x, v._y)
  60. }
  61. getComponent(index) {
  62. if (index === 0) return this._x
  63. return this._y
  64. }
  65. subtract(v) {
  66. return Vector2D.create(this._x - v._x, this._y - v._y)
  67. }
  68. equals(o) {
  69. if (!(o instanceof Vector2D))
  70. return false
  71. const v = o
  72. return this._x === v._x && this._y === v._y
  73. }
  74. normalize() {
  75. const length = this.length()
  76. if (length > 0.0) return this.divide(length)
  77. return Vector2D.create(0.0, 0.0)
  78. }
  79. angle() {
  80. if (arguments.length === 0) {
  81. return Math.atan2(this._y, this._x)
  82. } else if (arguments.length === 1) {
  83. const v = arguments[0]
  84. return Angle.diff(v.angle(), this.angle())
  85. }
  86. }
  87. weightedSum(v, frac) {
  88. return Vector2D.create(frac * this._x + (1.0 - frac) * v._x, frac * this._y + (1.0 - frac) * v._y)
  89. }
  90. divide(d) {
  91. return Vector2D.create(this._x / d, this._y / d)
  92. }
  93. rotateByQuarterCircle(numQuarters) {
  94. let nQuad = numQuarters % 4
  95. if (numQuarters < 0 && nQuad !== 0)
  96. nQuad = nQuad + 4
  97. switch (nQuad) {
  98. case 0:
  99. return Vector2D.create(this._x, this._y)
  100. case 1:
  101. return Vector2D.create(-this._y, this._x)
  102. case 2:
  103. return Vector2D.create(-this._x, -this._y)
  104. case 3:
  105. return Vector2D.create(this._y, -this._x)
  106. }
  107. Assert.shouldNeverReachHere()
  108. return null
  109. }
  110. rotate(angle) {
  111. const cos = Math.cos(angle)
  112. const sin = Math.sin(angle)
  113. return Vector2D.create(this._x * cos - this._y * sin, this._x * sin + this._y * cos)
  114. }
  115. angleTo(v) {
  116. const a1 = this.angle()
  117. const a2 = v.angle()
  118. const angDel = a2 - a1
  119. if (angDel <= -Math.PI) return angDel + Angle.PI_TIMES_2
  120. if (angDel > Math.PI) return angDel - Angle.PI_TIMES_2
  121. return angDel
  122. }
  123. getX() {
  124. return this._x
  125. }
  126. lengthSquared() {
  127. return this._x * this._x + this._y * this._y
  128. }
  129. negate() {
  130. return Vector2D.create(-this._x, -this._y)
  131. }
  132. clone() {
  133. return new Vector2D(this)
  134. }
  135. toCoordinate() {
  136. return new Coordinate(this._x, this._y)
  137. }
  138. translate(coord) {
  139. return new Coordinate(this._x + coord.x, this._y + coord.y)
  140. }
  141. multiply(d) {
  142. return Vector2D.create(this._x * d, this._y * d)
  143. }
  144. toString() {
  145. return '[' + this._x + ', ' + this._y + ']'
  146. }
  147. length() {
  148. return Math.sqrt(this._x * this._x + this._y * this._y)
  149. }
  150. average(v) {
  151. return this.weightedSum(v, 0.5)
  152. }
  153. getY() {
  154. return this._y
  155. }
  156. add(v) {
  157. return Vector2D.create(this._x + v._x, this._y + v._y)
  158. }
  159. distance(v) {
  160. const delx = v._x - this._x
  161. const dely = v._y - this._y
  162. return Math.sqrt(delx * delx + dely * dely)
  163. }
  164. hashCode() {
  165. let result = 17
  166. result = 37 * result + Coordinate.hashCode(this._x)
  167. result = 37 * result + Coordinate.hashCode(this._y)
  168. return result
  169. }
  170. }