Last active
May 22, 2021 21:12
-
-
Save steveruizok/912392baefa58811594a85e2d80b0c3a to your computer and use it in GitHub Desktop.
get bounds of a rotated ellipse
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function getRotatedEllipseBounds( | |
x: number, | |
y: number, | |
rx: number, | |
ry: number, | |
rotation: number | |
) { | |
const c = Math.cos(rotation) | |
const s = Math.sin(rotation) | |
const w = Math.hypot(rx * c, ry * s) | |
const h = Math.hypot(rx * s, ry * c) | |
return { | |
minX: x + rx - w, | |
minY: y + ry - h, | |
maxX: x + rx + w, | |
maxY: y + ry + h, | |
width: w * 2, | |
height: h * 2, | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// For vector functions, see here: https://gist.github.com/steveruizok/7e2cd98c2715f4c495233c1712cf6215 | |
function getIntersection(message: string, ...points: number[][]) { | |
return { didIntersect: points.length > 0, message, points } | |
} | |
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) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment