/** Copyright Stewart Allen -- All Rights Reserved */ "use strict"; // dep: kiri.api // dep: kiri.consts // dep: kiri.settings gapp.register("kiri-mode.cam.tools", (root, exports) => { let { kiri } = root, { api, consts } = kiri, { uc, ui } = api, { MODES } = consts, DOC = document, selectedTool = null, editTools = null, maxTool = 0; api.show.tools = showTools; api.tool = { update: updateTool }; function settings() { return api.conf.get(); } function renderTools() { ui.toolSelect.innerHTML = ''; maxTool = 0; editTools.forEach(function(tool, index) { maxTool = Math.max(maxTool, tool.number); tool.order = index; let opt = DOC.createElement('option'); opt.appendChild(DOC.createTextNode(tool.name)); opt.onclick = function() { selectTool(tool) }; ui.toolSelect.appendChild(opt); }); } function selectTool(tool) { selectedTool = tool; ui.toolName.value = tool.name; ui.toolNum.value = tool.number; ui.toolFluteDiam.value = tool.flute_diam; ui.toolFluteLen.value = tool.flute_len; ui.toolShaftDiam.value = tool.shaft_diam; ui.toolShaftLen.value = tool.shaft_len; ui.toolTaperTip.value = tool.taper_tip || 0; ui.toolMetric.checked = tool.metric; ui.toolType.selectedIndex = ['endmill','ballmill','tapermill'].indexOf(tool.type); if (tool.type === 'tapermill') { ui.toolTaperAngle.value = kiri.driver.CAM.calcTaperAngle( (tool.flute_diam - tool.taper_tip) / 2, tool.flute_len ).round(1); } else { ui.toolTaperAngle.value = 0; } renderTool(tool); } function otag(o) { if (Array.isArray(o)) { let out = [] o.forEach(oe => out.push(otag(oe))); return out.join(''); } let tags = []; Object.keys(o).forEach(key => { let val = o[key]; let att = []; Object.keys(val).forEach(tk => { let tv = val[tk]; att.push(`${tk.replace(/_/g,'-')}="${tv}"`); }); tags.push(`<${key} ${att.join(' ')}>`); }); return tags.join(''); } function renderTool(tool) { let type = selectedTool.type; let taper = type === 'tapermill'; ui.toolTaperAngle.disabled = taper ? undefined : 'true'; ui.toolTaperTip.disabled = taper ? undefined : 'true'; $('tool-view').innerHTML = ''; setTimeout(() => { let svg = $('tool-svg'); let pad = 10; let dim = { w: svg.clientWidth, h: svg.clientHeight } let max = { w: dim.w - pad * 2, h: dim.h - pad * 2}; let off = { x: pad, y: pad }; let shaft_fill = "#cccccc"; let flute_fill = "#dddddd"; let stroke = "#777777"; let stroke_width = 3; let stroke_thin = stroke_width / 2; let shaft = tool.shaft_len || 1; let flute = tool.flute_len || 1; let tip_len = type === "ballmill" ? tool.flute_diam / 2 : 0; let total_len = shaft + flute + tip_len; let units = dim.h / total_len; let shaft_len = (shaft / total_len) * max.h; let flute_len = (flute / total_len) * max.h; let total_wid = Math.max(tool.flute_diam, tool.shaft_diam); let shaft_off = (max.w - tool.shaft_diam * units) / 2; let flute_off = (max.w - tool.flute_diam * units) / 2; let taper_off = (max.w - (tool.taper_tip || 0) * units) / 2; let parts = [ { rect: { x:off.x + shaft_off, y:off.y, width:max.w - shaft_off * 2, height:shaft_len, stroke, fill: shaft_fill, stroke_width } } ]; if (type === "tapermill") { let yoff = off.y + shaft_len; let mid = dim.w / 2; parts.push({path: {stroke_width, stroke, fill:flute_fill, d:[ `M ${off.x + flute_off} ${yoff}`, `L ${off.x + taper_off} ${yoff + flute_len}`, `L ${dim.w - off.x - taper_off} ${yoff + flute_len}`, `L ${dim.w - off.x - flute_off} ${yoff}`, `z` ].join('\n')}}); } else { let x1 = off.x + flute_off; let y1 = off.y + shaft_len; let x2 = x1 + max.w - flute_off * 2; let y2 = y1 + flute_len; parts.push({ rect: { x:off.x + flute_off, y:off.y + shaft_len, width:max.w - flute_off * 2, height:flute_len, stroke, fill: flute_fill, stroke_width } }); parts.push({ line: { x1, y1, x2, y2, stroke, stroke_width: stroke_thin } }); parts.push({ line: { x1: (x1 + x2) / 2, y1, x2, y2: (y1 + y2) / 2, stroke, stroke_width: stroke_thin } }); parts.push({ line: { x1, y1: (y1 + y2) / 2, x2: (x1 + x2) / 2, y2, stroke, stroke_width: stroke_thin } }); } if (type === "ballmill") { let rad = (max.w - flute_off * 2) / 2; let xend = dim.w - off.x - flute_off; let yoff = off.y + shaft_len + flute_len + stroke_width/2; parts.push({path: {stroke_width, stroke, fill:flute_fill, d:[ `M ${off.x + flute_off} ${yoff}`, `A ${rad} ${rad} 0 0 0 ${xend} ${yoff}`, // `L ${off.x + flute_off} ${yoff}` ].join('\n')}}) } svg.innerHTML = otag(parts); }, 10); } function updateTool(ev) { selectedTool.name = ui.toolName.value; selectedTool.number = parseInt(ui.toolNum.value); selectedTool.flute_diam = parseFloat(ui.toolFluteDiam.value); selectedTool.flute_len = parseFloat(ui.toolFluteLen.value); selectedTool.shaft_diam = parseFloat(ui.toolShaftDiam.value); selectedTool.shaft_len = parseFloat(ui.toolShaftLen.value); selectedTool.taper_tip = parseFloat(ui.toolTaperTip.value); selectedTool.metric = ui.toolMetric.checked; selectedTool.type = ['endmill','ballmill','tapermill'][ui.toolType.selectedIndex]; if (selectedTool.type === 'tapermill') { const CAM = kiri.driver.CAM; const rad = (selectedTool.flute_diam - selectedTool.taper_tip) / 2; if (ev && ev.target === ui.toolTaperAngle) { const angle = parseFloat(ev.target.value); const len = CAM.calcTaperLength(rad, angle * DEG2RAD); selectedTool.flute_len = len; ui.toolTaperAngle.value = angle.round(1); ui.toolFluteLen.value = selectedTool.flute_len.round(4); } else { ui.toolTaperAngle.value = CAM.calcTaperAngle(rad, selectedTool.flute_len).round(1); } } else { ui.toolTaperAngle.value = 0; } renderTools(); ui.toolSelect.selectedIndex = selectedTool.order; setToolChanged(true); renderTool(selectedTool); } function setToolChanged(changed) { editTools.changed = changed; ui.toolsSave.disabled = !changed; } function showTools() { if (api.mode.get_id() !== MODES.CAM) return; api.settings.sync.get().then(_showTools); } function _showTools() { let selectedIndex = null; editTools = settings().tools.slice().sort((a,b) => { return a.name > b.name ? 1 : -1; }); setToolChanged(false); ui.toolsClose.onclick = function() { if (editTools.changed && !confirm("abandon changes?")) return; api.dialog.hide(); }; ui.toolAdd.onclick = function() { let metric = settings().controller.units === 'mm'; editTools.push(Object.assign({ id: Date.now(), number: maxTool + 1, name: "new tool", type: "endmill", taper_tip: 0, metric }, metric ? { shaft_diam: 2, shaft_len: 15, flute_diam: 2, flute_len: 20, } : { shaft_diam: 0.25, shaft_len: 1.5, flute_diam: 0.25, flute_len: 2, })); setToolChanged(true); renderTools(); ui.toolSelect.selectedIndex = editTools.length-1; selectTool(editTools[editTools.length-1]); }; ui.toolCopy.onclick = function() { let clone = Object.assign({}, selectedTool); let { name } = clone; let split = name.split(' '); let endv = parseInt(split.pop()); if (endv) { name = split.join(' ') + ' ' + (endv + 1); } else { name = `${name} 2`; } clone.number = maxTool + 1; clone.name = name; editTools.push(clone); setToolChanged(true); renderTools(); ui.toolSelect.selectedIndex = editTools.length-1; selectTool(editTools[editTools.length-1]); }; ui.toolDelete.onclick = function() { editTools.remove(selectedTool); setToolChanged(true); renderTools(); }; ui.toolsSave.onclick = function() { if (selectedTool) updateTool(); settings().tools = editTools.sort((a,b) => { return a.name < b.name ? -1 : 1; }); setToolChanged(false); api.conf.save(); api.conf.update_fields(); api.event.settings(); api.settings.sync.put(); }; ui.toolsExport.onclick = () => { uc.prompt("Export Tools Filename", "tools").then(name => { if (!name) { return; } const record = { version: kiri.version, tools: api.conf.get().tools, time: Date.now() }; api.util.download(api.util.b64enc(record), `${name}.km`); }); }; renderTools(); if (editTools.length > 0) { selectTool(editTools[0]); ui.toolSelect.selectedIndex = 0; } else { ui.toolAdd.onclick(); } api.dialog.show('tools'); ui.toolSelect.focus(); } });