Resources Numerical accuracy Calculation policy

Corner cabinet drilling positions

Works out where the shelf-pin, hinge-plate, fixing, pivot and shaft holes go on the parts of a corner cabinet.

How the precision is kept

Positions are sums and differences of the drilling rule values (system line, pitch, first hole, cup sizes) and the part sizes, in 64-bit numbers. Every coordinate that is kept is rounded to 6 decimals of the job unit by r6 (core.js: Math.round(v × 1e6) ÷ 1e6). Joint lines between boards are found with absolute tolerances of 1e-6 and 1e-4 job units.

Open item. Keep hole coordinates unrounded in the drilling plan and round only where a number is written out.

Calculations under this policy

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

addons/cabinetcraft/lib/cornerdrill.js CadCraft 0.8.2 64-bit 7 calculations Reviewed

Tests None found by the review

Board corners in the blank's own frame

64-bit

Turns a board's corners from the cabinet frame into the frame of its blank, and takes the blank's length and width.

x′ = (p − o)·uₓ; y′ = (p − o)·u_y; L = max x′; W = max y′

Each coordinate is one dot product of 64-bit numbers, then rounded to 6 decimals by r6. L and W are the largest of the already rounded coordinates and are rounded again.

  • addons/cabinetcraft/lib/cornerdrill.js
  • lines 35 to 39
  • CadCraft 0.8.2
const frameOf = (poly, o, ux, uy) => {
  const to = ([x, y]) => [r6((x - o[0]) * ux[0] + (y - o[1]) * ux[1]), r6((x - o[0]) * uy[0] + (y - o[1]) * uy[1])];
  const loc = poly.map(to);
  return { to, loc, L: r6(Math.max(...loc.map((p) => p[0]))), W: r6(Math.max(...loc.map((p) => p[1]))) };
};

Arm boards and triangle of a corner cabinet

64-bit

The outlines of the bottom and top boards for each corner form, from the cabinet's width, depth and panel thickness.

The L, diagonal and lazy susan outlines are sums and differences of w, d and t. For the angled diagonal form the direction of board B is its first edge divided by its length from Math.hypot; that division has no guard against a zero-length edge.

  • addons/cabinetcraft/lib/cornerdrill.js
  • lines 41 to 62
  • CadCraft 0.8.2
/** The arm boards and the triangle of a corner cabinet (bottom and top are the same shape), in the cabinet's frame, each with the frame of its blank. */
export function cornerBoards(cb) {
  const { form, w, d, t } = cb;
  const out = [];
  const mk = (key, poly, o, ux, uy) => { const f = frameOf(poly, o, ux, uy); out.push({ key, poly, o, ux, uy, ...f }); };
  if (form === 'l') {
    mk('a', [[t, 0], [w, 0], [w, d], [t, d]], [t, 0], [1, 0], [0, 1]);
    mk('b', [[w - d, d], [w, d], [w, w - t], [w - d, w - t]], [w - d, d], [0, 1], [1, 0]);
  } else if (form === 'diag' || form === 'lazy') {
    const a = w - d;
    mk('a', [[t, 0], [w, 0], [w, d], [t, d]], [t, 0], [1, 0], [0, 1]);
    mk('b', [[w - d, d], [w, d], [w, w - t], [w - d, w - t]], [w - d, d], [0, 1], [1, 0]);
    if (a - t > 1) mk('tri', [[t, d], [a, d], [a, w - t]], [t, d], [1, 0], [0, 1]);
  } else if (form === 'adiag' && cb.boards) {
    const { A, B, T, triBox } = cb.boards;
    mk('a', A, [A[0][0], A[0][1]], [1, 0], [0, 1]);
    const ux = [(B[1][0] - B[0][0]) / Math.hypot(B[1][0] - B[0][0], B[1][1] - B[0][1]), (B[1][1] - B[0][1]) / Math.hypot(B[1][0] - B[0][0], B[1][1] - B[0][1])];
    mk('b', B, B[0], ux, [-ux[1], ux[0]]);
    mk('tri', T, triBox.o, [triBox.ux.x, triBox.ux.y], [triBox.uy.x, triBox.uy.y]);
  }
  return out;
}

Joint lines two boards share

64-bit

Finds where an edge of one board lies along an edge of another, and the length of the overlap.

offset = (q − p₀) × u; s = (q − p₀)·u; overlap = [max(0, min s), min(L, max s)]

Collinearity is a perpendicular offset under 1e-4 at both end points, an absolute value in job units. Edges shorter than 1e-6 and overlaps shorter than 1 are skipped. The joint's end points and length are rounded to 6 decimals.

  • addons/cabinetcraft/lib/cornerdrill.js
  • lines 65 to 84
  • CadCraft 0.8.2
