Resources Numerical accuracy Calculation policy

Drawing sheet views, dimensions and PDF

Projects the part into front, top, right and isometric views, finds hidden lines, places overall dimensions and the hole table, and writes the sheet as a PDF.

How the precision is kept

Projection and placement are 64-bit dot products and scalings. Visible and hidden lines are found by sampling: each edge is cut into pieces of 1/150 of the part's size and the middle of each piece is tested against the facets in front of it, so the point where a line turns hidden is located to that step. Dimension texts are rounded to 2 decimals and the PDF stores sheet coordinates to 4 decimals of a millimeter.

Calculations under this policy

12 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.

drawing.js PartCraft 0.8.1 64-bit 12 calculations Reviewed

Tests bin/test-partcraft.mjs, tools/partcraft-test/test-drawing-auto.mjs, tools/partcraft-test/test-holetable.mjs

Edges and facet normals

64-bit

Finds the mesh's edges, each facet's unit normal, and which edges are sharp.

sharp when n₁ · n₂ < cos 20°

Normals are a normalized cross product stored in a Float64Array. An edge's key is a × 2³² + b held in a 64-bit number, which is an exact whole number only while the smaller vertex index is below 2²¹ (about 2.1 million vertices).

  • drawing.js
  • lines 54 to 78
  • PartCraft 0.8.1
/** Edges of a mesh: [{ a, b (vertex indices), t1, t2 (triangles), sharp }] and triangle normals. */
export function meshEdges(mesh) {
  const P = mesh.positions, I = mesh.indices, nt = I.length / 3;
  const normals = new Float64Array(nt * 3);
  for (let t = 0; t < nt; t++) {
    const p = (k) => [P[I[t * 3 + k] * 3], P[I[t * 3 + k] * 3 + 1], P[I[t * 3 + k] * 3 + 2]];
    const n = V.norm(V.cross(V.sub(p(1), p(0)), V.sub(p(2), p(0))));
    normals.set(n, t * 3);
  }
  const map = new Map();
  for (let t = 0; t < nt; t++) for (let k = 0; k < 3; k++) {
    const a = I[t * 3 + k], b = I[t * 3 + (k + 1) % 3];
    const key = a < b ? a * 4294967296 + b : b * 4294967296 + a;
    const e = map.get(key);
    if (e) e.t2 = t; else map.set(key, { a: Math.min(a, b), b: Math.max(a, b), t1: t, t2: -1 });
  }
  const edges = [];
  const nrm = (t) => [normals[t * 3], normals[t * 3 + 1], normals[t * 3 + 2]];
  for (const e of map.values()) {
    if (e.t2 < 0) { e.sharp = true; edges.push(e); continue; }
    e.sharp = V.dot(nrm(e.t1), nrm(e.t2)) < SHARP;
    edges.push(e);
  }
  return { edges, normals };
}

One projected view with hidden lines

64-bit

Projects the mesh onto a view plane, finds the view's extents, and splits each drawn edge into visible and hidden parts.

(u, v, w) = (p · right, p · up, p · dir)

Projected coordinates are 64-bit dot products; the view box is their minimum and maximum, and the overall dimensions are taken from it. Visibility is sampled at the middle of pieces size ÷ 150 long, with a depth allowance of 1e-5 × size, so a visible to hidden change is placed at a piece boundary, not at the exact crossing.

  • drawing.js
  • lines 80 to 156
  • PartCraft 0.8.1
