mirror of
https://github.com/gittrahan/support-fins.git
synced 2026-10-21 11:23:02 -05:00
pad: the Light/Custom pad's top grid is wound like its tip fans, side wall and bottom -- it ran the other way (closed, but the top inside-out against the rest), Bambu read the pad as a broken mesh and refused the separate-object 3MF over a G-code path conflict with the part; PrusaSlicer warned. Same triangles, only the winding: walls byte-identical over the 54 goldens (hashes re-recorded), sweep metrics unchanged. tests: isOriented (isClosed only counts edges), every pad style at X20/45/60
This commit is contained in:
1 parent
3d4f88cfd9
commit
a84098c7da
23 files changed
+59
-22
No files matched your search
@@ -115,6 +115,27 @@ export function isClosed(tris) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Every edge is walked once each way (no face flipped against its neighbour) and
|
||||
* the enclosed volume is positive (normals point out). isClosed only counts edges,
|
||||
* so it passes a mesh with a patch wound inside-out -- one a slicer misreads. */
|
||||
export function isOriented(tris) {
|
||||
const key = (p) => `${Math.round(p[0] * 1e3)},${Math.round(p[1] * 1e3)},${Math.round(p[2] * 1e3)}`;
|
||||
const edges = new Map();
|
||||
let vol = 0;
|
||||
for (let i = 0; i < tris.length; i += 3) {
|
||||
const [p, q, r] = [tris[i], tris[i + 1], tris[i + 2]];
|
||||
vol += p[0] * (q[1] * r[2] - q[2] * r[1]) - p[1] * (q[0] * r[2] - q[2] * r[0]) + p[2] * (q[0] * r[1] - q[1] * r[0]);
|
||||
for (let e = 0; e < 3; e++) {
|
||||
const a = key(tris[i + e]), b = key(tris[i + (e + 1) % 3]);
|
||||
if (a === b) continue;
|
||||
const k = a < b ? `${a}|${b}` : `${b}|${a}`;
|
||||
edges.set(k, (edges.get(k) ?? 0) + (a < b ? 1 : -1));
|
||||
}
|
||||
}
|
||||
for (const c of edges.values()) if (c !== 0) return false;
|
||||
return vol > 0;
|
||||
}
|
||||
|
||||
/** Squared distance from point p to triangle (a,b,c) (each [x,y,z]). */
|
||||
export function ptTriDist2(p, a, b, c) {
|
||||
const sub = (u, v) => [u[0] - v[0], u[1] - v[1], u[2] - v[2]];
|
||||
|
||||
@@ -63,5 +63,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "e876bede51197e3ff22204926e5551ac72da548b46f4bba2d9fd5c9e1e3c197f"
|
||||
"sha256": "b66ad252dbae0921ce6bdf285c9875c97d2a5efff386a635e85a879fff0ea40c"
|
||||
}
|
||||
@@ -63,5 +63,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "7ddf37cdeeb205df088ff6a576096249ef170860689aadbb2780512415e06ed5"
|
||||
"sha256": "dfb572b337c7b42a88887ef2989f6be2c3d769f314a76ebb68f28ea8ab71b2cd"
|
||||
}
|
||||
@@ -63,5 +63,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "f7efb66fb90b39aa0d4346f1666a0c337a85fd8b7b5e549075c1f869e7233c6b"
|
||||
"sha256": "90ac5b66684b22a6dbfc57aad4aeb7e8aab9223cf8610ef3d9338d1cb49c24d4"
|
||||
}
|
||||
@@ -31,5 +31,5 @@
|
||||
"length": 30
|
||||
}
|
||||
],
|
||||
"sha256": "9754230987afc241117ef27ec2a844bc1f732ed799948ba9fad7f2dbfb1f566f"
|
||||
"sha256": "e732369394923e9902d92ff24aead0644806adc4b1994eccd7d6505a11b010a4"
|
||||
}
|
||||
@@ -66,5 +66,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "7acaf466365c036d868f72eeade0146f793985fe6bb1bfe34a093eaeb60177e9"
|
||||
"sha256": "61f3c93c411482f26ef212fdbc88b99a3c9140a07406e7cdd6cc726e4ab8052a"
|
||||
}
|
||||
@@ -79,5 +79,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "fd21d177b088386a1389cafe665d48706ec7e9102a439bd673f7b539e306d60c"
|
||||
"sha256": "5b71f3e92b38628681a4d6aefb99414b61b004e495a8e90299c67de3ab0de191"
|
||||
}
|
||||
@@ -63,5 +63,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "18ff8f72b1de8331938d4eb8df184fb02de98b1541505eedd48794f55fc11de7"
|
||||
"sha256": "1b0884b96f061b179102cdf0cfe425adbf2271aa7d2c9f21a271681729e425de"
|
||||
}
|
||||
@@ -79,5 +79,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "016e8fa0c302dc4f9f7b2982aca6569f4b526f61ce6fe5d8a2df5f21d5777bcb"
|
||||
"sha256": "bba1ffcb675e190c42d7611db1edcc800ebdf869c41949dbfd1e8dbb99584d3d"
|
||||
}
|
||||
@@ -31,5 +31,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "0f1947d5432c9b86a5034b2594c8b4214cbaca5cf9db48ed9601d928f67a81de"
|
||||
"sha256": "c6d6c967efcf271c4743a4cae8ef66adc522591f7d61ecded6ea492f9490e06f"
|
||||
}
|
||||
@@ -63,5 +63,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "f7efb66fb90b39aa0d4346f1666a0c337a85fd8b7b5e549075c1f869e7233c6b"
|
||||
"sha256": "90ac5b66684b22a6dbfc57aad4aeb7e8aab9223cf8610ef3d9338d1cb49c24d4"
|
||||
}
|
||||
@@ -63,5 +63,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "8aa787df16c41675897a2c3bebceb607fdceae0655bcb19b67ef8d1bef336794"
|
||||
"sha256": "842f25edf4ae90cf557687686ddbc9e293f8ea29cea9ddce9dac455f7baa62c5"
|
||||
}
|
||||
@@ -31,5 +31,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "15536c40b46aaf6b566b754ff9bc8c2c59373b93cbf48fa77608dd454a382a3c"
|
||||
"sha256": "5eee6aeb5f5e54fb449363b0cb14a1341b65f7f7f181d2bfd5adfe9c0ef375d2"
|
||||
}
|
||||
@@ -127,5 +127,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "277859cb967ee2e3e6f00fd6030caf60b0d4e114555faa22105f94772a0eaa52"
|
||||
"sha256": "a7ee3a69557dc0a21679f269751e0a68dfe798f7a72f0bf69be9df8052684931"
|
||||
}
|
||||
@@ -79,5 +79,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "c0aeea1f027a208def2b061e5dfd0e3ea086cd1052155f1df095fa9f8c4a742e"
|
||||
"sha256": "5ea5698aa85944d1dfcd75b543ad95fa37f7ff7516903724910f32c6af126120"
|
||||
}
|
||||
@@ -47,5 +47,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "2405663113977690550e899839e0df53371da71879c676bbcbcf64e293b381f8"
|
||||
"sha256": "304b2cdf31f6532b4fcb655f5cd69cf0c5ed4bfc8949b687ff62e956cd5c6229"
|
||||
}
|
||||
@@ -111,5 +111,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "c6ed2f8d0f49f5f84abfa4a054de21f6827b956d49c4218766b82f6806820756"
|
||||
"sha256": "994920221d1211092e90563ab08fb20471ee59ec97876d6b6abc5d7aa940b72c"
|
||||
}
|
||||
@@ -63,5 +63,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "18ff8f72b1de8331938d4eb8df184fb02de98b1541505eedd48794f55fc11de7"
|
||||
"sha256": "1b0884b96f061b179102cdf0cfe425adbf2271aa7d2c9f21a271681729e425de"
|
||||
}
|
||||
@@ -95,5 +95,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "843aa33f69e6682d8c117b6792e5588f21ab13d9c6c264dc762f8ec0c1d6b240"
|
||||
"sha256": "3b14893331c7a0c1f54c632e9fa770c71f48acdb8aba7f58a57dc60753663e59"
|
||||
}
|
||||
@@ -47,5 +47,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "285058654c6fdca843eb14a52289e970a1c92b18024b5016313cf9345de33d06"
|
||||
"sha256": "3b452ca3d716d483dcca675f59fe3ae9350baf56d3105fb88f40ac7b4544c804"
|
||||
}
|
||||
@@ -63,5 +63,5 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"sha256": "b14e009ea9057f88a7d0c29c9fa445152313c2406316da039128e5d7e5582a39"
|
||||
"sha256": "61b28cb99c2faefb46b9725c871e1038f67d194a9d2257638a4dd296de0c5c26"
|
||||
}
|
||||
+14
-1
@@ -3,7 +3,7 @@
|
||||
// part's flank -- and stay watertight. These pin all three so a revert to the grid
|
||||
// (or a broken mesh) fails loudly.
|
||||
|
||||
import { loadModel, analyze, fins, isClosed, rotX, assert } from './_util.js';
|
||||
import { loadModel, analyze, fins, isClosed, isOriented, rotX, assert } from './_util.js';
|
||||
|
||||
// Build with a pad style set for this call only (PAD.style is module state).
|
||||
function withStyle(style, fn) {
|
||||
@@ -145,6 +145,19 @@ Deno.test('pad (brim): one layer thick, whatever the layer height', () => {
|
||||
}
|
||||
});
|
||||
|
||||
// Every pad style is one consistently wound outward solid. The Light/Custom top grid
|
||||
// once ran the other way to its tip fans, side wall and bottom: closed, but with
|
||||
// the top inside-out against the rest. Bambu misread it and refused the
|
||||
// separate-object 3MF ("G-code path conflicts" with the part); PrusaSlicer warned.
|
||||
Deno.test('pad: every style is wound consistently outward, at any tilt', () => {
|
||||
for (const style of ['light', 'sure', 'custom']) for (const deg of [20, 45, 60]) {
|
||||
const topo = loadModel('cube'), rot = rotX(deg);
|
||||
const b = withStyle(style, () => fins.buildFins(topo, analyze(topo, 45, rot), rot, { mode: 'auto', bedPad: true, tines: true }));
|
||||
assert(b.padTriangles.length > 0, `${style} X${deg}: no pad`);
|
||||
assert(isOriented(b.padTriangles), `${style} X${deg}: pad has faces wound against their neighbours`);
|
||||
}
|
||||
});
|
||||
|
||||
// What the SLICER sees: the pad's and the part's sections at the first layer's
|
||||
// mid-height. PrusaSlicer / Orca / Bambu merge any gap under 2 x 0.049mm, so the
|
||||
// sliced gap must clear 0.098 -- at every tilt, not just 45deg: 0.1mm through a
|
||||
|
||||
+4
-1
@@ -361,7 +361,10 @@ function brimPad(partTris, contact, e, h, g, grab, layerH, outline, out) {
|
||||
}
|
||||
for (let i = 0; i < cols.length - 1; i++) {
|
||||
const A = cols[i], B = cols[i + 1];
|
||||
for (let j = 0; j < nT - 1; j++) { tri(A[j], B[j], B[j + 1]); tri(A[j], B[j + 1], A[j + 1]); }
|
||||
// wound like the tip fans, side wall and bottom (it once ran the other way: the
|
||||
// top was inside-out against the rest, and Bambu, reading a broken pad, refused
|
||||
// the separate-object 3MF over a G-code path conflict with the part)
|
||||
for (let j = 0; j < nT - 1; j++) { tri(A[j], B[j + 1], B[j]); tri(A[j], A[j + 1], B[j + 1]); }
|
||||
}
|
||||
// boundary ring (tipL, the +t rim left to right, tipR, the -t rim right to
|
||||
// left), then its side wall and the flat bottom fan
|
||||
|
||||
Reference in new issue
Block a user