Resources Numerical accuracy Calculation policy

Steel section dimensions, areas and outlines

Gives the dimensions, published area and mass of every steel section in millimeters, the exact area from the dimensions, and the outline used to draw and sweep it.

How the precision is kept

Published inch values are multiplied by the exact 25.4 in 64-bit when the table is built, and nothing is rounded. The exact area uses closed forms with the fillet and corner arcs included; the drawn outline replaces each arc by a fixed number of straight segments. The lb/ft to kg/m factor is a nine-digit constant.

Open item. Write the lb/ft factor as 0.45359237 ÷ 0.3048 so it carries all 64-bit digits instead of nine.

Calculations under this policy

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

shapes/sections.js CadCraft 0.8.2 64-bit 6 calculations Reviewed

Tests bin/test-shapes.mjs, bin/test-cadcraft.mjs, bin/test-detailcraft.mjs, bin/test-frame.mjs, bin/test-frame-design.mjs

Table values in millimeters

64-bit

Converts each published row to millimeters, mm² and kg/m.

mm = in × 25.4; mm² = in² × 25.4 × 25.4; kg/m = lb/ft × 1.48816394; r = (k_des − t_f) × 25.4

Lengths use the exact 25.4. The mass factor 1.48816394 is 0.45359237 ÷ 0.3048 rounded to nine significant digits (the full value is 1.4881639435695…). The root radius of W shapes is a difference of two published values. EN areas are cm² × 100.

  • shapes/sections.js
  • lines 1446 to 1492
  • CadCraft 0.8.2
function build() {
  const out = [];
  const add = (p) => { if (p.pub) freeze(p.pub); out.push(freeze(p)); };
  const rev = (t) => t.slice().reverse();   // the database lists the largest first; pickers read better smallest first
  // the order DetailCraft has always listed (W, C, L, HSS, IPE/HEA/HEB), then the families added with this table
  for (const [n, d, bf, tw, tf, k, A, W] of rev(AISC_W)) {
    add({ name: displayName(n), family: 'W', kind: 'I', source: 'aisc', d: d * IN, bf: bf * IN, tw: tw * IN, tf: tf * IN, r: (k - tf) * IN,
      areaPub: A * IN * IN, massPub: W * LBFT, pub: { units: 'in', d, bf, tw, tf, kdes: k, A, W } });
  }
  for (const [n, d, bf, tw, tf, A, W] of rev(AISC_C)) {
    add({ name: displayName(n), family: 'C', kind: 'C', source: 'aisc', d: d * IN, bf: bf * IN, tw: tw * IN, tf: tf * IN, r: 0,
      areaPub: A * IN * IN, massPub: W * LBFT, pub: { units: 'in', d, bf, tw, tf, A, W } });
  }
  for (const [n, b, t, A, W] of rev(AISC_L)) {
    add({ name: displayName(n), family: 'L', kind: 'L', source: 'aisc', b: b * IN, t: t * IN,
      areaPub: A * IN * IN, massPub: W * LBFT, pub: { units: 'in', b, t, A, W } });
  }
  for (const [n, H, B, tn, t, A, W] of rev(AISC_RHS)) {
    add({ name: displayName(n), family: 'HSS', kind: 'RHS', source: 'aisc', h: H * IN, b: B * IN, t: t * IN, tNom: tn * IN, ro: 2 * t * IN, ri: t * IN,
      areaPub: A * IN * IN, massPub: W * LBFT, pub: { units: 'in', H, B, tNom: tn, t, A, W } });
  }
  for (const [n, D, tn, t, A, W] of rev(AISC_CHS)) {
    add({ name: displayName(n), family: 'HSS', kind: 'CHS', source: 'aisc', D: D * IN, t: t * IN, tNom: tn * IN,
      areaPub: A * IN * IN, massPub: W * LBFT, pub: { units: 'in', D, tNom: tn, t, A, W } });
  }
  for (const [n, h, b, tw, tf, r, A, G] of EN_I) {
    add({ name: n, family: n.slice(0, 3), kind: 'I', source: 'en', d: h, bf: b, tw, tf, r,
      areaPub: A * 100, massPub: G, pub: { units: 'mm', h, b, tw, tf, r, A, G } });
  }
  for (const [n, d, bf, tw, tf, k, A, W] of rev(AISC_S)) {
    add({ name: displayName(n), family: 'S', kind: 'I', source: 'aisc', d: d * IN, bf: bf * IN, tw: tw * IN, tf: tf * IN, r: 0,
      areaPub: A * IN * IN, massPub: W * LBFT, pub: { units: 'in', d, bf, tw, tf, kdes: k, A, W } });
  }
  for (const [n, long, short, t, A, W] of rev(AISC_L2)) {
    add({ name: displayName(n), family: 'L', kind: 'L', source: 'aisc', b: short * IN, h: long * IN, t: t * IN,
      areaPub: A * IN * IN, massPub: W * LBFT, pub: { units: 'in', long, short, t, A, W } });
  }
  for (const [n, D, tn, t, A, W] of AISC_PIPE.slice().sort((x, y) => x[1] - y[1] || x[2] - y[2])) {   // by OD, then wall
    add({ name: n, family: 'Pipe', kind: 'CHS', source: 'aisc', D: D * IN, t: t * IN, tNom: tn * IN,
      areaPub: A * IN * IN, massPub: W * LBFT, pub: { units: 'in', D, tNom: tn, t, A, W } });
  }
  for (const [n, h, b, tw, tf, r1, r2, A, G] of EN_UPN) {
    add({ name: n, family: 'UPN', kind: 'C', source: 'en', d: h, bf: b, tw, tf, r: 0,
      areaPub: A * 100, massPub: G, pub: { units: 'mm', h, b, tw, tf, r1, r2, A, G } });
  }
  return out;
}

