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 11KB

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