/** One view: visible and hidden 2D segments (model units, view coordinates u right, v up) and its 2D extents. */
export function projectView(mesh, edgesInfo, view, { hidden = true } = {}) {
  const P = mesh.positions, I = mesh.indices, nt = I.length / 3;
  const { edges, normals } = edgesInfo;
  const pr = (i) => { const p = [P[i * 3], P[i * 3 + 1], P[i * 3 + 2]]; return [V.dot(p, view.right), V.dot(p, view.up), V.dot(p, view.dir)]; };
  const nv = P.length / 3;
  const Q = new Float64Array(nv * 3);
  let u0 = Infinity, v0 = Infinity, u1 = -Infinity, v1 = -Infinity, wMin = Infinity, wMax = -Infinity;
  for (let i = 0; i < nv; i++) {
    const q = pr(i); Q.set(q, i * 3);
    u0 = Math.min(u0, q[0]); u1 = Math.max(u1, q[0]); v0 = Math.min(v0, q[1]); v1 = Math.max(v1, q[1]); wMin = Math.min(wMin, q[2]); wMax = Math.max(wMax, q[2]);
  }
  const size = Math.max(u1 - u0, v1 - v0, wMax - wMin, 1e-9);
  // grid of triangles facing the viewer (the back faces are behind them in a closed solid)
  const G = 96;
  const cellW = (u1 - u0) / G || 1, cellH = (v1 - v0) / G || 1;
  const grid = Array.from({ length: G * G }, () => []);
  const front = [];
  for (let t = 0; t < nt; t++) {
    const nd = normals[t * 3] * view.dir[0] + normals[t * 3 + 1] * view.dir[1] + normals[t * 3 + 2] * view.dir[2];
    if (nd <= 1e-9) continue;
    front.push(t);
    const a = I[t * 3], b = I[t * 3 + 1], c = I[t * 3 + 2];
    const tu0 = Math.min(Q[a * 3], Q[b * 3], Q[c * 3]), tu1 = Math.max(Q[a * 3], Q[b * 3], Q[c * 3]);
    const tv0 = Math.min(Q[a * 3 + 1], Q[b * 3 + 1], Q[c * 3 + 1]), tv1 = Math.max(Q[a * 3 + 1], Q[b * 3 + 1], Q[c * 3 + 1]);
    const gi0 = Math.max(0, Math.floor((tu0 - u0) / cellW)), gi1 = Math.min(G - 1, Math.floor((tu1 - u0) / cellW));
    const gj0 = Math.max(0, Math.floor((tv0 - v0) / cellH)), gj1 = Math.min(G - 1, Math.floor((tv1 - v0) / cellH));
    for (let i = gi0; i <= gi1; i++) for (let j = gj0; j <= gj1; j++) grid[j * G + i].push(t);
  }
  const eps = 1e-6 * size;
  const occluded = (u, v, w) => {
    const i = Math.min(G - 1, Math.max(0, Math.floor((u - u0) / cellW))), j = Math.min(G - 1, Math.max(0, Math.floor((v - v0) / cellH)));
    for (const t of grid[j * G + i]) {
      const a = I[t * 3] * 3, b = I[t * 3 + 1] * 3, c = I[t * 3 + 2] * 3;
      const x1 = Q[a], y1 = Q[a + 1], x2 = Q[b], y2 = Q[b + 1], x3 = Q[c], y3 = Q[c + 1];
      const den = (y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3);
      if (Math.abs(den) < 1e-12 * size * size) continue;
      const l1 = ((y2 - y3) * (u - x3) + (x3 - x2) * (v - y3)) / den;
      const l2 = ((y3 - y1) * (u - x3) + (x1 - x3) * (v - y3)) / den;
      const l3 = 1 - l1 - l2;
      const m = 1e-7;
      if (l1 < m || l2 < m || l3 < m) continue;
      const depth = l1 * Q[a + 2] + l2 * Q[b + 2] + l3 * Q[c + 2];
      if (depth > w + eps * 10) return true;
    }
    return false;
  };
  const visible = [], hid = [];
  const step = size / 150;
  const nd = (t) => normals[t * 3] * view.dir[0] + normals[t * 3 + 1] * view.dir[1] + normals[t * 3 + 2] * view.dir[2];
  for (const e of edges) {
    let draw = e.sharp;
    if (!draw && e.t2 >= 0) { const d1 = nd(e.t1), d2 = nd(e.t2); draw = (d1 > 1e-9) !== (d2 > 1e-9); }
    if (!draw) continue;
    const A = [Q[e.a * 3], Q[e.a * 3 + 1], Q[e.a * 3 + 2]], B = [Q[e.b * 3], Q[e.b * 3 + 1], Q[e.b * 3 + 2]];
    const len2 = Math.hypot(B[0] - A[0], B[1] - A[1]);
    if (len2 < 1e-9 * size) continue;   // seen end-on
    const n = Math.max(1, Math.ceil(Math.hypot(B[0] - A[0], B[1] - A[1], B[2] - A[2]) / step));
    let runStart = 0, runVis = null;
    const at = (k) => [A[0] + (B[0] - A[0]) * k / n, A[1] + (B[1] - A[1]) * k / n];
    for (let k = 0; k <= n; k++) {
      let vis = null;
      if (k < n) {
        const tm = (k + 0.5) / n;
        vis = !occluded(A[0] + (B[0] - A[0]) * tm, A[1] + (B[1] - A[1]) * tm, A[2] + (B[2] - A[2]) * tm);
      }
      if (k === n || (runVis !== null && vis !== runVis)) {
        const p = at(runStart), q = at(k);
        (runVis ? visible : hid).push([p[0], p[1], q[0], q[1]]);
        runStart = k;
      }
      runVis = vis;
    }
  }
  const vis = mergeSegments(visible, size);
  return { visible: vis, hidden: hidden ? mergeSegments(hid, size, vis) : [], box: { u0, v0, u1, v1 } };
}

