You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

vector.ts 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. export interface VectorOptions {
  2. x: number
  3. y: number
  4. }
  5. export interface Point {
  6. x: number
  7. y: number
  8. }
  9. export default class Vector {
  10. x = 0
  11. y = 0
  12. constructor(x: number, y: number)
  13. constructor(vector: Vector, b?: undefined)
  14. constructor(options: Point, b?: undefined)
  15. constructor(a: VectorOptions | Vector | number, b?: number) {
  16. if (typeof a === "number") {
  17. this.x = a
  18. this.y = b
  19. } else {
  20. const { x = 0, y = 0 } = a
  21. this.x = x
  22. this.y = y
  23. }
  24. }
  25. set(v: Vector | Point) {
  26. this.x = v.x
  27. this.y = v.y
  28. }
  29. copy() {
  30. return new Vector(this)
  31. }
  32. clone() {
  33. return this.copy()
  34. }
  35. toArray() {
  36. return [this.x, this.y]
  37. }
  38. add(b: Vector) {
  39. this.x += b.x
  40. this.y += b.y
  41. return this
  42. }
  43. static add(a: Vector, b: Vector) {
  44. const n = new Vector(a)
  45. n.x += b.x
  46. n.y += b.y
  47. return n
  48. }
  49. sub(b: Vector) {
  50. this.x -= b.x
  51. this.y -= b.y
  52. return this
  53. }
  54. static sub(a: Vector, b: Vector) {
  55. const n = new Vector(a)
  56. n.x -= b.x
  57. n.y -= b.y
  58. return n
  59. }
  60. mul(b: number): Vector
  61. mul(b: Vector): Vector
  62. mul(b: Vector | number) {
  63. if (b instanceof Vector) {
  64. this.x *= b.x
  65. this.y *= b.y
  66. } else {
  67. this.x *= b
  68. this.y *= b
  69. }
  70. return this
  71. }
  72. mulScalar(b: number) {
  73. return this.mul(b)
  74. }
  75. static mulScalar(a: Vector, b: number) {
  76. return Vector.mul(a, b)
  77. }
  78. static mul(a: Vector, b: number): Vector
  79. static mul(a: Vector, b: Vector): Vector
  80. static mul(a: Vector, b: Vector | number) {
  81. const n = new Vector(a)
  82. if (b instanceof Vector) {
  83. n.x *= b.x
  84. n.y *= b.y
  85. } else {
  86. n.x *= b
  87. n.y *= b
  88. }
  89. return n
  90. }
  91. div(b: number): Vector
  92. div(b: Vector): Vector
  93. div(b: Vector | number) {
  94. if (b instanceof Vector) {
  95. if (b.x) {
  96. this.x /= b.x
  97. }
  98. if (b.y) {
  99. this.y /= b.y
  100. }
  101. } else {
  102. if (b) {
  103. this.x /= b
  104. this.y /= b
  105. }
  106. }
  107. return this
  108. }
  109. static div(a: Vector, b: number): Vector
  110. static div(a: Vector, b: Vector): Vector
  111. static div(a: Vector, b: Vector | number) {
  112. const n = new Vector(a)
  113. if (b instanceof Vector) {
  114. if (b.x) n.x /= b.x
  115. if (b.y) n.y /= b.y
  116. } else {
  117. if (b) {
  118. n.x /= b
  119. n.y /= b
  120. }
  121. }
  122. return n
  123. }
  124. divScalar(b: number) {
  125. return this.div(b)
  126. }
  127. static divScalar(a: Vector, b: number) {
  128. return Vector.div(a, b)
  129. }
  130. vec(b: Vector) {
  131. const { x, y } = this
  132. this.x = b.x - x
  133. this.y = b.y - y
  134. return this
  135. }
  136. static vec(a: Vector, b: Vector) {
  137. const n = new Vector(a)
  138. n.x = b.x - a.x
  139. n.y = b.y - a.y
  140. return n
  141. }
  142. pry(b: Vector) {
  143. return this.dpr(b) / b.len()
  144. }
  145. static pry(a: Vector, b: Vector) {
  146. return a.dpr(b) / b.len()
  147. }
  148. dpr(b: Vector) {
  149. return this.x * b.x + this.y * b.y
  150. }
  151. static dpr(a: Vector, b: Vector) {
  152. return a.x & (b.x + a.y * b.y)
  153. }
  154. cpr(b: Vector) {
  155. return this.x * b.y - b.y * this.y
  156. }
  157. static cpr(a: Vector, b: Vector) {
  158. return a.x * b.y - b.y * a.y
  159. }
  160. tangent(b: Vector) {
  161. return this.sub(b).uni()
  162. }
  163. static tangent(a: Vector, b: Vector) {
  164. const n = new Vector(a)
  165. return n.sub(b).uni()
  166. }
  167. dist2(b: Vector) {
  168. return this.sub(b).len2()
  169. }
  170. static dist2(a: Vector, b: Vector) {
  171. const n = new Vector(a)
  172. return n.sub(b).len2()
  173. }
  174. dist(b: Vector) {
  175. return Math.hypot(b.y - this.y, b.x - this.x)
  176. }
  177. static dist(a: Vector, b: Vector) {
  178. const n = new Vector(a)
  179. return Math.hypot(b.y - n.y, b.x - n.x)
  180. }
  181. ang(b: Vector) {
  182. return Math.atan2(b.y - this.y, b.x - this.x)
  183. }
  184. static ang(a: Vector, b: Vector) {
  185. const n = new Vector(a)
  186. return Math.atan2(b.y - n.y, b.x - n.x)
  187. }
  188. med(b: Vector) {
  189. return this.add(b).mul(0.5)
  190. }
  191. static med(a: Vector, b: Vector) {
  192. const n = new Vector(a)
  193. return n.add(b).mul(0.5)
  194. }
  195. rot(r: number) {
  196. const { x, y } = this
  197. this.x = x * Math.cos(r) - y * Math.sin(r)
  198. this.y = x * Math.sin(r) + y * Math.cos(r)
  199. return this
  200. }
  201. static rot(a: Vector, r: number) {
  202. const n = new Vector(a)
  203. n.x = a.x * Math.cos(r) - a.y * Math.sin(r)
  204. n.y = a.x * Math.sin(r) + a.y * Math.cos(r)
  205. return n
  206. }
  207. rotAround(b: Vector, r: number) {
  208. const { x, y } = this
  209. const s = Math.sin(r)
  210. const c = Math.cos(r)
  211. const px = x - b.x
  212. const py = y - b.y
  213. this.x = px * c - py * s + b.x
  214. this.y = px * s + py * c + b.y
  215. return this
  216. }
  217. static rotAround(a: Vector, b: Vector, r: number) {
  218. const n = new Vector(a)
  219. const s = Math.sin(r)
  220. const c = Math.cos(r)
  221. const px = n.x - b.x
  222. const py = n.y - b.y
  223. n.x = px * c - py * s + b.x
  224. n.y = px * s + py * c + b.y
  225. return n
  226. }
  227. lrp(b: Vector, t: number) {
  228. const n = new Vector(this)
  229. this.vec(b).mul(t).add(n)
  230. }
  231. static lrp(a: Vector, b: Vector, t: number) {
  232. const n = new Vector(a)
  233. n.vec(b).mul(t).add(a)
  234. return n
  235. }
  236. nudge(b: Vector, d: number) {
  237. this.add(b.mul(d))
  238. }
  239. static nudge(a: Vector, b: Vector, d: number) {
  240. const n = new Vector(a)
  241. return n.add(b.mul(d))
  242. }
  243. nudgeToward(b: Vector, d: number) {
  244. return this.nudge(Vector.vec(this, b).uni(), d)
  245. }
  246. static nudgeToward(a: Vector, b: Vector, d: number) {
  247. return Vector.nudge(a, Vector.vec(a, b).uni(), d)
  248. }
  249. int(b: Vector, from: number, to: number, s: number) {
  250. const t = (Math.max(from, to) - from) / (to - from)
  251. this.add(Vector.mul(this, 1 - t).add(Vector.mul(b, s)))
  252. return this
  253. }
  254. static int(a: Vector, b: Vector, from: number, to: number, s: number) {
  255. const n = new Vector(a)
  256. const t = (Math.max(from, to) - from) / (to - from)
  257. n.add(Vector.mul(a, 1 - t).add(Vector.mul(b, s)))
  258. return n
  259. }
  260. equals(b: Vector) {
  261. return this.x === b.x && this.y === b.y
  262. }
  263. static equals(a: Vector, b: Vector) {
  264. return a.x === b.x && a.y === b.y
  265. }
  266. abs() {
  267. this.x = Math.abs(this.x)
  268. this.y = Math.abs(this.y)
  269. return this
  270. }
  271. static abs(a: Vector) {
  272. const n = new Vector(a)
  273. n.x = Math.abs(n.x)
  274. n.y = Math.abs(n.y)
  275. return n
  276. }
  277. len() {
  278. return Math.hypot(this.x, this.y)
  279. }
  280. static len(a: Vector) {
  281. return Math.hypot(a.x, a.y)
  282. }
  283. len2() {
  284. return this.x * this.x + this.y * this.y
  285. }
  286. static len2(a: Vector) {
  287. return a.x * a.x + a.y * a.y
  288. }
  289. per() {
  290. const t = this.x
  291. this.x = this.y
  292. this.y = -t
  293. return this
  294. }
  295. static per(a: Vector) {
  296. const n = new Vector(a)
  297. n.x = n.y
  298. n.y = -a.x
  299. return n
  300. }
  301. neg() {
  302. this.x *= -1
  303. this.y *= -1
  304. return this
  305. }
  306. static neg(v: Vector) {
  307. const n = new Vector(v)
  308. n.x *= -1
  309. n.y *= -1
  310. return n
  311. }
  312. uni() {
  313. return this.div(this.len())
  314. }
  315. static uni(v: Vector) {
  316. const n = new Vector(v)
  317. return n.div(n.len())
  318. }
  319. normalize() {
  320. return this.uni()
  321. }
  322. static normalize(v: Vector) {
  323. return Vector.uni(v)
  324. }
  325. isLeft(center: Vector, b: Vector) {
  326. return (
  327. (center.x - this.x) * (b.y - this.y) - (b.x - this.x) * (center.y - b.y)
  328. )
  329. }
  330. static isLeft(center: Vector, a: Vector, b: Vector) {
  331. return (center.x - a.x) * (b.y - a.y) - (b.x - a.x) * (center.y - b.y)
  332. }
  333. static ang3(center: Vector, a: Vector, b: Vector) {
  334. const v1 = Vector.vec(center, a)
  335. const v2 = Vector.vec(center, b)
  336. return Vector.ang(v1, v2)
  337. }
  338. static clockwise(center: Vector, a: Vector, b: Vector) {
  339. return Vector.isLeft(center, a, b) > 0
  340. }
  341. static cast(v: Point | Vector) {
  342. return "cast" in v ? v : new Vector(v)
  343. }
  344. static from(v: Vector) {
  345. return new Vector(v)
  346. }
  347. nearestPointOnLineThroughPoint(b: Vector, u: Vector) {
  348. return this.clone().add(u.clone().mul(Vector.sub(this, b).pry(u)))
  349. }
  350. static nearestPointOnLineThroughPoint(a: Vector, b: Vector, u: Vector) {
  351. return a.clone().add(u.clone().mul(Vector.sub(a, b).pry(u)))
  352. }
  353. distanceToLineThroughPoint(b: Vector, u: Vector) {
  354. return this.dist(Vector.nearestPointOnLineThroughPoint(b, u, this))
  355. }
  356. static distanceToLineThroughPoint(a: Vector, b: Vector, u: Vector) {
  357. return a.dist(Vector.nearestPointOnLineThroughPoint(b, u, a))
  358. }
  359. nearestPointOnLineSegment(p0: Vector, p1: Vector, clamp = true) {
  360. return Vector.nearestPointOnLineSegment(this, p0, p1, clamp)
  361. }
  362. static nearestPointOnLineSegment(
  363. a: Vector,
  364. p0: Vector,
  365. p1: Vector,
  366. clamp = true
  367. ) {
  368. const delta = Vector.sub(p1, p0)
  369. const length = delta.len()
  370. const u = Vector.div(delta, length)
  371. const pt = Vector.add(p0, Vector.mul(u, Vector.pry(Vector.sub(a, p0), u)))
  372. if (clamp) {
  373. const da = p0.dist(pt)
  374. const db = p1.dist(pt)
  375. if (db < da && da > length) return p1
  376. if (da < db && db > length) return p0
  377. }
  378. return pt
  379. }
  380. distanceToLineSegment(p0: Vector, p1: Vector, clamp = true) {
  381. return Vector.distanceToLineSegment(this, p0, p1, clamp)
  382. }
  383. static distanceToLineSegment(
  384. a: Vector,
  385. p0: Vector,
  386. p1: Vector,
  387. clamp = true
  388. ) {
  389. return Vector.dist(a, Vector.nearestPointOnLineSegment(a, p0, p1, clamp))
  390. }
  391. }