Resources Numerical accuracy Calculation policy

Circuit board outline, holes and part boxes

Reads a KiCad board file into a part: the board outline, its drilled holes and one box per component.

How the precision is kept

Coordinates are parsed from the file's text as 64-bit numbers and Y is negated once. Arcs and circles of the outline are replaced by straight segments of at most 7.5°, so a round board edge is a polygon. Component heights are guesses by reference letter, as the file's header says, and the board thickness parameter is written with 4 decimals.

Calculations under this policy

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

kicad.js PartCraft 0.8.1 64-bit 5 calculations Reviewed

Tests tools/partcraft-test/test-kicad.mjs

Arc through three points

64-bit

Center, radius, start angle and signed sweep of an outline arc given by its start, middle and end.

c_x = (|a|²(m_y − b_y) + |m|²(b_y − a_y) + |b|²(a_y − m_y)) ÷ d, d = 2 (a_x(m_y − b_y) + m_x(b_y − a_y) + b_x(a_y − m_y))

The formula uses the squared distances of the three points from the file's origin, so an arc far from the origin and small in radius subtracts large nearly equal terms. The collinear test |d| < 1e-12 is absolute and does not scale with the board's size.

  • kicad.js
  • lines 67 to 80
  • PartCraft 0.8.1
/** The arc through three points: [centre, radius, start angle, signed sweep] or null for collinear points. */
function threePoint(a, m, b) {
  const d = 2 * (a[0] * (m[1] - b[1]) + m[0] * (b[1] - a[1]) + b[0] * (a[1] - m[1]));
  if (Math.abs(d) < 1e-12) return null;
  const f = (p) => p[0] * p[0] + p[1] * p[1];
  const cx = (f(a) * (m[1] - b[1]) + f(m) * (b[1] - a[1]) + f(b) * (a[1] - m[1])) / d;
  const cy = (f(a) * (b[0] - m[0]) + f(m) * (a[0] - b[0]) + f(b) * (m[0] - a[0])) / d;
  const c = [cx, cy], r = Math.hypot(a[0] - cx, a[1] - cy);
  const ang = (p) => Math.atan2(p[1] - cy, p[0] - cx);
  const a0 = ang(a), am = ang(m), a1 = ang(b);
  const wrap = (v) => ((v % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI);
  const ccw = wrap(a1 - a0), mid = wrap(am - a0);
  return [c, r, a0, mid <= ccw ? ccw : ccw - 2 * Math.PI];
}

Arc as straight segments

64-bit

The points that replace an arc or circle of the board outline.

steps = max(2, ⌈|sweep| ÷ 7.5°⌉); pₖ = c + r (cos aₖ, sin aₖ), aₖ = a₀ + sweep × k ÷ steps

Each point's angle is computed from the step index, not accumulated. The outline that reaches the sketch is this polygon, whose corners lie on the arc and whose edges lie inside it.

  • kicad.js
  • lines 63 to 66
  • PartCraft 0.8.1
function arcPoints(c, r, a0, sweep) {
  const steps = Math.max(2, Math.ceil(Math.abs(sweep) / rad(7.5)));
  return Array.from({ length: steps + 1 }, (_, k) => { const a = a0 + sweep * k / steps; return [c[0] + r * Math.cos(a), c[1] + r * Math.sin(a)]; });
}

Joining outline pieces into loops

64-bit

Chains lines and arcs of the edge layer into closed outlines.

Two ends join when they are closer than 1e-3, an absolute distance. Of the two nearly equal points one is dropped, so the segment that led to it ends at the kept point and moves by up to that distance.

  • kicad.js
  • lines 82 to 104
  • PartCraft 0.8.1
/** Closed loops from loose pieces (each a list of points), joined where ends meet within 1e-3 mm. */
function chain(pieces, notes) {
  const left = pieces.map((p) => p.slice());
  const near = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]) < 1e-3;
  const loops = [];
  while (left.length) {
    const cur = left.shift();
    let grew = true;
    while (grew && !near(cur[0], cur[cur.length - 1])) {
      grew = false;
      for (let k = 0; k < left.length; k++) {
        const p = left[k], end = cur[cur.length - 1];
        if (near(p[0], end)) cur.push(...p.slice(1));
        else if (near(p[p.length - 1], end)) cur.push(...p.slice(0, -1).reverse());
        else continue;
        left.splice(k, 1); grew = true; break;
      }
    }
    if (near(cur[0], cur[cur.length - 1]) && cur.length > 3) loops.push(cur.slice(0, -1));
    else notes.push('An outline piece does not close (' + cur.length + ' points); it was left out.');
  }
  return loops;
}

Footprint point on the board

64-bit

Moves a point of a footprint (a pad, a corner of its box) to board coordinates.

x = at_x + l_x cos a − l_y sin a; y = −at_y + l_x sin a + l_y cos a; l_y = −p_y, a = rotation × π ÷ 180

The rotation uses Math.cos and Math.sin of the angle in radians, so a quarter turn is not exact: cos(90°) evaluates to about 6e-17 instead of 0. The effect on a coordinate is that factor times the point's local offset.

  • kicad.js
  • lines 106 to 110
  • PartCraft 0.8.1
/** Local point → board point (Y flipped once; angle counter-clockwise after the flip). */
const place = (at, rot, p) => {
  const lx = p[0], ly = -p[1], a = rad(rot);
  return [at[0] + lx * Math.cos(a) - ly * Math.sin(a), -at[1] + lx * Math.sin(a) + ly * Math.cos(a)];
};

Board thickness parameter

64-bit

The part parameter that drives the board's extrusion depth.

board_t = thickness rounded to 4 decimals

The parameter's expression is the thickness after toFixed(4), and the extrusion depth is linked to that parameter, so the built board uses the rounded value.

  • kicad.js
  • line 199
  • PartCraft 0.8.1
const vars = [{ name: 'board_t', expr: String(+board.thickness.toFixed(4)) }];

In detail

Method
Circle through three points for arcs; polyline sampling; chaining of outline pieces by end distance; rotation of footprint points by the footprint angle.
Number types
64-bit numbers from parseFloat.
Tolerances
Outline pieces join when their ends are within 1e-3 (model units, absolute). Three arc points count as collinear when the doubled signed area is under 1e-12 (absolute). A slot is recognized when its two drill sizes differ by more than 1e-6.
Limits
Arc steps of at most 7.5° (at least 2 per arc). A slot is cut as a round hole of its smaller width. The file's header states that its conventions are unverified against real boards.

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