options.json gains nozzle (hostSupplied: slicer, 0.2-0.8, default 0.4). The
entry sends it in tunables on every call, so a host's long-lived engine context
can't keep the last part's widths. Cura: machine_nozzle_size on the part's
extruder. Orca: nozzle_diameter, the wall extruder's entry. FreeCAD, Blender,
Fusion, SolidWorks and the CLI pick it up from the schema.
* engine: plateOnly -- every support on the build plate, none on the part (#218). Auto and the short walls drop a line the part-attached path would take (built or floored) and count it as skipped.onPart, never stilting it to the plate through the part; drawnWall refuses a line with part under it, naming the setting, and its squat fallback skips the part; the fill pass never retries a wall on the part. Off by default: sweep vs main identical. tests: plate_only -- a drawn line over the part refused, over open plate byte-identical; Auto and Full on the portal at Y90 build nothing on the part and count it; off, the lbracket is unchanged
* ui + plugins: a Plate only checkbox under Walls (#218) -- read by the auto build and the drawn walls; the readout says how many supports it left off and why, and names it when it emptied a part; undoable; German strings. options.json gains plateOnly, so every plugin dialog shows it, and both entries pass it on. tests: the dead-control check runs it on the portal at Y90 (the lbracket has no wall on the part); FreeCAD and Blender unit fixtures list it
* plate only: Full refuses a line over the part before building it, and a mixed line keeps its plate stretch (review) -- the local pass turned the on-part retry back on, and every try reached drawnWall to be refused: the portal at Y90 spent all 120 tries and blamed the wall limit. stationClear's `plate` now refuses a station with part under it on every try. Auto hands a line the part-attached path claims to the plate path (its column probes trim the stations over the part) instead of dropping it whole. The readout names Plate only when it emptied a part in any mode, not only Prop; German help line. tests: every built support reaches the plate, Full isn't capped
---------
Co-authored-by: matthewtrahan <mathyout@pm.me>
overFaces was every face past the threshold, but the site paints only the faces in
regions it supports red (res.kept) and the too-small ones amber. A host painting
overFaces red would have called slivers overhangs that no fin targets. Now
overFaces = red, smallFaces = amber, both pinned to paintOverhangs' rule (a shelf +
2 mm nub part gives an amber face, so that half of the check runs).
drawWall: the site comparison checks where the wall is, not just its counts, on an
off-centre part; layer height 0.1 / 0.3 reach the wall. host_draw_wall refuses a
NaN end in Python instead of a raw JS parse error; stats say partAttached.
engine/draw_entry.js drawWall(soup, a, b, options): the part as computeFins takes it
and the two clicked points in the same frame -> web/draw.js drawnWall, called as
web/ui/walls.js calls it (print-frame part, the material's tunables set). Same seat
as computeFins, so the wall is bit-identical wherever the part sits; a wall it can't
build comes back with the site's reason ("wall too short ..."). Bridge: drawWallB64;
Python: host_draw_wall.
Tests pin it to the site's wall (PLA and PETG), plate-position independent, no
clearance leak into the next wall or the next computeFins.
A host that wants each wall as its own object (Blender next) had to call buildFins
itself to get the fin ranges, skipping the entry's seat and material clearances.
computeFins now returns pieces ({id, kind, ranges} in triangles of its soup; every
triangle in exactly one) and overFaces (input faces analyze() marks red). Python:
host_compute_pieces; host_compute is unchanged for Orca, Cura and FreeCAD.
The seat (centre + 1 nm snap) and the material clearances move to engine/seat.js
so the next entry (Draw mode) seats a part exactly the same way.
- --rot is three.js Euler XYZ (Rx*Ry*Rz), the order printfins.com shows under
the part, so a pose found on the site carries over
- --fins-only maps the fins back onto the part as it is in the file; it can't
take --rot, and writes nothing when there are no fins
- a Support Fins 3MF (part + fins) is refused instead of finned again
- settings the site would hide warn on stderr; -o can't overwrite the input;
-- ends the options; Node 20.10+
- the summary rounds ties to even, word for word with Python's host_report
The entry's soup was fins+braces | pad, split only by finTriangles, so Fusion built
a braced 150 mm post as 4 'Support fin' bodies and headlined 4 fins. Now
finTriangles is fins only and swayTriangles follows it (fins | braces | pad); Fusion
splits three ways and counts fins from fins alone. Readouts (host_report) name sway
braces apart from walls. Fusion's Grip from / Brace tine spacing now need Tines too,
as on the site. Fusion add-in 0.6.1.
With Tines off every wall is a plain prop, which the entry's stats didn't carry, so
host_report said "0 walls" under a part with four. stats.props is new and walls =
braces + props, as the site counts them. host_options now raises a ValueError with
just the engine's message; V8's text carried the whole bundle source after it.
"false" was true, '' and None became the minimum, and a percent sent without the /100
was clamped to 1 (coverage 50 -> 1): all silent. Now bools take true/false or their
strings, null/'' mean the default, and anything out of range throws, naming the /100.
The slicer's own layer height is used as is (the site doesn't clamp it either).
Python hosts get host_schema / host_options / host_visible through the bridge. The
defaults test compared a cube with no fins (0 == 0); it now uses lbracket and the
sway-braced bar.
showIf is now a list (all must hold) of bool keys, {key, in} and {any}, enough for the
site's rules: brace grip/spacing need sway AND tines; layer height shows with tines or
a one-layer pad. Hosts pass dialog values through optionsFromDialog (nest, clamp,
check, drop sway when off) and ask optionVisible, so no host re-implements either.
Matthew's calls: layerHeight in (hostSupplied: slicer, CAD hosts show it), the
overhang angle in, placement mode stays Auto only. Tests: every site control is in
the schema or listed as excluded with a reason; pad styles are the entry's own list.
Material, tines, tine grip, bed pad (Off/Auto/Light/Sure hold), sway braces, cutouts
and coverage, with the site's labels and tooltips. Tests pin every default, range and
choice to web/index.html, the choices to the engine's own lists, and check each option
changes the fins (no dead control). The entry takes its defaults from the file, and
padStyle 'off' is the site's Off.
The PETG check compared counts only, which PLA passes too; it now compares every
vertex (site copy snapped to 1 nm like the entry, else tine noise). New: PLA profile
must equal FIN/PAD/PROP/PERP/SWAY defaults; sway braces carry the material and a
null gap/bite from a host counts as unset. Each option test resets in finally.
The entry sends the full clearance set on every call, as the site's worker does:
buildFins keeps it as module state and a plugin's V8 context lives across calls,
so a partial set would leak PETG into the next PLA run. Unknown values throw
instead of silently building PLA. Sway braces take the material's gap and bite.
The braces (web/sway.js) shipped on the site in #29, but no plugin could
reach them: computeFins forwards an explicit list of options to buildFins,
and `sway` was not on it, so every host silently got fins only.
It now forwards `sway` whole -- a host passes the same object the website's
options panel builds -- and reports the result apart from the fins:
swayBraces / swayTines / swaySkipped, plus swayReason for a host that asked
and got none. Off by default, so nothing changes for a caller that says
nothing: ENGINE_DEFAULTS.sway is null and the geometry is byte-identical.
Braces land in the same soup as the fins, so stats.finTriangles counts them
too; splitting them per body would need per-brace ranges this entry point
does not expose yet. Noted where it matters.
Tests: off by default is unchanged, { on: true } braces a tall post and says
so, the counts match the website brace for brace, tine spacing and "grip
from" reach the braces, and a part too short to brace reports why while its
fins still build. 26 pass in this suite, 200 across the JS suites.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
floatingPieces() (overhangs.js) finds any closed piece of the mesh that
neither reaches the plate nor rests on another piece (within 0.3 mm).
buildFins() returns them as built.floating and the readout says so; the
plugins' stats carry floating and floatingDrop.
Split out of #84 and ported onto the fins.js split.
Orca's engine glue wasn't Orca-specific. Move it where the Fusion add-in (and any
later JS-running plugin) can use it too:
- plugins/orca/panel/fins_entry.js -> plugins/shared/engine/fins_entry.js
- plugins/orca/panel/engine_bridge.js -> plugins/shared/engine/bridge.js
- the esbuild step in orca/build.py -> plugins/shared/bundle.py (bundle_engine)
- bridge/entry tests, sensitivity repro and ENGINE-SENSITIVITY.md move with them
No behaviour change: the built Orca plugin and its engine bundle are
byte-identical to main's. The Python V8 loader stays in Orca for now; it moves
to shared when Fusion needs it.