engine: a low-edge row is blocked by any row that can build within a brim width, behind the edge too (a tilted cube at X60Z30 got a second squat wall 1.6 mm from main's, one fused slab); only rows in the low band itself don't count; test pins no fused squat brims on the slab and that cube (review)

This commit is contained in:
matthewtrahan authored and Matthew Trahan committed 2026-10-05 10:49:39 -05:00
1 parent 92bf9d1e6c
commit 7ef4cfa4e5
4 files changed
+68 -10

No files matched your search

+2 -2
View File
@@ -97,8 +97,8 @@ welts.
toward the plate (a curved face lying on the bed), no wall fits under the squat floor
(0.6 mm wall + gap), and Auto's rows land on a pitch that ignores it. So the edge of
that band -- the first row position where a squat wall and its brim clear the part --
gets a row when no row stands within a brim width (5 mm) of it (`lowEdges` in
`prop/tracks.js`). Added, never moved: moving a row onto the edge lost real walls. Bands
gets a row when no row that can build stands within a brim width (5 mm) of it
(`lowEdges` in `prop/tracks.js`; a row inside the band never builds, so it doesn't count). Added, never moved: moving a row onto the edge lost real walls. Bands
under `lowBandMin` (1 mm) are sampling dips, not the plate.
- **Scale-aware profile.** The prototype's foot/chamfer/tip are fixed, which degenerates
into a 14 mm splayed sheet when the overhang sits low. Foot width must scale with wall
+3 -1
View File
@@ -75,7 +75,9 @@ of bow) lying on the plate gets a wall at **each edge of its low band** (where t
underside climbs past the squat floor, ~14.6 mm out) at sparse, default and dense.
Main put the nearest at 23.4 mm, ~9 mm of near-flat underside bare on each side
(a customer's DRO housing on its bowed back; at the default coverage one side got
nothing). The row is added beside the layout, never moved into it.
nothing). The row is added beside the layout, never moved into it, and **no two
squat brims fuse** -- on the slab, and on a 40 mm cube at X60Z30 sparse, where the
first version added a squat wall 1.6 mm from the one main builds (review).
**`pad.test.js`** -- the bed pad styles (FIN-SPEC "Bed pad styles"):
- **Sure hold** is a smooth oval that conforms under a tilted part's flank, stays
+52 -1
View File
@@ -4,7 +4,7 @@
// housing on its bowed back at X-90: at the default coverage one side got no row
// at all and the other its first at 18 mm). The row nearest the band goes at
// its edge -- ADDED, never moved, so every row that built before still does.
import { buildTopology, analyze, fins, prop, isClosed, assert } from './_util.js';
import { buildTopology, analyze, fins, prop, block, isClosed, rotX, assert } from './_util.js';
const { PROP } = prop;
const I = [1, 0, 0, 0, 1, 0, 0, 0, 1];
@@ -47,6 +47,32 @@ const wallYs = (tris, coverage) => {
return b.props.map((q) => q.line.reduce((s, p) => s + p[1], 0) / q.line.length - res.offset.y).sort((a, c) => a - c);
};
const build = (tris, rot, coverage) => {
const topo = topoOf(tris), res = analyze(topo, 45, rot);
return fins.buildFins(topo, res, rot, { mode: 'auto', bedPad: true, tines: true, coverage });
};
// Two squat walls whose brims (2 x squatBrimW wide) overlap: parallel, closer
// than a brim width, and running side by side for some length. One fused slab.
function fusedSquats(b) {
const sq = b.props.filter((q) => q.squat);
const out = [];
for (let i = 0; i < sq.length; i++) for (let j = i + 1; j < sq.length; j++) {
const A = sq[i].line, B = sq[j].line;
const a0 = A[0], a1 = A[A.length - 1];
let ux = a1[0] - a0[0], uy = a1[1] - a0[1];
const un = Math.hypot(ux, uy); ux /= un; uy /= un;
const b0 = B[0], b1 = B[B.length - 1];
const vx = b1[0] - b0[0], vy = b1[1] - b0[1], vn = Math.hypot(vx, vy);
if (Math.abs(ux * vy - uy * vx) / vn > 0.1) continue; // not parallel
const off = Math.abs((b0[0] - a0[0]) * -uy + (b0[1] - a0[1]) * ux);
const s = (p) => (p[0] - a0[0]) * ux + (p[1] - a0[1]) * uy;
const lap = Math.min(un, Math.max(s(b0), s(b1))) - Math.max(0, Math.min(s(b0), s(b1)));
if (off < 2 * PROP.squatBrimW && lap > 1) out.push(`${off.toFixed(2)} mm apart over ${lap.toFixed(1)} mm`);
}
return out;
}
Deno.test('low edge: the bowed slab is a closed solid', () => {
assert(isClosed(bowedSlab()), 'bowedSlab is not closed');
});
@@ -64,3 +90,28 @@ Deno.test('low edge: a bowed face on the bed gets a wall at each edge of its low
}
}
});
Deno.test('low edge: no two squat brims fuse -- the bowed slab, and a tilted cube whose old squat wall stands behind the edge', () => {
for (const cov of [0, 0.5, 1]) {
const f = fusedSquats(build(bowedSlab(), I, cov));
assert(!f.length, `bowed slab cov ${cov}: fused squat walls ${f}`);
}
// the sweep's cube35 at X60Z30, sparse: the first version added a squat wall
// 1.6 mm from the one main builds over a PART row behind the edge
const d = 35 * Math.PI / 180, c = Math.cos(d), sn = Math.sin(d);
const raw = block(-20, 20, -20, 20, -20, 20);
let minZ = Infinity;
for (let i = 0; i < raw.length; i += 3) {
const y = raw[i + 1], z = raw[i + 2];
raw[i + 1] = y * c - z * sn; raw[i + 2] = y * sn + z * c;
minZ = Math.min(minZ, raw[i + 2]);
}
for (let i = 2; i < raw.length; i += 3) raw[i] -= minZ;
const z30 = Math.PI / 6, rz = [Math.cos(z30), Math.sin(z30), 0, -Math.sin(z30), Math.cos(z30), 0, 0, 0, 1];
const rx = rotX(60), rot = new Array(9).fill(0); // rotZ(30) * rotX(60), col-major
for (let col = 0; col < 3; col++) for (let r = 0; r < 3; r++) for (let k = 0; k < 3; k++) rot[col * 3 + r] += rz[k * 3 + r] * rx[col * 3 + k];
const b = build(raw, rot, 0);
assert(b.props.some((q) => q.squat), 'the tilted cube lost its squat wall');
const f = fusedSquats(b);
assert(!f.length, `tilted cube: fused squat walls ${f}`);
});
+11 -6
View File
@@ -562,16 +562,21 @@ function lowEdges(probe, vLo, vHi) {
if (above >= 0) edges.push({ v: vs[above], dir: +1 });
k = j;
}
// a row's own class, so lowEdgeRows can tell a row behind an edge that still
// builds (a PART row) from one in the band that never will
edges.low = (v) => cls[Math.max(0, Math.min(cls.length - 1, Math.round((v - vLo) / step)))] === 'low';
return edges;
}
/**
* rowOffsets' rows plus a row at each low edge (lowEdges) with no row within a
* brim width (2 x squatBrimW) on its standing side, or within `near` behind it.
* Only ever ADDED: the layout's rows are a guess at where a wall builds, the
* edge scan is a coarser one, and moving a row onto the edge lost real walls
* (the flat torus's 9-tine rim walls slid 1.3 mm in and failed the build). The
* brim width keeps two squat brims from fusing.
* brim width (2 x squatBrimW) of it -- either side, except a row IN the low band
* behind it, which never builds. Only ever ADDED: the layout's rows are a guess
* at where a wall builds, the edge scan is a coarser one, and moving a row onto
* the edge lost real walls (the flat torus's 9-tine rim walls slid 1.3 mm in
* and failed the build). The brim width keeps two squat brims from fusing: a
* row behind the edge over a PART row still builds (a tilted cube at X60Z30
* got a second squat wall 1.6 mm from it, one fused slab).
*/
function lowEdgeRows(rows, edges, near) {
const out = [...rows];
@@ -579,7 +584,7 @@ function lowEdgeRows(rows, edges, near) {
for (const { v, dir } of edges) {
const blocked = out.some((r) => {
const d = (r - v) * dir;
return d >= 0 ? d < brim : -d < near;
return d >= 0 ? d < brim : -d < near || (-d < brim && !edges.low(r));
});
if (!blocked) out.push(v);
}