Merging collinear segments

64-bit

Joins touching or overlapping segments on the same line and removes hidden lines under visible ones.

Segments are grouped by a key of the direction angle rounded to 1e-6 radian and the line offset rounded to 1e-5 × size; the merged line takes the direction and offset of the first segment in the group. Two parallel edges closer than that offset step are therefore drawn as one line.

  • drawing.js
  • lines 158 to 213
  • PartCraft 0.8.1
/**
 * Join collinear segments that touch or overlap into one (so a dashed line keeps its rhythm and
 * an edge seen twice — front and back edges in line — is drawn once). `minus`: segments to cut out
 * (hidden lines are not drawn where a visible line covers them).
 */
export function mergeSegments(segs, size, minus = []) {
  const tol = 1e-6 * size;
  const lines = new Map();
  const lineOf = (s) => {
    let dx = s[2] - s[0], dy = s[3] - s[1];
    const l = Math.hypot(dx, dy);
    if (l < tol) return null;
    dx /= l; dy /= l;
    if (dx < -1e-12 || (Math.abs(dx) <= 1e-12 && dy < 0)) { dx = -dx; dy = -dy; }
    const off = dx * s[1] - dy * s[0];
    return { dx, dy, off, key: Math.round(Math.atan2(dy, dx) * 1e6) + ':' + Math.round(off / (tol * 10)) };
  };
  const group = (list, into) => {
    for (const s of list) {
      const L = lineOf(s);
      if (!L) continue;
      let g = into.get(L.key);
      if (!g) { g = { ...L, iv: [] }; into.set(L.key, g); }
      const t0 = s[0] * L.dx + s[1] * L.dy, t1 = s[2] * L.dx + s[3] * L.dy;
      g.iv.push([Math.min(t0, t1), Math.max(t0, t1)]);
    }
  };
  group(segs, lines);
  const cut = new Map();
  group(minus, cut);
  const union = (iv) => {
    iv.sort((a, b) => a[0] - b[0]);
    const out = [];
    for (const v of iv) { const last = out[out.length - 1]; if (last && v[0] <= last[1] + tol) last[1] = Math.max(last[1], v[1]); else out.push(v.slice()); }
    return out;
  };
  const result = [];
  for (const [key, g] of lines) {
    let iv = union(g.iv);
    const c = cut.get(key);
    if (c) {
      const cu = union(c.iv);
      const left = [];
      for (const [a, b] of iv) {
        let pieces = [[a, b]];
        for (const [x, y] of cu) pieces = pieces.flatMap(([p, q]) => (y <= p + tol || x >= q - tol ? [[p, q]] : [[p, Math.min(q, x)], [Math.max(p, y), q]]).filter(([u, v]) => v - u > tol));
        left.push(...pieces);
      }
      iv = left;
    }
    // a point on the line: foot from the origin
    const px = -g.dy * g.off, py = g.dx * g.off;
    for (const [a, b] of iv) result.push([px + g.dx * a, py + g.dy * a, px + g.dx * b, py + g.dy * b]);
  }
  return result;
}