Exact section area

64-bit

The cross-section area worked out from the dimensions, with fillets and rounded corners included.

I: 2·b_f·t_f + (d − 2t_f)·t_w + (4 − π)·r²; RHS: 2t(h + b − 2t) − (4 − π)(r_o² − r_i²); CHS: π(D − t)·t

One closed-form 64-bit expression per section kind; the arcs enter through (4 − π)·r², not through a polygon. The round tube form π(D − t)·t avoids subtracting two large circle areas.

  • shapes/sections.js
  • lines 1508 to 1519
  • CadCraft 0.8.2
/** Exact section area (mm²) from the dimensions (arcs included exactly). */
export function exactArea(p) {
  switch (p.kind) {
    case 'I': return 2 * p.bf * p.tf + (p.d - 2 * p.tf) * p.tw + (4 - Math.PI) * p.r * p.r;
    case 'C': return 2 * p.bf * p.tf + (p.d - 2 * p.tf) * p.tw;
    case 'L': return p.t * (p.b + (p.h ?? p.b) - p.t);
    case 'RHS': return 2 * p.t * (p.h + p.b - 2 * p.t) - (4 - Math.PI) * (p.ro * p.ro - p.ri * p.ri);
    case 'CHS': return Math.PI * (p.D - p.t) * p.t;
    case 'PL': return p.t * p.b;
    default: return 0;
  }
}

Section outline

64-bit

The outline of a section as loops of points, centered on its bounding box.

arc point = (cx + r·cos a, cy + r·sin a), a = a₀ + (a₁ − a₀)·i ÷ n

Corner points are sums and differences of half dimensions. Each arc point comes from its own angle, so nothing accumulates, but an arc is drawn with `seg` straight segments (6 by default) and a round tube with 8 × seg. The outline is therefore a polygon whose vertices lie on the true curve and whose edges are chords of it.

  • shapes/sections.js
  • lines 1521 to 1562
  • CadCraft 0.8.2
/**
 * Section outline as loops of [u, v] (first loop outer CCW, others holes CW), centered on the bounding box.
 * Plates: width along u, thickness along v. Angles: leg b along +u and leg h (= b when equal) along +v from the heel
 * (before centering).
 */
