123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- import { Bounds } from "types"
- import * as vec from "utils/vec"
-
- interface Intersection {
- didIntersect: boolean
- message: string
- points: number[][]
- }
-
- function getIntersection(
- points: number[][],
- message = points.length ? "Intersection" : "No intersection"
- ) {
- return { didIntersect: points.length > 0, message, points }
- }
-
- export function intersectLineSegments(
- a1: number[],
- a2: number[],
- b1: number[],
- b2: number[]
- ) {
- const AB = vec.sub(a1, b1)
- const BV = vec.sub(b2, b1)
- const AV = vec.sub(a2, a1)
-
- const ua_t = BV[0] * AB[1] - BV[1] * AB[0]
- const ub_t = AV[0] * AB[1] - AV[1] * AB[0]
- const u_b = BV[1] * AV[0] - BV[0] * AV[1]
-
- if (ua_t === 0 || ub_t === 0) {
- return getIntersection([], "Coincident")
- }
-
- if (u_b === 0) {
- return getIntersection([], "Parallel")
- }
-
- if (u_b != 0) {
- const ua = ua_t / u_b
- const ub = ub_t / u_b
- if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
- return getIntersection([vec.add(a1, vec.mul(AV, ua))])
- }
- }
-
- return getIntersection([])
- }
-
- export function intersectCircleLineSegment(
- c: number[],
- r: number,
- a1: number[],
- a2: number[]
- ): Intersection {
- const a =
- (a2[0] - a1[0]) * (a2[0] - a1[0]) + (a2[1] - a1[1]) * (a2[1] - a1[1])
- const b =
- 2 * ((a2[0] - a1[0]) * (a1[0] - c[0]) + (a2[1] - a1[1]) * (a1[1] - c[1]))
- const cc =
- c[0] * c[0] +
- c[1] * c[1] +
- a1[0] * a1[0] +
- a1[1] * a1[1] -
- 2 * (c[0] * a1[0] + c[1] * a1[1]) -
- r * r
-
- const deter = b * b - 4 * a * cc
-
- if (deter < 0) {
- return { didIntersect: false, message: "outside", points: [] }
- }
-
- if (deter === 0) {
- return { didIntersect: false, message: "tangent", points: [] }
- }
-
- var e = Math.sqrt(deter)
- var u1 = (-b + e) / (2 * a)
- var u2 = (-b - e) / (2 * a)
- if ((u1 < 0 || u1 > 1) && (u2 < 0 || u2 > 1)) {
- if ((u1 < 0 && u2 < 0) || (u1 > 1 && u2 > 1)) {
- return { didIntersect: false, message: "outside", points: [] }
- } else {
- return { didIntersect: false, message: "inside", points: [] }
- }
- }
-
- const result = { didIntersect: true, message: "intersection", points: [] }
- if (0 <= u1 && u1 <= 1) result.points.push(vec.lrp(a1, a2, u1))
- if (0 <= u2 && u2 <= 1) result.points.push(vec.lrp(a1, a2, u2))
-
- return result
- }
-
- export function intersectCircleRectangle(
- c: number[],
- r: number,
- point: number[],
- size: number[]
- ): Intersection[] {
- const tl = point
- const tr = vec.add(point, [size[0], 0])
- const br = vec.add(point, size)
- const bl = vec.add(point, [0, size[1]])
-
- const intersections: Intersection[] = []
-
- const topIntersection = intersectCircleLineSegment(c, r, tl, tr)
- const rightIntersection = intersectCircleLineSegment(c, r, tr, br)
- const bottomIntersection = intersectCircleLineSegment(c, r, bl, br)
- const leftIntersection = intersectCircleLineSegment(c, r, tl, bl)
-
- if (topIntersection.didIntersect) {
- intersections.push({ ...topIntersection, message: "top" })
- }
-
- if (rightIntersection.didIntersect) {
- intersections.push({ ...rightIntersection, message: "right" })
- }
-
- if (bottomIntersection.didIntersect) {
- intersections.push({ ...bottomIntersection, message: "bottom" })
- }
-
- if (leftIntersection.didIntersect) {
- intersections.push({ ...leftIntersection, message: "left" })
- }
-
- return intersections
- }
-
- export function intersectRectangleLineSegment(
- point: number[],
- size: number[],
- a1: number[],
- a2: number[]
- ) {
- const tl = point
- const tr = vec.add(point, [size[0], 0])
- const br = vec.add(point, size)
- const bl = vec.add(point, [0, size[1]])
-
- const intersections: Intersection[] = []
-
- const topIntersection = intersectLineSegments(a1, a2, tl, tr)
- const rightIntersection = intersectLineSegments(a1, a2, tr, br)
- const bottomIntersection = intersectLineSegments(a1, a2, bl, br)
- const leftIntersection = intersectLineSegments(a1, a2, tl, bl)
-
- if (topIntersection.didIntersect) {
- intersections.push({ ...topIntersection, message: "top" })
- }
-
- if (rightIntersection.didIntersect) {
- intersections.push({ ...rightIntersection, message: "right" })
- }
-
- if (bottomIntersection.didIntersect) {
- intersections.push({ ...bottomIntersection, message: "bottom" })
- }
-
- if (leftIntersection.didIntersect) {
- intersections.push({ ...leftIntersection, message: "left" })
- }
-
- return intersections
- }
-
- /* -------------------------------------------------- */
- /* Shape vs. Bounds */
- /* -------------------------------------------------- */
-
- export function intersectCircleBounds(
- c: number[],
- r: number,
- bounds: Bounds
- ): Intersection[] {
- const { minX, minY, width, height } = bounds
- return intersectCircleRectangle(c, r, [minX, minY], [width, height])
- }
-
- export function intersectLineSegmentBounds(
- a1: number[],
- a2: number[],
- bounds: Bounds
- ) {
- const { minX, minY, width, height } = bounds
- return intersectRectangleLineSegment([minX, minY], [width, height], a1, a2)
- }
-
- export function intersectPolylineBounds(points: number[][], bounds: Bounds) {
- const { minX, minY, width, height } = bounds
- const intersections: Intersection[] = []
-
- for (let i = 1; i < points.length; i++) {
- intersections.push(
- ...intersectRectangleLineSegment(
- [minX, minY],
- [width, height],
- points[i - 1],
- points[i]
- )
- )
- }
-
- return intersections
- }
|