diff --git a/src2/kiri-mode/cam/client.js b/src2/kiri-mode/cam/client.js index 21b4f223..90a54782 100644 --- a/src2/kiri-mode/cam/client.js +++ b/src2/kiri-mode/cam/client.js @@ -45,10 +45,9 @@ let isAnimate, popOp = {}, animVer = 0, seed = Date.now(), - func = { - hover: noop, - hoverUp: noop, - }; + hover = noop, + hoverUp = noop, + func = {}; function animFn() { return [{ @@ -117,9 +116,9 @@ function updateAxisMode(refresh) { } const clockOp = cp.ops.filter(op => op.type === '|')[0]; if (!clockOp) { - func.opAdd(popOp['|'].new()); + opAdd(popOp['|'].new()); } else { - func.opRender(); + opRender(); } updateStock(); } @@ -127,7 +126,7 @@ function updateAxisMode(refresh) { // create custom gcode editor function function gcodeEditor(label, field) { return function() { - func.opGCode(label, field); + opGCode(label, field); } } @@ -193,78 +192,78 @@ function zBottom() { return api.conf.get().process.camZBottom > 0; } -func.opAddLaserOn = () => { - func.opAdd(popOp['laser on'].new()); +const opAddLaserOn = () => { + opAdd(popOp['laser on'].new()); }; -func.opAddLaserOff = () => { - func.opAdd(popOp['laser off'].new()); +const opAddLaserOff = () => { + opAdd(popOp['laser off'].new()); }; -func.opAddGCode = () => { - func.opAdd(popOp.gcode.new()); +const opAddGCode = () => { + opAdd(popOp.gcode.new()); }; -func.opAddIndex = () => { - func.opAdd(popOp.index.new()); +const opAddIndex = () => { + opAdd(popOp.index.new()); }; -func.opAddLevel = () => { - func.opAdd(popOp.level.new()); +const opAddLevel = () => { + opAdd(popOp.level.new()); }; -func.opAddRough = () => { - func.opAdd(popOp.rough.new()); +const opAddRough = () => { + opAdd(popOp.rough.new()); }; -func.opAddOutline = () => { - func.opAdd(popOp.outline.new()); +const opAddOutline = () => { + opAdd(popOp.outline.new()); }; -func.opAddPocket = () => { - func.traceDone(); - func.surfaceDone(); +const opAddPocket = () => { + traceDone(); + surfaceDone(); let rec = popOp.pocket.new(); rec.surfaces = { /* widget.id: [ faces... ] */ }; - func.opAdd(rec); + opAdd(rec); }; -func.opAddContour = (axis) => { +const opAddContour = (axis) => { let rec = popOp.contour.new(); rec.axis = axis.toUpperCase(); - func.opAdd(rec); + opAdd(rec); }; -func.opAddLathe = (axis) => { +const opAddLathe = (axis) => { let rec = popOp.lathe.new(); rec.axis = axis.toUpperCase(); - func.opAdd(rec); + opAdd(rec); }; -func.opAddTrace = () => { +const opAddTrace = () => { let rec = popOp.trace.new(); rec.areas = { /* widget.id: [ polygons... ] */ }; - func.opAdd(rec); + opAdd(rec); }; -func.opAddDrill = () => { +const opAddDrill = () => { let rec = popOp.drill.new(); rec.drills = { }; - func.opAdd(rec); + opAdd(rec); }; -func.opAddRegister = (axis, points) => { +const opAddRegister = (axis, points) => { let rec = popOp.register.new(); rec.axis = axis.toUpperCase(); rec.points = points; - func.opAdd(rec); + opAdd(rec); }; -func.opAddFlip = () => { - func.opAdd(popOp.flip.new()); +const opAddFlip = () => { + opAdd(popOp.flip.new()); }; -func.opGCode = (label, field = 'gcode') => { +const opGCode = (label, field = 'gcode') => { api.dialog.show('any'); const { c_gcode } = h.bind( $('mod-any'), h.div({ id: "camop_dialog" }, [ @@ -284,7 +283,7 @@ func.opGCode = (label, field = 'gcode') => { c_gcode.focus(); }; -func.tabHover = function(data) { +const tabHover = function(data) { delbox('tabb'); const { int, type, point } = data; const object = int ? int.object : null; @@ -312,7 +311,7 @@ func.tabHover = function(data) { showTab = createTabBox(iw, ic, n); }; -func.tabHoverUp = function(int) { +const tabHoverUp = function(int) { delbox('tabb'); if (lastTab) { const {widget, box, id} = lastTab; @@ -353,11 +352,11 @@ func.tabHoverUp = function(int) { // SURFACE FUNCS let surfaceOn = false, lastWidget; -func.surfaceAdd = (ev) => { +const surfaceAdd = (ev) => { if (surfaceOn) { - return func.surfaceDone(); + return surfaceDone(); } - func.clearPops(); + clearPops(); alert = api.show.alert("analyzing surfaces...", 1000); let surfaces = poppedRec.surfaces; let radians = poppedRec.follow * DEG2RAD; @@ -387,7 +386,7 @@ func.surfaceAdd = (ev) => { }; }; -func.surfaceDone = () => { +const surfaceDone = () => { if (!(surfaceOn && poppedRec && poppedRec.surfaces)) { return; } @@ -408,18 +407,19 @@ func.surfaceDone = () => { // TRACE FUNCS let traceOn = false, lastTrace; -func.traceAdd = (ev) => { + +const traceAdd = (ev) => { if (traceOn) { - return func.traceDone(); + return traceDone(); } - func.clearPops(); + clearPops(); alert = api.show.alert("analyzing parts...", 1000); traceOn = hoveredOp; traceOn.classList.add("editing"); api.feature.hover = true; api.feature.hoverAdds = true; - func.hover = func.traceHover; - func.hoverUp = func.traceHoverUp; + hover = traceHover; + hoverUp = traceHoverUp; CAM.traces((ids) => { api.hide.alert(alert); alert = api.show.alert("[esc] cancels trace editing"); @@ -464,17 +464,17 @@ func.traceAdd = (ev) => { let match = areas.filter(arr => poly.matches(arr)); if (match.length > 0) { if (!mesh.selected) { - func.traceToggle(mesh, true); + traceToggle(mesh, true); } } else if (mesh.selected) { - func.traceToggle(mesh, true); + traceToggle(mesh, true); } }); }); }, poppedRec.select === 'lines'); }; -func.traceDone = () => { +const traceDone = () => { if (!traceOn) { return; } @@ -494,15 +494,15 @@ func.traceDone = () => { }); }; -func.clearPops = () => { +const clearPops = () => { if (func.unpop) func.unpop(); - func.tabDone(); - func.traceDone(); - func.surfaceDone(); - func.selectHolesDone(); + tabDone(); + traceDone(); + surfaceDone(); + selectHolesDone(); }; -func.traceHover = function(data) { +const traceHover = function(data) { if (lastTrace) { let { color, colorSave } = lastTrace.material[0] || lastTrace.material; color.r = colorSave.r; @@ -529,23 +529,23 @@ func.traceHover = function(data) { color.setHex(isDark() ? 0x0066ff : 0x0000ff); }; -func.traceHoverUp = function(int, ev) { +const traceHoverUp = function(int, ev) { if (!int) return; let { object } = int; - func.traceToggle(object); + traceToggle(object); if (ev.metaKey || ev.ctrlKey) { let { selected } = object; let { widget, poly } = object.trace; let avgZ = poly.avgZ(); for (let add of widget.adds) { if (add.trace && add.selected !== selected && add.trace.poly.onZ(avgZ)) { - func.traceToggle(add); + traceToggle(add); } } } }; -func.traceToggle = function(obj, skip) { +const traceToggle = function(obj, skip) { let material = obj.material[0] || obj.material; if (!material) return; let { color, colorSave } = material; @@ -582,19 +582,19 @@ let holeSelOn = false, lastSelHoles; * @param {boolean} individual - select all holes, false to only select * holes of tool diameter. */ -func.selectHoles = async function(individual){ +const selectHoles = async function(individual){ // console.log("client individual selected",individual) if (holeSelOn) { - return func.selectHolesDone(); + return selectHolesDone(); } - func.clearPops(); + clearPops(); let alert = api.show.alert("analyzing parts..."); holeSelOn = hoveredOp; holeSelOn.classList.add("editing"); api.feature.hover = true; api.feature.hoverAdds = true; - func.hover = func.selectHolesHover; - func.hoverUp = func.selectHolesHoverUp; + hover = selectHolesHover; + hoverUp = selectHolesHoverUp; const widgets = api.widgets.all() /** @@ -705,7 +705,7 @@ func.selectHoles = async function(individual){ api.widgets.opacity(0.8); } -func.selectHolesHover = function(data) { +const selectHolesHover = function(data) { //not used right now. may be useful in the future if (lastTrace) { let { color, colorSave } = lastTrace.material[0] || lastTrace.material; @@ -720,17 +720,17 @@ func.selectHolesHover = function(data) { } } -func.selectHolesHoverUp = function(int, ev) { +const selectHolesHoverUp = function(int, ev) { if (!int) return; //if not a hole mesh return let { object } = int; - func.selectHoleToggle(object); + selectHoleToggle(object); } /** * Toggle the selection of a hole mesh and update its color * @param {Object3D} mesh - the hole mesh to toggle */ -func.selectHoleToggle = function(mesh) { +const selectHoleToggle = function(mesh) { let {hole} = mesh if(!hole) return hole.selected = !hole.selected; @@ -742,7 +742,7 @@ func.selectHoleToggle = function(mesh) { * and also clears the widget.adds array. * @param {Object} widget - the widget with the drills array to clear */ -func.clearHolesRec = (widget)=>{ +const clearHolesRec = (widget)=>{ if(widget.drills){ widget.drills.forEach(rec=>{ }) @@ -757,7 +757,7 @@ func.clearHolesRec = (widget)=>{ * Removes all adds from the scene, hides the alert, and resets the opacity of the widgets. * Also resets the hover features and the editing class on the holeSelOn html element. */ -func.selectHolesDone = () => { +const selectHolesDone = () => { if (!holeSelOn) { return; } @@ -777,7 +777,7 @@ func.selectHolesDone = () => { }); }; -func.opFlip = () => { +const opFlip = () => { api.view.set_arrange(); let widgets = api.widgets.all(); let { process } = current; @@ -848,29 +848,340 @@ func.opFlip = () => { } } if (add2) { - func.opAdd(poppedRec); + opAdd(poppedRec); } else { - func.opRender(); + opRender(); } }; +let showTab, lastTab, tab, iw, ic; + +const tabAdd = () => { + traceDone(); + alert = api.show.alert("[esc] cancels tab editing"); + api.feature.hover = true; + hover = tabHover; + hoverUp = tabHoverUp; +}; + +const tabDone = () => { + delbox('tabb'); + api.hide.alert(alert); + api.feature.hover = false; + if (lastTab) { + lastTab.box.material.color.r = 0; + lastTab = null; + } +}; + +const tabClear = () => { + tabDone(); + api.widgets.all().forEach(widget => { + clearTabs(widget); + widget.saveState(); + }); + api.conf.save(); +}; + +const traceClear = () => { + traceDone(); + api.widgets.all().forEach(widget => { + unselectTraces(widget); + }); + api.conf.save(); +}; + +const opAdd = (rec) => { + if (!isCamMode) return; + clearPops(); + let oplist = current.process.ops; + if (oplist.indexOf(rec) < 0) { + if (oplist.length && oplist[oplist.length-1].type === '|') { + oplist.splice(oplist.length-1,0,rec); + } else { + oplist.push(rec); + } + let fpos = oplist.findWith(rec => rec.type === 'flip'); + if (fpos >= 0 && oplist.length > 1) { + let oprec = oplist.splice(fpos,1); + oplist.push(oprec[0]); + } + api.conf.save(); + opRender(); + } +}; + +const opDel = (rec) => { + if (!isCamMode) return; + clearPops(); + let oplist = current.process.ops; + let pos = oplist.indexOf(rec); + if (pos >= 0) { + oplist.splice(pos,1); + api.conf.save(); + opRender(); + } +}; + +const opRender = () => { + let oplist = current.process.ops; + if (!(isCamMode && oplist)) { + return; + } + oplist = oplist.filter(rec => !Array.isArray(rec)); + let mark = Date.now(); + let html = []; + let bind = {}; + let scale = api.view.unit_scale(); + let notime = false; + oplist.forEach((rec,i) => { + let title = ''; + let clock = rec.type === '|'; + let label = clock ? `` : rec.type; + let clazz = notime ? [ "draggable", "notime" ] : [ "draggable" ]; + let notable = rec.note ? rec.note.split(' ').filter(v => v.charAt(0) === '#') : undefined; + if (clock) { clazz.push('clock'); title = ` title="end of ops chain\ndrag/drop like an op\nops after this are disabled"` } + if (notable?.length) label += ` (${notable[0].slice(1)})`; + html.appendAll([ + `
`, + ``, + clock ? '' : + ``, + `
` + ]); + bind[mark+i] = rec; + notime = notime || clock; + }); + let listel = $('oplist'); + listel.innerHTML = html.join(''); + let bounds = []; + let unpop = null; + let index = 0; + let indexing = true; + // drag and drop re-ordering + for (let [id, rec] of Object.entries(bind)) { + let type = rec.type; + let clock = type === '|'; + if (!clock) { + $(`${id}-x`).onmousedown = (ev) => { + ev.stopPropagation(); + ev.preventDefault(); + surfaceDone(); + traceDone(); + tabDone(); + opDel(rec); + }; + } else { + indexing = false; + } + let el = $(id); + if (!isIndexed && type === 'lathe') { + rec.disabled = true; + } + if (!hasSharedArrays && (type === 'contour' || type === 'lathe')) { + rec.disabled = true; + } + if (rec.disabled) { + el.classList.add("disabled"); + } + bounds.push(el); + let timer = null; + let inside = true; + let popped = false; + let poprec = popOp[rec.type]; + if (type === 'index' && indexing && !rec.disabled) { + index = rec.absolute ? rec.degrees : index + rec.degrees; + } + el.rec = rec; + el.unpop = () => { + let pos = [...el.childNodes].indexOf(poprec.div); + if (pos >= 0) { + el.removeChild(poprec.div); + } + popped = false; + }; + function onEnter(ev) { + if ((surfaceOn || traceOn) && poppedRec != rec) { + return; + } + if (popped && poppedRec != rec) { + surfaceDone(); + traceDone(); + } + if (unpop) unpop(); + func.unpop = unpop = el.unpop; + inside = true; + // pointer to current rec for trace editing + poppedRec = rec; + popped = true; + poprec.use(rec); + hoveredOp = el; + if (!clock) { + // offset Y position of pop div by % of Y screen location of button + el.appendChild(poprec.div); + poprec.addNote(); + const { innerHeight } = window; + const brect = ev.target.getBoundingClientRect(); + const prect = poprec.div.getBoundingClientRect(); + const pcty = (brect.top / innerHeight) * 0.9; + const offpx = -pcty * prect.height; + poprec.div.style.transform = `translateY(${offpx}px)`; + } + // option click event appears latent + // and overides the sticky settings + setTimeout(() => { + UC.setSticky(false); + }, 0); + } + function onLeave(ev) { + inside = false; + clearTimeout(timer); + timer = setTimeout(() => { + if (!inside && poprec.using(rec) && !UC.isSticky()) { + el.unpop(); + } + }, 250); + } + function onDown(ev) { + if (!ev.target.rec) { + // only trigger on operation buttons bound to recs + return; + } + let mobile = ev.touches; + surfaceDone(); + traceDone(); + let target = ev.target, clist = target.classList; + if (!clist.contains("draggable")) { + return; + } + // toggle enable / disable + if (!clock && (ev.ctrlKey || ev.metaKey)) { + ev.preventDefault(); + ev.stopPropagation(); + rec.disabled = !rec.disabled; + for (let op of ev.shiftKey ? oplist : [ rec ]) { + if (op !== rec) { + op.disabled = op.type !== '|' ? !rec.disabled : false; + } + } + for (let el of bounds) { + if (el.rec.disabled) { + el.classList.add("disabled"); + } else { + el.classList.remove("disabled"); + } + } + if (isIndexed) { + updateIndex(); + } + return true; + } + // duplicate op + if (!clock && ev.shiftKey) { + ev.preventDefault(); + ev.stopPropagation(); + oplist = current.process.ops; + oplist.push(Object.clone(rec)); + api.conf.save(); + opRender(); + return true; + } + clist.add("drag"); + ev.stopPropagation(); + ev.preventDefault(); + let tracker = UI.tracker; + tracker.style.display = 'block'; + let cancel = tracker.onmouseup = (ev) => { + oplist = current.process.ops; + clist.remove("drag"); + tracker.style.display = 'none'; + if (ev) { + ev.stopPropagation(); + ev.preventDefault(); + } + oplist.length = 0; + for (let child of listel.childNodes) { + oplist.push(child.rec); + } + api.conf.save(); + opRender(); + if (mobile) { + el.ontouchmove = onDown; + el.ontouchend = undefined; + } + }; + function onMove(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 top = rect.top; + let bottom = rect.bottom;// + rect.height; + let tar = mobile ? ev.touches[0] : ev; + if (tar.pageY >= top && tar.pageY <= bottom) { + let mid = (top + bottom) / 2; + try { listel.removeChild(target); } catch (e) { } + el.insertAdjacentElement(tar.pageY < mid ? "beforebegin" : "afterend", target); + } + } + } + tracker.onmousemove = onMove; + if (mobile) { + el.ontouchmove = onMove; + el.ontouchend = cancel; + } + } + if (SPACE.info.mob) { + let touched = false; + el.ontouchstart = (ev) => { + touched = true; + if (poppedRec === rec && popped) { + onLeave(ev); + } else { + onEnter(ev); + } + }; + el.ontouchmove = onDown; + el.onmouseenter = (ev) => { + if (touched) { + // touches block mouse events on touchscreen PCs + // which are often sent along with touch events + // but when not touched, allow the mouse to work + return; + } + el.onmousedown = onDown; + el.onmouseleave = onLeave; + onEnter(ev); + }; + } else { + el.onmousedown = onDown; + el.onmouseenter = onEnter; + el.onmouseleave = onLeave; + } + } + if (lastMode !== VIEWS.ANIMATE) { + // update widget rotations from timeline marker + WIDGETS.setAxisIndex(isPreview || !isIndexed ? 0 : -index); + } + currentIndex = isIndexed && !isPreview ? index * DEG2RAD : 0; + updateStock(); +}; + export function init() { api.event.on('tool.mesh.face-normal', normal => { // console.log({ poppedRec }); poppedRec.degrees = (Math.atan2(normal.y, normal.z) * RAD2DEG).round(2); poppedRec.absolute = true; - func.opRender(); + opRender(); updateStock(); }); - api.event.on("cam.trace.clear", func.traceClear = () => { - func.traceDone(); - api.widgets.all().forEach(widget => { - unselectTraces(widget); - }); - api.conf.save(); - }); + api.event.on("cam.trace.clear", traceClear); api.event.on("cam.parse.gerber", opts => { const { data, mesh } = opts; @@ -934,12 +1245,12 @@ export function init() { api.uc.setClass(el, 'hide', !isCamMode); } if (!isCamMode) { - func.clearPops(); - func.tabClear(); + clearPops(); + tabClear(); } // do not persist traces across page reloads - func.traceClear(); - func.opRender(); + traceClear(); + opRender(); updateStock(); }); @@ -949,12 +1260,12 @@ export function init() { isPreview = (mode === VIEWS.PREVIEW); isAnimate = (mode === VIEWS.ANIMATE); animFn().animate_clear(api); - func.clearPops(); + clearPops(); if (isCamMode && isPreview) { WIDGETS.setAxisIndex(0); } updateStock(); - func.opRender(); + opRender(); api.uc.setVisible($('layer-animate'), isAnimate && isCamMode); }); @@ -978,12 +1289,12 @@ export function init() { updateStock(); updateAxisMode(); if (!poppedRec) { - func.opRender(); + opRender(); } }); api.event.on("settings.load", (settings) => { - func.opRender(); + opRender(); if (!isCamMode) return; validateTools(settings.tools); restoreTabs(api.widgets.all()); @@ -1034,7 +1345,7 @@ export function init() { api.event.on([ "selection.scale", ], () => { - func.tabClear(); + tabClear(); }); api.event.on([ @@ -1066,13 +1377,13 @@ export function init() { let settings = api.conf.get(); let { process, device } = settings; switch (ev.target.innerText.toLowerCase()) { - case "index": return func.opAddIndex(); - case "laser on": return func.opAddLaserOn(); - case "laser off": return func.opAddLaserOff(); - case "gcode": return func.opAddGCode(); - case "level": return func.opAddLevel(); - case "rough": return func.opAddRough(); - case "outline": return func.opAddOutline(); + case "index": return opAddIndex(); + case "laser on": return opAddLaserOn(); + case "laser off": return opAddLaserOff(); + case "gcode": return opAddGCode(); + case "level": return opAddLevel(); + case "rough": return opAddRough(); + case "outline": return opAddOutline(); case "contour": let caxis = "X"; for (let op of current.process.ops) { @@ -1080,7 +1391,7 @@ export function init() { caxis = "Y"; } } - return func.opAddContour(caxis); + return opAddContour(caxis); case "lathe": let laxis = "X"; for (let op of current.process.ops) { @@ -1088,11 +1399,11 @@ export function init() { laxis = "Y"; } } - return func.opAddLathe(laxis); - case "register": return func.opAddRegister('X', 2); - case "drill": return func.opAddDrill(); - case "trace": return func.opAddTrace(); - case "pocket": return func.opAddPocket(); + return opAddLathe(laxis); + case "register": return opAddRegister('X', 2); + case "drill": return opAddDrill(); + case "trace": return opAddTrace(); + case "pocket": return opAddPocket(); case "flip": // only one flip op permitted for (let op of current.process.ops) { @@ -1100,7 +1411,7 @@ export function init() { return; } } - return func.opAddFlip(); + return opAddFlip(); } }; @@ -1109,344 +1420,45 @@ export function init() { let process = api.conf.get().process; switch (target) { case api.ui.tabAdd: - return func.tabAdd(); + return tabAdd(); case api.ui.tabDun: - return func.tabDone(); + return tabDone(); case api.ui.tabClr: api.uc.confirm("clear tabs?").then(ok => { - if (ok) func.tabClear(); + if (ok) tabClear(); }); break; } }); - // OPS FUNCS - api.event.on("cam.op.add", func.opAdd = (rec) => { - if (!isCamMode) return; - func.clearPops(); - let oplist = current.process.ops; - if (oplist.indexOf(rec) < 0) { - if (oplist.length && oplist[oplist.length-1].type === '|') { - oplist.splice(oplist.length-1,0,rec); - } else { - oplist.push(rec); - } - let fpos = oplist.findWith(rec => rec.type === 'flip'); - if (fpos >= 0 && oplist.length > 1) { - let oprec = oplist.splice(fpos,1); - oplist.push(oprec[0]); - } - api.conf.save(); - func.opRender(); - } - }); + api.event.on("cam.op.add", opAdd); - api.event.on("cam.op.del", func.opDel = (rec) => { - if (!isCamMode) return; - func.clearPops(); - 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.del", opDel); - // (re)render the re-orderable op list - api.event.on("cam.op.render", func.opRender = () => { - let oplist = current.process.ops; - if (!(isCamMode && oplist)) { - return; - } - oplist = oplist.filter(rec => !Array.isArray(rec)); - let mark = Date.now(); - let html = []; - let bind = {}; - let scale = api.view.unit_scale(); - let notime = false; - oplist.forEach((rec,i) => { - let title = ''; - let clock = rec.type === '|'; - let label = clock ? `` : rec.type; - let clazz = notime ? [ "draggable", "notime" ] : [ "draggable" ]; - let notable = rec.note ? rec.note.split(' ').filter(v => v.charAt(0) === '#') : undefined; - if (clock) { clazz.push('clock'); title = ` title="end of ops chain\ndrag/drop like an op\nops after this are disabled"` } - if (notable?.length) label += ` (${notable[0].slice(1)})`; - html.appendAll([ - `
`, - ``, - clock ? '' : - ``, - `
` - ]); - bind[mark+i] = rec; - notime = notime || clock; - }); - let listel = $('oplist'); - listel.innerHTML = html.join(''); - let bounds = []; - let unpop = null; - let index = 0; - let indexing = true; - // drag and drop re-ordering - for (let [id, rec] of Object.entries(bind)) { - let type = rec.type; - let clock = type === '|'; - if (!clock) { - $(`${id}-x`).onmousedown = (ev) => { - ev.stopPropagation(); - ev.preventDefault(); - func.surfaceDone(); - func.traceDone(); - func.tabDone(); - func.opDel(rec); - }; - } else { - indexing = false; - } - let el = $(id); - if (!isIndexed && type === 'lathe') { - rec.disabled = true; - } - if (!hasSharedArrays && (type === 'contour' || type === 'lathe')) { - rec.disabled = true; - } - if (rec.disabled) { - el.classList.add("disabled"); - } - bounds.push(el); - let timer = null; - let inside = true; - let popped = false; - let poprec = popOp[rec.type]; - if (type === 'index' && indexing && !rec.disabled) { - index = rec.absolute ? rec.degrees : index + rec.degrees; - } - el.rec = rec; - el.unpop = () => { - let pos = [...el.childNodes].indexOf(poprec.div); - if (pos >= 0) { - el.removeChild(poprec.div); - } - popped = false; - }; - function onEnter(ev) { - if ((surfaceOn || traceOn) && poppedRec != rec) { - return; - } - if (popped && poppedRec != rec) { - func.surfaceDone(); - func.traceDone(); - } - if (unpop) unpop(); - unpop = func.unpop = el.unpop; - inside = true; - // pointer to current rec for trace editing - poppedRec = rec; - popped = true; - poprec.use(rec); - hoveredOp = el; - if (!clock) { - // offset Y position of pop div by % of Y screen location of button - el.appendChild(poprec.div); - poprec.addNote(); - const { innerHeight } = window; - const brect = ev.target.getBoundingClientRect(); - const prect = poprec.div.getBoundingClientRect(); - const pcty = (brect.top / innerHeight) * 0.9; - const offpx = -pcty * prect.height; - poprec.div.style.transform = `translateY(${offpx}px)`; - } - // option click event appears latent - // and overides the sticky settings - setTimeout(() => { - UC.setSticky(false); - }, 0); - } - function onLeave(ev) { - inside = false; - clearTimeout(timer); - timer = setTimeout(() => { - if (!inside && poprec.using(rec) && !UC.isSticky()) { - el.unpop(); - } - }, 250); - } - function onDown(ev) { - if (!ev.target.rec) { - // only trigger on operation buttons bound to recs - return; - } - let mobile = ev.touches; - func.surfaceDone(); - func.traceDone(); - let target = ev.target, clist = target.classList; - if (!clist.contains("draggable")) { - return; - } - // toggle enable / disable - if (!clock && (ev.ctrlKey || ev.metaKey)) { - ev.preventDefault(); - ev.stopPropagation(); - rec.disabled = !rec.disabled; - for (let op of ev.shiftKey ? oplist : [ rec ]) { - if (op !== rec) { - op.disabled = op.type !== '|' ? !rec.disabled : false; - } - } - for (let el of bounds) { - if (el.rec.disabled) { - el.classList.add("disabled"); - } else { - el.classList.remove("disabled"); - } - } - if (isIndexed) { - updateIndex(); - } - return true; - } - // duplicate op - if (!clock && ev.shiftKey) { - ev.preventDefault(); - ev.stopPropagation(); - oplist = current.process.ops; - oplist.push(Object.clone(rec)); - api.conf.save(); - func.opRender(); - return true; - } - clist.add("drag"); - ev.stopPropagation(); - ev.preventDefault(); - let tracker = UI.tracker; - tracker.style.display = 'block'; - let cancel = tracker.onmouseup = (ev) => { - oplist = current.process.ops; - clist.remove("drag"); - tracker.style.display = 'none'; - if (ev) { - ev.stopPropagation(); - ev.preventDefault(); - } - oplist.length = 0; - for (let child of listel.childNodes) { - oplist.push(child.rec); - } - api.conf.save(); - func.opRender(); - if (mobile) { - el.ontouchmove = onDown; - el.ontouchend = undefined; - } - }; - function onMove(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 top = rect.top; - let bottom = rect.bottom;// + rect.height; - let tar = mobile ? ev.touches[0] : ev; - if (tar.pageY >= top && tar.pageY <= bottom) { - let mid = (top + bottom) / 2; - try { listel.removeChild(target); } catch (e) { } - el.insertAdjacentElement(tar.pageY < mid ? "beforebegin" : "afterend", target); - } - } - } - tracker.onmousemove = onMove; - if (mobile) { - el.ontouchmove = onMove; - el.ontouchend = cancel; - } - } - if (SPACE.info.mob) { - let touched = false; - el.ontouchstart = (ev) => { - touched = true; - if (poppedRec === rec && popped) { - onLeave(ev); - } else { - onEnter(ev); - } - }; - el.ontouchmove = onDown; - el.onmouseenter = (ev) => { - if (touched) { - // touches block mouse events on touchscreen PCs - // which are often sent along with touch events - // but when not touched, allow the mouse to work - return; - } - el.onmousedown = onDown; - el.onmouseleave = onLeave; - onEnter(ev); - }; - } else { - el.onmousedown = onDown; - el.onmouseenter = onEnter; - el.onmouseleave = onLeave; - } - } - if (lastMode !== VIEWS.ANIMATE) { - // update widget rotations from timeline marker - WIDGETS.setAxisIndex(isPreview || !isIndexed ? 0 : -index); - } - currentIndex = isIndexed && !isPreview ? index * DEG2RAD : 0; - updateStock(); - }); + api.event.on("cam.op.render", opRender); - // TAB FUNCS - let showTab, lastTab, tab, iw, ic; - api.event.on("cam.tabs.add", func.tabAdd = () => { - func.traceDone(); - alert = api.show.alert("[esc] cancels tab editing"); - api.feature.hover = true; - func.hover = func.tabHover; - func.hoverUp = func.tabHoverUp; - }); + api.event.on("cam.tabs.add", tabAdd); - api.event.on("cam.tabs.done", func.tabDone = () => { - delbox('tabb'); - api.hide.alert(alert); - api.feature.hover = false; - if (lastTab) { - lastTab.box.material.color.r = 0; - lastTab = null; - } - }); + api.event.on("cam.tabs.done", tabDone); - api.event.on("cam.tabs.clear", func.tabClear = () => { - func.tabDone(); - api.widgets.all().forEach(widget => { - clearTabs(widget); - widget.saveState(); - }); - api.conf.save(); - }); + api.event.on("cam.tabs.clear", tabClear); // COMMON TAB/TRACE EVENT HANDLERS api.event.on("slice.begin", () => { if (isCamMode) { - func.clearPops(); + clearPops(); } }); api.event.on("key.esc", () => { if (isCamMode) { - func.clearPops(); + clearPops(); } }); api.event.on("selection.scale", () => { if (isCamMode) { - func.clearPops(); + clearPops(); } }); @@ -1455,7 +1467,7 @@ export function init() { return; } if (traceOn) { - func.traceDone(); + traceDone(); } unselectTraces(widget); if (flipping) { @@ -1475,12 +1487,12 @@ export function init() { return; } if (traceOn) { - func.traceDone(); + traceDone(); } if (holeSelOn) { - func.selectHolesDone(); + selectHolesDone(); } - func.clearHolesRec(widget) + clearHolesRec(widget) unselectTraces(widget); if (flipping) { return; @@ -1494,16 +1506,16 @@ export function init() { } let {widget, x, y, z} = rot; if (traceOn) { - func.traceDone(); + traceDone(); } unselectTraces(widget); if (holeSelOn) { - func.selectHolesDone(); + selectHolesDone(); } if (flipping) { return; } - func.clearHolesRec(widget) + clearHolesRec(widget) if (x || y) { clearTabs(widget); } else { @@ -1516,14 +1528,14 @@ export function init() { return; } let { object, event } = rec; - func.hoverUp(object, event); + hoverUp(object, event); }); api.event.on("mouse.hover", data => { if (!isCamMode) { return; } - func.hover(data); + hover(data); }); createPopOp('level', { @@ -1751,7 +1763,7 @@ export function init() { ov_conv: UC.newBoolean(LANG.ou_conv_s, undefined, {title:LANG.ou_conv_l}), exp_end: UC.endExpand(), sep: UC.newBlank({class:"pop-sep"}), - menu: UC.newRow([ UC.newButton("select", func.traceAdd) ], {class:"ext-buttons f-row"}), + menu: UC.newRow([ UC.newButton("select", traceAdd) ], {class:"ext-buttons f-row"}), }; createPopOp('pocket', { @@ -1798,7 +1810,7 @@ export function init() { ov_conv: UC.newBoolean(LANG.ou_conv_s, undefined, {title:LANG.ou_conv_l}), exp_end: UC.endExpand(), sep: UC.newBlank({class:"pop-sep"}), - menu: UC.newRow([ UC.newButton("select", func.surfaceAdd) ], {class:"ext-buttons f-row"}), + menu: UC.newRow([ UC.newButton("select", surfaceAdd) ], {class:"ext-buttons f-row"}), }; createPopOp('drill', { @@ -1827,8 +1839,8 @@ export function init() { precision:UC.newInput(LANG.cd_prcn_s, {title:LANG.cd_prcn_l, convert:UC.toFloat, units:true,show:() => !poppedRec.mark}), sep: UC.newBlank({class:"pop-sep", }), actions: UC.newRow([ - UC.newButton(LANG.select, ()=>func.selectHoles(true), {title:LANG.cd_seli_l}), - UC.newButton(LANG.cd_sela_s, ()=>func.selectHoles(false), {title:LANG.cd_sela_l}) + UC.newButton(LANG.select, ()=>selectHoles(true), {title:LANG.cd_seli_l}), + UC.newButton(LANG.cd_sela_s, ()=>selectHoles(false), {title:LANG.cd_sela_l}) ], {class:"ext-buttons f-col"}) }; @@ -1865,7 +1877,7 @@ export function init() { sep: UC.newBlank({class:"pop-sep", modes:MCAM, show:zBottom}), invert: UC.newBoolean(LANG.cf_nvrt_s, undefined, {title:LANG.cf_nvrt_l, show:zBottom}), sep: UC.newBlank({class:"pop-sep"}), - action: UC.newRow([ UC.newButton(LANG.cf_menu, func.opFlip) ], {class:"ext-buttons f-row"}) + action: UC.newRow([ UC.newButton(LANG.cf_menu, opFlip) ], {class:"ext-buttons f-row"}) }; createPopOp('gcode', { @@ -2028,7 +2040,7 @@ function createPopOp(type, map) { poppedRec.note = op.x = note; api.conf.save(); } - func.opRender(); + opRender(); }); }; const note = $(noteid); @@ -2134,7 +2146,7 @@ function unselectTraces(widget, skip) { if (widget.trace_stack) { widget.trace_stack.meshes.forEach(mesh => { if (mesh.selected) { - func.traceToggle(mesh, skip); + traceToggle(mesh, skip); } }); } diff --git a/src2/kiri-mode/cam/op-contour.js b/src2/kiri-mode/cam/op-contour.js index 0fdeef35..fca12f00 100644 --- a/src2/kiri-mode/cam/op-contour.js +++ b/src2/kiri-mode/cam/op-contour.js @@ -8,9 +8,10 @@ import { tip2tipEmit } from '../../geo/paths.js'; function createFilter(op) { let filter = slices => slices; - if (op.filter) { + let filterString = op.filter?.map(l => l.trim()).join('\n'); + if (filterString) { try { - const obj = eval(`( ${op.filter.join('\n')} )`); + const obj = eval(`( ${filterString} )`); let idx = 0; if (obj && obj.slices) { const nadd = []; diff --git a/src2/kiri-mode/cam/slice.js b/src2/kiri-mode/cam/slice.js index ffa58f12..b83c9315 100644 --- a/src2/kiri-mode/cam/slice.js +++ b/src2/kiri-mode/cam/slice.js @@ -667,7 +667,7 @@ function contourPolys(widget, polys) { raycaster.ray.origin.set(point.x, point.y, 10000); const intersects = raycaster.intersectObject(widget.mesh, false); const firstHit = intersects[0] || null; - point.z = firstHit.point.z; + if (firstHit) point.z = firstHit.point.z; } } } diff --git a/src2/kiri-mode/fdm/client.js b/src2/kiri-mode/fdm/client.js index e3c9919e..1dccce42 100644 --- a/src2/kiri-mode/fdm/client.js +++ b/src2/kiri-mode/fdm/client.js @@ -3,8 +3,9 @@ import { $ } from '../../moto/webui.js'; import { api } from '../../kiri/api.js'; import { conf } from '../../kiri/conf.js'; +import { util } from '../../geo/base.js'; import { client } from '../../kiri/client.js'; -import { addbox, delbox } from '../../kiri/boxes.js'; +import { addbox, delbox, getlastbox } from '../../kiri/boxes.js'; import { Layers } from '../../kiri/layers.js'; import { Stack } from '../../kiri/stack.js'; import { Widget, newWidget } from '../../kiri/widget.js'; @@ -371,6 +372,7 @@ export function init() { removeWidgetSupport(lastPillar.widget, lastPillar); return; } + lastBox = getlastbox(); if (!(iw && lastBox)) return; let { point, dim } = lastBox; p1.y = Math.max(0, p1.y); diff --git a/src2/kiri/boxes.js b/src2/kiri/boxes.js index 9d5784d6..e4bb1656 100644 --- a/src2/kiri/boxes.js +++ b/src2/kiri/boxes.js @@ -1,6 +1,14 @@ -const { BoxGeometry, Matrix4, MeshPhongMaterial, Quaternion, Vector3 } = THREE; +import { space } from '../moto/space.js'; + +const { BoxGeometry, Matrix4, MeshPhongMaterial, Quaternion, Vector3, Mesh } = THREE; const boxes = []; +let lastBox; + +export function getlastbox() { + return lastBox; +} + export function delbox(name) { const old = boxes[name]; if (old) { @@ -26,7 +34,7 @@ export function addbox(point, color, name, dim = {x:1,y:1,z:1,rz:0}, opt = {}) { lastBox = { point, dim }; - const group = opt.group || space.scene + const group = opt.group || space.scene; group.add(box); box.groupTo = group;