Resources Numerical accuracy Calculation policy

Standard sections: member solids, mass and parts list

Builds a steel member as a solid from a standard section, shows a section's mass per meter, and totals the placed parts into a parts list.

How the precision is kept

Member geometry is 64-bit: the section's millimeters are divided by 1000 and extruded along the member. Mass per meter is the published figure when the table has one, else the section's area times a steel density of 7850 kg/m³. Masses shown on cards and in the parts list are rounded to two decimals.

Calculations under this policy

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

stdlib.js CadCraft 0.8.2 64-bit 4 calculations Reviewed

Tests bin/test-cadcraft.mjs

Mass per meter of a section

64-bit

The kilograms per meter shown for a steel section.

kg/m = published mass, else area (mm²) × 1e-6 × 7850

STEEL_DENSITY is 7850 kg/m³ in shapes/sections.js, a nominal value for carbon steel. The area comes from exactArea of the same file. The card shows the result rounded to two decimals.

  • stdlib.js
  • lines 40 to 44
  • CadCraft 0.8.2
const kgm = (s) => s.massPub ?? exactArea(s) * 1e-6 * STEEL_DENSITY;
const secLine = (s) => {
  const d = s.kind === 'I' || s.kind === 'C' ? r1(s.d) + ' × ' + r1(s.bf) : s.kind === 'L' ? r1(s.h ?? s.b) + ' × ' + r1(s.b) + ' × ' + r1(s.t)
    : s.kind === 'RHS' ? r1(s.h) + ' × ' + r1(s.b) + ' × ' + r1(s.t) : s.kind === 'CHS' ? r1(s.D) + ' × ' + r1(s.t) : '';
  return t('std.card.section', { dims: d, kg: Math.round(kgm(s) * 100) / 100 });

Member solid

64-bit

The faces of a straight member along two points, or of a column of a given height.

outline (mm) ÷ 1000; L = |b − a| × k; point = eₓ·u + e_y·(v + lift) + e_z·s

Section coordinates are divided by 1000, a correctly rounded division, and placed on unit axes from the member's direction (hypot). A beam is lifted so the lowest point of its section sits on the start level. A length of 1e-6 m or less is refused.

  • stdlib.js
  • lines 102 to 131
  • CadCraft 0.8.2
/** Faces (meters, relative to the member's start) of a straight member along a → b, bottom on z0; or a column. */
export function memberPolys(sec, a, b, z0, k) {
  const loops = memberLoops(sec).map((l) => l.map(([u, v]) => [u / 1000, v / 1000]));
  let ex, ey, ez, L;
  if (a) {
    const dx = (b.x - a.x) * k, dy = (b.y - a.y) * k;
    L = Math.hypot(dx, dy);
    if (!(L > 1e-6)) throw new Error(t('std.member.zero'));
    const dir = [dx / L, dy / L, 0];
    ex = [-dir[1], dir[0], 0]; ey = [0, 0, 1]; ez = dir;            // u across (to the left), v up, along the member
  } else {
    L = z0 * k;
    if (!(L > 1e-6)) throw new Error(t('std.member.zero'));
    ex = [1, 0, 0]; ey = [0, 1, 0]; ez = [0, 0, 1];
  }
  let vmin = Infinity;
  for (const [, v] of loops[0]) vmin = Math.min(vmin, v);
  const lift = a ? -vmin : 0;                                      // a beam sits on its start point's level
  const at = (u, v, s) => [ex[0] * u + ey[0] * (v + lift) + ez[0] * s, ex[1] * u + ey[1] * (v + lift) + ez[1] * s, ex[2] * u + ey[2] * (v + lift) + ez[2] * s];
  const polys = [];
  for (const loop of loops) {
    for (let i = 0; i < loop.length; i++) {
      const p = loop[i], q = loop[(i + 1) % loop.length];
      polys.push([at(p[0], p[1], 0), at(q[0], q[1], 0), at(q[0], q[1], L), at(p[0], p[1], L)]);
    }
  }
  polys.push({ loops: loops.map((l) => l.map(([u, v]) => at(u, v, L))) });
  polys.push({ loops: loops.map((l) => l.slice().reverse().map(([u, v]) => at(u, v, 0))) });
  return { polys, length: L };
}

Parts list quantities

64-bit

Groups placed parts and adds up how many there are of each.

qty = Σ CC-QTY (1 when absent)

Quantity and unit mass are read with parseFloat from the block reference's attribute text, so they carry whatever digits that text has. Quantities are added in drawing order.

  • stdlib.js
  • lines 144 to 162
  • CadCraft 0.8.2
/** Every placed part and section, grouped (references in the model and on sheets). */
export function partsList(doc) {
  const map = new Map();
  for (const e of doc.everyEntity()) {
    if (e.type !== 'insert') continue;
    const a = e.attrs || {};
    if (!a[PART_TAGS.type] && !a[PART_TAGS.size]) continue;
    const row = { type: a[PART_TAGS.type] || '', std: a[PART_TAGS.std] || '', size: a[PART_TAGS.size] || '', length: a[PART_TAGS.length] || '', material: a[PART_TAGS.material] || '' };
    const key = [row.type, row.std, row.size, row.length, row.material].join('\u0001');
    let r = map.get(key);
    if (!r) { r = { ...row, qty: 0, numbers: [], massEach: parseFloat(a['CC-WEIGHT-KG']) > 0 ? parseFloat(a['CC-WEIGHT-KG']) : null }; map.set(key, r); }
    r.qty += parseFloat(a['CC-QTY']) > 0 ? parseFloat(a['CC-QTY']) : 1;
    const n = String(a['CC-ITEM'] ?? '').trim();
    if (n && !r.numbers.includes(n)) r.numbers.push(n);
  }
  const out = [...map.values()];
  for (const r of out) r.numbers.sort((x, y) => x.localeCompare(y, undefined, { numeric: true }));
  return out.sort((x, y) => (x.numbers[0] || '~').localeCompare(y.numbers[0] || '~', undefined, { numeric: true }) || x.type.localeCompare(y.type) || x.size.localeCompare(y.size, undefined, { numeric: true }));
}

Total mass in the parts list

64-bit

The mass column of the parts list and its CSV.

mass = round(mass each × qty × 100) ÷ 100

The total is rounded to two decimals, 0.01 kg, before it is written to the table and the CSV.

  • stdlib.js
  • line 165
  • CadCraft 0.8.2
const cellsOf = (r) => [r.numbers.join(', '), String(r.qty), r.type, r.std, r.size, r.length, r.material, r.massEach ? String(Math.round(r.massEach * r.qty * 100) / 100) : ''];

In detail

Method
Section outline extruded along a unit direction; mass = area × density; quantities summed per part.
Number types
64-bit numbers; quantities and unit masses are read from attribute text.
Tolerances
A member shorter than 1e-6 m is refused.
Limits
7850 kg/m³ is a nominal density. The parts list writes total mass to 0.01 kg and takes each part's mass from the text of its weight attribute.

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