remove roughing operations outside part boundaries. outline uses in/out activation. wide cutout and tabs tied to outline out.

This commit is contained in:
Stewart Allen 2020-11-03 09:30:00 -05:00
commit c5a8bf42f6
8 changed files with 74 additions and 139 deletions

View file

@ -53,6 +53,7 @@
* `B` outside cutting direction in roughing mode inverted
* `B` widen outside cuts to prevent chatter on deep (metal) features
* `F` provide planar or other visual hint of current z bottom offset
* `F` redo collision code use fixed slices and path/poly intersection instead of a topo map
* `F` z bounded slices (extension of z bottom offset feature)
* `F` z planar settings visualizations
* `F` use arcs to connect hard angles

View file

@ -1,6 +1,6 @@
{
"name": "grid-apps",
"version": "3.0.0-dev",
"version": "2.3.D0",
"description": "grid.space 3d slice & modeling tools",
"author": "Stewart Allen <sa@grid.space>",
"license": "MIT",

View file

@ -448,39 +448,6 @@
return offset(polys, [distance, distance2 || distance], {
z, outs: out, call: collector, minArea: min, count, flat: true
});
// prepare alignments for clipper lib
alignWindings(polys);
polys.forEach(function(poly) {
if (poly.inner) setWinding(poly.inner, !poly.isClockwise());
});
let fact = CONF.clipper,
clib = self.ClipperLib,
clip = clib.Clipper,
cpft = clib.PolyFillType,
cjnt = clib.JoinType,
cety = clib.EndType,
coff = new clib.ClipperOffset(),
ctre = new clib.PolyTree(),
circ = sumCirc(polys);
polys.forEach(function(poly) {
let clean = clip.CleanPolygons(poly.toClipper(), CONF.clipperClean);
let simple = clip.SimplifyPolygons(clean, cpft.pftNonZero);
coff.AddPaths(simple, cjnt.jtMiter, cety.etClosedPolygon);
});
coff.Execute(ctre, distance * fact);
polys = fromClipperTree(ctre, z, null, null, min);
if (out) out.appendAll(polys);
if (collector) collector(polys, count);
if ((count === 0 || count > 1) && polys.length > 0) {
expand(polys, distance2 || distance, z, out, count > 0 ? count-1 : 0, distance2, collector);
}
return polys;
}
/**

View file

@ -209,7 +209,8 @@
finishingOver: "camContourOver",
finishingSpeed: "camOutlineSpeed",
finishingPlunge: "camOutlinePlunge",
finishingOn: "camOutlineOn",
camOutlineOn: "camOutlineOut",
finishingOn: "camOutlineOut",
finishingXOn: "camContourXOn",
finishingYOn: "camContourYOn",
drillTool: "camDrillTool",
@ -219,8 +220,8 @@
drillDwell: "camDrillDwell",
drillLift: "camDrillLift",
drillingOn: "camDrillingOn",
camPocketOnlyRough: "camRoughPocket",
camPocketOnlyFinish: "camOutlinePocket",
camWideCutout: "camOutlineWide",
outputClockwise: "camConventional"
};
@ -396,49 +397,48 @@
// process defaults CAM:Process
p:{
processName: "default",
camRoughTool: 1000, // roughingTool
camRoughSpindle: 1000, // camRoughSpindle
camRoughDown: 2, // roughingDown
camRoughOver: 0.5, // roughingOver
camRoughSpeed: 1000, // roughingSpeed
camRoughPlunge: 250, // roughingPlunge
camRoughStock: 0, // roughingStock
camRoughVoid: false, // roughingPocket
camRoughOn: true, // roughingOn
camOutlineTool: 1000, // finishingTool
camOutlineSpindle: 1000,// finishingSpindle
camOutlineDown: 3, // finishingDown
camOutlineSpeed: 800, // finishingSpeed
camOutlinePlunge: 250, // finishingPlunge
camOutlineOn: true, // finishingOn
camRoughTool: 1000,
camRoughSpindle: 1000,
camRoughDown: 2,
camRoughOver: 0.5,
camRoughSpeed: 1000,
camRoughPlunge: 250,
camRoughStock: 0,
camRoughVoid: false,
camRoughOn: true,
camOutlineTool: 1000,
camOutlineSpindle: 1000,
camOutlineDown: 3,
camOutlineSpeed: 800,
camOutlinePlunge: 250,
camOutlineWide: false,
camOutlineIn: false,
camOutlineOut: false,
camContourTool: 1000,
camContourSpindle: 1000,// (new)
camContourOver: 0.5, // (new)
camContourSpeed: 1000, // (new)
camContourAngle: 85, // finishingAngle
camContourCurves: false,// finishCurvesOnly
camContourXOn: true, // finishingXOn
camContourYOn: true, // finishingYOn
camContourSpindle: 1000,
camContourOver: 0.5,
camContourSpeed: 1000,
camContourAngle: 85,
camContourCurves: false,
camContourXOn: true,
camContourYOn: true,
camTraceTool: 1000,
camTraceSpeed: 250,
camTraceOn: false,
camDrillTool: 1000, // drillTool
camDrillSpindle: 1000, // drillSpindle
camDrillDownSpeed: 250, // drillDownSpeed
camDrillDown: 5, // drillDown
camDrillDwell: 250, // drillDwell
camDrillLift: 2, // drillLift
camDrillingOn: false, // drillingOn
camDrillTool: 1000,
camDrillSpindle: 1000,
camDrillDownSpeed: 250,
camDrillDown: 5,
camDrillDwell: 250,
camDrillLift: 2,
camDrillingOn: false,
camDrillReg: "none",
camTabsAngle: 0,
camTabsCount: 4,
camTabsWidth: 5,
camTabsHeight: 5,
camTabsOn: false,
camRoughPocket: false, // camPocketOnlyRough
camOutlinePocket: false, // camPocketOnlyFinish
camDepthFirst: false,
camWideCutout: false,
camEaseDown: false,
camOriginTop: true,
camZTopOffset: 0,

View file

@ -1404,7 +1404,6 @@
camRoughPlunge: UC.newInput(LANG.cc_plng_s, {title:LANG.cc_plng_l, convert:UC.toInt, modes:CAM}),
camRoughStock: UC.newInput(LANG.cr_lsto_s, {title:LANG.cr_lsto_l, convert:UC.toFloat, modes:CAM}),
camRoughVoid: UC.newBoolean(LANG.cr_clrp_s, onBooleanClick, {title:LANG.cr_clrp_l, modes:CAM}),
camRoughPocket: UC.newBoolean(LANG.cc_pock_s, onBooleanClick, {title:LANG.cc_pock_l, modes:CAM}),
camRoughSep: UC.newBlank({class:"pop-sep"}),
camRoughOn: UC.newBoolean(LANG.enable, onBooleanClick, {modes:CAM}),
camOutline: UC.newGroup(LANG.co_menu, null, {modes:CAM, marker:true}),
@ -1413,9 +1412,10 @@
camOutlineDown: UC.newInput(LANG.cc_sdwn_s, {title:LANG.cc_sdwn_l, convert:UC.toFloat, modes:CAM}),
camOutlineSpeed: UC.newInput(LANG.cc_feed_s, {title:LANG.cc_feed_l, convert:UC.toInt, modes:CAM}),
camOutlinePlunge: UC.newInput(LANG.cc_plng_s, {title:LANG.cc_plng_l, convert:UC.toInt, modes:CAM}),
camOutlinePocket: UC.newBoolean(LANG.cc_pock_s, onBooleanClick, {title:LANG.cc_pock_l, modes:CAM}),
camOutlineWide: UC.newBoolean(LANG.co_wide_s, onBooleanClick, {title:LANG.co_wide_l, modes:CAM, show:() => { return UI.camOutlineOut.checked }}),
camOutlineSep: UC.newBlank({class:"pop-sep"}),
camOutlineOn: UC.newBoolean(LANG.cf_watr_s, onBooleanClick, {title:LANG.cf_watr_l, modes:CAM}),
camOutlineIn: UC.newBoolean(LANG.co_olin_s, onBooleanClick, {title:LANG.co_olin_l, modes:CAM}),
camOutlineOut: UC.newBoolean(LANG.co_olot_s, onBooleanClick, {title:LANG.co_olot_l, modes:CAM}),
camContour: UC.newGroup(LANG.cn_menu, null, {modes:CAM, marker:true}),
camContourTool: UC.newSelect(LANG.cc_tool, {modes:CAM}),
camContourSpindle: UC.newInput(LANG.cc_spnd_s, {title:LANG.cc_spnd_l, convert:UC.toInt, modes:CAM, visible:spindleShow}),
@ -1442,7 +1442,7 @@
camDrillingOn: UC.newBoolean(LANG.enable, onBooleanClick, {modes:CAM}),
camDrillSep: UC.newBlank({class:"pop-sep"}),
camDrillReg: UC.newSelect(LANG.cd_regi_s, {modes:CAM, title:LANG.cd_regi_l}, "drillreg"),
camTabs: UC.newGroup(LANG.ct_menu, null, {modes:CAM, marker:true}),
camTabs: UC.newGroup(LANG.ct_menu, null, {modes:CAM, marker:true, show:() => { return UI.camOutlineOut.checked }}),
camTabsAngle: UC.newInput(LANG.ct_angl_s, {title:LANG.ct_angl_l, convert:UC.toInt, bound:UC.bound(0,360), modes:CAM}),
camTabsCount: UC.newInput(LANG.ct_numb_s, {title:LANG.ct_numb_l, convert:UC.toInt, bound:UC.bound(1,20), modes:CAM}),
camTabsWidth: UC.newInput(LANG.ct_wdth_s, {title:LANG.ct_wdth_l, convert:UC.toFloat, bound:UC.bound(0.1,100), modes:CAM}),
@ -1493,7 +1493,6 @@
camConventional: UC.newBoolean(LANG.ou_conv_s, onBooleanClick, {title:LANG.ou_conv_l, modes:CAM}),
camEaseDown: UC.newBoolean(LANG.cr_ease_s, onBooleanClick, {title:LANG.cr_ease_l, modes:CAM}),
camDepthFirst: UC.newBoolean(LANG.ou_depf_s, onBooleanClick, {title:LANG.ou_depf_l, modes:CAM}),
camWideCutout: UC.newBoolean(LANG.ou_wide_s, onBooleanClick, {title:LANG.ou_wide_l, modes:CAM}),
outputOriginBounds: UC.newBoolean(LANG.or_bnds_s, onBooleanClick, {title:LANG.or_bnds_l, modes:LASER}),
outputOriginCenter: UC.newBoolean(LANG.or_cntr_s, onBooleanClick, {title:LANG.or_cntr_l, modes:CAM_LASER}),
camOriginTop: UC.newBoolean(LANG.or_topp_s, onBooleanClick, {title:LANG.or_topp_l, modes:CAM}),

View file

@ -3,7 +3,7 @@
let terms = {
COPYRIGHT: "Copyright (C) Stewart Allen <sa@grid.space> - All Rights Reserved",
LICENSE: "See the license.md file included with the source distribution",
VERSION: "3.0.0-dev"
VERSION: "2.3.D0"
};
if (typeof(module) === 'object') {

View file

@ -60,7 +60,8 @@
api.ui.camRough.marker.style.display = proc.camRoughOn ? 'flex' : 'none';
api.ui.camDrill.marker.style.display =
proc.camDrillingOn || proc.camDrillReg !== 'none' ? 'flex' : 'none';
api.ui.camOutline.marker.style.display = proc.camOutlineOn ? 'flex' : 'none';
api.ui.camOutline.marker.style.display =
proc.camOutlineIn || proc.camOutlineOut ? 'flex' : 'none';
api.ui.camContour.marker.style.display =
proc.camContourXOn || proc.camContourYOn ? 'flex' : 'none';
});
@ -638,11 +639,10 @@
* @param {Polygon[]} shell enclosing slice tops
* @param {number} diameter of tool
* @param {number} overlap % on each pass
* @param {boolean} true for pocket only mode
* @param {number} bounds shell offset
* @returns {Object} shell
*/
function createLevelPaths(slice, shell, diameter, overlap, pocket) {
function createLevelPaths(slice, shell, diameter, overlap) {
let outer = [],
offset = [];
@ -655,15 +655,6 @@
// inset offset array by 1/2 diameter then by tool overlap %
POLY.expand(offset, - (diameter / 2), slice.z, outer, 0, -diameter * overlap);
if (!pocket) {
// re-flatten offset polys
offset = POLY.flatten(outer.slice(), []);
// re-clone shell to offset polys (because it was lost in the offset)
shell.clone(true).forEach(function(poly) { poly.setZ(slice.z).flattenTo(offset) });
// re-nest offset polys
outer = POLY.nest(offset);
}
if (!slice.tops.length) { console.log({no_top: slice.z}); slice.addTop() }
slice.tops[0].traces = outer;
@ -677,11 +668,10 @@
* @param {number} diameter of tool (mm)
* @param {number} stock to leave (mm)
* @param {number} percent overlap on each pass
* @param {boolean} true for pocket only mode
* @param {boolean} widecut expand outer cutout
* @returns {Object} shell or newly generated shell
*/
function createRoughPaths(slice, shell, diameter, stock, overlap, pocket, holes, widecut) {
function createRoughPaths(slice, shell, diameter, stock, overlap, holes) {
let tops = slice.gatherTopPolys([]).clone(true),
outer = [],
offset = [];
@ -720,24 +710,6 @@
}
});
if (!pocket) {
// re-flatten offset polys
offset = POLY.flatten(outer.slice(), [], true);
// re-clone shell to offset polys (because it was lost in the offset)
shell.clone(true).forEach(function(poly) {
poly.setZ(slice.z).flattenTo(offset);
poly.setClosed();
poly.depth = 0;
});
// re-nest offset polys
// outer = POLY.nest(offset);
outer = offset;
// add another wider pass to the cutout polygon(s)
if (widecut) {
POLY.expand(offset, diameter * overlap, slice.z, outer, 1);
}
}
slice.tops[0].traces = outer;
};
@ -820,20 +792,23 @@
* @param {Slice} slice target
* @param {Polygon[]} outermost pancacked shells for fill
* @param {number} tool diameter
* @param {boolean} pocket only
* @param {boolean} widecut expand outer cutout
* @param {boolean} inside emit polys inside part boundaries
* @param {boolean} outside emit polys outside part boundaries
* @param {boolean} wide expand outside poly cuts
*/
function createOutlinePaths(slice, shell, diameter, pocket, widecut) {
function createOutlinePaths(slice, shell, diameter, inside, outside, wide) {
if (slice.tops.length === 0) return shell;
let tops = slice.gatherTopPolys([]).clone(true),
offset = POLY.expand(tops, diameter / 2, slice.z);
let tops = slice.gatherTopPolys([]).clone(inside);
let offset = POLY.expand(tops, diameter / 2, slice.z);
// when pocket only, drop first outer poly
// if it matches the shell and promote inner polys
if (pocket) {
if (!outside) {
offset = POLY.filter(POLY.diff(shell, offset, slice.z), [], function(poly) {
if (poly.area() < 1) return null;
if (poly.area() < 1) {
return null;
}
for (let sp=0; sp<shell.length; sp++) {
// eliminate shell only polys
if (poly.isEquivalent(shell[sp])) {
@ -843,7 +818,7 @@
}
return poly;
});
} else if (widecut) {
} else if (wide) {
offset.slice().forEach(op => {
POLY.expand([op], diameter * 0.5, slice.z, offset, 1);
});
@ -880,7 +855,10 @@
contourToolDiam = getToolDiameter(conf, proc.camContourTool),
procFacing = proc.camRoughOn && proc.camZTopOffset,
procRough = proc.camRoughOn && proc.camRoughDown && roughToolDiam,
procOutline = proc.camOutlineOn && proc.camOutlineDown && outlineToolDiam,
procOutlineIn = proc.camOutlineIn,
procOutlineOut = proc.camOutlineOut,
procOutlineWide = proc.camOutlineWide,
procOutline = (procOutlineIn || procOutlineOut) && proc.camOutlineDown && outlineToolDiam,
procContourX = proc.camContourXOn && proc.camOutlinePlunge && contourToolDiam,
procContourY = proc.camContourYOn && proc.camOutlinePlunge && contourToolDiam,
procContour = procContourX || procContourY,
@ -890,10 +868,7 @@
roughDown = procRough ? proc.camRoughDown : Infinity,
outlineDown = procOutline ? proc.camOutlineDown : Infinity,
sliceDepth = Math.max(0.1, Math.min(roughDown, outlineDown) / 3 * units),
pocketOnlyRough = proc.camRoughPocket,
pocketOnlyOutline = proc.camOutlinePocket,
addTabsRough = procRough && proc.camTabsOn && !pocketOnlyRough,
addTabsOutline = procOutline && proc.camTabsOn && !pocketOnlyOutline,
addTabsOutline = procOutlineOut && proc.camTabsOn,
tabWidth = proc.camTabsWidth * units,
tabHeight = proc.camTabsHeight * units,
bounds = widget.getBoundingBox(),
@ -907,8 +882,7 @@
shellRough,
shellOutline,
facePolys,
thruHoles,
wideCut = proc.camWideCutout;
thruHoles;
if (settings.stock.x + 0.00001 < bounds.max.x - bounds.min.x) {
return ondone('stock X too small for part. disable stock or use offset stock');
@ -1039,17 +1013,12 @@
}
if (procRough) {
if (pocketOnlyRough) {
// expand shell minimally triggering a clean
shellRough = POLY.expand(shellRough, 0.01, 0);
} else {
// expand shell by half tool diameter + stock to leave
shellRough = facePolys = POLY.expand(shellRough, (roughToolDiam / 2) + camRoughStock, 0);
}
// expand shell by half tool diameter + stock to leave
shellRough = facePolys = POLY.expand(shellRough, (roughToolDiam / 2) + camRoughStock, 0);
}
if (procOutline) {
if (pocketOnlyOutline) {
if (!procOutlineOut) {
// expand shell minimally triggering a clean
shellOutline = POLY.expand(shellOutline, -outlineToolDiam / 2, 0);
} else {
@ -1136,14 +1105,13 @@
let modekey = MODES[slice.camMode] || "?mode?";
switch (slice.camMode) {
case CPRO.LEVEL:
createLevelPaths(slice, facePolys, roughToolDiam, proc.camRoughOver, pocketOnlyRough);
createLevelPaths(slice, facePolys, roughToolDiam, proc.camRoughOver);
break;
case CPRO.ROUGH:
createRoughPaths(slice, shellRough, roughToolDiam, camRoughStock, proc.camRoughOver, pocketOnlyRough, thruHoles, wideCut);
if (addTabsRough) addCutoutTabs(slice, roughToolDiam);
createRoughPaths(slice, shellRough, roughToolDiam, camRoughStock, proc.camRoughOver, thruHoles);
break;
case CPRO.OUTLINE:
createOutlinePaths(slice, shellOutline, outlineToolDiam, pocketOnlyOutline, wideCut);
createOutlinePaths(slice, shellOutline, outlineToolDiam, procOutlineIn, procOutlineOut, procOutlineWide);
if (addTabsOutline) addCutoutTabs(slice, outlineToolDiam);
if (traceToolProfile) findTracingPaths(widget, slice, traceTool, traceToolProfile);
break;

View file

@ -248,8 +248,6 @@ kiri.lang['en-us'] = {
cc_feed_l: "max cutting speed in\nworkspace units / minute",
cc_plng_s: "plunge rate",
cc_plng_l: "max z axis speed in\nworkspace units / minute",
cc_pock_s: "inside only",
cc_pock_l: "constrain cuts to\npart boundaries",
// CNC COMMON
cc_menu: "limits",
@ -269,8 +267,12 @@ kiri.lang['en-us'] = {
// CNC OUTLINE
co_menu: "outline",
cf_watr_s: "enable",
cf_watr_l: "enable contour finishing\ndisabled when pocketing",
co_wide_s: "wide cutout",
co_wide_l: "widen outside cutout paths\nfor deep cuts in hard material",
co_olin_s: "inside",
co_olin_l: "enable outline cutting\ninside part boundaries",
co_olot_s: "outside",
co_olot_l: "enabled outline cutting\noutside part boundaries",
// CNC CONTOUR
cn_menu: "contour",
@ -383,8 +385,6 @@ kiri.lang['en-us'] = {
ou_conv_l: "milling direction\nuncheck for 'climb'",
ou_depf_s: "depth first",
ou_depf_l: "optimize pocket cuts\nwith depth priority",
ou_wide_s: "wide cutout",
ou_wide_l: "widen outside cutout paths\nfor deep cuts in hard material",
// CAM STOCK
cs_menu: "stock",