diff --git a/notes.md b/notes.md index 0dfc53be..c3123da7 100644 --- a/notes.md +++ b/notes.md @@ -74,34 +74,35 @@ * `B` need to force cut line at synthetic z bottom (midlines, etc) * `B` contour does not honor clip to stock +* `F` add custom gcode operation for things like injecting pauses +* `F` add optional hover notes or titles to help visually differentiate dup ops * `F` all ops should allow limit and milling direction / ease down overrides * `F` drill op should allow selecting holes >= current tool * `F` intelligently turn circle hole pocket clear into spiral down * `F` separate leveling op. add features like uni-directional cutting -* `F` add custom gcode operation for things like injecting pauses * `F` add option for op-major ordering (instead of part major) * `F` animate only selected range (as an option) * `F` add `plunge max` to contouring that can override z feed limit * `F` add ability to select regions for contouring op (like pocketing) -* `F` roughing flats should be constrained to flat region, not create a new layer -* `F` limit cut depth to flute length of selected tool (or warn) -* `F` change color of line selection in trace op when not a closed poly * `F` add lead-in milling (requires adding clamp / no go areas) * `F` add linear clearing strategy * `F` add adaptive clearing strategy * `F` add ease-down support to trace op -* `F` user-defined origin (issue #28) * `F` add climb/conventional into each operation +* `F` add lathe mode / A-axis / 4th axis / rotary milling ops +* `F` add support for tapered ball mills +* `F` roughing flats should be constrained to flat region, not create a new layer +* `F` change color of line selection in trace op when not a closed poly +* `F` limit cut depth to flute length of selected tool (or warn) +* `F` user-defined origin (issue #28) * `F` validate muti-part layout and spacing exceeds largest outside tool diameter * `F` parameterize dropping close points in prep.js. ensure long segments remain straight * `F` flat and volumetric rendering of paths * `F` z planar settings visualizations (edit using snappable dragging of planes) * `F` trapezoidal tabs in Z -* `F` add support for tapered ball mills * `F` ease-in and ease-out especially on tab cut-out start/stop * `F` add option to spiral in vs out (optimal tool life) vs mixed (optimal path) * `F` can animation clear the mesh in areas where the cuts go through the stock? -* `F` add lathe mode / A-axis / 4th axis / rotary milling ops * `F` gcode output option as zip for multiple or flip ops or tool change * `F` maintain several part orientations + op chains in a single profile diff --git a/release.md b/release.md index ef12a458..31bfad2f 100644 --- a/release.md +++ b/release.md @@ -20,6 +20,7 @@ Full docs @ https://docs.grid.space/projects/kiri-moto * add CAM pocket option to ignore interior features (outline only) * add CAM Z bottom visualization, make it relative to stock instead of part * add CAM z bottom inversion option to flip operator +* add CAM custom gcode operator * extend url loading of workspaces to all formats * alert when healing is enabled and non-manifold geometries are detected * fix thin output start and end point tracking which broke retraction diff --git a/src/kiri-mode/cam/client.js b/src/kiri-mode/cam/client.js index f0989e83..61e3e832 100644 --- a/src/kiri-mode/cam/client.js +++ b/src/kiri-mode/cam/client.js @@ -169,6 +169,7 @@ CAM.init = function(kiri, api) { let settings = API.conf.get(); let { process, device } = settings; switch (ev.target.innerText) { + case "gcode": return func.opAddGCode(); case "level": return func.opAddLevel(); case "rough": return func.opAddRough(); case "outline": return func.opAddOutline(); @@ -195,6 +196,10 @@ CAM.init = function(kiri, api) { } }; + func.opAddGCode = () => { + func.opAdd(popOp.gcode.new()); + }; + func.opAddLevel = () => { func.opAdd(popOp.level.new()); }; @@ -421,6 +426,25 @@ CAM.init = function(kiri, api) { } }); + func.opGCode = () => { + API.dialog.show('any'); + const { c_gcode } = h.bind( + $('mod-any'), h.div({ id: "camop_dialog" }, [ + h.label('custom gcode operation'), + h.textarea({ id: "c_gcode", rows: 15, cols: 50 }), + h.button({ _: 'done', onclick: () => { + API.dialog.hide(); + API.conf.save(); + } }) + ]) + ); + c_gcode.value = poppedRec.gcode || ''; + c_gcode.onkeyup = (el) => { + poppedRec.gcode = c_gcode.value; + }; + c_gcode.focus(); + }; + func.opFlip = () => { let widgets = API.widgets.all(); let { process } = current; @@ -932,6 +956,12 @@ CAM.init = function(kiri, api) { return current.device.spindleMax > 0; } + createPopOp('gcode', { + gcode: 'camCustomGcode', + }).inputs = { + action: UC.newRow([ UC.newButton(LANG.edit, func.opGCode) ], {class:"ext-buttons f-row"}) + }; + createPopOp('level', { tool: 'camLevelTool', spindle: 'camLevelSpindle', diff --git a/src/kiri-mode/cam/export.js b/src/kiri-mode/cam/export.js index d8187efa..a49a9501 100644 --- a/src/kiri-mode/cam/export.js +++ b/src/kiri-mode/cam/export.js @@ -275,9 +275,11 @@ CAM.export = function(print, online) { // collect tool info to add to header let toolz = {}, ctool; - // remap points as necessary for origins, offsets, inversions print.output.forEach(function(layer) { + if (!Array.isArray(layer)) { + return; + } layer.forEach(function(out) { if (out.tool && out.tool !== ctool) { ctool = toolByNumber(out.tool); @@ -311,6 +313,10 @@ CAM.export = function(print, online) { // emit all points in layer/point order for (let layerout of print.output) { + if (!Array.isArray(layerout)) { + append(layerout.toString().trim()); + continue; + } if (mode !== layerout.mode || consts.tool !== layerout[0].tool) { if (mode && !stripComments) { append("; ending " + mode + " op after " + Math.round(time/60) + " seconds"); diff --git a/src/kiri-mode/cam/ops.js b/src/kiri-mode/cam/ops.js index 6053283c..fc1cd706 100644 --- a/src/kiri-mode/cam/ops.js +++ b/src/kiri-mode/cam/ops.js @@ -38,6 +38,18 @@ class CamOp { prepare() { } } +class OpGCode extends CamOp { + constructor(state, op) { + super(state, op); + } + + prepare(ops, progress) { + if (this.op.gcode) { + ops.addGCode(this.op.gcode); + } + } +} + class OpLevel extends CamOp { constructor(state, op) { super(state, op); @@ -1504,6 +1516,7 @@ CAM.OPS = CamOp.MAP = { "trace": OpTrace, "drill": OpDrill, "register": OpRegister, + "gcode": OpGCode, "flip": CamOp }; diff --git a/src/kiri-mode/cam/prepare.js b/src/kiri-mode/cam/prepare.js index 4a88ad2d..dcff2dcd 100644 --- a/src/kiri-mode/cam/prepare.js +++ b/src/kiri-mode/cam/prepare.js @@ -41,7 +41,8 @@ CAM.prepare = function(widgets, settings, update) { }); }); - print.render = render.path(print.output, (progress, layer) => { + const output = print.output.filter(level => Array.isArray(level)); + print.render = render.path(output, (progress, layer) => { update(0.75 + progress * 0.25, "render", layer); }, { thin: true, @@ -127,6 +128,15 @@ function prepEach(widget, settings, print, firstPoint, update) { layerOut.spindle = spindle; } + function addGCode(text) { + newOutput.push(text); + if (layerOut.length) { + layerOut = []; + layerOut.mode = lastMode; + layerOut.spindle = spindle; + } + } + // non-zero means contouring function setTolerance(dist) { tolerance = dist; @@ -372,6 +382,7 @@ function prepEach(widget, settings, print, firstPoint, update) { setPrintPoint, printPoint, newLayer, + addGCode, camOut, polyEmit, poly2polyEmit, @@ -505,9 +516,13 @@ function prepEach(widget, settings, print, firstPoint, update) { } // last layer/move is to zmax - // injected into the last layer generated - if (lastPoint && newOutput.length) - print.addOutput(newOutput[newOutput.length-1], printPoint = lastPoint.clone().setZ(zmax_outer), 0, 0, tool.getNumber()); + // re-inject that point into the last layer generated + if (lastPoint && newOutput.length) { + let lastLayer = newOutput.filter(layer => Array.isArray(layer)).peek(); + if (Array.isArray(lastLayer)) { + print.addOutput(lastLayer, printPoint = lastPoint.clone().setZ(zmax_outer), 0, 0, tool.getNumber()); + } + } // replace output single flattened layer with all points print.output = newOutput; diff --git a/src/kiri/init.js b/src/kiri/init.js index c5d785f5..96462f11 100644 --- a/src/kiri/init.js +++ b/src/kiri/init.js @@ -1705,8 +1705,8 @@ gapp.register("kiri.init", [], (root, exports) => { extNozzle: uc.newInput(LANG.dv_nozl_s, {title:LANG.dv_nozl_l, convert:uc.toFloat, modes:FDM}), extOffsetX: uc.newInput(LANG.dv_exox_s, {title:LANG.dv_exox_l, convert:uc.toFloat, modes:FDM}), extOffsetY: uc.newInput(LANG.dv_exoy_s, {title:LANG.dv_exoy_l, convert:uc.toFloat, modes:FDM}), - extSelect: uc.newText(LANG.dv_exts_s, {title:LANG.dv_exts_l, modes:FDM, size:14, height:3, modes:FDM, area:gcode}), - extDeselect: uc.newText(LANG.dv_dext_s, {title:LANG.dv_dext_l, modes:FDM, size:14, height:3, modes:FDM, area:gcode}), + extSelect: uc.newText(LANG.dv_exts_s, {title:LANG.dv_exts_l, modes:FDM, size:14, height:3, area:gcode}), + extDeselect: uc.newText(LANG.dv_dext_s, {title:LANG.dv_dext_l, modes:FDM, size:14, height:3, area:gcode}), extPad: uc.newBlank({class:"grow", modes:FDM}), extActions: uc.newRow([ ui.extPrev = uc.newButton(undefined, undefined, {icon:''}), diff --git a/web/kiri/index.css b/web/kiri/index.css index 6b8e596a..10bdf2c1 100644 --- a/web/kiri/index.css +++ b/web/kiri/index.css @@ -788,6 +788,18 @@ th, tr, td, label { text-align: center; font-size: smaller; } +#camop_dialog { + flex-direction: column; +} +#camop_dialog textarea { + border: 1px solid gray; + margin: 5px 1px 5px 1px; + background-color: #f4f4f4; +} +#camop_dialog button, #camop_dialog label { + text-align: center; + display: block; +} #camops { display: none; padding: 0; diff --git a/web/kiri/index.html b/web/kiri/index.html index 591049db..c6e8db8f 100644 --- a/web/kiri/index.html +++ b/web/kiri/index.html @@ -483,6 +483,7 @@
drill

register
+
gcode
flip
diff --git a/web/kiri/lang/en.js b/web/kiri/lang/en.js index 6da2bbc3..69d03d2a 100644 --- a/web/kiri/lang/en.js +++ b/web/kiri/lang/en.js @@ -11,6 +11,7 @@ self.kiri.lang['en-us'] = { delete: "delete", detail: "detail", done: "done", + edit: "edit", enable: "enable", export: "export", files: "files",