162 lines
4.1 KiB
JavaScript
162 lines
4.1 KiB
JavaScript
class Point {
|
|
constructor(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
}
|
|
|
|
class QuadSpline {
|
|
constructor(c0, c1, c2) {
|
|
this.c0 = c0;
|
|
this.c1 = c1;
|
|
this.c2 = c2;
|
|
}
|
|
}
|
|
|
|
class QuadCurve {
|
|
constructor(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
}
|
|
|
|
// Subdivision by de Casteljau's algorithm.
|
|
function subdivideQuadSpline(q, t, u, v) {
|
|
const s = 1.0 - t;
|
|
const c0 = q.c0;
|
|
const c1 = q.c1;
|
|
const c2 = q.c2;
|
|
u.c0 = c0;
|
|
v.c2 = c2;
|
|
u.c1 = s * c0 + t * c1;
|
|
v.c1 = s * c1 + t * c2;
|
|
u.c2 = s * u.c1 + t * v.c1;
|
|
v.c0 = u.c2;
|
|
}
|
|
|
|
function subdivideQuadCurve(q, t, u, v) {
|
|
subdivideQuadSpline(q.x, t, u.x, v.x);
|
|
subdivideQuadSpline(q.y, t, u.y, v.y);
|
|
}
|
|
|
|
// It is assumed that xa0 <= xa1, ya0 <= ya1, xb0 <= xb1, and yb0 <= yb1.
|
|
function rectsOverlap(xa0, ya0, xa1, ya1, xb0, yb0, xb1, yb1) {
|
|
return (xb0 <= xa1 && xa0 <= xb1 && yb0 <= ya1 && ya0 <= yb1);
|
|
}
|
|
|
|
function max3(x, y, z) { return Math.max(Math.max(x, y), z); }
|
|
function min3(x, y, z) { return Math.min(Math.min(x, y), z); }
|
|
|
|
// This accepts the point as an intersection if the boxes are small enough.
|
|
function testIntersect(p, q, tol) {
|
|
const pxmin = min3(p.x.c0, p.x.c1, p.x.c2);
|
|
const pymin = min3(p.y.c0, p.y.c1, p.y.c2);
|
|
const pxmax = max3(p.x.c0, p.x.c1, p.x.c2);
|
|
const pymax = max3(p.y.c0, p.y.c1, p.y.c2);
|
|
|
|
const qxmin = min3(q.x.c0, q.x.c1, q.x.c2);
|
|
const qymin = min3(q.y.c0, q.y.c1, q.y.c2);
|
|
const qxmax = max3(q.x.c0, q.x.c1, q.x.c2);
|
|
const qymax = max3(q.y.c0, q.y.c1, q.y.c2);
|
|
|
|
let exclude = true;
|
|
let accept = false;
|
|
let intersect = new Point(0, 0);
|
|
|
|
if (rectsOverlap(pxmin, pymin, pxmax, pymax, qxmin, qymin, qxmax, qymax)) {
|
|
exclude = false;
|
|
const xmin = Math.max(pxmin, qxmin);
|
|
const xmax = Math.min(pxmax, qxmax);
|
|
|
|
if (xmax < xmin) {
|
|
throw new Error(`Assertion failure: ${xmax} < ${xmin}`);
|
|
}
|
|
|
|
if (xmax - xmin <= tol) {
|
|
const ymin = Math.max(pymin, qymin);
|
|
const ymax = Math.min(pymax, qymax);
|
|
|
|
if (ymax < ymin) {
|
|
throw new Error(`Assertion failure: ${ymax} < ${ymin}`);
|
|
}
|
|
|
|
if (ymax - ymin <= tol) {
|
|
accept = true;
|
|
intersect.x = 0.5 * xmin + 0.5 * xmax;
|
|
intersect.y = 0.5 * ymin + 0.5 * ymax;
|
|
}
|
|
}
|
|
}
|
|
|
|
return { exclude, accept, intersect };
|
|
}
|
|
|
|
function seemsToBeDuplicate(intersects, xy, spacing) {
|
|
let seemsToBeDup = false;
|
|
let i = 0;
|
|
|
|
while (!seemsToBeDup && i !== intersects.length) {
|
|
const pt = intersects[i];
|
|
seemsToBeDup = Math.abs(pt.x - xy.x) < spacing && Math.abs(pt.y - xy.y) < spacing;
|
|
i++;
|
|
}
|
|
|
|
return seemsToBeDup;
|
|
}
|
|
|
|
function findIntersects(p, q, tol, spacing) {
|
|
const intersects = [];
|
|
const workload = [{ p, q }];
|
|
|
|
// Quit looking after having emptied the workload.
|
|
while (workload.length > 0) {
|
|
const work = workload.pop();
|
|
const { exclude, accept, intersect } = testIntersect(work.p, work.q, tol);
|
|
|
|
if (accept) {
|
|
// To avoid detecting the same intersection twice, require some
|
|
// space between intersections.
|
|
if (!seemsToBeDuplicate(intersects, intersect, spacing)) {
|
|
intersects.push(intersect);
|
|
}
|
|
} else if (!exclude) {
|
|
const p0 = new QuadCurve(new QuadSpline(0, 0, 0), new QuadSpline(0, 0, 0));
|
|
const p1 = new QuadCurve(new QuadSpline(0, 0, 0), new QuadSpline(0, 0, 0));
|
|
const q0 = new QuadCurve(new QuadSpline(0, 0, 0), new QuadSpline(0, 0, 0));
|
|
const q1 = new QuadCurve(new QuadSpline(0, 0, 0), new QuadSpline(0, 0, 0));
|
|
|
|
subdivideQuadCurve(work.p, 0.5, p0, p1);
|
|
subdivideQuadCurve(work.q, 0.5, q0, q1);
|
|
|
|
workload.push({ p: p1, q: q1 });
|
|
workload.push({ p: p1, q: q0 });
|
|
workload.push({ p: p0, q: q1 });
|
|
workload.push({ p: p0, q: q0 });
|
|
}
|
|
}
|
|
|
|
return intersects;
|
|
}
|
|
|
|
function main() {
|
|
const p = new QuadCurve(
|
|
new QuadSpline(-1.0, 0.0, 1.0),
|
|
new QuadSpline(0.0, 10.0, 0.0)
|
|
);
|
|
|
|
const q = new QuadCurve(
|
|
new QuadSpline(2.0, -8.0, 2.0),
|
|
new QuadSpline(1.0, 2.0, 3.0)
|
|
);
|
|
|
|
const tol = 0.0000001;
|
|
const spacing = tol * 10;
|
|
|
|
const intersects = findIntersects(p, q, tol, spacing);
|
|
|
|
for (const intersect of intersects) {
|
|
console.log(`(${intersect.x}, ${intersect.y})`);
|
|
}
|
|
}
|
|
|
|
main();
|