move healPolys into polygons util class and rename as 'reconnect'
This commit is contained in:
parent
6bbfd243f5
commit
1e69db16f8
3 changed files with 94 additions and 86 deletions
|
|
@ -62,6 +62,7 @@ const POLYS = {
|
|||
outer,
|
||||
points,
|
||||
rayIntersect,
|
||||
reconnect,
|
||||
renest,
|
||||
route,
|
||||
setWinding,
|
||||
|
|
@ -1112,4 +1113,92 @@ export function route(polys, start) {
|
|||
return routed.map(r => r.poly);
|
||||
}
|
||||
|
||||
function dedup(arr, same) {
|
||||
const out = [];
|
||||
let prev;
|
||||
for (const x of arr) {
|
||||
if (!prev || !same(prev, x)) out.push(x);
|
||||
prev = x;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of Polygons, attempt to reconnect open polygons
|
||||
* into longer chains. If the chains close, emit a closed Polygon.
|
||||
* Closed polygons are ignored and passed into the output.
|
||||
*
|
||||
* @param {Polygon[]} polys polygons
|
||||
* @param {boolean} sameZ require open polygons to share endpoints with the same Z
|
||||
* @returns array of connected or closed polygons
|
||||
*/
|
||||
export function reconnect(polys, sameZ = true) {
|
||||
for (let p of polys) {
|
||||
if (p.appearsClosed()) {
|
||||
p.points.pop();
|
||||
p.setClosed();
|
||||
}
|
||||
}
|
||||
if (polys.length > 1) {
|
||||
let heal = 0;
|
||||
// heal/rejoin open segments that share endpoints
|
||||
outer: for (; ; heal++) {
|
||||
let ntmp = polys, tlen = ntmp.length;
|
||||
for (let i = 0; i < tlen; i++) {
|
||||
let s1 = ntmp[i];
|
||||
if (!s1) continue;
|
||||
for (let j = i + 1; j < tlen; j++) {
|
||||
let s2 = ntmp[j];
|
||||
if (!s2) continue;
|
||||
// require polys at same Z to heal
|
||||
if (sameZ && Math.abs(s1.getZ() - s2.getZ()) > 0.01) {
|
||||
continue;
|
||||
}
|
||||
if (!(s1.open && s2.open)) continue;
|
||||
if (s1.last().isMergable2D(s2.first())) {
|
||||
s1.addPoints(s2.points);
|
||||
ntmp[j] = null;
|
||||
continue outer;
|
||||
}
|
||||
if (s2.last().isMergable2D(s1.first())) {
|
||||
s2.addPoints(s1.points);
|
||||
ntmp[i] = null;
|
||||
continue outer;
|
||||
}
|
||||
if (s1.first().isMergable2D(s2.first())) {
|
||||
s1.reverse();
|
||||
s1.addPoints(s2.points);
|
||||
ntmp[j] = null;
|
||||
continue outer;
|
||||
}
|
||||
if (s1.last().isMergable2D(s2.last())) {
|
||||
s2.reverse();
|
||||
s1.addPoints(s2.points);
|
||||
ntmp[j] = null;
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (heal > 0) {
|
||||
// cull nulls
|
||||
polys = polys.filter(o => o);
|
||||
}
|
||||
// close poly if head meets tail
|
||||
for (let poly of polys) {
|
||||
poly.points = dedup(poly.points, (a,b) => a.isMergable3D(b));
|
||||
if (poly.open) {
|
||||
if (poly.first().isMergable3D(poly.last())) {
|
||||
poly.points.pop();
|
||||
poly.open = false;
|
||||
} else if (poly.first().isMergable2D(poly.last())) {
|
||||
poly.open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return polys;
|
||||
}
|
||||
|
||||
export const polygons = POLYS;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class OpArea extends CamOp {
|
|||
let { tool, mode, down, over, follow, expand, outline, smooth } = op;
|
||||
let { ov_topz, ov_botz, direction, rename } = op;
|
||||
let { settings, widget, tabs, color } = state;
|
||||
let { addSlices, setToolDiam, cutTabs, healPolys, shadowAt, workarea } = state;
|
||||
let { addSlices, setToolDiam, cutTabs, shadowAt, workarea } = state;
|
||||
|
||||
let areaTool = new Tool(settings, tool);
|
||||
let smoothVal = (smooth ?? 0) / 10;
|
||||
|
|
@ -68,7 +68,7 @@ class OpArea extends CamOp {
|
|||
|
||||
// connect open poly edge segments into closed loops (when possible)
|
||||
// surface and edge selections produce open polygons by default
|
||||
polys = POLY.nest(healPolys(polys, false));
|
||||
polys = POLY.nest(POLY.reconnect(polys, false));
|
||||
|
||||
// gather surface selections
|
||||
let vert = widget.getGeoVertices({ unroll: true, translate: true }).map(v => v.round(4));
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||
|
||||
import { base, util } from '../../../geo/base.js';
|
||||
import { util } from '../../../geo/base.js';
|
||||
import { newLine } from '../../../geo/line.js';
|
||||
import { newPoint } from '../../../geo/point.js';
|
||||
import { newPolygon } from '../../../geo/polygon.js';
|
||||
import { newSlopeFromAngle } from '../../../geo/slope.js';
|
||||
import { polygons as POLY } from '../../../geo/polygons.js';
|
||||
import { setSliceTracker } from '../../core/slice.js';
|
||||
import { ops as OPS } from './ops.js';
|
||||
|
|
@ -152,7 +151,6 @@ export async function cam_slice(settings, widget, onupdate, ondone) {
|
|||
cutPolys,
|
||||
cutTabs,
|
||||
dark,
|
||||
healPolys,
|
||||
isIndexed,
|
||||
ops: opList,
|
||||
setAxisIndex,
|
||||
|
|
@ -730,7 +728,7 @@ function cutTabs(tabs, offset) {
|
|||
hi.appendAll(POLY.setZ(poly.cut(tab.off, true), tab.top));
|
||||
}
|
||||
if (hi.length) {
|
||||
let heal = healPolys([ ...lo, ...hi ], false);
|
||||
let heal = POLY.reconnect([ ...lo, ...hi ], false);
|
||||
POLY.setWinding(heal, poly.isClockwise());
|
||||
out.push(...heal);
|
||||
} else {
|
||||
|
|
@ -743,7 +741,7 @@ function cutTabs(tabs, offset) {
|
|||
function cutPolys(polys, offset) {
|
||||
let noff = [];
|
||||
offset.forEach(op => noff.appendAll( op.cut(POLY.union(polys, 0, true), true) ));
|
||||
return healPolys(noff);
|
||||
return POLY.reconnect(noff);
|
||||
}
|
||||
|
||||
function contourPolys(widget, polys) {
|
||||
|
|
@ -758,82 +756,3 @@ function contourPolys(widget, polys) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dedup(arr, same) {
|
||||
const out = [];
|
||||
let prev;
|
||||
for (const x of arr) {
|
||||
if (!prev || !same(prev, x)) out.push(x);
|
||||
prev = x;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function healPolys(noff, sameZ = true) {
|
||||
for (let p of noff) {
|
||||
if (p.appearsClosed()) {
|
||||
p.points.pop();
|
||||
p.setClosed();
|
||||
}
|
||||
}
|
||||
if (noff.length > 1) {
|
||||
let heal = 0;
|
||||
// heal/rejoin open segments that share endpoints
|
||||
outer: for (; ; heal++) {
|
||||
let ntmp = noff, tlen = ntmp.length;
|
||||
for (let i = 0; i < tlen; i++) {
|
||||
let s1 = ntmp[i];
|
||||
if (!s1) continue;
|
||||
for (let j = i + 1; j < tlen; j++) {
|
||||
let s2 = ntmp[j];
|
||||
if (!s2) continue;
|
||||
// require polys at same Z to heal
|
||||
if (sameZ && Math.abs(s1.getZ() - s2.getZ()) > 0.01) {
|
||||
continue;
|
||||
}
|
||||
if (!(s1.open && s2.open)) continue;
|
||||
if (s1.last().isMergable2D(s2.first())) {
|
||||
s1.addPoints(s2.points);
|
||||
ntmp[j] = null;
|
||||
continue outer;
|
||||
}
|
||||
if (s2.last().isMergable2D(s1.first())) {
|
||||
s2.addPoints(s1.points);
|
||||
ntmp[i] = null;
|
||||
continue outer;
|
||||
}
|
||||
if (s1.first().isMergable2D(s2.first())) {
|
||||
s1.reverse();
|
||||
s1.addPoints(s2.points);
|
||||
ntmp[j] = null;
|
||||
continue outer;
|
||||
}
|
||||
if (s1.last().isMergable2D(s2.last())) {
|
||||
s2.reverse();
|
||||
s1.addPoints(s2.points);
|
||||
ntmp[j] = null;
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (heal > 0) {
|
||||
// cull nulls
|
||||
noff = noff.filter(o => o);
|
||||
}
|
||||
// close poly if head meets tail
|
||||
for (let poly of noff) {
|
||||
poly.points = dedup(poly.points, (a,b) => a.isMergable3D(b));
|
||||
if (poly.open) {
|
||||
if (poly.first().isMergable3D(poly.last())) {
|
||||
poly.points.pop();
|
||||
poly.open = false;
|
||||
} else if (poly.first().isMergable2D(poly.last())) {
|
||||
poly.open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return noff;
|
||||
}
|
||||
Loading…
Reference in a new issue