import React, { useState } from 'react';
import { SnowDriftDiagram } from '../diagrams/SnowDriftDiagram';
export const SnowLoadsSheet: React.FC = () => {
// Basic Inputs
const [inputs, setInputs] = useState({
slopeDeg: 0.4,
width: 11.9,
length: 50.4,
roofType: 'Hip and gable w/ trussed systems',
Pg: 21.0,
risk: 'III',
snowFactor: 1.0,
R: 35,
Ct: 1.1, // Thermal factor input (1.0, 1.1, 1.2, etc.)
Ce: 1.0, // Exposure factor input
userInputCs: 1.0, // Override
userInputPf: 0, // Not used directly if we calc it
rainSurcharge: false,
});
// Unbalanced Inputs
const [unbalanced, setUnbalanced] = useState({
W2: 0.35,
required: false,
});
// Drift Inputs 1
const [drift1, setDrift1] = useState({
lu: 78.0,
h: 5.0,
lp: 121.0,
});
// Drift Inputs 2
const [drift2, setDrift2] = useState({
lu: 0.0,
h: 0.0,
lp: 0.0,
});
// Constants / Derived
const Is = inputs.risk === 'I' ? 0.8 : inputs.risk === 'II' ? 1.0 : inputs.risk === 'III' ? 1.1 : 1.2;
const snowDensity = Math.min(30, 0.13 * inputs.Pg + 14); // Gamma = 0.13Pg + 14 (ASCE 7-22 approx)
// Calc Pf
// Pf = 0.7 * Ce * Ct * Is * Pg
const Pf = 0.7 * inputs.Ce * inputs.Ct * Is * inputs.Pg;
// Calc Cs (Slope Factor) - simplified placeholder logic
const Cs = inputs.userInputCs;
// Sloped Roof Load Ps
const Ps = Cs * Pf;
// Rain on Snow
const rainSurchargeLoad = inputs.rainSurcharge ? 5.0 : 0.0;
// Minimum Snow Load Pm
// Pg <= 20: Pm = Is * Pg. Pg > 20: Pm = Is * 20.
const Pm = inputs.Pg <= 20 ? Is * inputs.Pg : Is * 20;
// Design Snow Load (Uniform)
const designLoad = Math.max(Ps + rainSurchargeLoad, Pm);
// --- Drift Calculation Helper ---
// hd = 0.43 * lu^(1/3) * (pg + 10)^(1/4) - 1.5
const calculateDrift = (lu: number, h: number, lp: number) => {
if (lu <= 0) return { hd: 0, w: 0, pd: 0, hc: 0, hb: 0, driftReq: false, msg: '' };
const hb = Pf / snowDensity;
// drift height hd
const hd_calc = 0.43 * Math.pow(lu, 1 / 3) * Math.pow(inputs.Pg + 10, 0.25) - 1.5;
const hd = Math.max(0, hd_calc);
// hc = h - hb
const hc = h - hb;
// Check if drift required: hc/hb > 0.2
const ratio = hb > 0 ? hc / hb : 100; // safe fallback
let driftReq = true;
let msg = '';
if (ratio < 0.2) {
driftReq = false;
msg = `hc/hb < 0.2 (${ratio.toFixed(2)})`;
}
if (lp < 15 && lp > 0) {
driftReq = false;
msg = `lp < 15', drift not req'd`;
}
if (hc <= 0) {
driftReq = false;
msg = 'hc <= 0';
}
// Actual drift height limited by hc
const design_hd = Math.min(hd, hc);
// Drift width w = 4 * hd
const w = 4 * design_hd;
// Surcharge pd = gamma * hd
const pd = snowDensity * design_hd;
return { hd: design_hd, w, pd, hc, hb, driftReq, msg };
};
const d1Results = calculateDrift(drift1.lu, drift1.h, drift1.lp);
const d2Results = calculateDrift(drift2.lu, drift2.h, drift2.lp);
return (
{/* Main Inputs Grid */}
{/* Row 1: Slope, W, L */}
Roof slope = setInputs({ ...inputs, slopeDeg: parseFloat(e.target.value) })} style={{ width: '60px' }} /> deg
Horiz. eave to ridge dist (W) = setInputs({ ...inputs, width: parseFloat(e.target.value) })} style={{ width: '60px' }} /> ft
Roof length parallel to ridge (L) = setInputs({ ...inputs, length: parseFloat(e.target.value) })} style={{ width: '60px' }} /> ft
Type of Roof
Risk Category =
Pf = 0.7*Ce*Ct*I*Pg
= {Pf.toFixed(1)} psf
Balanced Snow Load =
{Ps.toFixed(1)} psf
Rain on Snow Surcharge Angle
0.24 deg
Rain on Snow Surcharge =
{rainSurchargeLoad.toFixed(1)} psf
Minimum Snow Load Pm =
{Pm.toFixed(1)} psf
Uniform Roof Design Snow Load =
{designLoad.toFixed(1)} psf
{/* Right Panel - Info */}
Snow load tools
Exposure Factor, Ce
{/* Placeholder Table */}
Refer to ASCE 7 Table 7.3-1
{/* Unbalanced Snow Loads */}
Unbalanced Snow Loads - for Hip & Gable roofs only
Required if slope is between 7 on 12 = 30.26 deg
and 2.38 deg
Unbalanced snow loads {inputs.slopeDeg > 2.38 && inputs.slopeDeg < 30.26 ? "ARE required" : "are not required"}
Windward snow load = {Ps.toFixed(1)} psf
Leeward snow load = {(Ps + d1Results.pd).toFixed(1)} psf (Placeholder calc)
{/* Snow Drift 1 */}
Snow Drift 1 - Against roof projections, parapets, etc
Up or downwind fetch lu =
setDrift1({ ...drift1, lu: parseFloat(e.target.value) })} style={{ color: 'red' }} />
ft
Projection height h =
setDrift1({ ...drift1, h: parseFloat(e.target.value) })} style={{ color: 'red' }} />
ft
Projection width/length lp =
setDrift1({ ...drift1, lp: parseFloat(e.target.value) })} style={{ color: 'red' }} />
ft
Snow density gamma = {snowDensity.toFixed(1)} pcf
Balanced snow height hb = {d1Results.hb.toFixed(2)} ft
Drift height hd = {d1Results.hd.toFixed(2)} ft
Clear height hc = {d1Results.hc.toFixed(2)} ft
{d1Results.driftReq ? "Therefore, design for drift" : d1Results.msg || "Drift not required"}
Drift width w = {d1Results.w.toFixed(2)} ft
Surcharge load pd = {d1Results.pd.toFixed(1)} psf
Balanced Snow load = {Pf.toFixed(1)} psf
Total = {(d1Results.pd + Pf).toFixed(1)} psf
{/* Diagram for Drift 1 */}
{/* Snow Drift 2 */}
Snow Drift 2 - Against roof projections, parapets, etc
{/* Similar to Drift 1 */}
Up or downwind fetch lu =
setDrift2({ ...drift2, lu: parseFloat(e.target.value) })} style={{ color: 'red' }} />
ft
Projection height h =
setDrift2({ ...drift2, h: parseFloat(e.target.value) })} style={{ color: 'red' }} />
ft
Projection width/length lp =
setDrift2({ ...drift2, lp: parseFloat(e.target.value) })} style={{ color: 'red' }} />
ft
Snow density gamma = {snowDensity.toFixed(1)} pcf
Balanced snow height hb = {d2Results.hb.toFixed(2)} ft
Drift height hd = {d2Results.hd.toFixed(2)} ft
Clear height hc = {d2Results.hc.toFixed(2)} ft
{d2Results.driftReq ? "Therefore, design for drift" : d2Results.msg || "Drift not required"}
Drift width w = {d2Results.w.toFixed(2)} ft
Surcharge load pd = {d2Results.pd.toFixed(1)} psf
Balanced Snow load = {Pf.toFixed(1)} psf
Total = {(d2Results.pd + Pf).toFixed(1)} psf
);
};