Offsets outlines made of lines and arcs by the tool radius, intersects and trims them, and measures their lengths, areas and distances.
How the precision is kept
Curves stay lines and circular arcs throughout, in 64-bit; nothing is faceted. An offset moves lines sideways and changes arc radii, then every piece is cut at its intersections and kept only if its midpoint is at the offset distance from the original outline. Decisions use fixed tolerances in millimeters and are made in ordinary 64-bit arithmetic, not with exact predicates.
Calculations under this policy
18 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 point a given fraction of the way along a line or arc.
line: s + (e − s) t; arc: c + r (cos, sin)(a₀ + sweep × t)
At t ≤ 0 and t ≥ 1 the stored end points are returned as they are, so shared ends stay identical. In between, an arc point is computed from the center and radius.
Whether a direction from the center falls within an arc, and how far along.
u = mod(±(a − a₀), 2π); on the arc when u ≤ |sweep| + eps; t = u ÷ |sweep|
The tolerance eps is in radians (1e-9 by default), so the allowed overshoot in millimeters grows with the radius; callers that need a length tolerance pass eps divided by the radius.
cam-offset.js
lines 67 to 74
PartCraft 0.8.1
/** Is angle a on the arc? → parameter t in [0, 1] or −1. */
export function arcParam(p, a, eps = EPS) {
const span = Math.abs(p.sw);
if (span >= TAU - eps) return mod((a - p.a0) * Math.sign(p.sw), TAU) / TAU;
let u = p.sw > 0 ? mod(a - p.a0, TAU) : mod(p.a0 - a, TAU);
if (u > span + eps) { if (u > TAU - eps) u = 0; else return -1; }
return Math.min(1, u / span);
}
Distance from a point to a line or arc
64-bit
The shortest distance from a point to one piece of an outline.
line: |q − (s + d × clamp(((q − s) · d) ÷ |d|², 0, 1))|; arc: ||q − c| − r| when the direction is on the arc, else the nearer end
For an arc the distance is a difference |ρ − r| of two lengths, so its absolute error is the rounding of the larger one. A zero-length line is handled without dividing.
cam-offset.js
lines 76 to 86
PartCraft 0.8.1
/** Closest distance from point q to a prim. */
export function distToPrim(q, p) {
if (p.k === 'L') {
const d = sub(p.e, p.s), l2 = dot(d, d);
const t = l2 > 0 ? Math.max(0, Math.min(1, dot(sub(q, p.s), d) / l2)) : 0;
return dist(q, [p.s[0] + d[0] * t, p.s[1] + d[1] * t]);
}
const v = sub(q, p.c), rho = len(v);
if (rho < 1e-300 || arcParam(p, Math.atan2(v[1], v[0]), 1e-12) >= 0) return Math.abs(rho - p.r);
return Math.min(dist(q, p.s), dist(q, p.e));
}
Signed area of a loop
64-bit
The area enclosed by a loop of lines and arcs, positive when counterclockwise.
A = ½ Σ [line: s × e; arc: r c_x (sin a₁ − sin a₀) − r c_y (cos a₁ − cos a₀) + r² × sweep]
Green's theorem with the exact arc term, so arcs are not approximated. The terms use absolute coordinates and are added in order; for a small loop far from the origin they are large and nearly cancel.
cam-offset.js
lines 123 to 134
PartCraft 0.8.1
/** Signed area (counter-clockwise positive), exact for arcs. */
export function loopArea(l) {
let s = 0;
for (const p of l) {
if (p.k === 'L') s += crs(p.s, p.e);
else {
const a1 = p.a0 + p.sw;
s += p.r * p.c[0] * (Math.sin(a1) - Math.sin(p.a0)) - p.r * p.c[1] * (Math.cos(a1) - Math.cos(p.a0)) + p.r * p.r * p.sw;
}
}
return s / 2;
}
Winding number
64-bit
How many times the loops wind round a point, which says whether the point is inside.
count signed crossings of a ray to +x; arc crossing at x = c_x ± √(r² − (q_y − c_y)²)
Side tests are signs of 64-bit cross products, not exact predicates, so a point within rounding of the boundary can be counted either way. The square root's argument is clamped at 0.
cam-offset.js
lines 136 to 158
PartCraft 0.8.1
/** Winding number of the loops about q (horizontal ray to +x; arcs split at their top and bottom). */
export function winding(loops, q) {
let w = 0;
const seg = (a, b) => {
if (a[1] <= q[1] && b[1] > q[1]) { if (crs(sub(b, a), sub(q, a)) > 0) w++; }
else if (b[1] <= q[1] && a[1] > q[1]) { if (crs(sub(b, a), sub(q, a)) < 0) w--; }
};
for (const l of loops) for (const p of l) {
if (p.k === 'L') { seg(p.s, p.e); continue; }
// monotone pieces of the arc between the angles π/2 + kπ
const pieces = monotonePieces(p);
for (const m of pieces) {
const ys = m.s[1], ye = m.e[1];
const up = ye > ys;
if (!(up ? (ys <= q[1] && ye > q[1]) : (ye <= q[1] && ys > q[1]))) continue;
const dy = q[1] - p.c[1];
const h = Math.sqrt(Math.max(0, p.r * p.r - dy * dy));
const x = p.c[0] + (Math.cos(m.a0 + m.sw / 2) >= 0 ? h : -h);
if (x > q[0]) w += up ? 1 : -1;
}
}
return w;
}
Line and line
64-bit
The point where two line pieces cross, or the ends of their overlap when they lie on one line.
The parallel test is relative to the two lengths. For nearly parallel lines the divisor is small and the crossing point loses digits in proportion. Parameters are accepted up to 1e-9 plus 1e-8 mm beyond the ends and then clamped.
cam-offset.js
lines 198 to 216
PartCraft 0.8.1
function llx(A, B) {
const r = sub(A.e, A.s), s = sub(B.e, B.s);
const den = crs(r, s), qp = sub(B.s, A.s);
const lr = len(r), ls = len(s);
if (lr < 1e-15 || ls < 1e-15) return [];
if (Math.abs(den) <= 1e-12 * lr * ls) {
if (Math.abs(crs(qp, r)) / lr > TOL * 0.1) return [];
// collinear: overlap ends
const out = [];
const tA = (q) => dot(sub(q, A.s), r) / (lr * lr), tB = (q) => dot(sub(q, B.s), s) / (ls * ls);
for (const q of [B.s, B.e]) { const t = tA(q); if (t > -EPS && t < 1 + EPS) out.push({ ta: clamp01(t), tb: tB(q), p: q }); }
for (const q of [A.s, A.e]) { const t = tB(q); if (t > -EPS && t < 1 + EPS) out.push({ ta: tA(q), tb: clamp01(t), p: q }); }
return out.map((x) => ({ ta: clamp01(x.ta), tb: clamp01(x.tb), p: x.p }));
}
const ta = crs(qp, s) / den, tb = crs(qp, r) / den;
const ea = EPS + TOL * 0.01 / lr, eb = EPS + TOL * 0.01 / ls;
if (ta < -ea || ta > 1 + ea || tb < -eb || tb > 1 + eb) return [];
return [{ ta: clamp01(ta), tb: clamp01(tb), p: [A.s[0] + r[0] * ta, A.s[1] + r[1] * ta] }];
}
Line and arc
64-bit
The points where a line piece meets an 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 slightly below zero (a miss of less than about 1e-8 mm) is treated as a tangent point.
cam-offset.js
lines 218 to 238
PartCraft 0.8.1
function lax(L, C) {
const d = sub(L.e, L.s), f = sub(L.s, C.c);
const a = dot(d, d), b = 2 * dot(f, d), c = dot(f, f) - C.r * C.r;
if (a < 1e-30) return [];
let disc = b * b - 4 * a * c;
const ld = Math.sqrt(a);
// tangent within tolerance: one touching point
if (disc < 0) { if (disc > -4 * a * (2 * C.r * TOL * 0.01)) disc = 0; else return []; }
const sq = Math.sqrt(disc);
const ts = disc === 0 ? [-b / (2 * a)] : [(-b - sq) / (2 * a), (-b + sq) / (2 * a)];
const out = [];
const e = EPS + TOL * 0.01 / ld;
for (const t of ts) {
if (t < -e || t > 1 + e) continue;
const p = pointAt(L, clamp01(t));
const tb = arcParam(C, Math.atan2(p[1] - C.c[1], p[0] - C.c[0]), EPS + TOL * 0.01 / C.r);
if (tb < 0) continue;
out.push({ ta: clamp01(t), tb, p });
}
return out;
}
Arc and arc
64-bit
The points where two arcs meet.
a = (r_A² − r_B² + d²) ÷ 2d; h = √(r_A² − a²); p = c_A + a u ± h n
The terms r_A² − a² subtract nearly equal numbers when the circles are close to tangent, so h loses digits there; a negative value is taken as 0 and h below 1e-12 gives one point. Circles whose centers are within 1e-12 × max(1, r) are treated as concentric.
cam-offset.js
lines 239 to 261
PartCraft 0.8.1
function aax(A, B) {
const d = dist(A.c, B.c);
const out = [];
if (d < 1e-12 * Math.max(1, A.r)) {
if (Math.abs(A.r - B.r) > TOL * 0.1) return [];
// concentric, same radius: overlap ends
for (const q of [B.s, B.e]) { const t = arcParam(A, Math.atan2(q[1] - A.c[1], q[0] - A.c[0])); if (t >= 0) out.push({ ta: t, tb: q === B.s ? 0 : 1, p: q }); }
for (const q of [A.s, A.e]) { const t = arcParam(B, Math.atan2(q[1] - B.c[1], q[0] - B.c[0])); if (t >= 0) out.push({ ta: q === A.s ? 0 : 1, tb: t, p: q }); }
return out;
}
if (d > A.r + B.r + TOL * 0.01 || d < Math.abs(A.r - B.r) - TOL * 0.01) return [];
const a = (A.r * A.r - B.r * B.r + d * d) / (2 * d);
const h2 = A.r * A.r - a * a;
const h = h2 > 0 ? Math.sqrt(h2) : 0;
const u = mul(sub(B.c, A.c), 1 / d), m = add(A.c, mul(u, a)), n = left(u);
const pts = h > 1e-12 ? [add(m, mul(n, h)), sub(m, mul(n, h))] : [m];
for (const p of pts) {
const ta = arcParam(A, Math.atan2(p[1] - A.c[1], p[0] - A.c[0]), EPS + TOL * 0.01 / A.r);
const tb = arcParam(B, Math.atan2(p[1] - B.c[1], p[0] - B.c[0]), EPS + TOL * 0.01 / B.r);
if (ta >= 0 && tb >= 0) out.push({ ta, tb, p });
}
return out;
}
Raw offset of a loop
64-bit
Moves every piece of a loop sideways by the offset and joins the corners with arcs.
line: s, e + d × left normal; arc: r ∓ d; corner arc radius d, sweep = atan2(t_a × t_b, t_a · t_b)
An offset arc keeps its center and angles and changes only its radius, so no fitting is involved. The corner turn uses atan2 of the cross and dot products of the tangents. Arcs whose new radius is within 1e-9 of zero are left out.
cam-offset.js
lines 265 to 290
PartCraft 0.8.1
/** Raw offset pieces of one loop, d to its left (continuous closed curve). */
function rawOffset(loop, d) {
const out = [];
const n = loop.length;
for (let i = 0; i < n; i++) {
const p = loop[i];
const t0 = tangentAt(p, 0), t1 = tangentAt(p, 1);
const s = add(p.s, mul(left(t0), d)), e = add(p.e, mul(left(t1), d));
if (p.k === 'L') out.push(line(s, e));
else {
const r2 = p.sw > 0 ? p.r - d : p.r + d;
if (r2 > 1e-9) out.push(arc(p.c, r2, p.a0, p.sw, s, e));
else if (r2 < -1e-9) out.push(arc(p.c, -r2, p.a0 + Math.PI, p.sw, s, e));
}
// corner to the next prim
const q = loop[(i + 1) % n];
const ta = t1, tb = tangentAt(q, 0);
const turn = Math.atan2(crs(ta, tb), dot(ta, tb));
if (Math.abs(turn) > 1e-12) {
const v = p.e;
const na = left(ta);
out.push(arc(v, d, Math.atan2(na[1], na[0]), turn, e, add(q.s, mul(left(tb), d))));
}
}
return out.filter((p) => lengthOf(p) > 1e-12);
}
Splitting pieces at intersections
64-bit
Cuts every offset piece where it meets another.
cut at parameter t unless within 1e-5 mm of an end or of the previous cut
Both pieces are cut at the same point object, so the two sides share identical coordinates. Cuts closer than JOIN (1e-5 mm) to an end are ignored.
cam-offset.js
lines 292 to 318
PartCraft 0.8.1
/** Split every piece at its intersections with the others → sub-pieces. */
export function splitAll(pieces) {
const boxes = pieces.map(boxOf);
const cuts = pieces.map(() => []);
const pad = JOIN;
for (let i = 0; i < pieces.length; i++) {
const bi = boxes[i];
for (let j = i + 1; j < pieces.length; j++) {
const bj = boxes[j];
if (bi[0] > bj[2] + pad || bj[0] > bi[2] + pad || bi[1] > bj[3] + pad || bj[1] > bi[3] + pad) continue;
for (const x of intersect(pieces[i], pieces[j])) { cuts[i].push([x.ta, x.p]); cuts[j].push([x.tb, x.p]); }
}
}
const out = [];
pieces.forEach((p, i) => {
const L = lengthOf(p);
const ts = cuts[i].filter(([t]) => t * L > JOIN && (1 - t) * L > JOIN).sort((a, b) => a[0] - b[0]);
let t0 = 0, s = p.s;
for (const [t, q] of ts) {
if ((t - t0) * L <= JOIN) continue;
out.push(subPrim(p, t0, t, s, q));
t0 = t; s = q;
}
out.push(subPrim(p, t0, 1, s, p.e));
});
return out;
}
Offset of a region
64-bit
The outline at a fixed distance inside or outside a region, which is the path of the tool center.
keep a piece when dist(midpoint, original outline) ≥ d − 1e-6 and the midpoint is on the offset side
The kept pieces are exact lines and arcs at distance d. The trim decision is made at each piece's midpoint with an absolute slack of 1e-6 mm. An offset below 1e-12 returns the input unchanged.
cam-offset.js
lines 320 to 344
PartCraft 0.8.1
/**
* Offset a region (loops oriented region-on-left) by d: d > 0 shrinks (moves into the region), d < 0 grows.
* → loops of the offset region, oriented the same way. Empty when nothing is left.
*/
export function offsetRegion(loops, d, { tol = TOL } = {}) {
if (!loops.length) return [];
if (Math.abs(d) < 1e-12) return loops.map((l) => l.slice());
const grow = d < 0, a = Math.abs(d);
const src = grow ? loops.map(reverseLoop) : loops;
const raw = [];
for (const l of src) raw.push(...rawOffset(l, a));
const parts = splitAll(raw);
const kept = [];
for (const p of parts) {
const m = pointAt(p, 0.5);
if (distToLoops(m, loops, a - tol) < a - tol) continue;
if (inside(loops, m) === grow) continue;
kept.push(p);
}
const clean = dropOpposed(kept);
let out = chain(clean);
out = out.map((l) => mergeLoop(l)).filter((l) => l.length && Math.abs(loopArea(l)) > 1e-10);
if (grow) out = out.map(reverseLoop); // built on the reversed loops: the grown region was on their right
return out;
}
Chaining pieces into loops
64-bit
Joins the kept pieces end to start into closed loops.
next piece starts within 4e-5 mm of the current end; sharpest left turn wins; then next.start = current.end
Joints are made identical by replacing each start with the previous end. An arc keeps its center, radius and angles, so its stored start point can differ from the point those give by up to the 4e-5 mm that was bridged.
cam-offset.js
lines 367 to 404
PartCraft 0.8.1
/** Chain directed pieces end → start into closed loops (sharpest left turn at a junction). */
export function chain(pieces) {
const used = new Array(pieces.length).fill(false);
const loops = [];
const G = JOIN * 10;
const cell = (p) => [Math.round(p[0] / G), Math.round(p[1] / G)];
const starts = new Map();
pieces.forEach((p, i) => { const k = cell(p.s).join(','); if (!starts.has(k)) starts.set(k, []); starts.get(k).push(i); });
const near = (q) => {
const [a, b] = cell(q), out = [];
for (let i = -1; i <= 1; i++) for (let j = -1; j <= 1; j++) for (const k of starts.get((a + i) + ',' + (b + j)) || []) if (!used[k] && dist(pieces[k].s, q) < JOIN * 4) out.push(k);
return out;
};
const turn = (p, q) => { const a = tangentAt(p, 1), b = tangentAt(q, 0); return Math.atan2(crs(a, b), dot(a, b)); };
for (let i0 = 0; i0 < pieces.length; i0++) {
if (used[i0]) continue;
const loop = [pieces[i0]];
used[i0] = true;
let closed = false;
for (let guard = 0; guard < 100000; guard++) {
const cur = loop[loop.length - 1];
const home = dist(cur.e, loop[0].s) < JOIN * 4;
let best = -1, bt = home ? turn(cur, loop[0]) : -Infinity;
for (const j of near(cur.e)) { const t = turn(cur, pieces[j]); if (t > bt) { bt = t; best = j; } }
if (best < 0) { closed = home; break; }
used[best] = true;
loop.push(pieces[best]);
}
if (!closed) continue;
// make the joints exact: each prim starts where the previous ends
for (let k = 0; k < loop.length; k++) {
const p = loop[k], j = (k + 1) % loop.length, q = loop[j];
if (p.e !== q.s) loop[j] = q.k === 'L' ? line(p.e, q.e) : arc(q.c, q.r, q.a0, q.sw, p.e, q === p ? p.e : q.e);
}
loops.push(loop);
}
return loops;
}
Merging collinear lines and co-circular arcs
64-bit
Joins neighboring pieces that lie on the same line or circle into one.
lines: |u × v| < 1e-10 and end within 1e-9 mm of the line; arcs: centers and radii within 1e-9 mm, same direction
A merged line runs from the first start to the last end, so intermediate points are dropped; a merged arc adds the sweeps. Pieces shorter than 1e-6 mm are removed first.
cam-offset.js
lines 406 to 427
PartCraft 0.8.1
/** Merge collinear lines and arcs on one circle; drop pieces shorter than the join tolerance. */
export function mergeLoop(loop) {
let l = loop.filter((p) => lengthOf(p) > JOIN * 0.1);
if (l.length < 2) return l;
const can = (p, q) => {
if (p.k !== q.k) return false;
if (p.k === 'L') { const u = unit(sub(p.e, p.s)), v = unit(sub(q.e, q.s)); return dot(u, v) > 0 && Math.abs(crs(u, v)) < 1e-10 && distToLineInf(q.e, p) < 1e-9; }
return dist(p.c, q.c) < 1e-9 && Math.abs(p.r - q.r) < 1e-9 && Math.sign(p.sw) === Math.sign(q.sw) && Math.abs(p.sw + q.sw) <= TAU + 1e-12;
};
const join = (p, q) => (p.k === 'L' ? line(p.s, q.e) : arc(p.c, p.r, p.a0, p.sw + q.sw, p.s, q.e));
let changed = true;
while (changed && l.length > 1) {
changed = false;
const out = [];
for (const p of l) {
if (out.length && can(out[out.length - 1], p)) { out[out.length - 1] = join(out[out.length - 1], p); changed = true; } else out.push(p);
}
if (out.length > 1 && can(out[out.length - 1], out[0])) { out[0] = join(out[out.length - 1], out[0]); out.pop(); changed = true; }
l = out;
}
return l;
}
Splitting arcs to half turns
64-bit
Cuts arcs so none sweeps more than 180°, as G-code controls expect.
n = ceil(|sweep| ÷ π); piece i from a₀ + sweep × i ÷ n
Split angles are computed from the start each time. The first and last pieces keep the arc's stored end points; inner points come from the center and radius.
cam-offset.js
lines 449 to 463
PartCraft 0.8.1
/** Split arcs so none sweeps more than maxSweep (every control reads ≤ 180° arcs the same way). */
export function limitArcs(loop, maxSweep = Math.PI) {
const out = [];
for (const p of loop) {
if (p.k !== 'A' || Math.abs(p.sw) <= maxSweep + 1e-12) { out.push(p); continue; }
const n = Math.ceil(Math.abs(p.sw) / maxSweep - 1e-12);
let s = p.s;
for (let i = 0; i < n; i++) {
const e = i === n - 1 ? p.e : polar(p.c, p.r, p.a0 + p.sw * (i + 1) / n);
out.push(arc(p.c, p.r, p.a0 + p.sw * i / n, p.sw / n, s, e));
s = e;
}
}
return out;
}
Points along a piece at a chord tolerance
64-bit
Sample points no farther than a tolerance from an arc, for checks and drawing.
step angle = 2 acos(1 − tol ÷ r)
The acos argument is clamped at −1. For a tolerance much smaller than the radius, 1 − tol ÷ r is close to 1, where acos loses digits; this affects only how many points are taken.
cam-offset.js
lines 465 to 474
PartCraft 0.8.1
/** Points along a prim no farther than chord tolerance tol from it (for drawing and tests). */
export function samplePrim(p, tol = 0.01, maxStep = Infinity) {
if (p.k === 'L') {
const L = lengthOf(p), n = Math.max(1, Math.ceil(L / maxStep));
return Array.from({ length: n + 1 }, (_, i) => pointAt(p, i / n));
}
const da = p.r > tol ? 2 * Math.acos(Math.max(-1, 1 - tol / p.r)) : Math.PI / 2;
const n = Math.max(2, Math.ceil(Math.abs(p.sw) / da), Math.ceil(lengthOf(p) / maxStep));
return Array.from({ length: n + 1 }, (_, i) => pointAt(p, i / n));
}
Bounding box of a piece
64-bit
The extent of a line or arc, including where an arc bulges past its ends.
ends, plus c ± r along each axis direction the arc passes
The four axis directions are tested with a 1e-12 radian tolerance; the extreme points are computed from the center and radius.
cam-offset.js
lines 107 to 116
PartCraft 0.8.1
/** Bounding box [minx, miny, maxx, maxy] of a prim (arcs: their end points plus the axis extremes they pass). */
export function boxOf(p) {
let x0 = Math.min(p.s[0], p.e[0]), y0 = Math.min(p.s[1], p.e[1]), x1 = Math.max(p.s[0], p.e[0]), y1 = Math.max(p.s[1], p.e[1]);
if (p.k === 'A') for (let k = 0; k < 4; k++) {
if (arcParam(p, k * Math.PI / 2, 1e-12) < 0) continue;
const q = polar(p.c, p.r, k * Math.PI / 2);
x0 = Math.min(x0, q[0]); y0 = Math.min(y0, q[1]); x1 = Math.max(x1, q[0]); y1 = Math.max(y1, q[1]);
}
return [x0, y0, x1, y1];
}
Splitting a loop at distances along it
64-bit
Cuts a loop at given distances from its start, used for tabs and ramps.
t = (cut − length so far) ÷ piece length
The running length is a sum of piece lengths added in order. A cut within 1e-5 mm of a joint is moved to the joint.
cam-offset.js
lines 476 to 496
PartCraft 0.8.1
/** Split a loop at arc lengths (sorted, 0..total) → list of prim lists between them. */
export function splitAtLengths(loop, cuts) {
const out = [[]];
let acc = 0, ci = 0;
for (const p0 of loop) {
let p = p0, L = lengthOf(p), base = acc;
while (ci < cuts.length && cuts[ci] < base + L - JOIN) {
if (cuts[ci] <= base + JOIN) { if (out[out.length - 1].length) out.push([]); ci++; continue; }
const t = (cuts[ci] - base) / L;
const x = pointAt(p, t);
out[out.length - 1].push(subPrim(p, 0, t, p.s, x));
out.push([]);
p = subPrim(p, t, 1, x, p.e);
base = cuts[ci]; L = lengthOf(p);
ci++;
}
out[out.length - 1].push(p);
acc += lengthOf(p0);
}
return out;
}
In detail
Method
Raw offset plus trim by distance, as described in the file's header. Intersections in closed form: line–line by cross products, line–arc by a quadratic, arc–arc by the radical line. Winding number by ray crossing with arcs split into monotone pieces. Area by Green's theorem, exact for arcs.
Number types
64-bit numbers.
Tolerances
EPS = 1e-9 (curve parameter). TOL = 1e-6 mm (trim distance). JOIN = 1e-5 mm (points this close are one point); chaining accepts 4 × JOIN. Lines parallel when |sin| ≤ 1e-12, collinear within 1e-7 mm. A line missing an arc by less than about 1e-8 mm counts as touching. Loops of area below 1e-10 mm² are dropped. Pieces shorter than 1e-6 mm are dropped when merging.
Limits
Tolerances are absolute millimeters and do not scale with the size of the outline. The header gives the cost as quadratic in the number of pieces.
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.