Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save steveruizok/6da65c97e4f9caf8a4bde5d475da1387 to your computer and use it in GitHub Desktop.
Save steveruizok/6da65c97e4f9caf8a4bde5d475da1387 to your computer and use it in GitHub Desktop.
Get the points where a line segment intersects a rectangle with rounded corners.
/**
* Get the intersection points between a line segment and a rectangle with rounded corners.
* @param x0 The x-axis coordinate of the segment's starting point.
* @param y0 The y-axis coordinate of ththe segment's ending point.
* @param x1 The delta-x of the ray.
* @param y1 The delta-y of the ray.
* @param x The x-axis coordinate of the rectangle.
* @param y The y-axis coordinate of the rectangle.
* @param w The width of the rectangle.
* @param h The height of the rectangle.
* @param r The corner radius of the rectangle.
*/
export function getSegmentRoundedRectangleIntersection(
x0: number,
y0: number,
x1: number,
y1: number,
x: number,
y: number,
w: number,
h: number,
r: number
) {
const mx = x + w,
my = y + h,
rx = x + r,
ry = y + r,
mrx = x + w - r,
mry = y + h - r;
const segments = [
[x, mry, x, ry, x, y],
[rx, y, mrx, y, mx, y],
[mx, ry, mx, mry, mx, my],
[mrx, my, rx, my, x, my]
];
const corners = [
[rx, ry, Math.PI, Math.PI * 1.5],
[mrx, ry, Math.PI * 1.5, Math.PI * 2],
[mrx, mry, 0, Math.PI * 0.5],
[rx, mry, Math.PI * 0.5, Math.PI]
];
let points: number[][] = [];
segments.forEach((segment, i) => {
const [px0, py0, px1, py1] = segment;
const [cx, cy, as, ae] = corners[i];
getSegmentCircleIntersections(cx, cy, r, x0, y0, x1, y1)
.filter(pt => {
const pointAngle = normalizeAngle(getAngle(cx, cy, pt[0], pt[1]));
return pointAngle > as && pointAngle < ae;
})
.forEach(pt => points.push(pt));
const segmentInt = getSegmentSegmentIntersection(
x0,
y0,
x1,
y1,
px0,
py0,
px1,
py1
);
if (!!segmentInt) {
points.push(segmentInt);
}
});
return points;
}
// Helpers ----------------------------------------------------------
export function normalizeAngle(radians: number) {
return radians - Math.PI * 2 * Math.floor(radians / (Math.PI * 2));
}
export function getAngle(x0: number, y0: number, x1: number, y1: number) {
return Math.atan2(y1 - y0, x1 - x0);
}
export function getSegmentCircleIntersections(
cx: number,
cy: number,
r: number,
x0: number,
y0: number,
x1: number,
y1: number
) {
var b: number,
c: number,
d: number,
u1: number,
u2: number,
ret: number[][],
retP1: number[],
retP2: number[],
v1 = [x1 - x0, y1 - y0],
v2 = [x0 - cx, y0 - cy];
b = v1[0] * v2[0] + v1[1] * v2[1];
c = 2 * (v1[0] * v1[0] + v1[1] * v1[1]);
b *= -2;
d = Math.sqrt(b * b - 2 * c * (v2[0] * v2[0] + v2[1] * v2[1] - r * r));
if (isNaN(d)) {
// no intercept
return [];
}
u1 = (b - d) / c; // these represent the unit distance of point one and two on the line
u2 = (b + d) / c;
retP1 = []; // return points
retP2 = [];
ret = []; // return array
if (u1 <= 1 && u1 >= 0) {
// add point if on the line segment
retP1[0] = x0 + v1[0] * u1;
retP1[1] = y0 + v1[1] * u1;
ret[0] = retP1;
}
if (u2 <= 1 && u2 >= 0) {
// second add point if on the line segment
retP2[0] = x0 + v1[0] * u2;
retP2[1] = y0 + v1[1] * u2;
ret[ret.length] = retP2;
}
return ret;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment