make clipper calls a little more readable, compact when packaged

This commit is contained in:
Stewart Allen 2024-07-02 22:03:35 -04:00
commit fd86b9b93a
2 changed files with 122 additions and 114 deletions

View file

@ -17,11 +17,24 @@ const { config, util, polygons, newBounds, newPoint } = base;
const POLY = polygons, const POLY = polygons,
XAXIS = new THREE.Vector3(1,0,0), XAXIS = new THREE.Vector3(1,0,0),
DEG2RAD = Math.PI / 180, DEG2RAD = Math.PI / 180,
clib = self.ClipperLib, ClipperLib = self.ClipperLib,
clip = clib.Clipper, Clipper = ClipperLib.Clipper,
ctyp = clib.ClipType, ClipType = ClipperLib.ClipType,
ptyp = clib.PolyType, PolyType = ClipperLib.PolyType,
cfil = clib.PolyFillType; PolyFillType = ClipperLib.PolyFillType,
CleanPolygon = Clipper.CleanPolygon,
FillNonZero = PolyFillType.pftNonZero,
FillEvenOdd = PolyFillType.pftEvenOdd,
PathSubject = PolyType.ptSubject,
PathClip = PolyType.ptClip,
EndType = ClipperLib.EndType,
JoinType = ClipperLib.JoinType,
PolyTree = ClipperLib.PolyTree,
ClipDiff = ClipType.ctDifference,
ClipUnion = ClipType.ctUnion,
ClipIntersect = ClipType.ctIntersection,
ClipperOffset = ClipperLib.ClipperOffset
;
let seqid = Math.round(Math.random() * 0xffffffff); let seqid = Math.round(Math.random() * 0xffffffff);
@ -1519,7 +1532,7 @@ class Polygon {
* non-nested polygons. used primarily in slicer/connectLines. * non-nested polygons. used primarily in slicer/connectLines.
*/ */
clean(deep, parent, merge = config.clipperClean) { clean(deep, parent, merge = config.clipperClean) {
let clean = clip.CleanPolygon(this.toClipper()[0], merge), let clean = CleanPolygon(this.toClipper()[0], merge),
poly = fromClipperPath(clean, this.getZ()); poly = fromClipperPath(clean, this.getZ());
if (poly.length === 0) return this; if (poly.length === 0) return this;
if (deep && this.inner) { if (deep && this.inner) {
@ -1590,23 +1603,21 @@ class Polygon {
*/ */
offset_open(distance, type = 'miter') { offset_open(distance, type = 'miter') {
if (this.isOpen()) { if (this.isOpen()) {
let cjnt = clib.JoinType, let coff = new ClipperOffset(),
cety = clib.EndType, tree = new PolyTree(),
coff = new clib.ClipperOffset(),
ctre = new clib.PolyTree(),
entt = { entt = {
'square' : cety.etOpenSquare, 'square' : EndType.etOpenSquare,
'round' : cety.etOpenRound, 'round' : EndType.etOpenRound,
'miter' : cety.etOpenSquare 'miter' : EndType.etOpenSquare
}[type] || cety.etOpenSquare, }[type] || EndType.etOpenSquare,
jntt = { jntt = {
'square': cjnt.jtSquare, 'square': JoinType.jtSquare,
'round': cjnt.jtRound, 'round': JoinType.jtRound,
'miter': cjnt.jtMiter 'miter': JoinType.jtMiter
}[type] || cjnt.jtMiter; }[type] || JoinType.jtMiter;
coff.AddPaths(this.toClipper(), jntt, entt); coff.AddPaths(this.toClipper(), jntt, entt);
coff.Execute(ctre, distance * config.clipper); coff.Execute(tree, distance * config.clipper);
return POLY.fromClipperTree(ctre, this.getZ(), null, null, 0); return POLY.fromClipperTree(tree, this.getZ(), null, null, 0);
} else { } else {
return this.offset(distance); return this.offset(distance);
} }
@ -1748,16 +1759,16 @@ class Polygon {
*/ */
diff(poly) { diff(poly) {
let fillang = this.fillang && this.area() > poly.area() ? this.fillang : poly.fillang, let fillang = this.fillang && this.area() > poly.area() ? this.fillang : poly.fillang,
clip = new clib.Clipper(), clip = new Clipper(),
ctre = new clib.PolyTree(), tree = new PolyTree(),
sp1 = this.toClipper(), sp1 = this.toClipper(),
sp2 = poly.toClipper(); sp2 = poly.toClipper();
clip.AddPaths(sp1, ptyp.ptSubject, true); clip.AddPaths(sp1, PathSubject, true);
clip.AddPaths(sp2, ptyp.ptClip, true); clip.AddPaths(sp2, PathClip, true);
if (clip.Execute(ctyp.ctDifference, ctre, cfil.pftEvenOdd, cfil.pftEvenOdd)) { if (clip.Execute(ClipDiff, tree, FillEvenOdd, FillEvenOdd)) {
poly = POLY.fromClipperTree(ctre, poly.getZ()); poly = POLY.fromClipperTree(tree, poly.getZ());
poly.forEach(p => { poly.forEach(p => {
p.fillang = fillang; p.fillang = fillang;
}) })
@ -1773,15 +1784,15 @@ class Polygon {
*/ */
mask(poly, nullOnEquiv, minarea) { mask(poly, nullOnEquiv, minarea) {
let fillang = this.fillang && this.area() > poly.area() ? this.fillang : poly.fillang, let fillang = this.fillang && this.area() > poly.area() ? this.fillang : poly.fillang,
clip = new clib.Clipper(), clip = new Clipper(),
ctre = new clib.PolyTree(), tree = new PolyTree(),
sp1 = this.toClipper(), sp1 = this.toClipper(),
sp2 = poly.toClipper(); sp2 = poly.toClipper();
clip.AddPaths(sp1, ptyp.ptSubject, true); clip.AddPaths(sp1, PathSubject, true);
clip.AddPaths(sp2, ptyp.ptClip, true); clip.AddPaths(sp2, PathClip, true);
if (clip.Execute(ctyp.ctIntersection, ctre, cfil.pftEvenOdd, cfil.pftEvenOdd)) { if (clip.Execute(ClipIntersect, tree, FillEvenOdd, FillEvenOdd)) {
poly = POLY.fromClipperTree(ctre, this.getZ(), undefined, undefined, minarea); poly = POLY.fromClipperTree(tree, this.getZ(), undefined, undefined, minarea);
poly.forEach(p => { poly.forEach(p => {
p.fillang = fillang; p.fillang = fillang;
}) })
@ -1810,17 +1821,17 @@ class Polygon {
} }
} }
let clip = new clib.Clipper(), let clip = new Clipper(),
ctre = new clib.PolyTree(), tree = new PolyTree(),
type = inter ? ctyp.ctIntersection : ctyp.ctDifference, type = inter ? ClipIntersect : ClipDiff,
sp1 = target.toClipper(), sp1 = target.toClipper(),
sp2 = POLY.toClipper(polys); sp2 = POLY.toClipper(polys);
clip.AddPaths(sp1, ptyp.ptSubject, false); clip.AddPaths(sp1, PathSubject, false);
clip.AddPaths(sp2, ptyp.ptClip, true); clip.AddPaths(sp2, PathClip, true);
if (clip.Execute(type, ctre, cfil.pftEvenOdd, cfil.pftEvenOdd)) { if (clip.Execute(type, tree, FillEvenOdd, FillEvenOdd)) {
let cuts = POLY.fromClipperTree(ctre, target.getZ(), null, null, 0); let cuts = POLY.fromClipperTree(tree, target.getZ(), null, null, 0);
cuts.forEach(no => { cuts.forEach(no => {
// heal open but really closed polygons because cutting // heal open but really closed polygons because cutting
// has to open the poly to perform the cut. but the result // has to open the poly to perform the cut. but the result
@ -1841,22 +1852,22 @@ class Polygon {
intersect(poly, min) { intersect(poly, min) {
if (!this.overlaps(poly)) return null; if (!this.overlaps(poly)) return null;
let clip = new clib.Clipper(),
ctre = new clib.PolyTree(),
sp1 = this.toClipper(),
sp2 = poly.toClipper(),
minarea = min >= 0 ? min : 0.1;
if (this.isInside(poly)) { if (this.isInside(poly)) {
return [this]; return [this];
} }
clip.AddPaths(sp1, ptyp.ptSubject, true); let clip = new Clipper(),
clip.AddPaths(sp2, ptyp.ptClip, true); tree = new PolyTree(),
sp1 = this.toClipper(),
sp2 = poly.toClipper(),
minarea = min >= 0 ? min : 0.1;
if (clip.Execute(ctyp.ctIntersection, ctre, cfil.pftNonZero, cfil.pftNonZero)) { clip.AddPaths(sp1, PathSubject, true);
clip.AddPaths(sp2, PathClip, true);
if (clip.Execute(ClipIntersect, tree, FillNonZero, FillNonZero)) {
let inter = POLY let inter = POLY
.fromClipperTreeUnion(ctre, poly.getZ(), minarea) .fromClipperTreeUnion(tree, poly.getZ(), minarea)
// .filter(p => p.isEquivalent(this) || p.isInside(this)) // .filter(p => p.isEquivalent(this) || p.isInside(this))
.filter(p => p.isInside(this)); .filter(p => p.isInside(this));
return inter; return inter;
@ -1890,7 +1901,7 @@ class Polygon {
} }
let clip = this.toClipper(), let clip = this.toClipper(),
res = clib.Clipper.SimplifyPolygons(clip, cfil.pftNonZero); res = CLIB.Clipper.SimplifyPolygons(clip, FillNonZero);
if (!(res && res.length)) { if (!(res && res.length)) {
return null; return null;
@ -1919,17 +1930,17 @@ class Polygon {
if (!this.overlaps(poly)) return null; if (!this.overlaps(poly)) return null;
let fillang = this.fillang && this.area() > poly.area() ? this.fillang : poly.fillang, let fillang = this.fillang && this.area() > poly.area() ? this.fillang : poly.fillang,
clip = new clib.Clipper(), clip = new Clipper(),
ctre = new clib.PolyTree(), tree = new PolyTree(),
sp1 = this.toClipper(), sp1 = this.toClipper(),
sp2 = poly.toClipper(), sp2 = poly.toClipper(),
minarea = min >= 0 ? min : 0.1; minarea = min >= 0 ? min : 0.1;
clip.AddPaths(sp1, ptyp.ptSubject, true); clip.AddPaths(sp1, PathSubject, true);
clip.AddPaths(sp2, ptyp.ptClip, true); clip.AddPaths(sp2, PathClip, true);
if (clip.Execute(ctyp.ctUnion, ctre, cfil.pftEvenOdd, cfil.pftEvenOdd)) { if (clip.Execute(ClipUnion, tree, FillEvenOdd, FillEvenOdd)) {
let union = POLY.fromClipperTreeUnion(ctre, poly.getZ(), minarea); let union = POLY.fromClipperTreeUnion(tree, poly.getZ(), minarea);
if (all) { if (all) {
if (union.length === 2) { if (union.length === 2) {
return null; return null;

View file

@ -18,11 +18,26 @@ const DEG2RAD = Math.PI / 180,
SQR = util.sqr, SQR = util.sqr,
ABS = Math.abs; ABS = Math.abs;
const clib = self.ClipperLib, const ClipperLib = self.ClipperLib,
clip = clib.Clipper, Clipper = ClipperLib.Clipper,
ctyp = clib.ClipType, ClipType = ClipperLib.ClipType,
ptyp = clib.PolyType, PolyType = ClipperLib.PolyType,
cfil = clib.PolyFillType; PolyFillType = ClipperLib.PolyFillType,
CleanPolygon = Clipper.CleanPolygon,
CleanPolygons = Clipper.CleanPolygons,
SimplifyPolygons = Clipper.SimplifyPolygons,
FillNonZero = PolyFillType.pftNonZero,
FillEvenOdd = PolyFillType.pftEvenOdd,
PathSubject = PolyType.ptSubject,
PathClip = PolyType.ptClip,
EndType = ClipperLib.EndType,
JoinType = ClipperLib.JoinType,
PolyTree = ClipperLib.PolyTree,
ClipDiff = ClipType.ctDifference,
ClipUnion = ClipType.ctUnion,
ClipIntersect = ClipType.ctIntersection,
ClipperOffset = ClipperLib.ClipperOffset
;
const POLYS = base.polygons = { const POLYS = base.polygons = {
rayIntersect, rayIntersect,
@ -152,7 +167,7 @@ function fromClipperTreeUnion(tnode, z, minarea, tops, parent) {
function cleanClipperTree(tree) { function cleanClipperTree(tree) {
if (tree.m_Childs) if (tree.m_Childs)
for (let child of tree.m_Childs) { for (let child of tree.m_Childs) {
child.m_polygon = clip.CleanPolygon(child.m_polygon, config.clipperClean); child.m_polygon = CleanPolygon(child.m_polygon, config.clipperClean);
cleanClipperTree(child.m_Childs); cleanClipperTree(child.m_Childs);
} }
@ -354,31 +369,31 @@ function subtract(setA, setB, outA, outB, z, minArea, opt = {}) {
outB.appendAll(filter(oB)); outB.appendAll(filter(oB));
} }
} else { } else {
let clip = new clib.Clipper(), let clip = new Clipper(),
ctre = new clib.PolyTree(), tree = new PolyTree(),
sp1 = toClipper(setA), sp1 = toClipper(setA),
sp2 = toClipper(setB); sp2 = toClipper(setB);
// more expensive? worth it? // more expensive? worth it?
clip.StrictlySimple = true; clip.StrictlySimple = true;
if (outA) { if (outA) {
clip.AddPaths(sp1, ptyp.ptSubject, true); clip.AddPaths(sp1, PathSubject, true);
clip.AddPaths(sp2, ptyp.ptClip, true); clip.AddPaths(sp2, PathClip, true);
if (clip.Execute(ctyp.ctDifference, ctre, cfil.pftEvenOdd, cfil.pftEvenOdd)) { if (clip.Execute(ClipDiff, tree, FillEvenOdd, FillEvenOdd)) {
cleanClipperTree(ctre); cleanClipperTree(tree);
filter(fromClipperTree(ctre, z, null, null, min), outA); filter(fromClipperTree(tree, z, null, null, min), outA);
} }
} }
if (outB) { if (outB) {
if (outA) { if (outA) {
ctre.Clear(); tree.Clear();
clip.Clear(); clip.Clear();
} }
clip.AddPaths(sp2, ptyp.ptSubject, true); clip.AddPaths(sp2, PathSubject, true);
clip.AddPaths(sp1, ptyp.ptClip, true); clip.AddPaths(sp1, PathClip, true);
if (clip.Execute(ctyp.ctDifference, ctre, cfil.pftEvenOdd, cfil.pftEvenOdd)) { if (clip.Execute(ClipDiff, tree, FillEvenOdd, FillEvenOdd)) {
cleanClipperTree(ctre); cleanClipperTree(tree);
filter(fromClipperTree(ctre, z, null, null, min), outB); filter(fromClipperTree(tree, z, null, null, min), outB);
} }
} }
} }
@ -459,16 +474,16 @@ function subtract(setA, setB, outA, outB, z, minArea, opt = {}) {
* @returns {?Polygon[]} * @returns {?Polygon[]}
*/ */
function diff(setA, setB, z) { function diff(setA, setB, z) {
let clip = new clib.Clipper(), let clip = new Clipper(),
ctre = new clib.PolyTree(), tree = new PolyTree(),
sp1 = toClipper(setA), sp1 = toClipper(setA),
sp2 = toClipper(setB); sp2 = toClipper(setB);
clip.AddPaths(sp1, ptyp.ptSubject, true); clip.AddPaths(sp1, PathSubject, true);
clip.AddPaths(sp2, ptyp.ptClip, true); clip.AddPaths(sp2, PathClip, true);
if (clip.Execute(ctyp.ctDifference, ctre, cfil.pftEvenOdd, cfil.pftEvenOdd)) { if (clip.Execute(ClipDiff, tree, FillEvenOdd, FillEvenOdd)) {
return fromClipperTree(ctre, z); return fromClipperTree(tree, z);
} else { } else {
return null; return null;
} }
@ -503,24 +518,6 @@ function sumCirc(polys) {
return sum; return sum;
} }
/**
* @param {Polygon[]} polys
* @param {number} distance offset
* @param {number} [z] defaults to 0
*/
// function expand_lines(poly, distance, z) {
// let fact = config.clipper,
// cjnt = clib.JoinType,
// cety = clib.EndType,
// coff = new clib.ClipperOffset(),
// ctre = new clib.PolyTree();
// coff.AddPaths(poly.toClipper(), cjnt.jtMiter, cety.etOpenSquare);
// coff.Execute(ctre, distance * fact);
// return fromClipperTree(ctre, z, null, null, 0);
// }
/** /**
* @param {Polygon[]} polys * @param {Polygon[]} polys
* @param {number} distance offset * @param {number} distance offset
@ -565,9 +562,9 @@ function offset(polys, dist, opts = {}) {
depth = numOrDefault(opts.depth, 0), depth = numOrDefault(opts.depth, 0),
clean = opts.clean !== false, clean = opts.clean !== false,
simple = opts.simple !== false, simple = opts.simple !== false,
fill = numOrDefault(opts.fill, clib.PolyFillType.pftNonZero), fill = numOrDefault(opts.fill, FillNonZero),
join = numOrDefault(opts.join, clib.JoinType.jtMiter), join = numOrDefault(opts.join, JoinType.jtMiter),
type = numOrDefault(opts.type, clib.EndType.etClosedPolygon), type = numOrDefault(opts.type, EndType.etClosedPolygon),
// if dist is array with values, shift out next offset // if dist is array with values, shift out next offset
offs = Array.isArray(dist) ? (dist.length > 1 ? dist.shift() : dist[0]) : dist, offs = Array.isArray(dist) ? (dist.length > 1 ? dist.shift() : dist[0]) : dist,
mina = numOrDefault(opts.minArea, 0.1), mina = numOrDefault(opts.minArea, 0.1),
@ -583,21 +580,21 @@ function offset(polys, dist, opts = {}) {
return offset(polys, dist, opts); return offset(polys, dist, opts);
} }
} else { } else {
let coff = new clib.ClipperOffset(opts.miter, opts.arc), let coff = new ClipperOffset(opts.miter, opts.arc),
ctre = new clib.PolyTree(); tree = new PolyTree();
// setup offset // setup offset
for (let poly of polys) { for (let poly of polys) {
// convert to clipper format // convert to clipper format
poly = poly.toClipper(); poly = poly.toClipper();
if (clean) poly = clib.Clipper.CleanPolygons(poly, config.clipperClean); if (clean) poly = CleanPolygons(poly, config.clipperClean);
if (simple) poly = clib.Clipper.SimplifyPolygons(poly, fill); if (simple) poly = SimplifyPolygons(poly, fill);
coff.AddPaths(poly, join, type); coff.AddPaths(poly, join, type);
} }
// perform offset // perform offset
coff.Execute(ctre, offs * config.clipper); coff.Execute(tree, offs * config.clipper);
// convert back from clipper output format // convert back from clipper output format
polys = fromClipperTree(ctre, zed, null, null, mina); polys = fromClipperTree(tree, zed, null, null, mina);
} }
@ -748,8 +745,8 @@ function fillArea(polys, angle, spacing, output, minLen, maxLen) {
step = SQRT(SQR(stepX) + SQR(stepY)), step = SQRT(SQR(stepX) + SQR(stepY)),
steps = dist / step, steps = dist / step,
start = angle < 0 ? { x:bounds.minx, y:bounds.miny, z:zpos } : { x:bounds.maxx, y:bounds.miny, z:zpos }, start = angle < 0 ? { x:bounds.minx, y:bounds.miny, z:zpos } : { x:bounds.maxx, y:bounds.miny, z:zpos },
clip = new clib.Clipper(), clip = new Clipper(),
ctre = new clib.PolyTree(), tree = new PolyTree(),
minlen = base.config.clipper * (minLen || 0), minlen = base.config.clipper * (minLen || 0),
maxlen = base.config.clipper * (maxLen || 0), maxlen = base.config.clipper * (maxLen || 0),
lines = []; lines = [];
@ -771,13 +768,13 @@ function fillArea(polys, angle, spacing, output, minLen, maxLen) {
start.y += stepY; start.y += stepY;
} }
clip.AddPaths(lines, ptyp.ptSubject, false); clip.AddPaths(lines, PathSubject, false);
clip.AddPaths(toClipper(polys), ptyp.ptClip, true); clip.AddPaths(toClipper(polys), PathClip, true);
lines = []; lines = [];
if (clip.Execute(ctyp.ctIntersection, ctre, cfil.pftNonZero, cfil.pftEvenOdd)) { if (clip.Execute(ClipIntersect, tree, FillNonZero, FillEvenOdd)) {
for (let poly of ctre.m_AllPolys) { for (let poly of tree.m_AllPolys) {
if (minlen || maxlen) { if (minlen || maxlen) {
let plen = clib.JS.PerimeterOfPath(poly.m_polygon, false, 1); let plen = clib.JS.PerimeterOfPath(poly.m_polygon, false, 1);
if (minlen && plen < minlen) continue; if (minlen && plen < minlen) continue;