checkpoint roughing refactor
This commit is contained in:
parent
3839a4af16
commit
53d75fd4ed
4 changed files with 119 additions and 107 deletions
|
|
@ -1286,7 +1286,7 @@
|
||||||
* @param {Polygon} poly
|
* @param {Polygon} poly
|
||||||
* @returns {?Polygon} intersected polygon or null if no intersection
|
* @returns {?Polygon} intersected polygon or null if no intersection
|
||||||
*/
|
*/
|
||||||
PRO.union = function(poly, min) {
|
PRO.union = function(poly, min, all) {
|
||||||
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,
|
||||||
|
|
@ -1305,10 +1305,15 @@
|
||||||
|
|
||||||
if (clip.Execute(ctyp.ctUnion, ctre, cfil.pftEvenOdd, cfil.pftEvenOdd)) {
|
if (clip.Execute(ctyp.ctUnion, ctre, cfil.pftEvenOdd, cfil.pftEvenOdd)) {
|
||||||
let union = POLY().fromClipperTreeUnion(ctre, poly.getZ(), minarea);
|
let union = POLY().fromClipperTreeUnion(ctre, poly.getZ(), minarea);
|
||||||
|
if (all) {
|
||||||
|
return union;
|
||||||
|
}
|
||||||
if (union.length === 1) {
|
if (union.length === 1) {
|
||||||
union = union[0];
|
union = union[0];
|
||||||
union.fillang = fillang;
|
union.fillang = fillang;
|
||||||
return union;
|
return union;
|
||||||
|
} else {
|
||||||
|
console.log({check_union_call_path: union});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
union : union,
|
union : union,
|
||||||
nest : nest,
|
nest : nest,
|
||||||
diff : doDiff,
|
diff : doDiff,
|
||||||
|
setZ : setZ,
|
||||||
filter : filter,
|
filter : filter,
|
||||||
toClipper : toClipper,
|
toClipper : toClipper,
|
||||||
fromClipperNode : fromClipperNode,
|
fromClipperNode : fromClipperNode,
|
||||||
|
|
@ -49,6 +50,11 @@
|
||||||
return num !== undefined ? num : def;
|
return num !== undefined ? num : def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setZ(polys, z) {
|
||||||
|
polys.forEach(p => p.setZ(z));
|
||||||
|
return polys;
|
||||||
|
}
|
||||||
|
|
||||||
function toClipper(polys,debug) {
|
function toClipper(polys,debug) {
|
||||||
let out = [];
|
let out = [];
|
||||||
polys.forEach(function(poly) { poly.toClipper(out,debug) });
|
polys.forEach(function(poly) { poly.toClipper(out,debug) });
|
||||||
|
|
@ -336,7 +342,7 @@
|
||||||
* @param {Polygon[]} polys
|
* @param {Polygon[]} polys
|
||||||
* @returns {Polygon[]}
|
* @returns {Polygon[]}
|
||||||
*/
|
*/
|
||||||
function union(polys, minarea) {
|
function union(polys, minarea, all) {
|
||||||
if (polys.length < 2) return polys;
|
if (polys.length < 2) return polys;
|
||||||
|
|
||||||
let out = polys.slice(), i, j, union, uset = [];
|
let out = polys.slice(), i, j, union, uset = [];
|
||||||
|
|
@ -345,11 +351,15 @@
|
||||||
if (!out[i]) continue;
|
if (!out[i]) continue;
|
||||||
for (j=i+1; j<out.length; j++) {
|
for (j=i+1; j<out.length; j++) {
|
||||||
if (!out[j]) continue;
|
if (!out[j]) continue;
|
||||||
union = out[i].union(out[j], minarea);
|
union = out[i].union(out[j], minarea, all);
|
||||||
if (union) {
|
if (union) {
|
||||||
out[i] = null;
|
out[i] = null;
|
||||||
out[j] = null;
|
out[j] = null;
|
||||||
out.push(union);
|
if (all) {
|
||||||
|
out.appendAll(union);
|
||||||
|
} else {
|
||||||
|
out.push(union);
|
||||||
|
}
|
||||||
continue outer;
|
continue outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -455,7 +465,7 @@
|
||||||
* and return resulting gaps from offsets for thin wall detection in
|
* and return resulting gaps from offsets for thin wall detection in
|
||||||
* in FDM mode and uncleared areas in CAM mode.
|
* in FDM mode and uncleared areas in CAM mode.
|
||||||
*/
|
*/
|
||||||
function offset(polys, dist, opt) {
|
function offset(polys, dist, options) {
|
||||||
// cause inner / outer polys to be reversed from each other
|
// cause inner / outer polys to be reversed from each other
|
||||||
alignWindings(polys);
|
alignWindings(polys);
|
||||||
polys.forEach(function(poly) {
|
polys.forEach(function(poly) {
|
||||||
|
|
@ -465,9 +475,9 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
let orig = polys,
|
let orig = polys,
|
||||||
opts = opt || {},
|
opts = options || {},
|
||||||
count = numOrDefault(opt.count, 1),
|
count = numOrDefault(opts.count, 1),
|
||||||
depth = numOrDefault(opt.depth, 0),
|
depth = numOrDefault(opts.depth, 0),
|
||||||
clean = opts.clean !== false,
|
clean = opts.clean !== false,
|
||||||
simple = opts.simple !== false,
|
simple = opts.simple !== false,
|
||||||
fill = opts.fill || ClipperLib.PolyFillType.pftNonZero,
|
fill = opts.fill || ClipperLib.PolyFillType.pftNonZero,
|
||||||
|
|
@ -478,7 +488,7 @@
|
||||||
// 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),
|
||||||
zed = opt.z || 0;
|
zed = opts.z || 0;
|
||||||
|
|
||||||
// setup offset
|
// setup offset
|
||||||
polys.forEach(function(poly) {
|
polys.forEach(function(poly) {
|
||||||
|
|
@ -494,45 +504,41 @@
|
||||||
polys = fromClipperTree(ctre, zed, null, null, mina);
|
polys = fromClipperTree(ctre, zed, null, null, mina);
|
||||||
|
|
||||||
// if specified, perform offset gap analysis
|
// if specified, perform offset gap analysis
|
||||||
if (opt.gaps && polys.length) {
|
if (opts.gaps && polys.length) {
|
||||||
let oneg = offset(polys, -offs, {
|
let oneg = offset(polys, -offs, {
|
||||||
fill: opt.fill, join: opt.join, type: opt.type, z: opt.z, minArea: mina
|
fill: opts.fill, join: opts.join, type: opts.type, z: opts.z, minArea: mina
|
||||||
});
|
});
|
||||||
let suba = [];
|
let suba = [];
|
||||||
let diff = subtract(orig, oneg, suba, null, zed);
|
let diff = subtract(orig, oneg, suba, null, zed);
|
||||||
opt.gaps.append(suba, opt.flat);
|
opts.gaps.append(suba, opts.flat);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if offset fails, consider last polygons as gap areas
|
// if offset fails, consider last polygons as gap areas
|
||||||
if (opt.gaps && !polys.length) {
|
if (opts.gaps && !polys.length) {
|
||||||
opt.gaps.append(orig, opt.flat);
|
opts.gaps.append(orig, opts.flat);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if specified, perform up to *count* successive offsets
|
// if specified, perform up to *count* successive offsets
|
||||||
if (polys.length) {
|
if (polys.length) {
|
||||||
// ensure opts has offset accumulator array
|
// ensure opts has offset accumulator array
|
||||||
opt.outs = opt.outs || [];
|
opts.outs = opts.outs || [];
|
||||||
// store polys in accumulator
|
// store polys in accumulator
|
||||||
opt.outs.append(polys, opt.flat);
|
opts.outs.append(polys, opts.flat);
|
||||||
// callback for expand() compatibility
|
// callback for expand() compatibility
|
||||||
if (opt.call) {
|
if (opts.call) {
|
||||||
opt.call(polys, count, depth);
|
opts.call(polys, count, depth);
|
||||||
}
|
}
|
||||||
// check for more offsets
|
// check for more offsets
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
// decrement count, increment depth
|
// decrement count, increment depth
|
||||||
opt.count = count - 1;
|
opts.count = count - 1;
|
||||||
opt.depth = depth + 1;
|
opts.depth = depth + 1;
|
||||||
// call next offset
|
// call next offset
|
||||||
offset(polys, dist, opt);
|
offset(polys, dist, opts);
|
||||||
// } else if (count === 0) {
|
|
||||||
// // depth = 0 means offset until failure
|
|
||||||
// opt.depth = depth + 1;
|
|
||||||
// offset(polys, dist, opt);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return opt.flat ? opt.outs : polys;
|
return opts.flat ? opts.outs : polys;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -180,12 +180,19 @@
|
||||||
return rarr;
|
return rarr;
|
||||||
}
|
}
|
||||||
|
|
||||||
let phash = {},
|
let znorm = z.toFixed(5),
|
||||||
|
flatoff = opt.flatoff || 0.007,
|
||||||
|
onflat = this.zFlat[znorm],
|
||||||
|
phash = {},
|
||||||
lines = [],
|
lines = [],
|
||||||
zScale = this.zScale,
|
zScale = this.zScale,
|
||||||
buckets = this.buckets,
|
buckets = this.buckets,
|
||||||
bucket = buckets.length ? buckets[Math.floor(z * zScale)] : points;
|
bucket = buckets.length ? buckets[Math.floor(z * zScale)] : points;
|
||||||
|
|
||||||
|
if (onflat) {
|
||||||
|
z += flatoff;
|
||||||
|
}
|
||||||
|
|
||||||
if (!bucket) {
|
if (!bucket) {
|
||||||
console.log({no_bucket_for_z: z});
|
console.log({no_bucket_for_z: z});
|
||||||
return;
|
return;
|
||||||
|
|
@ -236,7 +243,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt.each) {
|
if (opt.each && (lines.length || opt.emptyok)) {
|
||||||
opt.each(retn, index, total, UTIL.time() - mark);
|
opt.each(retn, index, total, UTIL.time() - mark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,6 @@
|
||||||
|
|
||||||
let out = [];
|
let out = [];
|
||||||
POLY.expand(last, -offset*1.1, 0, out, 1);
|
POLY.expand(last, -offset*1.1, 0, out, 1);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -667,50 +666,31 @@
|
||||||
* @param {Polygon[]} shell enclosing slice tops
|
* @param {Polygon[]} shell enclosing slice tops
|
||||||
* @param {number} diameter of tool (mm)
|
* @param {number} diameter of tool (mm)
|
||||||
* @param {number} stock to leave (mm)
|
* @param {number} stock to leave (mm)
|
||||||
* @param {number} percent overlap on each pass
|
* @param {number} overlap on each pass
|
||||||
* @param {boolean} widecut expand outer cutout
|
|
||||||
* @returns {Object} shell or newly generated shell
|
|
||||||
*/
|
*/
|
||||||
function createRoughPaths(slice, shell, diameter, stock, overlap, holes) {
|
function createRoughPaths(slice, shell, diameter, stock, overlap) {
|
||||||
let tops = slice.gatherTopPolys([]).clone(true),
|
let tops = slice.shadow;
|
||||||
outer = [],
|
let offset = [shell.clone(true),tops.clone(true)].flat();
|
||||||
offset = [];
|
let flat = POLY.flatten(offset, [], true);
|
||||||
|
|
||||||
// when thru holes present, treat them as top offsets
|
offset = POLY.setZ(POLY.nest(flat), slice.z);
|
||||||
// to prevent roughing out the entire hole
|
// slice.tops[0].inner = offset;
|
||||||
if (holes) {
|
|
||||||
tops.appendAll(holes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// clone and flatten the shell with tops to offset array
|
|
||||||
shell.clone(true).forEach(function(poly) {
|
|
||||||
poly.setZ(slice.z).flattenTo(offset);
|
|
||||||
});
|
|
||||||
POLY.flatten(tops, offset, true);
|
|
||||||
|
|
||||||
// only tab cut polys should be open (happens later)
|
|
||||||
offset.forEach(function(trace) {
|
|
||||||
trace.setClosed();
|
|
||||||
});
|
|
||||||
|
|
||||||
// re-nest offset array
|
|
||||||
offset = POLY.nest(offset);
|
|
||||||
|
|
||||||
// inset offset array by 1/2 diameter then by tool overlap %
|
// inset offset array by 1/2 diameter then by tool overlap %
|
||||||
|
slice.tops[0].traces =
|
||||||
POLY.offset(offset, [-(diameter / 2 + stock), -diameter * overlap], {
|
POLY.offset(offset, [-(diameter / 2 + stock), -diameter * overlap], {
|
||||||
z: slice.z,
|
z: slice.z,
|
||||||
count: 999,
|
count: 999,
|
||||||
flat: true,
|
flat: true,
|
||||||
outs: outer,
|
|
||||||
call: (polys, count, depth) => {
|
call: (polys, count, depth) => {
|
||||||
polys.forEach(p => {
|
polys.forEach(p => {
|
||||||
p.depth = depth;
|
p.depth = depth;
|
||||||
if (p.inner) p.inner.forEach(p => p.depth = depth);
|
if (p.inner) {
|
||||||
|
p.inner.forEach(p => p.depth = depth);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
slice.tops[0].traces = outer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -853,7 +833,7 @@
|
||||||
roughToolDiam = getToolDiameter(conf, proc.camRoughTool),
|
roughToolDiam = getToolDiameter(conf, proc.camRoughTool),
|
||||||
outlineToolDiam = getToolDiameter(conf, proc.camOutlineTool),
|
outlineToolDiam = getToolDiameter(conf, proc.camOutlineTool),
|
||||||
contourToolDiam = getToolDiameter(conf, proc.camContourTool),
|
contourToolDiam = getToolDiameter(conf, proc.camContourTool),
|
||||||
procFacing = proc.camRoughOn && proc.camZTopOffset,
|
procFacing = false && proc.camRoughOn && proc.camZTopOffset,
|
||||||
procRough = proc.camRoughOn && proc.camRoughDown && roughToolDiam,
|
procRough = proc.camRoughOn && proc.camRoughDown && roughToolDiam,
|
||||||
procOutlineIn = proc.camOutlineIn,
|
procOutlineIn = proc.camOutlineIn,
|
||||||
procOutlineOut = proc.camOutlineOut,
|
procOutlineOut = proc.camOutlineOut,
|
||||||
|
|
@ -1002,7 +982,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
// set all default shells
|
// set all default shells
|
||||||
const camShellPolys = shellRough = shellOutline = facePolys = camShell.gatherTopPolys([]);
|
const camShellPolys = shellOutline = facePolys = camShell.gatherTopPolys([]);
|
||||||
|
|
||||||
if (procDrillReg) {
|
if (procDrillReg) {
|
||||||
sliceDrillReg(settings, widget, sliceAll, zThru);
|
sliceDrillReg(settings, widget, sliceAll, zThru);
|
||||||
|
|
@ -1012,11 +992,6 @@
|
||||||
sliceDrill(settings, widget, slices, sliceAll);
|
sliceDrill(settings, widget, slices, sliceAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (procRough) {
|
|
||||||
// expand shell by half tool diameter + stock to leave
|
|
||||||
shellRough = facePolys = POLY.expand(shellRough, (roughToolDiam / 2) + camRoughStock, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (procOutline) {
|
if (procOutline) {
|
||||||
if (!procOutlineOut) {
|
if (!procOutlineOut) {
|
||||||
// expand shell minimally triggering a clean
|
// expand shell minimally triggering a clean
|
||||||
|
|
@ -1048,18 +1023,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (procRough) {
|
|
||||||
let selected = [];
|
|
||||||
selectSlices(slices, camRoughDown * units, CPRO.ROUGH, selected);
|
|
||||||
if (zThru) {
|
|
||||||
addZThru(selected);
|
|
||||||
}
|
|
||||||
sliceAll.appendAll(selected);
|
|
||||||
if (!proc.camRoughVoid) {
|
|
||||||
thruHoles = holes(slices, roughToolDiam + camRoughStock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (procOutline) {
|
if (procOutline) {
|
||||||
let selected = [];
|
let selected = [];
|
||||||
selectSlices(slices, proc.camOutlineDown * units, CPRO.OUTLINE, selected);
|
selectSlices(slices, proc.camOutlineDown * units, CPRO.OUTLINE, selected);
|
||||||
|
|
@ -1069,32 +1032,62 @@
|
||||||
sliceAll.appendAll(selected);
|
sliceAll.appendAll(selected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
let slicer = new KIRI.slicer2(widget.getPoints(), {
|
let slicer = new KIRI.slicer2(widget.getPoints(), {
|
||||||
zlist: true,
|
zlist: true,
|
||||||
zline: true,
|
zline: true
|
||||||
genso: true // generate slice object
|
|
||||||
});
|
});
|
||||||
let zindex = slicer.interval(1, { down: true });
|
let tshadow = [];
|
||||||
let terrain = slicer.slice(zindex, { each: (data, index, total) => {
|
let tzindex = slicer.interval(1); // bottom up
|
||||||
|
let terrain = slicer.slice(tzindex, { each: (data, index, total) => {
|
||||||
console.log('terrain', index, total, data);
|
console.log('terrain', index, total, data);
|
||||||
|
tshadow = POLY.union(tshadow.appendAll(data.tops), 0.01, true);
|
||||||
} });
|
} });
|
||||||
let shadow = [];
|
console.log({slicer, tzindex, tshadow, terrain});
|
||||||
// shadow generation bottom-up is faster
|
|
||||||
terrain.reverse().forEach(data => {
|
// do creating roughing slices
|
||||||
if (data.tops) {
|
if (procRough) {
|
||||||
shadow = POLY.union(shadow.appendAll(data.tops));
|
// identify through holes
|
||||||
}
|
thruHoles = tshadow.map(p => p.inner || []).flat();
|
||||||
});
|
|
||||||
console.log({slicer, zindex, shadow, terrain});
|
let shadow = [];
|
||||||
|
let slices = [];
|
||||||
|
slicer.slice(slicer.interval(roughDown, { down: true }), { each: (data, index, total) => {
|
||||||
|
shadow = POLY.union(shadow.appendAll(data.tops), 0.01, true);
|
||||||
|
data.shadow = shadow.clone(true);
|
||||||
|
data.slice.camMode = CPRO.ROUGH;
|
||||||
|
data.slice.shadow = data.shadow;
|
||||||
|
slices.push(data.slice);
|
||||||
|
}, genso: true });
|
||||||
|
|
||||||
|
// inset or eliminate thru holes from shadow
|
||||||
|
shadow = POLY.flatten(shadow.clone(true), [], true);
|
||||||
|
thruHoles.forEach(hole => {
|
||||||
|
shadow = shadow.map(p => {
|
||||||
|
if (p.isEquivalent(hole)) {
|
||||||
|
// eliminate thru holes when roughing voids enabled
|
||||||
|
if (proc.camRoughVoid) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
let po = POLY.offset([p], -(roughToolDiam + camRoughStock));
|
||||||
|
return po ? po[0] : undefined;
|
||||||
|
} else {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}).filter(p => p);
|
||||||
|
});
|
||||||
|
shadow = POLY.nest(shadow);
|
||||||
|
|
||||||
|
// expand shadow by half tool diameter + stock to leave
|
||||||
|
shellRough = POLY.offset(shadow, (roughToolDiam / 4) + camRoughStock);
|
||||||
|
|
||||||
|
if (zThru) {
|
||||||
|
addZThru(slices);
|
||||||
|
}
|
||||||
|
|
||||||
|
sliceAll.appendAll(slices);
|
||||||
|
}
|
||||||
|
|
||||||
// do roughing slices
|
|
||||||
if (procRough)
|
|
||||||
slicer.slice(slicer.interval(roughDown, { down: true }), { each: (data, index, total) => {
|
|
||||||
// annotate slice
|
|
||||||
console.log('rough', data.z, data);
|
|
||||||
} });
|
|
||||||
*/
|
|
||||||
// horizontal slices for rough/outline
|
// horizontal slices for rough/outline
|
||||||
doSlicing(widget, {height:sliceDepth, cam:true, zmin:zBottom, noEmpty:true}, camSlicesDone, function(update) {
|
doSlicing(widget, {height:sliceDepth, cam:true, zmin:zBottom, noEmpty:true}, camSlicesDone, function(update) {
|
||||||
onupdate(0.0 + update * 0.25, "slicing");
|
onupdate(0.0 + update * 0.25, "slicing");
|
||||||
|
|
@ -1103,15 +1096,15 @@
|
||||||
// we need topo for safe travel moves when roughing and outlining
|
// we need topo for safe travel moves when roughing and outlining
|
||||||
// not generated when drilling-only. then all z moves use bounds max.
|
// not generated when drilling-only. then all z moves use bounds max.
|
||||||
// also generates x and y contouring when selected
|
// also generates x and y contouring when selected
|
||||||
if (procRough || procOutline || procContour)
|
// if (procRough || procOutline || procContour)
|
||||||
generateTopoMap(widget, settings, function(slices) {
|
// generateTopoMap(widget, settings, function(slices) {
|
||||||
sliceAll.appendAll(slices);
|
// sliceAll.appendAll(slices);
|
||||||
// todo union rough / outline shells
|
// // todo union rough / outline shells
|
||||||
// todo union rough / outline tabs
|
// // todo union rough / outline tabs
|
||||||
// todo append to generated topo map
|
// // todo append to generated topo map
|
||||||
}, function(update, msg) {
|
// }, function(update, msg) {
|
||||||
onupdate(0.40 + update * 0.50, msg || "create topo");
|
// onupdate(0.40 + update * 0.50, msg || "create topo");
|
||||||
});
|
// });
|
||||||
|
|
||||||
// prepare for tracing paths
|
// prepare for tracing paths
|
||||||
let traceTool;
|
let traceTool;
|
||||||
|
|
@ -1253,7 +1246,8 @@
|
||||||
|
|
||||||
tops.forEach(function(top) {
|
tops.forEach(function(top) {
|
||||||
outline.poly(top.poly, 0x999900, true, open);
|
outline.poly(top.poly, 0x999900, true, open);
|
||||||
if (top.inner) outline.poly(top.inner, 0xdddddd, true);
|
// if (top.inner) outline.poly(top.inner, 0xdddddd, true);
|
||||||
|
if (top.inner) outline.poly(top.inner, 0xff0000, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// various outlining
|
// various outlining
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue