update single width extrusion claim system. does not require splitting medial axis segments
This commit is contained in:
parent
099c0ea0f9
commit
6a44654714
3 changed files with 29 additions and 39 deletions
|
|
@ -114,11 +114,16 @@ class Point {
|
||||||
return this.key;
|
return this.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
clone(keys) {
|
||||||
let p = newPoint(this.x, this.y, this.z, this._key);
|
let p = newPoint(this.x, this.y, this.z, this._key);
|
||||||
if (this.a !== undefined) {
|
if (this.a !== undefined) {
|
||||||
p.a = this.a;
|
p.a = this.a;
|
||||||
}
|
}
|
||||||
|
if (keys) {
|
||||||
|
for (let key of keys) {
|
||||||
|
p[key] = this[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2322,7 +2322,7 @@ export class Polygon {
|
||||||
|
|
||||||
// split long straight lines into segments no longer than max
|
// split long straight lines into segments no longer than max
|
||||||
// and return a new polygon
|
// and return a new polygon
|
||||||
segment(max = 1) {
|
segment(max = 1, mark) {
|
||||||
const newp = [];
|
const newp = [];
|
||||||
const points = this.points;
|
const points = this.points;
|
||||||
const length = points.length;
|
const length = points.length;
|
||||||
|
|
@ -2334,6 +2334,7 @@ export class Polygon {
|
||||||
const dy = p2.y - p1.y;
|
const dy = p2.y - p1.y;
|
||||||
const dl = Math.sqrt(dx * dx + dy * dy);
|
const dl = Math.sqrt(dx * dx + dy * dy);
|
||||||
newp.push(p1);
|
newp.push(p1);
|
||||||
|
if (mark) p1.segment = p1;
|
||||||
if (dl < max) {
|
if (dl < max) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -2348,10 +2349,12 @@ export class Polygon {
|
||||||
newp.push(newPoint(ox, oy, (p1.z + p2.z) / 2));
|
newp.push(newPoint(ox, oy, (p1.z + p2.z) / 2));
|
||||||
ox += ix;
|
ox += ix;
|
||||||
oy += iy;
|
oy += iy;
|
||||||
|
// mark new point with first point of originating segment
|
||||||
|
if (mark) newp.peek().segment = p1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newp.length > length) {
|
if (newp.length > length) {
|
||||||
return newPolygon().addPoints(newp.map(p => p.clone())).setOpenValue(this.open);
|
return newPolygon().addPoints(newp.map(p => p.clone(['segment']))).setOpenValue(this.open);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -218,11 +218,14 @@ function thin_type_3(params) {
|
||||||
// console.log({ lines, polys, trace });
|
// console.log({ lines, polys, trace });
|
||||||
|
|
||||||
// show extrusion
|
// show extrusion
|
||||||
|
let zo = z + 0.1;
|
||||||
if (debugExtrusion)
|
if (debugExtrusion)
|
||||||
|
for (let trace of traces)
|
||||||
for (let point of trace) {
|
for (let point of trace) {
|
||||||
polys.push(newPolygon().centerCircle(point, point.r, 12).setZ(z));
|
polys.push(newPolygon().centerCircle(point, point.r, 12).setZ(zo = zo + 0.005));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!debugExtrusion)
|
||||||
POLY.setZ([...lines, ...polys], z);
|
POLY.setZ([...lines, ...polys], z);
|
||||||
|
|
||||||
if (lines.length) top.thin_fill = lines;
|
if (lines.length) top.thin_fill = lines;
|
||||||
|
|
@ -279,7 +282,11 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// project from first inset and find greated offset intersection
|
// project from first inset and find greated offset intersection.
|
||||||
|
// this examples show how to brute force create a medial axis from
|
||||||
|
// micro stepping insets. the same intersect technique used with
|
||||||
|
// medial axis can be applied to this structure to produce an
|
||||||
|
// equivalent output. not as computationally efficient, but much simpler.
|
||||||
if (brute) {
|
if (brute) {
|
||||||
let { intersectRayLine } = base.util;
|
let { intersectRayLine } = base.util;
|
||||||
let tests = insets.slice(1);
|
let tests = insets.slice(1);
|
||||||
|
|
@ -375,8 +382,6 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) {
|
||||||
return rec;
|
return rec;
|
||||||
}
|
}
|
||||||
|
|
||||||
let nusegs = [];
|
|
||||||
|
|
||||||
// for each standalone noodle, construct medial axis
|
// for each standalone noodle, construct medial axis
|
||||||
// map medial axis points into de-duplicated segments
|
// map medial axis points into de-duplicated segments
|
||||||
for (let poly of noodle) {
|
for (let poly of noodle) {
|
||||||
|
|
@ -393,31 +398,6 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// break down long segments
|
|
||||||
for (let seg of lineMap.values()) {
|
|
||||||
let { p0, p1 } = seg;
|
|
||||||
let len = seg.len = pointDist(p0, p1);
|
|
||||||
// add to nusegs
|
|
||||||
if (len <= minR) {
|
|
||||||
nusegs.push({ p0, p1 });
|
|
||||||
if (showMedialAxis) {
|
|
||||||
lines.push(new Point(p0.x, p0.y), new Point(p1.x, p1.y));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let steps = Math.ceil(len / minR) + 1;
|
|
||||||
let dx = (p1.x - p0.x) / steps;
|
|
||||||
let dy = (p1.y - p0.y) / steps;
|
|
||||||
for (let i=0; i<steps; i++) {
|
|
||||||
p1 = { x:p0.x + dx, y:p0.y + dy };
|
|
||||||
nusegs.push({ p0, p1 });
|
|
||||||
if (showMedialAxis) {
|
|
||||||
lines.push(new Point(p0.x, p0.y), new Point(p1.x, p1.y));
|
|
||||||
}
|
|
||||||
p0 = p1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// divide a segment/redius into 1 or more equal subsegments
|
// divide a segment/redius into 1 or more equal subsegments
|
||||||
function div(cr) {
|
function div(cr) {
|
||||||
if (cr <= maxR) {
|
if (cr <= maxR) {
|
||||||
|
|
@ -439,6 +419,9 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// medial axis segments for comparison
|
||||||
|
let segs = [...lineMap.values()];
|
||||||
|
|
||||||
// project inset segment normals onto closest medial segment
|
// project inset segment normals onto closest medial segment
|
||||||
// use this to find a radius which we divide into extrusion lanes
|
// use this to find a radius which we divide into extrusion lanes
|
||||||
if (!brute)
|
if (!brute)
|
||||||
|
|
@ -455,13 +438,13 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) {
|
||||||
let ltpo;
|
let ltpo;
|
||||||
let trace = [];
|
let trace = [];
|
||||||
trace.shell = sno;
|
trace.shell = sno;
|
||||||
poly.segment(minR).forEachSegment((p1, p2) => {
|
poly.segment(minR, true).forEachSegment((p1, p2) => {
|
||||||
let np1 = { x:(p1.x+p2.x)/2, y:(p1.y+p2.y)/2 };
|
let np1 = { x:(p1.x+p2.x)/2, y:(p1.y+p2.y)/2 };
|
||||||
let len = pointDist(p1, p2);
|
let len = pointDist(p1, p2);
|
||||||
let dx = (p1.x - p2.x) / len;
|
let dx = (p1.x - p2.x) / len;
|
||||||
let dy = (p1.y - p2.y) / len;
|
let dy = (p1.y - p2.y) / len;
|
||||||
let min = { dist: Infinity };
|
let min = { dist: Infinity };
|
||||||
for (let seg of nusegs) {
|
for (let seg of segs) {
|
||||||
let { p0, p1 } = seg;
|
let { p0, p1 } = seg;
|
||||||
let int = intersectRayLine(np1, { dx: dy, dy: -dx }, p0, p1);
|
let int = intersectRayLine(np1, { dx: dy, dy: -dx }, p0, p1);
|
||||||
if (int?.dist < min.dist && int?.dist < noodleWidth) {
|
if (int?.dist < min.dist && int?.dist < noodleWidth) {
|
||||||
|
|
@ -486,24 +469,23 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) {
|
||||||
let len = Math.ceil(pop.length / 2);
|
let len = Math.ceil(pop.length / 2);
|
||||||
// check if segment was claimed by a different originating segment
|
// check if segment was claimed by a different originating segment
|
||||||
// for odd wall counts so that they're not emitted twice
|
// for odd wall counts so that they're not emitted twice
|
||||||
if (odd && mp1.claimed) len--;
|
if (odd && mp1.claimed && mp1.claimed !== p1.segment) len--;
|
||||||
if (len === 0) {
|
if (len === 0) {
|
||||||
nupoly.push(new Point(min.x, min.y));
|
nupoly.push(new Point(min.x, min.y));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let op;
|
|
||||||
if (pop.length === 1) {
|
if (pop.length === 1) {
|
||||||
// let rad_step = ((pop[0] * sstep) | 0) / sstep;
|
// let rad_step = ((pop[0] * sstep) | 0) / sstep;
|
||||||
// for single wall place it exactly on medial axis
|
// for single wall place it exactly on medial axis
|
||||||
nupoly.push(new Point(min.x, min.y));
|
nupoly.push(new Point(min.x, min.y));
|
||||||
trace.push(op = { x: min.x, y: min.y, r: mr })
|
trace.push({ x: min.x, y: min.y, r: mr })
|
||||||
} else {
|
} else {
|
||||||
// radius rounded to nearest step
|
// radius rounded to nearest step
|
||||||
let rad_step = ((pop[0] * sstep) | 0) / sstep;
|
let rad_step = ((pop[0] * sstep) | 0) / sstep;
|
||||||
// otherwise use first division
|
// otherwise use first division
|
||||||
// compensate for minimal inset
|
// compensate for minimal inset
|
||||||
let off = -dstep + rad_step;
|
let off = -dstep + rad_step;
|
||||||
trace.push(op = {
|
trace.push({
|
||||||
x: np1.x + dy * off,
|
x: np1.x + dy * off,
|
||||||
y: np1.y - dx * off,
|
y: np1.y - dx * off,
|
||||||
r: rad_step
|
r: rad_step
|
||||||
|
|
@ -512,7 +494,7 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) {
|
||||||
nupoly.push(new Point(np1.x + dy * off * 2, np1.y - dx * off * 2));
|
nupoly.push(new Point(np1.x + dy * off * 2, np1.y - dx * off * 2));
|
||||||
}
|
}
|
||||||
if (odd && !mp1.claimed) {
|
if (odd && !mp1.claimed) {
|
||||||
mp1.claimed = true;
|
mp1.claimed = p1.segment;
|
||||||
}
|
}
|
||||||
// detect trace jump and start new trace
|
// detect trace jump and start new trace
|
||||||
if (ltpo && pointDist(ltpo, trace.peek()) > maxR) {
|
if (ltpo && pointDist(ltpo, trace.peek()) > maxR) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue