Adds transforming, ellipse
This commit is contained in:
parent
d99507de5b
commit
b50045c9b7
21 changed files with 1223 additions and 158 deletions
|
@ -7,10 +7,7 @@ interface Intersection {
|
|||
points: number[][]
|
||||
}
|
||||
|
||||
function getIntersection(
|
||||
points: number[][],
|
||||
message = points.length ? "Intersection" : "No intersection"
|
||||
) {
|
||||
function getIntersection(message: string, ...points: number[][]) {
|
||||
return { didIntersect: points.length > 0, message, points }
|
||||
}
|
||||
|
||||
|
@ -29,22 +26,22 @@ export function intersectLineSegments(
|
|||
const u_b = BV[1] * AV[0] - BV[0] * AV[1]
|
||||
|
||||
if (ua_t === 0 || ub_t === 0) {
|
||||
return getIntersection([], "Coincident")
|
||||
return getIntersection("coincident")
|
||||
}
|
||||
|
||||
if (u_b === 0) {
|
||||
return getIntersection([], "Parallel")
|
||||
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("intersection", vec.add(a1, vec.mul(AV, ua)))
|
||||
}
|
||||
}
|
||||
|
||||
return getIntersection([])
|
||||
return getIntersection("no intersection")
|
||||
}
|
||||
|
||||
export function intersectCircleLineSegment(
|
||||
|
@ -68,11 +65,11 @@ export function intersectCircleLineSegment(
|
|||
const deter = b * b - 4 * a * cc
|
||||
|
||||
if (deter < 0) {
|
||||
return { didIntersect: false, message: "outside", points: [] }
|
||||
return getIntersection("outside")
|
||||
}
|
||||
|
||||
if (deter === 0) {
|
||||
return { didIntersect: false, message: "tangent", points: [] }
|
||||
return getIntersection("tangent")
|
||||
}
|
||||
|
||||
var e = Math.sqrt(deter)
|
||||
|
@ -80,17 +77,71 @@ export function intersectCircleLineSegment(
|
|||
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: [] }
|
||||
return getIntersection("outside")
|
||||
} else {
|
||||
return { didIntersect: false, message: "inside", points: [] }
|
||||
return getIntersection("inside")
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
const results: number[][] = []
|
||||
if (0 <= u1 && u1 <= 1) results.push(vec.lrp(a1, a2, u1))
|
||||
if (0 <= u2 && u2 <= 1) results.push(vec.lrp(a1, a2, u2))
|
||||
|
||||
return result
|
||||
return getIntersection("intersection", ...results)
|
||||
}
|
||||
|
||||
export function intersectEllipseLineSegment(
|
||||
center: number[],
|
||||
rx: number,
|
||||
ry: number,
|
||||
a1: number[],
|
||||
a2: number[],
|
||||
rotation = 0
|
||||
) {
|
||||
// If the ellipse or line segment are empty, return no tValues.
|
||||
if (rx === 0 || ry === 0 || vec.isEqual(a1, a2)) {
|
||||
return getIntersection("No intersection")
|
||||
}
|
||||
|
||||
// Get the semimajor and semiminor axes.
|
||||
rx = rx < 0 ? rx : -rx
|
||||
ry = ry < 0 ? ry : -ry
|
||||
|
||||
// Rotate points and translate so the ellipse is centered at the origin.
|
||||
a1 = vec.sub(vec.rotWith(a1, center, -rotation), center)
|
||||
a2 = vec.sub(vec.rotWith(a2, center, -rotation), center)
|
||||
|
||||
// Calculate the quadratic parameters.
|
||||
const diff = vec.sub(a2, a1)
|
||||
|
||||
var A = (diff[0] * diff[0]) / rx / rx + (diff[1] * diff[1]) / ry / ry
|
||||
var B = (2 * a1[0] * diff[0]) / rx / rx + (2 * a1[1] * diff[1]) / ry / ry
|
||||
var C = (a1[0] * a1[0]) / rx / rx + (a1[1] * a1[1]) / ry / ry - 1
|
||||
|
||||
// Make a list of t values (normalized points on the line where intersections occur).
|
||||
var tValues: number[] = []
|
||||
|
||||
// Calculate the discriminant.
|
||||
var discriminant = B * B - 4 * A * C
|
||||
|
||||
if (discriminant === 0) {
|
||||
// One real solution.
|
||||
tValues.push(-B / 2 / A)
|
||||
} else if (discriminant > 0) {
|
||||
const root = Math.sqrt(discriminant)
|
||||
// Two real solutions.
|
||||
tValues.push((-B + root) / 2 / A)
|
||||
tValues.push((-B - root) / 2 / A)
|
||||
}
|
||||
|
||||
// Filter to only points that are on the segment.
|
||||
// Solve for points, then counter-rotate points.
|
||||
const points = tValues
|
||||
.filter((t) => t >= 0 && t <= 1)
|
||||
.map((t) => vec.add(center, vec.add(a1, vec.mul(vec.sub(a2, a1), t))))
|
||||
.map((p) => vec.rotWith(p, center, rotation))
|
||||
|
||||
return getIntersection("intersection", ...points)
|
||||
}
|
||||
|
||||
export function intersectCircleRectangle(
|
||||
|
@ -130,6 +181,73 @@ export function intersectCircleRectangle(
|
|||
return intersections
|
||||
}
|
||||
|
||||
export function intersectEllipseRectangle(
|
||||
c: number[],
|
||||
rx: number,
|
||||
ry: number,
|
||||
point: number[],
|
||||
size: number[],
|
||||
rotation = 0
|
||||
): 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 = intersectEllipseLineSegment(
|
||||
c,
|
||||
rx,
|
||||
ry,
|
||||
tl,
|
||||
tr,
|
||||
rotation
|
||||
)
|
||||
const rightIntersection = intersectEllipseLineSegment(
|
||||
c,
|
||||
rx,
|
||||
ry,
|
||||
tr,
|
||||
br,
|
||||
rotation
|
||||
)
|
||||
const bottomIntersection = intersectEllipseLineSegment(
|
||||
c,
|
||||
rx,
|
||||
ry,
|
||||
bl,
|
||||
br,
|
||||
rotation
|
||||
)
|
||||
const leftIntersection = intersectEllipseLineSegment(
|
||||
c,
|
||||
rx,
|
||||
ry,
|
||||
tl,
|
||||
bl,
|
||||
rotation
|
||||
)
|
||||
|
||||
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[],
|
||||
|
@ -180,6 +298,24 @@ export function intersectCircleBounds(
|
|||
return intersectCircleRectangle(c, r, [minX, minY], [width, height])
|
||||
}
|
||||
|
||||
export function intersectEllipseBounds(
|
||||
c: number[],
|
||||
rx: number,
|
||||
ry: number,
|
||||
bounds: Bounds,
|
||||
rotation = 0
|
||||
): Intersection[] {
|
||||
const { minX, minY, width, height } = bounds
|
||||
return intersectEllipseRectangle(
|
||||
c,
|
||||
rx,
|
||||
ry,
|
||||
[minX, minY],
|
||||
[width, height],
|
||||
rotation
|
||||
)
|
||||
}
|
||||
|
||||
export function intersectLineSegmentBounds(
|
||||
a1: number[],
|
||||
a2: number[],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue