checkpoint cam-op-pop editing

This commit is contained in:
Stewart Allen 2020-12-23 14:27:18 -05:00
commit f6775130ca
8 changed files with 267 additions and 131 deletions

View file

@ -1,6 +1,6 @@
# Kiri:Moto todo and notes # Kiri:Moto todo and notes
## 2.5 plan ## short list
# `cnc` # `cnc`
# - split gcode output # - split gcode output
# - redo path planning # - redo path planning
@ -8,11 +8,8 @@
# - 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
# - non-planar actual
# - layer start point control # - layer start point control
# `sla` # - bind process to z ranges or boxed regions
# - common support area detection from new fdm code
## `C` cosmetic, `F` functional, `P` performance, `B` bug fix ## `C` cosmetic, `F` functional, `P` performance, `B` bug fix
@ -51,11 +48,16 @@
* `P` solid fill the tops of supports for down facing flats * `P` solid fill the tops of supports for down facing flats
* `P` sparse infill should follow polys between intersect points * `P` sparse infill should follow polys between intersect points
# SLA # FDM - SLA
# `P` common support area detection (fork new fdm code)
* `P` prioritize supports by length of unsupported span. mandatory when circularity > X * `P` prioritize supports by length of unsupported span. mandatory when circularity > X
* or % area of inner to outer poly is high (making it a thin shell) * or % area of inner to outer poly is high (making it a thin shell)
# FDM - BELT
# `B` z hop = y hop
# CAM # CAM
* `B` failure to go *up* when moving between parts? * `B` failure to go *up* when moving between parts?
@ -68,6 +70,7 @@
* `F` split out facing. make it use linear passes * `F` split out facing. make it use linear passes
* `F` add linear clearing strategy option for roughing / pocket * `F` add linear clearing strategy option for roughing / pocket
* `F` extend acute roughing on inside polys to clear small voids
* `F` option to use part / STL coordinate space to determine X,Y origin * `F` option to use part / STL coordinate space to determine X,Y origin
* `F` validate muti-part layout and spacing exceeds largest outside tool diameter * `F` validate muti-part layout and spacing exceeds largest outside tool diameter
* `F` skip "thru" holes checkbox for roughing and outlining * `F` skip "thru" holes checkbox for roughing and outlining
@ -80,12 +83,10 @@
* `F` z bounded slices (extension of z bottom offset feature) * `F` z bounded slices (extension of z bottom offset feature)
* `F` z planar settings visualizations * `F` z planar settings visualizations
* `F` convert acute angles to arcs * `F` convert acute angles to arcs
* `F` extend acute roughing on inside polys to clear small voids
* `F` lead-in milling * `F` lead-in milling
* `F` adaptive clearing in roughing mode * `F` adaptive clearing in roughing mode
* `F` trapezoidal tabs (in the Z axis) * `F` trapezoidal tabs (in the Z axis)
* `F` ease-in and ease-out especially on tab cut-out start/stop * `F` ease-in and ease-out especially on tab cut-out start/stop
* `F` implement z line-only follows for ball/taper
* `F` add option to spiral in vs out (optimal tool life) vs mixed (optimal path) * `F` add option to spiral in vs out (optimal tool life) vs mixed (optimal path)
* `F` add support for tapered ball mills * `F` add support for tapered ball mills
* `F` warn when part > stock or cuts go outside bed * `F` warn when part > stock or cuts go outside bed

View file

@ -1,6 +1,6 @@
{ {
"name": "grid-apps", "name": "grid-apps",
"version": "2.5.7D", "version": "2.5.8D",
"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",

View file

@ -6,7 +6,7 @@
if (!self.kiri) { if (!self.kiri) {
self.kiri = { self.kiri = {
beta: 2507, beta: 2508,
driver: {}, // driver modules driver: {}, // driver modules
loader: [] // module loading: array of functions loader: [] // module loading: array of functions
}; };

View file

@ -323,7 +323,9 @@
}, },
tweak, tweak,
util: { util: {
isSecure isSecure,
ui2rec: updateSettingsFromFields,
rec2ui: updateFieldsFromSettings
}, },
view: { view: {
get: function() { return viewMode }, get: function() { return viewMode },
@ -1633,46 +1635,51 @@
******************************************************************* */ ******************************************************************* */
// given a settings region, update values of matching bound UI fields // given a settings region, update values of matching bound UI fields
function updateFieldsFromSettings(scope) { function updateFieldsFromSettings(setrec, uirec = UI) {
if (!scope) return console.trace("missing scope"); if (!setrec) {
for (let key in scope) { return console.trace("missing scope");
if (!scope.hasOwnProperty(key)) continue; }
let val = scope[key]; for (let key in setrec) {
if (UI.hasOwnProperty(key)) { if (!setrec.hasOwnProperty(key)) {
let uie = UI[key], typ = uie ? uie.type : null; continue;
if (typ === 'text') { }
if (uie.setv) { let val = setrec[key];
uie.setv(val); if (!uirec.hasOwnProperty(key)) {
} else { continue;
uie.value = val; }
} let uie = uirec[key], typ = uie ? uie.type : null;
} else if (typ === 'checkbox') { if (typ === 'text') {
uie.checked = val; if (uie.setv) {
} else if (typ === 'select-one') { uie.setv(val);
uie.innerHTML = ''; } else {
let source = uie.parentNode.getAttribute('source'), uie.value = val;
list = settings[source] || lists[source], }
chosen = null; } else if (typ === 'checkbox') {
if (list) list.forEach(function(el, index) { uie.checked = val;
let id = el.id || el.name; } else if (typ === 'select-one') {
let ev = el.value || id; uie.innerHTML = '';
if (val == id) { let source = uie.parentNode.getAttribute('source'),
chosen = index; list = settings[source] || lists[source],
} chosen = null;
let opt = DOC.createElement('option'); if (list) list.forEach(function(el, index) {
opt.appendChild(DOC.createTextNode(el.name)); let id = el.id || el.name;
opt.setAttribute('value', ev); let ev = el.value || id;
uie.appendChild(opt); if (val == id) {
}); chosen = index;
if (chosen) {
uie.selectedIndex = chosen;
}
} else if (typ === 'textarea') {
if (Array.isArray(val)) {
uie.value = val.join('\n');
} else {
uie.value = '';
} }
let opt = DOC.createElement('option');
opt.appendChild(DOC.createTextNode(el.name));
opt.setAttribute('value', ev);
uie.appendChild(opt);
});
if (chosen) {
uie.selectedIndex = chosen;
}
} else if (typ === 'textarea') {
if (Array.isArray(val)) {
uie.value = val.join('\n');
} else {
uie.value = '';
} }
} }
} }
@ -1681,44 +1688,47 @@
/** /**
* @returns {Object} * @returns {Object}
*/ */
function updateSettingsFromFields(scope) { function updateSettingsFromFields(setrec, uirec = UI) {
if (!scope) return console.trace("missing scope"); if (!setrec) {
return console.trace("missing scope");
}
let key, changed = false; let key, changed = false;
// for each key in scope object // for each key in setrec object
for (key in scope) { for (key in setrec) {
if (!scope.hasOwnProperty(key)) { if (!setrec.hasOwnProperty(key)) {
continue; continue;
} }
if (UI.hasOwnProperty(key)) { if (!uirec.hasOwnProperty(key)) {
let nval = null, uie = UI[key]; continue;
// skip empty UI values }
if (!uie || uie === '') { let nval = null, uie = uirec[key];
continue; // skip empty UI values
} if (!uie || uie === '') {
if (uie.type === 'text') { continue;
nval = UI[key].convert(); }
} else if (uie.type === 'checkbox') { if (uie.type === 'text') {
nval = UI[key].checked; nval = uirec[key].convert();
} else if (uie.type === 'select-one') { } else if (uie.type === 'checkbox') {
if (uie.selectedIndex >= 0) { nval = uirec[key].checked;
nval = uie.options[uie.selectedIndex].value; } else if (uie.type === 'select-one') {
let src = uie.parentNode.getAttribute('source'); if (uie.selectedIndex >= 0) {
if (src === 'tools') { nval = uie.options[uie.selectedIndex].value;
nval = parseInt(nval); let src = uie.parentNode.getAttribute('source');
} if (src === 'tools') {
} else { nval = parseInt(nval);
nval = scope[key];
} }
} else if (uie.type === 'textarea') {
nval = uie.value.trim().split('\n').filter(v => v !== '');
} else { } else {
continue; nval = setrec[key];
}
if (scope[key] != nval) {
scope[key] = nval;
} }
} else if (uie.type === 'textarea') {
nval = uie.value.trim().split('\n').filter(v => v !== '');
} else {
continue;
}
if (setrec[key] != nval) {
setrec[key] = nval;
} }
} }

View file

@ -13,6 +13,7 @@
lastGroup = null, lastGroup = null,
lastDiv = null, lastDiv = null,
addTo = null, addTo = null,
bindTo = null,
groups = {}, groups = {},
groupShow = {}, groupShow = {},
groupSticky = false, groupSticky = false,
@ -51,6 +52,9 @@
toFloat, toFloat,
hidePop, hidePop,
isPopped, isPopped,
isSticky,
setSticky,
newElement,
newBoolean, newBoolean,
newButton, newButton,
newBlank, newBlank,
@ -177,13 +181,12 @@
return { addTo: at || addTo, lastDiv: at || lastDiv, lastGroup, groupName }; return { addTo: at || addTo, lastDiv: at || lastDiv, lastGroup, groupName };
} }
function restore(opt) { function restore(opt = {}) {
if (opt) { addTo = opt.addTo || addTo;
addTo = opt.addTo; bindTo = opt.bindTo || null;
lastDiv = opt.lastDiv; lastDiv = opt.lastDiv || lastDiv;
lastGroup = opt.lastGroup; lastGroup = opt.lastGroup || lastGroup;
groupName = opt.groupName; groupName = opt.groupName || groupName;
}
} }
// at present only used by the layers popup menu // at present only used by the layers popup menu
@ -194,13 +197,31 @@
return div; return div;
} }
function newElement(type, opt = {}) {
let el = DOC.createElement(type);
if (opt.id) {
el.setAttribute("id", opt.id);
}
if (opt.class) {
for (let cl of opt.class.split(' ')) {
el.classList.add(cl);
}
}
if (opt.attr) {
for (let [key, val] of Object.entries(opt.attr)) {
el.setAttribute(key, val);
}
}
return el;
}
function newGroup(label, div, options) { function newGroup(label, div, options) {
lastDiv = div || lastDiv; lastDiv = div || lastDiv;
return addCollapsableGroup(label, lastDiv, options); return addCollapsableGroup(label, lastDiv, options);
} }
function addCollapsableGroup(label, div, options) { function addCollapsableGroup(label, div, opt = {}) {
let opt = options || {};
let group = opt.group || label; let group = opt.group || label;
poppable[group] = opt.inline === true ? false : true; poppable[group] = opt.inline === true ? false : true;
@ -383,8 +404,8 @@
} }
} }
function addModeControls(el, options) { function addModeControls(el, opt = {}) {
let opt = options || {}; el.__opt = opt;
el.__show = true; el.__show = true;
el.__modeSave = null; el.__modeSave = null;
el.showMe = function() { el.showMe = function() {
@ -444,6 +465,14 @@
return lastPop !== null; return lastPop !== null;
} }
function isSticky() {
return groupSticky;
}
function setSticky(bool) {
groupSticky = bool;
}
function newGCode(label, options) { function newGCode(label, options) {
let opt = options || {}, let opt = options || {},
row = lastDiv, row = lastDiv,
@ -454,7 +483,7 @@
txt.setAttribute("wrap", "off"); txt.setAttribute("wrap", "off");
txt.setAttribute("spellcheck", "false"); txt.setAttribute("spellcheck", "false");
txt.setAttribute("style", "resize: none"); txt.setAttribute("style", "resize: none");
txt.onblur = inputAction; txt.onblur = bindTo || inputAction;
btn.setAttribute("class", "basis-50"); btn.setAttribute("class", "basis-50");
btn.appendChild(DOC.createTextNode(label)); btn.appendChild(DOC.createTextNode(label));
@ -585,7 +614,7 @@
size = opt ? opt.size || 5 : 5, size = opt ? opt.size || 5 : 5,
height = opt ? opt.height : 0, height = opt ? opt.height : 0,
ip = height > 1 ? DOC.createElement('textarea') : DOC.createElement('input'), ip = height > 1 ? DOC.createElement('textarea') : DOC.createElement('input'),
action = opt.action || inputAction; action = opt.action || bindTo || inputAction;
row.appendChild(newLabel(label)); row.appendChild(newLabel(label));
row.appendChild(ip); row.appendChild(ip);
@ -674,7 +703,7 @@
let row = newDiv(options), let row = newDiv(options),
ip = DOC.createElement('input'), ip = DOC.createElement('input'),
hide = options && options.hide, hide = options && options.hide,
action = inputAction; action = bindTo || inputAction;
if (label) row.appendChild(newLabel(label)); if (label) row.appendChild(newLabel(label));
row.appendChild(ip); row.appendChild(ip);
@ -700,7 +729,7 @@
let row = newDiv(options), let row = newDiv(options),
ip = DOC.createElement('select'), ip = DOC.createElement('select'),
hide = options && options.hide, hide = options && options.hide,
action = inputAction; action = bindTo || inputAction;
row.appendChild(newLabel(label)); row.appendChild(newLabel(label));
row.appendChild(ip); row.appendChild(ip);
@ -714,7 +743,7 @@
if (options.action) action = options.action; if (options.action) action = options.action;
} }
ip.setVisible = row.setVisible; ip.setVisible = row.setVisible;
ip.onchange = function() { ip.onchange = function(ev) {
action(); action();
}; };
ip.onclick = (ev) => { ip.onclick = (ev) => {
@ -728,7 +757,7 @@
return ip; return ip;
} }
function newBoolean(label, action, options) { function newBoolean(label, action = bindTo, options) {
let row = newDiv(options), let row = newDiv(options),
ip = DOC.createElement('input'), ip = DOC.createElement('input'),
hide = options && options.hide; hide = options && options.hide;
@ -748,7 +777,7 @@
row.setAttribute("title", options.title); row.setAttribute("title", options.title);
} }
} }
if (action) ip.onclick = function() { action() }; if (action) ip.onclick = function(ev) { action(ip) };
ip.setVisible = row.setVisible; ip.setVisible = row.setVisible;
return ip; return ip;

View file

@ -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.D7" VERSION: "2.5.D8"
}; };
if (typeof(module) === 'object') { if (typeof(module) === 'object') {

View file

@ -21,6 +21,7 @@
let traceOp = { type: "trace" }, let traceOp = { type: "trace" },
zaxis = { x: 0, y: 0, z: 1 }, zaxis = { x: 0, y: 0, z: 1 },
popOp = {},
func = {}; func = {};
CAM.restoreTabs = restoreTabs; CAM.restoreTabs = restoreTabs;
@ -96,11 +97,12 @@
let hasTabs = false; let hasTabs = false;
let hasTraces = false; let hasTraces = false;
// ensure trace op is a singleton for now // ensure trace op is a singleton for now
if (isCamMode && proc.ops) if (isCamMode && proc.ops) {
for (let i=0; i<proc.ops.length; i++) { proc.ops = proc.ops
if (proc.ops[i].type === "trace") { .filter(v => v)
proc.ops[i] = traceOp; .map(op => {
} return op.type === 'trace' ? traceOp : op
});
} }
// 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 => {
@ -306,30 +308,8 @@
let bind = {}; let bind = {};
let scale = API.view.unit_scale(); let scale = API.view.unit_scale();
oplist.forEach((rec,i) => { oplist.forEach((rec,i) => {
let entries = 0;
let title = Object.entries(rec).filter((v,i) => {
return v[0] !== 'type'
}).map(v => {
entries++;
let [ key, value ] = v;
switch (key) {
case 'tool':
value = new CAM.Tool(current, v[1]).getName();
break;
case 'down':
case 'rate':
case 'plunge':
case 'leave':
case 'lift':
value = (parseFloat(v[1]) / scale).round(2);
break;
}
return `<tr><th>${key}</th><td>${value}</td></tr>`;
}).join("\n");
title = `<table>${title}</table>`;
html.appendAll([ html.appendAll([
`<div id="${mark+i}">`, `<div id="${mark+i}" class="draggable">`,
entries ? `<div class="opop">${title}</div>` : '',
`<label class="label">${rec.type}</label>`, `<label class="label">${rec.type}</label>`,
`<label id="${mark+i}-x" class="del"><i class="fas fa-times"></i></label>`, `<label id="${mark+i}-x" class="del"><i class="fas fa-times"></i></label>`,
`</div>` `</div>`
@ -339,6 +319,7 @@
let listel = $('oplist'); let listel = $('oplist');
listel.innerHTML = html.join(''); listel.innerHTML = html.join('');
let bounds = []; let bounds = [];
let unpop = null;
for (let [id, rec] of Object.entries(bind)) { for (let [id, rec] of Object.entries(bind)) {
$(`${id}-x`).onmousedown = (ev) => { $(`${id}-x`).onmousedown = (ev) => {
ev.stopPropagation(); ev.stopPropagation();
@ -351,16 +332,49 @@
}; };
let el = $(id); let el = $(id);
bounds.push(el); bounds.push(el);
let timer = null;
let inside = true;
let poprec = popOp[rec.type];
el.unpop = () => {
let pos = [...el.childNodes].indexOf(poprec.div);
if (pos >= 0) {
el.removeChild(poprec.div);
}
};
el.onmouseenter = (ev) => {
if (unpop) unpop();
unpop = el.unpop;
inside = true;
poprec.use(rec);
el.appendChild(poprec.div);
// option click event appears latent
// and overides the sticky settings
setTimeout(() => {
UC.setSticky(false);
}, 0);
};
el.onmouseleave = () => {
inside = false;
clearTimeout(timer);
timer = setTimeout(() => {
if (!inside && poprec.using(rec) && !UC.isSticky()) {
el.unpop();
}
}, 250);
};
el.rec = rec; el.rec = rec;
el.onmousedown = (ev) => { el.onmousedown = (ev) => {
let target = ev.target; let target = ev.target, clist = target.classList;
target.classList.add("drag"); if (!clist.contains("draggable")) {
return;
}
clist.add("drag");
ev.stopPropagation(); ev.stopPropagation();
ev.preventDefault(); ev.preventDefault();
let tracker = UI.tracker; let tracker = UI.tracker;
tracker.style.display = 'block'; tracker.style.display = 'block';
let cancel = tracker.onmouseup = (ev) => { let cancel = tracker.onmouseup = (ev) => {
target.classList.remove("drag"); clist.remove("drag");
tracker.style.display = 'none'; tracker.style.display = 'none';
if (ev) { if (ev) {
ev.stopPropagation(); ev.stopPropagation();
@ -697,8 +711,78 @@
} }
func.hover(data); func.hover(data);
}); });
function hasSpindle() {
return current.device.spindleMax > 0;
}
createPopOp('rough').inputs = {
tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
sep: UC.newBlank({class:"pop-sep"}),
spindle: UC.newInput(LANG.cc_spnd_s, {title:LANG.cc_spnd_l, convert:UC.toInt, show:hasSpindle}),
down: UC.newInput(LANG.cc_sdwn_s, {title:LANG.cc_sdwn_l, convert:UC.toFloat, units:true}),
step: UC.newInput(LANG.cc_sovr_s, {title:LANG.cc_sovr_l, convert:UC.toFloat, bound:UC.bound(0.01,1.0)}),
rate: UC.newInput(LANG.cc_feed_s, {title:LANG.cc_feed_l, convert:UC.toInt, units:true}),
plunge: UC.newInput(LANG.cc_plng_s, {title:LANG.cc_plng_l, convert:UC.toInt, units:true}),
leave: UC.newInput(LANG.cr_lsto_s, {title:LANG.cr_lsto_l, convert:UC.toFloat, units:true}),
sep: UC.newBlank({class:"pop-sep"}),
voids: UC.newBoolean(LANG.cr_clrp_s, undefined, {title:LANG.cr_clrp_l}),
flats: UC.newBoolean(LANG.cr_clrf_s, undefined, {title:LANG.cr_clrf_l}),
top: UC.newBoolean(LANG.cr_clrt_s, undefined, {title:LANG.cr_clrt_l}),
inside: UC.newBoolean(LANG.cr_olin_s, undefined, {title:LANG.cr_olin_l}),
};
createPopOp('outline').inputs = {
tool: UC.newSelect(LANG.cc_tool, {}, "tools"),
sep: UC.newBlank({class:"pop-sep"}),
spindle: UC.newInput(LANG.cc_spnd_s, {title:LANG.cc_spnd_l, convert:UC.toInt, show:hasSpindle}),
down: UC.newInput(LANG.cc_sdwn_s, {title:LANG.cc_sdwn_l, convert:UC.toFloat, units:true}),
step: UC.newInput(LANG.cc_sovr_s, {title:LANG.cc_sovr_l, convert:UC.toFloat, bound:UC.bound(0.01,1.0)}),
rate: UC.newInput(LANG.cc_feed_s, {title:LANG.cc_feed_l, convert:UC.toInt, units:true}),
plunge: UC.newInput(LANG.cc_plng_s, {title:LANG.cc_plng_l, convert:UC.toInt, units:true}),
sep: UC.newBlank({class:"pop-sep"}),
dogbones: UC.newBoolean(LANG.co_dogb_s, undefined, {title:LANG.co_dogb_l, show:(op) => { return !op.inputs.wide.checked }}),
inside: UC.newBoolean(LANG.co_olin_s, undefined, {title:LANG.co_olin_l, show:(op) => { return !op.inputs.outside.checked }}),
outside: UC.newBoolean(LANG.co_olot_s, undefined, {title:LANG.co_olot_l, show:(op) => { return !op.inputs.inside.checked }}),
wide: UC.newBoolean(LANG.co_wide_s, undefined, {title:LANG.co_wide_l, show:(op) => { return !op.inputs.inside.checked }}),
};
}; };
function createPopOp(type) {
let op = popOp[type] = {
div: UC.newElement('div', { id:`${type}-op`, class:"cam-pop-op" }),
use: (rec) => {
op.rec = rec;
API.util.rec2ui(rec, op.inputs);
op.hideshow();
},
using: (rec) => {
return op.rec === rec;
},
bind: (ev) => {
API.util.ui2rec(op.rec, op.inputs);
API.conf.save();
op.hideshow();
},
hideshow: () => {
for (let inp of Object.values(op.inputs)) {
let parent = inp.parentElement;
if (parent.setVisible && parent.__opt.show) {
parent.setVisible(parent.__opt.show(op));
}
}
},
group: []
};
UC.restore({
addTo: op.div,
bindTo: op.bind,
lastDiv: op.div,
lastGroup: op.group
});
return op;
}
function createTabBox(iw, ic, n) { function createTabBox(iw, ic, n) {
const { track } = iw; const { track } = iw;
const { stock, bounds, process } = API.conf.get(); const { stock, bounds, process } = API.conf.get();

View file

@ -548,7 +548,19 @@ th, tr, td, label {
background-color: var(--bg-gray); background-color: var(--bg-gray);
margin-left: 1px; margin-left: 1px;
} }
#oplist div { .cam-pop-op {
text-align: left;
position: absolute;
white-space: nowrap;
flex-direction: column;
bottom: 40px;
background-color: rgba(255,255,255,0.8);
border: 1px solid #aaa;
border-radius: 5px;
margin-right: 5px;
padding: 5px;
}
#oplist > div {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;