Draws the sketch, its dimensions and their labels on the canvas over the 3-D view.
How the precision is kept
This code maps sketch coordinates to canvas pixels and lays out dimension lines, arrows and label text. It reads the sketch and does not change its geometry. Dimension labels show the stored value rounded to 3 decimals.
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.
x = w ÷ 2 + (u − c_x) × s; y = h ÷ 2 − (v − c_y) × s
64-bit arithmetic whose result is a pixel position; it is used to draw and to place the input box.
sketchview.js
line 91
PartCraft 0.8.1
toScreen([u, v]) { const { w, h } = this.size(); return [w / 2 + (u - this.view.cx) * this.view.s, h / 2 - (v - this.view.cy) * this.view.s]; }
Dimension lines and label
32-bit, display only
The lines, arcs, arrowheads and text of a dimension as drawn.
label text = fmt(value, 3)
The label shows the dimension's value rounded to 3 decimals; the stored value is not changed here. Default label positions use the rounded constant 0.7071 for a 45° direction, which only places the label.
sketchview.js
lines 1410 to 1507
PartCraft 0.8.1
/** Lines, arcs, arrowheads and the label for a dimension, all in sketch coordinates. */
export function dimGeometry(sk, c) {
if (c.off) { const a = dimAnchor(sk, c); if (a) c = { ...c, at: [a[0] + c.off[0], a[1] + c.off[1]] }; }
const P = (id) => S.pointXY(sk, id);
const E = (id) => S.byId(sk, id);
const m = S.matchSig(sk, c.type, c.refs);
if (!m) return null;
const sig = m.sig, r = c.refs;
const scale = Math.max(1, S.extent(sk));
const off = scale * 0.08;
const out = { lines: [], arcs: [], arrows: [], text: '', at: null };
const v = fmt(c.value, 3);
if (c.type === 'radius' || c.type === 'diameter') {
const e = E(r[0]), ce = P(e.c), rad = S.radiusOf(sk, e);
let dir = c.at ? [c.at[0] - ce[0], c.at[1] - ce[1]] : [0.7071, 0.7071];
if (e.type === 'arc' && !c.at) { const { a0, sweep } = S.arcAngles(sk, e); dir = [Math.cos(a0 + sweep / 2), Math.sin(a0 + sweep / 2)]; }
const dl = Math.hypot(...dir) || 1; dir = [dir[0] / dl, dir[1] / dl];
const edge = [ce[0] + dir[0] * rad, ce[1] + dir[1] * rad];
const at = c.at || [ce[0] + dir[0] * (rad + off), ce[1] + dir[1] * (rad + off)];
if (c.type === 'radius') { out.lines.push([ce, edge], [edge, at]); out.arrows.push([edge, dir]); out.text = 'R' + v; }
else { const back = [ce[0] - dir[0] * rad, ce[1] - dir[1] * rad]; out.lines.push([back, edge], [edge, at]); out.arrows.push([edge, dir], [back, [-dir[0], -dir[1]]]); out.text = '⌀' + v; }
out.at = at;
return out;
}
if (c.type === 'angle') {
const l1 = E(r[0]), l2 = E(r[1]);
const a1 = P(l1.p1), b1 = P(l1.p2), a2 = P(l2.p1), b2 = P(l2.p2);
const d1 = [b1[0] - a1[0], b1[1] - a1[1]], d2 = [b2[0] - a2[0], b2[1] - a2[1]];
const den = d1[0] * d2[1] - d1[1] * d2[0];
if (Math.abs(den) < 1e-12) return null;
const t = ((a2[0] - a1[0]) * d2[1] - (a2[1] - a1[1]) * d2[0]) / den;
const X = [a1[0] + d1[0] * t, a1[1] + d1[1] * t];
const far = (a, b) => (Math.hypot(a[0] - X[0], a[1] - X[1]) > Math.hypot(b[0] - X[0], b[1] - X[1]) ? a : b);
const f1 = far(a1, b1), f2 = far(a2, b2);
const ang1 = Math.atan2(f1[1] - X[1], f1[0] - X[0]), ang2 = Math.atan2(f2[1] - X[1], f2[0] - X[0]);
let s = ang1, e = ang2, d = e - s;
while (d > Math.PI) d -= 2 * Math.PI; while (d < -Math.PI) d += 2 * Math.PI;
if (d < 0) { s = ang2; d = -d; }
const rad = c.at ? Math.hypot(c.at[0] - X[0], c.at[1] - X[1]) : Math.min(Math.hypot(f1[0] - X[0], f1[1] - X[1]), Math.hypot(f2[0] - X[0], f2[1] - X[1])) * 0.6 || off;
out.arcs.push({ c: X, r: rad, a0: s, a1: s + d });
const mid = s + d / 2;
out.at = c.at || [X[0] + Math.cos(mid) * rad, X[1] + Math.sin(mid) * rad];
out.text = v + '°';
out.arrows.push([[X[0] + Math.cos(s) * rad, X[1] + Math.sin(s) * rad], [Math.sin(s), -Math.cos(s)]], [[X[0] + Math.cos(s + d) * rad, X[1] + Math.sin(s + d) * rad], [-Math.sin(s + d), Math.cos(s + d)]]);
return out;
}
if (c.type === 'offset' && sig === 'RR') {
const a = E(r[0]), b = E(r[1]), ce = P(a.c), ra = S.radiusOf(sk, a), rb = S.radiusOf(sk, b);
let dir = c.at ? [c.at[0] - ce[0], c.at[1] - ce[1]] : [0.7071, 0.7071];
if (b.type === 'arc' && !c.at) { const { a0, sweep } = S.arcAngles(sk, b); dir = [Math.cos(a0 + sweep / 2), Math.sin(a0 + sweep / 2)]; }
const dl = Math.hypot(...dir) || 1; dir = [dir[0] / dl, dir[1] / dl];
const pa = [ce[0] + dir[0] * ra, ce[1] + dir[1] * ra], pb = [ce[0] + dir[0] * rb, ce[1] + dir[1] * rb];
out.lines.push([pa, pb]);
out.arrows.push([pb, [pb[0] - pa[0], pb[1] - pa[1]]], [pa, [pa[0] - pb[0], pa[1] - pb[1]]]);
out.at = c.at || [(pa[0] + pb[0]) / 2 + dir[0] * off * 0.3, (pa[1] + pb[1]) / 2 + dir[1] * off * 0.3];
out.text = v;
return out;
}
if ((c.type === 'distance' || c.type === 'offset') && (sig === 'PL' || sig === 'LL')) {
const p = sig === 'PL' ? P(r[0]) : P(E(r[1]).p1);
const L = E(sig === 'PL' ? r[1] : r[0]);
const a = P(L.p1), b = P(L.p2);
const dx = b[0] - a[0], dy = b[1] - a[1], l2 = dx * dx + dy * dy || 1e-300;
const t = ((p[0] - a[0]) * dx + (p[1] - a[1]) * dy) / l2;
const q = [a[0] + t * dx, a[1] + t * dy];
out.lines.push([p, q]);
if (t < 0 || t > 1) out.lines.push([t < 0 ? a : b, q]);
const u = [q[0] - p[0], q[1] - p[1]];
out.arrows.push([q, u], [p, [-u[0], -u[1]]]);
out.at = c.at || [(p[0] + q[0]) / 2, (p[1] + q[1]) / 2];
if (c.at) out.lines.push([[(p[0] + q[0]) / 2, (p[1] + q[1]) / 2], c.at]);
out.text = v;
return out;
}
// point-to-point / line length: aligned, horizontal or vertical
const [a, b] = sig === 'L' ? [P(E(r[0]).p1), P(E(r[0]).p2)] : [P(r[0]), P(r[1])];
if (c.type === 'hdistance' || c.type === 'vdistance') {
const k = c.type === 'hdistance' ? 1 : 0; // the coordinate the dimension line sits at
const lvl = c.at ? c.at[k] : Math.max(a[k], b[k]) + off;
const A = k === 1 ? [a[0], lvl] : [lvl, a[1]], B = k === 1 ? [b[0], lvl] : [lvl, b[1]];
out.lines.push([a, A], [b, B], [A, B]);
const u = [B[0] - A[0], B[1] - A[1]];
out.arrows.push([A, [-u[0], -u[1]]], [B, u]);
out.at = c.at || [(A[0] + B[0]) / 2, (A[1] + B[1]) / 2];
out.text = v;
return out;
}
const d = [b[0] - a[0], b[1] - a[1]], l = Math.hypot(...d) || 1;
const n = [-d[1] / l, d[0] / l];
const mid = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
const o = c.at ? (c.at[0] - a[0]) * n[0] + (c.at[1] - a[1]) * n[1] : off;
const A = [a[0] + n[0] * o, a[1] + n[1] * o], B = [b[0] + n[0] * o, b[1] + n[1] * o];
out.lines.push([a, A], [b, B], [A, B]);
out.arrows.push([A, [-d[0], -d[1]]], [B, d]);
out.at = c.at || [mid[0] + n[0] * o, mid[1] + n[1] * o];
out.text = v;
return out;
}
In detail
Method
screen = (w ÷ 2 + (u − c_x) × s, h ÷ 2 − (v − c_y) × s); dimension lines and arcs placed from the measured geometry.
Number types
64-bit numbers handed to the 2-D canvas.
Approach: Display only
Computed to draw the screen, often in 32-bit on the graphics processor. No number a person reads, saves or exports comes from it.