tldraw/state/code/utils.ts

171 lines
4.2 KiB
TypeScript
Raw Normal View History

2021-06-08 10:32:20 +00:00
import { Bounds } from 'types'
import Vector, { Point } from './vector'
2021-05-15 13:02:13 +00:00
2021-06-23 22:32:21 +00:00
/**
* ## Utils
*/
2021-05-15 13:02:13 +00:00
export default class Utils {
2021-06-21 21:35:28 +00:00
static vectorToPoint(point: number[] | Vector | undefined): number[] {
if (typeof point === 'undefined') {
return [0, 0]
}
if (point instanceof Vector) {
return [point.x, point.y]
}
return point
}
static getRayRayIntersection(
p0: Vector,
n0: Vector,
p1: Vector,
n1: Vector
): Vector {
2021-05-15 13:02:13 +00:00
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
2021-06-21 21:35:28 +00:00
): Vector {
2021-05-15 13:02:13 +00:00
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)
}
2021-06-21 21:35:28 +00:00
static shortAngleDist(a: number, b: number): number {
2021-05-15 13:02:13 +00:00
const max = Math.PI * 2
const da = (b - a) % max
return ((2 * da) % max) - da
}
2021-06-21 21:35:28 +00:00
static getSweep(C: Vector, A: Vector, B: Vector): number {
2021-05-15 13:02:13 +00:00
return Utils.shortAngleDist(Vector.ang(C, A), Vector.ang(C, B))
}
2021-06-21 21:35:28 +00:00
static bez1d(a: number, b: number, c: number, d: number, t: number): number {
2021-05-15 13:02:13 +00:00
return (
a * (1 - t) * (1 - t) * (1 - t) +
3 * b * t * (1 - t) * (1 - t) +
3 * c * t * t * (1 - t) +
d * t * t * t
)
}
static getCubicBezierBounds(
p0: Point | Vector,
c0: Point | Vector,
c1: Point | Vector,
p1: Point | Vector
): Bounds {
// solve for x
let a = 3 * p1[0] - 9 * c1[0] + 9 * c0[0] - 3 * p0[0]
let b = 6 * p0[0] - 12 * c0[0] + 6 * c1[0]
let c = 3 * c0[0] - 3 * p0[0]
let disc = b * b - 4 * a * c
let xl = p0[0]
let xh = p0[0]
if (p1[0] < xl) xl = p1[0]
if (p1[0] > xh) xh = p1[0]
if (disc >= 0) {
const t1 = (-b + Math.sqrt(disc)) / (2 * a)
if (t1 > 0 && t1 < 1) {
const x1 = Utils.bez1d(p0[0], c0[0], c1[0], p1[0], t1)
if (x1 < xl) xl = x1
if (x1 > xh) xh = x1
}
const t2 = (-b - Math.sqrt(disc)) / (2 * a)
if (t2 > 0 && t2 < 1) {
const x2 = Utils.bez1d(p0[0], c0[0], c1[0], p1[0], t2)
if (x2 < xl) xl = x2
if (x2 > xh) xh = x2
}
}
// Solve for y
a = 3 * p1[1] - 9 * c1[1] + 9 * c0[1] - 3 * p0[1]
b = 6 * p0[1] - 12 * c0[1] + 6 * c1[1]
c = 3 * c0[1] - 3 * p0[1]
disc = b * b - 4 * a * c
let yl = p0[1]
let yh = p0[1]
if (p1[1] < yl) yl = p1[1]
if (p1[1] > yh) yh = p1[1]
if (disc >= 0) {
const t1 = (-b + Math.sqrt(disc)) / (2 * a)
if (t1 > 0 && t1 < 1) {
const y1 = Utils.bez1d(p0[1], c0[1], c1[1], p1[1], t1)
if (y1 < yl) yl = y1
if (y1 > yh) yh = y1
}
const t2 = (-b - Math.sqrt(disc)) / (2 * a)
if (t2 > 0 && t2 < 1) {
const y2 = Utils.bez1d(p0[1], c0[1], c1[1], p1[1], t2)
if (y2 < yl) yl = y2
if (y2 > yh) yh = y2
}
}
return {
minX: xl,
minY: yl,
maxX: xh,
maxY: yh,
width: Math.abs(xl - xh),
height: Math.abs(yl - yh),
}
}
2021-06-21 21:35:28 +00:00
static getExpandedBounds(a: Bounds, b: Bounds): Bounds {
2021-05-15 13:02:13 +00:00
const minX = Math.min(a.minX, b.minX),
minY = Math.min(a.minY, b.minY),
maxX = Math.max(a.maxX, b.maxX),
maxY = Math.max(a.maxY, b.maxY),
width = Math.abs(maxX - minX),
height = Math.abs(maxY - minY)
return { minX, minY, maxX, maxY, width, height }
}
2021-06-21 21:35:28 +00:00
static getCommonBounds(...b: Bounds[]): Bounds {
2021-05-15 13:02:13 +00:00
if (b.length < 2) return b[0]
let bounds = b[0]
for (let i = 1; i < b.length; i++) {
bounds = Utils.getExpandedBounds(bounds, b[i])
}
return bounds
}
}