add alt seg generator. prepare to drop chains
This commit is contained in:
parent
ddf3972f35
commit
2ca01ed004
1 changed files with 24 additions and 271 deletions
|
|
@ -171,12 +171,9 @@ function thin_type_3(params) {
|
||||||
|
|
||||||
top.gaps = remain;
|
top.gaps = remain;
|
||||||
|
|
||||||
let mergeChains = false;
|
|
||||||
let simplifyChain = false;
|
|
||||||
let shortenAtNexus = false;
|
|
||||||
let shortenEdgeSpur = false;
|
|
||||||
let intersectChains = true;
|
let intersectChains = true;
|
||||||
let maxInsetBypass = false;
|
let maxInsetBypass = false;
|
||||||
|
let altNuSegs = true;
|
||||||
|
|
||||||
let showNoodle = false;
|
let showNoodle = false;
|
||||||
let showMedLine = false;
|
let showMedLine = false;
|
||||||
|
|
@ -184,13 +181,8 @@ function thin_type_3(params) {
|
||||||
let showExtrusion = true;
|
let showExtrusion = true;
|
||||||
let showExtrudeInset = false;
|
let showExtrudeInset = false;
|
||||||
let showChainRawPoints = false;
|
let showChainRawPoints = false;
|
||||||
let showChainInterpPoints = true;
|
|
||||||
let showChainIntersect = false;
|
let showChainIntersect = false;
|
||||||
let showNuMedLine = true;
|
let showNuMedLine = false;
|
||||||
let showMedPoints = false;
|
|
||||||
let showMedNormals = false;
|
|
||||||
let showMedRadii = false;
|
|
||||||
let showNexuses = false;
|
|
||||||
|
|
||||||
let thin = top.thin_fill = [];
|
let thin = top.thin_fill = [];
|
||||||
let shells = top.shells;
|
let shells = top.shells;
|
||||||
|
|
@ -200,7 +192,6 @@ function thin_type_3(params) {
|
||||||
let minR = midR * 0.75; // smallest interior fill or single wall width
|
let minR = midR * 0.75; // smallest interior fill or single wall width
|
||||||
let maxR = midR * 1.5; // max double wall extrusion width
|
let maxR = midR * 1.5; // max double wall extrusion width
|
||||||
let maxF = midR * 2; // max interior extrusion width
|
let maxF = midR * 2; // max interior extrusion width
|
||||||
let minSpur = offsetN * 1;
|
|
||||||
|
|
||||||
let insets = [];
|
let insets = [];
|
||||||
let inset;
|
let inset;
|
||||||
|
|
@ -333,6 +324,7 @@ function thin_type_3(params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let chains = [];
|
let chains = [];
|
||||||
|
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
|
||||||
|
|
@ -355,37 +347,22 @@ function thin_type_3(params) {
|
||||||
for (let seg of lineMap.values()) {
|
for (let seg of lineMap.values()) {
|
||||||
let { p0, p1 } = seg;
|
let { p0, p1 } = seg;
|
||||||
let len = seg.len = pointDist(p0, p1);
|
let len = seg.len = pointDist(p0, p1);
|
||||||
// filter out lines where both points' radii are under the
|
if (altNuSegs) {
|
||||||
// minR threshold. shorten and fixup lines where minR falls
|
// add to nusegs
|
||||||
// somewhere along the gradient between ends.
|
if (len <= minR) {
|
||||||
if (shortenEdgeSpur)
|
nusegs.push({ p0, p1 });
|
||||||
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 {
|
} else {
|
||||||
// both points eliminated
|
let steps = Math.ceil(len / minR) + 1;
|
||||||
pointMap.delete(p0.key);
|
let dx = (p1.x - p0.x) / steps;
|
||||||
pointMap.delete(p1.key);
|
let dy = (p1.y - p0.y) / steps;
|
||||||
lineMap.delete(seg.key);
|
for (let i=0; i<steps; i++) {
|
||||||
continue;
|
p1 = { x:p0.x + dx, y:p0.y + dy };
|
||||||
|
nusegs.push({ p0, p1 });
|
||||||
|
p0 = p1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (altNuSegs) continue;
|
||||||
// for each line segment either add it to a chain or start a new chain
|
// for each line segment either add it to a chain or start a new chain
|
||||||
let add;
|
let add;
|
||||||
for (let chain of chains) {
|
for (let chain of chains) {
|
||||||
|
|
@ -417,10 +394,6 @@ function thin_type_3(params) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: merge chains at next of 3 or more preferencing
|
|
||||||
// merging longer chains. then shorten remaining nexus chain
|
|
||||||
// intersections by minR
|
|
||||||
|
|
||||||
// render inset "noodle"
|
// render inset "noodle"
|
||||||
if (showNoodle) {
|
if (showNoodle) {
|
||||||
shells.appendAll(noodle);
|
shells.appendAll(noodle);
|
||||||
|
|
@ -450,184 +423,12 @@ function thin_type_3(params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter chains that are too short from consideration
|
// filter chains that are too short from consideration
|
||||||
chains = chains.filter(c => c.total >= minR);
|
// chains = chains.filter(c => c.total >= minR);
|
||||||
chains.sort((c1,c2) => c2.total - c1.total);
|
|
||||||
|
|
||||||
function merge1chain() {
|
|
||||||
let clen = chains.length;
|
|
||||||
for (let i=0; i<clen; i++) {
|
|
||||||
let ci = chains[i];
|
|
||||||
if (!ci) continue;
|
|
||||||
for (let j=i+1; j<clen; j++) {
|
|
||||||
let cj = chains[j];
|
|
||||||
if (!cj) continue;
|
|
||||||
if (ci[0] === cj[0]) {
|
|
||||||
ci.reverse().appendAll(cj)
|
|
||||||
ci.total += cj.total;
|
|
||||||
cj.merged = true;
|
|
||||||
chains[j] = undefined;
|
|
||||||
return true;
|
|
||||||
} else if (ci.peek() === cj.peek()) {
|
|
||||||
ci.appendAll(cj.reverse());
|
|
||||||
ci.total += cj.total;
|
|
||||||
cj.merged = true;
|
|
||||||
chains[j] = undefined;
|
|
||||||
return true;
|
|
||||||
} else if (ci.peek() === cj[0]) {
|
|
||||||
ci.appendAll(cj);
|
|
||||||
ci.total += cj.total;
|
|
||||||
cj.merged = true;
|
|
||||||
chains[j] = undefined;
|
|
||||||
return true;
|
|
||||||
} else if (ci[0] === cj.peek()) {
|
|
||||||
cj.appendAll(ci);
|
|
||||||
cj.total += ci.total;
|
|
||||||
ci.merged = true;
|
|
||||||
chains[i] = undefined;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// map chains to nexus via endpoints
|
|
||||||
let nexus = {};
|
|
||||||
function addEp(point, chain) {
|
|
||||||
let key = point.key;
|
|
||||||
let rec = nexus[key];
|
|
||||||
if (!rec) rec = nexus[key] = { point, chains: [] };
|
|
||||||
if (rec.chains.indexOf(chain) < 0) rec.chains.push(chain);
|
|
||||||
}
|
|
||||||
// since chains are sorted longest to shortest,
|
|
||||||
// they will appear in the nexus record longest to shortest
|
|
||||||
for (let chain of chains) {
|
|
||||||
addEp(chain[0], chain);
|
|
||||||
addEp(chain.peek(), chain);
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect chains end to end following nexus branches that result
|
|
||||||
// in the longest final merged chain (open or closed)
|
|
||||||
// map chain endpoints to nexus points
|
|
||||||
while (mergeChains && merge1chain()) ;
|
|
||||||
|
|
||||||
// filter out null chains that were merged into other chains
|
|
||||||
chains = chains.filter(chain => chain);
|
|
||||||
|
|
||||||
// filter dup points from connecting ends
|
|
||||||
chains = chains.map(chain => chain.filter((v, i) => i === 0 || v !== chain[i - 1]));
|
|
||||||
|
|
||||||
// mark closed chains
|
|
||||||
// todo use exact point match by eliminating slice above
|
|
||||||
// then later removing repeated points in the resulting chain from merges
|
|
||||||
if (mergeChains)
|
|
||||||
for (let chain of chains) {
|
|
||||||
chain.closed = (chain[0] === chain.peek());
|
|
||||||
if (chain.closed) chain.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// shorten chain length by minR distance by popping or moving points
|
|
||||||
function shorten(chain) {
|
|
||||||
for (let rem=0, i=chain.length-1; i>=1 && rem < minR; i--) {
|
|
||||||
let c1 = chain[i];
|
|
||||||
let c2 = chain[i-1];
|
|
||||||
let d = pointDist(c1,c2);
|
|
||||||
if (rem + d <= minR) {
|
|
||||||
chain.pop();
|
|
||||||
rem += d;
|
|
||||||
} else {
|
|
||||||
let diff = minR - rem;
|
|
||||||
// move c1 toward c2
|
|
||||||
let pct = diff / d;
|
|
||||||
let dx = c1.x - c2.x;
|
|
||||||
let dy = c1.y - c2.y;
|
|
||||||
chain[i] = {
|
|
||||||
x: c1.x - dx * pct,
|
|
||||||
y: c1.y - dy * pct,
|
|
||||||
r: c1.r
|
|
||||||
};
|
|
||||||
rem = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// shorten chains terminating at nexus by midR
|
|
||||||
if (shortenAtNexus)
|
|
||||||
for (let rec of Object.values(nexus)) {
|
|
||||||
let { point } = rec;
|
|
||||||
rec.shorts = 0;
|
|
||||||
if (rec.chains.length < 3) continue;
|
|
||||||
for (let chain of chains.filter(c => !c.closed)) {
|
|
||||||
if (pointDist(chain[0], point) < minR) {
|
|
||||||
chain.reverse();
|
|
||||||
shorten(chain);
|
|
||||||
chain.reverse();
|
|
||||||
rec.shorts++;
|
|
||||||
} else if (chain.peek() === point) {
|
|
||||||
shorten(chain);
|
|
||||||
rec.shorts++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showNexuses)
|
|
||||||
for (let rec of Object.values(nexus)) {
|
|
||||||
let { point } = rec;
|
|
||||||
shells.push(newPolygon().centerCircle(point, 0.2, 6 - rec.shorts));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep endpoints; drop interior points closer than R from the last kept point.
|
|
||||||
function dropClosePoints(chain, R) {
|
|
||||||
if (!chain || chain.length <= 2 || R <= 0) {
|
|
||||||
return chain;
|
|
||||||
}
|
|
||||||
let last = chain[0];
|
|
||||||
let out = [ last ];
|
|
||||||
let R2 = R * R;
|
|
||||||
for (let i = 1; i < chain.length - 1; i++) {
|
|
||||||
let dx = chain[i].x - last.x,
|
|
||||||
dy = chain[i].y - last.y;
|
|
||||||
if (dx * dx + dy * dy >= R2) {
|
|
||||||
out.push(chain[i]);
|
|
||||||
last = chain[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.push(chain[chain.length - 1]);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// drop chain points < minR apart
|
|
||||||
if (simplifyChain) {
|
|
||||||
chains = chains.map(chain => dropClosePoints(chain, minR));
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderPointNormal(pt, ndx, ndy) {
|
|
||||||
if (showMedNormals) {
|
|
||||||
let xo = ndx * pt.r;
|
|
||||||
let yo = ndy * pt.r;
|
|
||||||
thin.push(new Point(pt.x + yo, pt.y - xo));
|
|
||||||
thin.push(new Point(pt.x - yo, pt.y + xo));
|
|
||||||
}
|
|
||||||
if (showMedRadii) {
|
|
||||||
const Sx = pt.x + ndy * pt.r;
|
|
||||||
const Sy = pt.y - ndx * pt.r;
|
|
||||||
const pop = div(pt.r);
|
|
||||||
let acc = 0;
|
|
||||||
for (let r of pop) {
|
|
||||||
const t = acc + r;
|
|
||||||
const px = Sx + -ndy * t;
|
|
||||||
const py = Sy + ndx * t;
|
|
||||||
shells.push(newPolygon().centerCircle({x:px, y:py}, r, 10));
|
|
||||||
acc += r * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// gather point offsets into shells
|
// gather point offsets into shells
|
||||||
let zi = z;
|
let zi = z;
|
||||||
let nuchains = [];
|
let nuchains = [];
|
||||||
let nusegs = [];
|
if (!altNuSegs)
|
||||||
for (let chain of chains) {
|
for (let chain of chains) {
|
||||||
let nuchain = [];
|
let nuchain = [];
|
||||||
nuchains.push(nuchain);
|
nuchains.push(nuchain);
|
||||||
|
|
@ -672,63 +473,25 @@ function thin_type_3(params) {
|
||||||
if (first || mid) {
|
if (first || mid) {
|
||||||
nuchain.push(p0);
|
nuchain.push(p0);
|
||||||
}
|
}
|
||||||
// for length 2, first and last segment are the same
|
|
||||||
if (showMedPoints) {
|
|
||||||
if (first) {
|
|
||||||
// todo: handle closed
|
|
||||||
shells.push(newPolygon().centerCircle(p0, p0.r/2, 10));
|
|
||||||
}
|
|
||||||
if (mid) {
|
|
||||||
// mid segments
|
|
||||||
shells.push(newPolygon().centerCircle(p0, p0.r/2, 10));
|
|
||||||
}
|
|
||||||
if (last) {
|
|
||||||
// todo: handle closed
|
|
||||||
shells.push(newPolygon().centerCircle(p0, p0.r/2, 10));
|
|
||||||
if (closed) {
|
|
||||||
console.log('closed');
|
|
||||||
} else {
|
|
||||||
shells.push(newPolygon().centerCircle(p1, p1.r/2, 10));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// todo: handle closed
|
|
||||||
if (first) {
|
|
||||||
renderPointNormal(p0, ndx, ndy);
|
|
||||||
}
|
|
||||||
if (mid) {
|
|
||||||
renderPointNormal(p0, ((ndx + segp.ndx)/2), ((ndy + segp.ndy)/2));
|
|
||||||
}
|
|
||||||
if (last) {
|
|
||||||
renderPointNormal(p1, ndx, ndy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// interpolate across the length of the chain segment
|
// interpolate across the length of the chain segment
|
||||||
for (let indx = 1; indx < steps; indx++) {
|
for (let indx = 1; indx < steps; indx++) {
|
||||||
const inc = indx * step;
|
const inc = indx * step;
|
||||||
const cr = p0.r + dr * inc;
|
const cr = p0.r + dr * inc;
|
||||||
const cx = p0.x + dx * inc;
|
const cx = p0.x + dx * inc;
|
||||||
const cy = p0.y + dy * inc;
|
const cy = p0.y + dy * inc;
|
||||||
const xo = ndx * cr;
|
|
||||||
const yo = ndy * cr;
|
|
||||||
if (showChainInterpPoints) {
|
|
||||||
if (showMedPoints) {
|
|
||||||
shells.push(newPolygon().centerCircle({ x:cx, y:cy }, cr/2, 10));
|
|
||||||
}
|
|
||||||
renderPointNormal({ x:cx, y:cy, r:cr }, ndx, ndy);
|
|
||||||
}
|
|
||||||
nuchain.push({ x:cx, y:cy, r:cr });
|
nuchain.push({ x:cx, y:cy, r:cr });
|
||||||
}
|
}
|
||||||
if (last) {
|
if (last) {
|
||||||
nuchain.push(p1);
|
nuchain.push(p1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (showNuMedLine) {
|
for (let i=0; i<nuchain.length-1; i++) {
|
||||||
for (let i=0; i<nuchain.length-1; i++) {
|
let p0 = nuchain[i];
|
||||||
let p0 = nuchain[i];
|
let p1 = nuchain[i+1];
|
||||||
let p1 = nuchain[i+1];
|
if (!altNuSegs) {
|
||||||
nusegs.push({ p0, p1 });
|
nusegs.push({ p0, p1 });
|
||||||
|
}
|
||||||
|
if (showNuMedLine) {
|
||||||
thin.push(new Point(p0.x, p0.y, z));
|
thin.push(new Point(p0.x, p0.y, z));
|
||||||
thin.push(new Point(p1.x, p1.y, z));
|
thin.push(new Point(p1.x, p1.y, z));
|
||||||
}
|
}
|
||||||
|
|
@ -751,16 +514,6 @@ function thin_type_3(params) {
|
||||||
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 chain of nuchains) {
|
|
||||||
// for (let i=0; i<chain.length-1; i++) {
|
|
||||||
// let c1 = chain[i];
|
|
||||||
// let c2 = chain[i+1];
|
|
||||||
// let int = intersectRayLine(np1, { dx: dy, dy: -dx }, c1, c2);
|
|
||||||
// if (int?.dist < min.dist && int?.dist < offsetN * count) {
|
|
||||||
// min = int;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
for (let seg of nusegs) {
|
for (let seg of nusegs) {
|
||||||
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);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue