diff --git a/src/kiri/init.js b/src/kiri/init.js index 136adc8e..4409b7a5 100644 --- a/src/kiri/init.js +++ b/src/kiri/init.js @@ -1545,12 +1545,18 @@ camDrillSep: UC.newBlank({class:"pop-sep"}), camDrillReg: UC.newSelect(LANG.cd_regi_s, {modes:CAM, title:LANG.cd_regi_l}, "drillreg"), camTabs: UC.newGroup(LANG.ct_menu, null, {modes:CAM, marker:true}), - camTabsAngle: UC.newInput(LANG.ct_angl_s, {title:LANG.ct_angl_l, convert:UC.toInt, bound:UC.bound(0,360), modes:CAM}), - camTabsCount: UC.newInput(LANG.ct_numb_s, {title:LANG.ct_numb_l, convert:UC.toInt, bound:UC.bound(1,20), modes:CAM}), camTabsWidth: UC.newInput(LANG.ct_wdth_s, {title:LANG.ct_wdth_l, convert:UC.toFloat, bound:UC.bound(0.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}), camTabSep: UC.newBlank({class:"pop-sep"}), - camTabsOn: UC.newBoolean(LANG.enable, onBooleanClick, {title:LANG.ct_nabl_l, modes:CAM}), + camTabsAngle: UC.newInput(LANG.ct_angl_s, {title:LANG.ct_angl_l, convert:UC.toInt, bound:UC.bound(0,360), modes:CAM}), + camTabsCount: UC.newInput(LANG.ct_numb_s, {title:LANG.ct_numb_l, convert:UC.toInt, bound:UC.bound(1,20), modes:CAM}), + camTabsOn: UC.newBoolean(LANG.ct_nabl_s, onBooleanClick, {title:LANG.ct_nabl_l, modes:CAM}), + camTabSep: UC.newBlank({class:"pop-sep"}), + camTabsManual: UC.newRow([ + (UI.tabAdd = UC.newButton(undefined, onButtonClick, {icon:''})), + (UI.tabDun = UC.newButton(undefined, onButtonClick, {icon:''})), + (UI.tabClr = UC.newButton(undefined, onButtonClick, {icon:''})) + ], {modes:CAM, class:"ext-buttons f-row"}), camSep1: UC.newGroup(null, null, {modes:CAM, class:"set-sep"}), camStock: UC.newGroup(LANG.cs_menu, null, {modes:CAM}), diff --git a/src/mode/cam/client.js b/src/mode/cam/client.js index 933cc1f4..8fcb86e8 100644 --- a/src/mode/cam/client.js +++ b/src/mode/cam/client.js @@ -15,9 +15,12 @@ isCamMode, isParsed, camStock, - API, SPACE, STACKS, MODES, VIEWS, UI, UC; + func = {}, + API, FDM, SPACE, STACKS, MODES, VIEWS, UI, UC; CAM.init = function(kiri, api) { + FDM = KIRI.driver.FDM; + // console.log({kiri,api}) STACKS = api.const.STACKS; SPACE = api.const.SPACE; @@ -72,7 +75,9 @@ api.event.on("preview.end", () => { isParsed = false; - if (camStock) STACKS.getStack("bounds").button("animate", animate); + if (isCamMode && camStock) { + STACKS.getStack("bounds").button("animate", animate); + } }); api.event.on("code.loaded", (info) => { @@ -81,8 +86,163 @@ STACKS.getStack("parse", SPACE.platform.world).button("animate", animate); } }); + + api.event.on("button.click", target => { + switch (target) { + case api.ui.tabAdd: + return func.tadd(); + case api.ui.tabDun: + return func.tdone(); + case api.ui.tabClr: + api.uc.confirm("clear tabs?").then(ok => { + if (ok) func.tclear(); + }); + break; + } + }); + + api.event.on("cam.tabs.add", func.tadd = () => { + alert = api.show.alert("<esc> key cancels editing tabs"); + api.feature.hover = true; + }); + api.event.on("cam.tabs.done", func.tdone = () => { + delbox('intZ'); + delbox('intW'); + delbox('supp'); + api.hide.alert(alert); + api.feature.hover = false; + }); + api.event.on("cam.tabs.clear", func.tclear = () => { + func.sdone(); + // clearAllWidgetSupports(); + // API.conf.save(); + }); + api.event.on("slice.begin", () => { + if (isCamMode) { + func.sdone(); + // updateVisiblity(); + } + }); + api.event.on("key.esc", () => { + if (isCamMode) { + func.sdone() + } + }); + api.event.on("widget.rotate", rot => { + if (!isCamMode) { + return; + } + const {widget, x, y, z} = rot; + if (x || y) { + // clearWidgetSupports(widget); + } else { + let ann = API.widgets.annotate(widget.id); + let tabs = ann.support || []; + // tabs.forEach(sup => { + // let wsup = widget.tabs[sup.id]; + // let vc = new THREE.Vector3(sup.x, sup.y, sup.z); + // let m4 = new THREE.Matrix4(); + // m4 = m4.makeRotationFromEuler(new THREE.Euler(x || 0, y || 0, z || 0)); + // vc.applyMatrix4(m4); + // wsup.box.position.x = wsup.x = sup.x = vc.x; + // wsup.box.position.y = wsup.y = sup.y = vc.y; + // wsup.box.position.z = wsup.z = sup.z = vc.z; + // }); + } + }); + api.event.on("mouse.hover.up", int => { + if (!isCamMode) { + return; + } + delbox('supp'); + if (lastTab) { + const {widget, box, id} = lastTab; + widget.adds.remove(box); + widget.mesh.remove(box); + delete widget.tabs[id]; + let ta = API.widgets.annotate(widget.id).tab; + let ix = 0; + ta.forEach((rec,i) => { + if (rec.id === id) { + ix = i; + } + }); + ta.splice(ix,1); + API.conf.save(); + return; + } + if (!iw) return; + let ip = iw.track.pos; + let wa = api.widgets.annotate(iw.id); + let wt = (wa.tab = wa.tab || []); + let x = ic.x - ip.x, y = -ic.z - ip.y, z = 0, id = Date.now(); + let rec = {x, y, z, dw:10, dh:10, id}; + wt.push(Object.clone(rec)); + addWidgetTab(iw, rec); + API.conf.save(); + }); + const zaxis = {x: 0, y: 0, z: 1}; + let lastTab, tab, iw, ic; + api.event.on("mouse.hover", data => { + if (!isCamMode) { + return; + } + delbox('supp'); + const { int, type, point } = data; + const object = int ? int.object : null; + const tab = int ? object.tab : null; + if (lastTab) { + lastTab.box.material.color.r = 0; + lastTab = null; + } + if (tab) { + tab.box.material.color.r = 0.5; + lastTab = tab; + return; + } + if (type !== 'widget') { + iw = null; + return; + } + let n = int.face.normal; + iw = int.object.widget; + ic = int.point; + // only near vertical faces + if (Math.abs(n.z) > 0.1) { + return; + } + const { track } = iw; + const { stock, bounds, process } = API.conf.get(); + const { camTabsWidth, camTabsHeight } = process; + const zp = stock.z - track.box.d + process.camZBottom - process.camZTopOffset + camTabsHeight / 2; + ic.x += n.x * 2.5; + ic.z -= n.y * 2.5; + ic.y = zp; // swap in world space y,z + const q = new THREE.Quaternion().setFromAxisAngle(zaxis, Math.atan2(n.y, n.x)); + addbox({x:ic.x, y:ic.y, z:ic.z}, 0x0000dd, 'supp', { + x:5, y:camTabsWidth, z:camTabsHeight + }, { rotate: q }); + }); }; + function addWidgetTab(widget, pos) { + const { x, y, z, dw, dh, id } = pos; + const tabs = widget.tabs = (widget.tabs || {}); + // prevent duplicate restore from repeated settings load calls + if (!tabs[id]) { + pos.box = addbox( + { x, y, z }, 0x0000dd, 'supp', + { x:dw, y:dw, z:dh }, { group: widget.mesh } + ); + pos.box.tab = Object.assign({widget}, pos); + tabs[id] = pos; + widget.adds.push(pos.box); + } + } + + function addbox() { return FDM.addbox(...arguments)}; + function delbox() { return FDM.delbox(...arguments)}; + function animate() { isAnimate = true; API.widgets.opacity(isParsed ? 0 : 0.75); diff --git a/src/mode/fdm/client.js b/src/mode/fdm/client.js index 7194fc72..62255bc7 100644 --- a/src/mode/fdm/client.js +++ b/src/mode/fdm/client.js @@ -11,13 +11,16 @@ SPACE, API, p1, p2, iw, lastMode, lastView, lastPillar, + isFdmMode = false, alert = [], + boxes = {}, func = {}; FDM.init = function(kiri, api) { API = api; SPACE = api.const.SPACE; api.event.on("mode.set", mode => { + isFdmMode = mode === 'FDM'; lastMode = mode; updateVisiblity(); }); @@ -64,13 +67,22 @@ API.conf.save(); }); api.event.on("slice.begin", () => { + if (!isFdmMode) { + return; + } func.sdone(); updateVisiblity(); }); api.event.on("key.esc", () => { + if (!isFdmMode) { + return; + } func.sdone() }); api.event.on("widget.rotate", rot => { + if (!isFdmMode) { + return; + } const {widget, x, y, z} = rot; if (x || y) { clearWidgetSupports(widget); @@ -90,6 +102,9 @@ } }); api.event.on("mouse.hover.up", int => { + if (!isFdmMode) { + return; + } delbox('supp'); if (lastPillar) { const {widget, box, id} = lastPillar; @@ -121,6 +136,9 @@ API.conf.save(); }); api.event.on("mouse.hover", data => { + if (!isFdmMode) { + return; + } // delbox('intZ'); // delbox('intW'); // addbox(point, 0xff0000, 'intZ'); @@ -198,7 +216,10 @@ const sups = widget.sups = (widget.sups || {}); // prevent duplicate restore from repeated settings load calls if (!sups[id]) { - pos.box = addbox({x, y, z}, 0x0000dd, 'supp', { x:dw, y:dw, z:dh }, widget.mesh); + pos.box = addbox( + { x, y, z }, 0x0000dd, 'supp', + { x:dw, y:dw, z:dh }, { group: widget.mesh } + ); pos.box.pillar = Object.assign({widget}, pos); sups[id] = pos; widget.adds.push(pos.box); @@ -232,17 +253,16 @@ delete API.widgets.annotate(widget.id).support; } - function delbox(name, group) { - const old = SPACE[`__${name}`]; + function delbox(name) { + const old = boxes[name]; if (old) { - (group || SPACE.scene).remove(old); + old.groupTo.remove(old); } } - function addbox(point, color, name, dim = {x:1,y:1,z:1}, group) { + function addbox(point, color, name, dim = {x:1,y:1,z:1}, opt = {}) { delbox(name); - name = `__${name}`; - const box = SPACE[name] = new THREE.Mesh( + const box = boxes[name] = new THREE.Mesh( new THREE.BoxGeometry(dim.x, dim.y, dim.z), new THREE.MeshPhongMaterial({ transparent: true, @@ -253,8 +273,21 @@ box.position.x = point.x; box.position.y = point.y; box.position.z = point.z; - (group || SPACE.scene).add(box); + + const group = opt.group || SPACE.scene + group.add(box); + box.groupTo = group; + + if (opt.rotate) { + opt.matrix = new THREE.Matrix4().makeRotationFromQuaternion(opt.rotate); + } + if (opt.matrix) { + box.geometry.applyMatrix4(opt.matrix); + } return box; } + FDM.delbox = delbox; + FDM.addbox = addbox; + })(); diff --git a/web/kiri/lang/en.js b/web/kiri/lang/en.js index f7d2fe94..930cefe2 100644 --- a/web/kiri/lang/en.js +++ b/web/kiri/lang/en.js @@ -349,7 +349,8 @@ kiri.lang['en-us'] = { ct_wdth_l: "width in workspace units", ct_hght_s: "height", ct_hght_l: "height in workspace units", - ct_nabl_l: "enable or disable tabs\ntab generation skipped when\ninside only mode enabled", + ct_nabl_s: "auto", + ct_nabl_l: "auto generate radial tabs\nprojected from part center\nusing count and angle offset", // OUTPUT ou_menu: "output",