All calculators
How to use this calculator
Choose a shape and what you want to solve for (volume or a dimension).
Pick your length unit and a volume output unit .
Optional stretch features: enable Composite (add/subtract) and/or Mass from density .
Click Calculate to get the answer + steps + quick visual (drag shapes in composite mode).
Formulas & Equations Used
Example Problem & Step-by-Step Solution
Example 1 — Aquarium volume
Box dimensions: L=60 cm, W=30 cm, H=40 cm
Compute volume: V = L·W·H = 60·30·40 = 72,000 cm³
Convert: 72,000 cm³ = 72,000 mL = 72 L
Example 2 — Beaker height from volume (cylinder)
Given: V = 250 mL , r = 3.5 cm
Convert volume: 250 mL = 250 cm³
Rearrange: h = V/(πr²)
Compute: h = 250 /(π·3.5²) ≈ 6.50 cm
Example 3 — Tube (hollow cylinder) volume
Given: outer radius R=2.0 cm , inner radius r=1.5 cm , height h=30 cm
Use: V = πh(R² − r²)
Compute: V = π·30·(2² − 1.5²) = π·30·(4 − 2.25) = π·52.5 ≈ 164.9 cm³
Convert: 164.9 cm³ ≈ 164.9 mL
Frequently Asked Questions
Q: Is cm³ the same as mL?
Yes. 1 cm³ = 1 mL .
Q: Can I solve for height or radius instead of volume?
Yes—use the Solve for dropdown to compute a missing dimension (supported shapes will show options).
Q: What does “composite volume” mean?
It means adding or subtracting volumes (like a block with a drilled hole).
Skip to content
Chat history
You said:
i thiunk it would be a wow factor to have mini SVG diagrams of shapes. Also some of the titles texts are to close to the textbox / dropdown that above them (e.g. ' Solve for (Shape B)' or 'Height (h)'). In addition I want another 2 examples in the Example Problem & Step-by-Step Solution section. Lastly lets add move shapes (see attached screenshot) . here is my current html: How to use this calculator Choose a shape and what you want to solve for (volume or a dimension). Pick your length unit and a volume output unit (we show equivalents too). Optional stretch features: enable Composite (add/subtract) and/or Mass from density . Click Calculate to get the answer + optional steps + quick visual.
Formulas & Equations Used
Example Problem & Step-by-Step Solution Example — Aquarium volume
Box dimensions: L=60 cm, W=30 cm, H=40 cm Compute volume: V = L·W·H = 60·30·40 = 72,000 cm³ Convert: 72,000 cm³ = 72,000 mL = 72 L
Frequently Asked Questions Q: Is cm³ the same as mL?
Yes. 1 cm³ = 1 mL .
Q: Can I solve for height or radius instead of volume?
Yes—use the Solve for dropdown to compute a missing dimension.
Q: What does “composite volume” mean?
It means adding or subtracting volumes (like a block with a drilled hole).
. and js: /* ✅ Production-ready JS: Volume Calculator (Shapes + Solve-for-anything + Composite + Density bridge + steps + visual) */ (function () { const NS = "volumeCalc_v1"; if (window[NS]?.isInitialized) return; window[NS] = { isInitialized: true }; const \( = (sel, root = document) => root.querySelector(sel); const \[ = (sel, root = document) => Array.from(root.querySelectorAll(sel)); const esc = (s) => (s ?? "").toString() .replace(/&/g, "&").replace(//g, ">") .replace(/"/g, """).replace(/'/g, "'"); const num = (v) => { const t = (v ?? "").toString().trim().replace(/,/g, ""); if (!t) return NaN; const x = Number(t); return Number.isFinite(x) ? x : NaN; }; const fmt = (x, dp = 8) => { if (!Number.isFinite(x)) return ""; const ax = Math.abs(x); if (ax !== 0 && (ax >= 1e7 || ax < 1e-6)) return x.toExponential(6); const s = x.toFixed(dp); return s.replace(/(\.\d*?[1-9])0+\)/,"\$1").replace(/\.0+\(/,""); }; const clamp = (x, a, b) => Math.min(b, Math.max(a, x)); // ---------- Unit conversions ---------- // Length to meters const LEN_TO_M = { m: 1, cm: 0.01, mm: 0.001, in: 0.0254, ft: 0.3048 }; // Volume (display/input) to m^3 const VOL_TO_M3 = { m3: 1, L: 1e-3, mL: 1e-6, cm3: 1e-6, mm3: 1e-9, in3: Math.pow(0.0254, 3), ft3: Math.pow(0.3048, 3), galUS: 0.003785411784 }; // Density to kg/m^3 const RHO_TO_KGM3 = { kgm3: 1, gcm3: 1000, // 1 g/cm^3 = 1000 kg/m^3 gml: 1000, // 1 g/mL = 1000 kg/m^3 lbft3: 16.01846337 }; // Convert m^3 to a chosen output unit const m3ToUnit = (V_m3, unit) => { const f = VOL_TO_M3[unit] ?? 1; return V_m3 / f; }; const unitLabel = (u) => ({ m3: "m³", L: "L", mL: "mL", cm3: "cm³", mm3: "mm³", in3: "in³", ft3: "ft³", galUS: "US gal" }[u] || u); // Helpful equivalents for lab + intuition const equivalents = (V_m3) => { const L = m3ToUnit(V_m3, "L"); const mL = m3ToUnit(V_m3, "mL"); const cm3 = m3ToUnit(V_m3, "cm3"); const ft3 = m3ToUnit(V_m3, "ft3"); const gal = m3ToUnit(V_m3, "galUS"); return { L, mL, cm3, ft3, gal }; }; // ---------- Shape definitions ---------- // We use up to 3 main dims (d1,d2,d3) plus optional extra. // Each shape provides: // - labels // - target options // - computeVolume(dimsSI) -> V(m^3) // - solveDim(target, dimsSI, V_m3) -> dimension in meters // - formula strings for steps const SHAPES = { cube: { name: "Cube", dims: ["Edge (a)"], map: { a: "d1" }, labels: { d1: "Edge length (a)" }, show: { d1: true, d2: false, d3: false, extra: false }, targets: [{ v: "V", label: "Volume (V)" }, { v: "a", label: "Edge (a)" }], formulas: { V: "V = a³", a: "a = ³√V" }, volume: ({ a }) => Math.pow(a, 3), solve: (target, { a }, V) => { if (target === "a") return Math.cbrt(V); return NaN; }, viz: (vals, unitTxt) => simpleViz("Cube", [a = \){unitTxt("a")}]) }, rectPrism: { name: "Rectangular prism", dims: ["Length (L)", "Width (W)", "Height (H)"], labels: { d1: "Length (L)", d2: "Width (W)", d3: "Height (H)" }, show: { d1: true, d2: true, d3: true, extra: false }, targets: [ { v: "V", label: "Volume (V)" }, { v: "L", label: "Length (L)" }, { v: "W", label: "Width (W)" }, { v: "H", label: "Height (H)" } ], formulas: { V: "V = L·W·H", L: "L = V/(W·H)", W: "W = V/(L·H)", H: "H = V/(L·W)" }, volume: ({ L, W, H }) => L * W * H, solve: (target, { L, W, H }, V) => { if (target === "L") return V / (W * H); if (target === "W") return V / (L * H); if (target === "H") return V / (L * W); return NaN; }, viz: (vals, unitTxt) => simpleViz("Box", [L = \({unitTxt("L")}, W = \){unitTxt("W")}, H = \({unitTxt("H")}]) }, cylinder: { name: "Cylinder", dims: ["Radius (r)", "Height (h)"], labels: { d1: "Radius (r)", d2: "Height (h)" }, show: { d1: true, d2: true, d3: false, extra: false }, targets: [ { v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" }, { v: "h", label: "Height (h)" } ], formulas: { V: "V = πr²h", r: "r = √(V/(πh))", h: "h = V/(πr²)" }, volume: ({ r, h }) => Math.PI * r * r * h, solve: (target, { r, h }, V) => { if (target === "r") return Math.sqrt(V / (Math.PI * h)); if (target === "h") return V / (Math.PI * r * r); return NaN; }, viz: (vals, unitTxt) => simpleViz("Cylinder", [r = \){unitTxt("r")}, h = \({unitTxt("h")}]) }, sphere: { name: "Sphere", dims: ["Radius (r)"], labels: { d1: "Radius (r)" }, show: { d1: true, d2: false, d3: false, extra: false }, targets: [ { v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" } ], formulas: { V: "V = (4/3)πr³", r: "r = ³√(3V/(4π))" }, volume: ({ r }) => (4 / 3) * Math.PI * Math.pow(r, 3), solve: (target, { r }, V) => { if (target === "r") return Math.cbrt((3 * V) / (4 * Math.PI)); return NaN; }, viz: (vals, unitTxt) => simpleViz("Sphere", [r = \){unitTxt("r")}]) }, cone: { name: "Cone", dims: ["Radius (r)", "Height (h)"], labels: { d1: "Radius (r)", d2: "Height (h)" }, show: { d1: true, d2: true, d3: false, extra: false }, targets: [ { v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" }, { v: "h", label: "Height (h)" } ], formulas: { V: "V = (1/3)πr²h", r: "r = √(3V/(πh))", h: "h = 3V/(πr²)" }, volume: ({ r, h }) => (1 / 3) * Math.PI * r * r * h, solve: (target, { r, h }, V) => { if (target === "r") return Math.sqrt((3 * V) / (Math.PI * h)); if (target === "h") return (3 * V) / (Math.PI * r * r); return NaN; }, viz: (vals, unitTxt) => simpleViz("Cone", [r = \({unitTxt("r")}, h = \){unitTxt("h")}]) }, pyramid: { name: "Pyramid (rectangular base)", dims: ["Base length (L)", "Base width (W)", "Height (H)"], labels: { d1: "Base length (L)", d2: "Base width (W)", d3: "Height (H)" }, show: { d1: true, d2: true, d3: true, extra: false }, targets: [ { v: "V", label: "Volume (V)" }, { v: "L", label: "Base length (L)" }, { v: "W", label: "Base width (W)" }, { v: "H", label: "Height (H)" } ], formulas: { V: "V = (1/3)L·W·H", L: "L = 3V/(W·H)", W: "W = 3V/(L·H)", H: "H = 3V/(L·W)" }, volume: ({ L, W, H }) => (1 / 3) * L * W * H, solve: (target, { L, W, H }, V) => { if (target === "L") return (3 * V) / (W * H); if (target === "W") return (3 * V) / (L * H); if (target === "H") return (3 * V) / (L * W); return NaN; }, viz: (vals, unitTxt) => simpleViz("Pyramid", [L = \({unitTxt("L")}, W = \){unitTxt("W")}, H = \({unitTxt("H")}]) }, triPrism: { name: "Triangular prism (right)", // d1 = base b, d2 = triangle height ht, d3 = prism length L labels: { d1: "Triangle base (b)", d2: "Triangle height (h)", d3: "Prism length (L)" }, show: { d1: true, d2: true, d3: true, extra: false }, targets: [ { v: "V", label: "Volume (V)" }, { v: "b", label: "Triangle base (b)" }, { v: "h", label: "Triangle height (h)" }, { v: "L", label: "Prism length (L)" } ], formulas: { V: "V = (1/2)·b·h·L", b: "b = 2V/(h·L)", h: "h = 2V/(b·L)", L: "L = 2V/(b·h)" }, volume: ({ b, h, L }) => 0.5 * b * h * L, solve: (target, { b, h, L }, V) => { if (target === "b") return (2 * V) / (h * L); if (target === "h") return (2 * V) / (b * L); if (target === "L") return (2 * V) / (b * h); return NaN; }, viz: (vals, unitTxt) => simpleViz("Triangular prism", [b = \){unitTxt("b")}, h = \({unitTxt("h")}, L = \){unitTxt("L")}]) } }; // ---------- UI helpers ---------- const clearOutput = () => { \(("#result").innerHTML = ""; \)("#viz").innerHTML = ""; \(("#steps").innerHTML = ""; \)("#noResultsHint").style.display = "block"; }; const showError = (msg) => { \(("#noResultsHint").style.display = "none"; \)("#result").innerHTML = ⚠️ \({esc(msg)}
; \)("#viz").innerHTML = ""; \(("#steps").innerHTML = ""; }; const setDisabled = (el, isDisabled, placeholder) => { if (!el) return; el.disabled = !!isDisabled; el.style.opacity = isDisabled ? "0.65" : "1"; el.style.pointerEvents = isDisabled ? "none" : "auto"; if (placeholder != null) el.placeholder = placeholder; }; const setWrapVisible = (wrap, yes) => { if (!wrap) return; wrap.style.display = yes ? "flex" : "none"; }; // Simple visual: a clean “card” with labels + tiny bar-ish hint const simpleViz = (title, lines) => { const safe = lines.map(x => \){esc(x)} ).join(""); return Quick visual
; }; const barViz = (labelA, a, labelB, b) => { const A = Number.isFinite(a) ? Math.abs(a) : 0; const B = Number.isFinite(b) ? Math.abs(b) : 0; const max = Math.max(A, B, 1e-12); const wa = clamp((A / max) * 100, 0, 100); const wb = clamp((B / max) * 100, 0, 100); return Magnitude compare
\({esc(labelA)}: \){esc(fmt(a, 6))}
\){esc(labelB)}: \({esc(fmt(b, 6))}
; }; // ---------- Map UI dims to named dims per shape ---------- const getShapeKey = () => \(("#shape").value || "cube"; const getTargetKey = () => \)("#target").value || "V"; const getLenUnit = () => \(("#lenUnit").value || "m"; const getVolUnit = () => \)("#volUnit").value || "m3"; // For composite B const getShapeKeyB = () => \(("#shapeB").value || "rectPrism"; const getTargetKeyB = () => \)("#targetB").value || "V"; const readDimsSI = (shapeKey, prefix = "") => { const lenUnit = getLenUnit(); const toM = LEN_TO_M[lenUnit] ?? 1; const d1 = num(\((#\){prefix}d1).value); const d2 = num(\((#\){prefix}d2).value); const d3 = num(\((#\){prefix}d3).value); const extra = num(\((#\){prefix}extra).value); // Convert to meters (when present) const d1m = Number.isFinite(d1) ? d1 * toM : NaN; const d2m = Number.isFinite(d2) ? d2 * toM : NaN; const d3m = Number.isFinite(d3) ? d3 * toM : NaN; const extram = Number.isFinite(extra) ? extra * toM : NaN; // Map to named dims const s = SHAPES[shapeKey]; if (!s) return {}; const out = {}; // Shape-specific mapping based on target names in formulas // cube: a=d1 // rectPrism/pyramid: L=d1,W=d2,H=d3 // cylinder/cone: r=d1,h=d2 // sphere: r=d1 // triPrism: b=d1,h=d2,L=d3 if (shapeKey === "cube") out.a = d1m; if (shapeKey === "rectPrism" || shapeKey === "pyramid") { out.L = d1m; out.W = d2m; out.H = d3m; } if (shapeKey === "cylinder" || shapeKey === "cone") { out.r = d1m; out.h = d2m; } if (shapeKey === "sphere") out.r = d1m; if (shapeKey === "triPrism") { out.b = d1m; out.h = d2m; out.L = d3m; } // Extra reserved if you expand later out._extra = extram; return out; }; const readVolumeInM3 = (prefix = "") => { const vu = getVolUnit(); const Vin = num(\((#\){prefix}vIn).value); if (!Number.isFinite(Vin)) return NaN; const f = VOL_TO_M3[vu] ?? 1; return Vin * f; }; // ---------- UI sync for a shape panel ---------- const fillTargets = (shapeKey, targetSel) => { const s = SHAPES[shapeKey]; if (!s) return; const current = targetSel.value; targetSel.innerHTML = s.targets.map(t => \){esc(t.label)} ).join(""); // preserve if possible if (s.targets.some(t => t.v === current)) targetSel.value = current; else targetSel.value = "V"; }; const applyShapeUI = (shapeKey, targetKey, prefix = "") => { const s = SHAPES[shapeKey]; if (!s) return; // Labels \((#\){prefix}d1Label).textContent = s.labels?.d1 || "Dimension 1"; \((#\){prefix}d2Label).textContent = s.labels?.d2 || "Dimension 2"; \((#\){prefix}d3Label).textContent = s.labels?.d3 || "Dimension 3"; // Show/hide dimension fields setWrapVisible(\((#\){prefix}d1Wrap), !!s.show.d1); setWrapVisible(\((#\){prefix}d2Wrap), !!s.show.d2); setWrapVisible(\((#\){prefix}d3Wrap), !!s.show.d3); // Extra (unused in current set) const extraWrap = \((#\){prefix}extraWrap); if (extraWrap) extraWrap.style.display = s.show.extra ? "flex" : "none"; // Enable/disable based on target const d1 = \((#\){prefix}d1); const d2 = \((#\){prefix}d2); const d3 = \((#\){prefix}d3); const vIn = \((#\){prefix}vIn); const vInWrap = \((#\){prefix}vInWrap); // default enable everything shown if (d1) setDisabled(d1, false); if (d2) setDisabled(d2, false); if (d3) setDisabled(d3, false); if (vIn) setDisabled(vIn, false); // When target is Volume: volume input computed, dimension inputs required // When target is a dimension: volume input required, that dimension computed if (targetKey === "V") { if (vInWrap) vInWrap.style.display = "none"; if (vIn) vIn.value = ""; // all shown dims enabled } else { if (vInWrap) vInWrap.style.display = "flex"; // Disable the dimension being solved for const disableByTarget = () => { if (shapeKey === "cube" && targetKey === "a") setDisabled(d1, true, "Computed"); if ((shapeKey === "rectPrism" || shapeKey === "pyramid") && targetKey === "L") setDisabled(d1, true, "Computed"); if ((shapeKey === "rectPrism" || shapeKey === "pyramid") && targetKey === "W") setDisabled(d2, true, "Computed"); if ((shapeKey === "rectPrism" || shapeKey === "pyramid") && targetKey === "H") setDisabled(d3, true, "Computed"); if ((shapeKey === "cylinder" || shapeKey === "cone") && targetKey === "r") setDisabled(d1, true, "Computed"); if ((shapeKey === "cylinder" || shapeKey === "cone") && targetKey === "h") setDisabled(d2, true, "Computed"); if (shapeKey === "sphere" && targetKey === "r") setDisabled(d1, true, "Computed"); if (shapeKey === "triPrism" && targetKey === "b") setDisabled(d1, true, "Computed"); if (shapeKey === "triPrism" && targetKey === "h") setDisabled(d2, true, "Computed"); if (shapeKey === "triPrism" && targetKey === "L") setDisabled(d3, true, "Computed"); }; disableByTarget(); } }; const syncPrimaryUI = () => { const shapeKey = getShapeKey(); fillTargets(shapeKey, \(("#target")); const targetKey = getTargetKey(); \)("#shapeHint").textContent = (targetKey === "V") ? "Tip: Enter dimensions to get volume + automatic equivalents." : "Tip: Enter the known volume and the other dimensions — we’ll compute the missing one."; applyShapeUI(shapeKey, targetKey, ""); clearOutput(); }; const syncBUI = () => { const shapeKey = getShapeKeyB(); fillTargets(shapeKey, \(("#targetB")); applyShapeUI(shapeKey, getTargetKeyB(), "b_"); clearOutput(); }; const syncStretchPanels = () => { \)("#compositePanel").style.display = \(("#compositeOn").checked ? "block" : "none"; \)("#densityPanel").style.display = \(("#densityOn").checked ? "block" : "none"; clearOutput(); }; // ---------- Validation + computation helpers ---------- const requireFinite = (val, msg) => { if (!Number.isFinite(val)) { showError(msg); return false; } return true; }; const requirePositive = (val, msg) => { if (!(Number.isFinite(val) && val > 0)) { showError(msg); return false; } return true; }; const computeOneShape = (shapeKey, targetKey, prefix = "") => { const s = SHAPES[shapeKey]; if (!s) return { ok: false }; const dims = readDimsSI(shapeKey, prefix); const V_in_m3 = readVolumeInM3(prefix); // Helpers to build readable unit strings in visual (uses selected units) const lenUnit = getLenUnit(); const unitTxt = (sym) => { // sym maps to dimension names; we read SI dims and convert back to chosen length unit for display const toM = LEN_TO_M[lenUnit] ?? 1; const fromM = 1 / toM; const x = dims[sym]; return \){fmt(x * fromM, 6)} \({lenUnit}; }; // Validate required fields const validateDimsForVolume = () => { if (shapeKey === "cube") return requirePositive(dims.a, "Please enter a valid edge length (a)."); if (shapeKey === "rectPrism" || shapeKey === "pyramid") { return requirePositive(dims.L, "Please enter a valid length (L).") && requirePositive(dims.W, "Please enter a valid width (W).") && requirePositive(dims.H, "Please enter a valid height (H)."); } if (shapeKey === "cylinder" || shapeKey === "cone") { return requirePositive(dims.r, "Please enter a valid radius (r).") && requirePositive(dims.h, "Please enter a valid height (h)."); } if (shapeKey === "sphere") return requirePositive(dims.r, "Please enter a valid radius (r)."); if (shapeKey === "triPrism") { return requirePositive(dims.b, "Please enter a valid triangle base (b).") && requirePositive(dims.h, "Please enter a valid triangle height (h).") && requirePositive(dims.L, "Please enter a valid prism length (L)."); } return true; }; const validateForSolveDim = () => { if (!requirePositive(V_in_m3, "Please enter a valid known volume (V).")) return false; // Need all dims except the target if (shapeKey === "cube") { // solving a only needs V return true; } if (shapeKey === "sphere") { // solving r only needs V return true; } if (shapeKey === "cylinder" || shapeKey === "cone") { if (targetKey === "r") return requirePositive(dims.h, "Please enter a valid height (h)."); if (targetKey === "h") return requirePositive(dims.r, "Please enter a valid radius (r)."); } if (shapeKey === "rectPrism" || shapeKey === "pyramid") { if (targetKey === "L") return requirePositive(dims.W, "Please enter a valid width (W).") && requirePositive(dims.H, "Please enter a valid height (H)."); if (targetKey === "W") return requirePositive(dims.L, "Please enter a valid length (L).") && requirePositive(dims.H, "Please enter a valid height (H)."); if (targetKey === "H") return requirePositive(dims.L, "Please enter a valid length (L).") && requirePositive(dims.W, "Please enter a valid width (W)."); } if (shapeKey === "triPrism") { if (targetKey === "b") return requirePositive(dims.h, "Please enter a valid triangle height (h).") && requirePositive(dims.L, "Please enter a valid prism length (L)."); if (targetKey === "h") return requirePositive(dims.b, "Please enter a valid triangle base (b).") && requirePositive(dims.L, "Please enter a valid prism length (L)."); if (targetKey === "L") return requirePositive(dims.b, "Please enter a valid triangle base (b).") && requirePositive(dims.h, "Please enter a valid triangle height (h)."); } return true; }; if (targetKey === "V") { if (!validateDimsForVolume()) return { ok: false }; const V = s.volume(dims); if (!Number.isFinite(V) || V <= 0) { showError("Inputs produce an invalid volume. Please check values."); return { ok: false }; } return { ok: true, kind: "volume", shapeKey, targetKey, V_m3: V, dims, formula: s.formulas.V, vizHtml: s.viz?.(dims, unitTxt) || "" }; } else { if (!validateForSolveDim()) return { ok: false }; const x_m = s.solve(targetKey, dims, V_in_m3); if (!Number.isFinite(x_m) || x_m <= 0) { showError("Inputs produce an invalid dimension. Check volume and other dimensions."); return { ok: false }; } return { ok: true, kind: "dimension", shapeKey, targetKey, V_m3: V_in_m3, x_m, dims, formula: s.formulas[targetKey] || "", vizHtml: s.viz?.(dims, unitTxt) || "" }; } }; const computeMassFromDensity = (V_m3) => { const rhoRaw = num(\)("#rhoVal").value); if (!Number.isFinite(rhoRaw) || rhoRaw <= 0) return { ok: false, err: "Please enter a valid density (ρ > 0)." }; const rhoUnit = \(("#rhoUnit").value; const rho = rhoRaw * (RHO_TO_KGM3[rhoUnit] ?? 1); // kg/m^3 const m_kg = rho * V_m3; return { ok: true, rho, m_kg }; }; // ---------- Rendering ---------- const renderVolumeResult = (label, V_m3, extraBlockHtml = "") => { const outUnit = getVolUnit(); const Vout = m3ToUnit(V_m3, outUnit); const eq = equivalents(V_m3); // choose 3 best equivalents depending on scale const eqLines = [ \){esc(fmt(eq.L, 6))} L, \({esc(fmt(eq.mL, 6))} mL, \){esc(fmt(eq.cm3, 6))} cm³, \({esc(fmt(eq.ft3, 6))} ft³, \){esc(fmt(eq.gal, 6))} US gal ]; \(("#noResultsHint").style.display = "none"; \)("#result").innerHTML = \({esc(label)}
Volume: \){esc(fmt(Vout, 8))} \({esc(unitLabel(outUnit))}
Helpful equivalents: \){eqLines.join(" ")}
\({extraBlockHtml}
; }; const renderDimensionResult = (shapeKey, targetKey, x_m, V_m3, extraBlockHtml = "") => { const lenUnit = getLenUnit(); const fromM = 1 / (LEN_TO_M[lenUnit] ?? 1); const x_out = x_m * fromM; const niceName = { a: "Edge (a)", L: "Length (L)", W: "Width (W)", H: "Height (H)", r: "Radius (r)", h: "Height (h)", b: "Triangle base (b)" }[targetKey] || "Dimension"; \)("#noResultsHint").style.display = "none"; \(("#result").innerHTML = Solved dimension
\){esc(niceName)}: \({esc(fmt(x_out, 8))} \){esc(lenUnit)}
Given volume: \({esc(fmt(m3ToUnit(V_m3, getVolUnit()), 8))} \){esc(unitLabel(getVolUnit()))}
\({extraBlockHtml}
; }; const renderSteps = (blocks) => { const showSteps = \)("#showSteps").checked; if (!showSteps) { \(("#steps").innerHTML = ""; return; } const html = blocks.map(b => \){esc(b.title)}
\({b.items.map(li => \){li} ).join("")} ).join(""); \(("#steps").innerHTML = html; }; const renderViz = (html) => { const showViz = \)("#showViz").checked; \(("#viz").innerHTML = showViz ? html : ""; }; // ---------- Main compute ---------- const compute = () => { clearOutput(); try { const a = computeOneShape(getShapeKey(), getTargetKey(), ""); if (!a.ok) return; let Vtotal_m3 = NaN; let V1_m3 = NaN; let V2_m3 = NaN; const compositeOn = \)("#compositeOn").checked; if (a.kind === "volume") V1_m3 = a.V_m3; else V1_m3 = a.V_m3; // if solving a dim, V is given; still usable if (compositeOn) { const b = computeOneShape(getShapeKeyB(), getTargetKeyB(), "b_"); if (!b.ok) return; V2_m3 = (b.kind === "volume") ? b.V_m3 : b.V_m3; const op = \(("#compOp").value; Vtotal_m3 = (op === "sub") ? (V1_m3 - V2_m3) : (V1_m3 + V2_m3); if (!(Number.isFinite(Vtotal_m3) && Vtotal_m3 > 0)) { return showError("Composite volume is not positive. If subtracting, ensure V₁ > V₂."); } } else { Vtotal_m3 = V1_m3; } // Density block (mass) let massBlockHtml = ""; let massSteps = null; if (\)("#densityOn").checked) { const m = computeMassFromDensity(Vtotal_m3); if (!m.ok) return showError(m.err); const m_g = m.m_kg * 1000; const m_lb = m.m_kg / 0.45359237; massBlockHtml =
Mass from density:
ρ = \({esc(fmt(m.rho, 6))} kg/m³
m = \){esc(fmt(m.m_kg, 8))} kg
= \({esc(fmt(m_g, 8))} g • \){esc(fmt(m_lb, 8))} lb
; massSteps = { title: "Mass from density (optional)", items: [ Convert density to SI: ρ in kg/m³, Compute mass: m = ρ·V , Convert mass to g and lb (optional) ] }; } // Output: // - If target is dimension -> show dimension result, but also show (composite) volume context. // - If target is volume -> show volume result. const targetA = getTargetKey(); const outUnit = getVolUnit(); if (targetA === "V") { // Show composite volume if enabled, otherwise V1 const label = compositeOn ? "Composite volume (Vtotal)" : Volume (\({SHAPES[getShapeKey()].name}); renderVolumeResult(label, Vtotal_m3, massBlockHtml); } else { // Dimension result uses A's dimension x_m, but show that volume context is composite total renderDimensionResult(getShapeKey(), targetA, a.x_m, a.V_m3, compositeOn ?
Composite total volume: \){esc(fmt(m3ToUnit(Vtotal_m3, outUnit), 8))} \({esc(unitLabel(outUnit))}
: "" + massBlockHtml ); // If composite is on AND density is on, we already included massBlockHtml. Ensure it appears: if (compositeOn && \)("#densityOn").checked && !\(("#result").innerHTML.includes("Mass from density")) { // Safe append \)("#result").innerHTML = \(("#result").innerHTML.replace("", \){massBlockHtml}
); } } // Visuals let vizHtml = ""; if (\(("#showViz").checked) { vizHtml += a.vizHtml || ""; if (compositeOn) { vizHtml +=
Composite mode: we combine volumes using Vtotal = V₁ ± V₂.
; vizHtml += barViz("|V₁| (m³)", V1_m3, "|V₂| (m³)", V2_m3); vizHtml += barViz("|Vtotal| (m³)", Vtotal_m3, "L equivalent", m3ToUnit(Vtotal_m3, "L")); } else { vizHtml += barViz("V (m³)", Vtotal_m3, "L equivalent", m3ToUnit(Vtotal_m3, "L")); } } renderViz(vizHtml); // Steps const lenUnit = getLenUnit(); const stepsBlocks = []; // Step block for A const shapeA = SHAPES[getShapeKey()]; if (targetA === "V") { stepsBlocks.push({ title: "Step-by-step (Shape A)", items: [ Convert all dimensions to meters (
m )., Use formula:
\){esc(shapeA.formulas.V)} ., Compute volume in
m³ , then convert to your chosen unit (\({esc(unitLabel(outUnit))})., Show helpful equivalents (L, mL, cm³, etc.). ] }); } else { stepsBlocks.push({ title: "Step-by-step (Solve for missing dimension — Shape A)", items: [ Convert volume to
m³ and other dimensions to meters., Rearrange formula:
\){esc(shapeA.formulas[targetA] || "")} ., Compute the missing dimension in meters, then convert to \({esc(lenUnit)}. ] }); } // Composite steps if (compositeOn) { stepsBlocks.push({ title: "Composite volume (optional)", items: [ Compute
V₁ from Shape A and
V₂ from Shape B., Combine:
Vtotal = V₁ ± V₂ ., Ensure the total is positive (especially when subtracting). ] }); } if (massSteps) stepsBlocks.push(massSteps); renderSteps(stepsBlocks); } catch (e) { showError("Something went wrong. Please check inputs and try again."); } }; // ---------- Presets ---------- const setModeA = (shapeKey, targetKey) => { \)("#shape").value = shapeKey; fillTargets(shapeKey, \(("#target")); \)("#target").value = targetKey; applyShapeUI(shapeKey, targetKey, ""); }; const setModeB = (shapeKey, targetKey) => { \(("#shapeB").value = shapeKey; fillTargets(shapeKey, \)("#targetB")); \(("#targetB").value = targetKey; applyShapeUI(shapeKey, targetKey, "b_"); }; const clearFields = () => { ["d1","d2","d3","extra","vIn","b_d1","b_d2","b_d3","b_extra","b_vIn","rhoVal"].forEach(id => { const el = \)(#\({id}); if (el) el.value = ""; }); }; const applyPreset = (key) => { // reset toggles \)("#compositeOn").checked = false; \(("#densityOn").checked = false; syncStretchPanels(); clearFields(); \)("#lenUnit").value = "cm"; \(("#volUnit").value = "L"; if (key === "cube10cm") { setModeA("cube", "V"); \)("#d1").value = "10"; \(("#lenUnit").value = "cm"; \)("#volUnit").value = "cm3"; } if (key === "aquarium") { setModeA("rectPrism", "V"); \(("#d1").value = "60"; \)("#d2").value = "30"; \(("#d3").value = "40"; \)("#lenUnit").value = "cm"; \(("#volUnit").value = "L"; } if (key === "cylBeaker") { // Solve for height h given V=250 mL and r=3.5 cm setModeA("cylinder", "h"); \)("#lenUnit").value = "cm"; \(("#volUnit").value = "mL"; \)("#d1").value = "3.5"; // radius \(("#vIn").value = "250"; // mL } if (key === "sphere8in") { // diameter 8 in => radius 4 in setModeA("sphere", "V"); \)("#lenUnit").value = "in"; \(("#volUnit").value = "in3"; \)("#d1").value = "4"; } if (key === "holeInBlock") { // Block 10x8x6 cm minus cylinder hole r=1 cm, h=6 cm \(("#compositeOn").checked = true; syncStretchPanels(); \)("#compOp").value = "sub"; setModeA("rectPrism", "V"); \(("#lenUnit").value = "cm"; \)("#volUnit").value = "cm3"; \(("#d1").value = "10"; \)("#d2").value = "8"; \(("#d3").value = "6"; setModeB("cylinder", "V"); \)("#b_d1").value = "1"; \(("#b_d2").value = "6"; } if (key === "massWater") { // 500 mL water, compute mass: V is given -> easiest: use cube solve? We'll treat as "known volume" and any shape isn't needed: // We'll do cylinder with radius 5 cm and solve for height from V=500 mL, then compute mass with rho=1 g/mL. \)("#densityOn").checked = true; syncStretchPanels(); setModeA("cylinder", "h"); \(("#lenUnit").value = "cm"; \)("#volUnit").value = "mL"; \(("#d1").value = "5"; // r \)("#vIn").value = "500"; // mL \(("#rhoVal").value = "1"; \)("#rhoUnit").value = "gml"; } clearOutput(); compute(); }; // ---------- Wiring ---------- const wire = () => { // init targets fillTargets(getShapeKey(), \(("#target")); fillTargets(getShapeKeyB(), \)("#targetB")); applyShapeUI(getShapeKey(), getTargetKey(), ""); applyShapeUI(getShapeKeyB(), getTargetKeyB(), "b_"); syncStretchPanels(); clearOutput(); \(("#shape").addEventListener("change", syncPrimaryUI); \)("#target").addEventListener("change", syncPrimaryUI); \(("#shapeB").addEventListener("change", syncBUI); \)("#targetB").addEventListener("change", syncBUI); \(("#lenUnit").addEventListener("change", () => { syncPrimaryUI(); if (\)("#compositeOn").checked) syncBUI(); }); \(("#volUnit").addEventListener("change", () => { syncPrimaryUI(); if (\)("#compositeOn").checked) syncBUI(); }); \(("#compositeOn").addEventListener("change", syncStretchPanels); \)("#densityOn").addEventListener("change", syncStretchPanels); \(("#compOp").addEventListener("change", clearOutput); // inputs clear on typing [ "#d1","#d2","#d3","#extra","#vIn", "#b_d1","#b_d2","#b_d3","#b_extra","#b_vIn", "#rhoVal","#rhoUnit" ].forEach(sel => { const el = \)(sel); if (!el) return; el.addEventListener("input", clearOutput); el.addEventListener("change", clearOutput); el.addEventListener("keydown", (e) => { if (e.key === "Enter") compute(); }); }); \(("#calculateButton").addEventListener("click", compute); \)("#resetButton").addEventListener("click", () => { // basic defaults \(("#shape").value = "cube"; fillTargets("cube", \)("#target")); \(("#target").value = "V"; \)("#lenUnit").value = "m"; \(("#volUnit").value = "m3"; \)("#compositeOn").checked = false; \(("#densityOn").checked = false; \)("#compOp").value = "add"; \(("#shapeB").value = "rectPrism"; fillTargets("rectPrism", \)("#targetB")); \(("#targetB").value = "V"; \)("#rhoUnit").value = "kgm3"; clearFields(); syncPrimaryUI(); syncBUI(); syncStretchPanels(); \(("#showSteps").checked = true; \)("#showViz").checked = true; clearOutput(); }); // quick picks \]("button.chip").forEach(btn => { btn.addEventListener("click", () => { const p = btn.getAttribute("data-preset"); if (p) applyPreset(p); }); }); // enter in form \(("#volForm").addEventListener("keydown", (e) => { if (e.key === "Enter") compute(); }); }; if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", wire); else wire(); })(); Please provide updated full htaml and js
ChatGPT said:
Stopped thinking
You said:
Uploaded image
i thiunk it would be a wow factor to have mini SVG diagrams of shapes. Also some of the titles texts are to close to the textbox / dropdown that above them (e.g. ' Solve for (Shape B)' or 'Height (h)'). In addition I want another 2 examples in the Example Problem & Step-by-Step Solution section. Lastly lets add move shapes (see attached screenshot) . here is my current html:
How to use this calculator Choose a shape and what you want to solve for (volume or a dimension). Pick your length unit and a volume output unit (we show equivalents too). Optional stretch features: enable Composite (add/subtract) and/or Mass from density . Click Calculate to get the answer + optional steps + quick visual.
Formulas & Equations Used
Example Problem & Step-by-Step Solution Example — Aquarium volume
Box dimensions: L=60 cm, W=30 cm, H=40 cm Compute volume: V = L·W·H = 60·30·40 = 72,000 cm³ Convert: 72,000 cm³ = 72,000 mL = 72 L
Frequently Asked Questions Q: Is cm³ the same as mL?
Yes. 1 cm³ = 1 mL .
Q: Can I solve for height or radius instead of volume?
Yes—use the Solve for dropdown to compute a missing dimension.
Q: What does “composite volume” mean?
It means adding or subtracting volumes (like a block with a drilled hole).
. and js: /* ✅ Production-ready JS: Volume Calculator (Shapes + Solve-for-anything + Composite + Density bridge + steps + visual) */ (function () { const NS = "volumeCalc_v1"; if (window[NS]?.isInitialized) return; window[NS] = { isInitialized: true }; const \) = (sel, root = document) => root.querySelector(sel); const \[ = (sel, root = document) => Array.from(root.querySelectorAll(sel)); const esc = (s) => (s ?? "").toString() .replace(/&/g, "&").replace(//g, ">") .replace(/"/g, """).replace(/'/g, "'"); const num = (v) => { const t = (v ?? "").toString().trim().replace(/,/g, ""); if (!t) return NaN; const x = Number(t); return Number.isFinite(x) ? x : NaN; }; const fmt = (x, dp = 8) => { if (!Number.isFinite(x)) return ""; const ax = Math.abs(x); if (ax !== 0 && (ax >= 1e7 || ax < 1e-6)) return x.toExponential(6); const s = x.toFixed(dp); return s.replace(/(\.\d*?[1-9])0+\(/,"\)1").replace(/\.0+\(/,""); }; const clamp = (x, a, b) => Math.min(b, Math.max(a, x)); // ---------- Unit conversions ---------- // Length to meters const LEN_TO_M = { m: 1, cm: 0.01, mm: 0.001, in: 0.0254, ft: 0.3048 }; // Volume (display/input) to m^3 const VOL_TO_M3 = { m3: 1, L: 1e-3, mL: 1e-6, cm3: 1e-6, mm3: 1e-9, in3: Math.pow(0.0254, 3), ft3: Math.pow(0.3048, 3), galUS: 0.003785411784 }; // Density to kg/m^3 const RHO_TO_KGM3 = { kgm3: 1, gcm3: 1000, // 1 g/cm^3 = 1000 kg/m^3 gml: 1000, // 1 g/mL = 1000 kg/m^3 lbft3: 16.01846337 }; // Convert m^3 to a chosen output unit const m3ToUnit = (V_m3, unit) => { const f = VOL_TO_M3[unit] ?? 1; return V_m3 / f; }; const unitLabel = (u) => ({ m3: "m³", L: "L", mL: "mL", cm3: "cm³", mm3: "mm³", in3: "in³", ft3: "ft³", galUS: "US gal" }[u] || u); // Helpful equivalents for lab + intuition const equivalents = (V_m3) => { const L = m3ToUnit(V_m3, "L"); const mL = m3ToUnit(V_m3, "mL"); const cm3 = m3ToUnit(V_m3, "cm3"); const ft3 = m3ToUnit(V_m3, "ft3"); const gal = m3ToUnit(V_m3, "galUS"); return { L, mL, cm3, ft3, gal }; }; // ---------- Shape definitions ---------- // We use up to 3 main dims (d1,d2,d3) plus optional extra. // Each shape provides: // - labels // - target options // - computeVolume(dimsSI) -> V(m^3) // - solveDim(target, dimsSI, V_m3) -> dimension in meters // - formula strings for steps const SHAPES = { cube: { name: "Cube", dims: ["Edge (a)"], map: { a: "d1" }, labels: { d1: "Edge length (a)" }, show: { d1: true, d2: false, d3: false, extra: false }, targets: [{ v: "V", label: "Volume (V)" }, { v: "a", label: "Edge (a)" }], formulas: { V: "V = a³", a: "a = ³√V" }, volume: ({ a }) => Math.pow(a, 3), solve: (target, { a }, V) => { if (target === "a") return Math.cbrt(V); return NaN; }, viz: (vals, unitTxt) => simpleViz("Cube", [a = \){unitTxt("a")}]) }, rectPrism: { name: "Rectangular prism", dims: ["Length (L)", "Width (W)", "Height (H)"], labels: { d1: "Length (L)", d2: "Width (W)", d3: "Height (H)" }, show: { d1: true, d2: true, d3: true, extra: false }, targets: [ { v: "V", label: "Volume (V)" }, { v: "L", label: "Length (L)" }, { v: "W", label: "Width (W)" }, { v: "H", label: "Height (H)" } ], formulas: { V: "V = L·W·H", L: "L = V/(W·H)", W: "W = V/(L·H)", H: "H = V/(L·W)" }, volume: ({ L, W, H }) => L * W * H, solve: (target, { L, W, H }, V) => { if (target === "L") return V / (W * H); if (target === "W") return V / (L * H); if (target === "H") return V / (L * W); return NaN; }, viz: (vals, unitTxt) => simpleViz("Box", [L = \({unitTxt("L")}, W = \){unitTxt("W")}, H = \({unitTxt("H")}]) }, cylinder: { name: "Cylinder", dims: ["Radius (r)", "Height (h)"], labels: { d1: "Radius (r)", d2: "Height (h)" }, show: { d1: true, d2: true, d3: false, extra: false }, targets: [ { v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" }, { v: "h", label: "Height (h)" } ], formulas: { V: "V = πr²h", r: "r = √(V/(πh))", h: "h = V/(πr²)" }, volume: ({ r, h }) => Math.PI * r * r * h, solve: (target, { r, h }, V) => { if (target === "r") return Math.sqrt(V / (Math.PI * h)); if (target === "h") return V / (Math.PI * r * r); return NaN; }, viz: (vals, unitTxt) => simpleViz("Cylinder", [r = \){unitTxt("r")}, h = \({unitTxt("h")}]) }, sphere: { name: "Sphere", dims: ["Radius (r)"], labels: { d1: "Radius (r)" }, show: { d1: true, d2: false, d3: false, extra: false }, targets: [ { v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" } ], formulas: { V: "V = (4/3)πr³", r: "r = ³√(3V/(4π))" }, volume: ({ r }) => (4 / 3) * Math.PI * Math.pow(r, 3), solve: (target, { r }, V) => { if (target === "r") return Math.cbrt((3 * V) / (4 * Math.PI)); return NaN; }, viz: (vals, unitTxt) => simpleViz("Sphere", [r = \){unitTxt("r")}]) }, cone: { name: "Cone", dims: ["Radius (r)", "Height (h)"], labels: { d1: "Radius (r)", d2: "Height (h)" }, show: { d1: true, d2: true, d3: false, extra: false }, targets: [ { v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" }, { v: "h", label: "Height (h)" } ], formulas: { V: "V = (1/3)πr²h", r: "r = √(3V/(πh))", h: "h = 3V/(πr²)" }, volume: ({ r, h }) => (1 / 3) * Math.PI * r * r * h, solve: (target, { r, h }, V) => { if (target === "r") return Math.sqrt((3 * V) / (Math.PI * h)); if (target === "h") return (3 * V) / (Math.PI * r * r); return NaN; }, viz: (vals, unitTxt) => simpleViz("Cone", [r = \({unitTxt("r")}, h = \){unitTxt("h")}]) }, pyramid: { name: "Pyramid (rectangular base)", dims: ["Base length (L)", "Base width (W)", "Height (H)"], labels: { d1: "Base length (L)", d2: "Base width (W)", d3: "Height (H)" }, show: { d1: true, d2: true, d3: true, extra: false }, targets: [ { v: "V", label: "Volume (V)" }, { v: "L", label: "Base length (L)" }, { v: "W", label: "Base width (W)" }, { v: "H", label: "Height (H)" } ], formulas: { V: "V = (1/3)L·W·H", L: "L = 3V/(W·H)", W: "W = 3V/(L·H)", H: "H = 3V/(L·W)" }, volume: ({ L, W, H }) => (1 / 3) * L * W * H, solve: (target, { L, W, H }, V) => { if (target === "L") return (3 * V) / (W * H); if (target === "W") return (3 * V) / (L * H); if (target === "H") return (3 * V) / (L * W); return NaN; }, viz: (vals, unitTxt) => simpleViz("Pyramid", [L = \({unitTxt("L")}, W = \){unitTxt("W")}, H = \({unitTxt("H")}]) }, triPrism: { name: "Triangular prism (right)", // d1 = base b, d2 = triangle height ht, d3 = prism length L labels: { d1: "Triangle base (b)", d2: "Triangle height (h)", d3: "Prism length (L)" }, show: { d1: true, d2: true, d3: true, extra: false }, targets: [ { v: "V", label: "Volume (V)" }, { v: "b", label: "Triangle base (b)" }, { v: "h", label: "Triangle height (h)" }, { v: "L", label: "Prism length (L)" } ], formulas: { V: "V = (1/2)·b·h·L", b: "b = 2V/(h·L)", h: "h = 2V/(b·L)", L: "L = 2V/(b·h)" }, volume: ({ b, h, L }) => 0.5 * b * h * L, solve: (target, { b, h, L }, V) => { if (target === "b") return (2 * V) / (h * L); if (target === "h") return (2 * V) / (b * L); if (target === "L") return (2 * V) / (b * h); return NaN; }, viz: (vals, unitTxt) => simpleViz("Triangular prism", [b = \){unitTxt("b")}, h = \({unitTxt("h")}, L = \){unitTxt("L")}]) } }; // ---------- UI helpers ---------- const clearOutput = () => { \(("#result").innerHTML = ""; \)("#viz").innerHTML = ""; \(("#steps").innerHTML = ""; \)("#noResultsHint").style.display = "block"; }; const showError = (msg) => { \(("#noResultsHint").style.display = "none"; \)("#result").innerHTML =
⚠️ \({esc(msg)}
; \)("#viz").innerHTML = ""; \(("#steps").innerHTML = ""; }; const setDisabled = (el, isDisabled, placeholder) => { if (!el) return; el.disabled = !!isDisabled; el.style.opacity = isDisabled ? "0.65" : "1"; el.style.pointerEvents = isDisabled ? "none" : "auto"; if (placeholder != null) el.placeholder = placeholder; }; const setWrapVisible = (wrap, yes) => { if (!wrap) return; wrap.style.display = yes ? "flex" : "none"; }; // Simple visual: a clean “card” with labels + tiny bar-ish hint const simpleViz = (title, lines) => { const safe = lines.map(x =>
\){esc(x)} ).join(""); return
Quick visual
; }; const barViz = (labelA, a, labelB, b) => { const A = Number.isFinite(a) ? Math.abs(a) : 0; const B = Number.isFinite(b) ? Math.abs(b) : 0; const max = Math.max(A, B, 1e-12); const wa = clamp((A / max) * 100, 0, 100); const wb = clamp((B / max) * 100, 0, 100); return
Magnitude compare
\({esc(labelA)}: \){esc(fmt(a, 6))}
\){esc(labelB)}: \({esc(fmt(b, 6))}
; }; // ---------- Map UI dims to named dims per shape ---------- const getShapeKey = () => \(("#shape").value || "cube"; const getTargetKey = () => \)("#target").value || "V"; const getLenUnit = () => \(("#lenUnit").value || "m"; const getVolUnit = () => \)("#volUnit").value || "m3"; // For composite B const getShapeKeyB = () => \(("#shapeB").value || "rectPrism"; const getTargetKeyB = () => \)("#targetB").value || "V"; const readDimsSI = (shapeKey, prefix = "") => { const lenUnit = getLenUnit(); const toM = LEN_TO_M[lenUnit] ?? 1; const d1 = num(\((#\){prefix}d1).value); const d2 = num(\((#\){prefix}d2).value); const d3 = num(\((#\){prefix}d3).value); const extra = num(\((#\){prefix}extra).value); // Convert to meters (when present) const d1m = Number.isFinite(d1) ? d1 * toM : NaN; const d2m = Number.isFinite(d2) ? d2 * toM : NaN; const d3m = Number.isFinite(d3) ? d3 * toM : NaN; const extram = Number.isFinite(extra) ? extra * toM : NaN; // Map to named dims const s = SHAPES[shapeKey]; if (!s) return {}; const out = {}; // Shape-specific mapping based on target names in formulas // cube: a=d1 // rectPrism/pyramid: L=d1,W=d2,H=d3 // cylinder/cone: r=d1,h=d2 // sphere: r=d1 // triPrism: b=d1,h=d2,L=d3 if (shapeKey === "cube") out.a = d1m; if (shapeKey === "rectPrism" || shapeKey === "pyramid") { out.L = d1m; out.W = d2m; out.H = d3m; } if (shapeKey === "cylinder" || shapeKey === "cone") { out.r = d1m; out.h = d2m; } if (shapeKey === "sphere") out.r = d1m; if (shapeKey === "triPrism") { out.b = d1m; out.h = d2m; out.L = d3m; } // Extra reserved if you expand later out._extra = extram; return out; }; const readVolumeInM3 = (prefix = "") => { const vu = getVolUnit(); const Vin = num(\((#\){prefix}vIn).value); if (!Number.isFinite(Vin)) return NaN; const f = VOL_TO_M3[vu] ?? 1; return Vin * f; }; // ---------- UI sync for a shape panel ---------- const fillTargets = (shapeKey, targetSel) => { const s = SHAPES[shapeKey]; if (!s) return; const current = targetSel.value; targetSel.innerHTML = s.targets.map(t =>
\){esc(t.label)} ).join(""); // preserve if possible if (s.targets.some(t => t.v === current)) targetSel.value = current; else targetSel.value = "V"; }; const applyShapeUI = (shapeKey, targetKey, prefix = "") => { const s = SHAPES[shapeKey]; if (!s) return; // Labels \((#\){prefix}d1Label).textContent = s.labels?.d1 || "Dimension 1"; \((#\){prefix}d2Label).textContent = s.labels?.d2 || "Dimension 2"; \((#\){prefix}d3Label).textContent = s.labels?.d3 || "Dimension 3"; // Show/hide dimension fields setWrapVisible(\((#\){prefix}d1Wrap), !!s.show.d1); setWrapVisible(\((#\){prefix}d2Wrap), !!s.show.d2); setWrapVisible(\((#\){prefix}d3Wrap), !!s.show.d3); // Extra (unused in current set) const extraWrap = \((#\){prefix}extraWrap); if (extraWrap) extraWrap.style.display = s.show.extra ? "flex" : "none"; // Enable/disable based on target const d1 = \((#\){prefix}d1); const d2 = \((#\){prefix}d2); const d3 = \((#\){prefix}d3); const vIn = \((#\){prefix}vIn); const vInWrap = \((#\){prefix}vInWrap); // default enable everything shown if (d1) setDisabled(d1, false); if (d2) setDisabled(d2, false); if (d3) setDisabled(d3, false); if (vIn) setDisabled(vIn, false); // When target is Volume: volume input computed, dimension inputs required // When target is a dimension: volume input required, that dimension computed if (targetKey === "V") { if (vInWrap) vInWrap.style.display = "none"; if (vIn) vIn.value = ""; // all shown dims enabled } else { if (vInWrap) vInWrap.style.display = "flex"; // Disable the dimension being solved for const disableByTarget = () => { if (shapeKey === "cube" && targetKey === "a") setDisabled(d1, true, "Computed"); if ((shapeKey === "rectPrism" || shapeKey === "pyramid") && targetKey === "L") setDisabled(d1, true, "Computed"); if ((shapeKey === "rectPrism" || shapeKey === "pyramid") && targetKey === "W") setDisabled(d2, true, "Computed"); if ((shapeKey === "rectPrism" || shapeKey === "pyramid") && targetKey === "H") setDisabled(d3, true, "Computed"); if ((shapeKey === "cylinder" || shapeKey === "cone") && targetKey === "r") setDisabled(d1, true, "Computed"); if ((shapeKey === "cylinder" || shapeKey === "cone") && targetKey === "h") setDisabled(d2, true, "Computed"); if (shapeKey === "sphere" && targetKey === "r") setDisabled(d1, true, "Computed"); if (shapeKey === "triPrism" && targetKey === "b") setDisabled(d1, true, "Computed"); if (shapeKey === "triPrism" && targetKey === "h") setDisabled(d2, true, "Computed"); if (shapeKey === "triPrism" && targetKey === "L") setDisabled(d3, true, "Computed"); }; disableByTarget(); } }; const syncPrimaryUI = () => { const shapeKey = getShapeKey(); fillTargets(shapeKey, \(("#target")); const targetKey = getTargetKey(); \)("#shapeHint").textContent = (targetKey === "V") ? "Tip: Enter dimensions to get volume + automatic equivalents." : "Tip: Enter the known volume and the other dimensions — we’ll compute the missing one."; applyShapeUI(shapeKey, targetKey, ""); clearOutput(); }; const syncBUI = () => { const shapeKey = getShapeKeyB(); fillTargets(shapeKey, \(("#targetB")); applyShapeUI(shapeKey, getTargetKeyB(), "b_"); clearOutput(); }; const syncStretchPanels = () => { \)("#compositePanel").style.display = \(("#compositeOn").checked ? "block" : "none"; \)("#densityPanel").style.display = \(("#densityOn").checked ? "block" : "none"; clearOutput(); }; // ---------- Validation + computation helpers ---------- const requireFinite = (val, msg) => { if (!Number.isFinite(val)) { showError(msg); return false; } return true; }; const requirePositive = (val, msg) => { if (!(Number.isFinite(val) && val > 0)) { showError(msg); return false; } return true; }; const computeOneShape = (shapeKey, targetKey, prefix = "") => { const s = SHAPES[shapeKey]; if (!s) return { ok: false }; const dims = readDimsSI(shapeKey, prefix); const V_in_m3 = readVolumeInM3(prefix); // Helpers to build readable unit strings in visual (uses selected units) const lenUnit = getLenUnit(); const unitTxt = (sym) => { // sym maps to dimension names; we read SI dims and convert back to chosen length unit for display const toM = LEN_TO_M[lenUnit] ?? 1; const fromM = 1 / toM; const x = dims[sym]; return \){fmt(x * fromM, 6)} \({lenUnit}; }; // Validate required fields const validateDimsForVolume = () => { if (shapeKey === "cube") return requirePositive(dims.a, "Please enter a valid edge length (a)."); if (shapeKey === "rectPrism" || shapeKey === "pyramid") { return requirePositive(dims.L, "Please enter a valid length (L).") && requirePositive(dims.W, "Please enter a valid width (W).") && requirePositive(dims.H, "Please enter a valid height (H)."); } if (shapeKey === "cylinder" || shapeKey === "cone") { return requirePositive(dims.r, "Please enter a valid radius (r).") && requirePositive(dims.h, "Please enter a valid height (h)."); } if (shapeKey === "sphere") return requirePositive(dims.r, "Please enter a valid radius (r)."); if (shapeKey === "triPrism") { return requirePositive(dims.b, "Please enter a valid triangle base (b).") && requirePositive(dims.h, "Please enter a valid triangle height (h).") && requirePositive(dims.L, "Please enter a valid prism length (L)."); } return true; }; const validateForSolveDim = () => { if (!requirePositive(V_in_m3, "Please enter a valid known volume (V).")) return false; // Need all dims except the target if (shapeKey === "cube") { // solving a only needs V return true; } if (shapeKey === "sphere") { // solving r only needs V return true; } if (shapeKey === "cylinder" || shapeKey === "cone") { if (targetKey === "r") return requirePositive(dims.h, "Please enter a valid height (h)."); if (targetKey === "h") return requirePositive(dims.r, "Please enter a valid radius (r)."); } if (shapeKey === "rectPrism" || shapeKey === "pyramid") { if (targetKey === "L") return requirePositive(dims.W, "Please enter a valid width (W).") && requirePositive(dims.H, "Please enter a valid height (H)."); if (targetKey === "W") return requirePositive(dims.L, "Please enter a valid length (L).") && requirePositive(dims.H, "Please enter a valid height (H)."); if (targetKey === "H") return requirePositive(dims.L, "Please enter a valid length (L).") && requirePositive(dims.W, "Please enter a valid width (W)."); } if (shapeKey === "triPrism") { if (targetKey === "b") return requirePositive(dims.h, "Please enter a valid triangle height (h).") && requirePositive(dims.L, "Please enter a valid prism length (L)."); if (targetKey === "h") return requirePositive(dims.b, "Please enter a valid triangle base (b).") && requirePositive(dims.L, "Please enter a valid prism length (L)."); if (targetKey === "L") return requirePositive(dims.b, "Please enter a valid triangle base (b).") && requirePositive(dims.h, "Please enter a valid triangle height (h)."); } return true; }; if (targetKey === "V") { if (!validateDimsForVolume()) return { ok: false }; const V = s.volume(dims); if (!Number.isFinite(V) || V <= 0) { showError("Inputs produce an invalid volume. Please check values."); return { ok: false }; } return { ok: true, kind: "volume", shapeKey, targetKey, V_m3: V, dims, formula: s.formulas.V, vizHtml: s.viz?.(dims, unitTxt) || "" }; } else { if (!validateForSolveDim()) return { ok: false }; const x_m = s.solve(targetKey, dims, V_in_m3); if (!Number.isFinite(x_m) || x_m <= 0) { showError("Inputs produce an invalid dimension. Check volume and other dimensions."); return { ok: false }; } return { ok: true, kind: "dimension", shapeKey, targetKey, V_m3: V_in_m3, x_m, dims, formula: s.formulas[targetKey] || "", vizHtml: s.viz?.(dims, unitTxt) || "" }; } }; const computeMassFromDensity = (V_m3) => { const rhoRaw = num(\)("#rhoVal").value); if (!Number.isFinite(rhoRaw) || rhoRaw <= 0) return { ok: false, err: "Please enter a valid density (ρ > 0)." }; const rhoUnit = \(("#rhoUnit").value; const rho = rhoRaw * (RHO_TO_KGM3[rhoUnit] ?? 1); // kg/m^3 const m_kg = rho * V_m3; return { ok: true, rho, m_kg }; }; // ---------- Rendering ---------- const renderVolumeResult = (label, V_m3, extraBlockHtml = "") => { const outUnit = getVolUnit(); const Vout = m3ToUnit(V_m3, outUnit); const eq = equivalents(V_m3); // choose 3 best equivalents depending on scale const eqLines = [
\){esc(fmt(eq.L, 6))} L,
\({esc(fmt(eq.mL, 6))} mL,
\){esc(fmt(eq.cm3, 6))} cm³,
\({esc(fmt(eq.ft3, 6))} ft³,
\){esc(fmt(eq.gal, 6))} US gal ]; \(("#noResultsHint").style.display = "none"; \)("#result").innerHTML =
\({esc(label)}
Volume: \){esc(fmt(Vout, 8))} \({esc(unitLabel(outUnit))}
Helpful equivalents: \){eqLines.join(" ")}
\({extraBlockHtml}
; }; const renderDimensionResult = (shapeKey, targetKey, x_m, V_m3, extraBlockHtml = "") => { const lenUnit = getLenUnit(); const fromM = 1 / (LEN_TO_M[lenUnit] ?? 1); const x_out = x_m * fromM; const niceName = { a: "Edge (a)", L: "Length (L)", W: "Width (W)", H: "Height (H)", r: "Radius (r)", h: "Height (h)", b: "Triangle base (b)" }[targetKey] || "Dimension"; \)("#noResultsHint").style.display = "none"; \(("#result").innerHTML =
Solved dimension
\){esc(niceName)}: \({esc(fmt(x_out, 8))} \){esc(lenUnit)}
Given volume: \({esc(fmt(m3ToUnit(V_m3, getVolUnit()), 8))} \){esc(unitLabel(getVolUnit()))}
\({extraBlockHtml}
; }; const renderSteps = (blocks) => { const showSteps = \)("#showSteps").checked; if (!showSteps) { \(("#steps").innerHTML = ""; return; } const html = blocks.map(b =>
\){esc(b.title)}
\({b.items.map(li => \){li} ).join("")} ).join(""); \(("#steps").innerHTML = html; }; const renderViz = (html) => { const showViz = \)("#showViz").checked; \(("#viz").innerHTML = showViz ? html : ""; }; // ---------- Main compute ---------- const compute = () => { clearOutput(); try { const a = computeOneShape(getShapeKey(), getTargetKey(), ""); if (!a.ok) return; let Vtotal_m3 = NaN; let V1_m3 = NaN; let V2_m3 = NaN; const compositeOn = \)("#compositeOn").checked; if (a.kind === "volume") V1_m3 = a.V_m3; else V1_m3 = a.V_m3; // if solving a dim, V is given; still usable if (compositeOn) { const b = computeOneShape(getShapeKeyB(), getTargetKeyB(), "b_"); if (!b.ok) return; V2_m3 = (b.kind === "volume") ? b.V_m3 : b.V_m3; const op = \(("#compOp").value; Vtotal_m3 = (op === "sub") ? (V1_m3 - V2_m3) : (V1_m3 + V2_m3); if (!(Number.isFinite(Vtotal_m3) && Vtotal_m3 > 0)) { return showError("Composite volume is not positive. If subtracting, ensure V₁ > V₂."); } } else { Vtotal_m3 = V1_m3; } // Density block (mass) let massBlockHtml = ""; let massSteps = null; if (\)("#densityOn").checked) { const m = computeMassFromDensity(Vtotal_m3); if (!m.ok) return showError(m.err); const m_g = m.m_kg * 1000; const m_lb = m.m_kg / 0.45359237; massBlockHtml =
Mass from density:
ρ = \({esc(fmt(m.rho, 6))} kg/m³
m = \){esc(fmt(m.m_kg, 8))} kg
= \({esc(fmt(m_g, 8))} g • \){esc(fmt(m_lb, 8))} lb
; massSteps = { title: "Mass from density (optional)", items: [ Convert density to SI:
ρ in kg/m³, Compute mass:
m = ρ·V , Convert mass to g and lb (optional) ] }; } // Output: // - If target is dimension -> show dimension result, but also show (composite) volume context. // - If target is volume -> show volume result. const targetA = getTargetKey(); const outUnit = getVolUnit(); if (targetA === "V") { // Show composite volume if enabled, otherwise V1 const label = compositeOn ? "Composite volume (Vtotal)" : Volume (\({SHAPES[getShapeKey()].name}); renderVolumeResult(label, Vtotal_m3, massBlockHtml); } else { // Dimension result uses A's dimension x_m, but show that volume context is composite total renderDimensionResult(getShapeKey(), targetA, a.x_m, a.V_m3, compositeOn ?
Composite total volume: \){esc(fmt(m3ToUnit(Vtotal_m3, outUnit), 8))} \({esc(unitLabel(outUnit))}
: "" + massBlockHtml ); // If composite is on AND density is on, we already included massBlockHtml. Ensure it appears: if (compositeOn && \)("#densityOn").checked && !\(("#result").innerHTML.includes("Mass from density")) { // Safe append \)("#result").innerHTML = \(("#result").innerHTML.replace("
", \){massBlockHtml}); } } // Visuals let vizHtml = ""; if (\(("#showViz").checked) { vizHtml += a.vizHtml || ""; if (compositeOn) { vizHtml +=
Composite mode: we combine volumes using Vtotal = V₁ ± V₂.
; vizHtml += barViz("|V₁| (m³)", V1_m3, "|V₂| (m³)", V2_m3); vizHtml += barViz("|Vtotal| (m³)", Vtotal_m3, "L equivalent", m3ToUnit(Vtotal_m3, "L")); } else { vizHtml += barViz("V (m³)", Vtotal_m3, "L equivalent", m3ToUnit(Vtotal_m3, "L")); } } renderViz(vizHtml); // Steps const lenUnit = getLenUnit(); const stepsBlocks = []; // Step block for A const shapeA = SHAPES[getShapeKey()]; if (targetA === "V") { stepsBlocks.push({ title: "Step-by-step (Shape A)", items: [ Convert all dimensions to meters (
m )., Use formula:
\){esc(shapeA.formulas.V)} ., Compute volume in
m³ , then convert to your chosen unit (\({esc(unitLabel(outUnit))})., Show helpful equivalents (L, mL, cm³, etc.). ] }); } else { stepsBlocks.push({ title: "Step-by-step (Solve for missing dimension — Shape A)", items: [ Convert volume to
m³ and other dimensions to meters., Rearrange formula:
\){esc(shapeA.formulas[targetA] || "")} ., Compute the missing dimension in meters, then convert to \({esc(lenUnit)}. ] }); } // Composite steps if (compositeOn) { stepsBlocks.push({ title: "Composite volume (optional)", items: [ Compute
V₁ from Shape A and
V₂ from Shape B., Combine:
Vtotal = V₁ ± V₂ ., Ensure the total is positive (especially when subtracting). ] }); } if (massSteps) stepsBlocks.push(massSteps); renderSteps(stepsBlocks); } catch (e) { showError("Something went wrong. Please check inputs and try again."); } }; // ---------- Presets ---------- const setModeA = (shapeKey, targetKey) => { \)("#shape").value = shapeKey; fillTargets(shapeKey, \(("#target")); \)("#target").value = targetKey; applyShapeUI(shapeKey, targetKey, ""); }; const setModeB = (shapeKey, targetKey) => { \(("#shapeB").value = shapeKey; fillTargets(shapeKey, \)("#targetB")); \(("#targetB").value = targetKey; applyShapeUI(shapeKey, targetKey, "b_"); }; const clearFields = () => { ["d1","d2","d3","extra","vIn","b_d1","b_d2","b_d3","b_extra","b_vIn","rhoVal"].forEach(id => { const el = \)(#\({id}); if (el) el.value = ""; }); }; const applyPreset = (key) => { // reset toggles \)("#compositeOn").checked = false; \(("#densityOn").checked = false; syncStretchPanels(); clearFields(); \)("#lenUnit").value = "cm"; \(("#volUnit").value = "L"; if (key === "cube10cm") { setModeA("cube", "V"); \)("#d1").value = "10"; \(("#lenUnit").value = "cm"; \)("#volUnit").value = "cm3"; } if (key === "aquarium") { setModeA("rectPrism", "V"); \(("#d1").value = "60"; \)("#d2").value = "30"; \(("#d3").value = "40"; \)("#lenUnit").value = "cm"; \(("#volUnit").value = "L"; } if (key === "cylBeaker") { // Solve for height h given V=250 mL and r=3.5 cm setModeA("cylinder", "h"); \)("#lenUnit").value = "cm"; \(("#volUnit").value = "mL"; \)("#d1").value = "3.5"; // radius \(("#vIn").value = "250"; // mL } if (key === "sphere8in") { // diameter 8 in => radius 4 in setModeA("sphere", "V"); \)("#lenUnit").value = "in"; \(("#volUnit").value = "in3"; \)("#d1").value = "4"; } if (key === "holeInBlock") { // Block 10x8x6 cm minus cylinder hole r=1 cm, h=6 cm \(("#compositeOn").checked = true; syncStretchPanels(); \)("#compOp").value = "sub"; setModeA("rectPrism", "V"); \(("#lenUnit").value = "cm"; \)("#volUnit").value = "cm3"; \(("#d1").value = "10"; \)("#d2").value = "8"; \(("#d3").value = "6"; setModeB("cylinder", "V"); \)("#b_d1").value = "1"; \(("#b_d2").value = "6"; } if (key === "massWater") { // 500 mL water, compute mass: V is given -> easiest: use cube solve? We'll treat as "known volume" and any shape isn't needed: // We'll do cylinder with radius 5 cm and solve for height from V=500 mL, then compute mass with rho=1 g/mL. \)("#densityOn").checked = true; syncStretchPanels(); setModeA("cylinder", "h"); \(("#lenUnit").value = "cm"; \)("#volUnit").value = "mL"; \(("#d1").value = "5"; // r \)("#vIn").value = "500"; // mL \(("#rhoVal").value = "1"; \)("#rhoUnit").value = "gml"; } clearOutput(); compute(); }; // ---------- Wiring ---------- const wire = () => { // init targets fillTargets(getShapeKey(), \(("#target")); fillTargets(getShapeKeyB(), \)("#targetB")); applyShapeUI(getShapeKey(), getTargetKey(), ""); applyShapeUI(getShapeKeyB(), getTargetKeyB(), "b_"); syncStretchPanels(); clearOutput(); \(("#shape").addEventListener("change", syncPrimaryUI); \)("#target").addEventListener("change", syncPrimaryUI); \(("#shapeB").addEventListener("change", syncBUI); \)("#targetB").addEventListener("change", syncBUI); \(("#lenUnit").addEventListener("change", () => { syncPrimaryUI(); if (\)("#compositeOn").checked) syncBUI(); }); \(("#volUnit").addEventListener("change", () => { syncPrimaryUI(); if (\)("#compositeOn").checked) syncBUI(); }); \(("#compositeOn").addEventListener("change", syncStretchPanels); \)("#densityOn").addEventListener("change", syncStretchPanels); \(("#compOp").addEventListener("change", clearOutput); // inputs clear on typing [ "#d1","#d2","#d3","#extra","#vIn", "#b_d1","#b_d2","#b_d3","#b_extra","#b_vIn", "#rhoVal","#rhoUnit" ].forEach(sel => { const el = \)(sel); if (!el) return; el.addEventListener("input", clearOutput); el.addEventListener("change", clearOutput); el.addEventListener("keydown", (e) => { if (e.key === "Enter") compute(); }); }); \(("#calculateButton").addEventListener("click", compute); \)("#resetButton").addEventListener("click", () => { // basic defaults \(("#shape").value = "cube"; fillTargets("cube", \)("#target")); \(("#target").value = "V"; \)("#lenUnit").value = "m"; \(("#volUnit").value = "m3"; \)("#compositeOn").checked = false; \(("#densityOn").checked = false; \)("#compOp").value = "add"; \(("#shapeB").value = "rectPrism"; fillTargets("rectPrism", \)("#targetB")); \(("#targetB").value = "V"; \)("#rhoUnit").value = "kgm3"; clearFields(); syncPrimaryUI(); syncBUI(); syncStretchPanels(); \(("#showSteps").checked = true; \)("#showViz").checked = true; clearOutput(); }); // quick picks \]("button.chip").forEach(btn => { btn.addEventListener("click", () => { const p = btn.getAttribute("data-preset"); if (p) applyPreset(p); }); }); // enter in form \(("#volForm").addEventListener("keydown", (e) => { if (e.key === "Enter") compute(); }); }; if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", wire); else wire(); })(); Please provide updated full htaml and js
ChatGPT said:
Thought for 39s
How to use this calculator
Choose a shape and what you want to solve for (volume or a dimension).
Pick your length unit and a volume output unit (we show equivalents too).
Optional stretch features: enable Composite (add/subtract) and/or Mass from density .
Click Calculate to get the answer + optional steps + quick visual (drag shapes in composite mode).
Formulas & Equations Used
Example Problem & Step-by-Step Solution
Example 1 — Aquarium volume
Box dimensions: L=60 cm, W=30 cm, H=40 cm
Compute volume: V = L·W·H = 60·30·40 = 72,000 cm³
Convert: 72,000 cm³ = 72,000 mL = 72 L
Example 2 — Beaker height from volume (cylinder)
Given: V = 250 mL , r = 3.5 cm
Convert volume: 250 mL = 250 cm³
Rearrange: h = V/(πr²)
Compute: h = 250 /(π·3.5²) ≈ 6.50 cm
Example 3 — Tube (hollow cylinder) volume
Given: outer radius R=2.0 cm , inner radius r=1.5 cm , height h=30 cm
Use: V = πh(R² − r²)
Compute: V = π·30·(2² − 1.5²) = π·30·(4 − 2.25) = π·52.5 ≈ 164.9 cm³
Convert: 164.9 cm³ ≈ 164.9 mL
Frequently Asked Questions
Q: Is cm³ the same as mL?
Yes. 1 cm³ = 1 mL .
Q: Can I solve for height or radius instead of volume?
Yes—use the Solve for dropdown to compute a missing dimension (supported shapes will show options).
Q: What does “composite volume” mean?
It means adding or subtracting volumes (like a block with a drilled hole).
/* ✅ Production-ready JS (updated): Volume Calculator (More shapes + mini SVG diagrams + swap/move shapes + extra fields) */
(function () {
const NS = "volumeCalc_v2";
if (window[NS]?.isInitialized) return;
window[NS] = { isInitialized: true };
const \) = (sel, root = document) => root.querySelector(sel);
const \[ = (sel, root = document) => Array.from(root.querySelectorAll(sel));
const esc = (s) => (s ?? "").toString()
.replace(/&/g, "&").replace(//g, ">")
.replace(/"/g, """).replace(/'/g, "'");
const num = (v) => {
const t = (v ?? "").toString().trim().replace(/,/g, "");
if (!t) return NaN;
const x = Number(t);
return Number.isFinite(x) ? x : NaN;
};
const fmt = (x, dp = 8) => {
if (!Number.isFinite(x)) return "";
const ax = Math.abs(x);
if (ax !== 0 && (ax >= 1e7 || ax < 1e-6)) return x.toExponential(6);
const s = x.toFixed(dp);
return s.replace(/(\.\d*?[1-9])0+\(/,"\)1").replace(/\.0+\(/,"");
};
const clamp = (x, a, b) => Math.min(b, Math.max(a, x));
// ---------- Unit conversions ----------
const LEN_TO_M = { m: 1, cm: 0.01, mm: 0.001, in: 0.0254, ft: 0.3048 };
const VOL_TO_M3 = {
m3: 1,
L: 1e-3,
mL: 1e-6,
cm3: 1e-6,
mm3: 1e-9,
in3: Math.pow(0.0254, 3),
ft3: Math.pow(0.3048, 3),
galUS: 0.003785411784
};
const RHO_TO_KGM3 = { kgm3: 1, gcm3: 1000, gml: 1000, lbft3: 16.01846337 };
const m3ToUnit = (V_m3, unit) => {
const f = VOL_TO_M3[unit] ?? 1;
return V_m3 / f;
};
const unitLabel = (u) => ({
m3: "m³", L: "L", mL: "mL", cm3: "cm³", mm3: "mm³", in3: "in³", ft3: "ft³", galUS: "US gal"
}[u] || u);
const equivalents = (V_m3) => {
const L = m3ToUnit(V_m3, "L");
const mL = m3ToUnit(V_m3, "mL");
const cm3 = m3ToUnit(V_m3, "cm3");
const ft3 = m3ToUnit(V_m3, "ft3");
const gal = m3ToUnit(V_m3, "galUS");
return { L, mL, cm3, ft3, gal };
};
// ---------- Shape mini SVGs ----------
const svgWrap = (inner) => `
Shape preview
A tiny diagram to help you sanity-check inputs.
\){inner}
`;
const shapeSVG = (key) => {
// Clean, minimal strokes; no external CSS assumptions.
const common = `fill="none" stroke="rgba(255,255,255,.78)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"`;
const faint = `stroke="rgba(255,255,255,.35)"`;
const base = (body) => `
\({body}
`;
const map = {
cube: base(`
`),
rectPrism: base(`
`),
sphere: base(`
`),
hemisphere: base(`
`),
sphericalCap: base(`
`),
ellipsoid: base(`
`),
cylinder: base(`
`),
hollowCylinder: base(`
`),
capsule: base(`
`),
cone: base(`
`),
conicalFrustum: base(`
`),
triPrism: base(`
`),
pyramid: base(`
`),
truncatedPyramid: base(`
`)
};
return map[key] || map.cube;
};
const renderShapePreview = () => {
const key = getShapeKey();
\)("#shapePreview").innerHTML = svgWrap(shapeSVG(key));
};
// ---------- Shape definitions ----------
// d1,d2,d3 + extra, extra2, extra3 (A & B each)
const SHAPES = {
cube: {
name: "Cube",
labels: { d1: "Edge length (a)" },
show: { d1: true, d2: false, d3: false, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "a", label: "Edge (a)" }],
formulas: { V: "V = a³", a: "a = ³√V" },
volume: ({ a }) => Math.pow(a, 3),
solve: (target, dims, V) => (target === "a" ? Math.cbrt(V) : NaN),
vizKey: "cube",
map: (raw) => ({ a: raw.d1 })
},
rectPrism: {
name: "Rectangular prism",
labels: { d1: "Length (L)", d2: "Width (W)", d3: "Height (H)" },
show: { d1: true, d2: true, d3: true, extra: false, extra2: false, extra3: false },
targets: [
{ v: "V", label: "Volume (V)" },
{ v: "L", label: "Length (L)" },
{ v: "W", label: "Width (W)" },
{ v: "H", label: "Height (H)" }
],
formulas: { V: "V = L·W·H", L: "L = V/(W·H)", W: "W = V/(L·H)", H: "H = V/(L·W)" },
volume: ({ L, W, H }) => L * W * H,
solve: (target, { L, W, H }, V) => {
if (target === "L") return V / (W * H);
if (target === "W") return V / (L * H);
if (target === "H") return V / (L * W);
return NaN;
},
vizKey: "rectPrism",
map: (raw) => ({ L: raw.d1, W: raw.d2, H: raw.d3 })
},
sphere: {
name: "Sphere",
labels: { d1: "Radius (r)" },
show: { d1: true, d2: false, d3: false, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" }],
formulas: { V: "V = (4/3)πr³", r: "r = ³√(3V/(4π))" },
volume: ({ r }) => (4 / 3) * Math.PI * Math.pow(r, 3),
solve: (target, { r }, V) => (target === "r" ? Math.cbrt((3 * V) / (4 * Math.PI)) : NaN),
vizKey: "sphere",
map: (raw) => ({ r: raw.d1 })
},
hemisphere: {
name: "Hemisphere",
labels: { d1: "Radius (r)" },
show: { d1: true, d2: false, d3: false, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" }],
formulas: { V: "V = (2/3)πr³", r: "r = ³√(3V/(2π))" },
volume: ({ r }) => (2 / 3) * Math.PI * Math.pow(r, 3),
solve: (target, { r }, V) => (target === "r" ? Math.cbrt((3 * V) / (2 * Math.PI)) : NaN),
vizKey: "hemisphere",
map: (raw) => ({ r: raw.d1 })
},
sphericalCap: {
name: "Spherical cap",
labels: { d1: "Sphere radius (R)", d2: "Cap height (h)" },
show: { d1: true, d2: true, d3: false, extra: false, extra2: false, extra3: false },
// solving for h requires a cubic; keep it clean: volume only
targets: [{ v: "V", label: "Volume (V)" }],
formulas: { V: "V = (1/3)πh²(3R − h)" },
volume: ({ R, h }) => (1 / 3) * Math.PI * h * h * (3 * R - h),
solve: () => NaN,
vizKey: "sphericalCap",
map: (raw) => ({ R: raw.d1, h: raw.d2 })
},
ellipsoid: {
name: "Ellipsoid",
labels: { d1: "Semi-axis a", d2: "Semi-axis b", d3: "Semi-axis c" },
show: { d1: true, d2: true, d3: true, extra: false, extra2: false, extra3: false },
targets: [
{ v: "V", label: "Volume (V)" },
{ v: "a", label: "Solve a" },
{ v: "b", label: "Solve b" },
{ v: "c", label: "Solve c" }
],
formulas: { V: "V = (4/3)πabc", a: "a = 3V/(4πbc)", b: "b = 3V/(4πac)", c: "c = 3V/(4πab)" },
volume: ({ a, b, c }) => (4 / 3) * Math.PI * a * b * c,
solve: (target, { a, b, c }, V) => {
if (target === "a") return (3 * V) / (4 * Math.PI * b * c);
if (target === "b") return (3 * V) / (4 * Math.PI * a * c);
if (target === "c") return (3 * V) / (4 * Math.PI * a * b);
return NaN;
},
vizKey: "ellipsoid",
map: (raw) => ({ a: raw.d1, b: raw.d2, c: raw.d3 })
},
cylinder: {
name: "Cylinder",
labels: { d1: "Radius (r)", d2: "Height (h)" },
show: { d1: true, d2: true, d3: false, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" }, { v: "h", label: "Height (h)" }],
formulas: { V: "V = πr²h", r: "r = √(V/(πh))", h: "h = V/(πr²)" },
volume: ({ r, h }) => Math.PI * r * r * h,
solve: (target, { r, h }, V) => {
if (target === "r") return Math.sqrt(V / (Math.PI * h));
if (target === "h") return V / (Math.PI * r * r);
return NaN;
},
vizKey: "cylinder",
map: (raw) => ({ r: raw.d1, h: raw.d2 })
},
hollowCylinder: {
name: "Hollow cylinder / tube",
labels: { d1: "Outer radius (R)", d2: "Inner radius (r)", d3: "Height (h)" },
show: { d1: true, d2: true, d3: true, extra: false, extra2: false, extra3: false },
targets: [
{ v: "V", label: "Volume (V)" },
{ v: "R", label: "Outer radius (R)" },
{ v: "r", label: "Inner radius (r)" },
{ v: "h", label: "Height (h)" }
],
formulas: { V: "V = πh(R² − r²)", h: "h = V/(π(R² − r²))" },
volume: ({ R, r, h }) => Math.PI * h * (R * R - r * r),
solve: (target, { R, r, h }, V) => {
if (target === "h") return V / (Math.PI * (R * R - r * r));
// Solving for R or r is possible but can surprise users; keep conservative.
return NaN;
},
vizKey: "hollowCylinder",
map: (raw) => ({ R: raw.d1, r: raw.d2, h: raw.d3 })
},
capsule: {
name: "Capsule",
labels: { d1: "Radius (r)", d2: "Cylinder length (L)" },
show: { d1: true, d2: true, d3: false, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "L", label: "Cylinder length (L)" }],
formulas: { V: "V = πr²L + (4/3)πr³", L: "L = (V − (4/3)πr³)/(πr²)" },
volume: ({ r, L }) => (Math.PI * r * r * L) + (4 / 3) * Math.PI * Math.pow(r, 3),
solve: (target, { r, L }, V) => {
if (target === "L") return (V - (4 / 3) * Math.PI * Math.pow(r, 3)) / (Math.PI * r * r);
return NaN;
},
vizKey: "capsule",
map: (raw) => ({ r: raw.d1, L: raw.d2 })
},
cone: {
name: "Cone",
labels: { d1: "Radius (r)", d2: "Height (h)" },
show: { d1: true, d2: true, d3: false, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" }, { v: "h", label: "Height (h)" }],
formulas: { V: "V = (1/3)πr²h", r: "r = √(3V/(πh))", h: "h = 3V/(πr²)" },
volume: ({ r, h }) => (1 / 3) * Math.PI * r * r * h,
solve: (target, { r, h }, V) => {
if (target === "r") return Math.sqrt((3 * V) / (Math.PI * h));
if (target === "h") return (3 * V) / (Math.PI * r * r);
return NaN;
},
vizKey: "cone",
map: (raw) => ({ r: raw.d1, h: raw.d2 })
},
conicalFrustum: {
name: "Conical frustum",
labels: { d1: "Top radius (r)", d2: "Bottom radius (R)", d3: "Height (h)" },
show: { d1: true, d2: true, d3: true, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "h", label: "Height (h)" }],
formulas: { V: "V = (1/3)πh(R² + Rr + r²)", h: "h = 3V/(π(R² + Rr + r²))" },
volume: ({ r, R, h }) => (1 / 3) * Math.PI * h * (R * R + R * r + r * r),
solve: (target, { r, R, h }, V) => {
if (target === "h") return (3 * V) / (Math.PI * (R * R + R * r + r * r));
return NaN;
},
vizKey: "conicalFrustum",
map: (raw) => ({ r: raw.d1, R: raw.d2, h: raw.d3 })
},
triPrism: {
name: "Triangular prism (right)",
labels: { d1: "Triangle base (b)", d2: "Triangle height (h)", d3: "Prism length (L)" },
show: { d1: true, d2: true, d3: true, extra: false, extra2: false, extra3: false },
targets: [
{ v: "V", label: "Volume (V)" },
{ v: "b", label: "Triangle base (b)" },
{ v: "h", label: "Triangle height (h)" },
{ v: "L", label: "Prism length (L)" }
],
formulas: { V: "V = (1/2)·b·h·L", b: "b = 2V/(h·L)", h: "h = 2V/(b·L)", L: "L = 2V/(b·h)" },
volume: ({ b, h, L }) => 0.5 * b * h * L,
solve: (target, { b, h, L }, V) => {
if (target === "b") return (2 * V) / (h * L);
if (target === "h") return (2 * V) / (b * L);
if (target === "L") return (2 * V) / (b * h);
return NaN;
},
vizKey: "triPrism",
map: (raw) => ({ b: raw.d1, h: raw.d2, L: raw.d3 })
},
pyramid: {
name: "Pyramid (rectangular base)",
labels: { d1: "Base length (L)", d2: "Base width (W)", d3: "Height (H)" },
show: { d1: true, d2: true, d3: true, extra: false, extra2: false, extra3: false },
targets: [
{ v: "V", label: "Volume (V)" },
{ v: "L", label: "Base length (L)" },
{ v: "W", label: "Base width (W)" },
{ v: "H", label: "Height (H)" }
],
formulas: { V: "V = (1/3)L·W·H", L: "L = 3V/(W·H)", W: "W = 3V/(L·H)", H: "H = 3V/(L·W)" },
volume: ({ L, W, H }) => (1 / 3) * L * W * H,
solve: (target, { L, W, H }, V) => {
if (target === "L") return (3 * V) / (W * H);
if (target === "W") return (3 * V) / (L * H);
if (target === "H") return (3 * V) / (L * W);
return NaN;
},
vizKey: "pyramid",
map: (raw) => ({ L: raw.d1, W: raw.d2, H: raw.d3 })
},
truncatedPyramid: {
name: "Truncated pyramid",
// Uses 5 dims: base L1,W1, top L2,W2, height H
labels: {
d1: "Base length (L₁)",
d2: "Base width (W₁)",
extra: "Top length (L₂)",
extra2: "Top width (W₂)",
d3: "Height (H)"
},
show: { d1: true, d2: true, d3: true, extra: true, extra2: true, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "H", label: "Height (H)" }],
formulas: { V: "V = (H/3)(A₁ + A₂ + √(A₁A₂))", H: "H = 3V/(A₁ + A₂ + √(A₁A₂))" },
volume: ({ L1, W1, L2, W2, H }) => {
const A1 = L1 * W1;
const A2 = L2 * W2;
return (H / 3) * (A1 + A2 + Math.sqrt(A1 * A2));
},
solve: (target, { L1, W1, L2, W2, H }, V) => {
if (target !== "H") return NaN;
const A1 = L1 * W1;
const A2 = L2 * W2;
return (3 * V) / (A1 + A2 + Math.sqrt(A1 * A2));
},
vizKey: "truncatedPyramid",
map: (raw) => ({ L1: raw.d1, W1: raw.d2, H: raw.d3, L2: raw.extra, W2: raw.extra2 })
}
};
// ---------- UI helpers ----------
const clearOutput = () => {
\(("#result").innerHTML = "";
\)("#viz").innerHTML = "";
\(("#steps").innerHTML = "";
\)("#noResultsHint").style.display = "block";
};
const showError = (msg) => {
\(("#noResultsHint").style.display = "none";
\)("#result").innerHTML = `
⚠️ \({esc(msg)}
`;
\)("#viz").innerHTML = "";
\(("#steps").innerHTML = "";
};
const setDisabled = (el, isDisabled, placeholder) => {
if (!el) return;
el.disabled = !!isDisabled;
el.style.opacity = isDisabled ? "0.65" : "1";
el.style.pointerEvents = isDisabled ? "none" : "auto";
if (placeholder != null) el.placeholder = placeholder;
};
const setWrapVisible = (wrap, yes) => {
if (!wrap) return;
wrap.style.display = yes ? "flex" : "none";
};
// ---------- SVG "move shapes" visual (draggable) ----------
const draggableViz = ({ aKey, bKey, compositeOn }) => {
// If not composite, return a single shape svg
const drawOne = (x, y, key, label) => `
\({esc(label)}
\){shapeSVG(key).replace(/]*>/, "").replace(" ", "")}
`;
const body = compositeOn
? `\({drawOne(110, 70, aKey, "Shape A")} \){drawOne(330, 70, bKey, "Shape B")}`
: `\({drawOne(220, 70, aKey, "Shape")}`;
return `
Quick visual
Drag the mini shapes to “move shapes” (purely visual).
\){body}
`;
};
const enableDragging = () => {
const svg = \(("#dragVizSvg");
if (!svg) return;
let active = null;
let start = null;
const getPoint = (evt) => {
const pt = svg.createSVGPoint();
const e = evt.touches?.[0] || evt;
pt.x = e.clientX;
pt.y = e.clientY;
const ctm = svg.getScreenCTM();
if (!ctm) return { x: 0, y: 0 };
const p = pt.matrixTransform(ctm.inverse());
return { x: p.x, y: p.y };
};
const onDown = (evt) => {
const t = evt.target.closest?.("g.draggable");
if (!t) return;
active = t;
active.style.cursor = "grabbing";
const p = getPoint(evt);
const tf = active.getAttribute("transform") || "translate(0 0)";
const m = /translate\(([-\d.]+)\s+([-\d.]+)\)/.exec(tf);
const tx = m ? Number(m[1]) : 0;
const ty = m ? Number(m[2]) : 0;
start = { p0: p, tx0: tx, ty0: ty };
evt.preventDefault?.();
};
const onMove = (evt) => {
if (!active || !start) return;
const p = getPoint(evt);
const dx = p.x - start.p0.x;
const dy = p.y - start.p0.y;
// clamp inside viewBox-ish
const tx = clamp(start.tx0 + dx, 90, 350);
const ty = clamp(start.ty0 + dy, 45, 95);
active.setAttribute("transform", `translate(\){tx} \({ty})`);
evt.preventDefault?.();
};
const onUp = () => {
if (!active) return;
active.style.cursor = "grab";
active = null;
start = null;
};
svg.addEventListener("mousedown", onDown);
window.addEventListener("mousemove", onMove);
window.addEventListener("mouseup", onUp);
svg.addEventListener("touchstart", onDown, { passive: false });
window.addEventListener("touchmove", onMove, { passive: false });
window.addEventListener("touchend", onUp);
};
// ---------- Accessors ----------
const getShapeKey = () => \)("#shape").value || "cube";
const getTargetKey = () => \(("#target").value || "V";
const getLenUnit = () => \)("#lenUnit").value || "m";
const getVolUnit = () => \(("#volUnit").value || "m3";
const getShapeKeyB = () => \)("#shapeB").value || "rectPrism";
const getTargetKeyB = () => \(("#targetB").value || "V";
// Read A/B raw values and convert to meters
const readRawMeters = (prefix = "") => {
const toM = LEN_TO_M[getLenUnit()] ?? 1;
const raw = {
d1: num(\)(`#\({prefix}d1`)?.value),
d2: num(\)(`#\({prefix}d2`)?.value),
d3: num(\)(`#\({prefix}d3`)?.value),
extra: num(\)(`#\({prefix}extra`)?.value),
extra2: num(\)(`#\({prefix}extra2`)?.value),
extra3: num(\)(`#\({prefix}extra3`)?.value)
};
// convert to meters (keep NaN if empty)
return {
d1: Number.isFinite(raw.d1) ? raw.d1 * toM : NaN,
d2: Number.isFinite(raw.d2) ? raw.d2 * toM : NaN,
d3: Number.isFinite(raw.d3) ? raw.d3 * toM : NaN,
extra: Number.isFinite(raw.extra) ? raw.extra * toM : NaN,
extra2: Number.isFinite(raw.extra2) ? raw.extra2 * toM : NaN,
extra3: Number.isFinite(raw.extra3) ? raw.extra3 * toM : NaN
};
};
const readVolumeInM3 = (prefix = "") => {
const vu = getVolUnit();
const Vin = num(\)(`#\({prefix}vIn`)?.value);
if (!Number.isFinite(Vin)) return NaN;
const f = VOL_TO_M3[vu] ?? 1;
return Vin * f;
};
// ---------- UI sync ----------
const fillTargets = (shapeKey, targetSel) => {
const s = SHAPES[shapeKey];
if (!s) return;
const current = targetSel.value;
targetSel.innerHTML = s.targets.map(t => `
\({esc(t.label)} `).join("");
if (s.targets.some(t => t.v === current)) targetSel.value = current;
else targetSel.value = "V";
};
const applyShapeUI = (shapeKey, targetKey, prefix = "") => {
const s = SHAPES[shapeKey];
if (!s) return;
const id = (x) => `#\){prefix}\({x}`;
// Labels
\)(`\({id("d1Label")}`).textContent = s.labels?.d1 || "Dimension 1";
\)(`\({id("d2Label")}`).textContent = s.labels?.d2 || "Dimension 2";
\)(`\({id("d3Label")}`).textContent = s.labels?.d3 || "Dimension 3";
if (\)(id("extraLabel"))) \((id("extraLabel")).textContent = s.labels?.extra || "Extra";
if (\)(id("extra2Label"))) \((id("extra2Label")).textContent = s.labels?.extra2 || "Extra 2";
if (\)(id("extra3Label"))) \((id("extra3Label")).textContent = s.labels?.extra3 || "Extra 3";
// Show/hide
setWrapVisible(\)(id("d1Wrap")), !!s.show.d1);
setWrapVisible(\((id("d2Wrap")), !!s.show.d2);
setWrapVisible(\)(id("d3Wrap")), !!s.show.d3);
const extraWrap = \((id("extraWrap"));
const extra2Wrap = \)(id("extra2Wrap"));
const extra3Wrap = \((id("extra3Wrap"));
if (extraWrap) extraWrap.style.display = s.show.extra ? "flex" : "none";
if (extra2Wrap) extra2Wrap.style.display = s.show.extra2 ? "flex" : "none";
if (extra3Wrap) extra3Wrap.style.display = s.show.extra3 ? "flex" : "none";
// Enable/disable fields based on target
const d1 = \)(id("d1"));
const d2 = \((id("d2"));
const d3 = \)(id("d3"));
const extra = \((id("extra"));
const extra2 = \)(id("extra2"));
const extra3 = \((id("extra3"));
const vIn = \)(id("vIn"));
const vInWrap = \((id("vInWrap"));
[d1,d2,d3,extra,extra2,extra3].forEach(el => el && setDisabled(el, false));
if (vIn) setDisabled(vIn, false);
if (targetKey === "V") {
if (vInWrap) vInWrap.style.display = "none";
if (vIn) vIn.value = "";
} else {
if (vInWrap) vInWrap.style.display = "flex";
// Disable the field that corresponds to the target (only for targets we truly support)
const disable = (el) => el && setDisabled(el, true, "Computed");
// Classic mapping (by shape)
if (shapeKey === "cube" && targetKey === "a") disable(d1);
if (shapeKey === "rectPrism") {
if (targetKey === "L") disable(d1);
if (targetKey === "W") disable(d2);
if (targetKey === "H") disable(d3);
}
if (shapeKey === "sphere" && targetKey === "r") disable(d1);
if (shapeKey === "hemisphere" && targetKey === "r") disable(d1);
if (shapeKey === "ellipsoid") {
if (targetKey === "a") disable(d1);
if (targetKey === "b") disable(d2);
if (targetKey === "c") disable(d3);
}
if (shapeKey === "cylinder") {
if (targetKey === "r") disable(d1);
if (targetKey === "h") disable(d2);
}
if (shapeKey === "cone") {
if (targetKey === "r") disable(d1);
if (targetKey === "h") disable(d2);
}
if (shapeKey === "triPrism") {
if (targetKey === "b") disable(d1);
if (targetKey === "h") disable(d2);
if (targetKey === "L") disable(d3);
}
if (shapeKey === "pyramid") {
if (targetKey === "L") disable(d1);
if (targetKey === "W") disable(d2);
if (targetKey === "H") disable(d3);
}
if (shapeKey === "capsule" && targetKey === "L") disable(d2);
if (shapeKey === "conicalFrustum" && targetKey === "h") disable(d3);
if (shapeKey === "truncatedPyramid" && targetKey === "H") disable(d3);
}
};
const syncPrimaryUI = () => {
const shapeKey = getShapeKey();
fillTargets(shapeKey, \)("#target"));
const targetKey = getTargetKey();
\(("#shapeHint").textContent =
(targetKey === "V")
? "Tip: Enter dimensions to get volume + automatic equivalents."
: "Tip: Enter the known volume and the other dimensions — we’ll compute the missing one (when supported).";
applyShapeUI(shapeKey, targetKey, "");
renderShapePreview();
clearOutput();
};
const syncBUI = () => {
const shapeKey = getShapeKeyB();
fillTargets(shapeKey, \)("#targetB"));
applyShapeUI(shapeKey, getTargetKeyB(), "b_");
clearOutput();
};
const syncStretchPanels = () => {
\(("#compositePanel").style.display = \)("#compositeOn").checked ? "block" : "none";
\(("#densityPanel").style.display = \)("#densityOn").checked ? "block" : "none";
clearOutput();
};
// ---------- Validation + computation ----------
const requirePositive = (val, msg) => {
if (!(Number.isFinite(val) && val > 0)) {
showError(msg);
return false;
}
return true;
};
const computeOneShape = (shapeKey, targetKey, prefix = "") => {
const s = SHAPES[shapeKey];
if (!s) return { ok: false };
const raw = readRawMeters(prefix);
const dims = s.map(raw); // mapped named dims in meters
const V_in_m3 = readVolumeInM3(prefix);
const lenUnit = getLenUnit();
const toM = LEN_TO_M[lenUnit] ?? 1;
const fromM = 1 / toM;
const unitTxt = (sym) => {
const x = dims[sym];
return `\({fmt(x * fromM, 6)} \){lenUnit}`;
};
const validateForVolume = () => {
// Check all required shown fields for that shape
const need = [];
if (shapeKey === "cube") need.push(["a", "Please enter a valid edge length (a)."]);
if (shapeKey === "rectPrism") need.push(["L","Please enter a valid length (L)."],["W","Please enter a valid width (W)."],["H","Please enter a valid height (H)."]);
if (shapeKey === "sphere") need.push(["r","Please enter a valid radius (r)."]);
if (shapeKey === "hemisphere") need.push(["r","Please enter a valid radius (r)."]);
if (shapeKey === "sphericalCap") need.push(["R","Please enter a valid sphere radius (R)."],["h","Please enter a valid cap height (h)."]);
if (shapeKey === "ellipsoid") need.push(["a","Please enter a valid semi-axis a."],["b","Please enter a valid semi-axis b."],["c","Please enter a valid semi-axis c."]);
if (shapeKey === "cylinder") need.push(["r","Please enter a valid radius (r)."],["h","Please enter a valid height (h)."]);
if (shapeKey === "hollowCylinder") need.push(["R","Please enter a valid outer radius (R)."],["r","Please enter a valid inner radius (r)."],["h","Please enter a valid height (h)."]);
if (shapeKey === "capsule") need.push(["r","Please enter a valid radius (r)."],["L","Please enter a valid cylinder length (L)."]);
if (shapeKey === "cone") need.push(["r","Please enter a valid radius (r)."],["h","Please enter a valid height (h)."]);
if (shapeKey === "conicalFrustum") need.push(["r","Please enter a valid top radius (r)."],["R","Please enter a valid bottom radius (R)."],["h","Please enter a valid height (h)."]);
if (shapeKey === "triPrism") need.push(["b","Please enter a valid triangle base (b)."],["h","Please enter a valid triangle height (h)."],["L","Please enter a valid prism length (L)."]);
if (shapeKey === "pyramid") need.push(["L","Please enter a valid base length (L)."],["W","Please enter a valid base width (W)."],["H","Please enter a valid height (H)."]);
if (shapeKey === "truncatedPyramid") need.push(["L1","Please enter a valid base length (L₁)."],["W1","Please enter a valid base width (W₁)."],["L2","Please enter a valid top length (L₂)."],["W2","Please enter a valid top width (W₂)."],["H","Please enter a valid height (H)."]);
for (const [k, msg] of need) {
if (!requirePositive(dims[k], msg)) return false;
}
// additional sanity checks
if (shapeKey === "hollowCylinder" && !(dims.R > dims.r)) {
showError("For a hollow cylinder, outer radius (R) must be greater than inner radius (r).");
return false;
}
if (shapeKey === "sphericalCap" && !(dims.h < 2 * dims.R)) {
showError("For a spherical cap, cap height (h) should be less than 2R.");
return false;
}
return true;
};
const validateForSolveDim = () => {
if (!requirePositive(V_in_m3, "Please enter a valid known volume (V).")) return false;
// Only allow solving when shape+target are supported
const allowed = new Set(s.targets.map(t => t.v));
if (!allowed.has(targetKey) || targetKey === "V") return true;
// Require all other needed dims
if (shapeKey === "cube") return true;
if (shapeKey === "sphere") return true;
if (shapeKey === "hemisphere") return true;
if (shapeKey === "rectPrism") {
if (targetKey === "L") return requirePositive(dims.W,"Please enter width (W).") && requirePositive(dims.H,"Please enter height (H).");
if (targetKey === "W") return requirePositive(dims.L,"Please enter length (L).") && requirePositive(dims.H,"Please enter height (H).");
if (targetKey === "H") return requirePositive(dims.L,"Please enter length (L).") && requirePositive(dims.W,"Please enter width (W).");
}
if (shapeKey === "cylinder") {
if (targetKey === "r") return requirePositive(dims.h,"Please enter height (h).");
if (targetKey === "h") return requirePositive(dims.r,"Please enter radius (r).");
}
if (shapeKey === "cone") {
if (targetKey === "r") return requirePositive(dims.h,"Please enter height (h).");
if (targetKey === "h") return requirePositive(dims.r,"Please enter radius (r).");
}
if (shapeKey === "triPrism") {
if (targetKey === "b") return requirePositive(dims.h,"Please enter triangle height (h).") && requirePositive(dims.L,"Please enter prism length (L).");
if (targetKey === "h") return requirePositive(dims.b,"Please enter triangle base (b).") && requirePositive(dims.L,"Please enter prism length (L).");
if (targetKey === "L") return requirePositive(dims.b,"Please enter triangle base (b).") && requirePositive(dims.h,"Please enter triangle height (h).");
}
if (shapeKey === "pyramid") {
if (targetKey === "L") return requirePositive(dims.W,"Please enter base width (W).") && requirePositive(dims.H,"Please enter height (H).");
if (targetKey === "W") return requirePositive(dims.L,"Please enter base length (L).") && requirePositive(dims.H,"Please enter height (H).");
if (targetKey === "H") return requirePositive(dims.L,"Please enter base length (L).") && requirePositive(dims.W,"Please enter base width (W).");
}
if (shapeKey === "ellipsoid") {
if (targetKey === "a") return requirePositive(dims.b,"Please enter b.") && requirePositive(dims.c,"Please enter c.");
if (targetKey === "b") return requirePositive(dims.a,"Please enter a.") && requirePositive(dims.c,"Please enter c.");
if (targetKey === "c") return requirePositive(dims.a,"Please enter a.") && requirePositive(dims.b,"Please enter b.");
}
if (shapeKey === "capsule") {
if (targetKey === "L") return requirePositive(dims.r,"Please enter radius (r).");
}
if (shapeKey === "conicalFrustum") {
if (targetKey === "h") return requirePositive(dims.R,"Please enter bottom radius (R).") && requirePositive(dims.r,"Please enter top radius (r).");
}
if (shapeKey === "truncatedPyramid") {
if (targetKey === "H") return requirePositive(dims.L1,"Please enter L₁.") && requirePositive(dims.W1,"Please enter W₁.") && requirePositive(dims.L2,"Please enter L₂.") && requirePositive(dims.W2,"Please enter W₂.");
}
showError("That solve-for option isn’t supported for this shape (yet). Choose Volume instead.");
return false;
};
if (targetKey === "V") {
if (!validateForVolume()) return { ok: false };
const V = s.volume(dims);
if (!(Number.isFinite(V) && V > 0)) {
showError("Inputs produce an invalid volume. Please check values.");
return { ok: false };
}
return { ok: true, kind: "volume", shapeKey, targetKey, V_m3: V, dims, formula: s.formulas.V, vizKey: s.vizKey };
}
// Solve for dimension
if (!validateForSolveDim()) return { ok: false };
const x_m = s.solve(targetKey, dims, V_in_m3);
if (!(Number.isFinite(x_m) && x_m > 0)) {
showError("Inputs produce an invalid dimension. Check volume and other dimensions.");
return { ok: false };
}
return { ok: true, kind: "dimension", shapeKey, targetKey, V_m3: V_in_m3, x_m, dims, formula: s.formulas[targetKey] || "", vizKey: s.vizKey };
};
const computeMassFromDensity = (V_m3) => {
const rhoRaw = num(\(("#rhoVal").value);
if (!(Number.isFinite(rhoRaw) && rhoRaw > 0)) return { ok: false, err: "Please enter a valid density (ρ > 0)." };
const rhoUnit = \)("#rhoUnit").value;
const rho = rhoRaw * (RHO_TO_KGM3[rhoUnit] ?? 1); // kg/m^3
const m_kg = rho * V_m3;
return { ok: true, rho, m_kg };
};
// ---------- Rendering ----------
const renderVolumeResult = (label, V_m3, extraBlockHtml = "") => {
const outUnit = getVolUnit();
const Vout = m3ToUnit(V_m3, outUnit);
const eq = equivalents(V_m3);
const eqLines = [
`
\({esc(fmt(eq.L, 6))} L`,
`
\){esc(fmt(eq.mL, 6))} mL`,
`
\({esc(fmt(eq.cm3, 6))} cm³`,
`
\){esc(fmt(eq.ft3, 6))} ft³`,
`
\({esc(fmt(eq.gal, 6))} US gal`
];
\)("#noResultsHint").style.display = "none";
\(("#result").innerHTML = `
\){esc(label)}
Volume: \({esc(fmt(Vout, 8))} \){esc(unitLabel(outUnit))}
Helpful equivalents: \({eqLines.join(" ")}
\){extraBlockHtml}
`;
};
const renderDimensionResult = (shapeKey, targetKey, x_m, V_m3, extraBlockHtml = "") => {
const lenUnit = getLenUnit();
const fromM = 1 / (LEN_TO_M[lenUnit] ?? 1);
const x_out = x_m * fromM;
const nice = {
a: "Edge (a)",
L: "Length (L)",
W: "Width (W)",
H: "Height (H)",
r: "Radius (r)",
h: "Height (h)",
b: "Triangle base (b)",
c: "Semi-axis c",
R: "Outer/Bottom radius (R)",
L1: "Base length (L₁)",
W1: "Base width (W₁)",
L2: "Top length (L₂)",
W2: "Top width (W₂)"
}[targetKey] || "Dimension";
\(("#noResultsHint").style.display = "none";
\)("#result").innerHTML = `
Solved dimension
\({esc(nice)}: \){esc(fmt(x_out, 8))} \({esc(lenUnit)}
Given volume: \){esc(fmt(m3ToUnit(V_m3, getVolUnit()), 8))} \({esc(unitLabel(getVolUnit()))}
\){extraBlockHtml}
`;
};
const renderSteps = (blocks) => {
if (!\(("#showSteps").checked) {
\)("#steps").innerHTML = "";
return;
}
const html = blocks.map(b => `
\({esc(b.title)}
\){b.items.map(li => `\({li} `).join("")}
`).join("");
\)("#steps").innerHTML = html;
};
const renderViz = (html) => {
\(("#viz").innerHTML = \)("#showViz").checked ? html : "";
// enable dragging if the draggable svg exists
enableDragging();
};
// ---------- Main compute ----------
const compute = () => {
clearOutput();
try {
const a = computeOneShape(getShapeKey(), getTargetKey(), "");
if (!a.ok) return;
let Vtotal_m3 = a.V_m3;
let V1_m3 = a.V_m3;
let V2_m3 = NaN;
const compositeOn = \(("#compositeOn").checked;
if (compositeOn) {
const b = computeOneShape(getShapeKeyB(), getTargetKeyB(), "b_");
if (!b.ok) return;
V2_m3 = b.V_m3;
const op = \)("#compOp").value;
Vtotal_m3 = (op === "sub") ? (V1_m3 - V2_m3) : (V1_m3 + V2_m3);
if (!(Number.isFinite(Vtotal_m3) && Vtotal_m3 > 0)) {
return showError("Composite volume is not positive. If subtracting, ensure V₁ > V₂.");
}
}
// Density
let massBlockHtml = "";
let massSteps = null;
if (\(("#densityOn").checked) {
const m = computeMassFromDensity(Vtotal_m3);
if (!m.ok) return showError(m.err);
const m_g = m.m_kg * 1000;
const m_lb = m.m_kg / 0.45359237;
massBlockHtml = `
Mass from density:
ρ = \){esc(fmt(m.rho, 6))} kg/m³
m = \({esc(fmt(m.m_kg, 8))} kg
= \){esc(fmt(m_g, 8))} g • \({esc(fmt(m_lb, 8))} lb
`;
massSteps = {
title: "Mass from density (optional)",
items: [
`Convert density to SI:
ρ in kg/m³`,
`Compute mass:
m = ρ·V `,
`Convert mass to g and lb (optional)`
]
};
}
const outUnit = getVolUnit();
const targetA = getTargetKey();
if (targetA === "V") {
const label = compositeOn ? "Composite volume (Vtotal)" : `Volume (\){SHAPES[getShapeKey()].name})`;
renderVolumeResult(label, Vtotal_m3, massBlockHtml);
} else {
const compositeHtml = compositeOn
? `
Composite total volume:
\({esc(fmt(m3ToUnit(Vtotal_m3, outUnit), 8))} \){esc(unitLabel(outUnit))}
`
: "";
renderDimensionResult(getShapeKey(), targetA, a.x_m, a.V_m3, compositeHtml + massBlockHtml);
}
// Visuals
let vizHtml = "";
if (\(("#showViz").checked) {
vizHtml += draggableViz({
aKey: SHAPES[getShapeKey()].vizKey || "cube",
bKey: compositeOn ? (SH
Network connection lost. Attempting to reconnect…
You said:
you did not provided the compete code... Network connection lost. Attempting to reconnect
ChatGPT said:
Got you — here’s the complete, copy-paste ready code (HTML + JS). Nothing missing.
1) Complete HTML
How to use this calculator
Choose a shape and what you want to solve for (volume or a dimension).
Pick your length unit and a volume output unit .
Optional stretch features: enable Composite (add/subtract) and/or Mass from density .
Click Calculate to get the answer + steps + quick visual (drag shapes in composite mode).
Formulas & Equations Used
Example Problem & Step-by-Step Solution
Example 1 — Aquarium volume
Box dimensions: L=60 cm, W=30 cm, H=40 cm
Compute volume: V = L·W·H = 60·30·40 = 72,000 cm³
Convert: 72,000 cm³ = 72,000 mL = 72 L
Example 2 — Beaker height from volume (cylinder)
Given: V = 250 mL , r = 3.5 cm
Convert volume: 250 mL = 250 cm³
Rearrange: h = V/(πr²)
Compute: h = 250 /(π·3.5²) ≈ 6.50 cm
Example 3 — Tube (hollow cylinder) volume
Given: outer radius R=2.0 cm , inner radius r=1.5 cm , height h=30 cm
Use: V = πh(R² − r²)
Compute: V = π·30·(2² − 1.5²) = π·30·(4 − 2.25) = π·52.5 ≈ 164.9 cm³
Convert: 164.9 cm³ ≈ 164.9 mL
Frequently Asked Questions
Q: Is cm³ the same as mL?
Yes. 1 cm³ = 1 mL .
Q: Can I solve for height or radius instead of volume?
Yes—use the Solve for dropdown to compute a missing dimension (supported shapes will show options).
Q: What does “composite volume” mean?
It means adding or subtracting volumes (like a block with a drilled hole).
2) Complete JS
/* ✅ Production-ready JS: Volume Calculator (mini SVG + better spacing + swap shapes + draggable shapes viz) */
(function () {
const NS = "volumeCalc_v2";
if (window[NS]?.isInitialized) return;
window[NS] = { isInitialized: true };
const \) = (sel, root = document) => root.querySelector(sel);
const \] = (sel, root = document) => Array.from(root.querySelectorAll(sel));
const esc = (s) => (s ?? "").toString()
.replace(/&/g, "&").replace(//g, ">")
.replace(/"/g, """).replace(/'/g, "'");
const num = (v) => {
const t = (v ?? "").toString().trim().replace(/,/g, "");
if (!t) return NaN;
const x = Number(t);
return Number.isFinite(x) ? x : NaN;
};
const fmt = (x, dp = 8) => {
if (!Number.isFinite(x)) return "";
const ax = Math.abs(x);
if (ax !== 0 && (ax >= 1e7 || ax < 1e-6)) return x.toExponential(6);
const s = x.toFixed(dp);
return s.replace(/(\.\d*?[1-9])0+\(/,"\)1").replace(/\.0+\(/,"");
};
const clamp = (x, a, b) => Math.min(b, Math.max(a, x));
// ---------- Unit conversions ----------
const LEN_TO_M = { m: 1, cm: 0.01, mm: 0.001, in: 0.0254, ft: 0.3048 };
const VOL_TO_M3 = {
m3: 1,
L: 1e-3,
mL: 1e-6,
cm3: 1e-6,
mm3: 1e-9,
in3: Math.pow(0.0254, 3),
ft3: Math.pow(0.3048, 3),
galUS: 0.003785411784
};
const RHO_TO_KGM3 = { kgm3: 1, gcm3: 1000, gml: 1000, lbft3: 16.01846337 };
const m3ToUnit = (V_m3, unit) => {
const f = VOL_TO_M3[unit] ?? 1;
return V_m3 / f;
};
const unitLabel = (u) => ({
m3: "m³", L: "L", mL: "mL", cm3: "cm³", mm3: "mm³", in3: "in³", ft3: "ft³", galUS: "US gal"
}[u] || u);
const equivalents = (V_m3) => {
const L = m3ToUnit(V_m3, "L");
const mL = m3ToUnit(V_m3, "mL");
const cm3 = m3ToUnit(V_m3, "cm3");
const ft3 = m3ToUnit(V_m3, "ft3");
const gal = m3ToUnit(V_m3, "galUS");
return { L, mL, cm3, ft3, gal };
};
// ---------- Shape mini SVGs ----------
const svgWrap = (inner) => `
Shape preview
A tiny diagram to help you sanity-check inputs.
\){inner}
`;
const shapeSVG = (key) => {
const common = `fill="none" stroke="rgba(255,255,255,.78)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"`;
const faint = `stroke="rgba(255,255,255,.35)"`;
const base = (body) => `
\({body}
`;
const map = {
cube: base(`
`),
rectPrism: base(`
`),
sphere: base(`
`),
hemisphere: base(`
`),
sphericalCap: base(`
`),
ellipsoid: base(`
`),
cylinder: base(`
`),
hollowCylinder: base(`
`),
capsule: base(`
`),
cone: base(`
`),
conicalFrustum: base(`
`),
triPrism: base(`
`),
pyramid: base(`
`),
truncatedPyramid: base(`
`)
};
return map[key] || map.cube;
};
const renderShapePreview = () => {
const key = getShapeKey();
\)("#shapePreview").innerHTML = svgWrap(shapeSVG(key));
};
// ---------- Shape definitions ----------
const SHAPES = {
cube: {
name: "Cube",
labels: { d1: "Edge length (a)" },
show: { d1: true, d2: false, d3: false, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "a", label: "Edge (a)" }],
formulas: { V: "V = a³", a: "a = ³√V" },
volume: ({ a }) => Math.pow(a, 3),
solve: (target, dims, V) => (target === "a" ? Math.cbrt(V) : NaN),
vizKey: "cube",
map: (raw) => ({ a: raw.d1 })
},
rectPrism: {
name: "Rectangular prism",
labels: { d1: "Length (L)", d2: "Width (W)", d3: "Height (H)" },
show: { d1: true, d2: true, d3: true, extra: false, extra2: false, extra3: false },
targets: [
{ v: "V", label: "Volume (V)" },
{ v: "L", label: "Length (L)" },
{ v: "W", label: "Width (W)" },
{ v: "H", label: "Height (H)" }
],
formulas: { V: "V = L·W·H", L: "L = V/(W·H)", W: "W = V/(L·H)", H: "H = V/(L·W)" },
volume: ({ L, W, H }) => L * W * H,
solve: (target, { L, W, H }, V) => {
if (target === "L") return V / (W * H);
if (target === "W") return V / (L * H);
if (target === "H") return V / (L * W);
return NaN;
},
vizKey: "rectPrism",
map: (raw) => ({ L: raw.d1, W: raw.d2, H: raw.d3 })
},
sphere: {
name: "Sphere",
labels: { d1: "Radius (r)" },
show: { d1: true, d2: false, d3: false, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" }],
formulas: { V: "V = (4/3)πr³", r: "r = ³√(3V/(4π))" },
volume: ({ r }) => (4 / 3) * Math.PI * Math.pow(r, 3),
solve: (target, { r }, V) => (target === "r" ? Math.cbrt((3 * V) / (4 * Math.PI)) : NaN),
vizKey: "sphere",
map: (raw) => ({ r: raw.d1 })
},
hemisphere: {
name: "Hemisphere",
labels: { d1: "Radius (r)" },
show: { d1: true, d2: false, d3: false, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" }],
formulas: { V: "V = (2/3)πr³", r: "r = ³√(3V/(2π))" },
volume: ({ r }) => (2 / 3) * Math.PI * Math.pow(r, 3),
solve: (target, { r }, V) => (target === "r" ? Math.cbrt((3 * V) / (2 * Math.PI)) : NaN),
vizKey: "hemisphere",
map: (raw) => ({ r: raw.d1 })
},
sphericalCap: {
name: "Spherical cap",
labels: { d1: "Sphere radius (R)", d2: "Cap height (h)" },
show: { d1: true, d2: true, d3: false, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }],
formulas: { V: "V = (1/3)πh²(3R − h)" },
volume: ({ R, h }) => (1 / 3) * Math.PI * h * h * (3 * R - h),
solve: () => NaN,
vizKey: "sphericalCap",
map: (raw) => ({ R: raw.d1, h: raw.d2 })
},
ellipsoid: {
name: "Ellipsoid",
labels: { d1: "Semi-axis a", d2: "Semi-axis b", d3: "Semi-axis c" },
show: { d1: true, d2: true, d3: true, extra: false, extra2: false, extra3: false },
targets: [
{ v: "V", label: "Volume (V)" },
{ v: "a", label: "Solve a" },
{ v: "b", label: "Solve b" },
{ v: "c", label: "Solve c" }
],
formulas: { V: "V = (4/3)πabc", a: "a = 3V/(4πbc)", b: "b = 3V/(4πac)", c: "c = 3V/(4πab)" },
volume: ({ a, b, c }) => (4 / 3) * Math.PI * a * b * c,
solve: (target, { a, b, c }, V) => {
if (target === "a") return (3 * V) / (4 * Math.PI * b * c);
if (target === "b") return (3 * V) / (4 * Math.PI * a * c);
if (target === "c") return (3 * V) / (4 * Math.PI * a * b);
return NaN;
},
vizKey: "ellipsoid",
map: (raw) => ({ a: raw.d1, b: raw.d2, c: raw.d3 })
},
cylinder: {
name: "Cylinder",
labels: { d1: "Radius (r)", d2: "Height (h)" },
show: { d1: true, d2: true, d3: false, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" }, { v: "h", label: "Height (h)" }],
formulas: { V: "V = πr²h", r: "r = √(V/(πh))", h: "h = V/(πr²)" },
volume: ({ r, h }) => Math.PI * r * r * h,
solve: (target, { r, h }, V) => {
if (target === "r") return Math.sqrt(V / (Math.PI * h));
if (target === "h") return V / (Math.PI * r * r);
return NaN;
},
vizKey: "cylinder",
map: (raw) => ({ r: raw.d1, h: raw.d2 })
},
hollowCylinder: {
name: "Hollow cylinder / tube",
labels: { d1: "Outer radius (R)", d2: "Inner radius (r)", d3: "Height (h)" },
show: { d1: true, d2: true, d3: true, extra: false, extra2: false, extra3: false },
targets: [
{ v: "V", label: "Volume (V)" },
{ v: "h", label: "Height (h)" }
],
formulas: { V: "V = πh(R² − r²)", h: "h = V/(π(R² − r²))" },
volume: ({ R, r, h }) => Math.PI * h * (R * R - r * r),
solve: (target, { R, r }, V) => {
if (target === "h") return V / (Math.PI * (R * R - r * r));
return NaN;
},
vizKey: "hollowCylinder",
map: (raw) => ({ R: raw.d1, r: raw.d2, h: raw.d3 })
},
capsule: {
name: "Capsule",
labels: { d1: "Radius (r)", d2: "Cylinder length (L)" },
show: { d1: true, d2: true, d3: false, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "L", label: "Cylinder length (L)" }],
formulas: { V: "V = πr²L + (4/3)πr³", L: "L = (V − (4/3)πr³)/(πr²)" },
volume: ({ r, L }) => (Math.PI * r * r * L) + (4 / 3) * Math.PI * Math.pow(r, 3),
solve: (target, { r }, V) => {
if (target === "L") return (V - (4 / 3) * Math.PI * Math.pow(r, 3)) / (Math.PI * r * r);
return NaN;
},
vizKey: "capsule",
map: (raw) => ({ r: raw.d1, L: raw.d2 })
},
cone: {
name: "Cone",
labels: { d1: "Radius (r)", d2: "Height (h)" },
show: { d1: true, d2: true, d3: false, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "r", label: "Radius (r)" }, { v: "h", label: "Height (h)" }],
formulas: { V: "V = (1/3)πr²h", r: "r = √(3V/(πh))", h: "h = 3V/(πr²)" },
volume: ({ r, h }) => (1 / 3) * Math.PI * r * r * h,
solve: (target, { r, h }, V) => {
if (target === "r") return Math.sqrt((3 * V) / (Math.PI * h));
if (target === "h") return (3 * V) / (Math.PI * r * r);
return NaN;
},
vizKey: "cone",
map: (raw) => ({ r: raw.d1, h: raw.d2 })
},
conicalFrustum: {
name: "Conical frustum",
labels: { d1: "Top radius (r)", d2: "Bottom radius (R)", d3: "Height (h)" },
show: { d1: true, d2: true, d3: true, extra: false, extra2: false, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "h", label: "Height (h)" }],
formulas: { V: "V = (1/3)πh(R² + Rr + r²)", h: "h = 3V/(π(R² + Rr + r²))" },
volume: ({ r, R, h }) => (1 / 3) * Math.PI * h * (R * R + R * r + r * r),
solve: (target, { r, R }, V) => {
if (target === "h") return (3 * V) / (Math.PI * (R * R + R * r + r * r));
return NaN;
},
vizKey: "conicalFrustum",
map: (raw) => ({ r: raw.d1, R: raw.d2, h: raw.d3 })
},
triPrism: {
name: "Triangular prism (right)",
labels: { d1: "Triangle base (b)", d2: "Triangle height (h)", d3: "Prism length (L)" },
show: { d1: true, d2: true, d3: true, extra: false, extra2: false, extra3: false },
targets: [
{ v: "V", label: "Volume (V)" },
{ v: "b", label: "Triangle base (b)" },
{ v: "h", label: "Triangle height (h)" },
{ v: "L", label: "Prism length (L)" }
],
formulas: { V: "V = (1/2)·b·h·L", b: "b = 2V/(h·L)", h: "h = 2V/(b·L)", L: "L = 2V/(b·h)" },
volume: ({ b, h, L }) => 0.5 * b * h * L,
solve: (target, { b, h, L }, V) => {
if (target === "b") return (2 * V) / (h * L);
if (target === "h") return (2 * V) / (b * L);
if (target === "L") return (2 * V) / (b * h);
return NaN;
},
vizKey: "triPrism",
map: (raw) => ({ b: raw.d1, h: raw.d2, L: raw.d3 })
},
pyramid: {
name: "Pyramid (rectangular base)",
labels: { d1: "Base length (L)", d2: "Base width (W)", d3: "Height (H)" },
show: { d1: true, d2: true, d3: true, extra: false, extra2: false, extra3: false },
targets: [
{ v: "V", label: "Volume (V)" },
{ v: "L", label: "Base length (L)" },
{ v: "W", label: "Base width (W)" },
{ v: "H", label: "Height (H)" }
],
formulas: { V: "V = (1/3)L·W·H", L: "L = 3V/(W·H)", W: "W = 3V/(L·H)", H: "H = 3V/(L·W)" },
volume: ({ L, W, H }) => (1 / 3) * L * W * H,
solve: (target, { L, W, H }, V) => {
if (target === "L") return (3 * V) / (W * H);
if (target === "W") return (3 * V) / (L * H);
if (target === "H") return (3 * V) / (L * W);
return NaN;
},
vizKey: "pyramid",
map: (raw) => ({ L: raw.d1, W: raw.d2, H: raw.d3 })
},
truncatedPyramid: {
name: "Truncated pyramid",
labels: {
d1: "Base length (L₁)",
d2: "Base width (W₁)",
extra: "Top length (L₂)",
extra2: "Top width (W₂)",
d3: "Height (H)"
},
show: { d1: true, d2: true, d3: true, extra: true, extra2: true, extra3: false },
targets: [{ v: "V", label: "Volume (V)" }, { v: "H", label: "Height (H)" }],
formulas: { V: "V = (H/3)(A₁ + A₂ + √(A₁A₂))", H: "H = 3V/(A₁ + A₂ + √(A₁A₂))" },
volume: ({ L1, W1, L2, W2, H }) => {
const A1 = L1 * W1;
const A2 = L2 * W2;
return (H / 3) * (A1 + A2 + Math.sqrt(A1 * A2));
},
solve: (target, { L1, W1, L2, W2 }, V) => {
if (target !== "H") return NaN;
const A1 = L1 * W1;
const A2 = L2 * W2;
return (3 * V) / (A1 + A2 + Math.sqrt(A1 * A2));
},
vizKey: "truncatedPyramid",
map: (raw) => ({ L1: raw.d1, W1: raw.d2, H: raw.d3, L2: raw.extra, W2: raw.extra2 })
}
};
// ---------- UI helpers ----------
const clearOutput = () => {
\(("#result").innerHTML = "";
\)("#viz").innerHTML = "";
\(("#steps").innerHTML = "";
\)("#noResultsHint").style.display = "block";
};
const showError = (msg) => {
\(("#noResultsHint").style.display = "none";
\)("#result").innerHTML = `
⚠️ \({esc(msg)}
`;
\)("#viz").innerHTML = "";
\(("#steps").innerHTML = "";
};
const setDisabled = (el, isDisabled, placeholder) => {
if (!el) return;
el.disabled = !!isDisabled;
el.style.opacity = isDisabled ? "0.65" : "1";
el.style.pointerEvents = isDisabled ? "none" : "auto";
if (placeholder != null) el.placeholder = placeholder;
};
const setWrapVisible = (wrap, yes) => {
if (!wrap) return;
wrap.style.display = yes ? "flex" : "none";
};
// ---------- Draggable "move shapes" visual ----------
const draggableViz = ({ aKey, bKey, compositeOn }) => {
const drawOne = (x, y, key, label) => `
\){esc(label)}
\({shapeSVG(key).replace(/]*>/, "").replace(" ", "")}
`;
const body = compositeOn
? `\){drawOne(110, 70, aKey, "Shape A")} \({drawOne(330, 70, bKey, "Shape B")}`
: `\){drawOne(220, 70, aKey, "Shape")}`;
return `
Quick visual
Drag the mini shapes to “move shapes” (visual only).
\({body}
`;
};
const enableDragging = () => {
const svg = \)("#dragVizSvg");
if (!svg) return;
let active = null;
let start = null;
const getPoint = (evt) => {
const pt = svg.createSVGPoint();
const e = evt.touches?.[0] || evt;
pt.x = e.clientX;
pt.y = e.clientY;
const ctm = svg.getScreenCTM();
if (!ctm) return { x: 0, y: 0 };
const p = pt.matrixTransform(ctm.inverse());
return { x: p.x, y: p.y };
};
const onDown = (evt) => {
const t = evt.target.closest?.("g.draggable");
if (!t) return;
active = t;
active.style.cursor = "grabbing";
const p = getPoint(evt);
const tf = active.getAttribute("transform") || "translate(0 0)";
const m = /translate\(([-\d.]+)\s+([-\d.]+)\)/.exec(tf);
const tx = m ? Number(m[1]) : 0;
const ty = m ? Number(m[2]) : 0;
start = { p0: p, tx0: tx, ty0: ty };
evt.preventDefault?.();
};
const onMove = (evt) => {
if (!active || !start) return;
const p = getPoint(evt);
const dx = p.x - start.p0.x;
const dy = p.y - start.p0.y;
const tx = clamp(start.tx0 + dx, 90, 350);
const ty = clamp(start.ty0 + dy, 45, 95);
active.setAttribute("transform", `translate(\({tx} \){ty})`);
evt.preventDefault?.();
};
const onUp = () => {
if (!active) return;
active.style.cursor = "grab";
active = null;
start = null;
};
svg.addEventListener("mousedown", onDown);
window.addEventListener("mousemove", onMove);
window.addEventListener("mouseup", onUp);
svg.addEventListener("touchstart", onDown, { passive: false });
window.addEventListener("touchmove", onMove, { passive: false });
window.addEventListener("touchend", onUp);
};
// ---------- Accessors ----------
const getShapeKey = () => \(("#shape").value || "cube";
const getTargetKey = () => \)("#target").value || "V";
const getLenUnit = () => \(("#lenUnit").value || "m";
const getVolUnit = () => \)("#volUnit").value || "m3";
const getShapeKeyB = () => \(("#shapeB").value || "rectPrism";
const getTargetKeyB = () => \)("#targetB").value || "V";
// Read A/B raw values and convert to meters
const readRawMeters = (prefix = "") => {
const toM = LEN_TO_M[getLenUnit()] ?? 1;
const raw = {
d1: num(\((`#\){prefix}d1`)?.value),
d2: num(\((`#\){prefix}d2`)?.value),
d3: num(\((`#\){prefix}d3`)?.value),
extra: num(\((`#\){prefix}extra`)?.value),
extra2: num(\((`#\){prefix}extra2`)?.value),
extra3: num(\((`#\){prefix}extra3`)?.value)
};
return {
d1: Number.isFinite(raw.d1) ? raw.d1 * toM : NaN,
d2: Number.isFinite(raw.d2) ? raw.d2 * toM : NaN,
d3: Number.isFinite(raw.d3) ? raw.d3 * toM : NaN,
extra: Number.isFinite(raw.extra) ? raw.extra * toM : NaN,
extra2: Number.isFinite(raw.extra2) ? raw.extra2 * toM : NaN,
extra3: Number.isFinite(raw.extra3) ? raw.extra3 * toM : NaN
};
};
const readVolumeInM3 = (prefix = "") => {
const vu = getVolUnit();
const Vin = num(\((`#\){prefix}vIn`)?.value);
if (!Number.isFinite(Vin)) return NaN;
const f = VOL_TO_M3[vu] ?? 1;
return Vin * f;
};
// ---------- UI sync ----------
const fillTargets = (shapeKey, targetSel) => {
const s = SHAPES[shapeKey];
if (!s) return;
const current = targetSel.value;
targetSel.innerHTML = s.targets.map(t => `
\){esc(t.label)} `).join("");
if (s.targets.some(t => t.v === current)) targetSel.value = current;
else targetSel.value = "V";
};
const applyShapeUI = (shapeKey, targetKey, prefix = "") => {
const s = SHAPES[shapeKey];
if (!s) return;
const id = (x) => `#\({prefix}\){x}`;
// Labels
\((id("d1Label")).textContent = s.labels?.d1 || "Dimension 1";
\)(id("d2Label")).textContent = s.labels?.d2 || "Dimension 2";
\((id("d3Label")).textContent = s.labels?.d3 || "Dimension 3";
const extraWrap = \)(id("extraWrap"));
const extra2Wrap = \((id("extra2Wrap"));
const extra3Wrap = \)(id("extra3Wrap"));
if (\((id("extraLabel"))) \)(id("extraLabel")).textContent = s.labels?.extra || "Extra";
if (\((id("extra2Label"))) \)(id("extra2Label")).textContent = s.labels?.extra2 || "Extra 2";
if (\((id("extra3Label"))) \)(id("extra3Label")).textContent = s.labels?.extra3 || "Extra 3";
setWrapVisible(\((id("d1Wrap")), !!s.show.d1);
setWrapVisible(\)(id("d2Wrap")), !!s.show.d2);
setWrapVisible(\((id("d3Wrap")), !!s.show.d3);
if (extraWrap) extraWrap.style.display = s.show.extra ? "flex" : "none";
if (extra2Wrap) extra2Wrap.style.display = s.show.extra2 ? "flex" : "none";
if (extra3Wrap) extra3Wrap.style.display = s.show.extra3 ? "flex" : "none";
const d1 = \)(id("d1"));
const d2 = \((id("d2"));
const d3 = \)(id("d3"));
const extra = \((id("extra"));
const extra2 = \)(id("extra2"));
const extra3 = \((id("extra3"));
const vIn = \)(id("vIn"));
const vInWrap = \((id("vInWrap"));
[d1,d2,d3,extra,extra2,extra3].forEach(el => el && setDisabled(el, false));
if (vIn) setDisabled(vIn, false);
if (targetKey === "V") {
if (vInWrap) vInWrap.style.display = "none";
if (vIn) vIn.value = "";
} else {
if (vInWrap) vInWrap.style.display = "flex";
const disable = (el) => el && setDisabled(el, true, "Computed");
// Disable relevant input when supported
if (shapeKey === "cube" && targetKey === "a") disable(d1);
if (shapeKey === "rectPrism") {
if (targetKey === "L") disable(d1);
if (targetKey === "W") disable(d2);
if (targetKey === "H") disable(d3);
}
if ((shapeKey === "sphere" || shapeKey === "hemisphere") && targetKey === "r") disable(d1);
if (shapeKey === "ellipsoid") {
if (targetKey === "a") disable(d1);
if (targetKey === "b") disable(d2);
if (targetKey === "c") disable(d3);
}
if (shapeKey === "cylinder") {
if (targetKey === "r") disable(d1);
if (targetKey === "h") disable(d2);
}
if (shapeKey === "cone") {
if (targetKey === "r") disable(d1);
if (targetKey === "h") disable(d2);
}
if (shapeKey === "triPrism") {
if (targetKey === "b") disable(d1);
if (targetKey === "h") disable(d2);
if (targetKey === "L") disable(d3);
}
if (shapeKey === "pyramid") {
if (targetKey === "L") disable(d1);
if (targetKey === "W") disable(d2);
if (targetKey === "H") disable(d3);
}
if (shapeKey === "capsule" && targetKey === "L") disable(d2);
if (shapeKey === "conicalFrustum" && targetKey === "h") disable(d3);
if (shapeKey === "truncatedPyramid" && targetKey === "H") disable(d3);
}
};
const syncPrimaryUI = () => {
const shapeKey = getShapeKey();
fillTargets(shapeKey, \)("#target"));
const targetKey = getTargetKey();
\(("#shapeHint").textContent =
(targetKey === "V")
? "Tip: Enter dimensions to get volume + automatic equivalents."
: "Tip: Enter the known volume and the other dimensions — we’ll compute the missing one (when supported).";
applyShapeUI(shapeKey, targetKey, "");
renderShapePreview();
clearOutput();
};
const syncBUI = () => {
const shapeKey = getShapeKeyB();
fillTargets(shapeKey, \)("#targetB"));
applyShapeUI(shapeKey, getTargetKeyB(), "b_");
clearOutput();
};
const syncStretchPanels = () => {
\(("#compositePanel").style.display = \)("#compositeOn").checked ? "block" : "none";
\(("#densityPanel").style.display = \)("#densityOn").checked ? "block" : "none";
clearOutput();
};
// ---------- Validation + computation ----------
const requirePositive = (val, msg) => {
if (!(Number.isFinite(val) && val > 0)) {
showError(msg);
return false;
}
return true;
};
const computeOneShape = (shapeKey, targetKey, prefix = "") => {
const s = SHAPES[shapeKey];
if (!s) return { ok: false };
const raw = readRawMeters(prefix);
const dims = s.map(raw);
const V_in_m3 = readVolumeInM3(prefix);
const validateForVolume = () => {
const need = [];
if (shapeKey === "cube") need.push(["a", "Please enter a valid edge length (a)."]);
if (shapeKey === "rectPrism") need.push(["L","Please enter a valid length (L)."],["W","Please enter a valid width (W)."],["H","Please enter a valid height (H)."]);
if (shapeKey === "sphere") need.push(["r","Please enter a valid radius (r)."]);
if (shapeKey === "hemisphere") need.push(["r","Please enter a valid radius (r)."]);
if (shapeKey === "sphericalCap") need.push(["R","Please enter a valid sphere radius (R)."],["h","Please enter a valid cap height (h)."]);
if (shapeKey === "ellipsoid") need.push(["a","Please enter a valid semi-axis a."],["b","Please enter a valid semi-axis b."],["c","Please enter a valid semi-axis c."]);
if (shapeKey === "cylinder") need.push(["r","Please enter a valid radius (r)."],["h","Please enter a valid height (h)."]);
if (shapeKey === "hollowCylinder") need.push(["R","Please enter a valid outer radius (R)."],["r","Please enter a valid inner radius (r)."],["h","Please enter a valid height (h)."]);
if (shapeKey === "capsule") need.push(["r","Please enter a valid radius (r)."],["L","Please enter a valid cylinder length (L)."]);
if (shapeKey === "cone") need.push(["r","Please enter a valid radius (r)."],["h","Please enter a valid height (h)."]);
if (shapeKey === "conicalFrustum") need.push(["r","Please enter a valid top radius (r)."],["R","Please enter a valid bottom radius (R)."],["h","Please enter a valid height (h)."]);
if (shapeKey === "triPrism") need.push(["b","Please enter a valid triangle base (b)."],["h","Please enter a valid triangle height (h)."],["L","Please enter a valid prism length (L)."]);
if (shapeKey === "pyramid") need.push(["L","Please enter a valid base length (L)."],["W","Please enter a valid base width (W)."],["H","Please enter a valid height (H)."]);
if (shapeKey === "truncatedPyramid") need.push(["L1","Please enter a valid base length (L₁)."],["W1","Please enter a valid base width (W₁)."],["L2","Please enter a valid top length (L₂)."],["W2","Please enter a valid top width (W₂)."],["H","Please enter a valid height (H)."]);
for (const [k, msg] of need) {
if (!requirePositive(dims[k], msg)) return false;
}
if (shapeKey === "hollowCylinder" && !(dims.R > dims.r)) {
showError("For a hollow cylinder, outer radius (R) must be greater than inner radius (r).");
return false;
}
if (shapeKey === "sphericalCap" && !(dims.h < 2 * dims.R)) {
showError("For a spherical cap, cap height (h) should be less than 2R.");
return false;
}
return true;
};
const validateForSolveDim = () => {
if (!requirePositive(V_in_m3, "Please enter a valid known volume (V).")) return false;
// If the solve is not implemented, block safely
const supported = new Set(s.targets.map(t => t.v));
if (!supported.has(targetKey)) {
showError("That solve-for option isn’t supported for this shape (yet). Choose Volume instead.");
return false;
}
// Require any additional dimensions depending on shape/target
// (Most are enforced by the solve formulas; we handle key ones)
if (shapeKey === "rectPrism") {
if (targetKey === "L") return requirePositive(dims.W,"Please enter width (W).") && requirePositive(dims.H,"Please enter height (H).");
if (targetKey === "W") return requirePositive(dims.L,"Please enter length (L).") && requirePositive(dims.H,"Please enter height (H).");
if (targetKey === "H") return requirePositive(dims.L,"Please enter length (L).") && requirePositive(dims.W,"Please enter width (W).");
}
if (shapeKey === "cylinder") {
if (targetKey === "r") return requirePositive(dims.h,"Please enter height (h).");
if (targetKey === "h") return requirePositive(dims.r,"Please enter radius (r).");
}
if (shapeKey === "cone") {
if (targetKey === "r") return requirePositive(dims.h,"Please enter height (h).");
if (targetKey === "h") return requirePositive(dims.r,"Please enter radius (r).");
}
if (shapeKey === "triPrism") {
if (targetKey === "b") return requirePositive(dims.h,"Please enter triangle height (h).") && requirePositive(dims.L,"Please enter prism length (L).");
if (targetKey === "h") return requirePositive(dims.b,"Please enter triangle base (b).") && requirePositive(dims.L,"Please enter prism length (L).");
if (targetKey === "L") return requirePositive(dims.b,"Please enter triangle base (b).") && requirePositive(dims.h,"Please enter triangle height (h).");
}
if (shapeKey === "pyramid") {
if (targetKey === "L") return requirePositive(dims.W,"Please enter base width (W).") && requirePositive(dims.H,"Please enter height (H).");
if (targetKey === "W") return requirePositive(dims.L,"Please enter base length (L).") && requirePositive(dims.H,"Please enter height (H).");
if (targetKey === "H") return requirePositive(dims.L,"Please enter base length (L).") && requirePositive(dims.W,"Please enter base width (W).");
}
if (shapeKey === "ellipsoid") {
if (targetKey === "a") return requirePositive(dims.b,"Please enter b.") && requirePositive(dims.c,"Please enter c.");
if (targetKey === "b") return requirePositive(dims.a,"Please enter a.") && requirePositive(dims.c,"Please enter c.");
if (targetKey === "c") return requirePositive(dims.a,"Please enter a.") && requirePositive(dims.b,"Please enter b.");
}
if (shapeKey === "capsule") {
if (targetKey === "L") return requirePositive(dims.r,"Please enter radius (r).");
}
if (shapeKey === "conicalFrustum") {
if (targetKey === "h") return requirePositive(dims.R,"Please enter bottom radius (R).") && requirePositive(dims.r,"Please enter top radius (r).");
}
if (shapeKey === "hollowCylinder") {
if (targetKey === "h") return requirePositive(dims.R,"Please enter outer radius (R).") && requirePositive(dims.r,"Please enter inner radius (r).") && (dims.R > dims.r);
}
if (shapeKey === "truncatedPyramid") {
if (targetKey === "H") return requirePositive(dims.L1,"Please enter L₁.") && requirePositive(dims.W1,"Please enter W₁.") && requirePositive(dims.L2,"Please enter L₂.") && requirePositive(dims.W2,"Please enter W₂.");
}
return true;
};
if (targetKey === "V") {
if (!validateForVolume()) return { ok: false };
const V = s.volume(dims);
if (!(Number.isFinite(V) && V > 0)) {
showError("Inputs produce an invalid volume. Please check values.");
return { ok: false };
}
return { ok: true, kind: "volume", shapeKey, targetKey, V_m3: V, dims, formula: s.formulas.V, vizKey: s.vizKey };
}
if (!validateForSolveDim()) return { ok: false };
const x_m = s.solve(targetKey, dims, V_in_m3);
if (!(Number.isFinite(x_m) && x_m > 0)) {
showError("Inputs produce an invalid dimension. Check volume and other dimensions.");
return { ok: false };
}
return { ok: true, kind: "dimension", shapeKey, targetKey, V_m3: V_in_m3, x_m, dims, formula: s.formulas[targetKey] || "", vizKey: s.vizKey };
};
const computeMassFromDensity = (V_m3) => {
const rhoRaw = num(\(("#rhoVal").value);
if (!(Number.isFinite(rhoRaw) && rhoRaw > 0)) return { ok: false, err: "Please enter a valid density (ρ > 0)." };
const rhoUnit = \)("#rhoUnit").value;
const rho = rhoRaw * (RHO_TO_KGM3[rhoUnit] ?? 1);
const m_kg = rho * V_m3;
return { ok: true, rho, m_kg };
};
// ---------- Rendering ----------
const renderVolumeResult = (label, V_m3, extraBlockHtml = "") => {
const outUnit = getVolUnit();
const Vout = m3ToUnit(V_m3, outUnit);
const eq = equivalents(V_m3);
\(("#noResultsHint").style.display = "none";
\)("#result").innerHTML = `
\({esc(label)}
Volume: \){esc(fmt(Vout, 8))} \({esc(unitLabel(outUnit))}
Helpful equivalents:
\){esc(fmt(eq.L, 6))} L
\({esc(fmt(eq.mL, 6))} mL
\){esc(fmt(eq.cm3, 6))} cm³
\({esc(fmt(eq.ft3, 6))} ft³
\){esc(fmt(eq.gal, 6))} US gal
\({extraBlockHtml}
`;
};
const renderDimensionResult = (targetKey, x_m, V_m3, extraBlockHtml = "") => {
const lenUnit = getLenUnit();
const fromM = 1 / (LEN_TO_M[lenUnit] ?? 1);
const x_out = x_m * fromM;
const nice = {
a: "Edge (a)",
L: "Length (L)",
W: "Width (W)",
H: "Height (H)",
r: "Radius (r)",
h: "Height (h)",
b: "Triangle base (b)"
}[targetKey] || "Dimension";
\)("#noResultsHint").style.display = "none";
\(("#result").innerHTML = `
Solved dimension
\){esc(nice)}: \({esc(fmt(x_out, 8))} \){esc(lenUnit)}
Given volume: \({esc(fmt(m3ToUnit(V_m3, getVolUnit()), 8))} \){esc(unitLabel(getVolUnit()))}
\({extraBlockHtml}
`;
};
const renderSteps = (blocks) => {
if (!\)("#showSteps").checked) { \(("#steps").innerHTML = ""; return; }
\)("#steps").innerHTML = blocks.map(b => `
\({esc(b.title)}
\){b.items.map(li => `\({li} `).join("")}
`).join("");
};
const renderViz = (html) => {
\)("#viz").innerHTML = \(("#showViz").checked ? html : "";
enableDragging();
};
// ---------- Main compute ----------
const compute = () => {
clearOutput();
const a = computeOneShape(getShapeKey(), getTargetKey(), "");
if (!a.ok) return;
let Vtotal_m3 = a.V_m3;
let V1_m3 = a.V_m3;
let V2_m3 = NaN;
const compositeOn = \)("#compositeOn").checked;
if (compositeOn) {
const b = computeOneShape(getShapeKeyB(), getTargetKeyB(), "b_");
if (!b.ok) return;
V2_m3 = b.V_m3;
const op = \(("#compOp").value;
Vtotal_m3 = (op === "sub") ? (V1_m3 - V2_m3) : (V1_m3 + V2_m3);
if (!(Number.isFinite(Vtotal_m3) && Vtotal_m3 > 0)) {
return showError("Composite volume is not positive. If subtracting, ensure V₁ > V₂.");
}
}
// Density
let massBlockHtml = "";
let massSteps = null;
if (\)("#densityOn").checked) {
const m = computeMassFromDensity(Vtotal_m3);
if (!m.ok) return showError(m.err);
const m_g = m.m_kg * 1000;
const m_lb = m.m_kg / 0.45359237;
massBlockHtml = `
Mass from density:
ρ = \({esc(fmt(m.rho, 6))} kg/m³
m = \){esc(fmt(m.m_kg, 8))} kg
= \({esc(fmt(m_g, 8))} g • \){esc(fmt(m_lb, 8))} lb
`;
massSteps = {
title: "Mass from density (optional)",
items: [
`Convert density to SI:
ρ in kg/m³`,
`Compute mass:
m = ρ·V `,
`Convert mass to g and lb (optional)`
]
};
}
// Result
const targetA = getTargetKey();
if (targetA === "V") {
const label = compositeOn ? "Composite volume (Vtotal)" : `Volume (\({SHAPES[getShapeKey()].name})`;
renderVolumeResult(label, Vtotal_m3, massBlockHtml);
} else {
const outUnit = getVolUnit();
const compositeHtml = compositeOn
? `
Composite total volume:
\){esc(fmt(m3ToUnit(Vtotal_m3, outUnit), 8))} \({esc(unitLabel(outUnit))}
`
: "";
renderDimensionResult(targetA, a.x_m, a.V_m3, compositeHtml + massBlockHtml);
}
// Visual
renderViz(
draggableViz({
aKey: SHAPES[getShapeKey()].vizKey || "cube",
bKey: compositeOn ? (SHAPES[getShapeKeyB()].vizKey || "rectPrism") : null,
compositeOn
})
);
// Steps
const stepsBlocks = [];
const shapeA = SHAPES[getShapeKey()];
if (targetA === "V") {
stepsBlocks.push({
title: "Step-by-step (Shape A)",
items: [
`Convert dimensions to meters (
m ).`,
`Use formula:
\){esc(shapeA.formulas.V)} .`,
`Compute volume in
m³ , then convert to your chosen unit.`
]
});
} else {
stepsBlocks.push({
title: "Step-by-step (Solve for missing dimension — Shape A)",
items: [
`Convert volume to
m³ and other dimensions to meters.`,
`Rearrange formula:
\({esc(shapeA.formulas[targetA] || "")} .`,
`Compute the missing dimension and convert back to your chosen length unit.`
]
});
}
if (compositeOn) {
stepsBlocks.push({
title: "Composite volume (optional)",
items: [
`Compute
V₁ and
V₂ .`,
`Combine:
Vtotal = V₁ ± V₂ .`
]
});
}
if (massSteps) stepsBlocks.push(massSteps);
renderSteps(stepsBlocks);
};
// ---------- Presets ----------
const setModeA = (shapeKey, targetKey) => {
\)("#shape").value = shapeKey;
fillTargets(shapeKey, \(("#target"));
\)("#target").value = targetKey;
applyShapeUI(shapeKey, targetKey, "");
renderShapePreview();
};
const setModeB = (shapeKey, targetKey) => {
\(("#shapeB").value = shapeKey;
fillTargets(shapeKey, \)("#targetB"));
\(("#targetB").value = targetKey;
applyShapeUI(shapeKey, targetKey, "b_");
};
const clearFields = () => {
[
"d1","d2","d3","extra","extra2","extra3","vIn",
"b_d1","b_d2","b_d3","b_extra","b_extra2","b_extra3","b_vIn",
"rhoVal"
].forEach(id => {
const el = \)(`#\({id}`);
if (el) el.value = "";
});
};
const applyPreset = (key) => {
\)("#compositeOn").checked = false;
\(("#densityOn").checked = false;
syncStretchPanels();
clearFields();
\)("#lenUnit").value = "cm";
\(("#volUnit").value = "L";
if (key === "cube10cm") {
setModeA("cube", "V");
\)("#d1").value = "10";
\(("#lenUnit").value = "cm";
\)("#volUnit").value = "cm3";
}
if (key === "aquarium") {
setModeA("rectPrism", "V");
\(("#d1").value = "60";
\)("#d2").value = "30";
\(("#d3").value = "40";
\)("#lenUnit").value = "cm";
\(("#volUnit").value = "L";
}
if (key === "cylBeaker") {
setModeA("cylinder", "h");
\)("#lenUnit").value = "cm";
\(("#volUnit").value = "mL";
\)("#d1").value = "3.5";
\(("#vIn").value = "250";
}
if (key === "sphere8in") {
setModeA("sphere", "V");
\)("#lenUnit").value = "in";
\(("#volUnit").value = "in3";
\)("#d1").value = "4";
}
if (key === "holeInBlock") {
\(("#compositeOn").checked = true;
syncStretchPanels();
\)("#compOp").value = "sub";
setModeA("rectPrism", "V");
\(("#lenUnit").value = "cm";
\)("#volUnit").value = "cm3";
\(("#d1").value = "10";
\)("#d2").value = "8";
\(("#d3").value = "6";
setModeB("cylinder", "V");
\)("#b_d1").value = "1";
\(("#b_d2").value = "6";
}
if (key === "massWater") {
\)("#densityOn").checked = true;
syncStretchPanels();
setModeA("cylinder", "h");
\(("#lenUnit").value = "cm";
\)("#volUnit").value = "mL";
\(("#d1").value = "5";
\)("#vIn").value = "500";
\(("#rhoVal").value = "1";
\)("#rhoUnit").value = "gml";
}
if (key === "tube") {
setModeA("hollowCylinder", "V");
\(("#lenUnit").value = "cm";
\)("#volUnit").value = "mL";
\(("#d1").value = "2";
\)("#d2").value = "1.5";
\(("#d3").value = "30";
}
if (key === "capsule") {
setModeA("capsule", "V");
\)("#lenUnit").value = "cm";
\(("#volUnit").value = "mL";
\)("#d1").value = "1.2";
\(("#d2").value = "5";
}
clearOutput();
compute();
};
// ---------- Swap shapes (A ↔ B) ----------
const swapShapes = () => {
const shapeA = \)("#shape").value;
const targA = \(("#target").value;
const shapeB = \)("#shapeB").value;
const targB = \(("#targetB").value;
\)("#shape").value = shapeB;
fillTargets(shapeB, \(("#target"));
\)("#target").value = targB;
\(("#shapeB").value = shapeA;
fillTargets(shapeA, \)("#targetB"));
\(("#targetB").value = targA;
const idsA = ["d1","d2","d3","extra","extra2","extra3","vIn"];
const idsB = ["b_d1","b_d2","b_d3","b_extra","b_extra2","b_extra3","b_vIn"];
idsA.forEach((id, i) => {
const aEl = \)(`#\({id}`);
const bEl = \)(`#\({idsB[i]}`);
if (!aEl || !bEl) return;
const tmp = aEl.value;
aEl.value = bEl.value;
bEl.value = tmp;
});
syncPrimaryUI();
syncBUI();
clearOutput();
};
// ---------- Wiring ----------
const wire = () => {
fillTargets(getShapeKey(), \)("#target"));
fillTargets(getShapeKeyB(), \(("#targetB"));
applyShapeUI(getShapeKey(), getTargetKey(), "");
applyShapeUI(getShapeKeyB(), getTargetKeyB(), "b_");
syncStretchPanels();
renderShapePreview();
clearOutput();
\)("#shape").addEventListener("change", syncPrimaryUI);
\(("#target").addEventListener("change", syncPrimaryUI);
\)("#shapeB").addEventListener("change", syncBUI);
\(("#targetB").addEventListener("change", syncBUI);
\)("#lenUnit").addEventListener("change", () => {
syncPrimaryUI();
if (\(("#compositeOn").checked) syncBUI();
});
\)("#volUnit").addEventListener("change", () => {
syncPrimaryUI();
if (\(("#compositeOn").checked) syncBUI();
});
\)("#compositeOn").addEventListener("change", syncStretchPanels);
\(("#densityOn").addEventListener("change", syncStretchPanels);
\)("#compOp").addEventListener("change", clearOutput);
\(("#swapShapesBtn").addEventListener("click", swapShapes);
[
"#d1","#d2","#d3","#extra","#extra2","#extra3","#vIn",
"#b_d1","#b_d2","#b_d3","#b_extra","#b_extra2","#b_extra3","#b_vIn",
"#rhoVal","#rhoUnit"
].forEach(sel => {
const el = \)(sel);
if (!el) return;
el.addEventListener("input", clearOutput);
el.addEventListener("change", clearOutput);
el.addEventListener("keydown", (e) => { if (e.key === "Enter") compute(); });
});
\(("#calculateButton").addEventListener("click", compute);
\)("#resetButton").addEventListener("click", () => {
\(("#shape").value = "cube";
fillTargets("cube", \)("#target"));
\(("#target").value = "V";
\)("#lenUnit").value = "m";
\(("#volUnit").value = "m3";
\)("#compositeOn").checked = false;
\(("#densityOn").checked = false;
\)("#compOp").value = "add";
\(("#shapeB").value = "rectPrism";
fillTargets("rectPrism", \)("#targetB"));
\(("#targetB").value = "V";
\)("#rhoUnit").value = "kgm3";
clearFields();
\(("#showSteps").checked = true;
\)("#showViz").checked = true;
syncPrimaryUI();
syncBUI();
syncStretchPanels();
renderShapePreview();
clearOutput();
});
$\(("button.chip").forEach(btn => {
btn.addEventListener("click", () => {
const p = btn.getAttribute("data-preset");
if (p) applyPreset(p);
});
});
\)("#volForm").addEventListener("keydown", (e) => {
if (e.key === "Enter") compute();
});
};
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", wire);
else wire();
})();
If you paste these two blocks and something still looks “cut off”, tell me which file (HTML or JS) and where it stops, and I’ll resend that part in smaller chunks (so it won’t get interrupted).
You said:
please Lex, could you provide application and faqs schemas for this html:
How to use this calculator
Choose a shape and what you want to solve for (volume or a dimension).
Pick your length unit and a volume output unit .
Optional stretch features: enable Composite (add/subtract) and/or Mass from density .
Click Calculate to get the answer + steps + quick visual (drag shapes in composite mode).
Formulas & Equations Used
Example Problem & Step-by-Step Solution
Example 1 — Aquarium volume
Box dimensions: L=60 cm, W=30 cm, H=40 cm
Compute volume: V = L·W·H = 60·30·40 = 72,000 cm³
Convert: 72,000 cm³ = 72,000 mL = 72 L
Example 2 — Beaker height from volume (cylinder)
Given: V = 250 mL , r = 3.5 cm
Convert volume: 250 mL = 250 cm³
Rearrange: h = V/(πr²)
Compute: h = 250 /(π·3.5²) ≈ 6.50 cm
Example 3 — Tube (hollow cylinder) volume
Given: outer radius R=2.0 cm , inner radius r=1.5 cm , height h=30 cm
Use: V = πh(R² − r²)
Compute: V = π·30·(2² − 1.5²) = π·30·(4 − 2.25) = π·52.5 ≈ 164.9 cm³
Convert: 164.9 cm³ ≈ 164.9 mL
Frequently Asked Questions
Q: Is cm³ the same as mL?
Yes. 1 cm³ = 1 mL .
Q: Can I solve for height or radius instead of volume?
Yes—use the Solve for dropdown to compute a missing dimension (supported shapes will show options).
Q: What does “composite volume” mean?
It means adding or subtracting volumes (like a block with a drilled hole).
ChatGPT said:
Related practice Related calculators