Where two drawn objects cross: the basis of snaps, trim and extend.
How the precision is kept
Closed-form solutions in 64-bit. Two lines count as parallel when their cross product is under 1e-12 of the product of their lengths; a point counts as on an object within 1e-7 of a drawing unit. These are tolerance tests, not exact predicates.
Open item. Exact orientation tests for the near-parallel and near-tangent cases.
Calculations under this policy
4 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.
The parallel test is scaled by both lengths, so it means the same angle at any drawing size. Whether two nearly parallel lines cross is decided by that tolerance, not by an exact test.
The textbook quadratic formula. When B² is much larger than 4AC, one of the two roots subtracts nearly equal numbers and loses digits. A discriminant just below zero is treated as a tangent.
geom.js
lines 477 to 486
CadCraft 0.8.2
function lcInter(l, c) {
const dx = l.x2 - l.x1, dy = l.y2 - l.y1, fx = l.x1 - c.cx, fy = l.y1 - c.cy;
const A = dx * dx + dy * dy, B = 2 * (fx * dx + fy * dy), C = fx * fx + fy * fy - c.r * c.r;
if (A < EPS * EPS) return [];
let disc = B * B - 4 * A * C;
if (disc < 0) { if (disc > -1e-9 * B * B) disc = 0; else return []; }
const s = Math.sqrt(disc), out = [];
for (const t of disc === 0 ? [-B / (2 * A)] : [(-B - s) / (2 * A), (-B + s) / (2 * A)]) out.push({ x: l.x1 + dx * t, y: l.y1 + dy * t });
return out;
}
Circle and circle
64-bit
The points where two circles or arcs meet.
x = (d² + r₁² − r₂²) ÷ 2d; h = √(r₁² − x²)
Near tangency r₁² − x² subtracts nearly equal numbers. A result below zero is clamped to zero, which gives the single touching point.
geom.js
lines 488 to 497
CadCraft 0.8.2
function ccInter(a, b) {
const d = Math.hypot(b.cx - a.cx, b.cy - a.cy);
if (d < EPS || d > a.r + b.r + 1e-9 || d < Math.abs(a.r - b.r) - 1e-9) return [];
const x = (d * d + a.r * a.r - b.r * b.r) / (2 * d);
const h = Math.sqrt(Math.max(0, a.r * a.r - x * x));
const ux = (b.cx - a.cx) / d, uy = (b.cy - a.cy) / d;
const px = a.cx + ux * x, py = a.cy + uy * x;
if (h < 1e-12) return [{ x: px, y: py }];
return [{ x: px - uy * h, y: py + ux * h }, { x: px + uy * h, y: py - ux * h }];
}
Is the point on the object
64-bit
Whether a computed crossing lies on the drawn part of each object.
−tol ≤ t·L ≤ L + tol, with tol = 1e-7; arcs: the angle within 1e-7 of the sweep
A fixed tolerance in drawing units for lines and in radians for arcs.
geom.js
lines 461 to 467
CadCraft 0.8.2
function onPrim(p, x, tol) {
if (p.k === 'L') {
const t = primParam(p, x, false), L = primLength(p);
return t >= -tol / Math.max(L, EPS) - 1e-9 && t <= 1 + tol / Math.max(L, EPS) + 1e-9;
}
return onSweep(Math.atan2(x.y - p.cy, x.x - p.cx), p.a0, p.a1, 1e-7);
}
In detail
Method
Every object breaks down into straight segments and arcs. Two lines are solved by Cramer's rule, a line and a circle by the quadratic formula, two circles through the line joining their centers.
Number types
64-bit numbers throughout.
Tolerances
Parallel: cross product under 1e-12 of the product of the lengths. On an object: within 1e-7. A discriminant that is negative by less than 1e-9 of its leading term counts as a tangent.
Checked by
The CadCraft test suite runs the geometry in Node.
Limits
Tolerance tests can disagree with each other on near-parallel and near-tangent input. That is the open item.
Approach: 64-bit, direct
Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.