/** Joint lines: where an edge of one board lies on an edge of another (collinear and overlapping). */
export function sharedEdges(boards) {
  const out = [];
  for (let i = 0; i < boards.length; i++) for (let j = i + 1; j < boards.length; j++) {
    const A = boards[i], B = boards[j];
    for (let ea = 0; ea < A.poly.length; ea++) for (let eb = 0; eb < B.poly.length; eb++) {
      const p0 = A.poly[ea], p1 = A.poly[(ea + 1) % A.poly.length], q0 = B.poly[eb], q1 = B.poly[(eb + 1) % B.poly.length];
      const dx = p1[0] - p0[0], dy = p1[1] - p0[1], L = Math.hypot(dx, dy);
      if (L < EPS) continue;
      const ux = dx / L, uy = dy / L;
      const off = (q) => (q[0] - p0[0]) * -uy + (q[1] - p0[1]) * ux;
      if (Math.abs(off(q0)) > 1e-4 || Math.abs(off(q1)) > 1e-4) continue;
      const s0 = (q0[0] - p0[0]) * ux + (q0[1] - p0[1]) * uy, s1 = (q1[0] - p0[0]) * ux + (q1[1] - p0[1]) * uy;
      const lo = Math.max(0, Math.min(s0, s1)), hi = Math.min(L, Math.max(s0, s1));
      if (hi - lo < 1) continue;
      out.push({ a: A.key, b: B.key, ea, eb, p: [r6(p0[0] + ux * lo), r6(p0[1] + uy * lo)], q: [r6(p0[0] + ux * hi), r6(p0[1] + uy * hi)], len: r6(hi - lo) });
    }
  }
  return out;
}

System hole grid along a side

64-bit

The positions of the system holes along a side panel.

x = sysStart, sysStart + pitch, … while x ≤ len − sysStart

The positions come from adding the pitch repeatedly, not from sysStart + k × pitch, so rounding error can build up along the row; each value is then rounded to 6 decimals. The end test allows 1e-9 over.

  • addons/cabinetcraft/lib/cornerdrill.js
  • line 86
  • CadCraft 0.8.2
const gridX = (len, R) => { const out = []; for (let x = R.sysStart; x <= len - R.sysStart + 1e-9; x += R.pitch) out.push(r6(x)); return out; };

Hinge plate holes

64-bit

Places the two plate holes of each hinge on the system grid of the side panel.

center = reveal ÷ 2 + hinge spot; first hole = grid hole nearest (center − pitch ÷ 2); second = first + pitch

The first hole is an existing grid value, so it carries that value's rounding. The second is the first plus the pitch, rounded to 6 decimals. Two plate holes closer than 1e-6 are treated as one.

  • addons/cabinetcraft/lib/cornerdrill.js
  • lines 105 to 113
  • CadCraft 0.8.2
const plateOn = form === 'l' ? ['both'] : [cb.fronts[0].hinge === 'right' ? 'right' : 'left'];
for (const which of plateOn) {
  const list = [];
  for (const hx of hingeSpots(doorH, R)) {
    const centre = R.reveal / 2 + hx;
    const lo = snap(centre - R.pitch / 2, grid);
    for (const x of [lo, r6(lo + R.pitch)]) if (!list.some((q) => Math.abs(q.x - x) < EPS)) list.push({ x, y: yF, d: R.plateD, depth: R.plateDepth, kind: 'plate', side: which });
  }
  holes.push(...list);

Fixing holes along a joint

64-bit

Places the dowel or cam holes on the edge two boards share, in each board's own frame.

P = p + (q − p) ÷ len × u, u = sysStart and len − sysStart, or len ÷ 2

The joint's end points and length were already rounded to 6 decimals, and the direction is their quotient. The point is then projected into each board's frame and rounded to 6 decimals twice.

  • addons/cabinetcraft/lib/cornerdrill.js
  • lines 127 to 138
  • CadCraft 0.8.2
  const n = j.len >= 2 * R.sysStart + R.pitch ? 2 : 1;
  const at = n === 2 ? [R.sysStart, j.len - R.sysStart] : [j.len / 2];
  const dx = (j.q[0] - j.p[0]) / j.len, dy = (j.q[1] - j.p[1]) / j.len;
  for (const u of at) {
    const P = [j.p[0] + dx * u, j.p[1] + dy * u];
    for (const bd of [A, B]) {
      const [x, y] = bd.to(P);
      const ed = bd === A ? j.ea : j.eb;
      holesOf.get(bd.key).push({ x: r6(x), y: r6(y), d: hw.fastenDia, depth: hw.fastenDepth, kind: 'dowel', face: 'edge', edge: String(ed + 1), joint: j.a + '-' + j.b });
    }
  }
}

Edge holes kept on the blank

64-bit

Moves an edge hole back onto the blank when the cut part is a little smaller than the drawn board.

x = min(max(x, 0), part length); y = min(max(y, 0), part width)

A clamp to the cut part's length and width, then a rounding to 6 decimals. A hole that is moved no longer sits at sysStart from the joint's end.

  • addons/cabinetcraft/lib/cornerdrill.js
  • lines 153 to 154
  • CadCraft 0.8.2
// the blank can be a little narrower than the drawn board (an applied back): a hole on its edge stays on the blank
const holes = holesOf.get(bd.key).map((h) => (part && h.face === 'edge' ? { ...h, x: r6(Math.min(Math.max(h.x, 0), part.length)), y: r6(Math.min(Math.max(h.y, 0), part.width)) } : h));

In detail

Method
Hole rows on the system grid x = sysStart + k × pitch; hinge plates on the grid hole nearest the hinge; fixing holes at sysStart from each end of a joint line two boards share, or one at the middle when the joint is shorter than 2 × sysStart + pitch.
Number types
64-bit numbers. A hole size the person has not typed is kept as an empty string, not a number.
Tolerances
Edges shorter than 1e-6 are skipped; two boards share an edge when both end points lie within 1e-4 of the other edge's line; a shared length under 1 is ignored; the grid end test adds 1e-9; plate holes within 1e-6 of each other are merged. All are absolute, in job units (mm or inches).
Limits
Stored coordinates carry 6 decimals of the job unit, not the full 64-bit value.

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