export function outline(p, seg = 6) {
  const arc = (pts, cx, cy, r, a0, a1, n = seg) => { for (let i = 1; i < n; i++) { const a = a0 + (a1 - a0) * i / n; pts.push([cx + r * Math.cos(a), cy + r * Math.sin(a)]); } };
  const H = Math.PI / 2;
  if (p.kind === 'I') {
    const { d, bf, tw, tf, r } = p, a = bf / 2, w = tw / 2, h = d / 2;
    const P = [[-a, -h], [a, -h], [a, -h + tf]];
    if (r > 0) { P.push([w + r, -h + tf]); arc(P, w + r, -h + tf + r, r, -H, -2 * H); P.push([w, -h + tf + r]); P.push([w, h - tf - r]); arc(P, w + r, h - tf - r, r, 2 * H, H); P.push([w + r, h - tf]); }
    else P.push([w, -h + tf], [w, h - tf]);
    P.push([a, h - tf], [a, h], [-a, h], [-a, h - tf]);
    if (r > 0) { P.push([-w - r, h - tf]); arc(P, -w - r, h - tf - r, r, H, 0); P.push([-w, h - tf - r]); P.push([-w, -h + tf + r]); arc(P, -w - r, -h + tf + r, r, 0, -H); P.push([-w - r, -h + tf]); }
    else P.push([-w, h - tf], [-w, -h + tf]);
    P.push([-a, -h + tf]);
    return [P];
  }
  if (p.kind === 'C') {   // web on the −u side (as IfcUShapeProfileDef), flanges toward +u
    const { d, bf, tw, tf } = p, a = bf / 2, h = d / 2;
    return [[[-a, -h], [a, -h], [a, -h + tf], [-a + tw, -h + tf], [-a + tw, h - tf], [a, h - tf], [a, h], [-a, h]]];
  }
  if (p.kind === 'L') {   // heel at (−b/2, −h/2): one leg along +u (horizontal), one along +v
    const { b, t } = p, a = b / 2, c = (p.h ?? b) / 2;
    return [[[-a, -c], [a, -c], [a, -c + t], [-a + t, -c + t], [-a + t, c], [-a, c]]];
  }
  if (p.kind === 'RHS') {
    const { h, b, t, ro, ri } = p;
    return [roundedRect(b, h, ro, seg, false), roundedRect(b - 2 * t, h - 2 * t, ri, seg, true)];
  }
  if (p.kind === 'CHS') {
    const n = seg * 8, R = p.D / 2, r = R - p.t, o = [], i = [];
    for (let k = 0; k < n; k++) { const a = 2 * Math.PI * k / n; o.push([R * Math.cos(a), R * Math.sin(a)]); i.push([r * Math.cos(-a), r * Math.sin(-a)]); }
    return [o, i];
  }
  if (p.kind === 'PL') {
    const a = p.b / 2, c = p.t / 2;
    return [[[-a, -c], [a, -c], [a, c], [-a, c]]];
  }
  return [[]];
}

Rounded rectangle loop

64-bit

The outer or inner loop of a rectangular hollow section with rounded corners.

Four quarter arcs of seg + 1 points each, computed by angle; the straight sides are the gaps between them. The inner loop is the same points reversed.

  • shapes/sections.js
  • lines 1564 to 1569
  • CadCraft 0.8.2
function roundedRect(w, h, r, seg, cw) {
  const a = w / 2, b = h / 2, pts = [];
  const corners = [[a - r, -b + r, -Math.PI / 2], [a - r, b - r, 0], [-a + r, b - r, Math.PI / 2], [-a + r, -b + r, Math.PI]];
  for (const [cx, cy, a0] of corners) for (let i = 0; i <= seg; i++) { const t = a0 + (Math.PI / 2) * i / seg; pts.push([cx + r * Math.cos(t), cy + r * Math.sin(t)]); }
  return cw ? pts.reverse() : pts;
}

Loop area

64-bit

The signed area of an outline loop.

A = ½ × Σ (xᵢ·yᵢ₊₁ − xᵢ₊₁·yᵢ)

Cross products added in point order. The outlines are centered on their bounding box, so the coordinates are small and little cancels.

  • shapes/sections.js
  • lines 1576 to 1580
  • CadCraft 0.8.2
export function shoelace(loop) {
  let s = 0;
  for (let i = 0; i < loop.length; i++) { const [x1, y1] = loop[i], [x2, y2] = loop[(i + 1) % loop.length]; s += x1 * y2 - x2 * y1; }
  return s / 2;
}

Loop perimeter

64-bit

The length around an outline loop.

L = Σ √(Δx² + Δy²)

Math.hypot per edge, added in order. For a loop with arcs this is the perimeter of the polygon, which is shorter than the true curve.

  • shapes/sections.js
  • lines 1581 to 1585
  • CadCraft 0.8.2
export function loopLength(loop) {
  let s = 0;
  for (let i = 0; i < loop.length; i++) { const [x1, y1] = loop[i], [x2, y2] = loop[(i + 1) % loop.length]; s += Math.hypot(x2 - x1, y2 - y1); }
  return s;
}

In detail

Method
Table values from the sources named in the file (AISC Shapes Database v16.0, EN 10365:2017); closed-form areas; polygon outlines with arcs sampled by angle; shoelace area and perimeter of a loop.
Number types
64-bit numbers. 25.4 mm per inch (exact); 1.48816394 kg/m per lb/ft; steel density 7850 kg/m³.
Limits
Outline arcs are polygons (6 segments per quarter turn by default, 8 for swept members), so the outline's area and perimeter differ slightly from the exact values. Channels, S shapes and angles are drawn with square corners.

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