Files
support-fins/tests/plate_only.test.js
T
Matthew Trahanandmatthewtrahan f2147bd19f Plate only: every support on the build plate, none on the part (#218) (#221)
* 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>
2026-10-08 22:32:15 -05:00

64 lines
3.5 KiB
JavaScript

// PLATE ONLY (#218): every support stands on the build plate, none on the part. A
// line with part under it is dropped and counted (skipped.onPart), never stilted to
// the plate through that part; a drawn line there is refused with the reason.
import { WEB, block, fins, analyze, loadModel, rotY, assert } from './_util.js';
const { drawnWall } = await import(`${WEB}draw.js`);
// an L: a base block (top z=5) and a shelf 30 mm above it -- a wall under the shelf
// stands on the base
const L = new Float32Array([...block(-40, 40, -10, 10, 0, 5), ...block(-40, 40, -10, 10, 35, 39)]);
// the shelf alone, over open plate
const SHELF = new Float32Array(block(-40, 40, -10, 10, 35, 39));
Deno.test('plate only: a drawn line with part under it is refused, with the reason', () => {
const on = drawnWall([-30, 0, 35], [30, 0, 35], L, 0);
assert(on.ok && on.partAttached, 'without Plate only the wall stands on the base');
const r = drawnWall([-30, 0, 35], [30, 0, 35], L, 0, { plateOnly: true });
assert(!r.ok, 'Plate only built a wall over the part');
assert(/Plate only/.test(r.reason), `reason doesn't name the setting: ${r.reason}`);
});
Deno.test('plate only: a drawn line over open plate builds the same wall', () => {
const a = drawnWall([-30, 0, 35], [30, 0, 35], SHELF, 0);
const b = drawnWall([-30, 0, 35], [30, 0, 35], SHELF, 0, { plateOnly: true });
assert(a.ok && b.ok && !b.partAttached, 'the plate wall should build either way');
assert(JSON.stringify(a.tris) === JSON.stringify(b.tris), 'Plate only changed a plate wall');
});
// the portal on its side (Y90): Auto stands its one wall on the lower leg
const portal = () => {
const topo = loadModel('portal'), rot = rotY(90);
return { topo, rot, res: analyze(topo, 45, rot) };
};
for (const mode of ['auto', 'full']) {
Deno.test(`plate only: ${mode} builds nothing on the part, and counts what it left`, () => {
const { topo, rot, res } = portal();
const base = fins.buildFins(topo, res, rot, { mode, bedPad: true });
assert(base.props.some((q) => q.partAttached), `${mode}: the fixture lost its wall on the part`);
const { res: res2 } = portal();
const b = fins.buildFins(topo, res2, rot, { mode, bedPad: true, plateOnly: true });
assert(!b.props.some((q) => q.partAttached), `${mode}: a wall still stands on the part`);
assert(b.skipped.onPart > 0, `${mode}: the dropped wall isn't counted`);
// every support that IS built reaches down to the plate
for (const f of b.fins) {
let low = Infinity;
for (const [i, j] of f.triRanges) for (let k = i; k < j; k++) low = Math.min(low, b.triangles[k][2]);
assert(low < 0.5, `${mode}: a ${f.kind} bottoms out at z ${low.toFixed(2)}, off the plate`);
}
// Full refuses lines over the part before building them, not one wall at a time
// until it hits its try limit (and then blames the limit)
if (mode === 'full') assert(!b.fill.capped, `full: capped after ${b.fill.tries} tries`);
});
}
Deno.test('plate only: off by default, and a part with nothing over it is unchanged', () => {
const topo = loadModel('lbracket'), rot = rotY(35);
const a = fins.buildFins(topo, analyze(topo, 45, rot), rot, { mode: 'auto', bedPad: true });
const b = fins.buildFins(topo, analyze(topo, 45, rot), rot, { mode: 'auto', bedPad: true, plateOnly: true });
assert(a.skipped.onPart === 0, 'onPart counted with Plate only off');
assert(a.triangles.length === b.triangles.length
&& a.triangles.every((p, i) => p.every((v, k) => v === b.triangles[i][k])), 'lbracket changed');
});