Section hatching

64-bit

Hatch lines at an angle inside the cut outline.

Each hatch line is crossed with the outline segments by linear interpolation k = p ÷ (p − q), skipped when p equals q. The line positions are stepped by repeated addition of the gap from the first multiple of the gap, so rounding can build up slightly across many lines. Spans shorter than 1e-9 are dropped.

  • drawing.js
  • lines 304 to 329
  • PartCraft 0.8.1
/** Even-odd hatch lines (45°, `gap` apart) inside the area bounded by `segs` (any order). */
export function hatch(segs, gap, angle = 45) {
  const a = angle * Math.PI / 180, dx = Math.cos(a), dy = Math.sin(a);
  const nx = -dy, ny = dx;   // lines are {p : p·n = c}
  let c0 = Infinity, c1 = -Infinity;
  for (const s of segs) for (const [x, y] of [[s[0], s[1]], [s[2], s[3]]]) { const c = x * nx + y * ny; c0 = Math.min(c0, c); c1 = Math.max(c1, c); }
  const out = [];
  if (!(c1 > c0)) return out;
  for (let c = Math.ceil(c0 / gap) * gap; c < c1; c += gap) {
    const ts = [];
    for (const s of segs) {
      const p = s[0] * nx + s[1] * ny - c, q = s[2] * nx + s[3] * ny - c;
      if ((p > 0) === (q > 0) || p === q) continue;
      const k = p / (p - q);
      const x = s[0] + (s[2] - s[0]) * k, y = s[1] + (s[3] - s[1]) * k;
      ts.push(x * dx + y * dy);
    }
    ts.sort((u, v) => u - v);
    for (let i = 0; i + 1 < ts.length; i += 2) {
      const t0 = ts[i], t1 = ts[i + 1];
      if (t1 - t0 < 1e-9) continue;
      out.push([nx * c + dx * t0, ny * c + dy * t0, nx * c + dx * t1, ny * c + dy * t1]);
    }
  }
  return out;
}

Clip to the detail circle

64-bit

Keeps the parts of segments inside the detail view's circle.

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

The textbook quadratic formula; one of the two roots subtracts nearly equal numbers when B² is much larger than 4AC. Segments with squared length under 1e-24 and segments with a discriminant at or below zero are dropped.

  • drawing.js
  • lines 331 to 347
  • PartCraft 0.8.1
/** Clip 2D segments to a circle (centre c, radius r). */
export function clipToCircle(segs, c, r) {
  const out = [];
  for (const s of segs) {
    const ax = s[0] - c[0], ay = s[1] - c[1], bx = s[2] - c[0], by = s[3] - c[1];
    const dx = bx - ax, dy = by - ay;
    const A = dx * dx + dy * dy, B = 2 * (ax * dx + ay * dy), C = ax * ax + ay * ay - r * r;
    if (A < 1e-24) continue;
    const disc = B * B - 4 * A * C;
    if (disc <= 0) continue;
    const sq = Math.sqrt(disc);
    const t0 = Math.max(0, (-B - sq) / (2 * A)), t1 = Math.min(1, (-B + sq) / (2 * A));
    if (t1 - t0 <= 1e-12) continue;
    out.push([s[0] + (s[2] - s[0]) * t0, s[1] + (s[3] - s[1]) * t0, s[0] + (s[2] - s[0]) * t1, s[1] + (s[3] - s[1]) * t1]);
  }
  return out;
}

Holes for the hole table

