mirror of
https://github.com/gittrahan/support-fins.git
synced 2026-10-21 19:32:48 -05:00
* 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>
61 lines
3.2 KiB
JavaScript
61 lines
3.2 KiB
JavaScript
// Support Fins -- Draw mode for the plugins: one wall along a line the user drew.
|
|
//
|
|
// The website's Draw mode (web/ui/walls.js rebuildDrawn) calls web/draw.js
|
|
// drawnWall with the part's triangles and the two endpoints in the seated print
|
|
// frame. This does the same for a host: the part as computeFins takes it (posed
|
|
// triangle soup, mm, z up, anywhere on the plate) and the two points in that same
|
|
// frame -- typically where the user's two clicks hit the part.
|
|
//
|
|
// Same seat and clearances as computeFins (seat.js), so a drawn wall grips with the
|
|
// tines and gap the auto fins next to it use, and doesn't move with plate position.
|
|
import { buildTopology, analyze, IDENTITY3 } from '../../../web/overhangs.js';
|
|
import { applyTunables } from '../../../web/fins.js';
|
|
import { drawnWall } from '../../../web/draw.js';
|
|
import { ENGINE_DEFAULTS } from './fins_entry.js';
|
|
import { seatSoup, clearances, flatten, snap } from './seat.js';
|
|
|
|
/**
|
|
* @param positions the part, as for computeFins
|
|
* @param a, b [x, y, z] the wall's ends, in the same frame as `positions`
|
|
* @param options as for computeFins (material, tines, tineDensity, layerHeight,
|
|
* threshold); the rest is ignored
|
|
* @returns {{ ok: true, triangles: Float32Array, offset, stats: {length, height, tines, partAttached} }
|
|
* | { ok: false, reason: string }}
|
|
* triangles are in the seated frame, mapped back exactly as computeFins':
|
|
* input = seated - offset. `reason` is the site's words (too short, at the plate...).
|
|
*/
|
|
export function drawWall(positions, a, b, options = {}) {
|
|
const opts = { ...ENGINE_DEFAULTS, ...options };
|
|
for (const [name, p] of [['a', a], ['b', b]]) {
|
|
if (!Array.isArray(p) || p.length !== 3 || !p.every(Number.isFinite)) {
|
|
throw new Error(`${name} must be [x, y, z], got ${JSON.stringify(p)}`);
|
|
}
|
|
}
|
|
const { tunables } = clearances(opts);
|
|
const { pos, shift } = seatSoup(positions);
|
|
const topo = buildTopology({ getAttribute: (k) => (k === 'position' ? { array: pos } : null) });
|
|
const result = analyze(topo, opts.threshold, IDENTITY3);
|
|
const off = result.offset;
|
|
// The part in the print frame, as the site's partPrintTriangles builds it.
|
|
const tris = new Float64Array(pos.length);
|
|
for (let i = 0; i < pos.length; i += 3) {
|
|
tris[i] = pos[i] + off.x; tris[i + 1] = pos[i + 1] + off.y; tris[i + 2] = pos[i + 2] + off.z;
|
|
}
|
|
const seat = (p) => [snap(p[0] - shift.x) + off.x, snap(p[1] - shift.y) + off.y, snap(p[2] - shift.z) + off.z];
|
|
// The site sets these on its settings change; buildFins sets them per build. A
|
|
// drawn wall goes through neither, so set them here -- the full set, every call.
|
|
applyTunables(tunables);
|
|
const r = drawnWall(seat(a), seat(b), tris, 0, {
|
|
tines: opts.tines, tineDensity: opts.tineDensity, layerHeight: opts.layerHeight,
|
|
plateOnly: opts.plateOnly, topo, rot: IDENTITY3, offset: off,
|
|
});
|
|
if (!r.ok) return { ok: false, reason: r.reason };
|
|
return {
|
|
ok: true,
|
|
triangles: flatten(r.tris),
|
|
offset: { x: off.x - shift.x, y: off.y - shift.y, z: off.z - shift.z },
|
|
// partAttached: it stands on the part below, not the plate (the site says so)
|
|
stats: { length: r.length, height: r.height, tines: r.tines ?? 0, partAttached: !!r.partAttached },
|
|
};
|
|
}
|