refactor cam around discrete process model with visual order control
This commit is contained in:
parent
ed23560355
commit
6973d51fd2
19 changed files with 1429 additions and 720 deletions
1
app.js
1
app.js
|
|
@ -328,6 +328,7 @@ const script = {
|
||||||
"mode/sla/slice",
|
"mode/sla/slice",
|
||||||
"mode/sla/export",
|
"mode/sla/export",
|
||||||
"mode/cam/driver",
|
"mode/cam/driver",
|
||||||
|
"mode/cam/ops",
|
||||||
"mode/cam/tool",
|
"mode/cam/tool",
|
||||||
"mode/cam/topo",
|
"mode/cam/topo",
|
||||||
"mode/cam/slice",
|
"mode/cam/slice",
|
||||||
|
|
|
||||||
4
notes.md
4
notes.md
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
## 2.5 plan
|
## 2.5 plan
|
||||||
# `cnc`
|
# `cnc`
|
||||||
# - complex process order
|
|
||||||
# - split gcode output
|
# - split gcode output
|
||||||
# - double-sided assistance
|
|
||||||
# - redo path planning
|
# - redo path planning
|
||||||
|
# - double-sided assistance
|
||||||
# - more tracing types (in, out, clear, pocket)
|
# - more tracing types (in, out, clear, pocket)
|
||||||
# - trace re-ordering
|
# - trace re-ordering
|
||||||
# `fdm`
|
# `fdm`
|
||||||
# - bind process to z ranges or boxed regions
|
# - bind process to z ranges or boxed regions
|
||||||
# - non-planar actual
|
# - non-planar actual
|
||||||
|
# - layer start point control
|
||||||
# `sla`
|
# `sla`
|
||||||
# - common support area detection from new fdm code
|
# - common support area detection from new fdm code
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "grid-apps",
|
"name": "grid-apps",
|
||||||
"version": "2.5.3D",
|
"version": "2.5.4D",
|
||||||
"description": "grid.space 3d slice & modeling tools",
|
"description": "grid.space 3d slice & modeling tools",
|
||||||
"author": "Stewart Allen <sa@grid.space>",
|
"author": "Stewart Allen <sa@grid.space>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
if (!self.kiri) {
|
if (!self.kiri) {
|
||||||
self.kiri = {
|
self.kiri = {
|
||||||
beta: 2503,
|
beta: 2504,
|
||||||
driver: {}, // driver modules
|
driver: {}, // driver modules
|
||||||
loader: [] // module loading: array of functions
|
loader: [] // module loading: array of functions
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -450,7 +450,7 @@
|
||||||
camDrillDwell: 250,
|
camDrillDwell: 250,
|
||||||
camDrillLift: 2,
|
camDrillLift: 2,
|
||||||
camDrillingOn: false,
|
camDrillingOn: false,
|
||||||
camDrillReg: "none",
|
// camDrillReg: "none",
|
||||||
camTabsWidth: 5,
|
camTabsWidth: 5,
|
||||||
camTabsHeight: 5,
|
camTabsHeight: 5,
|
||||||
camTabsDepth: 5,
|
camTabsDepth: 5,
|
||||||
|
|
@ -472,7 +472,8 @@
|
||||||
camConventional: false, // outputClockwise
|
camConventional: false, // outputClockwise
|
||||||
outputOriginCenter: false,
|
outputOriginCenter: false,
|
||||||
outputInvertX: false,
|
outputInvertX: false,
|
||||||
outputInvertY: false
|
outputInvertY: false,
|
||||||
|
ops: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
laser: {
|
laser: {
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,9 @@
|
||||||
gcode.push(line);
|
gcode.push(line);
|
||||||
}, (output) => {
|
}, (output) => {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(gcode.join('\n'), output);
|
callback(gcode.join('\r\n'), output);
|
||||||
} else {
|
} else {
|
||||||
exportGCodeDialog(gcode.join('\n'), output);
|
exportGCodeDialog(gcode.join('\r\n'), output);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
116
src/kiri/init.js
116
src/kiri/init.js
|
|
@ -1376,25 +1376,25 @@
|
||||||
SPACE.useDefaultKeys(API.feature.on_key === undefined || API.feature.on_key_defaults);
|
SPACE.useDefaultKeys(API.feature.on_key === undefined || API.feature.on_key_defaults);
|
||||||
|
|
||||||
Object.assign(UI, {
|
Object.assign(UI, {
|
||||||
|
tracker: tracker,
|
||||||
|
container: container,
|
||||||
|
|
||||||
alert: {
|
alert: {
|
||||||
dialog: $('alert-area'),
|
dialog: $('alert-area'),
|
||||||
text: $('alert-text')
|
text: $('alert-text')
|
||||||
},
|
},
|
||||||
|
|
||||||
func: {
|
func: {
|
||||||
slice: $('act-slice'),
|
slice: $('act-slice'),
|
||||||
preview: $('act-preview'),
|
preview: $('act-preview'),
|
||||||
animate: $('act-animate'),
|
animate: $('act-animate'),
|
||||||
export: $('act-export')
|
export: $('act-export')
|
||||||
},
|
},
|
||||||
|
|
||||||
label: {
|
label: {
|
||||||
slice: $('label-slice'),
|
slice: $('label-slice'),
|
||||||
preview: $('label-preview'),
|
preview: $('label-preview'),
|
||||||
animate: $('label-animate'),
|
animate: $('label-animate'),
|
||||||
export: $('label-export'),
|
export: $('label-export'),
|
||||||
},
|
},
|
||||||
|
|
||||||
acct: {
|
acct: {
|
||||||
help: $('acct-help'),
|
help: $('acct-help'),
|
||||||
export: $('acct-export')
|
export: $('acct-export')
|
||||||
|
|
@ -1406,7 +1406,6 @@
|
||||||
speedbar: $('speedbar'),
|
speedbar: $('speedbar'),
|
||||||
context: $('context-menu'),
|
context: $('context-menu'),
|
||||||
|
|
||||||
container: container,
|
|
||||||
back: $('lt-back'),
|
back: $('lt-back'),
|
||||||
trash: $('lt-trash'),
|
trash: $('lt-trash'),
|
||||||
rotate: $('lt-rotate'),
|
rotate: $('lt-rotate'),
|
||||||
|
|
@ -1618,7 +1617,7 @@
|
||||||
sliceSupportAngle: UC.newInput(LANG.sp_angl_s, {title:LANG.sp_angl_l, bound:UC.bound(0.0,90.0), convert:UC.toFloat, modes:FDM}),
|
sliceSupportAngle: UC.newInput(LANG.sp_angl_s, {title:LANG.sp_angl_l, bound:UC.bound(0.0,90.0), convert:UC.toFloat, modes:FDM}),
|
||||||
|
|
||||||
sliceSupportGen: UC.newRow([
|
sliceSupportGen: UC.newRow([
|
||||||
UI.ssaGen = UC.newButton('detect', onButtonClick, {class: "f-col grow a-center"})
|
UI.ssaGen = UC.newButton(LANG.sp_detect, onButtonClick, {class: "f-col grow a-center"})
|
||||||
], { modes: FDM, class: "ext-buttons f-row grow" }),
|
], { modes: FDM, class: "ext-buttons f-row grow" }),
|
||||||
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
||||||
sliceSupportManual: UC.newRow([
|
sliceSupportManual: UC.newRow([
|
||||||
|
|
@ -1627,84 +1626,123 @@
|
||||||
(UI.ssmClr = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
|
(UI.ssmClr = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
|
||||||
], {modes:FDM, class:"ext-buttons f-row"}),
|
], {modes:FDM, class:"ext-buttons f-row"}),
|
||||||
|
|
||||||
camRough: UC.newGroup(LANG.cr_menu, null, {modes:CAM, marker:true, top:true}),
|
camRough: UC.newGroup(LANG.cr_menu, null, {modes:CAM, top:true}),
|
||||||
camRoughTool: UC.newSelect(LANG.cc_tool, {modes:CAM}),
|
camRoughTool: UC.newSelect(LANG.cc_tool, {modes:CAM}),
|
||||||
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camRoughSpindle: UC.newInput(LANG.cc_spnd_s, {title:LANG.cc_spnd_l, convert:UC.toInt, modes:CAM, visible:spindleShow}),
|
camRoughSpindle: UC.newInput(LANG.cc_spnd_s, {title:LANG.cc_spnd_l, convert:UC.toInt, modes:CAM, visible:spindleShow}),
|
||||||
camRoughOver: UC.newInput(LANG.cc_sovr_s, {title:LANG.cc_sovr_l, convert:UC.toFloat, bound:UC.bound(0.01,1.0), modes:CAM}),
|
camRoughOver: UC.newInput(LANG.cc_sovr_s, {title:LANG.cc_sovr_l, convert:UC.toFloat, bound:UC.bound(0.01,1.0), modes:CAM}),
|
||||||
camRoughDown: UC.newInput(LANG.cc_sdwn_s, {title:LANG.cc_sdwn_l, convert:UC.toFloat, modes:CAM, units:true}),
|
camRoughDown: UC.newInput(LANG.cc_sdwn_s, {title:LANG.cc_sdwn_l, convert:UC.toFloat, modes:CAM, units:true}),
|
||||||
camRoughSpeed: UC.newInput(LANG.cc_feed_s, {title:LANG.cc_feed_l, convert:UC.toInt, modes:CAM, units:true}),
|
camRoughSpeed: UC.newInput(LANG.cc_feed_s, {title:LANG.cc_feed_l, convert:UC.toInt, modes:CAM, units:true}),
|
||||||
camRoughPlunge: UC.newInput(LANG.cc_plng_s, {title:LANG.cc_plng_l, convert:UC.toInt, modes:CAM, units:true}),
|
camRoughPlunge: UC.newInput(LANG.cc_plng_s, {title:LANG.cc_plng_l, convert:UC.toInt, modes:CAM, units:true}),
|
||||||
camRoughStock: UC.newInput(LANG.cr_lsto_s, {title:LANG.cr_lsto_l, convert:UC.toFloat, modes:CAM, units:true}),
|
camRoughStock: UC.newInput(LANG.cr_lsto_s, {title:LANG.cr_lsto_l, convert:UC.toFloat, modes:CAM, units:true}),
|
||||||
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camRoughVoid: UC.newBoolean(LANG.cr_clrp_s, onBooleanClick, {title:LANG.cr_clrp_l, modes:CAM}),
|
camRoughVoid: UC.newBoolean(LANG.cr_clrp_s, onBooleanClick, {title:LANG.cr_clrp_l, modes:CAM}),
|
||||||
camRoughFlat: UC.newBoolean(LANG.cr_clrf_s, onBooleanClick, {title:LANG.cr_clrf_l, modes:CAM}),
|
camRoughFlat: UC.newBoolean(LANG.cr_clrf_s, onBooleanClick, {title:LANG.cr_clrf_l, modes:CAM}),
|
||||||
camRoughTop: UC.newBoolean(LANG.cr_clrt_s, onBooleanClick, {title:LANG.cr_clrt_l, modes:CAM}),
|
camRoughTop: UC.newBoolean(LANG.cr_clrt_s, onBooleanClick, {title:LANG.cr_clrt_l, modes:CAM}),
|
||||||
camRoughIn: UC.newBoolean(LANG.cr_olin_s, onBooleanClick, {title:LANG.cr_olin_l, modes:CAM}),
|
camRoughIn: UC.newBoolean(LANG.cr_olin_s, onBooleanClick, {title:LANG.cr_olin_l, modes:CAM}),
|
||||||
camRoughSep: UC.newBlank({class:"pop-sep"}),
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camRoughOn: UC.newBoolean(LANG.enable, onBooleanClick, {modes:CAM}),
|
// camRoughOn: UC.newBoolean(LANG.enable, onBooleanClick, {modes:CAM}),
|
||||||
camOutline: UC.newGroup(LANG.co_menu, null, {modes:CAM, marker:true}),
|
camRoughAdd: UC.newRow([
|
||||||
|
UI.crAdd = UC.newButton('add', onButtonClick, {class: "f-col grow a-center"})
|
||||||
|
], { modes: CAM, class: "ext-buttons f-row grow" }),
|
||||||
|
|
||||||
|
camOutline: UC.newGroup(LANG.co_menu, null, {modes:CAM}),
|
||||||
camOutlineTool: UC.newSelect(LANG.cc_tool, {modes:CAM}),
|
camOutlineTool: UC.newSelect(LANG.cc_tool, {modes:CAM}),
|
||||||
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camOutlineSpindle: UC.newInput(LANG.cc_spnd_s, {title:LANG.cc_spnd_l, convert:UC.toInt, modes:CAM, visible:spindleShow}),
|
camOutlineSpindle: UC.newInput(LANG.cc_spnd_s, {title:LANG.cc_spnd_l, convert:UC.toInt, modes:CAM, visible:spindleShow}),
|
||||||
camOutlineDown: UC.newInput(LANG.cc_sdwn_s, {title:LANG.cc_sdwn_l, convert:UC.toFloat, modes:CAM, units:true}),
|
camOutlineDown: UC.newInput(LANG.cc_sdwn_s, {title:LANG.cc_sdwn_l, convert:UC.toFloat, modes:CAM, units:true}),
|
||||||
camOutlineSpeed: UC.newInput(LANG.cc_feed_s, {title:LANG.cc_feed_l, convert:UC.toInt, modes:CAM, units:true}),
|
camOutlineSpeed: UC.newInput(LANG.cc_feed_s, {title:LANG.cc_feed_l, convert:UC.toInt, modes:CAM, units:true}),
|
||||||
camOutlinePlunge: UC.newInput(LANG.cc_plng_s, {title:LANG.cc_plng_l, convert:UC.toInt, modes:CAM, units:true}),
|
camOutlinePlunge: UC.newInput(LANG.cc_plng_s, {title:LANG.cc_plng_l, convert:UC.toInt, modes:CAM, units:true}),
|
||||||
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camOutlineDogbone: UC.newBoolean(LANG.co_dogb_s, onBooleanClick, {title:LANG.co_dogb_l, modes:CAM, show:() => { return !UI.camOutlineWide.checked }}),
|
camOutlineDogbone: UC.newBoolean(LANG.co_dogb_s, onBooleanClick, {title:LANG.co_dogb_l, modes:CAM, show:() => { return !UI.camOutlineWide.checked }}),
|
||||||
camOutlineIn: UC.newBoolean(LANG.co_olin_s, onBooleanClick, {title:LANG.co_olin_l, modes:CAM, show:() => { return !UI.camOutlineOut.checked }}),
|
camOutlineIn: UC.newBoolean(LANG.co_olin_s, onBooleanClick, {title:LANG.co_olin_l, modes:CAM, show:() => { return !UI.camOutlineOut.checked }}),
|
||||||
camOutlineOut: UC.newBoolean(LANG.co_olot_s, onBooleanClick, {title:LANG.co_olot_l, modes:CAM, show:() => { return !UI.camOutlineIn.checked }}),
|
camOutlineOut: UC.newBoolean(LANG.co_olot_s, onBooleanClick, {title:LANG.co_olot_l, modes:CAM, show:() => { return !UI.camOutlineIn.checked }}),
|
||||||
camOutlineWide: UC.newBoolean(LANG.co_wide_s, onBooleanClick, {title:LANG.co_wide_l, modes:CAM, show:() => { return !UI.camOutlineIn.checked }}),
|
camOutlineWide: UC.newBoolean(LANG.co_wide_s, onBooleanClick, {title:LANG.co_wide_l, modes:CAM, show:() => { return !UI.camOutlineIn.checked }}),
|
||||||
camOutlineSep: UC.newBlank({class:"pop-sep"}),
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camOutlineOn: UC.newBoolean(LANG.co_olen_s, onBooleanClick, {title:LANG.co_olen_l, modes:CAM}),
|
// camOutlineOn: UC.newBoolean(LANG.co_olen_s, onBooleanClick, {title:LANG.co_olen_l, modes:CAM}),
|
||||||
camContour: UC.newGroup(LANG.cn_menu, null, {modes:CAM, marker:true}),
|
camOutlineAdd: UC.newRow([
|
||||||
|
UI.coAdd = UC.newButton('add', onButtonClick, {class: "f-col grow a-center"})
|
||||||
|
], { modes: CAM, class: "ext-buttons f-row grow" }),
|
||||||
|
|
||||||
|
camContour: UC.newGroup(LANG.cn_menu, null, {modes:CAM}),
|
||||||
camContourTool: UC.newSelect(LANG.cc_tool, {modes:CAM}),
|
camContourTool: UC.newSelect(LANG.cc_tool, {modes:CAM}),
|
||||||
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camContourSpindle: UC.newInput(LANG.cc_spnd_s, {title:LANG.cc_spnd_l, convert:UC.toInt, modes:CAM, visible:spindleShow}),
|
camContourSpindle: UC.newInput(LANG.cc_spnd_s, {title:LANG.cc_spnd_l, convert:UC.toInt, modes:CAM, visible:spindleShow}),
|
||||||
camContourOver: UC.newInput(LANG.cc_sovr_s, {title:LANG.cc_sovr_l, convert:UC.toFloat, bound:UC.bound(0.05,1.0), modes:CAM}),
|
camContourOver: UC.newInput(LANG.cc_sovr_s, {title:LANG.cc_sovr_l, convert:UC.toFloat, bound:UC.bound(0.05,1.0), modes:CAM}),
|
||||||
camContourSpeed: UC.newInput(LANG.cc_feed_s, {title:LANG.cc_feed_l, convert:UC.toInt, modes:CAM, units:true}),
|
camContourSpeed: UC.newInput(LANG.cc_feed_s, {title:LANG.cc_feed_l, convert:UC.toInt, modes:CAM, units:true}),
|
||||||
camContourAngle: UC.newInput(LANG.cf_angl_s, {title:LANG.cf_angl_l, convert:UC.toFloat, bound:UC.bound(45,90), modes:CAM}),
|
camContourAngle: UC.newInput(LANG.cf_angl_s, {title:LANG.cf_angl_l, convert:UC.toFloat, bound:UC.bound(45,90), modes:CAM}),
|
||||||
camTolerance: UC.newInput(LANG.ou_toll_s, {title:LANG.ou_toll_l, convert:UC.toFloat, bound:UC.bound(0,10.0), modes:CAM, units:true}),
|
camTolerance: UC.newInput(LANG.ou_toll_s, {title:LANG.ou_toll_l, convert:UC.toFloat, bound:UC.bound(0,10.0), modes:CAM, units:true}),
|
||||||
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camContourCurves: UC.newBoolean(LANG.cf_curv_s, onBooleanClick, {title:LANG.cf_curv_l, modes:CAM}),
|
camContourCurves: UC.newBoolean(LANG.cf_curv_s, onBooleanClick, {title:LANG.cf_curv_l, modes:CAM}),
|
||||||
camContourIn: UC.newBoolean(LANG.cf_olin_s, onBooleanClick, {title:LANG.cf_olin_l, modes:CAM}),
|
camContourIn: UC.newBoolean(LANG.cf_olin_s, onBooleanClick, {title:LANG.cf_olin_l, modes:CAM}),
|
||||||
camContourSep: UC.newBlank({class:"pop-sep"}),
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camContourYOn: UC.newBoolean(LANG.cf_liny_s, onBooleanClick, {title:LANG.cf_liny_l, modes:CAM}),
|
// camContourYOn: UC.newBoolean(LANG.cf_liny_s, onBooleanClick, {title:LANG.cf_liny_l, modes:CAM}),
|
||||||
camContourXOn: UC.newBoolean(LANG.cf_linx_s, onBooleanClick, {title:LANG.cf_linx_l, modes:CAM}),
|
// camContourXOn: UC.newBoolean(LANG.cf_linx_s, onBooleanClick, {title:LANG.cf_linx_l, modes:CAM}),
|
||||||
camTracing: UC.newGroup(LANG.cu_menu, null, {modes:CAM, marker:true}),
|
camOutlineAdd: UC.newRow([
|
||||||
|
UI.ccxAdd = UC.newButton('add x', onButtonClick, {class: "f-col grow a-center"}),
|
||||||
|
UI.ccyAdd = UC.newButton('add y', onButtonClick, {class: "f-col grow a-center"})
|
||||||
|
], { modes: CAM, class: "ext-buttons f-row grow" }),
|
||||||
|
|
||||||
|
camTracing: UC.newGroup(LANG.cu_menu, null, {modes:CAM}),
|
||||||
camTraceTool: UC.newSelect(LANG.cc_tool, {modes:CAM}),
|
camTraceTool: UC.newSelect(LANG.cc_tool, {modes:CAM}),
|
||||||
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camTraceType: UC.newSelect(LANG.cu_type_s, {title:LANG.cu_type_l, modes:CAM}, "trace"),
|
camTraceType: UC.newSelect(LANG.cu_type_s, {title:LANG.cu_type_l, modes:CAM}, "trace"),
|
||||||
camTraceSpeed: UC.newInput(LANG.cc_feed_s, {title:LANG.cc_feed_l, convert:UC.toInt, modes:CAM}),
|
camTraceSpeed: UC.newInput(LANG.cc_feed_s, {title:LANG.cc_feed_l, convert:UC.toInt, modes:CAM}),
|
||||||
camTracePlunge: UC.newInput(LANG.cc_plng_s, {title:LANG.cc_plng_l, convert:UC.toFloat, modes:CAM, units:true}),
|
camTracePlunge: UC.newInput(LANG.cc_plng_s, {title:LANG.cc_plng_l, convert:UC.toFloat, modes:CAM, units:true}),
|
||||||
camTraceSep: UC.newBlank({class:"pop-sep"}),
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camTraceSelect: UC.newRow([
|
camTraceSelect: UC.newRow([
|
||||||
(UI.traceAdd = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-plus"></i>'})),
|
(UI.traceAdd = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-plus"></i>'})),
|
||||||
(UI.traceDun = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-check"></i>'})),
|
(UI.traceDun = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-check"></i>'})),
|
||||||
(UI.traceClr = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
|
(UI.traceClr = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
|
||||||
], {modes:CAM, class:"ext-buttons f-row"}),
|
], {modes:CAM, class:"ext-buttons f-row"}),
|
||||||
camTabs: UC.newGroup(LANG.ct_menu, null, {modes:CAM, marker:true}),
|
|
||||||
camTabsWidth: UC.newInput(LANG.ct_wdth_s, {title:LANG.ct_wdth_l, convert:UC.toFloat, bound:UC.bound(0.005,100), modes:CAM, units:true}),
|
camDrill: UC.newGroup(LANG.cd_menu, null, {modes:CAM}),
|
||||||
camTabsHeight: UC.newInput(LANG.ct_hght_s, {title:LANG.ct_hght_l, convert:UC.toFloat, bound:UC.bound(0.005,100), modes:CAM, units:true}),
|
|
||||||
camTabsDepth: UC.newInput(LANG.ct_dpth_s, {title:LANG.ct_dpth_l, convert:UC.toFloat, bound:UC.bound(0.005,100), modes:CAM, units:true}),
|
|
||||||
camTabSep: UC.newBlank({class:"pop-sep"}),
|
|
||||||
camTabsManual: UC.newRow([
|
|
||||||
(UI.tabAdd = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-plus"></i>'})),
|
|
||||||
(UI.tabDun = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-check"></i>'})),
|
|
||||||
(UI.tabClr = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
|
|
||||||
], {modes:CAM, class:"ext-buttons f-row"}),
|
|
||||||
camDrill: UC.newGroup(LANG.cd_menu, null, {modes:CAM, marker:true}),
|
|
||||||
camDrillTool: UC.newSelect(LANG.cc_tool, {modes:CAM}),
|
camDrillTool: UC.newSelect(LANG.cc_tool, {modes:CAM}),
|
||||||
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camDrillSpindle: UC.newInput(LANG.cc_spnd_s, {title:LANG.cc_spnd_l, convert:UC.toInt, modes:CAM, visible:spindleShow}),
|
camDrillSpindle: UC.newInput(LANG.cc_spnd_s, {title:LANG.cc_spnd_l, convert:UC.toInt, modes:CAM, visible:spindleShow}),
|
||||||
camDrillDown: UC.newInput(LANG.cd_plpr_s, {title:LANG.cd_plpr_l, convert:UC.toFloat, modes:CAM, units:true}),
|
camDrillDown: UC.newInput(LANG.cd_plpr_s, {title:LANG.cd_plpr_l, convert:UC.toFloat, modes:CAM, units:true}),
|
||||||
camDrillDownSpeed: UC.newInput(LANG.cc_plng_s, {title:LANG.cc_plng_l, convert:UC.toFloat, modes:CAM, units:true}),
|
camDrillDownSpeed: UC.newInput(LANG.cc_plng_s, {title:LANG.cc_plng_l, convert:UC.toFloat, modes:CAM, units:true}),
|
||||||
camDrillDwell: UC.newInput(LANG.cd_dwll_s, {title:LANG.cd_dwll_l, convert:UC.toFloat, modes:CAM}),
|
camDrillDwell: UC.newInput(LANG.cd_dwll_s, {title:LANG.cd_dwll_l, convert:UC.toFloat, modes:CAM}),
|
||||||
camDrillLift: UC.newInput(LANG.cd_lift_s, {title:LANG.cd_lift_l, convert:UC.toFloat, modes:CAM, units:true}),
|
camDrillLift: UC.newInput(LANG.cd_lift_s, {title:LANG.cd_lift_l, convert:UC.toFloat, modes:CAM, units:true}),
|
||||||
camDrillSep: UC.newBlank({class:"pop-sep"}),
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camDrillingOn: UC.newBoolean(LANG.enable, onBooleanClick, {modes:CAM}),
|
// camDrillingOn: UC.newBoolean(LANG.enable, onBooleanClick, {modes:CAM}),
|
||||||
camDrillSep: UC.newBlank({class:"pop-sep"}),
|
camDrillAdd: UC.newRow([
|
||||||
camDrillReg: UC.newSelect(LANG.cd_regi_s, {modes:CAM, title:LANG.cd_regi_l}, "drillreg"),
|
UI.drAdd = UC.newButton('add', onButtonClick, {class: "f-col grow a-center"})
|
||||||
|
], { modes: CAM, class: "ext-buttons f-row grow" }),
|
||||||
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
|
camDrillAdd: UC.newRow([
|
||||||
|
UC.newLabel('registrations', {class: "f-col grow center"})
|
||||||
|
], { modes: CAM, class: "ext-buttons f-row" }),
|
||||||
|
// camDrillReg: UC.newSelect(LANG.cd_regi_s, {modes:CAM, title:LANG.cd_regi_l}, "drillreg"),
|
||||||
|
camDrillAdd: UC.newRow([
|
||||||
|
UI.drX2Add = UC.newButton('x axis 2', onButtonClick, {class: "f-col grow a-center"}),
|
||||||
|
UI.drX3Add = UC.newButton('x axis 3', onButtonClick, {class: "f-col grow a-center"}),
|
||||||
|
], { modes: CAM, xclass: "ext-buttons f-row grow" }),
|
||||||
|
camDrillAdd: UC.newRow([
|
||||||
|
UI.drY2Add = UC.newButton('y axis 2', onButtonClick, {class: "f-col grow a-center"}),
|
||||||
|
UI.drY3Add = UC.newButton('y axis 3', onButtonClick, {class: "f-col grow a-center"}),
|
||||||
|
], { modes: CAM, xclass: "ext-buttons f-row grow" }),
|
||||||
|
|
||||||
camSep1: UC.newGroup(null, null, {modes:CAM, class:"set-sep"}),
|
camSep1: UC.newGroup(null, null, {modes:CAM, class:"set-sep"}),
|
||||||
|
|
||||||
|
camTabs: UC.newGroup(LANG.ct_menu, null, {modes:CAM, marker:true}),
|
||||||
|
camTabsWidth: UC.newInput(LANG.ct_wdth_s, {title:LANG.ct_wdth_l, convert:UC.toFloat, bound:UC.bound(0.005,100), modes:CAM, units:true}),
|
||||||
|
camTabsHeight: UC.newInput(LANG.ct_hght_s, {title:LANG.ct_hght_l, convert:UC.toFloat, bound:UC.bound(0.005,100), modes:CAM, units:true}),
|
||||||
|
camTabsDepth: UC.newInput(LANG.ct_dpth_s, {title:LANG.ct_dpth_l, convert:UC.toFloat, bound:UC.bound(0.005,100), modes:CAM, units:true}),
|
||||||
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
|
camTabsManual: UC.newRow([
|
||||||
|
(UI.tabAdd = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-plus"></i>'})),
|
||||||
|
(UI.tabDun = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-check"></i>'})),
|
||||||
|
(UI.tabClr = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
|
||||||
|
], {modes:CAM, class:"ext-buttons f-row"}),
|
||||||
|
|
||||||
camStock: UC.newGroup(LANG.cs_menu, null, {modes:CAM, marker: true}),
|
camStock: UC.newGroup(LANG.cs_menu, null, {modes:CAM, marker: true}),
|
||||||
camStockX: UC.newInput(LANG.cs_wdth_s, {title:LANG.cs_wdth_l, convert:UC.toFloat, bound:UC.bound(0,9999), modes:CAM, units:true}),
|
camStockX: UC.newInput(LANG.cs_wdth_s, {title:LANG.cs_wdth_l, convert:UC.toFloat, bound:UC.bound(0,9999), modes:CAM, units:true}),
|
||||||
camStockY: UC.newInput(LANG.cs_dpth_s, {title:LANG.cs_dpth_l, convert:UC.toFloat, bound:UC.bound(0,9999), modes:CAM, units:true}),
|
camStockY: UC.newInput(LANG.cs_dpth_s, {title:LANG.cs_dpth_l, convert:UC.toFloat, bound:UC.bound(0,9999), modes:CAM, units:true}),
|
||||||
camStockZ: UC.newInput(LANG.cs_hght_s, {title:LANG.cs_hght_l, convert:UC.toFloat, bound:UC.bound(0,9999), modes:CAM, units:true}),
|
camStockZ: UC.newInput(LANG.cs_hght_s, {title:LANG.cs_hght_l, convert:UC.toFloat, bound:UC.bound(0,9999), modes:CAM, units:true}),
|
||||||
camStockOffset: UC.newBoolean(LANG.cs_offs_s, onBooleanClick, {title:LANG.cs_offs_l, modes:CAM}),
|
camStockOffset: UC.newBoolean(LANG.cs_offs_s, onBooleanClick, {title:LANG.cs_offs_l, modes:CAM}),
|
||||||
camStockSep: UC.newBlank({class:"pop-sep"}),
|
camSep: UC.newBlank({class:"pop-sep"}),
|
||||||
camStockOn: UC.newBoolean(LANG.cs_offe_s, onBooleanClick, {title:LANG.cs_offe_l, modes:CAM}),
|
camStockOn: UC.newBoolean(LANG.cs_offe_s, onBooleanClick, {title:LANG.cs_offe_l, modes:CAM}),
|
||||||
|
|
||||||
camCommon: UC.newGroup(LANG.cc_menu, null, {modes:CAM}),
|
camCommon: UC.newGroup(LANG.cc_menu, null, {modes:CAM}),
|
||||||
|
|
@ -1725,7 +1763,7 @@
|
||||||
outputKnifePasses: UC.newInput(LANG.dk_pass_s, {title:LANG.dk_pass_l, convert:UC.toInt, bound:UC.bound(0,5), modes:LASER}),
|
outputKnifePasses: UC.newInput(LANG.dk_pass_s, {title:LANG.dk_pass_l, convert:UC.toInt, bound:UC.bound(0,5), modes:LASER}),
|
||||||
outputKnifeTip: UC.newInput(LANG.dk_offs_s, {title:LANG.dk_offs_l, convert:UC.toFloat, bound:UC.bound(0.0,10.0), modes:LASER}),
|
outputKnifeTip: UC.newInput(LANG.dk_offs_s, {title:LANG.dk_offs_l, convert:UC.toFloat, bound:UC.bound(0.0,10.0), modes:LASER}),
|
||||||
knifeSep: UC.newBlank({class:"pop-sep", modes:LASER}),
|
knifeSep: UC.newBlank({class:"pop-sep", modes:LASER}),
|
||||||
knifeOn: UC.newBoolean(LANG.enable, onBooleanClick, {title:LANG.ou_drkn_l, modes:LASER}),
|
knifeOn: UC.newBoolean(LANG.enable, onBooleanClick, {title:LANG.ou_drkn_l, modes:LASER}),
|
||||||
|
|
||||||
output: UC.newGroup(LANG.ou_menu, null, {modes:GCODE}),
|
output: UC.newGroup(LANG.ou_menu, null, {modes:GCODE}),
|
||||||
outputLaserPower: UC.newInput(LANG.ou_powr_s, {title:LANG.ou_powr_l, convert:UC.toInt, bound:UC.bound(1,100), modes:LASER}),
|
outputLaserPower: UC.newInput(LANG.ou_powr_s, {title:LANG.ou_powr_l, convert:UC.toInt, bound:UC.bound(1,100), modes:LASER}),
|
||||||
|
|
@ -1809,14 +1847,8 @@
|
||||||
slaAntiAlias: UC.newSelect(LANG.sa_opaa_s, {title:LANG.sa_opaa_l, modes:SLA}, "antialias"),
|
slaAntiAlias: UC.newSelect(LANG.sa_opaa_s, {title:LANG.sa_opaa_l, modes:SLA}, "antialias"),
|
||||||
|
|
||||||
settingsGroup: UC.newGroup(LANG.se_menu, $('settings')),
|
settingsGroup: UC.newGroup(LANG.se_menu, $('settings')),
|
||||||
settingsTable: UC.newTableRow([
|
settingsTable: UC.newRow([ UI.settingsLoad = UC.newButton(LANG.se_load, settingsLoad) ]),
|
||||||
[
|
settingsTable: UC.newRow([ UI.settingsSave = UC.newButton(LANG.se_save, settingsSave) ]),
|
||||||
UI.settingsLoad =
|
|
||||||
UC.newButton(LANG.se_load, settingsLoad),
|
|
||||||
UI.settingsSave =
|
|
||||||
UC.newButton(LANG.se_save, settingsSave)
|
|
||||||
]
|
|
||||||
]),
|
|
||||||
|
|
||||||
layers: UC.setGroup($("layers")),
|
layers: UC.setGroup($("layers")),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -157,13 +157,13 @@
|
||||||
{ name: "4", id: 4 },
|
{ name: "4", id: 4 },
|
||||||
{ name: "8", id: 8 }
|
{ name: "8", id: 8 }
|
||||||
],
|
],
|
||||||
drillreg: [
|
// drillreg: [
|
||||||
{ name: "none" },
|
// { name: "none" },
|
||||||
{ name: "x axis" },
|
// { name: "x axis" },
|
||||||
{ name: "y axis" },
|
// { name: "y axis" },
|
||||||
{ name: "x axis 3" },
|
// { name: "x axis 3" },
|
||||||
{ name: "y axis 3" }
|
// { name: "y axis 3" }
|
||||||
],
|
// ],
|
||||||
detail: [
|
detail: [
|
||||||
{ name: "best" },
|
{ name: "best" },
|
||||||
{ name: "good" },
|
{ name: "good" },
|
||||||
|
|
@ -2105,7 +2105,6 @@
|
||||||
name = e.target.getAttribute("name"),
|
name = e.target.getAttribute("name"),
|
||||||
load = settings.sproc[mode][name],
|
load = settings.sproc[mode][name],
|
||||||
edit = prompt(`settings for "${name}"`, JSON.stringify(load));
|
edit = prompt(`settings for "${name}"`, JSON.stringify(load));
|
||||||
|
|
||||||
if (edit) {
|
if (edit) {
|
||||||
try {
|
try {
|
||||||
settings.sproc[mode][name] = JSON.parse(edit);
|
settings.sproc[mode][name] = JSON.parse(edit);
|
||||||
|
|
|
||||||
|
|
@ -363,10 +363,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function newLabel(text) {
|
function newLabel(text, opt = {}) {
|
||||||
let label = DOC.createElement('label');
|
let label = DOC.createElement('label');
|
||||||
label.appendChild(DOC.createTextNode(text));
|
label.appendChild(DOC.createTextNode(text));
|
||||||
label.setAttribute("class", "noselect");
|
label.setAttribute("class", "noselect");
|
||||||
|
if (opt.class) opt.class.split(' ').forEach(cl => {
|
||||||
|
label.classList.add(cl);
|
||||||
|
})
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
let terms = {
|
let terms = {
|
||||||
COPYRIGHT: "Copyright (C) Stewart Allen <sa@grid.space> - All Rights Reserved",
|
COPYRIGHT: "Copyright (C) Stewart Allen <sa@grid.space> - All Rights Reserved",
|
||||||
LICENSE: "See the license.md file included with the source distribution",
|
LICENSE: "See the license.md file included with the source distribution",
|
||||||
VERSION: "2.5.D3"
|
VERSION: "2.5.D4"
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof(module) === 'object') {
|
if (typeof(module) === 'object') {
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,10 @@
|
||||||
isParsed,
|
isParsed,
|
||||||
camStock,
|
camStock,
|
||||||
current,
|
current,
|
||||||
API, FDM, SPACE, STACKS, MODES, VIEWS, UI, UC;
|
API, FDM, SPACE, STACKS, MODES, VIEWS, UI, UC, LANG;
|
||||||
|
|
||||||
let zaxis = {x: 0, y: 0, z: 1},
|
let traceOp = { type: "trace" },
|
||||||
|
zaxis = { x: 0, y: 0, z: 1 },
|
||||||
func = {};
|
func = {};
|
||||||
|
|
||||||
CAM.restoreTabs = restoreTabs;
|
CAM.restoreTabs = restoreTabs;
|
||||||
|
|
@ -32,6 +33,7 @@
|
||||||
SPACE = api.const.SPACE;
|
SPACE = api.const.SPACE;
|
||||||
MODES = api.const.MODES;
|
MODES = api.const.MODES;
|
||||||
VIEWS = api.const.VIEWS;
|
VIEWS = api.const.VIEWS;
|
||||||
|
LANG = api.const.LANG;
|
||||||
UI = api.ui;
|
UI = api.ui;
|
||||||
UC = api.uc;
|
UC = api.uc;
|
||||||
API = api;
|
API = api;
|
||||||
|
|
@ -50,6 +52,7 @@
|
||||||
api.event.on("mode.set", (mode) => {
|
api.event.on("mode.set", (mode) => {
|
||||||
isCamMode = mode === 'CAM';
|
isCamMode = mode === 'CAM';
|
||||||
$('set-tools').style.display = isCamMode ? '' : 'none';
|
$('set-tools').style.display = isCamMode ? '' : 'none';
|
||||||
|
$('set-label').innerText = isCamMode ? "" : LANG.settings;
|
||||||
kiri.space.platform.setColor(isCamMode ? 0xeeeeee : 0xcccccc);
|
kiri.space.platform.setColor(isCamMode ? 0xeeeeee : 0xcccccc);
|
||||||
updateStock(undefined, 'internal');
|
updateStock(undefined, 'internal');
|
||||||
if (!isCamMode) {
|
if (!isCamMode) {
|
||||||
|
|
@ -65,6 +68,7 @@
|
||||||
}
|
}
|
||||||
// do not persist traces across page reloads
|
// do not persist traces across page reloads
|
||||||
func.traceClear();
|
func.traceClear();
|
||||||
|
func.opRender();
|
||||||
});
|
});
|
||||||
|
|
||||||
api.event.on("view.set", (mode) => {
|
api.event.on("view.set", (mode) => {
|
||||||
|
|
@ -73,6 +77,7 @@
|
||||||
CAM.animate_clear(api);
|
CAM.animate_clear(api);
|
||||||
func.tabDone();
|
func.tabDone();
|
||||||
func.traceDone();
|
func.traceDone();
|
||||||
|
func.opRender();
|
||||||
});
|
});
|
||||||
|
|
||||||
function checkOutlineSettings(settings) {
|
function checkOutlineSettings(settings) {
|
||||||
|
|
@ -87,9 +92,15 @@
|
||||||
|
|
||||||
api.event.on("settings.saved", (settings) => {
|
api.event.on("settings.saved", (settings) => {
|
||||||
current = settings;
|
current = settings;
|
||||||
const proc = settings.process;
|
let proc = settings.process;
|
||||||
let hasTabs = false;
|
let hasTabs = false;
|
||||||
let hasTraces = false;
|
let hasTraces = false;
|
||||||
|
// ensure trace op is a singleton for now
|
||||||
|
for (let i=0; i<proc.ops.length; i++) {
|
||||||
|
if (proc.ops[i].type === "trace") {
|
||||||
|
proc.ops[i] = traceOp;
|
||||||
|
}
|
||||||
|
}
|
||||||
// for any tabs or traces to set markers
|
// for any tabs or traces to set markers
|
||||||
Object.keys(settings.widget).forEach(wid => {
|
Object.keys(settings.widget).forEach(wid => {
|
||||||
let wannot = settings.widget[wid];
|
let wannot = settings.widget[wid];
|
||||||
|
|
@ -98,19 +109,13 @@
|
||||||
});
|
});
|
||||||
checkOutlineSettings(settings);
|
checkOutlineSettings(settings);
|
||||||
// show/hide dots in enabled process pop buttons
|
// show/hide dots in enabled process pop buttons
|
||||||
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.camContour.marker.style.display =
|
|
||||||
proc.camContourXOn || proc.camContourYOn ? 'flex' : 'none';
|
|
||||||
api.ui.camTracing.marker.style.display = hasTraces ? 'flex' : 'none';
|
|
||||||
api.ui.camTabs.marker.style.display = hasTabs ? 'flex' : 'none';
|
api.ui.camTabs.marker.style.display = hasTabs ? 'flex' : 'none';
|
||||||
api.ui.camStock.marker.style.display = proc.camStockOn ? 'flex' : 'none';
|
api.ui.camStock.marker.style.display = proc.camStockOn ? 'flex' : 'none';
|
||||||
updateStock(settings, 'settings.saved.internal');
|
updateStock(settings, 'settings.saved.internal');
|
||||||
});
|
});
|
||||||
|
|
||||||
api.event.on("settings.load", (settings) => {
|
api.event.on("settings.load", (settings) => {
|
||||||
|
func.opRender();
|
||||||
if (!isCamMode) return;
|
if (!isCamMode) return;
|
||||||
restoreTabs(api.widgets.all());
|
restoreTabs(api.widgets.all());
|
||||||
});
|
});
|
||||||
|
|
@ -148,6 +153,9 @@
|
||||||
|
|
||||||
// TAB/TRACE BUTTON HANDLERS
|
// TAB/TRACE BUTTON HANDLERS
|
||||||
api.event.on("button.click", target => {
|
api.event.on("button.click", target => {
|
||||||
|
let settings = API.conf.get();
|
||||||
|
let { process, device } = settings;
|
||||||
|
let drill;
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case api.ui.tabAdd:
|
case api.ui.tabAdd:
|
||||||
return func.tabAdd();
|
return func.tabAdd();
|
||||||
|
|
@ -167,6 +175,200 @@
|
||||||
if (ok) func.traceClear();
|
if (ok) func.traceClear();
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case api.ui.crAdd:
|
||||||
|
func.opAdd({
|
||||||
|
type: "rough",
|
||||||
|
tool: process.camRoughTool,
|
||||||
|
spindle: process.camRoughSpindle,
|
||||||
|
step: process.camRoughOver,
|
||||||
|
down: process.camRoughDown,
|
||||||
|
rate: process.camRoughSpeed,
|
||||||
|
plunge: process.camRoughPlunge,
|
||||||
|
leave: process.camRoughStock,
|
||||||
|
voids: process.camRoughVoid,
|
||||||
|
flats: process.camRoughFlat,
|
||||||
|
inside: process.camRoughIn,
|
||||||
|
top: process.camRoughTop
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case api.ui.coAdd:
|
||||||
|
func.opAdd({
|
||||||
|
type: "outline",
|
||||||
|
tool: process.camOutlineTool,
|
||||||
|
spindle: process.camOutlineSpindle,
|
||||||
|
down: process.camOutlineDown,
|
||||||
|
rate: process.camOutlineSpeed,
|
||||||
|
plunge: process.camOutlinePlunge,
|
||||||
|
dogbones: process.camOutlineDogbone,
|
||||||
|
outside: process.camOutlineOut,
|
||||||
|
inside: process.camOutlineIn,
|
||||||
|
wide: process.camOutlineWide
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case api.ui.ccxAdd:
|
||||||
|
func.opAdd({
|
||||||
|
type: "contour x",
|
||||||
|
tool: process.camContourTool,
|
||||||
|
spindle: process.camContourSpindle,
|
||||||
|
step: process.camContourOver,
|
||||||
|
rate: process.camContourSpeed,
|
||||||
|
angle: process.camContourAngle,
|
||||||
|
tolerance: process.camTolerance,
|
||||||
|
curves: process.camContourCurves,
|
||||||
|
inside: process.camContourIn
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case api.ui.ccyAdd:
|
||||||
|
func.opAdd({
|
||||||
|
type: "contour y",
|
||||||
|
tool: process.camContourTool,
|
||||||
|
spindle: process.camContourSpindle,
|
||||||
|
step: process.camContourOver,
|
||||||
|
rate: process.camContourSpeed,
|
||||||
|
angle: process.camContourAngle,
|
||||||
|
tolerance: process.camTolerance,
|
||||||
|
curves: process.camContourCurves,
|
||||||
|
inside: process.camContourIn
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case api.ui.drAdd:
|
||||||
|
func.opAdd({
|
||||||
|
type: "drill",
|
||||||
|
tool: process.camDrillTool,
|
||||||
|
spindle: process.camDrillSpindle,
|
||||||
|
down: process.camDrillDown,
|
||||||
|
rate: process.camDrillDownSpeed,
|
||||||
|
dwell: process.camDrillDwell,
|
||||||
|
lift: process.camDrillLift
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case api.ui.drX2Add:
|
||||||
|
drill = { axis: "x", points: 2 };
|
||||||
|
break;
|
||||||
|
case api.ui.drX3Add:
|
||||||
|
drill = { axis: "x", points: 3 };
|
||||||
|
break;
|
||||||
|
case api.ui.drY2Add:
|
||||||
|
drill = { axis: "y", points: 2 };
|
||||||
|
break;
|
||||||
|
case api.ui.drY3Add:
|
||||||
|
drill = { axis: "y", points: 3 };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (drill) {
|
||||||
|
func.opAdd({
|
||||||
|
type: "register",
|
||||||
|
tool: process.camDrillTool,
|
||||||
|
spindle: process.camDrillSpindle,
|
||||||
|
down: process.camDrillDown,
|
||||||
|
rate: process.camDrillDownSpeed,
|
||||||
|
dwell: process.camDrillDwell,
|
||||||
|
lift: process.camDrillLift,
|
||||||
|
points: drill.points,
|
||||||
|
axis: drill.axis
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// OPS FUNCS
|
||||||
|
api.event.on("cam.op.add", func.opAdd = (rec) => {
|
||||||
|
let oplist = current.process.ops;
|
||||||
|
if (oplist.indexOf(rec) < 0) {
|
||||||
|
oplist.push(rec);
|
||||||
|
API.conf.save();
|
||||||
|
func.opRender();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
api.event.on("cam.op.del", func.opDel = (rec) => {
|
||||||
|
let oplist = current.process.ops;
|
||||||
|
let pos = oplist.indexOf(rec);
|
||||||
|
if (pos >= 0) {
|
||||||
|
oplist.splice(pos,1);
|
||||||
|
API.conf.save();
|
||||||
|
func.opRender();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
api.event.on("cam.op.render", func.opRender = () => {
|
||||||
|
$('camops').style.display = isCamMode && isArrange ? 'flex' : '';
|
||||||
|
if (!isCamMode) return;
|
||||||
|
let mark = Date.now();
|
||||||
|
let html = [];
|
||||||
|
let ops = current.process.ops;
|
||||||
|
let oplist = ops.length > 0 ? ops : [{
|
||||||
|
type: "use right menu to add milling operations ... drag & drop to re-order"
|
||||||
|
}];
|
||||||
|
let bind = {};
|
||||||
|
oplist.forEach((rec,i) => {
|
||||||
|
let title = Object.entries(rec).filter((v,i) => {
|
||||||
|
return v[0] !== 'type'
|
||||||
|
}).map(v => v.join(": ")).join("\n");
|
||||||
|
html.appendAll([
|
||||||
|
`<div id="${mark+i}">`,
|
||||||
|
title.length ? `<div class="opop">${title}</div>` : '',
|
||||||
|
`<label class="label">${rec.type}</label>`,
|
||||||
|
`<label id="${mark+i}-x" class="del"><i class="fas fa-times"></i></label>`,
|
||||||
|
`</div>`
|
||||||
|
]);
|
||||||
|
bind[mark+i] = rec;
|
||||||
|
});
|
||||||
|
let listel = $('oplist');
|
||||||
|
listel.innerHTML = html.join('');
|
||||||
|
let bounds = [];
|
||||||
|
for (let [id, rec] of Object.entries(bind)) {
|
||||||
|
$(`${id}-x`).onmousedown = (ev) => {
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
};
|
||||||
|
$(`${id}-x`).onclick = (ev) => {
|
||||||
|
oplist.splice(oplist.indexOf(rec), 1);
|
||||||
|
API.conf.save();
|
||||||
|
func.opRender();
|
||||||
|
};
|
||||||
|
let el = $(id);
|
||||||
|
bounds.push(el);
|
||||||
|
el.rec = rec;
|
||||||
|
el.onmousedown = (ev) => {
|
||||||
|
let target = ev.target;
|
||||||
|
target.classList.add("drag");
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
let tracker = UI.tracker;
|
||||||
|
tracker.style.display = 'block';
|
||||||
|
let cancel = tracker.onmouseup = (ev) => {
|
||||||
|
target.classList.remove("drag");
|
||||||
|
tracker.style.display = 'none';
|
||||||
|
if (ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
ops.length = 0;
|
||||||
|
for (let child of listel.childNodes) {
|
||||||
|
ops.push(child.rec);
|
||||||
|
}
|
||||||
|
API.conf.save();
|
||||||
|
func.opRender();
|
||||||
|
};
|
||||||
|
tracker.onmousemove = (ev) => {
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
if (ev.buttons === 0) {
|
||||||
|
return cancel();
|
||||||
|
}
|
||||||
|
for (let el of bounds) {
|
||||||
|
if (el === target) continue;
|
||||||
|
let rect = el.getBoundingClientRect();
|
||||||
|
let left = rect.left;
|
||||||
|
let right = rect.left + rect.width;
|
||||||
|
if (ev.pageX >= left && ev.pageX <= right) {
|
||||||
|
let mid = (left + right) / 2;
|
||||||
|
try { listel.removeChild(target); } catch (e) { }
|
||||||
|
el.insertAdjacentElement(ev.pageX < mid ? "beforebegin" : "afterend", target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -401,6 +603,11 @@
|
||||||
wtrace.remove(poly);
|
wtrace.remove(poly);
|
||||||
atrace.remove(poly.traceInfo);
|
atrace.remove(poly.traceInfo);
|
||||||
}
|
}
|
||||||
|
if (atrace.length) {
|
||||||
|
func.opAdd(traceOp);
|
||||||
|
} else {
|
||||||
|
func.opDel(traceOp);
|
||||||
|
}
|
||||||
API.conf.save();
|
API.conf.save();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@
|
||||||
output.append(line);
|
output.append(line);
|
||||||
}
|
}
|
||||||
if (!line || output.length > 1000) {
|
if (!line || output.length > 1000) {
|
||||||
online(output.join("\n"));
|
online(output.join("\r\n"));
|
||||||
output = [];
|
output = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
868
src/mode/cam/ops.js
Normal file
868
src/mode/cam/ops.js
Normal file
|
|
@ -0,0 +1,868 @@
|
||||||
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
let KIRI = self.kiri,
|
||||||
|
BASE = self.base,
|
||||||
|
UTIL = BASE.util,
|
||||||
|
CAM = KIRI.driver.CAM,
|
||||||
|
PRO = CAM.process,
|
||||||
|
POLY = BASE.polygons,
|
||||||
|
newSlice = KIRI.newSlice,
|
||||||
|
newPoint = BASE.newPoint,
|
||||||
|
newPolygon = BASE.newPolygon;
|
||||||
|
|
||||||
|
class CamOp {
|
||||||
|
constructor(state, op) {
|
||||||
|
this.state = state;
|
||||||
|
this.op = op
|
||||||
|
}
|
||||||
|
|
||||||
|
type() {
|
||||||
|
return this.op.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
weight() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
slice() { }
|
||||||
|
|
||||||
|
prepare() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpRough extends CamOp {
|
||||||
|
constructor(state, op) {
|
||||||
|
super(state, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
slice(progress) {
|
||||||
|
let { op, state } = this;
|
||||||
|
let { settings, widget, slicer, sliceAll } = state;
|
||||||
|
let { updateToolDiams, thruHoles, tabs, cutTabs } = state;
|
||||||
|
let { tshadow, shadowTop, ztOff, zBottom, zMax } = state;
|
||||||
|
|
||||||
|
let roughIn = op.inside;
|
||||||
|
let roughTop = op.top;
|
||||||
|
let roughDown = op.down;
|
||||||
|
let roughTool = new CAM.Tool(settings, op.tool);
|
||||||
|
let roughToolDiam = roughTool.fluteDiameter();
|
||||||
|
let roughLeave = op.leave;
|
||||||
|
|
||||||
|
// create facing slices
|
||||||
|
if (roughTop) {
|
||||||
|
let shadow = tshadow.clone();
|
||||||
|
let inset = POLY.offset(shadow, (roughToolDiam / (roughIn ? 2 : 1)));
|
||||||
|
let facing = POLY.offset(inset, -(roughToolDiam * op.step), { count: 999, flat: true });
|
||||||
|
let zdiv = ztOff / roughDown;
|
||||||
|
let zstep = (zdiv % 1 > 0) ? ztOff / (Math.floor(zdiv) + 1) : roughDown;
|
||||||
|
if (roughTop && ztOff === 0) {
|
||||||
|
// compensate for lack of z top offset in this scenario
|
||||||
|
ztOff = zstep;
|
||||||
|
}
|
||||||
|
let camFaces = this.camFaces = [];
|
||||||
|
for (let z = zMax + ztOff - zstep; z >= zMax; z -= zstep) {
|
||||||
|
let slice = shadowTop.slice.clone(false);
|
||||||
|
slice.z = z;
|
||||||
|
slice.camMode = PRO.LEVEL;
|
||||||
|
slice.camLines = POLY.setZ(facing.clone(true), slice.z);
|
||||||
|
slice.output()
|
||||||
|
.setLayer("facing", {face: 0, line: 0})
|
||||||
|
.addPolys(slice.camLines);
|
||||||
|
sliceAll.push(slice);
|
||||||
|
camFaces.push(slice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create roughing slices
|
||||||
|
updateToolDiams(roughToolDiam);
|
||||||
|
|
||||||
|
let flats = [];
|
||||||
|
let shadow = [];
|
||||||
|
let slices = [];
|
||||||
|
let indices = slicer.interval(roughDown, {
|
||||||
|
down: true, min: zBottom, fit: true, off: 0.01
|
||||||
|
});
|
||||||
|
// shift out first (top-most) slice
|
||||||
|
indices.shift();
|
||||||
|
if (op.flats) {
|
||||||
|
let flats = Object.keys(slicer.zFlat)
|
||||||
|
.map(v => parseFloat(v).round(4))
|
||||||
|
.filter(v => v >= zBottom);
|
||||||
|
flats.forEach(v => {
|
||||||
|
if (!indices.contains(v)) {
|
||||||
|
indices.push(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
indices = indices.sort((a,b) => { return b - a });
|
||||||
|
// if layer is not on a flat and next one is,
|
||||||
|
// then move this layer up to mid-point to previous layer
|
||||||
|
// this is not perfect. the best method is to interpolate
|
||||||
|
// between flats so that each step is < step down. on todo list
|
||||||
|
for (let i=1; i<indices.length-1; i++) {
|
||||||
|
const prev = indices[i-1];
|
||||||
|
const curr = indices[i];
|
||||||
|
const next = indices[i+1];
|
||||||
|
if (!flats.contains(curr) && flats.contains(next)) {
|
||||||
|
// console.log('move',curr,'up toward',prev,'b/c next',next,'is flat');
|
||||||
|
indices[i] = next + ((prev - next) / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// add flats to shadow
|
||||||
|
flats = Object.keys(slicer.zFlat)
|
||||||
|
.map(v => (parseFloat(v) - 0.01).round(5))
|
||||||
|
.filter(v => v > 0 && indices.indexOf(v) < 0);
|
||||||
|
indices = indices.appendAll(flats).sort((a,b) => b-a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('indices', ...indices, {zBottom});
|
||||||
|
slicer.slice(indices, { each: (data, index, total) => {
|
||||||
|
shadow = POLY.union(shadow.slice().appendAll(data.tops), 0.01, true);
|
||||||
|
if (flats.indexOf(data.z) >= 0) {
|
||||||
|
// exclude flats injected to complete shadow
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data.shadow = shadow.clone(true);
|
||||||
|
data.slice.camMode = PRO.ROUGH;
|
||||||
|
data.slice.shadow = data.shadow;
|
||||||
|
// data.slice.tops[0].inner = data.shadow;
|
||||||
|
// data.slice.tops[0].inner = POLY.setZ(tshadow.clone(true), data.z);
|
||||||
|
slices.push(data.slice);
|
||||||
|
progress((index / total) * 0.5);
|
||||||
|
}, genso: true });
|
||||||
|
|
||||||
|
shadow = POLY.union(shadow.appendAll(shadowTop.tops), 0.01, 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 (op.voids) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
let po = POLY.offset([p], -(roughToolDiam / 2 + roughLeave + 0.01));
|
||||||
|
return po ? po[0] : undefined;
|
||||||
|
} else {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}).filter(p => p);
|
||||||
|
});
|
||||||
|
shadow = POLY.nest(shadow);
|
||||||
|
|
||||||
|
// expand shadow by half tool diameter + stock to leave
|
||||||
|
// const sadd = roughIn ? roughToolDiam / 4 : roughToolDiam / 2;
|
||||||
|
const sadd = roughIn ? roughToolDiam / 2 : roughToolDiam / 2;
|
||||||
|
const shell = POLY.offset(shadow, sadd + roughLeave);
|
||||||
|
|
||||||
|
slices.forEach((slice, index) => {
|
||||||
|
let offset = [shell.clone(true),slice.shadow.clone(true)].flat();
|
||||||
|
let flat = POLY.flatten(offset, [], true);
|
||||||
|
let nest = POLY.setZ(POLY.nest(flat), slice.z);
|
||||||
|
|
||||||
|
// inset offset array by 1/2 diameter then by tool overlap %
|
||||||
|
offset = POLY.offset(nest, [-(roughToolDiam / 2 + roughLeave), -roughToolDiam * op.step], {
|
||||||
|
minArea: 0,
|
||||||
|
z: slice.z,
|
||||||
|
count: 999,
|
||||||
|
flat: true,
|
||||||
|
call: (polys, count, depth) => {
|
||||||
|
// used in depth-first path creation
|
||||||
|
polys.forEach(p => {
|
||||||
|
p.depth = depth;
|
||||||
|
if (p.inner) {
|
||||||
|
p.inner.forEach(p => p.depth = depth);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}) || [];
|
||||||
|
|
||||||
|
// add outside pass if not inside only
|
||||||
|
if (!roughIn) {
|
||||||
|
const outside = POLY.offset(shadow.clone(), roughToolDiam * op.step, {z: slice.z});
|
||||||
|
if (outside) {
|
||||||
|
offset.appendAll(outside);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tabs) {
|
||||||
|
tabs.forEach(tab => {
|
||||||
|
tab.off = POLY.expand([tab.poly], roughToolDiam / 2).flat();
|
||||||
|
});
|
||||||
|
offset = cutTabs(tabs, offset, slice.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!offset) return;
|
||||||
|
|
||||||
|
// elimate double inset on inners
|
||||||
|
offset.forEach(op => {
|
||||||
|
if (op.inner) {
|
||||||
|
let operim = op.perimeter();
|
||||||
|
let newinner = [];
|
||||||
|
op.inner.forEach(oi => {
|
||||||
|
if (Math.abs(oi.perimeter() - operim) > 0.01) {
|
||||||
|
newinner.push(oi);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
op.inner = newinner;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
slice.camLines = offset;
|
||||||
|
if (true) slice.output()
|
||||||
|
.setLayer("slice", {line: 0xaaaa00}, true)
|
||||||
|
.addPolys(slice.topPolys())
|
||||||
|
// .setLayer("top shadow", {line: 0x0000aa})
|
||||||
|
// .addPolys(tshadow)
|
||||||
|
// .setLayer("rough shadow", {line: 0x00aa00})
|
||||||
|
// .addPolys(shadow)
|
||||||
|
// .setLayer("rough shell", {line: 0xaa0000})
|
||||||
|
// .addPolys(shell);
|
||||||
|
slice.output()
|
||||||
|
.setLayer("roughing", {face: 0, line: 0})
|
||||||
|
.addPolys(offset);
|
||||||
|
progress(0.5 + (index / slices.length) * 0.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.sliceOut = slices.filter(slice => slice.camLines);
|
||||||
|
sliceAll.appendAll(this.sliceOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare(ops, progress) {
|
||||||
|
let { op, state, sliceOut, camFaces } = this;
|
||||||
|
let { setTool, setSpindle, setPrintPoint } = ops;
|
||||||
|
let { polyEmit, poly2polyEmit, depthRoughPath } = ops;
|
||||||
|
let { camOut, newLayer, printPoint } = ops;
|
||||||
|
let { settings, widget } = state;
|
||||||
|
let { process } = settings;
|
||||||
|
|
||||||
|
let cutdir = process.camConventional;
|
||||||
|
let depthFirst = process.camDepthFirst;
|
||||||
|
let depthData = [];
|
||||||
|
|
||||||
|
setTool(op.tool, op.rate, op.plunge);
|
||||||
|
setSpindle(op.spindle);
|
||||||
|
|
||||||
|
for (let slice of (camFaces || [])) {
|
||||||
|
const level = [];
|
||||||
|
for (let poly of slice.camLines) {
|
||||||
|
level.push(poly);
|
||||||
|
if (poly.inner) {
|
||||||
|
poly.inner.forEach(function(inner) {
|
||||||
|
level.push(inner);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set winding specified in output
|
||||||
|
POLY.setWinding(level, cutdir, false);
|
||||||
|
printPoint = poly2polyEmit(level, printPoint, function(poly, index, count) {
|
||||||
|
poly.forEachPoint(function(point, pidx, points, offset) {
|
||||||
|
camOut(point.clone(), offset !== 0);
|
||||||
|
}, true, index);
|
||||||
|
});
|
||||||
|
newLayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let slice of sliceOut) {
|
||||||
|
let polys = [], t = [], c = [];
|
||||||
|
POLY.flatten(slice.camLines).forEach(function (poly) {
|
||||||
|
let child = poly.parent;
|
||||||
|
if (depthFirst) { poly = poly.clone(); poly.parent = child ? 1 : 0 }
|
||||||
|
if (child) c.push(poly); else t.push(poly);
|
||||||
|
poly.layer = depthData.layer;
|
||||||
|
polys.push(poly);
|
||||||
|
});
|
||||||
|
|
||||||
|
// set cut direction on outer polys
|
||||||
|
POLY.setWinding(t, cutdir);
|
||||||
|
// set cut direction on inner polys
|
||||||
|
POLY.setWinding(c, !cutdir);
|
||||||
|
|
||||||
|
if (depthFirst) {
|
||||||
|
depthData.push(polys);
|
||||||
|
} else {
|
||||||
|
printPoint = poly2polyEmit(polys, printPoint, function(poly, index, count) {
|
||||||
|
poly.forEachPoint(function(point, pidx, points, offset) {
|
||||||
|
camOut(point.clone(), offset !== 0);
|
||||||
|
}, poly.isClosed(), index);
|
||||||
|
});
|
||||||
|
newLayer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depthFirst) {
|
||||||
|
let tops = depthData.map(level => {
|
||||||
|
return POLY.nest(level.filter(poly => poly.depth === 0).clone());
|
||||||
|
});
|
||||||
|
printPoint = depthRoughPath(printPoint, 0, depthData, tops, polyEmit);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPrintPoint(printPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpOutline extends CamOp {
|
||||||
|
constructor(state, op) {
|
||||||
|
super(state, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
slice(progress) {
|
||||||
|
let { op, state } = this;
|
||||||
|
let { settings, widget, slicer, sliceAll, tshadow } = state;
|
||||||
|
let { updateToolDiams, zThru, zBottom, shadowTop, tabs, cutTabs } = state;
|
||||||
|
|
||||||
|
let outlineTool = new CAM.Tool(settings, op.tool);
|
||||||
|
let outlineToolDiam = this.toolDiam = outlineTool.fluteDiameter();
|
||||||
|
updateToolDiams(outlineToolDiam);
|
||||||
|
|
||||||
|
let shadow = [];
|
||||||
|
let slices = [];
|
||||||
|
let indices = slicer.interval(op.down, { down: true, min: zBottom, fit: true, off: 0.01 });
|
||||||
|
// shift out first (top-most) slice
|
||||||
|
indices.shift();
|
||||||
|
// add flats to shadow
|
||||||
|
const flats = Object.keys(slicer.zFlat)
|
||||||
|
.map(v => (parseFloat(v) - 0.01).round(5))
|
||||||
|
.filter(v => v > 0 && indices.indexOf(v) < 0);
|
||||||
|
indices = indices.appendAll(flats).sort((a,b) => b-a);
|
||||||
|
// console.log('indices', ...indices, {zBottom, slicer});
|
||||||
|
if (op.outside && !op.inside) {
|
||||||
|
console.log({outline_bypass: indices, down: op.down});
|
||||||
|
indices.forEach((ind,i) => {
|
||||||
|
if (flats.indexOf(ind) >= 0) {
|
||||||
|
// exclude flats
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let slice = newSlice(ind);
|
||||||
|
slice.camMode = PRO.OUTLINE;
|
||||||
|
slice.shadow = shadow.clone(true);
|
||||||
|
slices.push(slice);
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
slicer.slice(indices, { each: (data, index, total) => {
|
||||||
|
shadow = POLY.union(shadow.slice().appendAll(data.tops), 0.01, true);
|
||||||
|
if (flats.indexOf(data.z) >= 0) {
|
||||||
|
// exclude flats injected to complete shadow
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data.shadow = shadow.clone(true);
|
||||||
|
data.slice.camMode = PRO.OUTLINE;
|
||||||
|
data.slice.shadow = data.shadow;
|
||||||
|
// data.slice.tops[0].inner = data.shadow;
|
||||||
|
// data.slice.tops[0].inner = POLY.setZ(tshadow.clone(true), data.z);
|
||||||
|
slices.push(data.slice);
|
||||||
|
// data.slice.xray();
|
||||||
|
// onupdate(0.2 + (index/total) * 0.1, "outlines");
|
||||||
|
progress((index / total) * 0.5);
|
||||||
|
}, genso: true });
|
||||||
|
shadow = POLY.union(shadow.appendAll(shadowTop.tops), 0.01, true);
|
||||||
|
|
||||||
|
// extend cut thru (only when z bottom is 0)
|
||||||
|
if (zThru) {
|
||||||
|
let last = slices[slices.length-1];
|
||||||
|
let add = last.clone(true);
|
||||||
|
add.camMode = last.camMode;
|
||||||
|
add.tops.forEach(top => {
|
||||||
|
top.poly.setZ(add.z);
|
||||||
|
});
|
||||||
|
add.shadow = last.shadow.clone(true);
|
||||||
|
add.z -= zThru;
|
||||||
|
slices.push(add);
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.forEach(slice => {
|
||||||
|
let tops = slice.shadow;
|
||||||
|
|
||||||
|
// outside only (use tshadow for entire cut)
|
||||||
|
if (op.outside) {
|
||||||
|
tops = tshadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = POLY.expand(tops, outlineToolDiam / 2, slice.z);
|
||||||
|
if (!(offset && offset.length)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// when pocket only, drop first outer poly
|
||||||
|
// if it matches the shell and promote inner polys
|
||||||
|
if (op.inside) {
|
||||||
|
let shell = POLY.expand(tops.clone(), outlineToolDiam / 2);
|
||||||
|
offset = POLY.filter(offset, [], function(poly) {
|
||||||
|
if (poly.area() < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (let sp=0; sp<shell.length; sp++) {
|
||||||
|
// eliminate shell only polys
|
||||||
|
if (poly.isEquivalent(shell[sp])) {
|
||||||
|
if (poly.inner) return poly.inner;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return poly;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (op.wide) {
|
||||||
|
offset.slice().forEach(op => {
|
||||||
|
// clone removes inners but the real solution is
|
||||||
|
// to limit expanded shells to through holes
|
||||||
|
POLY.expand([op.clone(true)], outlineToolDiam * 0.5, slice.z, offset, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tabs) {
|
||||||
|
tabs.forEach(tab => {
|
||||||
|
tab.off = POLY.expand([tab.poly], outlineToolDiam / 2).flat();
|
||||||
|
});
|
||||||
|
offset = cutTabs(tabs, offset, slice.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op.dogbones && !op.wide) {
|
||||||
|
CAM.addDogbones(offset, outlineToolDiam / 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// offset.xout(`slice ${slice.z}`);
|
||||||
|
slice.camLines = offset;
|
||||||
|
if (false) slice.output()
|
||||||
|
.setLayer("slice", {line: 0xaaaa00}, false)
|
||||||
|
.addPolys(slice.topPolys())
|
||||||
|
slice.output()
|
||||||
|
.setLayer("outline", {face: 0, line: 0})
|
||||||
|
.addPolys(offset);
|
||||||
|
});
|
||||||
|
|
||||||
|
sliceAll.appendAll(slices);
|
||||||
|
this.sliceOut = slices;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare(ops, progress) {
|
||||||
|
let { op, state, sliceOut } = this;
|
||||||
|
let { setTool, setSpindle, setPrintPoint } = ops;
|
||||||
|
let { polyEmit, poly2polyEmit, depthOutlinePath } = ops;
|
||||||
|
let { camOut, newLayer, printPoint } = ops;
|
||||||
|
let { settings, widget } = state;
|
||||||
|
let { process } = settings;
|
||||||
|
|
||||||
|
let toolDiam = this.toolDiam;
|
||||||
|
let cutdir = process.camConventional;
|
||||||
|
let depthFirst = process.camDepthFirst;
|
||||||
|
let depthData = [];
|
||||||
|
|
||||||
|
setTool(op.tool, op.rate, op.plunge);
|
||||||
|
setSpindle(op.spindle);
|
||||||
|
|
||||||
|
if (!process.camOutlinePocket) {
|
||||||
|
cutdir = !cutdir;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let slice of sliceOut) {
|
||||||
|
let polys = [], t = [], c = [];
|
||||||
|
POLY.flatten(slice.camLines).forEach(function (poly) {
|
||||||
|
let child = poly.parent;
|
||||||
|
if (depthFirst) { poly = poly.clone(); poly.parent = child ? 1 : 0 }
|
||||||
|
if (child) c.push(poly); else t.push(poly);
|
||||||
|
poly.layer = depthData.layer;
|
||||||
|
polys.push(poly);
|
||||||
|
});
|
||||||
|
|
||||||
|
// set cut direction on outer polys
|
||||||
|
POLY.setWinding(t, cutdir);
|
||||||
|
// set cut direction on inner polys
|
||||||
|
POLY.setWinding(c, !cutdir);
|
||||||
|
|
||||||
|
if (depthFirst) {
|
||||||
|
depthData.push(polys);
|
||||||
|
} else {
|
||||||
|
printPoint = poly2polyEmit(polys, printPoint, function(poly, index, count) {
|
||||||
|
poly.forEachPoint(function(point, pidx, points, offset) {
|
||||||
|
camOut(point.clone(), offset !== 0);
|
||||||
|
}, poly.isClosed(), index);
|
||||||
|
});
|
||||||
|
newLayer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depthFirst) {
|
||||||
|
let flatLevels = depthData.map(level => {
|
||||||
|
return POLY.flatten(level.clone(true), [], true).filter(p => !(p.depth = 0));
|
||||||
|
}).filter(l => l.length > 0);
|
||||||
|
// start with the smallest polygon on the top
|
||||||
|
printPoint = flatLevels[0]
|
||||||
|
.sort((a,b) => { return a.area() - b.area() })[0]
|
||||||
|
.average();
|
||||||
|
printPoint = depthOutlinePath(printPoint, 0, flatLevels, toolDiam, polyEmit, false);
|
||||||
|
printPoint = depthOutlinePath(printPoint, 0, flatLevels, toolDiam, polyEmit, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPrintPoint(printPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpContour extends CamOp {
|
||||||
|
constructor(state, op) {
|
||||||
|
super(state, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
slice(progress) {
|
||||||
|
let { op, state } = this;
|
||||||
|
let { sliceAll } = state;
|
||||||
|
// we need topo for safe travel moves when roughing and outlining
|
||||||
|
// not generated when drilling-only. then all z moves use bounds max.
|
||||||
|
// also generates x and y contouring when selected
|
||||||
|
new CAM.Topo({
|
||||||
|
// onupdate: (update, msg) => {
|
||||||
|
onupdate: (index, total, msg) => {
|
||||||
|
progress(index / total, msg);
|
||||||
|
},
|
||||||
|
ondone: (slices) => {
|
||||||
|
this.sliceOut = slices;
|
||||||
|
sliceAll.appendAll(slices);
|
||||||
|
},
|
||||||
|
contour: op,
|
||||||
|
state: state
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare(ops, progress) {
|
||||||
|
let { op, state, sliceOut } = this;
|
||||||
|
let { setTool, setSpindle, setPrintPoint } = ops;
|
||||||
|
let { polyEmit, poly2polyEmit } = ops;
|
||||||
|
let { camOut, newLayer, printPoint } = ops;
|
||||||
|
let { settings, widget } = state;
|
||||||
|
let { process } = settings;
|
||||||
|
|
||||||
|
let toolDiam = this.toolDiam;
|
||||||
|
let cutdir = process.camConventional;
|
||||||
|
let depthFirst = process.camDepthFirst;
|
||||||
|
let depthData = [];
|
||||||
|
|
||||||
|
setTool(op.tool, op.rate, op.plunge);
|
||||||
|
setSpindle(op.spindle);
|
||||||
|
|
||||||
|
printPoint = newPoint(bounds.min.x,bounds.min.y,zmax);
|
||||||
|
|
||||||
|
for (let slice of sliceOut) {
|
||||||
|
let polys = [], poly, emit;
|
||||||
|
slice.camLines.forEach(function (poly) {
|
||||||
|
if (depthFirst) poly = poly.clone(true);
|
||||||
|
polys.push({first:poly.first(), last:poly.last(), poly:poly});
|
||||||
|
});
|
||||||
|
if (depthFirst) {
|
||||||
|
depthData.appendAll(polys);
|
||||||
|
} else {
|
||||||
|
printPoint = tip2tipEmit(polys, printPoint, function(el, point, count) {
|
||||||
|
poly = el.poly;
|
||||||
|
if (poly.last() === point) {
|
||||||
|
poly.reverse();
|
||||||
|
}
|
||||||
|
poly.forEachPoint(function(point, pidx) {
|
||||||
|
camOut(point.clone(), pidx > 0);
|
||||||
|
}, false);
|
||||||
|
});
|
||||||
|
newLayer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depthFirst) {
|
||||||
|
printPoint = tip2tipEmit(depthData, printPoint, function(el, point, count) {
|
||||||
|
let poly = el.poly;
|
||||||
|
if (poly.last() === point) {
|
||||||
|
poly.reverse();
|
||||||
|
}
|
||||||
|
poly.forEachPoint(function(point, pidx) {
|
||||||
|
camOut(point.clone(), pidx > 0);
|
||||||
|
}, false);
|
||||||
|
newLayer();
|
||||||
|
return lastPoint;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setPrintPoint(printPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpTrace extends CamOp {
|
||||||
|
constructor(state, op) {
|
||||||
|
super(state, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
slice(progress) {
|
||||||
|
let { op, state } = this;
|
||||||
|
let { settings, widget, sliceAll, updateToolDiams } = state;
|
||||||
|
|
||||||
|
// generate tracing offsets from chosen features
|
||||||
|
let sliceOut = this.sliceOut = [];
|
||||||
|
let traces = (settings.widget[widget.id] || {}).trace || [];
|
||||||
|
traces.forEach(trace => {
|
||||||
|
let { tool, path } = trace;
|
||||||
|
let traceTool = new CAM.Tool(settings, tool);
|
||||||
|
let traceToolDiam = traceTool.fluteDiameter();
|
||||||
|
let slice = newSlice();
|
||||||
|
let poly = KIRI.codec.decode(path);
|
||||||
|
slice.addTop(poly);
|
||||||
|
slice.camMode = PRO.TRACE;
|
||||||
|
slice.camLines = [ poly ];
|
||||||
|
slice.camTrace = trace;
|
||||||
|
if (true) slice.output()
|
||||||
|
.setLayer("trace", {line: 0xaa00aa}, false)
|
||||||
|
.addPolys(slice.topPolys())
|
||||||
|
sliceAll.push(slice);
|
||||||
|
sliceOut.push(slice);
|
||||||
|
updateToolDiams(traceToolDiam);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare(ops, progress) {
|
||||||
|
for (let slice of this.sliceOut) {
|
||||||
|
ops.emitTrace(slice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpDrill extends CamOp {
|
||||||
|
constructor(state, op) {
|
||||||
|
super(state, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
slice(progress) {
|
||||||
|
let { op, state } = this;
|
||||||
|
let { settings, widget, sliceAll, tslices, updateToolDiams } = state;
|
||||||
|
|
||||||
|
let drills = [],
|
||||||
|
drillTool = new CAM.Tool(settings, op.tool),
|
||||||
|
drillToolDiam = drillTool.fluteDiameter(),
|
||||||
|
centerDiff = drillToolDiam * 0.1,
|
||||||
|
area = (drillToolDiam/2) * (drillToolDiam/2) * Math.PI,
|
||||||
|
areaDelta = area * 0.05,
|
||||||
|
sliceOut = this.sliceOut = [];
|
||||||
|
|
||||||
|
updateToolDiams(drillToolDiam);
|
||||||
|
|
||||||
|
// for each slice, look for polygons with 98.5% circularity whose
|
||||||
|
// area is within the tolerance of a circle matching the tool diameter
|
||||||
|
tslices.forEach(function(slice) {
|
||||||
|
let inner = slice.topPolyInners([]);
|
||||||
|
inner.forEach(function(poly) {
|
||||||
|
if (poly.circularity() >= 0.985 && Math.abs(poly.area() - area) <= areaDelta) {
|
||||||
|
let center = poly.circleCenter(),
|
||||||
|
merged = false,
|
||||||
|
closest = Infinity,
|
||||||
|
dist;
|
||||||
|
// TODO reject if inside camShellPolys (means there is material above)
|
||||||
|
// if (center.isInPolygon(camShellPolys)) return;
|
||||||
|
drills.forEach(function(drill) {
|
||||||
|
if (merged) return;
|
||||||
|
if ((dist = drill.last().distTo2D(center)) <= centerDiff) {
|
||||||
|
merged = true;
|
||||||
|
drill.push(center);
|
||||||
|
}
|
||||||
|
closest = Math.min(closest,dist);
|
||||||
|
});
|
||||||
|
if (!merged) {
|
||||||
|
drills.push(newPolygon().append(center));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// drill points to use center (average of all points) of the polygon
|
||||||
|
drills.forEach(function(drill) {
|
||||||
|
let center = drill.center(true),
|
||||||
|
slice = newSlice(0,null);
|
||||||
|
drill.points.forEach(function(point) {
|
||||||
|
point.x = center.x;
|
||||||
|
point.y = center.y;
|
||||||
|
});
|
||||||
|
slice.camMode = PRO.DRILL;
|
||||||
|
slice.camLines = [ drill ];
|
||||||
|
slice.output()
|
||||||
|
.setLayer("drill", {face: 0, line: 0})
|
||||||
|
.addPolys(drill);
|
||||||
|
sliceAll.push(slice);
|
||||||
|
sliceOut.push(slice);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare(ops, progress) {
|
||||||
|
let { op, state } = this;
|
||||||
|
let { settings, widget, sliceAll, tslices, updateToolDiams } = state;
|
||||||
|
let { setTool, setDrill, emitDrills } = ops;
|
||||||
|
|
||||||
|
setTool(op.tool, op.down, op.rate);
|
||||||
|
setDrill(op.down, op.rate, op.dwell);
|
||||||
|
emitDrills(this.sliceOut.map(slice => slice.camLines).flat());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpRegister extends CamOp {
|
||||||
|
constructor(state, op) {
|
||||||
|
super(state, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
slice(progress) {
|
||||||
|
let { op, state } = this;
|
||||||
|
let { settings, widget, bounds, sliceAll, tslices, updateToolDiams, zThru } = state;
|
||||||
|
|
||||||
|
let tool = new CAM.Tool(settings, op.tool);
|
||||||
|
let sliceOut = this.sliceOut = [];
|
||||||
|
|
||||||
|
updateToolDiams(tool.fluteDiameter());
|
||||||
|
|
||||||
|
let { stock } = settings,
|
||||||
|
o3 = tool.fluteDiameter() * 2,
|
||||||
|
mx = (bounds.max.x + bounds.min.x) / 2,
|
||||||
|
my = (bounds.max.y + bounds.min.y) / 2,
|
||||||
|
mz = zThru || 0,
|
||||||
|
dx = (stock.x - (bounds.max.x - bounds.min.x)) / 4,
|
||||||
|
dy = (stock.y - (bounds.max.y - bounds.min.y)) / 4,
|
||||||
|
dz = stock.z,
|
||||||
|
points = [];
|
||||||
|
|
||||||
|
switch (op.axis) {
|
||||||
|
case "x":
|
||||||
|
if (op.points === 3) {
|
||||||
|
points.push(newPoint(bounds.min.x - dx, my, 0));
|
||||||
|
points.push(newPoint(bounds.max.x + dx, my - o3, 0));
|
||||||
|
points.push(newPoint(bounds.max.x + dx, my + o3, 0));
|
||||||
|
} else {
|
||||||
|
points.push(newPoint(bounds.min.x - dx, my, 0));
|
||||||
|
points.push(newPoint(bounds.max.x + dx, my, 0));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "y":
|
||||||
|
if (op.points === 3) {
|
||||||
|
points.push(newPoint(mx, bounds.min.y - dy, 0));
|
||||||
|
points.push(newPoint(mx - o3, bounds.max.y + dy, 0));
|
||||||
|
points.push(newPoint(mx + o3, bounds.max.y + dy, 0));
|
||||||
|
} else {
|
||||||
|
points.push(newPoint(mx, bounds.min.y - dy, 0));
|
||||||
|
points.push(newPoint(mx, bounds.max.y + dy, 0));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (points.length) {
|
||||||
|
let slice = newSlice(0,null), polys = [];
|
||||||
|
points.forEach(point => {
|
||||||
|
polys.push(newPolygon()
|
||||||
|
.append(point.clone().setZ(bounds.max.z))
|
||||||
|
.append(point.clone().setZ(bounds.max.z - stock.z - mz)));
|
||||||
|
});
|
||||||
|
slice.camMode = PRO.DRILL;
|
||||||
|
slice.camLines = polys;
|
||||||
|
slice.output()
|
||||||
|
.setLayer("register", {face: 0, line: 0})
|
||||||
|
.addPolys(polys);
|
||||||
|
sliceAll.push(slice);
|
||||||
|
sliceOut.push(slice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare(ops, progress) {
|
||||||
|
let { op, state } = this;
|
||||||
|
let { settings, widget, sliceAll, tslices, updateToolDiams } = state;
|
||||||
|
let { setTool, setDrill, emitDrills } = ops;
|
||||||
|
|
||||||
|
setTool(op.tool, op.down, op.rate);
|
||||||
|
setDrill(op.down, op.rate, op.dwell);
|
||||||
|
emitDrills(this.sliceOut.map(slice => slice.camLines).flat());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpXRay extends CamOp {
|
||||||
|
constructor(state, op) {
|
||||||
|
super(state, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
slice(progress) {
|
||||||
|
let { widget, sliceAll } = this.state;
|
||||||
|
let slicer = new KIRI.slicer2(widget.getPoints(), {
|
||||||
|
zlist: true,
|
||||||
|
zline: true
|
||||||
|
});
|
||||||
|
let xrayind = Object.keys(slicer.zLine)
|
||||||
|
.map(v => parseFloat(v).round(5))
|
||||||
|
.sort((a,b) => a-b);
|
||||||
|
let xrayopt = { each: (data, index, total) => {
|
||||||
|
let slice = newSlice(data.z);
|
||||||
|
slice.addTops(data.tops);
|
||||||
|
// data.tops.forEach(top => slice.addTop(top));
|
||||||
|
slice.lines = data.lines;
|
||||||
|
slice.xray();
|
||||||
|
sliceAll.push(slice);
|
||||||
|
}, over: false, flatoff: 0, edges: true, openok: true };
|
||||||
|
slicer.slice(xrayind, xrayopt);
|
||||||
|
// xrayopt.over = true;
|
||||||
|
// slicer.slice(xrayind, xrayopt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpShadow extends CamOp {
|
||||||
|
constructor(state, op) {
|
||||||
|
super(state, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
slice(progress) {
|
||||||
|
let state = this.state;
|
||||||
|
let { ops, slicer } = state;
|
||||||
|
|
||||||
|
let real = ops.map(rec => rec.op).filter(op => op);
|
||||||
|
let rough = real.map(op => op.type === 'rough').length > 0;
|
||||||
|
let outlineIn = real.map(op => op.type === 'outline' && op.inside).length > 0;
|
||||||
|
|
||||||
|
let minStepDown = real
|
||||||
|
.map(op => (op.down || 3) / 3)
|
||||||
|
.reduce((a,v) => Math.min(a, v, 1));
|
||||||
|
|
||||||
|
let tslices = [];
|
||||||
|
let tshadow = [];
|
||||||
|
let tzindex = slicer.interval(minStepDown, { fit: true, off: 0.01, down: true, flats: true });
|
||||||
|
let skipTerrain = !(rough || outlineIn) && tzindex.length > 50;
|
||||||
|
|
||||||
|
if (skipTerrain) {
|
||||||
|
console.log("skipping terrain generation for speed");
|
||||||
|
tzindex = [ tzindex.pop() ];
|
||||||
|
}
|
||||||
|
|
||||||
|
let terrain = slicer.slice(tzindex, { each: (data, index, total) => {
|
||||||
|
tshadow = POLY.union(tshadow.slice().appendAll(data.tops), 0.01, true);
|
||||||
|
tslices.push(data.slice);
|
||||||
|
if (false) {
|
||||||
|
const slice = data.slice;
|
||||||
|
sliceAll.push(slice);
|
||||||
|
slice.output()
|
||||||
|
.setLayer("terrain", {line: 0x888800, thin: true })
|
||||||
|
.addPolys(POLY.setZ(tshadow.clone(true), data.z), { thin: true });
|
||||||
|
}
|
||||||
|
progress(index / total);
|
||||||
|
}, genso: true });
|
||||||
|
|
||||||
|
state.shadowTop = terrain[terrain.length - 1];
|
||||||
|
state.center = tshadow[0].bounds.center();
|
||||||
|
state.tshadow = tshadow;
|
||||||
|
state.terrain = terrain;
|
||||||
|
state.tslices = tslices;
|
||||||
|
state.skipTerrain = skipTerrain;
|
||||||
|
|
||||||
|
// identify through holes
|
||||||
|
state.thruHoles = tshadow.map(p => p.inner || []).flat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CAM.OPS = CamOp.MAP = {
|
||||||
|
"xray": OpXRay,
|
||||||
|
"shadow": OpShadow,
|
||||||
|
"rough": OpRough,
|
||||||
|
"outline": OpOutline,
|
||||||
|
"contour x": OpContour,
|
||||||
|
"contour y": OpContour,
|
||||||
|
"trace": OpTrace,
|
||||||
|
"drill": OpDrill,
|
||||||
|
"register": OpRegister
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
@ -49,6 +49,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
function prepEach(widget, settings, print, firstPoint, update) {
|
function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
|
|
||||||
let device = settings.device,
|
let device = settings.device,
|
||||||
process = settings.process,
|
process = settings.process,
|
||||||
stock = settings.stock,
|
stock = settings.stock,
|
||||||
|
|
@ -74,12 +75,10 @@
|
||||||
output = print.output,
|
output = print.output,
|
||||||
easeDown = process.camEaseDown,
|
easeDown = process.camEaseDown,
|
||||||
depthFirst = process.camDepthFirst,
|
depthFirst = process.camDepthFirst,
|
||||||
tolerance = process.camTolerance,
|
tolerance = 0,
|
||||||
contour = process.camContourXOn || process.camContourYOn,
|
drillDown = 0,
|
||||||
contourCurves = process.camContourCurves,
|
drillLift = 0,
|
||||||
drillDown = process.camDrillDown,
|
drillDwell = 0,
|
||||||
drillLift = process.camDrillLift,
|
|
||||||
drillDwell = process.camDrillDwell,
|
|
||||||
newOutput = print.output || [],
|
newOutput = print.output || [],
|
||||||
layerOut = [],
|
layerOut = [],
|
||||||
printPoint,
|
printPoint,
|
||||||
|
|
@ -117,6 +116,19 @@
|
||||||
layerOut.spindle = spindle;
|
layerOut.spindle = spindle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// non-zero means contouring
|
||||||
|
function setTolerance(dist) {
|
||||||
|
tolerance = dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPrintPoint(point) {
|
||||||
|
printPoint = point;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSpindle(speed) {
|
||||||
|
spindle = Math.min(speed, spindleMax);
|
||||||
|
}
|
||||||
|
|
||||||
function setTool(toolID, feed, plunge) {
|
function setTool(toolID, feed, plunge) {
|
||||||
if (toolID !== lastTool) {
|
if (toolID !== lastTool) {
|
||||||
tool = new CAM.Tool(settings, toolID);
|
tool = new CAM.Tool(settings, toolID);
|
||||||
|
|
@ -129,6 +141,12 @@
|
||||||
plungeRate = plunge;
|
plungeRate = plunge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setDrill(down, lift, dwell) {
|
||||||
|
drillDown = down;
|
||||||
|
drillLift = lift;
|
||||||
|
drillDwell = dwell;
|
||||||
|
}
|
||||||
|
|
||||||
function emitDrills(polys) {
|
function emitDrills(polys) {
|
||||||
polys = polys.slice();
|
polys = polys.slice();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
@ -180,6 +198,7 @@
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
camOut(point.clone().setZ(zmax));
|
||||||
points.forEach(function(point, index) {
|
points.forEach(function(point, index) {
|
||||||
camOut(point, 1);
|
camOut(point, 1);
|
||||||
if (index > 0 && index < points.length - 1) {
|
if (index > 0 && index < points.length - 1) {
|
||||||
|
|
@ -246,7 +265,7 @@
|
||||||
|
|
||||||
// convert short planar moves to cuts in some cases
|
// convert short planar moves to cuts in some cases
|
||||||
if (isMove && deltaXY <= toolDiamMove) {
|
if (isMove && deltaXY <= toolDiamMove) {
|
||||||
let iscontour = lastMode === PRO.CONTOUR_X || lastMode === PRO.CONTOUR_Y;
|
let iscontour = tolerance > 0;
|
||||||
let isflat = absDeltaZ < 0.001;
|
let isflat = absDeltaZ < 0.001;
|
||||||
// restrict this to contouring
|
// restrict this to contouring
|
||||||
if (isflat || (iscontour && absDeltaZ <= tolerance)) {
|
if (isflat || (iscontour && absDeltaZ <= tolerance)) {
|
||||||
|
|
@ -334,8 +353,37 @@
|
||||||
layer: 0,
|
layer: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let ops = {
|
||||||
|
setTool,
|
||||||
|
setDrill,
|
||||||
|
setSpindle,
|
||||||
|
setPrintPoint,
|
||||||
|
printPoint,
|
||||||
|
newLayer,
|
||||||
|
camOut,
|
||||||
|
polyEmit,
|
||||||
|
poly2polyEmit,
|
||||||
|
depthRoughPath,
|
||||||
|
depthOutlinePath,
|
||||||
|
emitDrills,
|
||||||
|
emitTrace
|
||||||
|
};
|
||||||
|
|
||||||
|
let opSum = 0;
|
||||||
|
let opTot = widget.camops.map(op => op.weight()).reduce((a,v) => a + v);
|
||||||
|
|
||||||
|
for (let op of widget.camops) {
|
||||||
|
setTolerance(0);
|
||||||
|
nextIsMove = true;
|
||||||
|
let weight = op.weight();
|
||||||
|
op.prepare(ops, (progress, message) => {
|
||||||
|
onupdate((opSum + (progress * weight)) / opTot, message || op.type());
|
||||||
|
});
|
||||||
|
opSum += weight;
|
||||||
|
}
|
||||||
|
|
||||||
// todo first move into positon
|
// todo first move into positon
|
||||||
slices.forEach(function(slice, sliceIndex) {
|
if (false) slices.forEach(function(slice, sliceIndex) {
|
||||||
depthData.layer++;
|
depthData.layer++;
|
||||||
isNewMode = slice.camMode != lastMode;
|
isNewMode = slice.camMode != lastMode;
|
||||||
lastMode = slice.camMode;
|
lastMode = slice.camMode;
|
||||||
|
|
@ -534,7 +582,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// act on accumulated layer data
|
// act on accumulated layer data
|
||||||
if (depthFirst) {
|
if (false && depthFirst) {
|
||||||
// roughing depth first
|
// roughing depth first
|
||||||
if (depthData.rough.length > 0) {
|
if (depthData.rough.length > 0) {
|
||||||
lastMode = PRO.ROUGH;
|
lastMode = PRO.ROUGH;
|
||||||
|
|
@ -620,7 +668,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// drilling is always depth first, and always output last (change?)
|
// drilling is always depth first, and always output last (change?)
|
||||||
if (depthData.drill.length > 0) {
|
if (false && depthData.drill.length > 0) {
|
||||||
lastMode = PRO.DRILL;
|
lastMode = PRO.DRILL;
|
||||||
setTool(process.camDrillTool, process.camDrillDownSpeed, process.camDrillDownSpeed);
|
setTool(process.camDrillTool, process.camDrillDownSpeed, process.camDrillDownSpeed);
|
||||||
emitDrills(depthData.drill);
|
emitDrills(depthData.drill);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
BASE = self.base,
|
BASE = self.base,
|
||||||
UTIL = BASE.util,
|
UTIL = BASE.util,
|
||||||
CAM = KIRI.driver.CAM,
|
CAM = KIRI.driver.CAM,
|
||||||
|
OPS = CAM.OPS,
|
||||||
PRO = CAM.process,
|
PRO = CAM.process,
|
||||||
POLY = BASE.polygons,
|
POLY = BASE.polygons,
|
||||||
newSlice = KIRI.newSlice,
|
newSlice = KIRI.newSlice,
|
||||||
|
|
@ -26,15 +27,9 @@
|
||||||
proc = conf.process,
|
proc = conf.process,
|
||||||
stock = settings.stock || {},
|
stock = settings.stock || {},
|
||||||
hasStock = stock.x && stock.y && stock.z && proc.camStockOn,
|
hasStock = stock.x && stock.y && stock.z && proc.camStockOn,
|
||||||
|
camOps = widget.camops = [],
|
||||||
sliceAll = widget.slices = [],
|
sliceAll = widget.slices = [],
|
||||||
unitsName = settings.controller.units,
|
unitsName = settings.controller.units,
|
||||||
roughTool = new CAM.Tool(conf, proc.camRoughTool),
|
|
||||||
roughToolDiam = roughTool.fluteDiameter(),
|
|
||||||
drillTool = new CAM.Tool(conf, proc.camDrillTool),
|
|
||||||
drillToolDiam = drillTool.fluteDiameter(),
|
|
||||||
procFacing = proc.camRoughOn && proc.camZTopOffset && hasStock,
|
|
||||||
procRough = proc.camRoughOn && proc.camRoughDown,
|
|
||||||
procRoughIn = proc.camRoughIn,
|
|
||||||
procOutlineWide = proc.camOutlineWide,
|
procOutlineWide = proc.camOutlineWide,
|
||||||
procOutlineOut = proc.camOutlineOut,
|
procOutlineOut = proc.camOutlineOut,
|
||||||
procOutlineIn = proc.camOutlineIn,
|
procOutlineIn = proc.camOutlineIn,
|
||||||
|
|
@ -45,9 +40,7 @@
|
||||||
procContour = procContourX || procContourY,
|
procContour = procContourX || procContourY,
|
||||||
procDrill = proc.camDrillingOn && proc.camDrillDown && proc.camDrillDownSpeed,
|
procDrill = proc.camDrillingOn && proc.camDrillDown && proc.camDrillDownSpeed,
|
||||||
procDrillReg = proc.camDrillReg,
|
procDrillReg = proc.camDrillReg,
|
||||||
roughDown = procRough ? proc.camRoughDown : Infinity,
|
|
||||||
outlineDown = procOutline ? proc.camOutlineDown : Infinity,
|
outlineDown = procOutline ? proc.camOutlineDown : Infinity,
|
||||||
sliceDepth = Math.max(0.1, Math.min(roughDown, outlineDown) / 3),
|
|
||||||
bounds = widget.getBoundingBox(),
|
bounds = widget.getBoundingBox(),
|
||||||
mesh = widget.mesh,
|
mesh = widget.mesh,
|
||||||
zBottom = proc.camZBottom,
|
zBottom = proc.camZBottom,
|
||||||
|
|
@ -55,9 +48,6 @@
|
||||||
zMax = bounds.max.z,
|
zMax = bounds.max.z,
|
||||||
zThru = zBottom === 0 ? (proc.camZThru || 0) : 0,
|
zThru = zBottom === 0 ? (proc.camZThru || 0) : 0,
|
||||||
ztOff = hasStock ? proc.camZTopOffset : 0,
|
ztOff = hasStock ? proc.camZTopOffset : 0,
|
||||||
camRoughStock = proc.camRoughStock,
|
|
||||||
camRoughDown = proc.camRoughDown,
|
|
||||||
minStepDown = Math.min(1, roughDown/3, outlineDown/3),
|
|
||||||
minToolDiam = Infinity,
|
minToolDiam = Infinity,
|
||||||
maxToolDiam = -Infinity,
|
maxToolDiam = -Infinity,
|
||||||
thruHoles,
|
thruHoles,
|
||||||
|
|
@ -83,6 +73,10 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!proc.ops || proc.ops.length === 0) {
|
||||||
|
return ondone('no processes specified');
|
||||||
|
}
|
||||||
|
|
||||||
if (stock.x && stock.y && stock.z) {
|
if (stock.x && stock.y && stock.z) {
|
||||||
if (stock.x + 0.00001 < bounds.max.x - bounds.min.x) {
|
if (stock.x + 0.00001 < bounds.max.x - bounds.min.x) {
|
||||||
return ondone('stock X too small for part. disable stock or use offset stock');
|
return ondone('stock X too small for part. disable stock or use offset stock');
|
||||||
|
|
@ -97,469 +91,76 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sliceDepth <= 0.05) {
|
|
||||||
return ondone(`invalid slice depth (${sliceDepth.toFixed(2)} ${unitsName})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(procFacing || procRough || procOutline || procContour || procDrill || procDrillReg)) {
|
|
||||||
return ondone("no processes selected");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zMin >= bounds.max.z) {
|
if (zMin >= bounds.max.z) {
|
||||||
return ondone(`invalid z bottom >= bounds z max ${bounds.max.z}`);
|
return ondone(`invalid z bottom >= bounds z max ${bounds.max.z}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// allows progress output to me weighted and matched to processes
|
|
||||||
let ops = [ [ "mapping", 1.5 ] ];
|
|
||||||
if (procRough) ops.push([ "roughing", 1 ]);
|
|
||||||
if (procRough) ops.push([ "rough offset", 1 ]);
|
|
||||||
if (procOutline) ops.push([ "outline", 0.5 ]);
|
|
||||||
if (procContour) ops.push([ "contour", 4 ]);
|
|
||||||
let opsTot = ops.map(op => op[1]).reduce((a,v) => a + v);
|
|
||||||
let opSum = 0;
|
|
||||||
let opTot;
|
|
||||||
let opOn;
|
|
||||||
|
|
||||||
function nextOp() {
|
|
||||||
if (opOn) opSum += opOn[1];
|
|
||||||
opOn = ops.shift();
|
|
||||||
opTot = opOn[1] / opsTot;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateOp(index, total, msg) {
|
|
||||||
onupdate((opSum/opsTot) + (index/total) * opTot, msg || opOn[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateToolDiams(toolDiam) {
|
|
||||||
minToolDiam = Math.min(minToolDiam, toolDiam);
|
|
||||||
maxToolDiam = Math.max(maxToolDiam, toolDiam);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mark = Date.now();
|
let mark = Date.now();
|
||||||
let slicer = new KIRI.slicer2(widget.getPoints(), {
|
let slicer = new KIRI.slicer2(widget.getPoints(), {
|
||||||
zlist: true,
|
zlist: true,
|
||||||
zline: true
|
zline: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// xray debug
|
function updateToolDiams(toolDiam) {
|
||||||
|
minToolDiam = Math.min(minToolDiam, toolDiam);
|
||||||
|
maxToolDiam = Math.max(maxToolDiam, toolDiam);
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = {
|
||||||
|
settings,
|
||||||
|
widget,
|
||||||
|
bounds,
|
||||||
|
tabs,
|
||||||
|
cutTabs,
|
||||||
|
slicer,
|
||||||
|
sliceAll,
|
||||||
|
updateToolDiams,
|
||||||
|
zBottom,
|
||||||
|
zThru,
|
||||||
|
ztOff,
|
||||||
|
zMax
|
||||||
|
};
|
||||||
|
|
||||||
|
let opList = [
|
||||||
|
new CAM.OPS.shadow(state, { type: "shadow" })
|
||||||
|
];
|
||||||
|
|
||||||
if (false) {
|
if (false) {
|
||||||
console.log({slicer_setup: Date.now() - mark});
|
opList.push(new CAM.OPS.xray(state, { type: "xray" }));
|
||||||
let xlicer = new KIRI.slicer2(widget.getPoints(), {
|
|
||||||
zlist: true,
|
|
||||||
zline: true
|
|
||||||
});
|
|
||||||
let xrayind = Object.keys(xlicer.zLine)
|
|
||||||
.map(v => parseFloat(v).round(5))
|
|
||||||
.sort((a,b) => a-b);
|
|
||||||
let xrayopt = { each: (data, index, total) => {
|
|
||||||
let slice = newSlice(data.z);
|
|
||||||
slice.addTops(data.tops);
|
|
||||||
// data.tops.forEach(top => slice.addTop(top));
|
|
||||||
slice.lines = data.lines;
|
|
||||||
slice.xray();
|
|
||||||
sliceAll.push(slice);
|
|
||||||
}, over: false, flatoff: 0, edges: true, openok: true };
|
|
||||||
xlicer.slice(xrayind, xrayopt);
|
|
||||||
// xrayopt.over = true;
|
|
||||||
// slicer.slice(xrayind, xrayopt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nextOp();
|
let opSum = 0;
|
||||||
let tslices = [];
|
let opTot = opList.map(op => op.weight()).reduce((a,v) => a + v);
|
||||||
let tshadow = [];
|
|
||||||
let tzindex = slicer.interval(minStepDown, { fit: true, off: 0.01, down: true, flats: true });
|
|
||||||
let skipTerrain = !(procRough || procOutlineIn) && tzindex.length > 50;
|
|
||||||
|
|
||||||
if (skipTerrain) {
|
for (let op of proc.ops) {
|
||||||
console.log("skipping terrain generation for speed");
|
let opfn = OPS[op.type];
|
||||||
tzindex = [ tzindex.pop() ];
|
if (opfn) {
|
||||||
}
|
let opin = new opfn(state, op);
|
||||||
|
opList.push(opin);
|
||||||
let terrain = slicer.slice(tzindex, { each: (data, index, total) => {
|
opTot += opin.weight();
|
||||||
tshadow = POLY.union(tshadow.slice().appendAll(data.tops), 0.01, true);
|
|
||||||
tslices.push(data.slice);
|
|
||||||
if (false) {
|
|
||||||
const slice = data.slice;
|
|
||||||
sliceAll.push(slice);
|
|
||||||
slice.output()
|
|
||||||
.setLayer("terrain", {line: 0x888800, thin: true })
|
|
||||||
.addPolys(POLY.setZ(tshadow.clone(true), data.z), { thin: true });
|
|
||||||
}
|
|
||||||
updateOp(index, total);
|
|
||||||
}, genso: true });
|
|
||||||
|
|
||||||
let shadowTop = terrain[terrain.length - 1];
|
|
||||||
let center = tshadow[0].bounds.center();
|
|
||||||
|
|
||||||
if (procDrillReg) {
|
|
||||||
updateToolDiams(drillToolDiam);
|
|
||||||
sliceDrillReg(settings, drillTool, sliceAll, zThru);
|
|
||||||
}
|
|
||||||
|
|
||||||
// identify through holes
|
|
||||||
thruHoles = tshadow.map(p => p.inner || []).flat();
|
|
||||||
|
|
||||||
// create facing slices
|
|
||||||
if (procFacing || proc.camRoughTop) {
|
|
||||||
let shadow = tshadow.clone();
|
|
||||||
let inset = POLY.offset(shadow, (roughToolDiam / (procRoughIn ? 2 : 1)));
|
|
||||||
let facing = POLY.offset(inset, -(roughToolDiam * proc.camRoughOver), { count: 999, flat: true });
|
|
||||||
let zdiv = ztOff / roughDown;
|
|
||||||
let zstep = (zdiv % 1 > 0) ? ztOff / (Math.floor(zdiv) + 1) : roughDown;
|
|
||||||
if (proc.camRoughTop && ztOff === 0) {
|
|
||||||
// compensate for lack of z top offset in this scenario
|
|
||||||
ztOff = zstep;
|
|
||||||
}
|
|
||||||
for (let z = zMax + ztOff - zstep; z >= zMax; z -= zstep) {
|
|
||||||
let slice = shadowTop.slice.clone(false);
|
|
||||||
slice.z = z;
|
|
||||||
slice.camMode = PRO.LEVEL;
|
|
||||||
slice.camLines = POLY.setZ(facing.clone(true), slice.z);
|
|
||||||
slice.output()
|
|
||||||
.setLayer("facing", {face: 0, line: 0})
|
|
||||||
.addPolys(slice.camLines);
|
|
||||||
sliceAll.push(slice);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create roughing slices
|
// give ops access to entire sequence
|
||||||
if (procRough) {
|
state.ops = opList;
|
||||||
nextOp();
|
|
||||||
updateToolDiams(roughToolDiam);
|
|
||||||
|
|
||||||
let flats = [];
|
for (let op of opList) {
|
||||||
let shadow = [];
|
let weight = op.weight();
|
||||||
let slices = [];
|
op.slice((progress, message) => {
|
||||||
let indices = slicer.interval(roughDown, { down: true, min: zBottom, fit: true, off: 0.01 });
|
onupdate((opSum + (progress * weight)) / opTot, message || op.type());
|
||||||
// shift out first (top-most) slice
|
|
||||||
indices.shift();
|
|
||||||
if (proc.camRoughFlat) {
|
|
||||||
let flats = Object.keys(slicer.zFlat)
|
|
||||||
.map(v => parseFloat(v).round(4))
|
|
||||||
.filter(v => v >= zBottom);
|
|
||||||
flats.forEach(v => {
|
|
||||||
if (!indices.contains(v)) {
|
|
||||||
indices.push(v);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
indices = indices.sort((a,b) => { return b - a });
|
|
||||||
// if layer is not on a flat and next one is,
|
|
||||||
// then move this layer up to mid-point to previous layer
|
|
||||||
// this is not perfect. the best method is to interpolate
|
|
||||||
// between flats so that each step is < step down. on todo list
|
|
||||||
for (let i=1; i<indices.length-1; i++) {
|
|
||||||
const prev = indices[i-1];
|
|
||||||
const curr = indices[i];
|
|
||||||
const next = indices[i+1];
|
|
||||||
if (!flats.contains(curr) && flats.contains(next)) {
|
|
||||||
// console.log('move',curr,'up toward',prev,'b/c next',next,'is flat');
|
|
||||||
indices[i] = next + ((prev - next) / 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// add flats to shadow
|
|
||||||
flats = Object.keys(slicer.zFlat)
|
|
||||||
.map(v => (parseFloat(v) - 0.01).round(5))
|
|
||||||
.filter(v => v > 0 && indices.indexOf(v) < 0);
|
|
||||||
indices = indices.appendAll(flats).sort((a,b) => b-a);
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log('indices', ...indices, {zBottom});
|
|
||||||
slicer.slice(indices, { each: (data, index, total) => {
|
|
||||||
shadow = POLY.union(shadow.slice().appendAll(data.tops), 0.01, true);
|
|
||||||
if (flats.indexOf(data.z) >= 0) {
|
|
||||||
// exclude flats injected to complete shadow
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
data.shadow = shadow.clone(true);
|
|
||||||
data.slice.camMode = PRO.ROUGH;
|
|
||||||
data.slice.shadow = data.shadow;
|
|
||||||
// data.slice.tops[0].inner = data.shadow;
|
|
||||||
// data.slice.tops[0].inner = POLY.setZ(tshadow.clone(true), data.z);
|
|
||||||
slices.push(data.slice);
|
|
||||||
updateOp(index, total);
|
|
||||||
}, genso: true });
|
|
||||||
|
|
||||||
shadow = POLY.union(shadow.appendAll(shadowTop.tops), 0.01, 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));
|
|
||||||
let po = POLY.offset([p], -(roughToolDiam / 2 + camRoughStock + 0.001));
|
|
||||||
return po ? po[0] : undefined;
|
|
||||||
} else {
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}).filter(p => p);
|
|
||||||
});
|
});
|
||||||
shadow = POLY.nest(shadow);
|
camOps.push(op);
|
||||||
|
opSum += weight;
|
||||||
// expand shadow by half tool diameter + stock to leave
|
|
||||||
// const sadd = procRoughIn ? roughToolDiam / 4 : roughToolDiam / 2;
|
|
||||||
const sadd = procRoughIn ? roughToolDiam / 2 : roughToolDiam / 2;
|
|
||||||
const shell = POLY.offset(shadow, sadd + camRoughStock);
|
|
||||||
|
|
||||||
nextOp();
|
|
||||||
slices.forEach((slice, index) => {
|
|
||||||
let offset = [shell.clone(true),slice.shadow.clone(true)].flat();
|
|
||||||
let flat = POLY.flatten(offset, [], true);
|
|
||||||
let nest = POLY.setZ(POLY.nest(flat), slice.z);
|
|
||||||
|
|
||||||
// inset offset array by 1/2 diameter then by tool overlap %
|
|
||||||
offset = POLY.offset(nest, [-(roughToolDiam / 2 + camRoughStock), -roughToolDiam * proc.camRoughOver], {
|
|
||||||
minArea: 0,
|
|
||||||
z: slice.z,
|
|
||||||
count: 999,
|
|
||||||
flat: true,
|
|
||||||
call: (polys, count, depth) => {
|
|
||||||
// used in depth-first path creation
|
|
||||||
polys.forEach(p => {
|
|
||||||
p.depth = depth;
|
|
||||||
if (p.inner) {
|
|
||||||
p.inner.forEach(p => p.depth = depth);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}) || [];
|
|
||||||
|
|
||||||
// add outside pass if not inside only
|
|
||||||
if (!procRoughIn) {
|
|
||||||
const outside = POLY.offset(shadow.clone(), roughToolDiam * proc.camRoughOver, {z: slice.z});
|
|
||||||
if (outside) {
|
|
||||||
offset.appendAll(outside);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tabs) {
|
|
||||||
tabs.forEach(tab => {
|
|
||||||
tab.off = POLY.expand([tab.poly], roughToolDiam / 2).flat();
|
|
||||||
});
|
|
||||||
offset = cutTabs(tabs, offset, slice.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!offset) return;
|
|
||||||
|
|
||||||
// elimate double inset on inners
|
|
||||||
offset.forEach(op => {
|
|
||||||
if (op.inner) {
|
|
||||||
let operim = op.perimeter();
|
|
||||||
let newinner = [];
|
|
||||||
op.inner.forEach(oi => {
|
|
||||||
if (Math.abs(oi.perimeter() - operim) > 0.01) {
|
|
||||||
newinner.push(oi);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
op.inner = newinner;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
slice.camLines = offset;
|
|
||||||
if (true) slice.output()
|
|
||||||
.setLayer("slice", {line: 0xaaaa00}, true)
|
|
||||||
.addPolys(slice.topPolys())
|
|
||||||
// .setLayer("top shadow", {line: 0x0000aa})
|
|
||||||
// .addPolys(tshadow)
|
|
||||||
// .setLayer("rough shadow", {line: 0x00aa00})
|
|
||||||
// .addPolys(shadow)
|
|
||||||
// .setLayer("rough shell", {line: 0xaa0000})
|
|
||||||
// .addPolys(shell);
|
|
||||||
slice.output()
|
|
||||||
.setLayer("roughing", {face: 0, line: 0})
|
|
||||||
.addPolys(offset);
|
|
||||||
updateOp(index, slices.length);
|
|
||||||
});
|
|
||||||
|
|
||||||
sliceAll.appendAll(slices.filter(slice => slice.camLines));
|
|
||||||
}
|
|
||||||
|
|
||||||
// create outline slices
|
|
||||||
if (procOutline) {
|
|
||||||
nextOp();
|
|
||||||
let outlineTool = new CAM.Tool(conf, proc.camOutlineTool);
|
|
||||||
let outlineToolDiam = outlineTool.fluteDiameter();
|
|
||||||
updateToolDiams(outlineToolDiam);
|
|
||||||
|
|
||||||
let shadow = [];
|
|
||||||
let slices = [];
|
|
||||||
let indices = slicer.interval(outlineDown, { down: true, min: zBottom, fit: true, off: 0.01 });
|
|
||||||
// shift out first (top-most) slice
|
|
||||||
indices.shift();
|
|
||||||
// add flats to shadow
|
|
||||||
const flats = Object.keys(slicer.zFlat)
|
|
||||||
.map(v => (parseFloat(v) - 0.01).round(5))
|
|
||||||
.filter(v => v > 0 && indices.indexOf(v) < 0);
|
|
||||||
indices = indices.appendAll(flats).sort((a,b) => b-a);
|
|
||||||
// console.log('indices', ...indices, {zBottom, slicer});
|
|
||||||
if (procOutlineOut && !procOutlineIn) {
|
|
||||||
console.log({outline_bypass: indices, outlineDown});
|
|
||||||
indices.forEach((ind,i) => {
|
|
||||||
if (flats.indexOf(ind) >= 0) {
|
|
||||||
// exclude flats
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let slice = newSlice(ind);
|
|
||||||
slice.camMode = PRO.OUTLINE;
|
|
||||||
slice.shadow = shadow.clone(true);
|
|
||||||
slices.push(slice);
|
|
||||||
updateOp(i, indices.length);
|
|
||||||
});
|
|
||||||
} else
|
|
||||||
slicer.slice(indices, { each: (data, index, total) => {
|
|
||||||
shadow = POLY.union(shadow.slice().appendAll(data.tops), 0.01, true);
|
|
||||||
if (flats.indexOf(data.z) >= 0) {
|
|
||||||
// exclude flats injected to complete shadow
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
data.shadow = shadow.clone(true);
|
|
||||||
data.slice.camMode = PRO.OUTLINE;
|
|
||||||
data.slice.shadow = data.shadow;
|
|
||||||
// data.slice.tops[0].inner = data.shadow;
|
|
||||||
// data.slice.tops[0].inner = POLY.setZ(tshadow.clone(true), data.z);
|
|
||||||
slices.push(data.slice);
|
|
||||||
// data.slice.xray();
|
|
||||||
// onupdate(0.2 + (index/total) * 0.1, "outlines");
|
|
||||||
updateOp(index, total);
|
|
||||||
}, genso: true });
|
|
||||||
shadow = POLY.union(shadow.appendAll(shadowTop.tops), 0.01, true);
|
|
||||||
|
|
||||||
// extend cut thru (only when z bottom is 0)
|
|
||||||
if (zThru) {
|
|
||||||
let last = slices[slices.length-1];
|
|
||||||
let add = last.clone(true);
|
|
||||||
add.camMode = last.camMode;
|
|
||||||
add.tops.forEach(top => {
|
|
||||||
top.poly.setZ(add.z);
|
|
||||||
});
|
|
||||||
add.shadow = last.shadow.clone(true);
|
|
||||||
add.z -= zThru;
|
|
||||||
slices.push(add);
|
|
||||||
}
|
|
||||||
|
|
||||||
slices.forEach(slice => {
|
|
||||||
let tops = slice.shadow;
|
|
||||||
|
|
||||||
// outside only (use tshadow for entire cut)
|
|
||||||
if (procOutlineOut) {
|
|
||||||
tops = tshadow;
|
|
||||||
}
|
|
||||||
|
|
||||||
let offset = POLY.expand(tops, outlineToolDiam / 2, slice.z);
|
|
||||||
if (!(offset && offset.length)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// when pocket only, drop first outer poly
|
|
||||||
// if it matches the shell and promote inner polys
|
|
||||||
if (procOutlineIn) {
|
|
||||||
let shell = POLY.expand(tops.clone(), outlineToolDiam / 2);
|
|
||||||
offset = POLY.filter(offset, [], function(poly) {
|
|
||||||
if (poly.area() < 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (let sp=0; sp<shell.length; sp++) {
|
|
||||||
// eliminate shell only polys
|
|
||||||
if (poly.isEquivalent(shell[sp])) {
|
|
||||||
if (poly.inner) return poly.inner;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return poly;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (procOutlineWide) {
|
|
||||||
offset.slice().forEach(op => {
|
|
||||||
// clone removes inners but the real solution is
|
|
||||||
// to limit expanded shells to through holes
|
|
||||||
POLY.expand([op.clone(true)], outlineToolDiam * 0.5, slice.z, offset, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tabs) {
|
|
||||||
tabs.forEach(tab => {
|
|
||||||
tab.off = POLY.expand([tab.poly], outlineToolDiam / 2).flat();
|
|
||||||
});
|
|
||||||
offset = cutTabs(tabs, offset, slice.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proc.camOutlineDogbone && !procOutlineWide) {
|
|
||||||
CAM.addDogbones(offset, outlineToolDiam / 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
// offset.xout(`slice ${slice.z}`);
|
|
||||||
slice.camLines = offset;
|
|
||||||
if (false) slice.output()
|
|
||||||
.setLayer("slice", {line: 0xaaaa00}, false)
|
|
||||||
.addPolys(slice.topPolys())
|
|
||||||
slice.output()
|
|
||||||
.setLayer("outline", {face: 0, line: 0})
|
|
||||||
.addPolys(offset);
|
|
||||||
});
|
|
||||||
|
|
||||||
sliceAll.appendAll(slices);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we need topo for safe travel moves when roughing and outlining
|
|
||||||
// not generated when drilling-only. then all z moves use bounds max.
|
|
||||||
// also generates x and y contouring when selected
|
|
||||||
if (procContour) {
|
|
||||||
nextOp();
|
|
||||||
new CAM.Topo(widget, settings, {
|
|
||||||
// onupdate: (update, msg) => {
|
|
||||||
onupdate: (index, total, msg) => {
|
|
||||||
updateOp(index, total, msg);
|
|
||||||
// onupdate(0.30 + update * 0.50, msg || "create topo");
|
|
||||||
},
|
|
||||||
ondone: (slices) => {
|
|
||||||
sliceAll.appendAll(slices);
|
|
||||||
},
|
|
||||||
shadow: tshadow,
|
|
||||||
center: center,
|
|
||||||
tabs
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate tracing offsets from chosen features
|
|
||||||
{
|
|
||||||
let proc = settings.process;
|
|
||||||
let traces = (settings.widget[widget.id] || {}).trace || [];
|
|
||||||
traces.forEach(trace => {
|
|
||||||
let { tool, path } = trace;
|
|
||||||
let traceTool = new CAM.Tool(settings, tool);
|
|
||||||
let traceToolDiam = traceTool.fluteDiameter();
|
|
||||||
let slice = newSlice();
|
|
||||||
let poly = KIRI.codec.decode(path);
|
|
||||||
slice.addTop(poly);
|
|
||||||
slice.camMode = PRO.TRACE;
|
|
||||||
slice.camLines = [ poly ];
|
|
||||||
slice.camTrace = trace;
|
|
||||||
if (true) slice.output()
|
|
||||||
.setLayer("trace", {line: 0xaa00aa}, false)
|
|
||||||
.addPolys(slice.topPolys())
|
|
||||||
sliceAll.push(slice);
|
|
||||||
updateToolDiams(traceToolDiam);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (procDrill) {
|
|
||||||
updateToolDiams(drillToolDiam);
|
|
||||||
sliceDrill(drillTool, tslices, sliceAll);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reindex
|
||||||
sliceAll.forEach((slice, index) => slice.index = index);
|
sliceAll.forEach((slice, index) => slice.index = index);
|
||||||
|
|
||||||
// used in printSetup()
|
// used in printSetup()
|
||||||
// used in CAM.prepare.getZClearPath()
|
// used in CAM.prepare.getZClearPath()
|
||||||
// add tabs to terrain tops so moves avoid them
|
// add tabs to terrain tops so moves avoid them
|
||||||
if (tabs) {
|
if (tabs) {
|
||||||
terrain.forEach(slab => {
|
state.terrain.forEach(slab => {
|
||||||
tabs.forEach(tab => {
|
tabs.forEach(tab => {
|
||||||
if (tab.pos.z + tab.dim.z/2 >= slab.z) {
|
if (tab.pos.z + tab.dim.z/2 >= slab.z) {
|
||||||
let all = [...slab.tops, tab.poly];
|
let all = [...slab.tops, tab.poly];
|
||||||
|
|
@ -574,15 +175,11 @@
|
||||||
|
|
||||||
// add shadow perimeter to terrain to catch outside moves off part
|
// add shadow perimeter to terrain to catch outside moves off part
|
||||||
let tabpoly = tabs ? tabs.map(tab => tab.poly) : [];
|
let tabpoly = tabs ? tabs.map(tab => tab.poly) : [];
|
||||||
let allpoly = POLY.union([...shadowTop.tops, ...tabpoly], 0, true);
|
let allpoly = POLY.union([...state.shadowTop.tops, ...tabpoly], 0, true);
|
||||||
let shadowOff = POLY.offset(allpoly, [minToolDiam/2,maxToolDiam/2], { count: 2, flat: true });
|
let shadowOff = POLY.offset(allpoly, [minToolDiam/2,maxToolDiam/2], { count: 2, flat: true });
|
||||||
terrain.forEach(level => level.tops.appendAll(shadowOff));
|
state.terrain.forEach(level => level.tops.appendAll(shadowOff));
|
||||||
|
|
||||||
// let dslice = KIRI.newSlice(-1);
|
widget.terrain = state.skipTerrain ? null : state.terrain;
|
||||||
// dslice.output().setLayer("shadowOff", { line: 0xff0000 }).addPolys(shadowOff);
|
|
||||||
// sliceAll.push(dslice);
|
|
||||||
|
|
||||||
widget.terrain = skipTerrain ? null : terrain;
|
|
||||||
widget.minToolDiam = minToolDiam;
|
widget.minToolDiam = minToolDiam;
|
||||||
widget.maxToolDiam = maxToolDiam;
|
widget.maxToolDiam = maxToolDiam;
|
||||||
|
|
||||||
|
|
@ -644,15 +241,6 @@
|
||||||
.filter(v => v !== null)))]
|
.filter(v => v !== null)))]
|
||||||
.sort((a,b) => b - a);
|
.sort((a,b) => b - a);
|
||||||
|
|
||||||
// create shadow
|
|
||||||
// let sindex = {};
|
|
||||||
// let shadow = [];
|
|
||||||
// let sindices = indices.map((v,i) => v > 0 ? v : v + 0.005);
|
|
||||||
// slicer.slice(sindices, { each: (data, index, total) => {
|
|
||||||
// shadow = POLY.union(shadow.slice().appendAll(data.tops), 0, true);
|
|
||||||
// POLY.setZ(shadow, data.z);
|
|
||||||
// sindex[index] = shadow;
|
|
||||||
// }, flatoff: 0 });
|
|
||||||
let traces = [];
|
let traces = [];
|
||||||
// find and trim polys (including open) to shadow
|
// find and trim polys (including open) to shadow
|
||||||
let oneach = (data, index, total) => {
|
let oneach = (data, index, total) => {
|
||||||
|
|
@ -672,35 +260,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
traces.push(poly);
|
traces.push(poly);
|
||||||
// do trimming
|
|
||||||
// let trimto = sindex[index];
|
|
||||||
// if (!trimto) {
|
|
||||||
// traces.push(poly);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// trimto = trimto.clone(true);
|
|
||||||
// if (poly.open) {
|
|
||||||
// let cuts = poly.cut(trimto);
|
|
||||||
// if (cuts.length) {
|
|
||||||
// traces.appendAll(cuts);
|
|
||||||
// } else {
|
|
||||||
// traces.push(poly);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// let limit = 1000;
|
|
||||||
// while (trimto.length && limit-- > 0) {
|
|
||||||
// let trim = trimto.shift();
|
|
||||||
// let mask = poly.mask(trim, true);
|
|
||||||
// if (mask) {
|
|
||||||
// trimto.appendAll(mask);
|
|
||||||
// } else {
|
|
||||||
// traces.push(poly);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (limit === 1000) {
|
|
||||||
// traces.push(poly);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
let opts = { each: oneach, over: false, flatoff: 0, edges: true, openok: true };
|
let opts = { each: oneach, over: false, flatoff: 0, edges: true, openok: true };
|
||||||
|
|
@ -712,109 +271,6 @@
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// drilling op
|
|
||||||
function sliceDrill(tool, slices, output) {
|
|
||||||
let drills = [],
|
|
||||||
drillToolDiam = tool.fluteDiameter(),
|
|
||||||
centerDiff = drillToolDiam * 0.1,
|
|
||||||
area = (drillToolDiam/2) * (drillToolDiam/2) * Math.PI,
|
|
||||||
areaDelta = area * 0.05;
|
|
||||||
|
|
||||||
// for each slice, look for polygons with 98.5% circularity whose
|
|
||||||
// area is within the tolerance of a circle matching the tool diameter
|
|
||||||
slices.forEach(function(slice) {
|
|
||||||
let inner = slice.topPolyInners([]);
|
|
||||||
inner.forEach(function(poly) {
|
|
||||||
if (poly.circularity() >= 0.985 && Math.abs(poly.area() - area) <= areaDelta) {
|
|
||||||
let center = poly.circleCenter(),
|
|
||||||
merged = false,
|
|
||||||
closest = Infinity,
|
|
||||||
dist;
|
|
||||||
// TODO reject if inside camShellPolys (means there is material above)
|
|
||||||
// if (center.isInPolygon(camShellPolys)) return;
|
|
||||||
drills.forEach(function(drill) {
|
|
||||||
if (merged) return;
|
|
||||||
if ((dist = drill.last().distTo2D(center)) <= centerDiff) {
|
|
||||||
merged = true;
|
|
||||||
drill.push(center);
|
|
||||||
}
|
|
||||||
closest = Math.min(closest,dist);
|
|
||||||
});
|
|
||||||
if (!merged) {
|
|
||||||
drills.push(newPolygon().append(center));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// drill points to use center (average of all points) of the polygon
|
|
||||||
drills.forEach(function(drill) {
|
|
||||||
let center = drill.center(true),
|
|
||||||
slice = newSlice(0,null);
|
|
||||||
drill.points.forEach(function(point) {
|
|
||||||
point.x = center.x;
|
|
||||||
point.y = center.y;
|
|
||||||
});
|
|
||||||
slice.camMode = PRO.DRILL;
|
|
||||||
slice.camLines = [ drill ];
|
|
||||||
slice.output()
|
|
||||||
.setLayer("drill", {face: 0, line: 0})
|
|
||||||
.addPolys(drill);
|
|
||||||
output.append(slice);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// drill registration holes
|
|
||||||
function sliceDrillReg(settings, tool, output, zThru) {
|
|
||||||
let proc = settings.process,
|
|
||||||
stock = settings.stock,
|
|
||||||
bounds = settings.bounds,
|
|
||||||
r3 = tool.fluteDiameter() * 2,
|
|
||||||
mx = (bounds.max.x + bounds.min.x) / 2,
|
|
||||||
my = (bounds.max.y + bounds.min.y) / 2,
|
|
||||||
mz = zThru || 0,
|
|
||||||
dx = (stock.x - (bounds.max.x - bounds.min.x)) / 4,
|
|
||||||
dy = (stock.y - (bounds.max.y - bounds.min.y)) / 4,
|
|
||||||
dz = stock.z,
|
|
||||||
points = [];
|
|
||||||
|
|
||||||
switch(proc.camDrillReg) {
|
|
||||||
case "x axis":
|
|
||||||
points.push(newPoint(bounds.min.x - dx, my, 0));
|
|
||||||
points.push(newPoint(bounds.max.x + dx, my, 0));
|
|
||||||
break;
|
|
||||||
case "x axis 3":
|
|
||||||
points.push(newPoint(bounds.min.x - dx, my, 0));
|
|
||||||
points.push(newPoint(bounds.max.x + dx, my - r3, 0));
|
|
||||||
points.push(newPoint(bounds.max.x + dx, my + r3, 0));
|
|
||||||
break;
|
|
||||||
case "y axis":
|
|
||||||
points.push(newPoint(mx, bounds.min.y - dy, 0));
|
|
||||||
points.push(newPoint(mx, bounds.max.y + dy, 0));
|
|
||||||
break;
|
|
||||||
case "y axis 3":
|
|
||||||
points.push(newPoint(mx, bounds.min.y - dy, 0));
|
|
||||||
points.push(newPoint(mx - r3, bounds.max.y + dy, 0));
|
|
||||||
points.push(newPoint(mx + r3, bounds.max.y + dy, 0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (points.length) {
|
|
||||||
let slice = newSlice(0,null), polys = [];
|
|
||||||
points.forEach(point => {
|
|
||||||
polys.push(newPolygon()
|
|
||||||
.append(point.clone().setZ(bounds.max.z))
|
|
||||||
.append(point.clone().setZ(bounds.max.z - stock.z - mz)));
|
|
||||||
});
|
|
||||||
slice.camMode = PRO.DRILL;
|
|
||||||
slice.camLines = polys;
|
|
||||||
slice.output()
|
|
||||||
.setLayer("register", {face: 0, line: 0})
|
|
||||||
.addPolys(polys);
|
|
||||||
output.append(slice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cutTabs(tabs, offset, z) {
|
function cutTabs(tabs, offset, z) {
|
||||||
let noff = [];
|
let noff = [];
|
||||||
tabs = tabs.filter(tab => z < tab.pos.z + tab.dim.z/2).map(tab => tab.off).flat();
|
tabs = tabs.filter(tab => z < tab.pos.z + tab.dim.z/2).map(tab => tab.off).flat();
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,16 @@
|
||||||
noop = function() {};
|
noop = function() {};
|
||||||
|
|
||||||
class Topo {
|
class Topo {
|
||||||
constructor(widget, settings, opt = {}) {
|
constructor(opt = {}) {
|
||||||
const density = parseInt(settings.controller.animesh) * 2500,
|
let { state, contour, onupdate, ondone } = opt;
|
||||||
proc = settings.process,
|
let { widget, settings, tshadow, center, tabs } = opt.state;
|
||||||
tolerance = proc.camTolerance,
|
let density = parseInt(settings.controller.animesh) * 2500,
|
||||||
onupdate = opt.onupdate || noop,
|
contourX = contour.type === "contour x",
|
||||||
ondone = opt.ondone || noop,
|
contourY = contour.type === "contour y",
|
||||||
shadow = opt.shadow,
|
|
||||||
bounds = widget.getBoundingBox().clone(),
|
bounds = widget.getBoundingBox().clone(),
|
||||||
|
tolerance = contour.tolerace,
|
||||||
|
proc = settings.process,
|
||||||
|
shadow = tshadow,
|
||||||
minX = bounds.min.x,
|
minX = bounds.min.x,
|
||||||
maxX = bounds.max.x,
|
maxX = bounds.max.x,
|
||||||
minY = bounds.min.y,
|
minY = bounds.min.y,
|
||||||
|
|
@ -34,32 +36,32 @@
|
||||||
zMin = Math.max(bounds.min.z, zBottom) + 0.0001,
|
zMin = Math.max(bounds.min.z, zBottom) + 0.0001,
|
||||||
boundsX = maxX - minX,
|
boundsX = maxX - minX,
|
||||||
boundsY = maxY - minY,
|
boundsY = maxY - minY,
|
||||||
inside = proc.camContourIn,
|
inside = contour.inside,
|
||||||
resolution = tolerance ? tolerance : 1/Math.sqrt(density/(boundsX * boundsY)),
|
resolution = tolerance ? tolerance : 1/Math.sqrt(density/(boundsX * boundsY)),
|
||||||
tool = new CAM.Tool(settings, proc.camContourTool),
|
tool = new CAM.Tool(settings, contour.tool),
|
||||||
toolOffset = tool.generateProfile(resolution).profile,
|
toolOffset = tool.generateProfile(resolution).profile,
|
||||||
toolDiameter = tool.fluteDiameter(),
|
toolDiameter = tool.fluteDiameter(),
|
||||||
toolStep = toolDiameter * proc.camContourOver,
|
toolStep = toolDiameter * contour.step,
|
||||||
traceJoin = toolDiameter / 2,
|
traceJoin = toolDiameter / 2,
|
||||||
pocketOnly = proc.camOutlinePocket,
|
maxangle = contour.angle,
|
||||||
maxangle = proc.camContourAngle,
|
curvesOnly = contour.curves,
|
||||||
curvesOnly = proc.camContourCurves,
|
|
||||||
R2A = 180 / Math.PI,
|
R2A = 180 / Math.PI,
|
||||||
stepsx = Math.ceil(boundsX / resolution),
|
stepsx = Math.ceil(boundsX / resolution),
|
||||||
stepsy = Math.ceil(boundsY / resolution),
|
stepsy = Math.ceil(boundsY / resolution),
|
||||||
data = new Float32Array(stepsx * stepsy),
|
topo = widget.topo = widget.topo || {
|
||||||
topo = this.topo = widget.topo = {
|
data: new Float32Array(stepsx * stepsy),
|
||||||
data: data,
|
|
||||||
stepsx: stepsx,
|
stepsx: stepsx,
|
||||||
stepsy: stepsy,
|
stepsy: stepsy,
|
||||||
bounds: bounds,
|
bounds: bounds,
|
||||||
diameter: toolDiameter,
|
diameter: toolDiameter,
|
||||||
resolution: resolution,
|
resolution: resolution,
|
||||||
profile: toolOffset,
|
profile: toolOffset,
|
||||||
widget: widget
|
widget: widget,
|
||||||
|
raster: true,
|
||||||
|
slices: null
|
||||||
},
|
},
|
||||||
|
data = topo.data,
|
||||||
newslices = [],
|
newslices = [],
|
||||||
tabs = opt.tabs,
|
|
||||||
tabsMax = tabs ? Math.max(...tabs.map(tab => tab.dim.z)) : 0,
|
tabsMax = tabs ? Math.max(...tabs.map(tab => tab.dim.z)) : 0,
|
||||||
tabsOn = tabs,
|
tabsOn = tabs,
|
||||||
tabHeight = Math.max(proc.camTabsHeight + zBottom, tabsMax),
|
tabHeight = Math.max(proc.camTabsHeight + zBottom, tabsMax),
|
||||||
|
|
@ -67,7 +69,6 @@
|
||||||
clipTo = inside ? shadow : POLY.expand(shadow, toolDiameter/2 + resolution * 3),
|
clipTo = inside ? shadow : POLY.expand(shadow, toolDiameter/2 + resolution * 3),
|
||||||
partOff = inside ? 0 : toolDiameter / 2 + resolution,
|
partOff = inside ? 0 : toolDiameter / 2 + resolution,
|
||||||
gridDelta = Math.floor(partOff / resolution),
|
gridDelta = Math.floor(partOff / resolution),
|
||||||
center = opt.center,
|
|
||||||
debug = false,
|
debug = false,
|
||||||
debug_clips = debug && true,
|
debug_clips = debug && true,
|
||||||
debug_topo = debug && true,
|
debug_topo = debug && true,
|
||||||
|
|
@ -178,7 +179,13 @@
|
||||||
lastP = undefined;
|
lastP = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function processSlices(slices) {
|
function raster(slices) {
|
||||||
|
if (!topo.raster) {
|
||||||
|
// console.log({skipping_raster: widget.id});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
topo.raster = false;
|
||||||
|
|
||||||
let gridx = 0,
|
let gridx = 0,
|
||||||
gridy,
|
gridy,
|
||||||
gridi, // index
|
gridi, // index
|
||||||
|
|
@ -264,6 +271,16 @@
|
||||||
gridx++;
|
gridx++;
|
||||||
onupdate(++stepsTaken, stepsTotal, "raster surface");
|
onupdate(++stepsTaken, stepsTotal, "raster surface");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function processSlices(slices) {
|
||||||
|
let gridx = 0,
|
||||||
|
gridy,
|
||||||
|
gridi, // index
|
||||||
|
gridv, // value
|
||||||
|
i, il, j, jl, x, y, tv, ltv;
|
||||||
|
|
||||||
|
raster(slices);
|
||||||
|
|
||||||
const checkr = newPoint(0,0);
|
const checkr = newPoint(0,0);
|
||||||
const inClip = function(polys, checkZ) {
|
const inClip = function(polys, checkZ) {
|
||||||
|
|
@ -281,8 +298,7 @@
|
||||||
};
|
};
|
||||||
let tabZ;
|
let tabZ;
|
||||||
|
|
||||||
// x contouring
|
if (contourY) {
|
||||||
if (proc.camContourXOn) {
|
|
||||||
startTime = time();
|
startTime = time();
|
||||||
// emit slice per X
|
// emit slice per X
|
||||||
for (x = minX - partOff; x <= maxX + partOff; x += toolStep) {
|
for (x = minX - partOff; x <= maxX + partOff; x += toolStep) {
|
||||||
|
|
@ -318,15 +334,14 @@
|
||||||
newslices.push(slice);
|
newslices.push(slice);
|
||||||
slice.camLines = sliceout;
|
slice.camLines = sliceout;
|
||||||
slice.output()
|
slice.output()
|
||||||
.setLayer("contour x", {face: 0, line: 0})
|
.setLayer("contour y", {face: 0, line: 0})
|
||||||
.addPolys(sliceout);
|
.addPolys(sliceout);
|
||||||
}
|
}
|
||||||
onupdate(++stepsTaken, stepsTotal, "contour y");
|
onupdate(++stepsTaken, stepsTotal, "contour y");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// y contouring
|
if (contourX) {
|
||||||
if (proc.camContourYOn) {
|
|
||||||
startTime = time();
|
startTime = time();
|
||||||
// emit slice per Y
|
// emit slice per Y
|
||||||
for (y = minY - partOff; y <= maxY + partOff; y += toolStep) {
|
for (y = minY - partOff; y <= maxY + partOff; y += toolStep) {
|
||||||
|
|
@ -357,7 +372,7 @@
|
||||||
newslices.push(slice);
|
newslices.push(slice);
|
||||||
slice.camLines = sliceout;
|
slice.camLines = sliceout;
|
||||||
slice.output()
|
slice.output()
|
||||||
.setLayer("contour y", {face: 0, line: 0})
|
.setLayer("contour x", {face: 0, line: 0})
|
||||||
.addPolys(sliceout);
|
.addPolys(sliceout);
|
||||||
}
|
}
|
||||||
onupdate(++stepsTaken, stepsTotal, "contour x");
|
onupdate(++stepsTaken, stepsTotal, "contour x");
|
||||||
|
|
@ -371,11 +386,11 @@
|
||||||
swapX: true, emptyok: true, notopok: true
|
swapX: true, emptyok: true, notopok: true
|
||||||
});
|
});
|
||||||
let sindex = slicer.interval(resolution);
|
let sindex = slicer.interval(resolution);
|
||||||
stepsTotal += sindex.length * 2;
|
if (!topo.slices) stepsTotal += sindex.length * 2;
|
||||||
if (proc.camContourXOn) stepsTotal += (maxX-minX) / toolStep;
|
if (contourX) stepsTotal += Math.round((maxY-minY) / toolStep);
|
||||||
if (proc.camContourYOn) stepsTotal += (maxY-minY) / toolStep;
|
if (contourY) stepsTotal += Math.round((maxX-minX) / toolStep);
|
||||||
|
|
||||||
let slices = slicer.slice(sindex, { each: (data, index, total) => {
|
let slices = topo.slices = topo.slices || slicer.slice(sindex, { each: (data, index, total) => {
|
||||||
onupdate(++stepsTaken, stepsTotal, "topo slice");
|
onupdate(++stepsTaken, stepsTotal, "topo slice");
|
||||||
}, genso: true });
|
}, genso: true });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
--gradbar: linear-gradient(to right, #ddd, #666, #ddd);
|
--gradbar: linear-gradient(to right, #ddd, #666, #ddd);
|
||||||
--holdbar: linear-gradient(to top, rgba(100,100,100,0.1), rgba(255,255,255,1), rgba(100,100,100,0.1));
|
--holdbar: linear-gradient(to top, rgba(100,100,100,0.1), rgba(255,255,255,1), rgba(100,100,100,0.1));
|
||||||
--holdbar-sf: linear-gradient(to top, rgba(200,200,200,0.1), rgba(255,255,255,1), rgba(200,200,200,0.1)) !important;
|
--holdbar-sf: linear-gradient(to top, rgba(200,200,200,0.1), rgba(255,255,255,1), rgba(200,200,200,0.1)) !important;
|
||||||
|
--highlight: rgba(200,220,220,0.9);
|
||||||
}
|
}
|
||||||
a, a:hover, a:visited {
|
a, a:hover, a:visited {
|
||||||
border: none;
|
border: none;
|
||||||
|
|
@ -485,6 +486,10 @@ th, tr, td, label {
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
height: 75%;
|
height: 75%;
|
||||||
}
|
}
|
||||||
|
.ext-buttons label {
|
||||||
|
font-size: smaller;
|
||||||
|
margin: 3px 0 3px 0;
|
||||||
|
}
|
||||||
.txt-sel {
|
.txt-sel {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
@ -522,7 +527,71 @@ th, tr, td, label {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: smaller;
|
font-size: smaller;
|
||||||
height: 25px;
|
}
|
||||||
|
#camops {
|
||||||
|
display: none;
|
||||||
|
padding: 0;
|
||||||
|
height: 38px;
|
||||||
|
margin: 0 0 4px 0;
|
||||||
|
}
|
||||||
|
#camops .left {
|
||||||
|
width: 12px;
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
background-color: var(--bg-gray);
|
||||||
|
margin-right: 1px;
|
||||||
|
}
|
||||||
|
#camops .right {
|
||||||
|
width: 12px;
|
||||||
|
border-top-right-radius: 10px;
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
background-color: var(--bg-gray);
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
#oplist div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid rgba(0,0,0,0.25);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0 2px 0 2px;
|
||||||
|
padding: 2px 10px 2px 10px;
|
||||||
|
background-color: rgba(255,255,255,0.75);
|
||||||
|
}
|
||||||
|
#oplist .drag {
|
||||||
|
background-color: var(--highlight) !important;
|
||||||
|
}
|
||||||
|
#oplist div .label {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
#oplist div .del {
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
right: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(0,0,0,0);
|
||||||
|
}
|
||||||
|
#oplist div:hover .del {
|
||||||
|
color: #aaa;
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
#oplist div .del:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
#oplist div .opop {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
|
white-space: pre;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: smaller;
|
||||||
|
text-align: left;
|
||||||
|
margin: 2px;
|
||||||
|
padding: 5px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#oplist div:hover .opop {
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
#progress {
|
#progress {
|
||||||
|
|
@ -633,8 +702,8 @@ th, tr, td, label {
|
||||||
background-color: rgba(200,200,200,0.9);
|
background-color: rgba(200,200,200,0.9);
|
||||||
}
|
}
|
||||||
.devlist button.selected {
|
.devlist button.selected {
|
||||||
background-color: rgba(200,220,220,0.9);
|
background-color: var(--highlight);
|
||||||
border: 1px splid rgba(200,220,200,1);
|
border: 1px solid var(--highlight);
|
||||||
}
|
}
|
||||||
#mod-print {
|
#mod-print {
|
||||||
background-color: rgba(255,255,255,0.85);
|
background-color: rgba(255,255,255,0.85);
|
||||||
|
|
@ -726,6 +795,9 @@ th, tr, td, label {
|
||||||
#settingsList button.selected:hover {
|
#settingsList button.selected:hover {
|
||||||
background-color: #aaa;
|
background-color: #aaa;
|
||||||
}
|
}
|
||||||
|
#settingsList svg {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
#help p {
|
#help p {
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
|
|
@ -935,15 +1007,6 @@ th, tr, td, label {
|
||||||
color: #335;
|
color: #335;
|
||||||
}
|
}
|
||||||
|
|
||||||
#layer-slider {
|
|
||||||
position: absolute;
|
|
||||||
font-size: smaller;
|
|
||||||
left: 15px;
|
|
||||||
right: 15px;
|
|
||||||
bottom: 3px;
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
|
|
||||||
#layer-animate {
|
#layer-animate {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 15px;
|
left: 15px;
|
||||||
|
|
@ -966,6 +1029,7 @@ th, tr, td, label {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
#layer-toolpos {
|
#layer-toolpos {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 3px;
|
right: 3px;
|
||||||
|
|
@ -993,6 +1057,14 @@ th, tr, td, label {
|
||||||
margin: 1px 1px 2px 1px;
|
margin: 1px 1px 2px 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#layer-slider {
|
||||||
|
position: absolute;
|
||||||
|
font-size: smaller;
|
||||||
|
left: 15px;
|
||||||
|
right: 15px;
|
||||||
|
bottom: 3px;
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
#slider {
|
#slider {
|
||||||
display: none;
|
display: none;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
|
|
@ -1092,7 +1164,6 @@ th, tr, td, label {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#speedbar {
|
#speedbar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 3px;
|
top: 3px;
|
||||||
|
|
@ -1102,7 +1173,6 @@ th, tr, td, label {
|
||||||
border: 3px solid rgba(200,200,200,0.5);
|
border: 3px solid rgba(200,200,200,0.5);
|
||||||
background-color: #888;
|
background-color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
#speedbar label {
|
#speedbar label {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
@ -1125,6 +1195,7 @@ th, tr, td, label {
|
||||||
border-top-left-radius: 10px;
|
border-top-left-radius: 10px;
|
||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
background-color: var(--bg-gray);
|
background-color: var(--bg-gray);
|
||||||
|
min-height: 10px;
|
||||||
}
|
}
|
||||||
#set-end {
|
#set-end {
|
||||||
color: black;
|
color: black;
|
||||||
|
|
|
||||||
|
|
@ -393,19 +393,25 @@
|
||||||
<div id="mid-rcol" class="f-col j-center noshow">
|
<div id="mid-rcol" class="f-col j-center noshow">
|
||||||
<div id="set-menu" class="f-col">
|
<div id="set-menu" class="f-col">
|
||||||
<div id="speeds"><div id="speedbar" class="f-col a-stretch j-center"></div></div>
|
<div id="speeds"><div id="speedbar" class="f-col a-stretch j-center"></div></div>
|
||||||
<div id="set-top" class="set-group"><a>settings</a></div>
|
<div id="set-top" class="set-group"><a id="set-label">settings</a></div>
|
||||||
<div id="settings" class="f-col"></div>
|
<div id="settings" class="f-col"></div>
|
||||||
<div id="set-end" class="set-group"></div>
|
<div id="set-end" class="set-group"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="end" class="f-col a-center">
|
<div id="end" class="f-col a-center">
|
||||||
<div id="bottom"></div>
|
|
||||||
<div id="alert-area">
|
<div id="alert-area">
|
||||||
<div id="alert-border">
|
<div id="alert-border">
|
||||||
<div id="alert-text">alerts<br>alerts<br>alerts</div>
|
<div id="alert-text">alerts<br>alerts<br>alerts</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="bottom">
|
||||||
|
<div id="camops" class="f-row a-stretch j-center">
|
||||||
|
<div class="left"></div>
|
||||||
|
<div id="oplist" class="f-row j-center"></div>
|
||||||
|
<div class="right"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="gdpr" class="noshow">
|
<div id="gdpr" class="noshow">
|
||||||
<p>this site uses <A href="/privacy.html" target="privacy">cookies</a> to preserve application preferences</p>
|
<p>this site uses <A href="/privacy.html" target="privacy">cookies</a> to preserve application preferences</p>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ kiri.lang['en'] =
|
||||||
kiri.lang['en-us'] = {
|
kiri.lang['en-us'] = {
|
||||||
version: "version",
|
version: "version",
|
||||||
enable: "enable",
|
enable: "enable",
|
||||||
|
settings: "settings",
|
||||||
|
|
||||||
acct_xpo: "make a backup of your device\nand device profiles with the\noption to include workspace\nobjects and positions",
|
acct_xpo: "make a backup of your device\nand device profiles with the\noption to include workspace\nobjects and positions",
|
||||||
|
|
||||||
|
|
@ -164,7 +165,7 @@ kiri.lang['en-us'] = {
|
||||||
|
|
||||||
// SETTINGS
|
// SETTINGS
|
||||||
se_menu: "library",
|
se_menu: "library",
|
||||||
se_load: "load",
|
se_load: "open",
|
||||||
se_save: "save",
|
se_save: "save",
|
||||||
|
|
||||||
// FDM SLICING
|
// FDM SLICING
|
||||||
|
|
@ -225,6 +226,7 @@ kiri.lang['en-us'] = {
|
||||||
|
|
||||||
// FDM SUPPORT
|
// FDM SUPPORT
|
||||||
sp_menu: "support",
|
sp_menu: "support",
|
||||||
|
sp_detect: "detect",
|
||||||
sp_dens_s: "density",
|
sp_dens_s: "density",
|
||||||
sp_dens_l: "fraction 0.0 - 1.0\nrecommended 0.15\n0 to disable",
|
sp_dens_l: "fraction 0.0 - 1.0\nrecommended 0.15\n0 to disable",
|
||||||
sp_size_s: "pillar size",
|
sp_size_s: "pillar size",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue