Cuts lines and arcs into the dashes, gaps and dots of their linetype for the screen and for plots.
How the precision is kept
Standard patterns are defined in inches and multiplied by 25.4 in a metric drawing. Dashes are cut from each piece by fractions of its length in 64-bit numbers; positions along a piece are kept as a running sum.
Calculations under this policy
2 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 dash and gap lengths of a linetype for this drawing.
metric drawing: inch pattern × 25.4
A pattern the file brought is used as it is. A standard pattern is in inches and each of its numbers is multiplied by 25.4 for a metric drawing; the half-size and double-size variants are the base pattern × 0.5 and × 2, which are exact.
linetypes.js
lines 45 to 53
CadCraft 0.8.2
/** The dash pattern of a linetype in drawing units, or null when it draws continuous. */
export function patternOf(doc, name) {
if (isCont(name) || isByLayer(name) || isByBlock(name)) return null;
const own = doc?.linetypes?.get?.(name) || [...(doc?.linetypes?.values?.() || [])].find((l) => l.name.toUpperCase() === String(name).toUpperCase());
if (own) return usable(own.pattern) ? own.pattern : null;
const std = STANDARD[String(name).toUpperCase()];
if (!std) return null;
return hatchIsMetric({}, doc) ? X(25.4, std) : std;
}
Dashes along lines and arcs
64-bit
The pieces of each line or arc that are drawn.
piece from fraction s ÷ length to (s + step) ÷ length; arc length = r × sweep
The position s along a piece and the length left of the current dash are updated by repeated addition and subtraction, with a 1e-12 absolute allowance. Each dash is then cut by its two fractions of the piece's length, so its ends are computed from the piece's own ends or angles.
linetypes.js
lines 71 to 95
CadCraft 0.8.2
/** Dashes of geom.js primitives: L {x1,y1,x2,y2} and A {cx,cy,r,a0,a1,rev?}; the pattern restarts on every primitive run. */
export function dashPrims(prims, pattern, scale = 1) {
if (!pattern || !(scale > 0)) return prims;
const p = pattern.map((v) => v * scale);
const period = p.reduce((s, v) => s + Math.abs(v), 0);
if (!(period > 1e-12)) return prims;
let total = 0;
for (const q of prims) total += q.k === 'L' ? Math.hypot(q.x2 - q.x1, q.y2 - q.y1) : q.r * Math.abs(sweep(q));
if (total / period * p.length > MAX_DASHES) return prims; // too fine to see: drawn continuous
const dot = period * 0.01;
const out = [];
let i = 0, left = Math.abs(p[0]); // the pattern runs on along a polyline's pieces
for (const q of prims) {
const len = q.k === 'L' ? Math.hypot(q.x2 - q.x1, q.y2 - q.y1) : q.r * Math.abs(sweep(q));
let s = 0;
while (s < len - 1e-12) {
const v = p[i], step = Math.min(left, len - s);
if (v > 0) out.push(piece(q, s / len, (s + step) / len));
else if (v === 0 && left === Math.abs(v)) out.push(piece(q, s / len, Math.min(len, s + dot) / len));
s += step; left -= step;
if (left <= 1e-12) { i = (i + 1) % p.length; left = Math.abs(p[i]); if (left === 0) { if (p[i] === 0 && s < len - 1e-12) out.push(piece(q, s / len, Math.min(len, s + dot) / len)); i = (i + 1) % p.length; left = Math.abs(p[i]); } }
}
}
return out;
}
In detail
Method
Walk along each piece, taking dash and gap lengths in turn; the pattern carries on from one piece of a polyline to the next.
Number types
64-bit numbers.
Tolerances
1e-12 absolute on the remaining length of a piece and of a dash. A pattern whose total length is 1e-12 or less is not used.
Limits
A line that would need more than 20,000 pieces is drawn continuous. A dot is drawn as a dash 1% of the pattern's period long.
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.