123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- import { Bounds } from "types"
- import Vector, { Point } from "./vector"
-
- export default class Utils {
- static getRayRayIntersection(p0: Vector, n0: Vector, p1: Vector, n1: 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
- ) {
- 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)
- }
-
- static shortAngleDist(a: number, b: number) {
- const max = Math.PI * 2
- const da = (b - a) % max
- return ((2 * da) % max) - da
- }
-
- static getSweep(C: Vector, A: Vector, B: Vector) {
- return Utils.shortAngleDist(Vector.ang(C, A), Vector.ang(C, B))
- }
-
- static bez1d(a: number, b: number, c: number, d: number, t: number) {
- 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),
- }
- }
-
- static getExpandedBounds(a: Bounds, b: Bounds) {
- 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 }
- }
-
- static getCommonBounds(...b: Bounds[]) {
- 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
- }
- }
|