Skip to content

Instantly share code, notes, and snippets.

@steveruizok
Last active May 22, 2021 21:12
Show Gist options
  • Save steveruizok/912392baefa58811594a85e2d80b0c3a to your computer and use it in GitHub Desktop.
Save steveruizok/912392baefa58811594a85e2d80b0c3a to your computer and use it in GitHub Desktop.
get bounds of a rotated ellipse
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,
}
}
// 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