64-bit

Collects each hole's axis, diameter and callout from the kernel's cylindrical faces.

diameter = 2 × smallest radius on the axis

Cylinders are grouped into one hole when their axis direction and axis foot point agree after rounding to 1e-4. The diameter and depth kept in the row are unrounded; the callout text shows 2 decimals.

  • drawing.js
  • lines 362 to 392
  • PartCraft 0.8.1
/**
 * The part's holes for the hole table (0.4 P23): the exact kernel's cylindrical faces made by hole features (pattern copies
 * too), one per axis line (a counterbore's two cylinders are one hole), the smallest radius the drill; the callout in words
 * (THRU / DEEP, counterbore, countersink; a tapped hole's thread callout). → [{ o, d, dia, depth, callout, feature }] or null
 * (the mesh kernel has no face records).
 */
export function partHoles(part, regen) {
  if (!(regen?.exact || regen?.named) || !Array.isArray(regen.faces)) return null;   // either kernel's named faces (0.4 K.2b)
  const holeF = new Map(part.features.filter((f) => f.type === 'hole').map((f) => [f.id, f]));
  const groups = new Map();
  for (const fr of regen.faces) {
    if (!fr || fr.type !== 'cylinder' || !fr.axis || !fr.name) continue;
    const fid = nameFeatures(fr.name).find((id) => holeF.has(id));
    if (!fid) continue;
    let d = V.norm(fr.axis.d);
    if (d[0] < -1e-9 || (Math.abs(d[0]) <= 1e-9 && (d[1] < -1e-9 || (Math.abs(d[1]) <= 1e-9 && d[2] < 0)))) d = V.mul(d, -1);
    const o = V.sub(fr.axis.o, V.mul(d, V.dot(fr.axis.o, d)));
    const key = fid + '|' + [...d, ...o].map((v) => Math.round(v * 1e4)).join(',');
    const g = groups.get(key);
    if (g) g.r = Math.min(g.r, fr.r); else groups.set(key, { fid, d, o, r: fr.r });
  }
  const f2 = (v) => String(+v.toFixed(2));
  return [...groups.values()].map(({ fid, d, o, r }) => {
    const f = holeF.get(fid), dia = 2 * r, th = regen.threads?.get?.(fid);
    let callout = f.through ? t('drawing.holes.thru', { d: f2(dia) }) : t('drawing.holes.deep', { d: f2(dia), depth: f2(f.depth) });
    if (f.kind === 'cbore') callout += t('drawing.holes.cbore', { d: f2(f.cboreD), depth: f2(f.cboreDepth) });
    if (f.kind === 'csink') callout += t('drawing.holes.csink', { d: f2(f.csinkD), angle: f.csinkAngle });
    if (f.kind === 'tapped' && th?.callout) callout = th.callout;
    return { o, d, dia, depth: f.through ? null : f.depth, callout, feature: f.name };
  });
}

Hole table rows

64-bit

The X and Y of each hole measured from the view's lower-left corner, and its tag.

x = o · right − u₀; y = o · up − v₀

Positions are 64-bit dot products minus the view box corner, kept unrounded in the rows. A hole is listed only when its axis is within 1e-9 (in cosine) of the view direction. Sizes are grouped by the diameter written to 6 decimals.

  • drawing.js
  • lines 394 to 419
  • PartCraft 0.8.1
/**
 * The hole table's rows (0.4 P23): the holes seen end-on in `view` (axis along its direction), measured from the view box's
 * lower-left corner (u0, v0), tagged by size: A1, A2 … for the first size, B1 … for the next (sizes by diameter, then callout).
 * holes = [{ o, d (unit axis), dia, depth (null = through), callout, feature }] (partcraft.js gathers them from the exact
 * kernel's cylindrical faces, pattern copies included). → { rows: [{ tag, x, y, u, v, dia, size, depth, feature }], skipped }
 */
