Resources Numerical accuracy Calculation policy

Line, arc and circle intersections

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.

geom.js CadCraft 0.8.2 64-bit 4 calculations Reviewed

Tests bin/test-cadcraft.mjs

Line and line

64-bit

The point where two lines cross.

t = ((b₁ − a₁) × d₂) ÷ (d₁ × d₂); parallel when |d₁ × d₂| < 1e-12 × |d₁| × |d₂|

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.

  • geom.js
  • lines 469 to 475
  • CadCraft 0.8.2
function llInter(a, b) {
  const d1x = a.x2 - a.x1, d1y = a.y2 - a.y1, d2x = b.x2 - b.x1, d2y = b.y2 - b.y1;
  const den = d1x * d2y - d1y * d2x;
  if (Math.abs(den) < 1e-12 * Math.hypot(d1x, d1y) * Math.hypot(d2x, d2y)) return [];
  const t = ((b.x1 - a.x1) * d2y - (b.y1 - a.y1) * d2x) / den;
  return [{ x: a.x1 + d1x * t, y: a.y1 + d1y * t }];
}

Line and circle

64-bit

The points where a line meets a circle or arc.

A·t² + B·t + C = 0; t = (−B ± √(B² − 4AC)) ÷ 2A

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.

Every policy that takes this approach