| 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
  }
}
 |