diff --git a/busbar_export.py b/busbar_export.py
index 602e4a7..d2d8a03 100644
--- a/busbar_export.py
+++ b/busbar_export.py
@@ -46,6 +46,7 @@ from build123d import (
Sketch,
add,
export_step,
+ export_stl,
extrude,
)
@@ -299,8 +300,29 @@ def to_svg(payload: dict) -> bytes:
return path.read_bytes()
+def to_stl(payload: dict) -> bytes:
+ """STL with the busbars extruded to a printable plate.
+
+ STL is inherently 3D, so extrusion is forced regardless of the payload
+ flag. Thickness defaults to 2 mm when missing or <0.5 mm (slicers can't
+ do 0.2 mm sheet — that figure is for the 'thin nickel strip' STEP case).
+ """
+ busbars, extrude_flag, thickness = parse_payload(payload)
+ if thickness < 0.5:
+ thickness = 2.0
+ shapes = build_shapes(busbars, extrude_flag=True, thickness=thickness)
+ compound = _as_compound(shapes)
+ with TemporaryDirectory() as tmp:
+ path = Path(tmp) / "busbars.stl"
+ # build123d's export_stl takes a single shape; the Compound carries
+ # all busbars together so the slicer sees them as one job.
+ export_stl(compound, str(path))
+ return path.read_bytes()
+
+
WRITERS = {
"step": (to_step, "application/step", "step"),
"dxf": (to_dxf, "image/vnd.dxf", "dxf"),
"svg": (to_svg, "image/svg+xml", "svg"),
+ "stl": (to_stl, "model/stl", "stl"),
}
diff --git a/static/index.html b/static/index.html
index 1ccbd91..c8ee782 100644
--- a/static/index.html
+++ b/static/index.html
@@ -115,7 +115,23 @@
-
+
+
+
+
+ FDM test print
+
+
+
+
+
+
+
+
diff --git a/static/js/app.js b/static/js/app.js
index f03b378..4ea9aad 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -157,6 +157,16 @@
$("btn-export-dxf" ).addEventListener("click", () => Exporter.exportFormat("dxf", state, params));
$("btn-export-svg" ).addEventListener("click", () => Exporter.exportFormat("svg", state, params));
+ // ---- FDM test print -----------------------------------------------------
+ // Forces extrusion at the FDM thickness, regardless of the 'Extrude solid'
+ // checkbox above (which keeps owning the thin nickel-strip case).
+ function _exportFdm(fmt) {
+ const t = Math.max(0.5, +$("p-fdm-thickness").value || 2);
+ Exporter.exportFormat(fmt, state, { ...params, extrude: true, thickness: t });
+ }
+ $("btn-export-fdm-stl") .addEventListener("click", () => _exportFdm("stl"));
+ $("btn-export-fdm-step").addEventListener("click", () => _exportFdm("step"));
+
// ---- viewport init ------------------------------------------------------
Viewport.init($("viewport"), state, params, {
onCellClick: (cellId, mods) => {
diff --git a/static/styles.css b/static/styles.css
index 24e9c0b..7ad5de2 100644
--- a/static/styles.css
+++ b/static/styles.css
@@ -420,6 +420,36 @@ label.checkbox {
color: var(--text);
}
+/* FDM test-print block inside the Params panel */
+.fdm-block {
+ margin-top: 10px;
+ padding-top: 10px;
+ border-top: 1px solid var(--border);
+}
+.fdm-title {
+ font-size: 11px;
+ font-weight: 600;
+ color: var(--muted);
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ margin: 0 0 6px;
+}
+.fdm-row {
+ display: flex;
+ align-items: end;
+ gap: 10px;
+}
+.fdm-row label {
+ flex: 1;
+}
+.fdm-buttons {
+ display: flex;
+ gap: 4px;
+}
+.fdm-buttons button {
+ min-width: 50px;
+}
+
input[type=number], input[type=text], select, textarea {
background: var(--bg);
color: var(--text);