checkpoint thin3 work
This commit is contained in:
parent
76d028bd15
commit
ba61e39143
2 changed files with 152 additions and 96 deletions
|
|
@ -259,8 +259,9 @@ export class Polygon {
|
||||||
noodle(width) {
|
noodle(width) {
|
||||||
let clone = this.clone(true);
|
let clone = this.clone(true);
|
||||||
let ins = clone.offset(width);
|
let ins = clone.offset(width);
|
||||||
|
let remain = ins.clone(true);
|
||||||
let nood = POLY.nest(POLY.flatten([ clone, ...ins ], [], true));
|
let nood = POLY.nest(POLY.flatten([ clone, ...ins ], [], true));
|
||||||
return { noodle: nood, remain: ins };
|
return { noodle: nood, remain };
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate center crossing point cloud
|
// generate center crossing point cloud
|
||||||
|
|
|
||||||
|
|
@ -173,17 +173,23 @@ function thin_type_3(params) {
|
||||||
top.gaps = last = remain;
|
top.gaps = last = remain;
|
||||||
|
|
||||||
let thin = top.thin_fill;
|
let thin = top.thin_fill;
|
||||||
let scale = 5000;
|
let scale = 1000;
|
||||||
let minR = offsetN / 2;
|
let minR = offsetN / 2;
|
||||||
let maxR = minR * 1.5;
|
let maxR = minR * 1.5;
|
||||||
let minSpur = 0; // offsetN * 3;
|
let minSpur = 0; // offsetN * 3;
|
||||||
|
let mergeClosePoints = true;
|
||||||
|
let interpolateShortSpur = true;
|
||||||
|
|
||||||
let pointMap = new Map();
|
let pointMap = new Map();
|
||||||
let lineMap = new Map();
|
let lineMap = new Map();
|
||||||
|
|
||||||
|
function point2key(point) {
|
||||||
|
return `${(point.x*100)|0}|${(point.y*100)|0}`;
|
||||||
|
}
|
||||||
|
|
||||||
// convert point to deduplicated / unique point record
|
// convert point to deduplicated / unique point record
|
||||||
function pointToRec(point) {
|
function pointToRec(point) {
|
||||||
let key = `${(point.x*100)|0}|${(point.y*100)|0}`;
|
let key = point2key(point);
|
||||||
let rec = pointMap.get(key);
|
let rec = pointMap.get(key);
|
||||||
if (!rec) {
|
if (!rec) {
|
||||||
rec = {
|
rec = {
|
||||||
|
|
@ -222,6 +228,9 @@ function thin_type_3(params) {
|
||||||
|
|
||||||
let chains = [];
|
let chains = [];
|
||||||
|
|
||||||
|
// for each standalone noodle, construct medial axis
|
||||||
|
// map medial axis points into de-duplicated segments
|
||||||
|
// which will be re-connected into chains
|
||||||
for (let poly of noodle) {
|
for (let poly of noodle) {
|
||||||
// scale the polygon (including inners) so the medial axis code works properly
|
// scale the polygon (including inners) so the medial axis code works properly
|
||||||
poly = poly.clone(true).scale({ x:scale, y:scale, z:scale });
|
poly = poly.clone(true).scale({ x:scale, y:scale, z:scale });
|
||||||
|
|
@ -234,63 +243,75 @@ function thin_type_3(params) {
|
||||||
for (let { point0, point1 } of ma) {
|
for (let { point0, point1 } of ma) {
|
||||||
pointsToLine(point0, point1);
|
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);
|
// connect segments into chains
|
||||||
// filter out lines where both points' radii are under the
|
// console.log({ seg: [...lineMap.values()].sort((s1,s2) => s2.len - s1.len) });
|
||||||
// minR threshold. shorten and fixup lines where minR falls
|
for (let seg of lineMap.values()) {
|
||||||
// somewhere along the gradient between ends.
|
let { p0, p1 } = seg;
|
||||||
if (p0.r < minR || p1.r < minR) {
|
let len = seg.len = Math.hypot(p0.x-p1.x, p0.y-p1.y);
|
||||||
if (p0.r > minR && len > minSpur) {
|
// filter out lines where both points' radii are under the
|
||||||
// move p1 toward p0 until minR met
|
// minR threshold. shorten and fixup lines where minR falls
|
||||||
let pct = (minR - p1.r) / (p0.r - p1.r);
|
// somewhere along the gradient between ends.
|
||||||
let dx = p1.x - p0.x;
|
if (interpolateShortSpur)
|
||||||
let dy = p1.y - p0.y;
|
if (p0.r < minR || p1.r < minR) {
|
||||||
p1.x -= dx * pct;
|
if (p0.r > minR && len > minSpur) {
|
||||||
p1.y -= dy * pct;
|
// move p1 toward p0 until minR met
|
||||||
p1.r = minR;
|
let pct = (minR - p1.r) / (p0.r - p1.r);
|
||||||
pointMap.delete(p1.key);
|
let dx = p1.x - p0.x;
|
||||||
} else if (p1.r > minR && len > minSpur) {
|
let dy = p1.y - p0.y;
|
||||||
// move p0 toward p1 until minR met
|
p1.x -= dx * pct;
|
||||||
let pct = (minR - p0.r) / (p1.r - p0.r);
|
p1.y -= dy * pct;
|
||||||
let dx = p0.x - p1.x;
|
p1.r = minR;
|
||||||
let dy = p0.y - p1.y;
|
pointMap.delete(p1.key);
|
||||||
p0.x -= dx * pct;
|
} else if (p1.r > minR && len > minSpur) {
|
||||||
p0.y -= dy * pct;
|
// move p0 toward p1 until minR met
|
||||||
p0.r = minR;
|
let pct = (minR - p0.r) / (p1.r - p0.r);
|
||||||
pointMap.delete(p0.key);
|
let dx = p0.x - p1.x;
|
||||||
} else {
|
let dy = p0.y - p1.y;
|
||||||
lineMap.delete(seg.key);
|
p0.x -= dx * pct;
|
||||||
continue;
|
p0.y -= dy * pct;
|
||||||
}
|
p0.r = minR;
|
||||||
|
pointMap.delete(p0.key);
|
||||||
|
} else {
|
||||||
|
// both points eliminated
|
||||||
|
pointMap.delete(p0.key);
|
||||||
|
pointMap.delete(p1.key);
|
||||||
|
lineMap.delete(seg.key);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
// for each line segment either add it to a chain or start a new chain
|
}
|
||||||
let add;
|
// for each line segment either add it to a chain or start a new chain
|
||||||
for (let chain of chains) {
|
let add;
|
||||||
if (p0.count === 2 && p0.key === chain[0].key) {
|
for (let chain of chains) {
|
||||||
chain.splice(0,0,{...p1, len});
|
if (p0.count === 2 && p0.key === chain[0].key) {
|
||||||
} else if (p0.count === 2 && p0.key === chain.peek().key) {
|
// prefix chain with p1
|
||||||
chain.peek().len = len;
|
chain.splice(0,0,{...p1, len});
|
||||||
chain.push({...p1});
|
add = true;
|
||||||
add = true;
|
} else if (p0.count === 2 && p0.key === chain.peek().key) {
|
||||||
} else if (p1.count === 2 && p1.key === chain[0].key) {
|
// append chain with p1
|
||||||
chain.splice(0,0,{...p0, len});
|
chain.peek().len = len;
|
||||||
add = true;
|
chain.push({...p1});
|
||||||
} else if (p1.count === 2 && p1.key === chain.peek().key) {
|
add = true;
|
||||||
chain.peek().len = len;
|
} else if (p1.count === 2 && p1.key === chain[0].key) {
|
||||||
chain.push({...p0});
|
// prefix chain with p0
|
||||||
add = true;
|
chain.splice(0,0,{...p0, len});
|
||||||
}
|
add = true;
|
||||||
if (add) {
|
} else if (p1.count === 2 && p1.key === chain.peek().key) {
|
||||||
chain.total += len;
|
// append chain with p0
|
||||||
break;
|
chain.peek().len = len;
|
||||||
}
|
chain.push({...p0});
|
||||||
|
add = true;
|
||||||
}
|
}
|
||||||
if (!add) {
|
if (add) {
|
||||||
chains.push([ {...p0 ,len}, {...p1} ]);
|
chain.total += len;
|
||||||
chains.peek().total = len;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!add) {
|
||||||
|
chains.push([ {...p0 ,len}, {...p1} ]);
|
||||||
|
chains.peek().total = len;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: merge chains at next of 3 or more preferencing
|
// todo: merge chains at next of 3 or more preferencing
|
||||||
|
|
@ -300,7 +321,7 @@ function thin_type_3(params) {
|
||||||
let showNoodle = false;
|
let showNoodle = false;
|
||||||
let showMid = true;
|
let showMid = true;
|
||||||
let showCross = false;
|
let showCross = false;
|
||||||
let showRad = true;
|
let showRad = false;
|
||||||
let showShells = false;
|
let showShells = false;
|
||||||
|
|
||||||
// render inset "noodle"
|
// render inset "noodle"
|
||||||
|
|
@ -327,10 +348,16 @@ 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).map(chain => {
|
chains = chains.filter(c => c.total >= minR);
|
||||||
|
chains.sort((c1,c2) => c2.total - c1.total);
|
||||||
|
|
||||||
|
// merge chain points that are too close
|
||||||
|
if (mergeClosePoints)
|
||||||
|
chains = chains.map(chain => {
|
||||||
// chain reduction by merging short segments
|
// chain reduction by merging short segments
|
||||||
let end = chain[0];
|
let end = chain[0];
|
||||||
let nuchain = [ end ];
|
let nuchain = [ end ];
|
||||||
|
nuchain.total = chain.total;
|
||||||
for (let point of chain.slice(1)) {
|
for (let point of chain.slice(1)) {
|
||||||
if (point.len === undefined || point.len + end.len >= minR) {
|
if (point.len === undefined || point.len + end.len >= minR) {
|
||||||
nuchain.push(point);
|
nuchain.push(point);
|
||||||
|
|
@ -342,9 +369,37 @@ function thin_type_3(params) {
|
||||||
return nuchain;
|
return nuchain;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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] = [];
|
||||||
|
if (rec.indexOf(chain) < 0) rec.push(chain);
|
||||||
|
}
|
||||||
|
for (let chain of chains) {
|
||||||
|
addEp(chain[0], chain);
|
||||||
|
addEp(chain.peek(), chain);
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark spurs
|
||||||
|
for (let [key,val] of Object.entries(nexus)) {
|
||||||
|
if (val.length === 1) {
|
||||||
|
val[0].spur = true;
|
||||||
|
delete nexus[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// detach spurs from nexus and shorten by ?
|
||||||
|
for (let [key,val] of Object.entries(nexus)) {
|
||||||
|
nexus[key] = val.filter(c => !c.spur);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log({ chains, nexus });
|
||||||
|
|
||||||
// connect chains end to end following nexus branches that result
|
// connect chains end to end following nexus branches that result
|
||||||
// in the longest final merged chain (open or closed)
|
// in the longest final merged chain (open or closed)
|
||||||
console.log(chains);
|
// map chain endpoints to nexus points
|
||||||
|
|
||||||
// gather point offsets into shells
|
// gather point offsets into shells
|
||||||
// todo: order outer shell innersection to inner
|
// todo: order outer shell innersection to inner
|
||||||
|
|
@ -410,43 +465,43 @@ function thin_type_3(params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// follow shells and output lines
|
// follow shells and output lines
|
||||||
console.log({ shells });
|
// console.log({ shells });
|
||||||
if (showShells)
|
// if (showShells)
|
||||||
for (let shell = 1; shell <= 1; shell++) {
|
// for (let shell = 1; shell <= 1; shell++) {
|
||||||
let poly = new Polygon().setOpen();
|
// let poly = new Polygon().setOpen();
|
||||||
let arr = shells[shell];
|
// let arr = shells[shell];
|
||||||
let lp = arr[0];
|
// let lp = arr[0];
|
||||||
poly.push(lp);
|
// poly.push(lp);
|
||||||
let find;
|
// let find;
|
||||||
for (;;) {
|
// for (;;) {
|
||||||
find = { dist: Infinity };
|
// find = { dist: Infinity };
|
||||||
// find next closest point
|
// // find next closest point
|
||||||
for (let i=1; i<arr.length; i++) {
|
// for (let i=1; i<arr.length; i++) {
|
||||||
let pt = arr[i];
|
// let pt = arr[i];
|
||||||
if (!pt) continue;
|
// if (!pt) continue;
|
||||||
let dist = lp.distTo2D(pt);
|
// let dist = lp.distTo2D(pt);
|
||||||
if (dist < find.dist) {
|
// if (dist < find.dist) {
|
||||||
find.dist = dist;
|
// find.dist = dist;
|
||||||
find.pi = i;
|
// find.pi = i;
|
||||||
find.pt = pt;
|
// find.pt = pt;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (find.pt) {
|
// if (find.pt) {
|
||||||
if (find.dist > 1) {
|
// if (find.dist > 1) {
|
||||||
top.shells.push(poly);
|
// top.shells.push(poly);
|
||||||
poly = new Polygon().setOpen();
|
// poly = new Polygon().setOpen();
|
||||||
}
|
// }
|
||||||
arr[find.pi] = undefined;
|
// arr[find.pi] = undefined;
|
||||||
poly.push(lp = find.pt);
|
// poly.push(lp = find.pt);
|
||||||
} else {
|
// } else {
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
console.log({ poly });
|
// console.log({ poly });
|
||||||
if (poly.length > 1) {
|
// if (poly.length > 1) {
|
||||||
top.shells.push(poly);
|
// top.shells.push(poly);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return { last, gaps };
|
return { last, gaps };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue