Alright, just for people who might stumble upon this in the future..
Here is what i came up with in javascript (ES6). But it should be easy to port this to other languages..
/**
* Calculate the intersection of the border of the given rectangle
* and a circular arc.
* @param {Object} rectangle Rectangle object with dimensions and position properties.
* @param {Object} arc Arc object with center and radius properties.
* @return {Object} Resulting intersection point, with x- and y-coordinates.
*/
calculateBorderPoint(rectangle, arc) {
// Clone the rectangle, because we don't want to mutate the original.
const a = Object.assign({}, rectangle.position);
// Treat center of circle as coordinate origin.
a.x -= arc.center.x;
a.y -= arc.center.y;
let points = [];
// Check east & west with possible x values
const possibleX = [
a.x - rectangle.dimensions.width / 2,
a.x + rectangle.dimensions.width / 2,
];
possibleX.forEach((x) => {
const ySquared = [
Math.sqrt(Math.pow(arc.radius, 2) - Math.pow(x, 2)),
-Math.sqrt(Math.pow(arc.radius, 2) - Math.pow(x, 2)),
];
// Check if the derived y value is in range of rectangle
ySquared.forEach((y) => {
if (y >= a.y - rectangle.dimensions.height / 2 &&
y <= a.y + rectangle.dimensions.height / 2) {
points.push({x, y});
}
});
});
// Check north & south with possible y values
const possibleY = [
a.y - rectangle.dimensions.height / 2,
a.y + rectangle.dimensions.height / 2,
];
possibleY.forEach((y) => {
const xSquared = [
Math.sqrt(Math.pow(arc.radius, 2) - Math.pow(y, 2)),
-Math.sqrt(Math.pow(arc.radius, 2) - Math.pow(y, 2)),
];
// Check if the derived x value is in range of rectangle
xSquared.forEach((x) => {
if (x >= a.x - rectangle.dimensions.width / 2 &&
x <= a.x + rectangle.dimensions.width / 2) {
points.push({x, y});
}
});
});
// At this point you will propably have multiple possible solutions,
// because the circle might intersect the rectangle at multiple points.
// You need to select the point, that makes most sense in your case.
// One solution would be to select the one closest to the other rectangle.
// Translate it back.
points[0].x += arc.center.x;
points[0].y += arc.center.y;
return points[0];
}
It's not pretty, but it works. I'm happy to hear any suggestions..