export function holeTableRows(holes, view, box) {
  const V0 = VIEWS[view];
  if (!V0) return { rows: [], skipped: holes.length };
  const rows = [];
  let skipped = 0;
  for (const h of holes) {
    if (Math.abs(V.dot(V.norm(h.d), V0.dir)) < 1 - 1e-9) { skipped++; continue; }
    const u = V.dot(h.o, V0.right), v = V.dot(h.o, V0.up);
    rows.push({ u, v, x: u - box.u0, y: v - box.v0, dia: h.dia, size: h.callout || 'Ø' + fmtLen(h.dia), depth: h.depth, feature: h.feature });
  }
  const sizes = [...new Set(rows.map((r) => r.dia.toFixed(6) + '|' + r.size))].sort((a, b) => parseFloat(a) - parseFloat(b) || a.localeCompare(b));
  rows.sort((a, b) => sizes.indexOf(a.dia.toFixed(6) + '|' + a.size) - sizes.indexOf(b.dia.toFixed(6) + '|' + b.size) || b.y - a.y || a.x - b.x);
  const count = new Map();
  for (const r of rows) {
    const k = sizes.indexOf(r.dia.toFixed(6) + '|' + r.size), n = (count.get(k) || 0) + 1;
    count.set(k, n);
    r.tag = (k < 26 ? String.fromCharCode(65 + k) : 'Z' + (k - 25)) + n;
  }
  return { rows, skipped };
}

Sheet scale

Exact

Picks the drawing scale for a paper size.

first s in the ISO 5455 series with width × s ≤ 0.82 × area width and height × s ≤ 0.82 × area height

The scale is chosen from a fixed list (10, 5, 2, 1, 1/2 … 1/1000); nothing is computed or rounded into a non-standard scale.

  • drawing.js
  • lines 421 to 428
  • PartCraft 0.8.1
/** The scale composeSheet gives the three main views on a paper (the same rule it uses). */
export function sheetScale(views, key, revH = 0) {
  const [W, H] = PAPERS[key], gap = 18, tbH = 40;
  const bw = (v) => v.box.u1 - v.box.u0, bh = (v) => v.box.v1 - v.box.v0;
  const areaW = W - 2 * margin0 - 2 * gap, areaH = H - 2 * margin0 - tbH - 2 * gap - revH;
  const needW = bw(views.front) + bw(views.right), needH = bh(views.front) + bh(views.top);
  return SCALES.find((s) => needW * s <= areaW * 0.82 && needH * s <= areaH * 0.82) || SCALES[SCALES.length - 1];
}

Dimension text

64-bit

The text of an overall dimension or a hole table position.

text = value rounded to 2 decimals

The value is written with toFixed(2) and trailing zeros removed. Only the text is rounded; the geometry is not changed.

  • drawing.js
  • line 215
  • PartCraft 0.8.1
const fmtLen = (v) => String(Number(v.toFixed(2)));

Overall dimensions

64-bit

The width and height on the front view and the depth on the top view.

width = u₁ − u₀; height = v₁ − v₀

Each dimension is the difference of a view box's extents, which are the extreme projected mesh vertices. The printed value is that difference rounded to 2 decimals by fmtLen.

  • drawing.js
  • lines 569 to 574
  • PartCraft 0.8.1
const F = views.front, Tv = views.top;
const fx0 = place.front.x, fy0 = place.front.y, fx1 = fx0 + bw(F) * scale, fy1 = fy0 + bh(F) * scale;
dimH(fx0, fx1, fy0 - 14, fy0 - 1, fmtLen(bw(F)));
dimV(fy0, fy1, fx0 - 8, fx0 - 1, fmtLen(bh(F)));
const tx0 = place.top.x, ty0 = place.top.y, ty1 = ty0 + bh(Tv) * scale;
dimV(ty0, ty1, tx0 - 8, tx0 - 1, fmtLen(bh(Tv)));

Numbers in the PDF

64-bit

Writes one coordinate or size into the PDF content.

