Sizes a shelf bracket or a round shaft from a load and a material, and reports the stresses, deflection, bolt loads, volume and mass.
How the precision is kept
The arithmetic is 64-bit closed-form beam and shaft formulas. The accuracy of the result is set by the method: textbook hand formulas, the factor 0.577 in place of 1 ÷ √3, material table values, and sizes rounded up to stock. Pass or fail is decided on unrounded values, but every number returned in the result is rounded to 2 decimals (3 for two of them).
Open item. Use 1 ÷ √3 and the full pound-force torque factors, and return the unrounded values next to the rounded ones.
Calculations under this policy
9 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 factors that turn the given force, length and torque units into newtons, millimeters and newton millimeters.
kN, lbf (4.4482216152605 N), kgf (9.80665 N), inch (25.4 mm) and foot (304.8 mm) are the exact defined values. The torque factors 112.98482902 and 1355.8179483 are shortened: the products 4.4482216152605 × 25.4 and × 304.8 are 112.9848290276167 and 1355.8179483314004, so a pound-force torque is right to about 10 digits.
A required thickness is rounded up, with 1e-9 mm allowed so a value that is a stock size within rounding is not pushed to the next one. r2 and r3 round the numbers that are returned in the result to 2 and 3 decimals.
The two bolt heights and the flange height are rounded to 0.01 mm before they are used in the checks and in the built part. The spacing is rounded to a 5 mm step by design.
Bending and shear stress, tip deflection and bolt loads of the bracket, each with its limit and safety factor.
σ = 6 F a ÷ (b t²); τ = 1.5 F ÷ (b t); δ = F a³ ÷ (3 E I), I = b t³ ÷ 12; T = M y₂ ÷ (y₁² + y₂²); (T ÷ T_all)² + (V ÷ V_all)² ≤ 1
Each check is one 64-bit expression and is passed or failed on the unrounded value with 1e-9 absolute allowance. The shear limits use 0.577 for 1 ÷ √3 (0.57735…), which makes them about 0.06 percent lower than the exact factor. The values, limits and safety factors in the result are rounded to 2 decimals, and the overall safety factor is the smallest of the already rounded ones.
design-it.js
lines 132 to 152
PartCraft 0.8.1
/** The checks of a bracket as dimensioned (dims: t, b, a (arm = shelf length), bolt, H, ys). Pure hand formulas. */
export function checkBracket(dims, load, mat, safety) {
const { t, b, a } = dims, F = load.force_N, M = F * a;
const sigma = 6 * M / (b * t * t), tau = 1.5 * F / (b * t);
const I = b * t ** 3 / 12, delta = F * a ** 3 / (3 * mat.E * I);
const allow = mat.yield / safety;
const rows = [
{ id: 'shelf-bending', what: 'Bending stress at the wall face (σ = 6 F a / (b t²))', value: r2(sigma), limit: r2(allow), unit: 'MPa', sf: r2(mat.yield / sigma), ok: sigma <= allow + 1e-9 },
{ id: 'shelf-shear', what: 'Shear stress in the shelf (τ = 1.5 F / (b t), limit 0.577 Sy / SF)', value: r2(tau), limit: r2(0.577 * allow), unit: 'MPa', sf: r2(0.577 * mat.yield / tau), ok: tau <= 0.577 * allow + 1e-9 },
];
const dl = load.max_deflection_mm;
rows.push({ id: 'tip-deflection', what: 'Tip deflection of the shelf (δ = F a³ / (3 E I); the flange\'s own rotation is not included)', value: r2(delta), limit: dl ? r2(dl) : null, unit: 'mm', sf: dl ? r2(dl / delta) : null, ok: dl ? delta <= dl + 1e-9 : true });
// bolts: rotation about the flange's bottom edge; force proportional to height above it
const ys = [dims.ys[0], dims.ys[1]], sum2 = ys[0] ** 2 + ys[1] ** 2;
const Ttop = M * ys[1] / sum2, V = F / 2;
const Tall = dims.bolt.as * BOLT_PROOF / safety, Vall = 0.577 * dims.bolt.as * BOLT_PROOF / safety;
const comb = (Ttop / Tall) ** 2 + (V / Vall) ** 2;
rows.push({ id: 'bolt-tension', what: 'Tension in the top bolt (T = M y / Σy²; ' + dims.bolt.size + ' class 8.8, Tall = As Sp / SF)', value: r2(Ttop), limit: r2(Tall), unit: 'N', sf: r2(Tall * safety / Ttop), ok: Ttop <= Tall + 1e-9 });
rows.push({ id: 'bolt-combined', what: 'Tension and shear together ((T/Tall)² + (V/Vall)² ≤ 1)', value: r3(comb), limit: 1, unit: '', sf: r2(1 / Math.sqrt(comb || 1e-9)), ok: comb <= 1 + 1e-9 });
return { rows, ok: rows.every((r) => r.ok), safetyFactor: r2(Math.min(...rows.filter((r) => r.sf !== null && r.id !== 'tip-deflection' && r.id !== 'bolt-combined').map((r) => r.sf))), bolt: { top_N: r2(Ttop), shear_N: r2(V) } };
}
Bracket sizing, volume and mass
64-bit
The thickness for strength and for the deflection limit, the bolt size, and the bracket's volume and mass.
t = √(6 M ÷ (b σ_allow)); t = ∛(4 F a³ ÷ (E b δ)); V = b (L t + (H − t) t) − 2 (π ÷ 4) hole² t; mass = V × density × 1e-6
Thicknesses come from Math.sqrt and Math.cbrt in 64-bit and are then rounded up to stock. The volume is the L-shaped section times the width minus two bolt holes through one thickness; volume, mass, force, arm and moment are rounded to 2 decimals in the returned result. The steps text prints values rounded to 2 decimals.
design-it.js
lines 154 to 202
PartCraft 0.8.1
export function estimateBracket(spec) {
const mat = materialFor(spec.material);
const F = fin(spec.force_N, 'The force'), a = fin(spec.arm_mm, 'The arm');
const safety = safetyOf(mat, spec.safety);
const pr = PROCESS[spec.process || 'machined'];
if (!pr) throw new DesignError('invalid_argument', 'Unknown process "' + spec.process + '".', ['Use machined, printed or any.']);
const M = F * a;
const b = spec.width_mm ? fin(spec.width_mm, 'The width') : Math.max(20, Math.ceil(0.15 * a / 5) * 5);
const steps = [];
steps.push('Moment at the wall: M = F·a = ' + r2(F) + ' N × ' + r2(a) + ' mm = ' + r2(M) + ' N·mm.');
steps.push('Allowable stress: Sy / SF = ' + mat.yield + ' / ' + safety + ' = ' + r2(mat.yield / safety) + ' MPa (' + MAT_LABEL[mat.id] + ').');
const tStrength = Math.sqrt(6 * M / (b * mat.yield / safety));
steps.push('Thickness for strength: t = √(6 M / (b σ_allow)) = √(6 × ' + r2(M) + ' / (' + b + ' × ' + r2(mat.yield / safety) + ')) = ' + r2(tStrength) + ' mm at width b = ' + b + ' mm.');
let t = tStrength;
const dl = spec.max_deflection_mm;
if (dl) {
const tStiff = Math.cbrt(4 * F * a ** 3 / (mat.E * b * fin(dl, 'max_deflection')));
steps.push('Thickness for the tip deflection limit ' + dl + ' mm: t = ∛(4 F a³ / (E b δ)) = ' + r2(tStiff) + ' mm.');
t = Math.max(t, tStiff);
}
t = Math.max(t, pr.tmin);
const tr = spec.thickness_mm ? fin(spec.thickness_mm, 'The thickness') : pr.round(t);
if (spec.thickness_mm) steps.push('Thickness given: t = ' + tr + ' mm (checked, not sized).');
else steps.push('Rounded up for the ' + (spec.process || 'machined') + ' process (' + (spec.process === 'printed' ? '0.5 mm steps, 2.5 mm minimum' : spec.process === 'any' ? '0.5 mm steps' : 'stock thicknesses, 3 mm minimum') + '): t = ' + tr + ' mm.');
// the smallest bolt that carries the load
const dims = { t: tr, b, a, L: a };
let chosen = null;
for (const bolt of BOLTS) {
if (bolt.d < 5) continue; // M5 is the smallest a bracket this size is given
const lay = boltLayout({ t: tr, a, bolt });
const trial = { ...dims, bolt, H: lay.H, ys: [lay.yb, lay.yt], spacing: lay.spacing };
const c = checkBracket(trial, { force_N: F, max_deflection_mm: dl }, mat, safety);
if (c.rows.filter((r) => r.id.startsWith('bolt')).every((r) => r.ok)) { chosen = { trial, c }; break; }
}
if (!chosen) throw new DesignError('failed', 'No bolt up to M12 carries this load with two bolts.', ['Lower the force or the arm.', 'Ask for a wider mounting (three or more bolts is not built).']);
const { trial, c } = chosen;
steps.push('Two ' + trial.bolt.size + ' class 8.8 bolts ' + trial.spacing + ' mm apart in the flange (top bolt tension ' + c.bolt.top_N + ' N, shear ' + c.bolt.shear_N + ' N each).');
const volume = trial.b * (trial.L * trial.t + (trial.H - trial.t) * trial.t) - 2 * Math.PI / 4 * trial.bolt.hole ** 2 * trial.t;
const massG = volume * mat.density * 1e-6; // mm³ × kg/m³ → g (1 mm³ = 1e-9 m³; kg → g ×1e3)
return {
generator: 'bracket', process: spec.process || 'machined',
material: { id: mat.id, name: MAT_LABEL[mat.id], E_MPa: mat.E, yield_MPa: mat.yield, density_kg_m3: mat.density },
load: { force_N: r2(F), arm_mm: r2(a), moment_Nmm: r2(M), safety },
dims: { thickness_mm: trial.t, width_mm: trial.b, shelf_length_mm: trial.L, flange_height_mm: trial.H, bolt: trial.bolt.size, bolt_hole_mm: trial.bolt.hole, bolt_heights_mm: trial.ys, bolt_spacing_mm: trial.spacing },
checks: c.rows, ok: c.ok, safetyFactor: c.safetyFactor, volume_mm3: r2(volume), massG: r2(massG), steps,
notes: NOT_CHECKED_BRACKET.concat(PLASTIC.has(mat.id) ? ['Plastic: the yield figure is a short-term value; creep under a steady load is not checked.'] : []),
_t: trial,
};
}
Shaft checks
64-bit
Von Mises stress, torsional shear and twist of a round shaft.
σ' = 32 ÷ (π d³) × √(M² + ¾T²); τ = 16 T ÷ (π d³); θ = T L ÷ (G J), G = E ÷ (2(1 + ν)), J = π d⁴ ÷ 32
64-bit expressions with Math.PI; the square root is of a sum of squares, so nothing cancels. The shear limit uses 0.577 for 1 ÷ √3. Reported stress is rounded to 2 decimals and twist to 3 decimals of a degree.
design-it.js
lines 210 to 222
PartCraft 0.8.1
export function checkShaft(d, L, load, mat, safety) {
const T = load.torque_Nmm || 0, M = load.moment_Nmm || 0;
const sigma = 32 / (Math.PI * d ** 3) * Math.sqrt(M ** 2 + 0.75 * T ** 2);
const allow = mat.yield / safety;
const rows = [{ id: 'von-mises', what: 'Von Mises stress at the surface (32/(π d³)·√(M² + ¾T²))', value: r2(sigma), limit: r2(allow), unit: 'MPa', sf: r2(mat.yield / sigma), ok: sigma <= allow + 1e-9 }];
if (T) {
const tau = 16 * T / (Math.PI * d ** 3);
rows.push({ id: 'torsion-shear', what: 'Torsional shear (16 T / (π d³), limit 0.577 Sy / SF)', value: r2(tau), limit: r2(0.577 * allow), unit: 'MPa', sf: r2(0.577 * mat.yield / tau), ok: tau <= 0.577 * allow + 1e-9 });
const theta = T * L / (mat.E / (2 * (1 + mat.nu)) * Math.PI * d ** 4 / 32) * 180 / Math.PI;
rows.push({ id: 'twist', what: 'Twist over the length (θ = T L / (G J))', value: r3(theta), limit: null, unit: 'degrees', sf: null, ok: true });
}
return { rows, ok: rows.every((r) => r.ok), safetyFactor: r2(Math.min(...rows.filter((r) => r.sf !== null).map((r) => r.sf))) };
}
Shaft sizing, volume and mass
64-bit
The required diameter, the standard size above it, and the shaft's volume and mass.
d = ∛(32 SF √(M² + ¾T²) ÷ (π S_y)); V = (π ÷ 4) d² L
The required diameter is one 64-bit expression; the first standard diameter at or above it (within 1e-9 mm) is taken, or the next whole millimeter above 100 mm. Volume and mass are rounded to 2 decimals in the result.
design-it.js
lines 224 to 245
PartCraft 0.8.1
export function estimateShaft(spec) {
const mat = materialFor(spec.material);
const T = spec.torque_Nmm || 0, M = spec.moment_Nmm || 0;
if (!(T > 0 || M > 0)) throw new DesignError('invalid_argument', 'A shaft needs a torque or a bending moment.', ['Give torque (with torque_unit) and/or moment.']);
const L = fin(spec.length_mm, 'The length');
const safety = safetyOf(mat, spec.safety);
const dReq = Math.cbrt(32 * safety * Math.sqrt(M ** 2 + 0.75 * T ** 2) / (Math.PI * mat.yield));
const d = SHAFT_D.find((s) => s >= dReq - 1e-9) ?? Math.ceil(dReq);
const c = checkShaft(d, L, { torque_Nmm: T, moment_Nmm: M }, mat, safety);
const steps = [
'Equivalent moment √(M² + ¾T²) = ' + r2(Math.sqrt(M ** 2 + 0.75 * T ** 2)) + ' N·mm; allowable ' + r2(mat.yield / safety) + ' MPa (' + MAT_LABEL[mat.id] + ', SF ' + safety + ').',
'Diameter d = ∛(32 SF √(M² + ¾T²) / (π Sy)) = ' + r2(dReq) + ' mm, rounded up to the standard size ' + d + ' mm.',
];
const volume = Math.PI / 4 * d * d * L;
return {
generator: 'shaft', process: spec.process || 'machined',
material: { id: mat.id, name: MAT_LABEL[mat.id], E_MPa: mat.E, yield_MPa: mat.yield, density_kg_m3: mat.density },
load: { torque_Nmm: r2(T), moment_Nmm: r2(M), safety },
dims: { diameter_mm: d, length_mm: L }, checks: c.rows, ok: c.ok, safetyFactor: c.safetyFactor, volume_mm3: r2(volume), massG: r2(volume * mat.density * 1e-6), steps,
notes: ['Not checked: keyways, shoulders and fillets (stress concentration), fatigue, critical speed, bearing loads.'],
};
}
The built part
64-bit
Sketches and extrudes the sized bracket or shaft as part features.
density (g/cm³) = density (kg/m³) ÷ 1000
Sketch points, constraint values and depths are the estimate's dimensions passed on unchanged as 64-bit numbers. The bolt circle radius is hole ÷ 2 and the cut depth 3 t.
design-it.js
lines 296 to 330
PartCraft 0.8.1
/** The part of an estimate: sketches, extrudes and cuts, as the Features panel would make them. */
export function buildPart(est, name) {
const mat = materialById(est.material.id);
const part = newPart(name || (est.generator === 'bracket' ? 'Bracket' : 'Shaft'));
if (est.generator === 'bracket') {
const { t, b, L, H, bolt, ys } = est._t;
const sk = S.newSketch();
const pts = [[0, 0], [L, 0], [L, t], [t, t], [t, H], [0, H]];
const lines = [];
let prev = null;
for (let i = 0; i < 6; i++) { const l = S.addLine(sk, prev || pts[i], pts[(i + 1) % 6]); lines.push(l); prev = l.p2; }
S.addConstraint(sk, 'coincident', [lines[5].p2, lines[0].p1]);
S.addConstraint(sk, 'coincident', ['O', lines[0].p1]);
[0, 2, 4].forEach((i) => S.addConstraint(sk, 'horizontal', [lines[i].id]));
[1, 3, 5].forEach((i) => S.addConstraint(sk, 'vertical', [lines[i].id]));
S.addConstraint(sk, 'hdistance', ['O', lines[0].p2], L);
S.addConstraint(sk, 'distance', [lines[1].id], t);
S.addConstraint(sk, 'vdistance', ['O', lines[4].p1], H);
S.addConstraint(sk, 'hdistance', ['O', lines[2].p2], t);
const s1 = addFeature(part, 'sketch', { plane: { kind: 'base', name: 'front' }, sketch: sk });
addFeature(part, 'extrude', { sketch: s1.id, depth: b });
const hs = S.newSketch();
for (const y of ys) S.addCircle(hs, [-b / 2, y], bolt.hole / 2); // the right plane has sketch x = −Z
const s2 = addFeature(part, 'sketch', { plane: { kind: 'base', name: 'right' }, sketch: hs });
addFeature(part, 'extrude', { sketch: s2.id, op: 'cut', end: 'mid', depth: 3 * t });
} else {
const { diameter_mm: d, length_mm: L } = est.dims;
const sk = S.newSketch();
S.addCircle(sk, [0, 0], d / 2);
const s1 = addFeature(part, 'sketch', { plane: { kind: 'base', name: 'top' }, sketch: sk });
addFeature(part, 'extrude', { sketch: s1.id, depth: L });
}
part.material = { name: MAT_LABEL[mat.id], density: mat.density / 1000 };
return part;
}
In detail
Method
The file header cites Timoshenko, Strength of Materials, and Shigley's bolt joint simplifications: σ = 6M ÷ (b t²), τ = 1.5F ÷ (b t), δ = 4F a³ ÷ (E b t³), bolt tension by rotation about the flange's bottom edge, shaft von Mises stress 32 ÷ (π d³) × √(M² + ¾T²).
Number types
64-bit numbers.
Tolerances
Pass or fail comparisons allow 1e-9 absolute. Rounding up to a stock size allows 1e-9.
Limits
Stress concentration, fatigue, creep, the wall and bolt preload are not checked; the result's own notes say so. 0.577 is used for 1 ÷ √3. The pound-force torque factors are shortened to 11 significant digits.
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.