tldraw/state/code/vector.ts

543 lines
11 KiB
TypeScript
Raw Normal View History

2021-05-15 13:02:13 +00:00
export interface VectorOptions {
x: number
y: number
}
export interface Point {
x: number
y: number
}
2021-06-23 22:32:21 +00:00
/**
* ## Vector
*/
2021-05-15 13:02:13 +00:00
export default class Vector {
x = 0
y = 0
constructor(x: number, y: number)
constructor(vector: Vector, b?: undefined)
constructor(options: Point, b?: undefined)
constructor(a: VectorOptions | Vector | number, b?: number) {
2021-06-08 10:32:20 +00:00
if (typeof a === 'number') {
2021-05-15 13:02:13 +00:00
this.x = a
this.y = b
} else {
const { x = 0, y = 0 } = a
this.x = x
this.y = y
}
}
2021-06-21 21:35:28 +00:00
set(v: Vector | Point): Vector {
2021-05-15 13:02:13 +00:00
this.x = v.x
this.y = v.y
2021-06-21 21:35:28 +00:00
return this
2021-05-15 13:02:13 +00:00
}
2021-06-21 21:35:28 +00:00
copy(): Vector {
2021-05-15 13:02:13 +00:00
return new Vector(this)
}
2021-06-21 21:35:28 +00:00
clone(): Vector {
2021-05-15 13:02:13 +00:00
return this.copy()
}
2021-06-21 21:35:28 +00:00
toArray(): number[] {
2021-05-15 13:02:13 +00:00
return [this.x, this.y]
}
2021-06-21 21:35:28 +00:00
add(b: Vector): Vector {
2021-05-15 13:02:13 +00:00
this.x += b.x
this.y += b.y
return this
}
2021-06-21 21:35:28 +00:00
static add(a: Vector, b: Vector): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
n.x += b.x
n.y += b.y
return n
}
2021-06-21 21:35:28 +00:00
sub(b: Vector): Vector {
2021-05-15 13:02:13 +00:00
this.x -= b.x
this.y -= b.y
return this
}
2021-06-21 21:35:28 +00:00
static sub(a: Vector, b: Vector): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
n.x -= b.x
n.y -= b.y
return n
}
mul(b: number): Vector
mul(b: Vector): Vector
2021-06-21 21:35:28 +00:00
mul(b: Vector | number): Vector {
2021-05-15 13:02:13 +00:00
if (b instanceof Vector) {
this.x *= b.x
this.y *= b.y
} else {
this.x *= b
this.y *= b
}
return this
}
2021-06-21 21:35:28 +00:00
mulScalar(b: number): Vector {
2021-05-15 13:02:13 +00:00
return this.mul(b)
}
2021-06-21 21:35:28 +00:00
static mulScalar(a: Vector, b: number): Vector {
2021-05-15 13:02:13 +00:00
return Vector.mul(a, b)
}
static mul(a: Vector, b: number): Vector
static mul(a: Vector, b: Vector): Vector
2021-06-21 21:35:28 +00:00
static mul(a: Vector, b: Vector | number): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
if (b instanceof Vector) {
n.x *= b.x
n.y *= b.y
} else {
n.x *= b
n.y *= b
}
return n
}
div(b: number): Vector
div(b: Vector): Vector
2021-06-21 21:35:28 +00:00
div(b: Vector | number): Vector {
2021-05-15 13:02:13 +00:00
if (b instanceof Vector) {
if (b.x) {
this.x /= b.x
}
if (b.y) {
this.y /= b.y
}
} else {
if (b) {
this.x /= b
this.y /= b
}
}
return this
}
static div(a: Vector, b: number): Vector
static div(a: Vector, b: Vector): Vector
2021-06-21 21:35:28 +00:00
static div(a: Vector, b: Vector | number): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
if (b instanceof Vector) {
if (b.x) n.x /= b.x
if (b.y) n.y /= b.y
} else {
if (b) {
n.x /= b
n.y /= b
}
}
return n
}
2021-06-21 21:35:28 +00:00
divScalar(b: number): Vector {
2021-05-15 13:02:13 +00:00
return this.div(b)
}
2021-06-21 21:35:28 +00:00
static divScalar(a: Vector, b: number): Vector {
2021-05-15 13:02:13 +00:00
return Vector.div(a, b)
}
2021-06-21 21:35:28 +00:00
vec(b: Vector): Vector {
2021-05-15 13:02:13 +00:00
const { x, y } = this
this.x = b.x - x
this.y = b.y - y
return this
}
2021-06-21 21:35:28 +00:00
static vec(a: Vector, b: Vector): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
n.x = b.x - a.x
n.y = b.y - a.y
return n
}
2021-06-21 21:35:28 +00:00
pry(b: Vector): number {
2021-05-15 13:02:13 +00:00
return this.dpr(b) / b.len()
}
2021-06-21 21:35:28 +00:00
static pry(a: Vector, b: Vector): number {
2021-05-15 13:02:13 +00:00
return a.dpr(b) / b.len()
}
2021-06-21 21:35:28 +00:00
dpr(b: Vector): number {
2021-05-15 13:02:13 +00:00
return this.x * b.x + this.y * b.y
}
2021-06-21 21:35:28 +00:00
static dpr(a: Vector, b: Vector): number {
2021-05-15 13:02:13 +00:00
return a.x & (b.x + a.y * b.y)
}
2021-06-21 21:35:28 +00:00
cpr(b: Vector): number {
2021-05-15 13:02:13 +00:00
return this.x * b.y - b.y * this.y
}
2021-06-21 21:35:28 +00:00
static cpr(a: Vector, b: Vector): number {
2021-05-15 13:02:13 +00:00
return a.x * b.y - b.y * a.y
}
2021-06-21 21:35:28 +00:00
tangent(b: Vector): Vector {
2021-05-15 13:02:13 +00:00
return this.sub(b).uni()
}
2021-06-21 21:35:28 +00:00
static tangent(a: Vector, b: Vector): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
return n.sub(b).uni()
}
2021-06-21 21:35:28 +00:00
dist2(b: Vector): number {
2021-05-15 13:02:13 +00:00
return this.sub(b).len2()
}
2021-06-21 21:35:28 +00:00
static dist2(a: Vector, b: Vector): number {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
return n.sub(b).len2()
}
2021-06-21 21:35:28 +00:00
dist(b: Vector): number {
2021-05-15 13:02:13 +00:00
return Math.hypot(b.y - this.y, b.x - this.x)
}
2021-06-21 21:35:28 +00:00
static dist(a: Vector, b: Vector): number {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
return Math.hypot(b.y - n.y, b.x - n.x)
}
2021-06-21 21:35:28 +00:00
ang(b: Vector): number {
2021-05-15 13:02:13 +00:00
return Math.atan2(b.y - this.y, b.x - this.x)
}
2021-06-21 21:35:28 +00:00
static ang(a: Vector, b: Vector): number {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
return Math.atan2(b.y - n.y, b.x - n.x)
}
2021-06-21 21:35:28 +00:00
med(b: Vector): Vector {
2021-05-15 13:02:13 +00:00
return this.add(b).mul(0.5)
}
2021-06-21 21:35:28 +00:00
static med(a: Vector, b: Vector): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
return n.add(b).mul(0.5)
}
2021-06-21 21:35:28 +00:00
rot(r: number): Vector {
2021-05-15 13:02:13 +00:00
const { x, y } = this
this.x = x * Math.cos(r) - y * Math.sin(r)
this.y = x * Math.sin(r) + y * Math.cos(r)
return this
}
2021-06-21 21:35:28 +00:00
static rot(a: Vector, r: number): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
n.x = a.x * Math.cos(r) - a.y * Math.sin(r)
n.y = a.x * Math.sin(r) + a.y * Math.cos(r)
return n
}
2021-06-21 21:35:28 +00:00
rotAround(b: Vector, r: number): Vector {
2021-05-15 13:02:13 +00:00
const { x, y } = this
const s = Math.sin(r)
const c = Math.cos(r)
const px = x - b.x
const py = y - b.y
this.x = px * c - py * s + b.x
this.y = px * s + py * c + b.y
return this
}
2021-06-21 21:35:28 +00:00
static rotAround(a: Vector, b: Vector, r: number): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
const s = Math.sin(r)
const c = Math.cos(r)
const px = n.x - b.x
const py = n.y - b.y
n.x = px * c - py * s + b.x
n.y = px * s + py * c + b.y
return n
}
2021-06-21 21:35:28 +00:00
lrp(b: Vector, t: number): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(this)
2021-05-16 08:33:08 +00:00
this.vec(b).mul(t).add(n)
2021-06-21 21:35:28 +00:00
return this
2021-05-15 13:02:13 +00:00
}
2021-06-21 21:35:28 +00:00
static lrp(a: Vector, b: Vector, t: number): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
2021-05-16 08:33:08 +00:00
n.vec(b).mul(t).add(a)
2021-05-15 13:02:13 +00:00
return n
}
2021-06-21 21:35:28 +00:00
nudge(b: Vector, d: number): Vector {
2021-05-15 13:02:13 +00:00
this.add(b.mul(d))
2021-06-21 21:35:28 +00:00
return this
2021-05-15 13:02:13 +00:00
}
2021-06-21 21:35:28 +00:00
static nudge(a: Vector, b: Vector, d: number): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
return n.add(b.mul(d))
}
2021-06-21 21:35:28 +00:00
nudgeToward(b: Vector, d: number): Vector {
2021-05-15 13:02:13 +00:00
return this.nudge(Vector.vec(this, b).uni(), d)
}
2021-06-21 21:35:28 +00:00
static nudgeToward(a: Vector, b: Vector, d: number): Vector {
2021-05-15 13:02:13 +00:00
return Vector.nudge(a, Vector.vec(a, b).uni(), d)
}
2021-06-21 21:35:28 +00:00
int(b: Vector, from: number, to: number, s: number): Vector {
2021-05-15 13:02:13 +00:00
const t = (Math.max(from, to) - from) / (to - from)
this.add(Vector.mul(this, 1 - t).add(Vector.mul(b, s)))
return this
}
2021-06-21 21:35:28 +00:00
static int(
a: Vector,
b: Vector,
from: number,
to: number,
s: number
): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
const t = (Math.max(from, to) - from) / (to - from)
n.add(Vector.mul(a, 1 - t).add(Vector.mul(b, s)))
return n
}
2021-06-21 21:35:28 +00:00
equals(b: Vector): boolean {
2021-05-15 13:02:13 +00:00
return this.x === b.x && this.y === b.y
}
2021-06-21 21:35:28 +00:00
static equals(a: Vector, b: Vector): boolean {
2021-05-15 13:02:13 +00:00
return a.x === b.x && a.y === b.y
}
2021-06-21 21:35:28 +00:00
abs(): Vector {
2021-05-15 13:02:13 +00:00
this.x = Math.abs(this.x)
this.y = Math.abs(this.y)
return this
}
2021-06-21 21:35:28 +00:00
static abs(a: Vector): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
n.x = Math.abs(n.x)
n.y = Math.abs(n.y)
return n
}
2021-06-21 21:35:28 +00:00
len(): number {
2021-05-15 13:02:13 +00:00
return Math.hypot(this.x, this.y)
}
2021-06-21 21:35:28 +00:00
static len(a: Vector): number {
2021-05-15 13:02:13 +00:00
return Math.hypot(a.x, a.y)
}
2021-06-21 21:35:28 +00:00
len2(): number {
2021-05-15 13:02:13 +00:00
return this.x * this.x + this.y * this.y
}
2021-06-21 21:35:28 +00:00
static len2(a: Vector): number {
2021-05-15 13:02:13 +00:00
return a.x * a.x + a.y * a.y
}
2021-06-21 21:35:28 +00:00
per(): Vector {
2021-05-15 13:02:13 +00:00
const t = this.x
this.x = this.y
this.y = -t
return this
}
2021-06-21 21:35:28 +00:00
static per(a: Vector): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(a)
n.x = n.y
n.y = -a.x
return n
}
2021-06-21 21:35:28 +00:00
neg(): Vector {
2021-05-15 13:02:13 +00:00
this.x *= -1
this.y *= -1
return this
}
2021-06-21 21:35:28 +00:00
static neg(v: Vector): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(v)
n.x *= -1
n.y *= -1
return n
}
2021-06-21 21:35:28 +00:00
uni(): Vector {
2021-05-15 13:02:13 +00:00
return this.div(this.len())
}
2021-06-21 21:35:28 +00:00
static uni(v: Vector): Vector {
2021-05-15 13:02:13 +00:00
const n = new Vector(v)
return n.div(n.len())
}
2021-06-21 21:35:28 +00:00
normalize(): Vector {
2021-05-16 08:33:08 +00:00
return this.uni()
}
2021-06-21 21:35:28 +00:00
static normalize(v: Vector): Vector {
2021-05-16 08:33:08 +00:00
return Vector.uni(v)
}
2021-06-21 21:35:28 +00:00
isLeft(center: Vector, b: Vector): number {
2021-05-15 13:02:13 +00:00
return (
(center.x - this.x) * (b.y - this.y) - (b.x - this.x) * (center.y - b.y)
)
}
2021-06-21 21:35:28 +00:00
static isLeft(center: Vector, a: Vector, b: Vector): number {
2021-05-15 13:02:13 +00:00
return (center.x - a.x) * (b.y - a.y) - (b.x - a.x) * (center.y - b.y)
}
2021-06-21 21:35:28 +00:00
static ang3(center: Vector, a: Vector, b: Vector): number {
2021-05-15 13:02:13 +00:00
const v1 = Vector.vec(center, a)
const v2 = Vector.vec(center, b)
return Vector.ang(v1, v2)
}
2021-06-21 21:35:28 +00:00
static clockwise(center: Vector, a: Vector, b: Vector): boolean {
2021-05-15 13:02:13 +00:00
return Vector.isLeft(center, a, b) > 0
}
2021-06-21 21:35:28 +00:00
static cast(v: Point | Vector): Vector {
2021-06-08 10:32:20 +00:00
return 'cast' in v ? v : new Vector(v)
2021-05-15 13:02:13 +00:00
}
2021-06-21 21:35:28 +00:00
static from(v: Vector): Vector {
2021-05-15 13:02:13 +00:00
return new Vector(v)
}
2021-06-21 21:35:28 +00:00
nearestPointOnLineThroughPoint(b: Vector, u: Vector): Vector {
2021-05-15 13:02:13 +00:00
return this.clone().add(u.clone().mul(Vector.sub(this, b).pry(u)))
}
2021-06-21 21:35:28 +00:00
static nearestPointOnLineThroughPoint(
a: Vector,
b: Vector,
u: Vector
): Vector {
2021-05-15 13:02:13 +00:00
return a.clone().add(u.clone().mul(Vector.sub(a, b).pry(u)))
}
2021-06-21 21:35:28 +00:00
distanceToLineThroughPoint(b: Vector, u: Vector): number {
2021-05-15 13:02:13 +00:00
return this.dist(Vector.nearestPointOnLineThroughPoint(b, u, this))
}
2021-06-21 21:35:28 +00:00
static distanceToLineThroughPoint(a: Vector, b: Vector, u: Vector): number {
2021-05-15 13:02:13 +00:00
return a.dist(Vector.nearestPointOnLineThroughPoint(b, u, a))
}
2021-06-21 21:35:28 +00:00
nearestPointOnLineSegment(p0: Vector, p1: Vector, clamp = true): Vector {
2021-05-15 13:02:13 +00:00
return Vector.nearestPointOnLineSegment(this, p0, p1, clamp)
}
static nearestPointOnLineSegment(
a: Vector,
p0: Vector,
p1: Vector,
clamp = true
2021-06-21 21:35:28 +00:00
): Vector {
2021-05-15 13:02:13 +00:00
const delta = Vector.sub(p1, p0)
const length = delta.len()
const u = Vector.div(delta, length)
const pt = Vector.add(p0, Vector.mul(u, Vector.pry(Vector.sub(a, p0), u)))
if (clamp) {
const da = p0.dist(pt)
const db = p1.dist(pt)
if (db < da && da > length) return p1
if (da < db && db > length) return p0
}
return pt
}
2021-06-21 21:35:28 +00:00
distanceToLineSegment(p0: Vector, p1: Vector, clamp = true): number {
2021-05-15 13:02:13 +00:00
return Vector.distanceToLineSegment(this, p0, p1, clamp)
}
static distanceToLineSegment(
a: Vector,
p0: Vector,
p1: Vector,
clamp = true
2021-06-21 21:35:28 +00:00
): number {
2021-05-15 13:02:13 +00:00
return Vector.dist(a, Vector.nearestPointOnLineSegment(a, p0, p1, clamp))
}
}
2021-06-24 08:18:14 +00:00
export class Utils {
static getRayRayIntersection(
p0: Vector,
n0: Vector,
p1: Vector,
n1: Vector
): Vector {
const p0e = Vector.add(p0, n0),
p1e = Vector.add(p1, n1),
m0 = (p0e.y - p0.y) / (p0e.x - p0.x),
m1 = (p1e.y - p1.y) / (p1e.x - p1.x),
b0 = p0.y - m0 * p0.x,
b1 = p1.y - m1 * p1.x,
x = (b1 - b0) / (m0 - m1),
y = m0 * x + b0
return new Vector({ x, y })
}
static getCircleTangentToPoint(
A: Point | Vector,
r0: number,
P: Point | Vector,
side: number
): Vector {
const v0 = Vector.cast(A)
const v1 = Vector.cast(P)
const B = Vector.lrp(v0, v1, 0.5),
r1 = Vector.dist(v0, B),
delta = Vector.sub(B, v0),
d = Vector.len(delta)
if (!(d <= r0 + r1 && d >= Math.abs(r0 - r1))) {
return
}
const a = (r0 * r0 - r1 * r1 + d * d) / (2.0 * d),
n = 1 / d,
p = Vector.add(v0, Vector.mul(delta, a * n)),
h = Math.sqrt(r0 * r0 - a * a),
k = Vector.mul(Vector.per(delta), h * n)
return side === 0 ? p.add(k) : p.sub(k)
}
}