Written with toFixed(4), that is to 0.0001 mm on paper; a magnitude under 5e-5 is written as 0. A value that is not finite is written as 0 without a warning.

  • drawing.js
  • lines 730 to 734
  • PartCraft 0.8.1
function num(v) {
  if (!Number.isFinite(v)) return '0';
  const s = (Math.abs(v) < 5e-5 ? 0 : v).toFixed(4);
  return s.includes('.') ? s.replace(/0+$/, '').replace(/\.$/, '') : s;
}

PDF page content

64-bit

Writes the sheet's lines, fills and texts in millimeters under a millimeter to point transform.

1 mm = 72 ÷ 25.4 pt

The transform factor 72 ÷ 25.4 is written with 9 decimals (2.834645669). Text is placed from Helvetica character widths and rotated with Math.cos and Math.sin of the angle in radians.

  • drawing.js
  • lines 752 to 776
  • PartCraft 0.8.1
export async function toPdf(sheet) {
  const ops = [];
  ops.push((72 / 25.4).toFixed(9) + ' 0 0 ' + (72 / 25.4).toFixed(9) + ' 0 0 cm', '0 0 0 RG 0 0 0 rg 1 J 1 j');
  for (const s of sheet.strokes) {
    if (!s.segs.length) continue;
    ops.push(num(s.width) + ' w ' + (s.dash ? '[' + s.dash.map(num).join(' ') + '] 0 d' : '[] 0 d'));
    for (const g of s.segs) ops.push(num(g[0]) + ' ' + num(g[1]) + ' m ' + num(g[2]) + ' ' + num(g[3]) + ' l');
    ops.push('S');
  }
  for (const f of sheet.fills) ops.push(f.pts.map((p, i) => num(p[0]) + ' ' + num(p[1]) + (i ? ' l' : ' m')).join(' ') + ' h f');
  let missing = 0;
  for (const t of sheet.texts) {
    const enc = winAnsi(t.text);
    missing += enc.missing;
    const w = enc.width / 1000 * t.size;
    const dx = t.anchor === 'middle' ? -w / 2 : t.anchor === 'end' ? -w : 0;
    const r = (t.rot || 0) * Math.PI / 180, c = Math.cos(r), s = Math.sin(r);
    const x = t.x + dx * c, y = t.y + dx * s;
    // i18n-ignore: PDF content stream
    ops.push('BT /' + (t.bold ? 'F2' : 'F1') + ' 1 Tf ' + [t.size * c, t.size * s, -t.size * s, t.size * c, x, y].map(num).join(' ') + ' Tm (' + enc.str + ') Tj ET');
  }
  const content = new TextEncoder().encode(ops.join('\n') + '\n');
  const packed = await deflate(content);
  return { bytes: assemble(sheet.w, sheet.h, packed || content, !!packed, sheet.name), missing };
}

In detail

Method
Orthographic projection of the faceted mesh; sharp edges where facet normals differ by more than 20 degrees plus silhouettes; midpoint visibility test over a 96 × 96 grid of front-facing facets; collinear segment merging; ISO 5455 scale series.
Number types
64-bit numbers and Float64Array for projected coordinates and normals. The mesh positions it reads are whatever the kernel gave (32-bit from the mesh kernel).
Tolerances
All relative to the view's largest extent (size): depth test 1e-5 × size; barycentric margin 1e-7; degenerate facet 1e-12 × size²; edge seen end-on 1e-9 × size; merge tolerance 1e-6 × size, with lines keyed to 1e-6 radian and 1e-5 × size offset. Hole axis seen end-on: |d · view| ≥ 1 − 1e-9. Holes grouped by axis rounded to 1e-4.
Limits
Curves are their facets. Visible to hidden transitions are located to size ÷ 150. Dimension texts show 2 decimals; PDF coordinates 4 decimals of a millimeter on paper.

Approach: 64-bit, stated limit

The arithmetic is 64-bit, but the method or the data sets the accuracy: a solver tolerance, a mesh, a step size, a datum shift, a table. The limit is stated where the result is shown.

Every policy that takes this approach