add jspoly and checkpoint thinwall v3

This commit is contained in:
Stewart Allen 2025-07-25 18:38:40 -07:00
commit c76085f6f9
3 changed files with 7428 additions and 34 deletions

7295
src/ext/jspoly.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -257,32 +257,14 @@ export class Polygon {
// including inner polys. return the noodle and the remainder
// of the polygon with the noodle removed (for the next pass)
noodle(width) {
// inset outer polygon
let inner = this.inner;
let inset = this.clone().offset(width);
let noodot = [ this.clone().setInner(inset) ];
let noodin = [];
// subtract inners from the inset
if (inner) {
noodot = POLY.subtract(noodot, inner, []);
// for each inner, offset and subtract noodle + itself
let nood = [];
for (let innie of inner) {
let inexp = innie.clone().offset(-width);
let intrm = POLY.trimTo(inexp, [ this ]);
let inood = POLY.subtract(intrm, [ ...noodot, innie ], []);
nood.push(...inood);
}
noodin = POLY.union(nood, 0, true);
}
// unify inner noodles and outer noodle
let noodle = POLY.union([ ...noodot, ...noodin ], 0, true);
// subtract noodle from poly for next noodle calc
let remain = POLY.subtract([ this ], noodle, []);
return { noodle, remain };
let clone = this.clone(true);
let ins = clone.offset(width);
let nood = POLY.nest(POLY.flatten([ clone, ...ins ], [], true));
return { noodle: nood, remain: ins };
}
// generate center crossing point cloud
// only used for fdm thin-wall type 1 (fdm/post.js)
centers(step, z, min, max, opt = {}) {
let cloud = [],
bounds = this.bounds,
@ -555,6 +537,7 @@ export class Polygon {
return this;
}
// only used in fdm/slice.js for enclosing supports
createConvexHull(points) {
function removeMiddle(a, b, c) {
let cross = (a.x - b.x) * (c.y - b.y) - (a.y - b.y) * (c.x - b.x);
@ -886,6 +869,7 @@ export class Polygon {
/**
* hint fill angle hinting from longest segment
* only used in fdm/slice.js for projected infill orientation
*/
hintFillAngle() {
let index = 0,
@ -1217,6 +1201,8 @@ export class Polygon {
* 2. ease-down starts, following the polygonal path, decreasing Z at a fixed slope until target Z is hit,
* 3. then the rest of the path is completed and repeated at target Z until touchdown point is reached.
* 4. this function should probably move to CAM prepare since it's only called from there
*
* possibly no longer used anywhere
*/
forEachPointEaseDown(fn, fromPoint, degrees = 45) {
let index = this.findClosestPointTo(fromPoint).index,
@ -1537,6 +1523,7 @@ export class Polygon {
return this;
}
// used in geo/slicer.js to free memory
freeParentRefs() {
if (this.inner && this.inner.length > 0) {
for (let inner of this.inner) {
@ -1549,6 +1536,7 @@ export class Polygon {
}
}
// possibly ununsed
newUndeleted() {
let poly = newPolygon();
this.forEachPoint(p => {
@ -1888,6 +1876,7 @@ export class Polygon {
return out;
}
// possibly unused
shortestSegmentLength() {
let len = Infinity;
this.forEachSegment((p1, p2) => {
@ -2362,6 +2351,8 @@ export class Polygon {
return this;
}
// only used by cam/op-pocket.js
// for any poly points closer than `dist`, replace them with their midpoint
midpoints(dist = 0.01) {
const newp = [];
const points = this.points;

View file

@ -1,9 +1,12 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import '../../../ext/jspoly.js';
import { base } from '../../../geo/base.js';
import { Line } from '../../../geo/line.js';
import { polygons as POLY, fillArea } from '../../../geo/polygons.js';
import { getRangeParameters } from './driver.js';
import { slicer } from '../../../geo/slicer.js';
import { Point } from '../../../geo/point.js';
slicer.slicePost.FDM = slicePost;
@ -164,19 +167,124 @@ function thin_type_3(params) {
top.thin_fill = [];
top.fill_sparse = [];
let { noodle, remain } = top.poly.noodle(offsetN);
let { noodle, remain } = top.poly.noodle(offsetN * 2);
top.shells = noodle;
top.gaps = remain;
// top.fill_off = noodle;
// if (remain.length) {
// top.gaps = [];
// for (let poly of remain) {
// let { noodle } = poly.noodle(offsetN);
// console.log({ next_noodle: noodle });
// top.gaps.push(...noodle);
// }
// }
let thin = top.thin_fill;
let scale = 5000;
let minR = offsetN / 2;
let minSpur = offsetN * 3;
let pointMap = new Map();
let lineMap = new Map();
function pointToRec(point) {
let key = `${(point.x*100)|0}|${(point.y*100)|0}`;
let rec = pointMap.get(key);
if (!rec) {
rec = {
key,
count: 0,
x: point.x/scale,
y: point.y/scale,
r: point.radius/scale,
};
pointMap.set(key, rec);
}
return rec;
}
function pointsToLine(p0, p1) {
p0 = pointToRec(p0);
p1 = pointToRec(p1);
let key = p0.key > p1.key ? `${p1.key}:${p0.key}` : `${p0.key}:${p1.key}`;
let rec = lineMap.get(key);
if (rec) {
rec.count++;
} else {
rec = {
p0,
p1,
key,
count: 1,
};
p0.count++;
p1.count++;
lineMap.set(key, rec);
}
return rec;
}
let chains = [];
for (let poly of noodle) {
poly = poly.clone(true).scale({ x:scale, y:scale, z:scale });
let out = poly.points.map(p => ({ x: p.x|0, y: p.y|0 }));
let inr = (poly.inner ?? []).map(p => p.points.map(p => ({ x: p.x|0, y: p.y|0 })));
let ma = JSPoly.construct_medial_axis(out, inr);
for (let { point0, point1 } of ma) {
pointsToLine(point0, point1);
}
for (let seg of lineMap.values()) {
let { p0, p1 } = seg;
let len = seg.len = Math.hypot(p0.x-p1.x, p0.y-p1.y);
if (p0.r < minR || p1.r < minR) {
if (p0.r > minR && len > minSpur) {
// move p1 toward p0 until minR met
let pct = (minR - p1.r) / (p0.r - p1.r);
let dx = p1.x - p0.x;
let dy = p1.y - p0.y;
p1.x -= dx * pct;
p1.y -= dy * pct;
p1.r = minR;
pointMap.delete(p1.key);
} else if (p1.r > minR && len > minSpur) {
// move p0 toward p1 until minR met
let pct = (minR - p0.r) / (p1.r - p0.r);
let dx = p0.x - p1.x;
let dy = p0.y - p1.y;
p0.x -= dx * pct;
p0.y -= dy * pct;
p0.r = minR;
pointMap.delete(p0.key);
} else {
lineMap.delete(seg.key);
continue;
}
}
// thin.push(new Point(p0.x, p0.y));
// thin.push(new Point(p1.x, p1.y));
let add;
for (let chain of chains) {
if (p0.count === 2 && p0 === chain[0]) {
chain.splice(0,0,p1);
} else if (p0.count === 2 && p0 === chain.peek()) {
chain.push(p1);
add = true;
} else if (p1.count === 2 && p1 === chain[0]) {
chain.splice(0,0,p0);
add = true;
} else if (p1.count ===2 && p1 === chain.last) {
chain.push(p0);
add = true;
}
if (add) {
break;
}
}
if (!add) {
chains.push([ p0, p1 ]);
}
}
}
for (let chain of chains) {
for (let i=0; i<chain.length-1; i++) {
thin.push(new Point(chain[i].x, chain[i].y));
thin.push(new Point(chain[i+1].x, chain[i+1].y));
}
}
return { last, gaps };
}