2813 lines
120 KiB
JavaScript
2813 lines
120 KiB
JavaScript
|
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
|
|
|
"use strict";
|
|
|
|
(function() {
|
|
|
|
if (self.kiri.init) return;
|
|
|
|
const KIRI = self.kiri,
|
|
BASE = self.base,
|
|
MOTO = self.moto,
|
|
CONF = KIRI.conf,
|
|
WIN = self.window,
|
|
DOC = self.document,
|
|
LOC = self.location,
|
|
API = KIRI.api,
|
|
SDB = API.sdb,
|
|
UI = API.ui,
|
|
UC = API.uc,
|
|
SEED = API.const.SEED,
|
|
LANG = API.const.LANG,
|
|
VIEWS = API.const.VIEWS,
|
|
MODES = API.const.MODES,
|
|
LOCAL = API.const.LOCAL,
|
|
SETUP = API.const.SETUP,
|
|
DEFMODE = SETUP.dm && SETUP.dm.length === 1 ? SETUP.dm[0] : 'FDM',
|
|
STARTMODE = SETUP.sm && SETUP.sm.length === 1 ? SETUP.sm[0] : null,
|
|
SPACE = KIRI.space,
|
|
STATS = API.stats,
|
|
DEG = Math.PI/180,
|
|
ALL = [MODES.FDM, MODES.LASER, MODES.CAM, MODES.SLA],
|
|
CAM = [MODES.CAM],
|
|
SLA = [MODES.SLA],
|
|
FDM = [MODES.FDM],
|
|
FDM_SLA = [MODES.FDM,MODES.SLA],
|
|
FDM_CAM = [MODES.CAM,MODES.FDM],
|
|
FDM_LASER = [MODES.LASER,MODES.FDM],
|
|
FDM_LASER_SLA = [MODES.LASER,MODES.FDM,MODES.SLA],
|
|
CAM_LASER = [MODES.LASER,MODES.CAM],
|
|
GCODE = [MODES.FDM, MODES.LASER, MODES.CAM],
|
|
LASER = [MODES.LASER],
|
|
CATALOG = API.catalog,
|
|
js2o = API.js2o,
|
|
o2js = API.o2js,
|
|
platform = API.platform,
|
|
selection = API.selection,
|
|
proto = location.protocol,
|
|
ver = Date.now().toString(36);
|
|
|
|
let currentDevice = null,
|
|
deviceURL = null,
|
|
deviceTexture = null,
|
|
deviceFilter = null,
|
|
deviceImage = null,
|
|
selectedTool = null,
|
|
editTools = null,
|
|
maxTool = 0,
|
|
fpsTimer = null,
|
|
platformColor,
|
|
contextInt;
|
|
|
|
// extend KIRI API with local functions
|
|
API.show.devices = showDevices;
|
|
API.device.set = selectDevice;
|
|
API.device.clone = cloneDevice;
|
|
|
|
function settings() {
|
|
return API.conf.get();
|
|
}
|
|
|
|
function checkSeed(then) {
|
|
// skip sample object load in onshape (or any script postload)
|
|
if (!SDB[SEED]) {
|
|
SDB[SEED] = new Date().getTime();
|
|
if (!SETUP.s && API.feature.seed) {
|
|
if (SETUP.debug) {
|
|
return then();
|
|
}
|
|
platform.load_stl("/obj/cube.stl", function(vert) {
|
|
CATALOG.putFile("sample cube.stl", vert);
|
|
platform.compute_max_z();
|
|
SPACE.view.home();
|
|
setTimeout(() => { API.space.save(true) },500);
|
|
then();
|
|
API.help.show();
|
|
});
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function unitsSave() {
|
|
API.conf.update({controller:true});
|
|
platform.update_size();
|
|
}
|
|
|
|
function aniMeshSave() {
|
|
API.conf.update({controller:true});
|
|
API.conf.save();
|
|
}
|
|
|
|
function lineTypeSave() {
|
|
const sel = UI.lineType.options[UI.lineType.selectedIndex];
|
|
if (sel) {
|
|
settings().controller.lineType = sel.value;
|
|
API.conf.save();
|
|
}
|
|
}
|
|
|
|
function detailSave() {
|
|
let level = UI.detail.options[UI.detail.selectedIndex];
|
|
if (level) {
|
|
level = level.value;
|
|
let rez = BASE.config.clipperClean;
|
|
switch (level) {
|
|
case '100': rez = 50; break;
|
|
case '75': rez = BASE.config.clipperClean; break;
|
|
case '50': rez = 500; break;
|
|
case '25': rez = 1000; break;
|
|
}
|
|
KIRI.client.config({
|
|
base: { clipperClean: rez }
|
|
});
|
|
settings().controller.detail = level;
|
|
API.conf.save();
|
|
}
|
|
}
|
|
|
|
function speedSave() {
|
|
settings().controller.showSpeeds = UI.showSpeeds.checked;
|
|
API.platform.update_speeds();
|
|
}
|
|
|
|
function zAnchorSave() {
|
|
API.conf.update();
|
|
API.platform.update_top_z();
|
|
}
|
|
|
|
function booleanSave() {
|
|
let control = settings().controller;
|
|
let isDark = control.dark;
|
|
let doAlert = UI.ortho.checked !== control.ortho;
|
|
if (control.assembly != UI.assembly.checked) {
|
|
KIRI.client.wasm(UI.assembly.checked);
|
|
}
|
|
control.decals = UI.decals.checked;
|
|
control.danger = UI.danger.checked;
|
|
control.showOrigin = UI.showOrigin.checked;
|
|
control.showRulers = UI.showRulers.checked;
|
|
control.autoLayout = UI.autoLayout.checked;
|
|
control.freeLayout = UI.freeLayout.checked;
|
|
control.autoSave = UI.autoSave.checked;
|
|
control.reverseZoom = UI.reverseZoom.checked;
|
|
control.dark = UI.dark.checked;
|
|
control.exportOcto = UI.exportOcto.checked;
|
|
control.exportGhost = UI.exportGhost.checked;
|
|
control.exportLocal = UI.exportLocal.checked;
|
|
control.exportThumb = UI.exportThumb.checked;
|
|
control.exportPreview = UI.exportPreview.checked;
|
|
control.decimate = UI.decimate.checked;
|
|
control.healMesh = UI.healMesh.checked;
|
|
control.threaded = UI.threaded.checked;
|
|
control.assembly = UI.assembly.checked;
|
|
control.ortho = UI.ortho.checked;
|
|
control.devel = UI.devel.checked;
|
|
SPACE.view.setZoom(control.reverseZoom, control.zoomSpeed);
|
|
// platform.layout();
|
|
API.conf.save();
|
|
API.platform.update_size();
|
|
API.catalog.setOptions({
|
|
maxpass: control.decimate ? 10 : 0
|
|
});
|
|
UC.setHoverPop(false);
|
|
updateFPS();
|
|
if (control.decals) {
|
|
loadDeviceTexture(currentDevice, deviceTexture);
|
|
} else {
|
|
clearDeviceTexture();
|
|
}
|
|
API.event.emit('boolean.update');
|
|
if (doAlert) {
|
|
API.show.alert("change requires page refresh");
|
|
}
|
|
}
|
|
|
|
function updateFPS() {
|
|
clearTimeout(fpsTimer);
|
|
UI.fps.style.display = 'block';
|
|
setInterval(() => {
|
|
const nv = SPACE.view.getFPS().toFixed(2);
|
|
if (nv !== UI.fps.innerText) {
|
|
UI.fps.innerText = nv;
|
|
}
|
|
}, 100);
|
|
}
|
|
|
|
function parentWithClass(el, classname) {
|
|
while (el) {
|
|
if (el.classList.contains(classname)) {
|
|
return el;
|
|
}
|
|
el = el.parentNode;
|
|
}
|
|
return el;
|
|
}
|
|
|
|
// function onLayerToggle() {
|
|
// API.conf.update();
|
|
// API.show.slices();
|
|
// }
|
|
|
|
function onBooleanClick() {
|
|
// prevent hiding elements in device editor on clicks
|
|
// if (!API.modal.visible()) {
|
|
UC.refresh();
|
|
// }
|
|
API.conf.update();
|
|
DOC.activeElement.blur();
|
|
API.event.emit("boolean.click");
|
|
}
|
|
|
|
function onButtonClick(ev) {
|
|
let target = ev.target;
|
|
while (target && target.tagName !== 'BUTTON') {
|
|
target = target.parentNode;
|
|
}
|
|
API.event.emit("button.click", target);
|
|
}
|
|
|
|
function inputHasFocus() {
|
|
let active = DOC.activeElement;
|
|
return active && (active.nodeName === "INPUT" || active.nodeName === "TEXTAREA");
|
|
}
|
|
|
|
function inputTextOK() {
|
|
return DOC.activeElement === UI.deviceName;
|
|
}
|
|
|
|
function textAreaHasFocus() {
|
|
let active = DOC.activeElement;
|
|
return active && active.nodeName === "TEXTAREA";
|
|
}
|
|
|
|
function inputSize() {
|
|
return parseInt(DOC.activeElement.size);
|
|
}
|
|
|
|
function cca(c) {
|
|
return c.charCodeAt(0);
|
|
}
|
|
|
|
function keyUpHandler(evt) {
|
|
if (API.feature.on_key) {
|
|
if (API.feature.on_key({up:evt})) return;
|
|
}
|
|
switch (evt.keyCode) {
|
|
// escape
|
|
case 27:
|
|
// blur text input focus
|
|
DOC.activeElement.blur();
|
|
// dismiss modals
|
|
API.modal.hide();
|
|
// deselect widgets
|
|
platform.deselect();
|
|
// hide all dialogs
|
|
API.dialog.hide();
|
|
// cancel slicing
|
|
API.function.cancel();
|
|
// kill any poppers in compact mode
|
|
UC.hidePoppers();
|
|
// and send an event (used by FDM client)
|
|
API.event.emit("key.esc");
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function keyDownHandler(evt) {
|
|
if (API.modal.visible()) {
|
|
return false;
|
|
}
|
|
if (API.feature.on_key) {
|
|
if (API.feature.on_key({down:evt})) return;
|
|
}
|
|
let move = evt.altKey ? 5 : 0,
|
|
deg = move ? 0 : -Math.PI / (evt.shiftKey ? 36 : 2);
|
|
switch (evt.keyCode) {
|
|
case 8: // apple: delete/backspace
|
|
case 46: // others: delete
|
|
if (inputHasFocus()) return false;
|
|
platform.delete(API.selection.meshes());
|
|
evt.preventDefault();
|
|
break;
|
|
case 37: // left arrow
|
|
if (inputHasFocus()) return false;
|
|
if (deg) API.selection.rotate(0, 0, -deg);
|
|
if (move > 0) API.selection.move(-move, 0, 0);
|
|
evt.preventDefault();
|
|
break;
|
|
case 39: // right arrow
|
|
if (inputHasFocus()) return false;
|
|
if (deg) API.selection.rotate(0, 0, deg);
|
|
if (move > 0) API.selection.move(move, 0, 0);
|
|
evt.preventDefault();
|
|
break;
|
|
case 38: // up arrow
|
|
if (inputHasFocus()) return false;
|
|
if (evt.metaKey) return API.show.layer(API.var.layer_at+1);
|
|
if (deg) API.selection.rotate(deg, 0, 0);
|
|
if (move > 0) API.selection.move(0, move, 0);
|
|
evt.preventDefault();
|
|
break;
|
|
case 40: // down arrow
|
|
if (inputHasFocus()) return false;
|
|
if (evt.metaKey) return API.show.layer(API.var.layer_at-1);
|
|
if (deg) API.selection.rotate(-deg, 0, 0);
|
|
if (move > 0) API.selection.move(0, -move, 0);
|
|
evt.preventDefault();
|
|
break;
|
|
case 65: // 'a' for select all
|
|
if (evt.metaKey || evt.ctrlKey) {
|
|
if (inputHasFocus()) return false;
|
|
evt.preventDefault();
|
|
platform.deselect();
|
|
platform.select_all();
|
|
}
|
|
break;
|
|
case 83: // 's' for save workspace
|
|
if (evt.ctrlKey) {
|
|
evt.preventDefault();
|
|
API.conf.save();
|
|
console.log("settings saved");
|
|
} else
|
|
if (evt.metaKey) {
|
|
evt.preventDefault();
|
|
API.space.save();
|
|
}
|
|
break;
|
|
case 76: // 'l' for restore workspace
|
|
if (evt.metaKey) {
|
|
evt.preventDefault();
|
|
API.space.restore();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
function keyHandler(evt) {
|
|
let handled = true;
|
|
if (API.modal.visible() || inputHasFocus()) {
|
|
return false;
|
|
}
|
|
if (API.feature.on_key) {
|
|
if (API.feature.on_key({key:evt})) return;
|
|
}
|
|
if (evt.ctrlKey) {
|
|
switch (evt.key) {
|
|
case 'g': return API.group.merge();
|
|
case 'u': return API.group.split();
|
|
}
|
|
}
|
|
switch (evt.charCode) {
|
|
case cca('`'): API.show.slices(0); break;
|
|
case cca('0'): API.show.slices(API.var.layer_max); break;
|
|
case cca('1'): API.show.slices(API.var.layer_max/10); break;
|
|
case cca('2'): API.show.slices(API.var.layer_max*2/10); break;
|
|
case cca('3'): API.show.slices(API.var.layer_max*3/10); break;
|
|
case cca('4'): API.show.slices(API.var.layer_max*4/10); break;
|
|
case cca('5'): API.show.slices(API.var.layer_max*5/10); break;
|
|
case cca('6'): API.show.slices(API.var.layer_max*6/10); break;
|
|
case cca('7'): API.show.slices(API.var.layer_max*7/10); break;
|
|
case cca('8'): API.show.slices(API.var.layer_max*8/10); break;
|
|
case cca('9'): API.show.slices(API.var.layer_max*9/10); break;
|
|
case cca('?'):
|
|
API.help.show();
|
|
break;
|
|
case cca('Z'): // reset stored state
|
|
UC.confirm('clear all settings and preferences?').then(yes => {
|
|
if (yes) SDB.clear();
|
|
});
|
|
break;
|
|
case cca('C'): // refresh catalog
|
|
CATALOG.refresh();
|
|
break;
|
|
case cca('i'): // file import
|
|
API.event.import();
|
|
break;
|
|
case cca('S'): // slice
|
|
case cca('s'): // slice
|
|
if (evt.shiftKey) {
|
|
API.show.alert('CAPS lock on?');
|
|
}
|
|
API.function.slice();
|
|
break;
|
|
case cca('P'): // prepare
|
|
case cca('p'): // prepare
|
|
if (evt.shiftKey) {
|
|
API.show.alert('CAPS lock on?');
|
|
}
|
|
if (API.mode.get() !== 'SLA') {
|
|
// hidden in SLA mode
|
|
API.function.print();
|
|
}
|
|
break;
|
|
case cca('X'): // export
|
|
case cca('x'): // export
|
|
if (evt.shiftKey) {
|
|
API.show.alert('CAPS lock on?');
|
|
}
|
|
API.function.export();
|
|
break;
|
|
case cca('g'): // CAM animate
|
|
API.function.animate();
|
|
break;
|
|
case cca('O'): // manual rotation
|
|
rotateInputSelection();
|
|
break;
|
|
case cca('r'): // recent files
|
|
API.modal.show('files');
|
|
break;
|
|
case cca('q'): // preferences
|
|
API.modal.show('prefs');
|
|
break;
|
|
case cca('l'): // device
|
|
settingsLoad();
|
|
break;
|
|
case cca('e'): // device
|
|
showDevices();
|
|
break;
|
|
case cca('w'): // scale
|
|
UI.tool.scale();
|
|
break;
|
|
case cca('o'): // tools
|
|
showTools();
|
|
break;
|
|
case cca('c'): // local devices
|
|
API.show.local();
|
|
break;
|
|
case cca('v'): // toggle single slice view mode
|
|
if (API.view.get() === VIEWS.ARRANGE) {
|
|
API.space.set_focus(API.selection.widgets());
|
|
}
|
|
if (API.var.layer_hi == API.var.layer_lo) {
|
|
API.var.layer_lo = 0;
|
|
} else {
|
|
API.var.layer_lo = API.var.layer_hi;
|
|
}
|
|
API.show.slices();
|
|
break;
|
|
case cca('d'): // duplicate object
|
|
duplicateSelection();
|
|
break;
|
|
case cca('m'): // mirror object
|
|
mirrorSelection();
|
|
break;
|
|
case cca('R'): // toggle slice render mode
|
|
renderMode++;
|
|
API.function.slice();
|
|
break;
|
|
case cca('a'):
|
|
if (API.view.get() === VIEWS.ARRANGE) {
|
|
// auto arrange items on platform
|
|
platform.layout();
|
|
API.space.set_focus(API.selection.widgets());
|
|
} else {
|
|
// go to arrange view
|
|
API.view.set(VIEWS.ARRANGE);
|
|
}
|
|
break;
|
|
default:
|
|
API.event.emit('keypress', evt);
|
|
handled = false;
|
|
break;
|
|
}
|
|
if (handled) {
|
|
evt.preventDefault();
|
|
evt.stopPropagation();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function layFlat() {
|
|
let int = contextInt[0];
|
|
if (int && int.object && int.object.widget) {
|
|
let q = new THREE.Quaternion();
|
|
q.setFromUnitVectors(contextInt[0].face.normal, new THREE.Vector3(0,0,-1));
|
|
API.selection.rotate(q);
|
|
}
|
|
}
|
|
|
|
function setFocus() {
|
|
let int = contextInt[0];
|
|
if (int && int.object && int.object.widget) {
|
|
API.space.set_focus(undefined, int.point);
|
|
}
|
|
}
|
|
|
|
function duplicateSelection() {
|
|
API.selection.for_widgets(function(widget) {
|
|
let mesh = widget.mesh;
|
|
let bb = mesh.getBoundingBox();
|
|
let ow = widget;
|
|
let nw = API.widgets.new().loadGeometry(mesh.geometry.clone());
|
|
nw.meta.file = ow.meta.file;
|
|
nw.meta.vertices = ow.meta.vertices;
|
|
nw.move(bb.max.x - bb.min.x + 1, 0, 0);
|
|
platform.add(nw,true);
|
|
let owa = API.widgets.annotate(ow.id);
|
|
let nwa = API.widgets.annotate(nw.id);
|
|
if (owa.tab) {
|
|
nwa.tab = Object.clone(owa.tab);
|
|
nwa.tab.forEach((tab,i) => {
|
|
tab.id = Date.now() + i
|
|
});
|
|
}
|
|
KIRI.driver.CAM.restoreTabs([nw]);
|
|
});
|
|
}
|
|
|
|
function mirrorSelection() {
|
|
API.selection.mirror();
|
|
}
|
|
|
|
function keys(o) {
|
|
let key, list = [];
|
|
for (key in o) { if (o.hasOwnProperty(key)) list.push(key) }
|
|
return list.sort();
|
|
}
|
|
|
|
function clearSelected(children) {
|
|
for (let i=0; i<children.length; i++) {
|
|
children[i].setAttribute('class','');
|
|
}
|
|
}
|
|
|
|
function rotateInputSelection() {
|
|
if (API.selection.meshes().length === 0) {
|
|
API.show.alert("select object to rotate");
|
|
return;
|
|
}
|
|
let coord = (prompt("Enter X,Y,Z degrees of rotation") || '').split(','),
|
|
prod = Math.PI / 180,
|
|
x = parseFloat(coord[0] || 0.0) * prod,
|
|
y = parseFloat(coord[1] || 0.0) * prod,
|
|
z = parseFloat(coord[2] || 0.0) * prod;
|
|
|
|
API.selection.rotate(x, y, z);
|
|
}
|
|
|
|
function positionSelection() {
|
|
if (API.selection.meshes().length === 0) {
|
|
API.show.alert("select object to position");
|
|
return;
|
|
}
|
|
let current = settings(),
|
|
center = current.process.outputOriginCenter || current.device.bedRound,
|
|
bounds = boundsSelection(),
|
|
coord = prompt("Enter X,Y coordinates for selection").split(','),
|
|
x = parseFloat(coord[0] || 0.0),
|
|
y = parseFloat(coord[1] || 0.0),
|
|
z = parseFloat(coord[2] || 0.0);
|
|
|
|
if (!center) {
|
|
x = x - current.device.bedWidth/2 + (bounds.max.x - bounds.min.x)/2;
|
|
y = y - current.device.bedDepth/2 + (bounds.max.y - bounds.min.y)/2
|
|
}
|
|
|
|
API.selection.move(x, y, z, true);
|
|
}
|
|
|
|
function deviceExport(exp, name) {
|
|
name = (name || "device")
|
|
.toLowerCase()
|
|
.replace(/ /g,'_')
|
|
.replace(/\./g,'_');
|
|
UC.prompt("Export Device Filename", name).then(name => {
|
|
if (name) {
|
|
API.util.download(exp, `${name}.km`);
|
|
}
|
|
});
|
|
}
|
|
|
|
function objectsExport(format = "stl") {
|
|
// return API.selection.export();
|
|
UC.confirm("Export Filename", {ok:true, cancel: false}, `selected.${format}`).then(name => {
|
|
if (!name) return;
|
|
if (name.toLowerCase().indexOf(`.${format}`) < 0) {
|
|
name = `${name}.${format}`;
|
|
}
|
|
API.util.download(API.selection.export(format), name);
|
|
});
|
|
}
|
|
|
|
function profileExport(workspace) {
|
|
let checked = workspace ? ' checked' : '';
|
|
const opt = {pre: [
|
|
"<div class='f-col a-center'>",
|
|
` <h3>${workspace ? "Workspace" : "Profile"} Export</h3>`,
|
|
" <label>This will create a backup of</label>",
|
|
workspace ?
|
|
" <label>your workspace and settings</label>" :
|
|
" <label>your device profiles and settings</label><br>",
|
|
` <div class='f-row' style="display:${workspace ? 'none' : ''}">`,
|
|
` <input id='incwork' type='checkbox'${checked}> include workspace`,
|
|
" </div>",
|
|
"</div>"
|
|
]};
|
|
UC.confirm("Export Filename", {ok:true, cancel: false}, "workspace", opt).then(name => {
|
|
if (name) {
|
|
let work = $('incwork').checked;
|
|
let json = API.conf.export({work, clear:true});
|
|
|
|
KIRI.client.zip([{
|
|
name:"workspace.json", data:JSON.stringify(json)}
|
|
], progress => {
|
|
API.show.progress(progress.percent/100, "compressing workspace");
|
|
}, output => {
|
|
API.show.progress(0);
|
|
API.util.download(output, `${name}.kmz`);
|
|
})
|
|
}
|
|
});
|
|
}
|
|
|
|
function settingsSave(ev, name) {
|
|
if (ev) {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
}
|
|
|
|
API.dialog.hide();
|
|
let mode = API.mode.get(),
|
|
s = settings(),
|
|
def = "default",
|
|
cp = s.process,
|
|
pl = s.sproc[mode],
|
|
lp = s.cproc[mode],
|
|
saveAs = (name) => {
|
|
if (!name) {
|
|
return;
|
|
}
|
|
let np = pl[name] = {};
|
|
cp.processName = name;
|
|
pl[name] = Object.clone(cp);
|
|
for (let k in cp) {
|
|
if (!cp.hasOwnProperty(k)) continue;
|
|
np[k] = cp[k];
|
|
}
|
|
s.cproc[mode] = name;
|
|
s.devproc[s.device.deviceName] = name;
|
|
API.conf.save();
|
|
API.conf.update();
|
|
API.event.settings();
|
|
};
|
|
|
|
if (name) {
|
|
saveAs(name);
|
|
} else {
|
|
UC.prompt("Save Settings As", cp ? lp || def : def).then(saveAs);
|
|
}
|
|
}
|
|
|
|
function settingsLoad() {
|
|
UC.hidePoppers();
|
|
API.conf.show();
|
|
}
|
|
|
|
function putLocalDevice(devicename, obj) {
|
|
settings().devices[devicename] = obj;
|
|
API.conf.save();
|
|
}
|
|
|
|
function removeLocalDevice(devicename) {
|
|
delete settings().devices[devicename];
|
|
API.conf.save();
|
|
}
|
|
|
|
function isLocalDevice(devicename) {
|
|
return settings().devices[devicename] ? true : false;
|
|
}
|
|
|
|
function getSelectedDevice() {
|
|
return API.device.get();
|
|
}
|
|
|
|
function selectDevice(devicename) {
|
|
if (isLocalDevice(devicename)) {
|
|
setDeviceCode(settings().devices[devicename], devicename);
|
|
} else {
|
|
let code = devices[API.mode.get_lower()][devicename];
|
|
if (code) {
|
|
setDeviceCode(code, devicename);
|
|
}
|
|
}
|
|
}
|
|
|
|
// only for local filters
|
|
function cloneDevice() {
|
|
let name = `${getSelectedDevice().replace(/\./g,' ')} Copy`;
|
|
let code = API.clone(settings().device);
|
|
code.mode = API.mode.get();
|
|
putLocalDevice(name, code);
|
|
setDeviceCode(code, name);
|
|
}
|
|
|
|
function setDeviceCode(code, devicename) {
|
|
try {
|
|
if (typeof(code) === 'string') code = js2o(code) || {};
|
|
|
|
API.event.emit('device.set', devicename);
|
|
|
|
let mode = API.mode.get(),
|
|
current = settings(),
|
|
local = isLocalDevice(devicename),
|
|
dev = current.device = CONF.device_from_code(code,mode),
|
|
dproc = current.devproc[devicename], // last process name for this device
|
|
newdev = dproc === undefined, // first time device is selected
|
|
predev = current.filter[mode], // previous device selection
|
|
chgdev = predev !== devicename; // device is changing
|
|
|
|
// first time device use, add any print profiles and set to default if present
|
|
if (code.profiles) {
|
|
for (let profile of code.profiles) {
|
|
let profname = profile.processName;
|
|
// if no saved profile by that name for this mode...
|
|
if (!current.sproc[mode][profname]) {
|
|
console.log('adding profile', profname, 'to', mode);
|
|
current.sproc[mode][profname] = profile;
|
|
}
|
|
// if it's a new device, seed the new profile name as last profile
|
|
if (newdev && !current.devproc[devicename]) {
|
|
console.log('setting default profile for new device', devicename, 'to', profname);
|
|
current.devproc[devicename] = dproc = profname;
|
|
}
|
|
}
|
|
}
|
|
|
|
dev.new = false;
|
|
dev.deviceName = devicename;
|
|
|
|
UI.deviceName.value = devicename;
|
|
UI.deviceBelt.checked = dev.bedBelt;
|
|
UI.deviceRound.checked = dev.bedRound;
|
|
UI.deviceOrigin.checked = dev.outputOriginCenter || dev.originCenter || dev.bedRound;
|
|
UI.fwRetract.checked = dev.fwRetract;
|
|
|
|
// add extruder selection buttons
|
|
if (dev.extruders) {
|
|
for (let ext of dev.extruders) {
|
|
// add missing deselect field from legacy configs
|
|
if (!ext.extDeselect) {
|
|
ext.extDeselect = [];
|
|
}
|
|
}
|
|
let ext = API.lists.extruders = [];
|
|
dev.internal = 0;
|
|
let selext = $('pop-nozzle');
|
|
selext.innerHTML = '';
|
|
for (let i=0; i<dev.extruders.length; i++) {
|
|
let d = DOC.createElement('div');
|
|
d.appendChild(DOC.createTextNode(i));
|
|
d.setAttribute('id', `sel-ext-${i}`);
|
|
d.setAttribute('class', 'col j-center');
|
|
d.onclick = function() {
|
|
API.selection.for_widgets(w => {
|
|
API.widgets.annotate(w.id).extruder = i;
|
|
});
|
|
API.platform.update_selected();
|
|
};
|
|
selext.appendChild(d);
|
|
ext.push({id:i, name:i});
|
|
}
|
|
}
|
|
|
|
// disable editing for non-local devices
|
|
[
|
|
UI.deviceName,
|
|
UI.gcodePre,
|
|
UI.gcodePost,
|
|
UI.gcodeExt,
|
|
UI.gcodeInt,
|
|
UI.gcodePause,
|
|
UI.bedDepth,
|
|
UI.bedWidth,
|
|
UI.maxHeight,
|
|
UI.resolutionX,
|
|
UI.resolutionY,
|
|
UI.deviceOrigin,
|
|
UI.deviceRound,
|
|
UI.deviceBelt,
|
|
UI.fwRetract,
|
|
UI.deviceZMax,
|
|
UI.gcodeFan,
|
|
UI.gcodeTrack,
|
|
UI.gcodeLayer,
|
|
UI.extFilament,
|
|
UI.extNozzle,
|
|
UI.spindleMax,
|
|
UI.gcodeSpindle,
|
|
UI.gcodeDwell,
|
|
UI.gcodeChange,
|
|
UI.gcodeFExt,
|
|
UI.gcodeSpace,
|
|
UI.gcodeStrip,
|
|
UI.gcodeLaserOn,
|
|
UI.gcodeLaserOff,
|
|
UI.extPrev,
|
|
UI.extNext,
|
|
UI.extAdd,
|
|
UI.extDel,
|
|
UI.extOffsetX,
|
|
UI.extOffsetY,
|
|
UI.extSelect,
|
|
UI.extDeselect
|
|
].forEach(function(e) {
|
|
e.disabled = !local;
|
|
});
|
|
|
|
UI.deviceSave.disabled = !local;
|
|
UI.deviceDelete.disabled = !local;
|
|
UI.deviceExport.disabled = !local;
|
|
if (local) {
|
|
UI.deviceAdd.innerText = "copy";
|
|
UI.deviceDelete.style.display = '';
|
|
UI.deviceExport.style.display = '';
|
|
} else {
|
|
UI.deviceAdd.innerText = "customize";
|
|
UI.deviceDelete.style.display = 'none';
|
|
UI.deviceExport.style.display = 'none';
|
|
}
|
|
UI.deviceAdd.disabled = dev.noclone;
|
|
|
|
API.view.update_fields();
|
|
platform.update_size();
|
|
platform.update_origin();
|
|
platform.update();
|
|
|
|
// store current device name for this mode
|
|
current.filter[mode] = devicename;
|
|
// cache device record for this mode (restored in setMode)
|
|
current.cdev[mode] = currentDevice = dev;
|
|
|
|
if (dproc) {
|
|
// restore last process associated with this device
|
|
API.conf.load(null, dproc);
|
|
} else {
|
|
API.conf.update();
|
|
}
|
|
|
|
API.conf.save();
|
|
|
|
API.const.SPACE.view.setHome(dev.bedBelt ? Math.PI/2 : 0);
|
|
// when changing devices, update focus on widgets
|
|
if (chgdev) {
|
|
setTimeout(API.space.set_focus, 0);
|
|
}
|
|
|
|
UC.refresh();
|
|
|
|
if (dev.imageURL) {
|
|
if (dev.imageURL !== deviceURL) {
|
|
deviceURL = dev.imageURL;
|
|
loadDeviceImage(dev);
|
|
}
|
|
} else {
|
|
clearDeviceTexture();
|
|
}
|
|
} catch (e) {
|
|
console.log({error:e, device:code, devicename});
|
|
API.show.alert(`invalid or deprecated device: "${devicename}"`, 10);
|
|
API.show.alert(`please select a new device`, 10);
|
|
throw e;
|
|
showDevices();
|
|
}
|
|
API.function.clear();
|
|
API.event.settings();
|
|
}
|
|
|
|
function clearDeviceTexture() {
|
|
if (deviceImage) {
|
|
SPACE.world.remove(deviceImage);
|
|
deviceImage = null;
|
|
deviceURL = null;
|
|
}
|
|
}
|
|
|
|
function loadDeviceImage(dev, url) {
|
|
let turl = url || dev.imageURL;
|
|
if (!turl) return;
|
|
new THREE.TextureLoader().load(turl, texture => {
|
|
loadDeviceTexture(dev, texture);
|
|
}, inc => {
|
|
console.log({load_inc: inc});
|
|
}, error => {
|
|
console.log({load_error: error, turl});
|
|
clearDeviceTexture();
|
|
});
|
|
}
|
|
|
|
function loadDeviceTexture(dev, texture) {
|
|
clearDeviceTexture();
|
|
deviceTexture = texture;
|
|
if (!(texture && API.conf.get().controller.decals)) {
|
|
return;
|
|
}
|
|
let { width, height } = texture.image;
|
|
let { bedWidth, bedDepth, bedHeight } = dev;
|
|
let scale = dev.imageScale || 0.75;
|
|
let img_ratio = width / height;
|
|
if (scale <= 1) {
|
|
let dev_ratio = bedWidth / bedDepth;
|
|
if (dev_ratio > img_ratio) {
|
|
scale *= bedDepth / height;
|
|
} else {
|
|
scale *= bedWidth / width;
|
|
}
|
|
} else if (scale > 1) {
|
|
scale = (scale / Math.max(width, height));
|
|
}
|
|
width *= scale;
|
|
height *= scale;
|
|
let pos = null;
|
|
if (true) switch (dev.imageAnchor) {
|
|
case 1: pos = { x: -(bedWidth - width)/2, y: (bedDepth - height)/2 }; break;
|
|
case 2: pos = { x: 0, y: (bedDepth - height)/2 }; break;
|
|
case 3: pos = { x: (bedWidth - width)/2, y: (bedDepth - height)/2 }; break;
|
|
case 4: pos = { x: -(bedWidth - width)/2, y: 0 }; break;
|
|
case 5: pos = { x: (bedWidth - width)/2, y: 0 }; break;
|
|
case 6: pos = { x: -(bedWidth - width)/2, y: -(bedDepth - height)/2 }; break;
|
|
case 7: pos = { x: 0, y: -(bedDepth - height)/2 }; break;
|
|
case 8: pos = { x: (bedWidth - width)/2, y: -(bedDepth - height)/2 }; break;
|
|
}
|
|
let geometry = new THREE.PlaneBufferGeometry(width, height, 1),
|
|
material = new THREE.MeshBasicMaterial({ map: texture, transparent: true }),
|
|
mesh = new THREE.Mesh(geometry, material);
|
|
mesh.position.z = -bedHeight;
|
|
mesh.renderOrder = -1;
|
|
if (pos) {
|
|
mesh.position.x = pos.x;
|
|
mesh.position.y = pos.y;
|
|
}
|
|
SPACE.world.add(deviceImage = mesh);
|
|
}
|
|
|
|
function updateDeviceName() {
|
|
let newname = UI.deviceName.value,
|
|
selected = API.device.get(),
|
|
devs = settings().devices;
|
|
if (newname !== selected) {
|
|
devs[newname] = devs[selected];
|
|
delete devs[selected];
|
|
UI.deviceSave.onclick();
|
|
selectDevice(newname);
|
|
updateDeviceList();
|
|
}
|
|
}
|
|
|
|
function renderDevices(devices) {
|
|
let selectedIndex = -1,
|
|
selected = API.device.get(),
|
|
features = API.feature,
|
|
devs = settings().devices,
|
|
dfilter = typeof(features.device_filter) === 'function' ? features.device_filter : undefined;
|
|
|
|
for (let local in devs) {
|
|
if (!(devs.hasOwnProperty(local) && devs[local])) {
|
|
continue;
|
|
}
|
|
let dev = devs[local],
|
|
fdmCode = dev.cmd,
|
|
fdmMode = (API.mode.get() === 'FDM');
|
|
|
|
if (dev.mode ? (dev.mode === API.mode.get()) : (fdmCode ? fdmMode : !fdmMode)) {
|
|
devices.push(local);
|
|
}
|
|
};
|
|
|
|
devices = devices.sort();
|
|
|
|
UI.deviceSave.onclick = function() {
|
|
API.function.clear();
|
|
API.conf.save();
|
|
showDevices();
|
|
API.modal.hide();
|
|
};
|
|
UI.deviceAdd.onclick = function() {
|
|
API.function.clear();
|
|
cloneDevice();
|
|
showDevices();
|
|
};
|
|
UI.deviceDelete.onclick = function() {
|
|
API.function.clear();
|
|
removeLocalDevice(getSelectedDevice());
|
|
showDevices();
|
|
};
|
|
UI.deviceExport.onclick = function() {
|
|
let exp = API.util.b64enc({
|
|
version: KIRI.version,
|
|
device: selected,
|
|
process: API.process.code(),
|
|
code: devs[selected],
|
|
time: Date.now()
|
|
});
|
|
deviceExport(exp, selected);
|
|
};
|
|
|
|
UI.deviceList.innerHTML = '';
|
|
UI.deviceMy.innerHTML = '';
|
|
let incr = 0;
|
|
let found = null;
|
|
let first = devices[0];
|
|
let dedup = {};
|
|
devices.forEach(function(device, index) {
|
|
// prevent device from appearing twice
|
|
// such as local name = standard device name
|
|
if (dedup[device]) {
|
|
return;
|
|
}
|
|
dedup[device] = device;
|
|
let loc = isLocalDevice(device);
|
|
// if filter set, use
|
|
if (!loc && deviceFilter && device.toLowerCase().indexOf(deviceFilter) < 0) {
|
|
return;
|
|
}
|
|
// allow for device filter feature
|
|
if (dfilter && dfilter(device) === false) {
|
|
return;
|
|
}
|
|
if (incr === 0) {
|
|
first = device;
|
|
}
|
|
let opt = DOC.createElement('button');
|
|
opt.appendChild(DOC.createTextNode(device.replace(/\./g,' ')));
|
|
opt.onclick = function() {
|
|
selectDevice(device);
|
|
opt.classList.add("selected");
|
|
if (found && found !== opt) {
|
|
found.classList.remove("selected");
|
|
}
|
|
found = opt;
|
|
API.platform.layout();
|
|
};
|
|
if (loc) {
|
|
UI.deviceMy.appendChild(opt);
|
|
} else {
|
|
UI.deviceList.appendChild(opt);
|
|
}
|
|
if (device === selected) {
|
|
// scroll to highlighted selection
|
|
setTimeout(() => UI.deviceList.scrollTop = opt.offsetTop, 0);
|
|
opt.classList.add("selected");
|
|
selectedIndex = incr;
|
|
found = opt;
|
|
}
|
|
incr++;
|
|
});
|
|
|
|
if (selectedIndex >= 0) {
|
|
selectDevice(selected);
|
|
} else {
|
|
selectDevice(first);
|
|
}
|
|
}
|
|
|
|
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.toolTaperAngle.value = tool.taper_angle || 70;
|
|
UI.toolTaperTip.value = tool.taper_tip || 0;
|
|
UI.toolMetric.checked = tool.metric;
|
|
UI.toolType.selectedIndex = ['endmill','ballmill','tapermill'].indexOf(tool.type);
|
|
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(' ')}></${key}>`);
|
|
});
|
|
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 = '<svg id="tool-svg" width="100%" height="100%"></svg>';
|
|
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() {
|
|
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_angle = parseFloat(UI.toolTaperAngle.value);
|
|
selectedTool.taper_tip = parseFloat(UI.toolTaperTip.value);
|
|
selectedTool.metric = UI.toolMetric.checked;
|
|
selectedTool.type = ['endmill','ballmill','tapermill'][UI.toolType.selectedIndex];
|
|
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;
|
|
|
|
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() {
|
|
editTools.push({
|
|
id: Date.now(),
|
|
number: maxTool + 1,
|
|
name: "new",
|
|
type: "endmill",
|
|
shaft_diam: 0.25,
|
|
shaft_len: 1,
|
|
flute_diam: 0.25,
|
|
flute_len: 2,
|
|
// taper_angle: 70,
|
|
taper_tip: 0,
|
|
metric: false
|
|
});
|
|
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.view.update_fields();
|
|
API.event.settings();
|
|
};
|
|
|
|
renderTools();
|
|
if (editTools.length > 0) {
|
|
selectTool(editTools[0]);
|
|
UI.toolSelect.selectedIndex = 0;
|
|
} else {
|
|
UI.toolAdd.onclick();
|
|
}
|
|
|
|
API.dialog.show('tools');
|
|
UI.toolSelect.focus();
|
|
}
|
|
|
|
function updateDeviceList() {
|
|
renderDevices(Object.keys(devices[API.mode.get_lower()]).sort());
|
|
}
|
|
|
|
function showDevices() {
|
|
// disable device filter and show devices
|
|
UI.dev.search.onclick(true);
|
|
API.modal.show('setup');
|
|
UI.deviceList.focus();
|
|
}
|
|
|
|
function dragOverHandler(evt) {
|
|
evt.stopPropagation();
|
|
evt.preventDefault();
|
|
|
|
// prevent drop actions when a dialog is open
|
|
if (API.modal.visible()) {
|
|
return;
|
|
}
|
|
|
|
evt.dataTransfer.dropEffect = 'copy';
|
|
let oldcolor = SPACE.platform.setColor(0x00ff00);
|
|
if (oldcolor !== 0x00ff00) platformColor = oldcolor;
|
|
}
|
|
|
|
function dragLeave() {
|
|
SPACE.platform.setColor(platformColor);
|
|
}
|
|
|
|
function dropHandler(evt) {
|
|
evt.stopPropagation();
|
|
evt.preventDefault();
|
|
|
|
// prevent drop actions when a dialog is open
|
|
if (API.modal.visible()) {
|
|
return;
|
|
}
|
|
|
|
SPACE.platform.setColor(platformColor);
|
|
|
|
let files = evt.dataTransfer.files;
|
|
|
|
switch (API.feature.drop_group) {
|
|
case true:
|
|
return API.platform.load_files(files, []);
|
|
case false:
|
|
return API.platform.load_files(files, undefined);
|
|
}
|
|
|
|
function ck_group() {
|
|
if (files.length === 1) {
|
|
API.platform.load_files(files);
|
|
} else {
|
|
UC.confirm(`group ${files.length} files?`).then(yes => {
|
|
API.platform.load_files(files, yes ? [] : undefined);
|
|
});
|
|
}
|
|
}
|
|
|
|
if (files.length > 5) {
|
|
UC.confirm(`add ${files.length} objects to workspace?`).then(yes => {
|
|
if (yes) ck_group();
|
|
});
|
|
} else {
|
|
ck_group();
|
|
}
|
|
}
|
|
|
|
function loadCatalogFile(e) {
|
|
API.widgets.load(e.target.getAttribute('load'), function(widget) {
|
|
platform.add(widget);
|
|
API.dialog.hide();
|
|
});
|
|
}
|
|
|
|
function updateCatalog(files) {
|
|
let table = UI.catalogList,
|
|
list = [];
|
|
table.innerHTML = '';
|
|
for (let name in files) {
|
|
list.push({n:name, ln:name.toLowerCase(), v:files[name].vertices, t:files[name].updated});
|
|
}
|
|
list.sort(function(a,b) {
|
|
return a.ln < b.ln ? -1 : 1;
|
|
});
|
|
for (let i=0; i<list.length; i++) {
|
|
let row = DOC.createElement('div'),
|
|
renm = DOC.createElement('button'),
|
|
load = DOC.createElement('button'),
|
|
size = DOC.createElement('button'),
|
|
del = DOC.createElement('button'),
|
|
file = list[i],
|
|
name = file.n,
|
|
date = new Date(file.t),
|
|
split = name.split('.'),
|
|
short = split[0],
|
|
ext = split[1] ? `.${split[1]}` : '';
|
|
|
|
renm.setAttribute('class', 'rename');
|
|
renm.setAttribute('title', 'rename file');
|
|
renm.innerHTML = '<i class="far fa-edit"></i>';
|
|
renm.onclick = () => {
|
|
let newname = prompt(`rename file`, short);
|
|
if (newname && newname !== short) {
|
|
CATALOG.rename(name, `${newname}${ext}`, then => {
|
|
CATALOG.refresh();
|
|
});
|
|
}
|
|
};
|
|
|
|
load.setAttribute('load', name);
|
|
load.setAttribute('title', `file: ${name}\nvertices: ${file.v}\ndate: ${date}`);
|
|
load.onclick = loadCatalogFile;
|
|
load.appendChild(DOC.createTextNode(short));
|
|
|
|
del.setAttribute('del', name);
|
|
del.setAttribute('title', "remove '"+name+"'");
|
|
del.onclick = () => { CATALOG.deleteFile(name) };
|
|
del.innerHTML = '<i class="far fa-trash-alt"></i>';
|
|
|
|
size.setAttribute("disabled", true);
|
|
size.setAttribute("class", "label");
|
|
size.appendChild(DOC.createTextNode(BASE.util.comma(file.v)));
|
|
|
|
row.setAttribute("class", "f-row a-center");
|
|
row.appendChild(renm);
|
|
row.appendChild(load);
|
|
row.appendChild(size);
|
|
row.appendChild(del);
|
|
table.appendChild(row);
|
|
}
|
|
}
|
|
|
|
function isMultiHead() {
|
|
let dev = API.conf.get().device;
|
|
return isNotBelt() && dev.extruders && dev.extruders.length > 1;
|
|
}
|
|
|
|
function isBelt() {
|
|
return UI.deviceBelt.checked;
|
|
}
|
|
|
|
function isNotBelt() {
|
|
return !isBelt();
|
|
}
|
|
|
|
function isDanger() {
|
|
return UI.danger.checked;
|
|
}
|
|
|
|
// MAIN INITIALIZATION FUNCTION
|
|
|
|
function init_one() {
|
|
API.event.emit('init.one');
|
|
|
|
// ensure we have settings from last session
|
|
API.conf.restore();
|
|
|
|
let container = $('container'),
|
|
welcome = $('welcome'),
|
|
gcode = $('dev-gcode'),
|
|
tracker = $('tracker'),
|
|
controller = settings().controller;
|
|
|
|
UC.setHoverPop(false);
|
|
|
|
WIN.addEventListener("resize", () => {
|
|
API.event.emit('resize');
|
|
});
|
|
|
|
API.event.on('resize', () => {
|
|
if (WIN.innerHeight < 800) {
|
|
UI.modalBox.classList.add('mh85');
|
|
} else {
|
|
UI.modalBox.classList.remove('mh85');
|
|
}
|
|
API.view.update_slider();
|
|
});
|
|
|
|
SPACE.showSkyGrid(false);
|
|
SPACE.setSkyColor(controller.dark ? 0 : 0xffffff);
|
|
SPACE.init(container, function (delta) {
|
|
if (API.var.layer_max === 0 || !delta) return;
|
|
if (controller.reverseZoom) delta = -delta;
|
|
let same = API.var.layer_hi === API.var.layer_lo;
|
|
let track = API.var.layer_lo > 0;
|
|
if (delta > 0) {
|
|
API.var.layer_hi = Math.max(same ? 0 : API.var.layer_lo, API.var.layer_hi - 1);
|
|
if (track) {
|
|
API.var.layer_lo = Math.max(0, API.var.layer_lo - 1);
|
|
}
|
|
} else if (delta < 0) {
|
|
API.var.layer_hi = Math.min(API.var.layer_max, API.var.layer_hi + 1);
|
|
if (track) {
|
|
API.var.layer_lo = Math.min(API.var.layer_hi, API.var.layer_lo + 1);
|
|
}
|
|
}
|
|
if (same) {
|
|
API.var.layer_lo = API.var.layer_hi;
|
|
}
|
|
API.view.update_slider();
|
|
API.show.slices();
|
|
}, controller.ortho);
|
|
SPACE.platform.onMove(API.conf.save);
|
|
SPACE.platform.setRound(true);
|
|
SPACE.useDefaultKeys(API.feature.on_key === undefined || API.feature.on_key_defaults);
|
|
|
|
Object.assign(UI, {
|
|
tracker: tracker,
|
|
container: container,
|
|
|
|
alert: {
|
|
dialog: $('alert-area'),
|
|
text: $('alert-text')
|
|
},
|
|
func: {
|
|
slice: $('act-slice'),
|
|
preview: $('act-preview'),
|
|
animate: $('act-animate'),
|
|
export: $('act-export')
|
|
},
|
|
label: {
|
|
slice: $('label-slice'),
|
|
preview: $('label-preview'),
|
|
animate: $('label-animate'),
|
|
export: $('label-export'),
|
|
},
|
|
acct: {
|
|
help: $('acct-help'),
|
|
export: $('acct-export')
|
|
},
|
|
dev: {
|
|
search: $('dev-search'),
|
|
filter: $('dev-filter')
|
|
},
|
|
tool: {
|
|
rotate: () => { UI.tool.show('ft-rotate') },
|
|
scale: () => { UI.tool.show('ft-scale') },
|
|
mesh: () => { UI.tool.show('ft-mesh') },
|
|
select: () => { UI.tool.show('ft-select') },
|
|
},
|
|
mesh: {
|
|
name: $('mesh-name'),
|
|
points: $('mesh-points'),
|
|
faces: $('mesh-faces'),
|
|
},
|
|
|
|
fps: $('fps'),
|
|
load: $('load-file'),
|
|
speeds: $('speeds'),
|
|
speedbar: $('speedbar'),
|
|
context: $('context-menu'),
|
|
|
|
back: $('lt-back'),
|
|
trash: $('lt-trash'),
|
|
ltsetup: $('lt-setup'),
|
|
ltfile: $('lt-file'),
|
|
ltview: $('lt-view'),
|
|
ltact: $('act-slice'),
|
|
edit: $('lt-tools'),
|
|
nozzle: $('lt-nozzle'),
|
|
render: $('lt-render'),
|
|
|
|
modal: $('modal'),
|
|
modalBox: $('modal-box'),
|
|
help: $('mod-help'),
|
|
setup: $('mod-setup'),
|
|
prefs: $('mod-prefs'),
|
|
files: $('mod-files'),
|
|
saves: $('mod-saves'),
|
|
tools: $('mod-tools'),
|
|
print: $('mod-print'),
|
|
local: $('mod-local'),
|
|
any: $('mod-any'),
|
|
|
|
catalogBody: $('catalogBody'),
|
|
catalogList: $('catalogList'),
|
|
|
|
devices: $('devices'),
|
|
deviceList: $('device-list'),
|
|
deviceMy: $('device-my'),
|
|
deviceAdd: $('device-add'),
|
|
deviceDelete: $('device-del'),
|
|
deviceExport: $('device-exp'),
|
|
deviceSave: $('device-save'),
|
|
|
|
toolsSave: $('tools-save'),
|
|
toolsClose: $('tools-close'),
|
|
toolSelect: $('tool-select'),
|
|
toolAdd: $('tool-add'),
|
|
toolDelete: $('tool-del'),
|
|
toolType: $('tool-type'),
|
|
toolName: $('tool-name'),
|
|
toolNum: $('tool-num'),
|
|
toolFluteDiam: $('tool-fdiam'),
|
|
toolFluteLen: $('tool-flen'),
|
|
toolShaftDiam: $('tool-sdiam'),
|
|
toolShaftLen: $('tool-slen'),
|
|
// toolTaperAngle: $('tool-tangle'),
|
|
toolTaperTip: $('tool-ttip'),
|
|
toolMetric: $('tool-metric'),
|
|
|
|
setMenu: $('set-menu'),
|
|
settings: $('settings'),
|
|
settingsBody: $('settingsBody'),
|
|
settingsList: $('settingsList'),
|
|
|
|
slider: $('slider'),
|
|
sliderMax: $('slider-max'),
|
|
sliderMin: $('slider-zero'),
|
|
sliderLo: $('slider-lo'),
|
|
sliderMid: $('slider-mid'),
|
|
sliderHi: $('slider-hi'),
|
|
sliderHold: $('slider-hold'),
|
|
sliderRange: $('slider-center'),
|
|
|
|
loading: $('progress').style,
|
|
progress: $('progbar').style,
|
|
prostatus: $('progtxt'),
|
|
|
|
selection: $('selection'),
|
|
sizeX: $('size_x'),
|
|
sizeY: $('size_y'),
|
|
sizeZ: $('size_z'),
|
|
scaleX: $('scale_x'),
|
|
scaleY: $('scale_y'),
|
|
scaleZ: $('scale_z'),
|
|
lockX: $('lock_x'),
|
|
lockY: $('lock_y'),
|
|
lockZ: $('lock_z'),
|
|
stock: $('stock'),
|
|
stockWidth: $('stock-width'),
|
|
stockDepth: $('stock-width'),
|
|
stockHeight: $('stock-width'),
|
|
|
|
device: UC.newGroup(LANG.dv_gr_dev, $('device1'), {group:"ddev", inline:true, class:"noshow"}),
|
|
deviceName: UC.newInput(LANG.dv_name_s, {title:LANG.dv_name_l, size:"65%", text:true, action:updateDeviceName}),
|
|
bedWidth: UC.newInput(LANG.dv_bedw_s, {title:LANG.dv_bedw_l, convert:UC.toFloat, size:6, units:true, round:2}),
|
|
bedDepth: UC.newInput(LANG.dv_bedd_s, {title:LANG.dv_bedd_l, convert:UC.toFloat, size:6, units:true, round:2}),
|
|
maxHeight: UC.newInput(LANG.dv_bedh_s, {title:LANG.dv_bedh_l, convert:UC.toFloat, size:6, modes:FDM_SLA}),
|
|
resolutionX: UC.newInput(LANG.dv_rezx_s, {title:LANG.dv_rezx_l, convert:UC.toInt, size:6, modes:SLA}),
|
|
resolutionY: UC.newInput(LANG.dv_rezy_s, {title:LANG.dv_rezy_l, convert:UC.toInt, size:6, modes:SLA}),
|
|
spindleMax: UC.newInput(LANG.dv_spmx_s, {title:LANG.dv_spmx_l, convert:UC.toInt, size: 6, modes:CAM}),
|
|
deviceZMax: UC.newInput(LANG.dv_zmax_s, {title:LANG.dv_zmax_l, convert:UC.toInt, size: 6, modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
fwRetract: UC.newBoolean(LANG.dv_retr_s, onBooleanClick, {title:LANG.dv_retr_l, modes:FDM}),
|
|
deviceOrigin: UC.newBoolean(LANG.dv_orgc_s, onBooleanClick, {title:LANG.dv_orgc_l, modes:FDM_LASER, show:() => !UI.deviceRound.checked}),
|
|
deviceRound: UC.newBoolean(LANG.dv_bedc_s, onBooleanClick, {title:LANG.dv_bedc_l, modes:FDM, trigger:true, show:isNotBelt}),
|
|
deviceBelt: UC.newBoolean(LANG.dv_belt_s, onBooleanClick, {title:LANG.dv_belt_l, modes:FDM, trigger:true, show:() => !UI.deviceRound.checked}),
|
|
|
|
extruder: UC.newGroup(LANG.dv_gr_ext, $('device2'), {group:"dext", inline:true, modes:FDM}),
|
|
extFilament: UC.newInput(LANG.dv_fila_s, {title:LANG.dv_fila_l, convert:UC.toFloat, modes:FDM}),
|
|
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}),
|
|
extActions: UC.newRow([
|
|
UI.extPrev = UC.newButton(undefined, undefined, {icon:'<i class="fas fa-less-than"></i>'}),
|
|
UI.extAdd = UC.newButton(undefined, undefined, {icon:'<i class="fas fa-plus"></i>'}),
|
|
UI.extDel = UC.newButton(undefined, undefined, {icon:'<i class="fas fa-minus"></i>'}),
|
|
UI.extNext = UC.newButton(undefined, undefined, {icon:'<i class="fas fa-greater-than"></i>'})
|
|
], {modes:FDM, class:"dev-buttons ext-buttons"}),
|
|
|
|
gcode: UC.newGroup(LANG.dv_gr_out, $('device2'), {group:"dgco", inline:true, modes:CAM_LASER}),
|
|
gcodeSpace: UC.newBoolean(LANG.dv_tksp_s, onBooleanClick, {title:LANG.dv_tksp_l, modes:CAM_LASER}),
|
|
gcodeStrip: UC.newBoolean(LANG.dv_strc_s, onBooleanClick, {title:LANG.dv_strc_l, modes:CAM}),
|
|
gcodeFExt: UC.newInput(LANG.dv_fext_s, {title:LANG.dv_fext_l, modes:CAM_LASER, size:7, text:true}),
|
|
|
|
gcodeEd: UC.newGroup(LANG.dv_gr_gco, $('dg'), {group:"dgcp", inline:true, modes:GCODE}),
|
|
gcodeMacros: UC.newRow([
|
|
(UI.gcodePre = UC.newGCode(LANG.dv_head_s, {title:LANG.dv_head_l, modes:GCODE, area:gcode})).button,
|
|
(UI.gcodePost = UC.newGCode(LANG.dv_foot_s, {title:LANG.dv_foot_l, modes:GCODE, area:gcode})).button,
|
|
(UI.gcodeFan = UC.newGCode(LANG.dv_fanp_s, {title:LANG.dv_fanp_l, modes:FDM, area:gcode})).button,
|
|
(UI.gcodeTrack = UC.newGCode(LANG.dv_prog_s, {title:LANG.dv_prog_l, modes:FDM, area:gcode})).button,
|
|
(UI.gcodeLayer = UC.newGCode(LANG.dv_layr_s, {title:LANG.dv_layr_l, modes:FDM, area:gcode})).button,
|
|
(UI.gcodePause = UC.newGCode(LANG.dv_paus_s, {title:LANG.dv_paus_l, modes:FDM, area:gcode})).button,
|
|
(UI.gcodeExt = UC.newGCode(LANG.dv_pext_s, {title:LANG.dv_pext_l, modes:FDM, area:gcode, show:isDanger})).button,
|
|
(UI.gcodeInt = UC.newGCode(LANG.dv_pint_s, {title:LANG.dv_pint_l, modes:FDM, area:gcode, show:isDanger})).button,
|
|
(UI.gcodeLaserOn = UC.newGCode(LANG.dv_lzon_s, {title:LANG.dv_lzon_l, modes:LASER, area:gcode})).button,
|
|
(UI.gcodeLaserOff = UC.newGCode(LANG.dv_lzof_s, {title:LANG.dv_lzof_l, modes:LASER, area:gcode})).button,
|
|
(UI.gcodeChange = UC.newGCode(LANG.dv_tool_s, {title:LANG.dv_tool_l, modes:CAM, area:gcode})).button,
|
|
(UI.gcodeDwell = UC.newGCode(LANG.dv_dwll_s, {title:LANG.dv_dwll_l, modes:CAM, area:gcode})).button,
|
|
(UI.gcodeSpindle = UC.newGCode(LANG.dv_sspd_s, {title:LANG.dv_sspd_l, modes:CAM, area:gcode})).button
|
|
], {class:"ext-buttons f-row gcode-macros"}),
|
|
|
|
lprefs: UC.newGroup(LANG.op_menu, $('prefs-gen1'), {inline: true}),
|
|
reverseZoom: UC.newBoolean(LANG.op_invr_s, booleanSave, {title:LANG.op_invr_l}),
|
|
ortho: UC.newBoolean(LANG.op_orth_s, booleanSave, {title:LANG.op_orth_l}),
|
|
dark: UC.newBoolean(LANG.op_dark_s, booleanSave, {title:LANG.op_dark_l}),
|
|
devel: UC.newBoolean(LANG.op_devl_s, booleanSave, {title:LANG.op_devl_l}),
|
|
danger: UC.newBoolean(LANG.op_dang_s, booleanSave, {title:LANG.op_dang_l}),
|
|
|
|
lprefs: UC.newGroup(LANG.op_disp, $('prefs-gen2'), {inline: true}),
|
|
showOrigin: UC.newBoolean(LANG.op_shor_s, booleanSave, {title:LANG.op_shor_l}),
|
|
showRulers: UC.newBoolean(LANG.op_shru_s, booleanSave, {title:LANG.op_shru_l}),
|
|
showSpeeds: UC.newBoolean(LANG.op_sped_s, speedSave, {title:LANG.op_sped_l}),
|
|
decals: UC.newBoolean(LANG.op_decl_s, booleanSave, {title:LANG.op_decl_s}),
|
|
lineType: UC.newSelect(LANG.op_line_s, {title: LANG.op_line_l, action: lineTypeSave, modes:FDM}, "linetype"),
|
|
animesh: UC.newSelect(LANG.op_anim_s, {title: LANG.op_anim_l, action: aniMeshSave, modes:CAM}, "animesh"),
|
|
units: UC.newSelect(LANG.op_unit_s, {title: LANG.op_unit_l, action: unitsSave, modes:CAM, trace:true}, "units"),
|
|
|
|
layout: UC.newGroup(LANG.lo_menu, $('prefs-lay'), {inline: true}),
|
|
autoSave: UC.newBoolean(LANG.op_save_s, booleanSave, {title:LANG.op_save_l}),
|
|
autoLayout: UC.newBoolean(LANG.op_auto_s, booleanSave, {title:LANG.op_auto_l}),
|
|
freeLayout: UC.newBoolean(LANG.op_free_s, booleanSave, {title:LANG.op_free_l}),
|
|
spaceLayout: UC.newInput(LANG.op_spcr_s, {title:LANG.op_spcr_l, convert:UC.toFloat, size:3, units:true}),
|
|
|
|
export: UC.newGroup(LANG.xp_menu, $('prefs-xpo'), {inline: true}),
|
|
exportOcto: UC.newBoolean(`OctoPrint`, booleanSave),
|
|
exportGhost: UC.newBoolean(`Grid:Host`, booleanSave),
|
|
exportLocal: UC.newBoolean(`Grid:Local`, booleanSave),
|
|
exportThumb: UC.newBoolean(`Thumbnail`, booleanSave, {modes:FDM}),
|
|
exportPreview: UC.newBoolean(`Code Preview`, booleanSave),
|
|
|
|
parts: UC.newGroup(LANG.pt_menu, $('prefs-prt'), {inline: true}),
|
|
detail: UC.newSelect(LANG.pt_qual_s, {title: LANG.pt_qual_l, action: detailSave}, "detail"),
|
|
decimate: UC.newBoolean(LANG.pt_deci_s, booleanSave, {title: LANG.pt_deci_l}),
|
|
healMesh: UC.newBoolean(LANG.pt_heal_s, booleanSave, {title: LANG.pt_heal_l}),
|
|
threaded: UC.newBoolean(LANG.pt_thrd_s, booleanSave, {title: LANG.pt_thrd_l, modes:FDM_SLA}),
|
|
assembly: UC.newBoolean(LANG.pt_assy_s, booleanSave, {title: LANG.pt_assy_l, modes:FDM_SLA}),
|
|
|
|
prefadd: UC.checkpoint($('prefs-add')),
|
|
|
|
process: UC.newGroup(LANG.sl_menu, $('settings'), {modes:FDM_LASER}),
|
|
sliceHeight: UC.newInput(LANG.sl_lahi_s, {title:LANG.sl_lahi_l, convert:UC.toFloat, modes:FDM}),
|
|
sliceMinHeight: UC.newInput(LANG.ad_minl_s, {title:LANG.ad_minl_l, bound:UC.bound(0,3.0), convert:UC.toFloat, modes:FDM, show: () => UI.sliceAdaptive.checked}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
sliceShells: UC.newInput(LANG.sl_shel_s, {title:LANG.sl_shel_l, convert:UC.toFloat, modes:FDM}),
|
|
sliceLineWidth: UC.newInput(LANG.sl_line_s, {title:LANG.sl_line_l, convert:UC.toFloat, bound:UC.bound(0,5), modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
sliceTopLayers: UC.newInput(LANG.sl_ltop_s, {title:LANG.sl_ltop_l, convert:UC.toInt, modes:FDM}),
|
|
sliceSolidLayers: UC.newInput(LANG.sl_lsld_s, {title:LANG.sl_lsld_l, convert:UC.toInt, modes:FDM}),
|
|
sliceBottomLayers: UC.newInput(LANG.sl_lbot_s, {title:LANG.sl_lbot_l, convert:UC.toInt, modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
sliceAdaptive: UC.newBoolean(LANG.ad_adap_s, onBooleanClick, {title: LANG.ad_adap_l, modes:FDM, trigger: true}),
|
|
detectThinWalls: UC.newBoolean(LANG.ad_thin_s, onBooleanClick, {title: LANG.ad_thin_l, modes:FDM}),
|
|
|
|
laserOffset: UC.newInput(LANG.ls_offs_s, {title:LANG.ls_offs_l, convert:UC.toFloat, modes:LASER}),
|
|
laserSliceHeight: UC.newInput(LANG.ls_lahi_s, {title:LANG.ls_lahi_l, convert:UC.toFloat, modes:LASER, trigger: true}),
|
|
laserSliceHeightMin: UC.newInput(LANG.ls_lahm_s, {title:LANG.ls_lahm_l, convert:UC.toFloat, modes:LASER, show:() => { return UI.laserSliceHeight.value == 0 }}),
|
|
laserSliceSingle: UC.newBoolean(LANG.ls_sngl_s, onBooleanClick, {title:LANG.ls_sngl_l, modes:LASER}),
|
|
|
|
firstLayer: UC.newGroup(LANG.fl_menu, null, {modes:FDM}),
|
|
firstSliceHeight: UC.newInput(LANG.fl_lahi_s, {title:LANG.fl_lahi_l, convert:UC.toFloat, modes:FDM, show:isNotBelt}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isNotBelt}),
|
|
firstLayerNozzleTemp:UC.newInput(LANG.fl_nozl_s, {title:LANG.fl_nozl_l, convert:UC.toInt, modes:FDM, show:isNotBelt}),
|
|
firstLayerBedTemp: UC.newInput(LANG.fl_bedd_s, {title:LANG.fl_bedd_l, convert:UC.toInt, modes:FDM, show:isNotBelt}),
|
|
firstLayerFanSpeed: UC.newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:UC.toInt, bound:UC.bound(0,255), modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isNotBelt}),
|
|
firstLayerYOffset: UC.newInput(LANG.fl_zoff_s, {title:LANG.fl_zoff_l, convert:UC.toFloat, modes:FDM, show:isBelt}),
|
|
firstLayerFlatten: UC.newInput(LANG.fl_flat_s, {title:LANG.fl_flat_l, convert:UC.toFloat, modes:FDM, show:isBelt}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isBelt}),
|
|
firstLayerRate: UC.newInput(LANG.fl_rate_s, {title:LANG.fl_rate_l, convert:UC.toFloat, modes:FDM}),
|
|
firstLayerFillRate: UC.newInput(LANG.fl_frat_s, {title:LANG.fl_frat_l, convert:UC.toFloat, modes:FDM, show:isNotBelt}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isNotBelt}),
|
|
firstLayerLineMult: UC.newInput(LANG.fl_sfac_s, {title:LANG.fl_sfac_l, convert:UC.toFloat, bound:UC.bound(0.5,2), modes:FDM, show:isNotBelt}),
|
|
firstLayerPrintMult: UC.newInput(LANG.fl_mult_s, {title:LANG.fl_mult_l, convert:UC.toFloat, modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isBelt}),
|
|
firstLayerBrim: UC.newInput(LANG.fl_brim_s, {title:LANG.fl_brim_l, convert:UC.toInt, modes:FDM, show:isBelt}),
|
|
firstLayerBrimIn: UC.newInput(LANG.fl_brin_s, {title:LANG.fl_brin_l, convert:UC.toInt, modes:FDM, show:isBelt}),
|
|
firstLayerBrimComb: UC.newInput(LANG.fl_brco_s, {title:LANG.fl_brco_l, convert:UC.toInt, modes:FDM, show:isBelt}),
|
|
firstLayerBrimTrig: UC.newInput(LANG.fl_brmn_s, {title:LANG.fl_brmn_l, convert:UC.toInt, modes:FDM, show:isBelt}),
|
|
firstLayerBrimGap: UC.newInput(LANG.fl_brgp_s, {title:LANG.fl_brgp_l, convert:UC.toFloat, modes:FDM, show:isBelt}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isBelt}),
|
|
firstLayerBeltLead: UC.newInput(LANG.fl_bled_s, {title:LANG.fl_bled_l, convert:UC.toFloat, modes:FDM, show:isBelt}),
|
|
firstLayerBeltBump: UC.newInput(LANG.fl_blmp_s, {title:LANG.fl_blmp_l, convert:UC.toFloat, bound:UC.bound(0, 10), modes:FDM, show:isBelt}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isNotBelt}),
|
|
outputBrimCount: UC.newInput(LANG.fl_skrt_s, {title:LANG.fl_skrt_l, convert:UC.toInt, modes:FDM, show:isNotBelt}),
|
|
outputBrimOffset: UC.newInput(LANG.fl_skro_s, {title:LANG.fl_skro_l, convert:UC.toFloat, modes:FDM, show:isNotBelt}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isNotBelt}),
|
|
outputRaftSpacing: UC.newInput(LANG.fr_spac_s, {title:LANG.fr_spac_l, convert:UC.toFloat, bound:UC.bound(0.0,3.0), modes:FDM, show: () => UI.outputRaft.checked && isNotBelt() }),
|
|
outputRaft: UC.newBoolean(LANG.fr_nabl_s, onBooleanClick, {title:LANG.fr_nabl_l, modes:FDM, trigger: true, show:isNotBelt}),
|
|
|
|
fdmInfill: UC.newGroup(LANG.fi_menu, $('settings'), {modes:FDM}),
|
|
sliceFillType: UC.newSelect(LANG.fi_type, {modes:FDM, trigger:true}, "infill"),
|
|
sliceFillSparse: UC.newInput(LANG.fi_pcnt_s, {title:LANG.fi_pcnt_l, convert:UC.toFloat, bound:UC.bound(0.0,1.0), modes:FDM}),
|
|
sliceFillRepeat: UC.newInput(LANG.fi_rept_s, {title:LANG.fi_rept_l, convert:UC.toInt, bound:UC.bound(1,10), show:fillShow, modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
sliceFillOverlap: UC.newInput(LANG.fi_over_s, {title:LANG.fi_over_l, convert:UC.toFloat, bound:UC.bound(0.0,2.0), modes:FDM}),
|
|
sliceFillRate: UC.newInput(LANG.ou_feed_s, {title:LANG.fi_rate_l, convert:UC.toInt, bound:UC.bound(0,300), modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
sliceFillAngle: UC.newInput(LANG.fi_angl_s, {title:LANG.fi_angl_l, convert:UC.toFloat, modes:FDM}),
|
|
// sliceFillWidth: UC.newInput(LANG.fi_wdth_s, {title:LANG.fi_wdth_l, convert:UC.toFloat, modes:FDM}),
|
|
|
|
fdmSupport: UC.newGroup(LANG.sp_menu, null, {modes:FDM, marker:false}),
|
|
sliceSupportNozzle: UC.newSelect(LANG.sp_nozl_s, {title:LANG.sp_nozl_l, modes:FDM}, "extruders"),
|
|
sliceSupportDensity: UC.newInput(LANG.sp_dens_s, {title:LANG.sp_dens_l, convert:UC.toFloat, bound:UC.bound(0.0,1.0), modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
sliceSupportSize: UC.newInput(LANG.sp_size_s, {title:LANG.sp_size_l, bound:UC.bound(1.0,200.0), convert:UC.toFloat, modes:FDM}),
|
|
sliceSupportOffset: UC.newInput(LANG.sp_offs_s, {title:LANG.sp_offs_l, bound:UC.bound(0.0,200.0), convert:UC.toFloat, modes:FDM}),
|
|
sliceSupportGap: UC.newInput(LANG.sp_gaps_s, {title:LANG.sp_gaps_l, bound:UC.bound(0,5), convert:UC.toInt, modes:FDM, show:isNotBelt}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
sliceSupportArea: UC.newInput(LANG.sp_area_s, {title:LANG.sp_area_l, bound:UC.bound(0.0,200.0), convert:UC.toFloat, modes:FDM}),
|
|
sliceSupportExtra: UC.newInput(LANG.sp_xpnd_s, {title:LANG.sp_xpnd_l, bound:UC.bound(0.0,200.0), convert:UC.toFloat, modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
sliceSupportAngle: UC.newInput(LANG.sp_angl_s, {title:LANG.sp_angl_l, bound:UC.bound(0.0,90.0), convert:UC.toFloat, modes:FDM, show:() => !UI.sliceSupportEnable.checked}),
|
|
sliceSupportSpan: UC.newInput(LANG.sp_span_s, {title:LANG.sp_span_l, bound:UC.bound(0.0,200.0), convert:UC.toFloat, modes:FDM, show:() => UI.sliceSupportEnable.checked}),
|
|
sliceSupportEnable: UC.newBoolean(LANG.sp_auto_s, onBooleanClick, {title:LANG.sp_auto_l, modes:FDM, show:isNotBelt}),
|
|
sliceSupportOutline: UC.newBoolean(LANG.sp_outl_s, onBooleanClick, {title:LANG.sp_outl_l, modes:FDM, show:() => !UI.sliceSupportEnable.checked}),
|
|
|
|
sliceSupportGen: UC.newRow([
|
|
UI.ssaGen = UC.newButton(LANG.sp_detect, onButtonClick, {class: "f-col grow a-center"})
|
|
], { modes: FDM, class: "ext-buttons f-row grow" }),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM, show:isNotBelt}),
|
|
sliceSupportManual: UC.newRow([
|
|
(UI.ssmAdd = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-plus"></i>'})),
|
|
(UI.ssmDun = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-check"></i>'})),
|
|
(UI.ssmClr = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
|
|
], {modes:FDM, class:"ext-buttons f-row"}),
|
|
|
|
camTabs: UC.newGroup(LANG.ct_menu, null, {modes:CAM, marker:true}),
|
|
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}),
|
|
camTabsDepth: UC.newInput(LANG.ct_dpth_s, {title:LANG.ct_dpth_l, convert:UC.toFloat, bound:UC.bound(0.005,100), modes:CAM, units:true}),
|
|
camTabsMidline: UC.newBoolean(LANG.ct_midl_s, onBooleanClick, {title:LANG.ct_midl_l, modes:CAM}),
|
|
camSep: UC.newBlank({class:"pop-sep"}),
|
|
camTabsManual: UC.newRow([
|
|
(UI.tabAdd = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-plus"></i>'})),
|
|
(UI.tabDun = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-check"></i>'})),
|
|
(UI.tabClr = UC.newButton(undefined, onButtonClick, {icon:'<i class="fas fa-trash-alt"></i>'}))
|
|
], {modes:CAM, class:"ext-buttons f-row"}),
|
|
|
|
camStock: UC.newGroup(LANG.cs_menu, null, {modes:CAM, marker: true}),
|
|
camStockX: UC.newInput(LANG.cs_wdth_s, {title:LANG.cs_wdth_l, convert:UC.toFloat, bound:UC.bound(0,9999), modes:CAM, units:true}),
|
|
camStockY: UC.newInput(LANG.cs_dpth_s, {title:LANG.cs_dpth_l, convert:UC.toFloat, bound:UC.bound(0,9999), modes:CAM, units:true}),
|
|
camStockZ: UC.newInput(LANG.cs_hght_s, {title:LANG.cs_hght_l, convert:UC.toFloat, bound:UC.bound(0,9999), modes:CAM, units:true}),
|
|
camStockOffset: UC.newBoolean(LANG.cs_offs_s, onBooleanClick, {title:LANG.cs_offs_l, modes:CAM}),
|
|
camStockClipTo: UC.newBoolean(LANG.cs_clip_s, onBooleanClick, {title:LANG.cs_clip_l, modes:CAM}),
|
|
camSep: UC.newBlank({class:"pop-sep"}),
|
|
camStockOn: UC.newBoolean(LANG.cs_offe_s, onBooleanClick, {title:LANG.cs_offe_l, modes:CAM}),
|
|
|
|
camCommon: UC.newGroup(LANG.cc_menu, null, {modes:CAM}),
|
|
camZAnchor: UC.newSelect(LANG.ou_zanc_s, {title: LANG.ou_zanc_l, action:zAnchorSave, modes:CAM, trace:true}, "zanchor"),
|
|
camZOffset: UC.newInput(LANG.ou_ztof_s, {title:LANG.ou_ztof_l, convert:UC.toFloat, modes:CAM, units:true}),
|
|
camZBottom: UC.newInput(LANG.ou_zbot_s, {title:LANG.ou_zbot_l, convert:UC.toFloat, modes:CAM, units:true, trigger: true}),
|
|
camZThru: UC.newInput(LANG.ou_ztru_s, {title:LANG.ou_ztru_l, convert:UC.toFloat, bound:UC.bound(0.0,100), modes:CAM, units:true, show:() => { return UI.camZBottom.value == 0 }}),
|
|
camSep: UC.newBlank({class:"pop-sep"}),
|
|
camZClearance: UC.newInput(LANG.ou_zclr_s, {title:LANG.ou_zclr_l, convert:UC.toFloat, bound:UC.bound(0.01,100), modes:CAM, units:true}),
|
|
camSep: UC.newBlank({class:"pop-sep"}),
|
|
camFastFeedZ: UC.newInput(LANG.cc_rzpd_s, {title:LANG.cc_rzpd_l, convert:UC.toFloat, modes:CAM, units:true}),
|
|
camFastFeed: UC.newInput(LANG.cc_rapd_s, {title:LANG.cc_rapd_l, convert:UC.toFloat, modes:CAM, units:true}),
|
|
|
|
laserLayout: UC.newGroup(LANG.lo_menu, null, {modes:LASER, group:"lz-lo"}),
|
|
outputTileSpacing: UC.newInput(LANG.ou_spac_s, {title:LANG.ou_spac_l, convert:UC.toInt, modes:LASER}),
|
|
outputLaserMerged: UC.newBoolean(LANG.ou_mrgd_s, onBooleanClick, {title:LANG.ou_mrgd_l, modes:LASER}),
|
|
outputLaserGroup: UC.newBoolean(LANG.ou_grpd_s, onBooleanClick, {title:LANG.ou_grpd_l, modes:LASER}),
|
|
|
|
knife: UC.newGroup(LANG.dk_menu, null, {modes:LASER, marker:true}),
|
|
outputKnifeDepth: UC.newInput(LANG.dk_dpth_s, {title:LANG.dk_dpth_l, convert:UC.toFloat, bound:UC.bound(0.0,5.0), modes:LASER}),
|
|
outputKnifePasses: UC.newInput(LANG.dk_pass_s, {title:LANG.dk_pass_l, convert:UC.toInt, bound:UC.bound(0,5), modes:LASER}),
|
|
outputKnifeTip: UC.newInput(LANG.dk_offs_s, {title:LANG.dk_offs_l, convert:UC.toFloat, bound:UC.bound(0.0,10.0), modes:LASER}),
|
|
knifeSep: UC.newBlank({class:"pop-sep", modes:LASER}),
|
|
knifeOn: UC.newBoolean(LANG.enable, onBooleanClick, {title:LANG.ou_drkn_l, modes:LASER}),
|
|
|
|
output: UC.newGroup(LANG.ou_menu, null, {modes:GCODE}),
|
|
outputLaserPower: UC.newInput(LANG.ou_powr_s, {title:LANG.ou_powr_l, convert:UC.toInt, bound:UC.bound(1,100), modes:LASER}),
|
|
outputLaserSpeed: UC.newInput(LANG.ou_sped_s, {title:LANG.ou_sped_l, convert:UC.toInt, modes:LASER}),
|
|
outputLaserZColor: UC.newBoolean(LANG.ou_layo_s, onBooleanClick, {title:LANG.ou_layo_l, modes:LASER, show:() => { return UI.outputLaserMerged.checked === false }}),
|
|
outputLaserLayer: UC.newBoolean(LANG.ou_layr_s, onBooleanClick, {title:LANG.ou_layr_l, modes:LASER}),
|
|
|
|
outputTemp: UC.newInput(LANG.ou_nozl_s, {title:LANG.ou_nozl_l, convert:UC.toInt, modes:FDM}),
|
|
outputBedTemp: UC.newInput(LANG.ou_bedd_s, {title:LANG.ou_bedd_l, convert:UC.toInt, modes:FDM}),
|
|
outputFanSpeed: UC.newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:UC.toInt, bound:UC.bound(0,255), modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
outputFeedrate: UC.newInput(LANG.ou_feed_s, {title:LANG.ou_feed_l, convert:UC.toInt, modes:FDM}),
|
|
outputFinishrate: UC.newInput(LANG.ou_fini_s, {title:LANG.ou_fini_l, convert:UC.toInt, modes:FDM}),
|
|
outputSeekrate: UC.newInput(LANG.ou_move_s, {title:LANG.ou_move_l, convert:UC.toInt, modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
outputShellMult: UC.newInput(LANG.ou_shml_s, {title:LANG.ou_exml_l, convert:UC.toFloat, bound:UC.bound(0.0,2.0), modes:FDM}),
|
|
outputFillMult: UC.newInput(LANG.ou_flml_s, {title:LANG.ou_exml_l, convert:UC.toFloat, bound:UC.bound(0.0,2.0), modes:FDM}),
|
|
outputSparseMult: UC.newInput(LANG.ou_spml_s, {title:LANG.ou_exml_l, convert:UC.toFloat, bound:UC.bound(0.0,2.0), modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
sliceShellOrder: UC.newSelect(LANG.sl_ordr_s, {title:LANG.sl_ordr_l, modes:FDM}, "shell"),
|
|
sliceLayerStart: UC.newSelect(LANG.sl_strt_s, {title:LANG.sl_strt_l, modes:FDM}, "start"),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
outputLayerRetract: UC.newBoolean(LANG.ad_lret_s, onBooleanClick, {title:LANG.ad_lret_l, modes:FDM}),
|
|
outputBeltFirst: UC.newBoolean(LANG.ad_lbir_s, onBooleanClick, {title:LANG.ad_lbir_l, show: isBelt, modes:FDM}),
|
|
camConventional: UC.newBoolean(LANG.ou_conv_s, onBooleanClick, {title:LANG.ou_conv_l, modes:CAM}),
|
|
camEaseDown: UC.newBoolean(LANG.cr_ease_s, onBooleanClick, {title:LANG.cr_ease_l, modes:CAM}),
|
|
camDepthFirst: UC.newBoolean(LANG.ou_depf_s, onBooleanClick, {title:LANG.ou_depf_l, modes:CAM}),
|
|
outputOriginBounds: UC.newBoolean(LANG.or_bnds_s, onBooleanClick, {title:LANG.or_bnds_l, modes:LASER}),
|
|
outputOriginCenter: UC.newBoolean(LANG.or_cntr_s, onBooleanClick, {title:LANG.or_cntr_l, modes:CAM_LASER}),
|
|
camOriginTop: UC.newBoolean(LANG.or_topp_s, onBooleanClick, {title:LANG.or_topp_l, modes:CAM}),
|
|
|
|
camExpert: UC.newGroup(LANG.op_xprt_s, null, {group: "cam_expert", modes:CAM, marker: false}),
|
|
camExpertFast: UC.newBoolean(LANG.cx_fast_s, onBooleanClick, {title:LANG.cx_fast_l, modes:CAM, show: () => !UI.camTrueShadow.checked }),
|
|
camTrueShadow: UC.newBoolean(LANG.cx_true_s, onBooleanClick, {title:LANG.cx_true_l, modes:CAM, show: () => !UI.camExpertFast.checked }),
|
|
|
|
advanced: UC.newGroup(LANG.ad_menu, null, {modes:FDM}),
|
|
outputRetractDist: UC.newInput(LANG.ad_rdst_s, {title:LANG.ad_rdst_l, convert:UC.toFloat, modes:FDM}),
|
|
outputRetractSpeed: UC.newInput(LANG.ad_rrat_s, {title:LANG.ad_rrat_l, convert:UC.toInt, modes:FDM}),
|
|
outputRetractWipe: UC.newInput(LANG.ad_wpln_s, {title:LANG.ad_wpln_l, bound:UC.bound(0.0,10), convert:UC.toFloat, modes:FDM}),
|
|
outputRetractDwell: UC.newInput(LANG.ad_rdwl_s, {title:LANG.ad_rdwl_l, convert:UC.toInt, modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
sliceSolidMinArea: UC.newInput(LANG.ad_msol_s, {title:LANG.ad_msol_l, convert:UC.toFloat, modes:FDM}),
|
|
outputMinSpeed: UC.newInput(LANG.ad_mins_s, {title:LANG.ad_mins_l, bound:UC.bound(5,200), convert:UC.toFloat, modes:FDM}),
|
|
outputShortPoly: UC.newInput(LANG.ad_spol_s, {title:LANG.ad_spol_l, bound:UC.bound(0,10000), convert:UC.toFloat, modes:FDM}),
|
|
outputCoastDist: UC.newInput(LANG.ad_scst_s, {title:LANG.ad_scst_l, bound:UC.bound(0.0,10), convert:UC.toFloat, modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
zHopDistance: UC.newInput(LANG.ad_zhop_s, {title:LANG.ad_zhop_l, bound:UC.bound(0,3.0), convert:UC.toFloat, modes:FDM}),
|
|
arcTolerance: UC.newInput(LANG.ad_arct_s, {title:LANG.ad_arct_l, bound:UC.bound(0,1.0), convert:UC.toFloat, modes:FDM, show:() => { return isDanger() && isNotBelt() }}),
|
|
antiBacklash: UC.newInput(LANG.ad_abkl_s, {title:LANG.ad_abkl_l, bound:UC.bound(0,3), convert:UC.toInt, modes:FDM}),
|
|
fdmSep: UC.newBlank({class:"pop-sep", modes:FDM}),
|
|
gcodePauseLayers: UC.newInput(LANG.ag_paws_s, {title:LANG.ag_paws_l, modes:FDM, comma:true, show:isNotBelt}),
|
|
outputLoops: UC.newInput(LANG.ag_loop_s, {title:LANG.ag_loop_l, convert:UC.toInt, bound:UC.bound(0,1000), modes:FDM, show:isBelt}),
|
|
outputPurgeTower: UC.newBoolean(LANG.ad_purg_s, onBooleanClick, {title:LANG.ad_purg_l, modes:FDM, show:isMultiHead}),
|
|
|
|
// SLA
|
|
slaProc: UC.newGroup(LANG.sa_menu, null, {modes:SLA, group:"sla-slice"}),
|
|
slaSlice: UC.newInput(LANG.sa_lahe_s, {title:LANG.sa_lahe_l, convert:UC.toFloat, modes:SLA}),
|
|
slaShell: UC.newInput(LANG.sa_shel_s, {title:LANG.sa_shel_l, convert:UC.toFloat, modes:SLA}),
|
|
slaOpenTop: UC.newBoolean(LANG.sa_otop_s, onBooleanClick, {title:LANG.sa_otop_l, modes:SLA}),
|
|
slaOpenBase: UC.newBoolean(LANG.sa_obas_s, onBooleanClick, {title:LANG.sa_obas_l, modes:SLA}),
|
|
|
|
// SLA
|
|
slaOutput: UC.newGroup(LANG.sa_layr_m, null, {modes:SLA, group:"sla-layers"}),
|
|
slaLayerOn: UC.newInput(LANG.sa_lton_s, {title:LANG.sa_lton_l, convert:UC.toFloat, modes:SLA}),
|
|
slaLayerOff: UC.newInput(LANG.sa_ltof_s, {title:LANG.sa_ltof_l, convert:UC.toFloat, modes:SLA}),
|
|
slaPeelDist: UC.newInput(LANG.sa_pldi_s, {title:LANG.sa_pldi_l, convert:UC.toFloat, modes:SLA}),
|
|
slaPeelLiftRate: UC.newInput(LANG.sa_pllr_s, {title:LANG.sa_pllr_l, convert:UC.toFloat, modes:SLA}),
|
|
slaPeelDropRate: UC.newInput(LANG.sa_pldr_s, {title:LANG.sa_pldr_l, convert:UC.toFloat, modes:SLA}),
|
|
|
|
slaOutput: UC.newGroup(LANG.sa_base_m, null, {modes:SLA, group:"sla-base"}),
|
|
slaBaseLayers: UC.newInput(LANG.sa_balc_s, {title:LANG.sa_balc_l, convert:UC.toInt, modes:SLA}),
|
|
slaBaseOn: UC.newInput(LANG.sa_lton_s, {title:LANG.sa_bltn_l, convert:UC.toFloat, modes:SLA}),
|
|
slaBaseOff: UC.newInput(LANG.sa_ltof_s, {title:LANG.sa_bltf_l, convert:UC.toFloat, modes:SLA}),
|
|
slaBasePeelDist: UC.newInput(LANG.sa_pldi_s, {title:LANG.sa_pldi_l, convert:UC.toFloat, modes:SLA}),
|
|
slaBasePeelLiftRate: UC.newInput(LANG.sa_pllr_s, {title:LANG.sa_pllr_l, convert:UC.toFloat, modes:SLA}),
|
|
|
|
slaFill: UC.newGroup(LANG.sa_infl_m, null, {modes:SLA, group:"sla-infill"}),
|
|
slaFillDensity: UC.newInput(LANG.sa_ifdn_s, {title:LANG.sa_ifdn_l, convert:UC.toFloat, bound:UC.bound(0,1), modes:SLA}),
|
|
slaFillLine: UC.newInput(LANG.sa_iflw_s, {title:LANG.sa_iflw_l, convert:UC.toFloat, bound:UC.bound(0,5), modes:SLA}),
|
|
|
|
slaSupport: UC.newGroup(LANG.sa_supp_m, null, {modes:SLA, group:"sla-support"}),
|
|
slaSupportLayers: UC.newInput(LANG.sa_slyr_s, {title:LANG.sa_slyr_l, convert:UC.toInt, bound:UC.bound(5,100), modes:SLA}),
|
|
slaSupportGap: UC.newInput(LANG.sa_slgp_s, {title:LANG.sa_slgp_l, convert:UC.toInt, bound:UC.bound(3,30), modes:SLA}),
|
|
slaSupportDensity: UC.newInput(LANG.sa_sldn_s, {title:LANG.sa_sldn_l, convert:UC.toFloat, bound:UC.bound(0.01,0.9), modes:SLA}),
|
|
slaSupportSize: UC.newInput(LANG.sa_slsz_s, {title:LANG.sa_slsz_l, convert:UC.toFloat, bound:UC.bound(0.1,1), modes:SLA}),
|
|
slaSupportPoints: UC.newInput(LANG.sa_slpt_s, {title:LANG.sa_slpt_l, convert:UC.toInt, bound:UC.bound(3,10), modes:SLA}),
|
|
slaSupportEnable: UC.newBoolean(LANG.enable, onBooleanClick, {title:LANG.sl_slen_l, modes:SLA}),
|
|
|
|
slaOutput: UC.newGroup(LANG.sa_outp_m, null, {modes:SLA, group:"sla-first"}),
|
|
slaFirstOffset: UC.newInput(LANG.sa_opzo_s, {title:LANG.sa_opzo_l, convert:UC.toFloat, bound:UC.bound(0,1), modes:SLA}),
|
|
slaAntiAlias: UC.newSelect(LANG.sa_opaa_s, {title:LANG.sa_opaa_l, modes:SLA}, "antialias"),
|
|
|
|
rangeGroup: UC.newGroup("ranges", null, {modes:FDM, group:"ranges"}),
|
|
rangeList: UC.newRow([], {}),
|
|
|
|
settingsGroup: UC.newGroup(LANG.se_menu, $('settings')),
|
|
settingsTable: UC.newRow([ UI.settingsLoad = UC.newButton(LANG.se_load, settingsLoad) ]),
|
|
settingsTable: UC.newRow([ UI.settingsSave = UC.newButton(LANG.se_save, settingsSave) ]),
|
|
settingsSave: $('settingsSave'),
|
|
settingsName: $('settingsName'),
|
|
|
|
layers: UC.setGroup($("layers")),
|
|
});
|
|
|
|
// override old style settings two-button menu
|
|
UI.settingsGroup.onclick = settingsLoad;
|
|
UI.settingsSave.onclick = () => {
|
|
settingsSave(undefined, UI.settingsName.value);
|
|
};
|
|
|
|
function optSelected(sel) {
|
|
let opt = sel.options[sel.selectedIndex];
|
|
return opt ? opt.value : undefined;
|
|
}
|
|
|
|
function fillShow() {
|
|
return optSelected(UI.sliceFillType) === 'linear';
|
|
}
|
|
|
|
function spindleShow() {
|
|
return settings().device.spindleMax > 0;
|
|
}
|
|
|
|
// slider setup
|
|
const slbar = 30;
|
|
const slbar2 = slbar * 2;
|
|
const slider = UI.sliderRange;
|
|
const drag = { };
|
|
|
|
function pxToInt(txt) {
|
|
return txt ? parseInt(txt.substring(0,txt.length-2)) : 0;
|
|
}
|
|
|
|
function sliderUpdate() {
|
|
let start = drag.low / drag.maxval;
|
|
let end = (drag.low + drag.mid - slbar) / drag.maxval;
|
|
API.event.emit('slider.pos', { start, end });
|
|
API.var.layer_lo = Math.round(start * API.var.layer_max);
|
|
API.var.layer_hi = Math.round(end * API.var.layer_max);
|
|
API.show.layer();
|
|
SPACE.scene.active();
|
|
}
|
|
|
|
function dragit(el, delta) {
|
|
el.onmousedown = (ev) => {
|
|
tracker.style.display = 'block';
|
|
ev.stopPropagation();
|
|
drag.width = slider.clientWidth;
|
|
drag.maxval = drag.width - slbar2;
|
|
drag.start = ev.screenX;
|
|
drag.loat = drag.low = pxToInt(UI.sliderHold.style.marginLeft);
|
|
drag.mdat = drag.mid = UI.sliderMid.clientWidth;
|
|
drag.hiat = pxToInt(UI.sliderHold.style.marginRight);
|
|
drag.mdmax = drag.width - slbar - drag.loat;
|
|
drag.himax = drag.width - slbar - drag.mdat;
|
|
let cancel_drag = tracker.onmouseup = (ev) => {
|
|
if (ev) {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
}
|
|
slider.onmousemove = undefined;
|
|
tracker.style.display = 'none';
|
|
};
|
|
tracker.onmousemove = (ev) => {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
if (ev.buttons === 0) {
|
|
return cancel_drag();
|
|
}
|
|
if (delta) delta(ev.screenX - drag.start);
|
|
};
|
|
};
|
|
}
|
|
|
|
dragit(UI.sliderLo, (delta) => {
|
|
let midval = drag.mdat - delta;
|
|
let lowval = drag.loat + delta;
|
|
if (midval < slbar || lowval < 0) {
|
|
return;
|
|
}
|
|
UI.sliderHold.style.marginLeft = `${lowval}px`;
|
|
UI.sliderMid.style.width = `${midval}px`;
|
|
drag.low = lowval;
|
|
drag.mid = midval;
|
|
sliderUpdate();
|
|
});
|
|
dragit(UI.sliderMid, (delta) => {
|
|
let loval = drag.loat + delta;
|
|
let hival = drag.hiat - delta;
|
|
if (loval < 0 || hival < 0) return;
|
|
UI.sliderHold.style.marginLeft = `${loval}px`;
|
|
UI.sliderHold.style.marginRight = `${hival}px`;
|
|
drag.low = loval;
|
|
sliderUpdate();
|
|
});
|
|
dragit(UI.sliderHi, (delta) => {
|
|
let midval = drag.mdat + delta;
|
|
let hival = drag.hiat - delta;
|
|
if (midval < slbar || midval > drag.mdmax || hival < 0) return;
|
|
UI.sliderMid.style.width = `${midval}px`;
|
|
UI.sliderHold.style.marginRight = `${hival}px`;
|
|
drag.mid = midval;
|
|
sliderUpdate();
|
|
});
|
|
|
|
UI.sliderMin.onclick = () => {
|
|
API.show.layer(0,0);
|
|
}
|
|
|
|
UI.sliderMax.onclick = () => {
|
|
API.show.layer(API.var.layer_max,0);
|
|
}
|
|
|
|
UI.slider.onmouseover = (ev) => {
|
|
API.event.emit('slider.label');
|
|
};
|
|
|
|
UI.slider.onmouseleave = (ev) => {
|
|
if (!ev.buttons) API.event.emit('slider.unlabel');
|
|
};
|
|
|
|
UI.dev.search.onclick = (hide) => {
|
|
let style = UI.dev.filter.style;
|
|
if (style.display === 'flex' || hide === true) {
|
|
style.display = '';
|
|
deviceFilter = null;
|
|
updateDeviceList();
|
|
} else {
|
|
style.display = 'flex';
|
|
UI.dev.filter.focus();
|
|
deviceFilter = UI.dev.filter.value.toLowerCase();
|
|
updateDeviceList();
|
|
}
|
|
};
|
|
|
|
UI.dev.filter.onkeyup = (ev) => {
|
|
deviceFilter = UI.dev.filter.value.toLowerCase();
|
|
updateDeviceList();
|
|
};
|
|
|
|
UI.dev.filter.onclick = (ev) => {
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
};
|
|
|
|
API.event.on('slider.unlabel', (values) => {
|
|
});
|
|
|
|
API.event.on('slider.label', (values) => {
|
|
let digits = API.var.layer_max.toString().length;
|
|
$('slider-zero').style.width = `${digits}em`;
|
|
$('slider-max').style.width = `${digits}em`;
|
|
$('slider-zero').innerText = API.var.layer_lo;
|
|
$('slider-max').innerText = API.var.layer_hi;
|
|
});
|
|
|
|
API.event.on('slider.set', (values) => {
|
|
let width = slider.clientWidth;
|
|
let maxval = width - slbar2;
|
|
let start = Math.max(0, Math.min(1, values.start));
|
|
let end = Math.max(start, Math.min(1, values.end));
|
|
let lowval = start * maxval;
|
|
let midval = ((end - start) * maxval) + slbar;
|
|
let hival = maxval - end * maxval;
|
|
UI.sliderHold.style.marginLeft = `${lowval}px`;
|
|
UI.sliderMid.style.width = `${midval}px`;
|
|
UI.sliderHold.style.marginRight = `${hival}px`;
|
|
});
|
|
|
|
// store layer preferences
|
|
API.event.on('stack.show', label => {
|
|
let mode = API.mode.get();
|
|
let view = API.view.get();
|
|
API.conf.get().labels[`${mode}-${view}-${label}`] = true;
|
|
});
|
|
|
|
API.event.on('stack.hide', label => {
|
|
let mode = API.mode.get();
|
|
let view = API.view.get();
|
|
API.conf.get().labels[`${mode}-${view}-${label}`] = false;
|
|
});
|
|
|
|
// bind language choices
|
|
$('lset-en').onclick = function() {
|
|
SDB.setItem('kiri-lang', 'en-us');
|
|
API.space.reload();
|
|
};
|
|
$('lset-da').onclick = function() {
|
|
SDB.setItem('kiri-lang', 'da-dk');
|
|
API.space.reload();
|
|
};
|
|
$('lset-de').onclick = function() {
|
|
SDB.setItem('kiri-lang', 'de-de');
|
|
API.space.reload();
|
|
};
|
|
$('lset-fr').onclick = function() {
|
|
SDB.setItem('kiri-lang', 'fr-fr');
|
|
API.space.reload();
|
|
};
|
|
$('lset-pl').onclick = function() {
|
|
SDB.setItem('kiri-lang', 'pl-pl');
|
|
API.space.reload();
|
|
};
|
|
$('lset-pt').onclick = function() {
|
|
SDB.setItem('kiri-lang', 'pt-pt');
|
|
API.space.reload();
|
|
};
|
|
$('lset-es').onclick = function() {
|
|
SDB.setItem('kiri-lang', 'es-es');
|
|
API.space.reload();
|
|
};
|
|
$('lset-zh').onclick = function() {
|
|
SDB.setItem('kiri-lang', 'zh');
|
|
API.space.reload();
|
|
};
|
|
|
|
SPACE.addEventHandlers(self, [
|
|
'keyup', keyUpHandler,
|
|
'keydown', keyDownHandler,
|
|
'keypress', keyHandler,
|
|
'dragover', dragOverHandler,
|
|
'dragleave', dragLeave,
|
|
'drop', dropHandler
|
|
]);
|
|
|
|
function selectionSize(e) {
|
|
let dv = parseFloat(e.target.value || 1),
|
|
pv = parseFloat(e.target.was || 1),
|
|
ra = dv / pv,
|
|
xv = parseFloat(UI.sizeX.was),
|
|
yv = parseFloat(UI.sizeY.was),
|
|
zv = parseFloat(UI.sizeZ.was),
|
|
ta = e.target,
|
|
xc = UI.lockX.checked,
|
|
yc = UI.lockY.checked,
|
|
zc = UI.lockZ.checked,
|
|
xt = ta === UI.sizeX,
|
|
yt = ta === UI.sizeY,
|
|
zt = ta === UI.sizeZ,
|
|
tl = (xt && xc) || (yt && yc) || (zt && zc),
|
|
xr = ((tl && xc) || (!tl && xt) ? ra : 1),
|
|
yr = ((tl && yc) || (!tl && yt) ? ra : 1),
|
|
zr = ((tl && zc) || (!tl && zt) ? ra : 1);
|
|
// prevent null scale
|
|
if (xr * yr * zr === 0) {
|
|
return;
|
|
}
|
|
API.selection.scale(xr,yr,zr);
|
|
UI.sizeX.was = UI.sizeX.value = xv * xr;
|
|
UI.sizeY.was = UI.sizeY.value = yv * yr;
|
|
UI.sizeZ.was = UI.sizeZ.value = zv * zr;
|
|
}
|
|
|
|
function selectionScale(e) {
|
|
let dv = parseFloat(e.target.value || 1),
|
|
pv = parseFloat(e.target.was || 1),
|
|
ra = dv / pv,
|
|
xv = parseFloat(UI.scaleX.was),
|
|
yv = parseFloat(UI.scaleY.was),
|
|
zv = parseFloat(UI.scaleZ.was),
|
|
ta = e.target,
|
|
xc = UI.lockX.checked,
|
|
yc = UI.lockY.checked,
|
|
zc = UI.lockZ.checked,
|
|
xt = ta === UI.scaleX,
|
|
yt = ta === UI.scaleY,
|
|
zt = ta === UI.scaleZ,
|
|
tl = (xt && xc) || (yt && yc) || (zt && zc),
|
|
xr = ((tl && xc) || (!tl && xt) ? ra : 1),
|
|
yr = ((tl && yc) || (!tl && yt) ? ra : 1),
|
|
zr = ((tl && zc) || (!tl && zt) ? ra : 1);
|
|
API.selection.scale(xr,yr,zr);
|
|
UI.scaleX.was = UI.scaleX.value = xv * xr;
|
|
UI.scaleY.was = UI.scaleY.value = yv * yr;
|
|
UI.scaleZ.was = UI.scaleZ.value = zv * zr;
|
|
}
|
|
|
|
function selectionRotate(e) {
|
|
let deg = parseFloat(e.target.value) * DEG;
|
|
e.target.value = 0;
|
|
switch (e.target.id.split('').pop()) {
|
|
case 'x': return API.selection.rotate(deg,0,0);
|
|
case 'y': return API.selection.rotate(0,deg,0);
|
|
case 'z': return API.selection.rotate(0,0,deg);
|
|
}
|
|
}
|
|
|
|
SPACE.onEnterKey([
|
|
UI.scaleX, selectionScale,
|
|
UI.scaleY, selectionScale,
|
|
UI.scaleZ, selectionScale,
|
|
UI.sizeX, selectionSize,
|
|
UI.sizeY, selectionSize,
|
|
UI.sizeZ, selectionSize,
|
|
UI.toolName, updateTool,
|
|
UI.toolNum, updateTool,
|
|
UI.toolFluteDiam, updateTool,
|
|
UI.toolFluteLen, updateTool,
|
|
UI.toolShaftDiam, updateTool,
|
|
UI.toolShaftLen, updateTool,
|
|
UI.toolTaperTip, updateTool,
|
|
$('rot_x'), selectionRotate,
|
|
$('rot_y'), selectionRotate,
|
|
$('rot_z'), selectionRotate
|
|
], true);
|
|
|
|
$('lab-axis').onclick = () => {
|
|
UI.lockX.checked =
|
|
UI.lockY.checked =
|
|
UI.lockZ.checked = !(
|
|
UI.lockX.checked ||
|
|
UI.lockY.checked ||
|
|
UI.lockZ.checked
|
|
);
|
|
};
|
|
|
|
$('scale-reset').onclick = $('lab-scale').onclick = () => {
|
|
API.selection.scale(1 / UI.scaleX.was, 1 / UI.scaleY.was, 1 / UI.scaleZ.was);
|
|
UI.scaleX.value = UI.scaleY.value = UI.scaleZ.value =
|
|
UI.scaleX.was = UI.scaleY.was = UI.scaleZ.was = 1;
|
|
};
|
|
|
|
let hpops = [];
|
|
UC.hoverPop(UI.ltsetup, { group: hpops, target: $('set-pop') });
|
|
UC.hoverPop(UI.ltfile, { group: hpops, target: $('file-pop') });
|
|
UC.hoverPop(UI.ltview, { group: hpops, target: $('pop-view') });
|
|
UC.hoverPop(UI.ltact, { group: hpops, target: $('pop-slice') });
|
|
UC.hoverPop(UI.render, { group: hpops, target: $('pop-render'), sticky: false });
|
|
UC.hoverPop(UI.edit, { group: hpops, target: $('pop-tools'), sticky: false });
|
|
// UC.hoverPop(UI.rotate, { group: hpops, target: $('pop-rotate'), sticky: true });
|
|
// UC.hoverPop(UI.scale, { group: hpops, target: $('pop-scale'), sticky: true });
|
|
UC.hoverPop(UI.nozzle, { group: hpops, target: $('pop-nozzle'), sticky: true });
|
|
UC.hoverPop($('app-acct'), { group: hpops, target: $('acct-pop') } );
|
|
UC.hoverPop($('app-mode'), { group: hpops, target: $('mode-info') } );
|
|
UC.hoverPop($('app-name'), { group: hpops, target: $('app-info') } );
|
|
|
|
UC.onBlur([
|
|
UI.toolName,
|
|
UI.toolNum,
|
|
UI.toolFluteDiam,
|
|
UI.toolFluteLen,
|
|
UI.toolShaftDiam,
|
|
UI.toolShaftLen,
|
|
UI.toolTaperTip,
|
|
], updateTool);
|
|
|
|
UI.toolMetric.onclick = updateTool;
|
|
UI.toolType.onchange = updateTool;
|
|
// default show gcode pre
|
|
UI.gcodePre.button.click();
|
|
|
|
function mksvg(src) {
|
|
let svg = DOC.createElement('svg');
|
|
svg.innerHTML = src;
|
|
return svg;
|
|
}
|
|
|
|
function mklbl(src) {
|
|
let lbl = DOC.createElement('label');
|
|
lbl.innerText = src;
|
|
return lbl;
|
|
}
|
|
|
|
for (let mode of ["fdm","sla","cam","laser"]) {
|
|
if (API.feature.modes.indexOf(mode) >= 0) {
|
|
$(`mode-${mode}`).appendChild(mksvg(icons[mode]));
|
|
} else {
|
|
$(`mode-${mode}`).style.display = 'none';
|
|
}
|
|
}
|
|
|
|
API.platform.update_size();
|
|
|
|
SPACE.mouse.onHover((int, event, ints) => {
|
|
if (!API.feature.hover) return;
|
|
if (!int) return API.feature.hovers || API.widgets.meshes();
|
|
API.event.emit('mouse.hover', {int, ints, event, point: int.point, type: 'widget'});
|
|
});
|
|
|
|
SPACE.platform.onHover((int, event) => {
|
|
if (!API.feature.hover) return;
|
|
if (int) API.event.emit('mouse.hover', {point: int, event, type: 'platform'});
|
|
});
|
|
|
|
// block standard browser context menu
|
|
DOC.oncontextmenu = (event) => {
|
|
let et = event.target;
|
|
if (et.tagName === 'CANVAS' || et.id === 'context-menu' || et.classList.contains('draggable')) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return false;
|
|
}
|
|
};
|
|
|
|
SPACE.mouse.up((event, int) => {
|
|
// context menu
|
|
if (event.button === 2) {
|
|
let et = event.target;
|
|
if (et.tagName != 'CANVAS' && et.id != 'context-menu') {
|
|
return;
|
|
}
|
|
let full = API.view.isArrange();
|
|
for (let key of ["layflat","mirror","duplicate"]) {
|
|
$(`context-${key}`).disabled = !full;
|
|
}
|
|
let style = UI.context.style;
|
|
style.display = 'flex';
|
|
style.left = `${event.clientX-3}px`;
|
|
style.top = `${event.clientY-3}px`;
|
|
UI.context.onmouseleave = () => {
|
|
style.display = '';
|
|
};
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
contextInt = int;
|
|
}
|
|
});
|
|
|
|
SPACE.mouse.downSelect((int,event) => {
|
|
if (API.feature.hover) {
|
|
if (int) {
|
|
API.event.emit('mouse.hover.down', {int, point: int.point});
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
// lay flat with meta or ctrl clicking a selected face
|
|
if (int && (event.ctrlKey || event.metaKey || API.feature.on_face_select)) {
|
|
let q = new THREE.Quaternion();
|
|
// find intersecting point, look "up" on Z and rotate to face that
|
|
q.setFromUnitVectors(int.face.normal, new THREE.Vector3(0,0,-1));
|
|
API.selection.rotate(q);
|
|
}
|
|
if (API.view.get() !== VIEWS.ARRANGE) {
|
|
// return no selection in modes other than arrange
|
|
return null;
|
|
} else {
|
|
// return selected meshes for further mouse processing
|
|
return API.feature.hovers || API.selection.meshes();
|
|
}
|
|
});
|
|
|
|
SPACE.mouse.upSelect(function(object, event) {
|
|
if (event && API.feature.hover) {
|
|
API.event.emit('mouse.hover.up', { object, event });
|
|
return;
|
|
}
|
|
if (event && event.target.nodeName === "CANVAS") {
|
|
if (object && object.object) {
|
|
if (object.object.widget) {
|
|
platform.select(object.object.widget, event.shiftKey);
|
|
}
|
|
} else {
|
|
platform.deselect();
|
|
}
|
|
} else {
|
|
return API.feature.hovers || API.widgets.meshes();
|
|
}
|
|
});
|
|
|
|
SPACE.mouse.onDrag(function(delta) {
|
|
if (API.feature.hover) {
|
|
return;
|
|
}
|
|
if (delta && UI.freeLayout.checked) {
|
|
let set = settings();
|
|
let dev = set.device;
|
|
let bound = set.bounds_sel;
|
|
let width = dev.bedWidth/2;
|
|
let depth = dev.bedDepth/2;
|
|
let isout = (
|
|
bound.min.x <= -width ||
|
|
bound.min.y <= -depth ||
|
|
bound.max.x >= width ||
|
|
bound.max.y >= depth
|
|
);
|
|
if (!isout) {
|
|
if (bound.min.x + delta.x <= -width) return;
|
|
if (bound.min.y + delta.y <= -depth) return;
|
|
if (bound.max.x + delta.x >= width) return;
|
|
if (bound.max.y + delta.y >= depth) return;
|
|
}
|
|
API.selection.move(delta.x, delta.y, 0);
|
|
API.event.emit('selection.drag', delta);
|
|
} else {
|
|
return API.selection.meshes().length > 0;
|
|
}
|
|
});
|
|
|
|
API.space.restore(init_two) || checkSeed(init_two) || init_two();
|
|
|
|
};
|
|
|
|
// SECOND STAGE INIT AFTER UI RESTORED
|
|
|
|
function init_two() {
|
|
API.event.emit('init.two');
|
|
|
|
// API.space.set_focus();
|
|
|
|
// call driver initializations, if present
|
|
Object.values(KIRI.driver).forEach(driver => {
|
|
if (driver.init) try {
|
|
driver.init(KIRI, API);
|
|
} catch (error) {
|
|
console.log({driver_init_fail: driver, error})
|
|
}
|
|
});
|
|
|
|
// load script extensions
|
|
if (SETUP.s) SETUP.s.forEach(function(lib) {
|
|
let scr = DOC.createElement('script');
|
|
scr.setAttribute('defer',true);
|
|
scr.setAttribute('src',`/code/${lib}.js?${KIRI.version}`);
|
|
DOC.body.appendChild(scr);
|
|
STATS.add('load_'+lib);
|
|
API.event.emit('load.lib', lib);
|
|
});
|
|
|
|
// load CSS extensions
|
|
if (SETUP.ss) SETUP.ss.forEach(function(style) {
|
|
style = style.charAt(0) === '/' ? style : `/kiri/style-${style}`;
|
|
let ss = DOC.createElement('link');
|
|
ss.setAttribute("type", "text/css");
|
|
ss.setAttribute("rel", "stylesheet");
|
|
ss.setAttribute("href", `${style}.css?${KIRI.version}`);
|
|
DOC.body.appendChild(ss);
|
|
});
|
|
|
|
// override stored settings
|
|
if (SETUP.v) SETUP.v.forEach(function(kv) {
|
|
kv = kv.split('=');
|
|
SDB.setItem(kv[0],kv[1]);
|
|
});
|
|
|
|
// import octoprint settings
|
|
if (SETUP.ophost) {
|
|
let ohost = API.const.OCTO = {
|
|
host: SETUP.ophost[0],
|
|
apik: SETUP.opkey ? SETUP.opkey[0] : ''
|
|
};
|
|
SDB['octo-host'] = ohost.host;
|
|
SDB['octo-apik'] = ohost.apik;
|
|
console.log({octoprint:ohost});
|
|
}
|
|
|
|
// bind this to UI so main can call it on settings import
|
|
UI.sync = function() {
|
|
const current = settings();
|
|
const control = current.controller;
|
|
const process = settings.process;
|
|
|
|
platform.deselect();
|
|
CATALOG.addFileListener(updateCatalog);
|
|
SPACE.view.setZoom(control.reverseZoom, control.zoomSpeed);
|
|
SPACE.platform.setZOff(0.2);
|
|
|
|
// restore UI state from settings
|
|
UI.showOrigin.checked = control.showOrigin;
|
|
UI.showRulers.checked = control.showRulers;
|
|
UI.showSpeeds.checked = control.showSpeeds;
|
|
UI.freeLayout.checked = control.freeLayout;
|
|
UI.autoLayout.checked = control.autoLayout;
|
|
UI.reverseZoom.checked = control.reverseZoom;
|
|
UI.autoSave.checked = control.autoSave;
|
|
UI.decimate.checked = control.decimate;
|
|
UI.healMesh.checked = control.healMesh;
|
|
UI.threaded.checked = control.threaded;
|
|
UI.assembly.checked = control.assembly;
|
|
UI.ortho.checked = control.ortho;
|
|
UI.devel.checked = control.devel;
|
|
lineTypeSave();
|
|
detailSave();
|
|
updateFPS();
|
|
|
|
// optional set-and-lock mode (hides mode menu)
|
|
let SETMODE = SETUP.mode ? SETUP.mode[0] : null;
|
|
|
|
// optional set-and-lock device (hides device menu)
|
|
let DEVNAME = SETUP.dev ? SETUP.dev[0] : null;
|
|
|
|
// setup default mode and enable mode locking, if set
|
|
API.mode.set(SETMODE || STARTMODE || current.mode, SETMODE);
|
|
|
|
// fill device list
|
|
updateDeviceList();
|
|
|
|
// ensure settings has gcode
|
|
selectDevice(DEVNAME || API.device.get());
|
|
|
|
// update ui fields from settings
|
|
API.view.update_fields();
|
|
|
|
// default to ARRANGE view mode
|
|
API.view.set(VIEWS.ARRANGE);
|
|
|
|
// add ability to override
|
|
API.show.controls(API.feature.controls);
|
|
|
|
// update everything dependent on the platform size
|
|
platform.update_size();
|
|
|
|
// load wasm if indicated
|
|
KIRI.client.wasm(control.assembly === true);
|
|
};
|
|
|
|
UI.sync();
|
|
|
|
// clear alerts as they build up
|
|
setInterval(API.event.alerts, 1000);
|
|
|
|
// add hide-alerts-on-alert-click
|
|
UI.alert.dialog.onclick = function() {
|
|
API.event.alerts(true);
|
|
};
|
|
|
|
// enable modal hiding
|
|
$('mod-x').onclick = API.modal.hide;
|
|
|
|
if (!SETUP.s) console.log(`kiri | init main | ${KIRI.version}`);
|
|
|
|
// send init-done event
|
|
API.event.emit('init-done', STATS);
|
|
|
|
// show gdpr if it's never been seen and we're not iframed
|
|
if (!SDB.gdpr && WIN.self === WIN.top && !SETUP.debug) {
|
|
$('gdpr').style.display = 'flex';
|
|
}
|
|
|
|
// add keyboard focus handler (must use for iframes)
|
|
WIN.addEventListener('load', function () {
|
|
WIN.focus();
|
|
DOC.body.addEventListener('click', function() {
|
|
WIN.focus();
|
|
},false);
|
|
});
|
|
|
|
// dismiss gdpr alert
|
|
$('gotit').onclick = () => {
|
|
$('gdpr').style.display = 'none';
|
|
SDB.gdpr = Date.now();
|
|
};
|
|
|
|
// lift curtain
|
|
$('curtain').style.display = 'none';
|
|
|
|
function showSetup() {
|
|
API.modal.show('setup');
|
|
}
|
|
|
|
// bind interface action elements
|
|
$('app-name').onclick = API.help.show;
|
|
$('app-mode').onclick = (ev) => { ev.stopPropagation(); showSetup() };
|
|
$('set-device').onclick = (ev) => { ev.stopPropagation(); showSetup() };
|
|
$('set-tools').onclick = (ev) => { ev.stopPropagation(); showTools() };
|
|
$('set-prefs').onclick = (ev) => { ev.stopPropagation(); API.modal.show('prefs') };
|
|
UI.acct.help.onclick = (ev) => { ev.stopPropagation(); API.help.show() };
|
|
UI.acct.export.onclick = (ev) => { ev.stopPropagation(); profileExport() };
|
|
UI.acct.export.title = LANG.acct_xpo;
|
|
$('file-recent').onclick = () => { API.modal.show('files') };
|
|
$('file-import').onclick = (ev) => { API.event.import(ev) };
|
|
UI.back.onclick = API.platform.layout;
|
|
UI.trash.onclick = API.selection.delete;
|
|
UI.func.slice.onclick = (ev) => { ev.stopPropagation(); API.function.slice() };
|
|
UI.func.preview.onclick = (ev) => { ev.stopPropagation(); API.function.print() };
|
|
UI.func.animate.onclick = (ev) => { ev.stopPropagation(); API.function.animate() };
|
|
UI.func.export.onclick = (ev) => { ev.stopPropagation(); API.function.export() };
|
|
$('view-arrange').onclick = API.platform.layout;
|
|
$('view-top').onclick = SPACE.view.top;
|
|
$('view-home').onclick = SPACE.view.home;
|
|
$('view-clear').onclick = API.platform.clear;
|
|
$('mode-fdm').onclick = () => { API.mode.set('FDM') };
|
|
$('mode-sla').onclick = () => { API.mode.set('SLA') };
|
|
$('mode-cam').onclick = () => { API.mode.set('CAM') };
|
|
$('mode-laser').onclick = () => { API.mode.set('LASER') };
|
|
$('unrotate').onclick = () => { API.widgets.for(w => w.unrotate()) };
|
|
// rotation buttons
|
|
let d = (Math.PI / 180) * 5;
|
|
$('rot_x_lt').onclick = () => { API.selection.rotate(-d,0,0) };
|
|
$('rot_x_gt').onclick = () => { API.selection.rotate( d,0,0) };
|
|
$('rot_y_lt').onclick = () => { API.selection.rotate(0,-d,0) };
|
|
$('rot_y_gt').onclick = () => { API.selection.rotate(0, d,0) };
|
|
$('rot_z_lt').onclick = () => { API.selection.rotate(0,0, d) };
|
|
$('rot_z_gt').onclick = () => { API.selection.rotate(0,0,-d) };
|
|
// rendering options
|
|
$('render-hide').onclick = () => { API.view.wireframe(false, 0, 0); };
|
|
$('render-ghost').onclick = () => { API.view.wireframe(false, 0, 0.5); };
|
|
$('render-wire').onclick = () => { API.view.wireframe(true, 0, 0.5); };
|
|
$('render-solid').onclick = () => { API.view.wireframe(false, 0, 1); };
|
|
// mesh buttons
|
|
$('mesh-heal').onclick = () => { API.widgets.heal() };
|
|
$('mesh-export-stl').onclick = () => { objectsExport('stl') };
|
|
$('mesh-export-obj').onclick = () => { objectsExport('obj') };
|
|
// context menu
|
|
$('context-export-workspace').onclick = () => { profileExport(true) };
|
|
$('context-clear-workspace').onclick = () => {
|
|
API.view.set(VIEWS.ARRANGE);
|
|
API.platform.clear();
|
|
UI.context.onmouseleave();
|
|
};
|
|
$('context-duplicate').onclick = duplicateSelection;
|
|
$('context-mirror').onclick = mirrorSelection;
|
|
$('context-layflat').onclick = layFlat;
|
|
$('context-setfocus').onclick = setFocus;
|
|
|
|
UI.modal.onclick = API.modal.hide;
|
|
UI.modalBox.onclick = (ev) => { ev.stopPropagation() };
|
|
|
|
// add app name hover info
|
|
$('app-info').innerText = KIRI.version;
|
|
// show topline separator when iframed
|
|
// try { if (WIN.self !== WIN.top) $('top-sep').style.display = 'flex' } catch (e) { }
|
|
|
|
// bind tictac buttons to panel show/hide
|
|
let groups = {};
|
|
for (let tt of [...document.getElementsByClassName('tictac')]) {
|
|
let sid = tt.getAttribute('select');
|
|
let gid = tt.getAttribute('group') || 'default';
|
|
let lid = tt.getAttribute('label');
|
|
let grp = groups[gid] = groups[gid] || [];
|
|
let target = $(sid);
|
|
target.style.display = 'none';
|
|
target.control = tt;
|
|
grp.push({button: tt, div: target});
|
|
tt.onclick = () => {
|
|
for (let rec of grp) {
|
|
let {button, div} = rec;
|
|
if (div === target) {
|
|
div.style.display = '';
|
|
button.classList.add('selected');
|
|
} else {
|
|
div.style.display = 'none';
|
|
button.classList.remove('selected');
|
|
}
|
|
}
|
|
if (lid) {
|
|
$(lid).innerText = tt.getAttribute('title');
|
|
}
|
|
};
|
|
}
|
|
// bind closer X to hiding parent action
|
|
for (let tt of [...document.getElementsByClassName('closer')]) {
|
|
let tid = tt.getAttribute('target');
|
|
let target = tid ? $(tid) : parentWithClass(tt, 'movable');
|
|
target.style.display = 'none';
|
|
tt.onmousedown = (ev) => {
|
|
target.style.display = 'none';
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
};
|
|
}
|
|
// add drag behavior to movers
|
|
[...document.getElementsByClassName('mover')].forEach(mover => {
|
|
mover.onmousedown = (ev) => {
|
|
let moving = parentWithClass(mover, 'movable');
|
|
let mpos = {
|
|
x: moving.offsetLeft,
|
|
y: moving.offsetTop
|
|
};
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
let origin = {
|
|
x: ev.screenX,
|
|
y: ev.screenY
|
|
};
|
|
let tracker = UI.tracker;
|
|
tracker.style.display = 'block';
|
|
tracker.onmousemove = (ev) => {
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
let pos = {
|
|
x: ev.screenX,
|
|
y: ev.screenY
|
|
};
|
|
let delta = {
|
|
x: pos.x - origin.x,
|
|
y: pos.y - origin.y
|
|
};
|
|
moving.style.left = `${mpos.x + delta.x}px`;
|
|
moving.style.top = `${mpos.y + delta.y}px`;
|
|
};
|
|
tracker.onmouseup = (ev) => {
|
|
UI.tracker.style.display = 'none';
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
tracker.onmouseup = null;
|
|
tracker.onmouseout = null;
|
|
tracker.onmousemove = null;
|
|
};
|
|
};
|
|
});
|
|
// bind tool show buttons
|
|
UI.tool.show = function(tictac) {
|
|
if (typeof(tictac) === 'string') {
|
|
tictac = $(tictac);
|
|
}
|
|
let mover = parentWithClass(tictac.control, 'movable');
|
|
mover.style.display = 'flex';
|
|
tictac.control.onclick();
|
|
}
|
|
$('tool-rotate').onclick = () => { UI.tool.show('ft-rotate') };
|
|
$('tool-scale').onclick = () => { UI.tool.show('ft-scale') };
|
|
$('tool-mesh').onclick = () => { UI.tool.show('ft-mesh') };
|
|
$('tool-select').onclick = () => { UI.tool.show('ft-select') };
|
|
|
|
// warn users they are running a beta release
|
|
if (KIRI.beta && KIRI.beta > 0 && SDB.kiri_beta != KIRI.beta) {
|
|
API.show.alert("this is a beta / development release");
|
|
SDB.kiri_beta = KIRI.beta;
|
|
}
|
|
}
|
|
|
|
// update static html elements with language overrides
|
|
UI.lang = function() {
|
|
// lk attribute causes inner text to be replaced with lang value
|
|
for (let el of [...DOC.querySelectorAll("[lk]")]) {
|
|
let key = el.getAttribute('lk');
|
|
let val = LANG[key];
|
|
if (val) {
|
|
el.innerText = val;
|
|
} else {
|
|
console.log({missing_ln: key});
|
|
}
|
|
}
|
|
// lkt attribute causes a title attribute to be set from lang value
|
|
for (let el of [...DOC.querySelectorAll("[lkt]")]) {
|
|
let key = el.getAttribute('lkt');
|
|
let val = LANG[key];
|
|
if (val) {
|
|
el.setAttribute("title", val);
|
|
} else {
|
|
console.log({missing_ln: key});
|
|
}
|
|
}
|
|
};
|
|
|
|
// if a language needs to load, the script is injected and loaded
|
|
// first. once this loads, or doesn't, the initialization begins
|
|
let lang_load = false;
|
|
let lang_set = undefined;
|
|
let lang = SETUP.ln ? SETUP.ln[0] : SDB.getItem('kiri-lang') || KIRI.lang.get();
|
|
|
|
// inject language script if not english
|
|
if (lang && lang !== 'en' && lang !== 'en-us') {
|
|
lang_set = lang;
|
|
let map = KIRI.lang.map(lang);
|
|
let scr = DOC.createElement('script');
|
|
// scr.setAttribute('defer',true);
|
|
scr.setAttribute('src',`/kiri/lang/${map}.js?${KIRI.version}`);
|
|
(DOC.body || DOC.head).appendChild(scr);
|
|
STATS.set('ll',lang);
|
|
scr.onload = function() {
|
|
lang_load = true;
|
|
KIRI.lang.set(map);
|
|
UI.lang();
|
|
init_one();
|
|
};
|
|
scr.onerror = function(err) {
|
|
console.log({language_load_error: err, lang})
|
|
init_one();
|
|
}
|
|
}
|
|
|
|
// set to browser default which will be overridden
|
|
// by any future script loads (above)
|
|
if (!lang_load) {
|
|
KIRI.lang.set();
|
|
UI.lang();
|
|
}
|
|
|
|
// schedule init_one to run after all page content is loaded
|
|
// unless a languge script is loading first, in which case it
|
|
// will call init once it's done (or failed)
|
|
if (!lang_set) {
|
|
if (document.readyState === 'loading') {
|
|
SPACE.addEventListener(DOC, 'DOMContentLoaded', init_one);
|
|
} else {
|
|
// happens in debug mode when scripts are chain loaded
|
|
init_one();
|
|
}
|
|
}
|
|
|
|
})();
|