Places the bars in a pad footing and works out their bends, cut lengths, counts and weights for the bending schedule.
How the precision is kept
64-bit arithmetic on tabulated bar sizes. ASTM bar diameters and areas are converted from inches with the exact factors 25.4 and 645.16; the weight per length uses 1.48816394, a rounded factor for pounds per foot to kilograms per meter. Bars are grouped into marks by their sizes rounded to the millimeter, and the schedule prints lengths in whole millimeters.
Open item. Use the full factor 1.4881639435695537 for pounds per foot to kilograms per meter.
Calculations under this policy
13 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.
The diameter, mass per meter, area and bend diameter of bars #3 to #11.
d_b = in × 25.4; mass = lb/ft × 1.48816394; area = in² × 645.16; bend D = 6·d_b (to #8) or 8·d_b
25.4 and 645.16 are exact. 1.48816394 is rounded to nine figures; the exact factor is 0.45359237 ÷ 0.3048 = 1.4881639435…. The published inch values are kept beside the converted ones.
rebar.js
lines 45 to 49
DetailCraft 0.8.1
export const BARS = new Map(A615.map(([n, d, w, a]) => [n, Object.freeze({
name: n, num: Number(n.slice(1)), db: d * 25.4, mass: w * 1.48816394, area: a * 645.16,
bendD: (Number(n.slice(1)) <= 8 ? 6 : 8) * d * 25.4, // ACI 318-19 Table 25.3.1
pub: { d, w, a }, set: 'astm', markPre: n.slice(1), metric: '#' + Math.round(d * 25.4),
})]));
Metric bar area and mass
64-bit
The section area and mass per meter of bars H8 to H40.
area = π·d² ÷ 4; mass = area × 7850 × 10⁻⁶
Math.PI and the density 7850 kg/m³ written in the file. The bend diameter is twice the tabulated radius.
rebar.js
lines 54 to 55
DetailCraft 0.8.1
const area = Math.PI * d * d / 4;
BARS.set('H' + d, Object.freeze({ name: 'H' + d, num: d, db: d, mass: area * 7850 * 1e-6, area, bendD: 2 * r, r, P, set: 'bs', markPre: 'H' + d, metric: 'H' + d }));
Number of bars and their spacing
64-bit
How many bars go across a width at no more than a given spacing, and the spacing that results.
n = max(2, ⌈w ÷ s − 1e-9⌉ + 1); spacing = w ÷ (n − 1)
The 1e-9 keeps a width that is an exact multiple of the spacing from gaining a bar. The spacing is one division; n is at least 2, so it is never by zero.
rebar.js
lines 135 to 139
DetailCraft 0.8.1
/** n bars evenly over a width w (bar centers), spacing no more than s. */
export function layout(w, s) {
const n = Math.max(2, Math.ceil(w / s - 1e-9) + 1);
return { n, spacing: w / (n - 1) };
}
Hook leg length
64-bit
The out-to-out length of a standard 90 degree hook leg.
ASTM: D ÷ 2 + d_b + 12·d_b; BS: the tabulated end projection P
A sum of multiples of the bar diameter, or a table value returned as it is.
rebar.js
lines 140 to 141
DetailCraft 0.8.1
/** Out-to-out length of a standard 90° hook leg: ACI 318-19 Table 25.3.1 D/2 + db + 12·db; BS 8666 the end projection P. */
export function hookLeg(b) { return b.set === 'bs' ? b.P : b.bendD / 2 + b.db + 12 * b.db; }
Cut length of a bent bar
64-bit
The length of bar to cut for a straight, L or U shape given its out-to-out sizes.
ASTM L: A + B − 2k + (π ÷ 2)·r_c; U: A + B + C − 4k + π·r_c; k = D ÷ 2 + d_b, r_c = D ÷ 2 + d_b ÷ 2; BS L: A + B − 0.5r − d; U: A + B + C − r − 2d
ASTM bars use the exact center line with Math.PI. BS bars use the BS 8666 formulas named in the file, which are the standard's own approximations.
rebar.js
lines 143 to 154
DetailCraft 0.8.1
/** Centerline cut length of a shape with out-to-out dims. */
export function cutLength(shape, dims, b) {
const k = b.bendD / 2 + b.db, rc = b.bendD / 2 + b.db / 2;
if (shape === 'S') return dims.A;
if (b.set === 'bs') { // BS 8666:2020 shape codes 11 and 21
if (shape === 'L') return dims.A + dims.B - 0.5 * b.r - b.db;
if (shape === 'U') return dims.A + dims.B + dims.C - b.r - 2 * b.db;
}
if (shape === 'L') return dims.A + dims.B - 2 * k + Math.PI / 2 * rc;
if (shape === 'U') return dims.A + dims.B + dims.C - 4 * k + Math.PI * rc;
throw new Error(t('rebar.err.shape', { shape }));
}
Center line of a bar
64-bit
The center line of a bar in its own plane as straight pieces and quarter-circle arcs.
arc radius r_c = D ÷ 2 + d_b ÷ 2; points c + r_c·(cos a, sin a)
Arc end points come from Math.cos and Math.sin of multiples of π ÷ 2, so they are off the exact corner values by rounding in the last digits.
rebar.js
lines 156 to 177
DetailCraft 0.8.1
/**
* Centerline of a bar in its own plane: s along A, t toward the bends' side. Returns
* [{ k:'L', a:[s,t], b:[s,t] } | { k:'A', a, m, b, c:[s,t], r }] from one end to the other.
* U: legs up at both ends; L: the hook leg at the s = A end (main leg along s).
*/
export function centerline2d(shape, dims, b) {
const h = b.db / 2, rc = b.bendD / 2 + h, A = dims.A;
const arc = (c, a0, a1) => ({ k: 'A', c, r: rc, a: [c[0] + rc * Math.cos(a0), c[1] + rc * Math.sin(a0)], m: [c[0] + rc * Math.cos((a0 + a1) / 2), c[1] + rc * Math.sin((a0 + a1) / 2)], b: [c[0] + rc * Math.cos(a1), c[1] + rc * Math.sin(a1)] });
if (shape === 'S') return [{ k: 'L', a: [0, h], b: [A, h] }];
const segs = [];
if (shape === 'U') {
const c1 = [h + rc, h + rc];
segs.push({ k: 'L', a: [h, dims.B - 0], b: [h, h + rc] });
segs.push(arc(c1, Math.PI, 1.5 * Math.PI));
segs.push({ k: 'L', a: [h + rc, h], b: [A - h - rc, h] });
} else segs.push({ k: 'L', a: [0, h], b: [A - h - rc, h] });
const c2 = [A - h - rc, h + rc];
segs.push(arc(c2, 1.5 * Math.PI, 2 * Math.PI));
const legEnd = shape === 'U' ? dims.C : dims.B;
segs.push({ k: 'L', a: [A - h, h + rc], b: [A - h, legEnd] });
return segs;
}
Length of a center line
64-bit
The length along a bar's center line.
Σ straight lengths + Σ r·π ÷ 2
Straight parts by Math.hypot, each arc as an exact quarter circle. A running sum over a handful of pieces.
rebar.js
lines 178 to 182
DetailCraft 0.8.1
export function centerlineLength(segs) {
let s = 0;
for (const g of segs) s += g.k === 'L' ? Math.hypot(g.b[0] - g.a[0], g.b[1] - g.a[1]) : g.r * Math.PI / 2;
return s;
}
Bottom mat bars
64-bit
The size, shape, count and position of the bottom bars each way.
A = run − 2·cover; hook B = min(hook leg, room); offset_i = −width ÷ 2 + cover + d_b ÷ 2 + i·spacing
Bar positions are the first offset plus i times the spacing, not a running sum, so the last bar does not carry accumulated rounding.
rebar.js
lines 194 to 210
DetailCraft 0.8.1
// bottom layer: bars along local X (outermost), then bars along local Y on top of them
const layer = (b, along, across, runLen, acrossLen, s, zBottom, role) => {
const A = runLen - 2 * c;
const hooks = f.mat.hooks;
const room = f.H - 2 * c - (zBottom - (bottom + c));
const B = hooks ? Math.min(hookLeg(b), room) : 0;
const shape = hooks ? 'U' : 'S';
const dims = hooks ? { A, B, C: B } : { A };
const lay = layout(acrossLen - 2 * c - b.db, s);
const sets = [];
for (let i = 0; i < lay.n; i++) {
const off = -acrossLen / 2 + c + b.db / 2 + i * lay.spacing;
const o = along === 'x' ? P(-runLen / 2 + c, off, zBottom) : P(off, -runLen / 2 + c, zBottom);
sets.push({ origin: o, sDir: along === 'x' ? ex : ey, tDir: ez });
}
out.push({ role, bar: b.name, shape, dims, count: lay.n, spacing: lay.spacing, length: cutLength(shape, dims, b), sets });
};
The grid of starters is centered by construction. The hook turns toward the side the bar is farther out on; a bar within 1e-9 mm of the center turns along the footing's X.
rebar.js
lines 215 to 228
DetailCraft 0.8.1
const z0 = bottom + c + bx.db + by.db; // sitting on the mat
const A = f.top + s.projection - z0; // out-to-out, vertical
const shape = s.hook ? 'L' : 'S';
const dims = s.hook ? { A, B: hookLeg(b) } : { A };
const sets = [];
for (let i = 0; i < s.nx; i++) for (let j = 0; j < s.ny; j++) {
const lx = (i - (s.nx - 1) / 2) * s.sx, ly = (j - (s.ny - 1) / 2) * s.sy;
// hook points outward, along local X (or Y when the bar is further out that way)
let hd = Math.abs(lx) >= Math.abs(ly) ? (lx >= 0 ? ex : ex.map((v) => -v)) : (ly >= 0 ? ey : ey.map((v) => -v));
if (Math.abs(lx) < 1e-9 && Math.abs(ly) < 1e-9) hd = ex;
// L shape: main leg along s (here: down from the top), hook at s = A turning toward t = hook direction.
// The bar's centerline is drawn from its top (s = 0) down to the bend; the bar axis sits at (lx, ly).
const top = P(lx, ly, z0 + A);
sets.push({ origin: [top[0] - hd[0] * b.db / 2, top[1] - hd[1] * b.db / 2, top[2]], sDir: [0, 0, -1], tDir: hd });
Clear spacing check
64-bit
Warns when bars are closer than the minimum clear spacing.
clear = spacing − d_b ≥ max(25.4, d_b) for ASTM bars, max(20, d_b) for BS bars
25.4 mm is exactly 1 inch. The comparison allows 1e-6 mm.
Groups identical bars under one mark and totals their length and weight.
total length = length × count; weight each = mass × length ÷ 1000; total weight = weight each × count
Bars are grouped by size, shape and A, B, C rounded to the millimeter with Math.round. The row keeps the sizes and the length of the first bar that opened it, so a later bar that differs by less than the rounding is counted at the first one's length.
rebar.js
lines 255 to 290
DetailCraft 0.8.1
/**
* Bar marks across all footings: identical bars (size, shape, dims to the mm) share a mark "<size>F<n>".
* Returns { rows (bending schedule), byFooting: Map(footingId → [{ group, mark }]) }.
*/
export function bendingSchedule(footings) {
const marks = new Map();
const rows = [];
const byFooting = new Map();
const counters = new Map();
for (const f of footings) {
const list = [];
for (const g of footingBars(f)) {
const b = BARS.get(g.bar);
const key = [g.bar, g.shape, ...['A', 'B', 'C'].map((k) => g.dims[k] === undefined ? '' : Math.round(g.dims[k]))].join('|');
let row = marks.get(key);
if (!row) {
const n = (counters.get(b.markPre) || 0) + 1;
counters.set(b.markPre, n);
row = { mark: b.markPre + 'F' + n, bar: g.bar, shape: g.shape, dims: { ...g.dims }, count: 0, length: g.length, mass: b.mass, roles: new Set() };
marks.set(key, row);
rows.push(row);
}
row.count += g.count;
row.roles.add(g.role);
list.push({ group: g, mark: row.mark });
}
byFooting.set(f.id, list);
}
for (const r of rows) {
r.totalLength = r.length * r.count;
r.unitWeight = r.mass * r.length / 1000;
r.totalWeight = r.unitWeight * r.count;
r.roles = [...r.roles];
}
return { rows, byFooting };
}
The schedule as a table
64-bit
The figures printed in the bending schedule and its export.
sizes and cut length: whole mm; total length: m, 2 decimals; kg/m: 3 decimals; weights: 2 and 1 decimals
The total weight is summed from the unrounded rows and rounded once, so it can differ in the last digit from the sum of the rounded rows above it.
ASTM A615 Table 1 sizes, hooks to ACI 318-19 Table 25.3.1, BS 8666:2020 for metric bars, all as cited in the file. Cut length is the center line: straight parts plus arcs.
Number types
64-bit numbers; schedule text rounded.
Tolerances
Bar count: Math.ceil(w ÷ s − 1e-9). Clear spacing and hook checks: 1e-6 mm. Starter direction: 1e-9 mm.
Limits
The pounds-per-foot factor is low by about 2.4 parts in a billion. Metric bar mass uses 7850 kg/m³ on the nominal area. Bars whose sizes round to the same millimeter share one mark and the first bar's length.
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.