Resources Numerical accuracy Calculation policy

Figures shown in the residential panels and schedules

Works out the numbers the residential tools show in Properties and on schedules: a roof plane's area, the cut list, board feet, cut and fill, a new stair's direction and a building's width on an elevation.

How the precision is kept

These are short 64-bit expressions on stored model values, formatted for display at the end. Board feet and volumes are shown with one decimal or none; the values behind them are not changed by the display.

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.

res-ui.js BimCraft 0.8.1 64-bit 7 calculations Reviewed

Tests None found by the review

Roof plane area in Properties

64-bit

The sloped area of a manual roof plane shown in its properties.

area = |½ Σ (xᵢ yᵢ₊₁ − yᵢ xᵢ₊₁)| ÷ cos θ

The shoelace sum on the plane's stored points, divided by the cosine of the pitch, then formatted. The points were stored with 3 decimals of the file's length unit.

  • res-ui.js
  • lines 260 to 261
  • BimCraft 0.8.1
let a = 0; for (let i = 0; i < P.length; i++) { const p = P[i], q = P[(i + 1) % P.length]; a += p[0] * q[1] - p[1] * q[0]; }
kit.rows.push(el('dt', {}, t('res.rp.area')), el('dd', {}, fmtArea(Math.abs(a / 2) / Math.cos(rp.pitch * D2R), A.units())));

Cut list schedule

64-bit

The lumber schedule's rows: size, stock length, pieces and board feet, with totals.

board feet shown = bf.toFixed(1)

Rows and totals come from lumberList. Board feet are shown with one decimal, each row and the total rounded on its own, so the shown rows need not add up to the shown total.

  • res-ui.js
  • lines 648 to 661
  • BimCraft 0.8.1
// the cut list as a schedule (model-ui.js Schedules panel): size, stock length, pieces, board feet
function lumberSchedule() {
  const w = writer();
  if (!w) return null;
  const mm = isMetric(A.units()), lst = lumberList(w, { metric: mm });
  if (!lst.rows.length) return { head: [t('res.fr.sch.size'), t('res.fr.sch.length'), t('res.fr.sch.count'), t('res.fr.sch.bf')], rows: [], nums: [2, 3], total: null };
  const tj = R.frames.find((f) => f.kind === 'floor' && f.floorMode === 'tji' && f.tji?.Assumptions);   // GH.3-E: the assumptions go on the schedule
  return {
    ...(tj ? { note: String(tj.tji.Assumptions).replace(/\n/g, ' ') } : {}),
    head: [t('res.fr.sch.size'), t('res.fr.sch.length'), t('res.fr.sch.count'), t('res.fr.sch.bf')],
    rows: lst.rows.map((r) => [r.label, L(r.stock), String(r.count), r.bf.toFixed(1)]),
    nums: [2, 3], total: [t('model.sch.total'), '', String(lst.pieces), lst.bf.toFixed(1)],
  };
}

Board feet of a framing in Properties

64-bit

Shows the piece count and board feet of one framing.

shown = Math.round(BoardFeet)

The stored total, already rounded to 2 decimals, is shown as a whole number.

  • res-ui.js
  • line 579
  • BimCraft 0.8.1
kit.rows.push(el('dt', {}, t('res.fr.pieces')), el('dd', { 'data-mp-frpieces': '' }, String(q.Pieces ?? fr.parts.length)), el('dt', {}, t('res.fr.bf')), el('dd', {}, String(Math.round(Number(q.BoardFeet) || 0))));

Cut and fill in Properties

64-bit

Shows the terrain's cut and fill volumes.

shown = V.toFixed(1) m³

The stored volumes, rounded to 3 decimals of a cubic meter, are shown with one decimal.

  • res-ui.js
  • line 837
  • BimCraft 0.8.1
kit.rows.push(el('dt', {}, t('res.tr.cut')), el('dd', { 'data-mp-trcut': '' }, (trn.cut || 0).toFixed(1) + ' m³'), el('dt', {}, t('res.tr.fill')), el('dd', {}, (trn.fill || 0).toFixed(1) + ' m³'));   // i18n-ignore: unit

Direction and height of a new stair

64-bit

The direction a new stair runs, from its start point to the pointer, and the height it climbs.

angle = atan2(Δy, Δx) × 180 ÷ π; height = elevation above − elevation here

The angle is Math.atan2 converted to degrees and later stored with 6 decimals. A second point closer than 50 mm to the start gives no stair.

  • res-ui.js
  • lines 358 to 367
  • BimCraft 0.8.1
function stairSpecAt(p, e) {
  const st = R.stair;
  if (!st.start) return null;
  const q = snap(p, e, [st.start]).p;
  const dx = q[0] - st.start[0], dy = q[1] - st.start[1];
  if (Math.hypot(dx, dy) < 50) return null;
  const lv = A.level(), above = A.levelAbove();
  const height = above ? above.elevation - lv.elevation : lv.height;
  return { kind: st.kind, turn: st.turn, width: st.width, railings: st.railings, start: st.start, angle: Math.atan2(dy, dx) * 180 / Math.PI, height, ...packStair(st.kind) };
}

Building width on an elevation (fallback)

64-bit

The overall width of the building across a view direction, used for the width note on an elevation when the wall-based width is not available.

x = q · r ± t ÷ 2 over every wall end; width from min to max

Half the wall thickness is added on both sides of every wall end whatever the wall's direction. That is exact for walls square to the view and overstates the width for a wall that runs across the view, by up to half its thickness at each end.

  • res-ui.js
  • line 1105
  • BimCraft 0.8.1
const widthOn = (dir) => { const r = [dir[1], -dir[0]], xs = []; for (const x of A.S.data.walls || []) for (const q of [x.a, x.b]) xs.push(q[0] * r[0] + q[1] * r[1] - x.t / 2, q[0] * r[0] + q[1] * r[1] + x.t / 2); /* the outside faces (square walls exactly) */ return xs.length ? { x0: Math.min(...xs), x1: Math.max(...xs) } : null; };

Inverse of a level's placement

64-bit

Inverts a level's 4×4 world matrix to bring site coordinates back into the level's plan.

R⁻¹ = Rᵀ; t⁻¹ = −Rᵀ t

Valid for rotation and translation only: the rotation is transposed, not inverted, so a matrix that also scales would not be inverted correctly. No test of the matrix is made.

  • res-ui.js
  • lines 73 to 79
  • BimCraft 0.8.1
function inv4m(m) {
  // rigid transforms only (rotation + translation): the inverse is the transpose of the rotation and the moved origin
  const r = [m[0], m[4], m[8], 0, m[1], m[5], m[9], 0, m[2], m[6], m[10], 0, 0, 0, 0, 1];
  const t = apply4m(r, m[12], m[13], m[14]);
  r[12] = -t[0]; r[13] = -t[1]; r[14] = -t[2];
  return r;
}

In detail

Method
Shoelace area divided by cos(pitch); totals from lumberList; a rigid 4×4 inverse by transposing the rotation.
Number types
64-bit numbers.
Limits
The fallback building width adds half a wall thickness whatever the wall's direction, which is exact only for walls square to the view. The 4×4 inverse assumes rotation and translation only.

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.

Every policy that takes this approach