Turns the drawing's lines, arcs, polylines, curves and hatches into paths in millimeters for the laser panel.
How the precision is kept
Objects are taken as line and arc pieces, chained into paths and put through the block transforms and one drawing-unit-to-millimeter scale in 64-bit numbers. Arcs stay arcs (as bulges) while the transform keeps shapes; otherwise they are flattened to a tolerance of 0.01 mm by default.
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.
Joins line and arc pieces that follow one another into open or closed paths.
arc → bulge = tan((a1 − a0) ÷ 4); full circle → two half arcs with bulge 1
A piece joins the path when it starts within 1e-6 × max(1, |coordinate|) of where the last one ended, and the path keeps the earlier point. Arc end points are computed from center, radius and angle with cos and sin. A full circle is written as two half arcs from (cx + r, cy) to (cx − r, cy).
laser-cad.js
lines 30 to 56
CadCraft 0.8.2
/** Primitives (geom.js L / A, in order) → paths [{ pts: [{x, y, b?}], closed }]: joined while each starts where the last ended. */
export function chainPrims(list) {
const out = [];
let cur = null;
const finish = () => {
if (!cur) return;
const pts = cur.pts;
if (pts.length > 2 && near(pts[0], pts[pts.length - 1])) { pts.pop(); cur.closed = true; }
if (pts.length >= 2) out.push(cur);
cur = null;
};
for (const p of list) {
if (p.box) continue; // text boxes, arrowheads: for picking only
if (p.k === 'A' && (p.full || p.a1 - p.a0 >= TAU - 1e-9)) { // a whole circle: two half arcs, closed
finish();
out.push({ pts: [{ x: p.cx + p.r, y: p.cy, b: 1 }, { x: p.cx - p.r, y: p.cy, b: 1 }], closed: true });
continue;
}
const s = primStart(p), e = primEnd(p);
if (!cur || !near(cur.pts[cur.pts.length - 1], s)) { finish(); cur = { pts: [{ x: s.x, y: s.y }], closed: false }; }
const last = cur.pts[cur.pts.length - 1];
if (p.k === 'A') last.b = Math.tan((p.a1 - p.a0) / 4);
cur.pts.push({ x: e.x, y: e.y });
}
finish();
return out;
}
A path through a block transform
64-bit
A path placed by its block reference, keeping arcs where the transform allows.
The transform keeps shapes when |a² + b² − (c² + d²)| and |a·c + b·d| are both within 1e-9 of the sum of the four squares. Then points go through the matrix and bulges are kept, negated under a mirror. Otherwise arcs are flattened first, by laser/laser-geom.js, to a tolerance of tol ÷ √|det|.
laser-cad.js
lines 58 to 65
CadCraft 0.8.2
/** A path through the matrix m; arcs stay arcs while m keeps shapes (a mirror turns their bulges round), else they are flattened. */
function txPath(path, m, tol) {
const [a, b, c, d] = m, det = a * d - b * c;
const similar = Math.abs(a * a + b * b - (c * c + d * d)) <= 1e-9 * (a * a + b * b + c * c + d * d) && Math.abs(a * c + b * d) <= 1e-9 * (a * a + b * b + c * c + d * d);
if (similar || !path.pts.some((p) => p.b)) return { closed: path.closed, pts: path.pts.map((p) => { const q = mApply(m, p); return p.b && similar ? { x: q.x, y: q.y, b: det < 0 ? -p.b : p.b } : { x: q.x, y: q.y }; }) };
const s = Math.sqrt(Math.abs(det)) || 1;
return { closed: path.closed, pts: flatten(path.pts, path.closed, tol / s).map((p) => mApply(m, p)) };
}
Drawing units to millimeters
64-bit
The one scale factor that puts the drawing into millimeters.
k = millimeters per drawing unit, or 1 when the drawing has no units
The factor comes from mmPerDrawingUnit in units.js and is used as the scale of the root transform, so every coordinate is multiplied by it once.
laser-cad.js
lines 83 to 84
CadCraft 0.8.2
const per = mmPerDrawingUnit(doc.meta?.lunits, doc.meta?.units);
const k = per || 1;
Picture placement
64-bit
Where a picture lies on the laser bed and how large it is.
w = u_x × picture width; h = v_y × picture height, u and v the picture's axes through the transform
A picture is sent only when it lies square: its u axis along +x and its v axis along +y, with the cross components within 1e-6 of the main ones. Its position is the picture's corner through the block transform.
Pieces from geom.js chained end to end; bulge = tan(sweep ÷ 4); 2 × 3 matrices for blocks.
Number types
64-bit numbers.
Tolerances
Ends chain within 1e-6 × max(1, |coordinate|). A transform keeps shapes when its columns agree within 1e-9 of the sum of their squared lengths. Flattening tolerance tol ÷ √|det|, tol = 0.01 by default. A picture lies square within 1e-6, relative.
Limits
Ellipses and splines arrive as the short segments geom.js makes of them. Text, dimensions, leaders, points and construction lines are not sent.
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.