/** Copyright Stewart Allen -- All Rights Reserved */ import { $, $c } from '../moto/webui.js'; import { api } from '../kiri/api.js'; import { base } from '../geo/base.js'; import { local as sdb } from '../data/local.js'; import { space } from '../moto/space.js'; import { LOCAL, SETUP } from './main.js'; import { VIEWS, MODES, SEED } from '../kiri/consts.js'; import { beta, version } from '../moto/license.js'; import { settings as set_ctrl } from './settings.js'; import { init as initCAM } from '../kiri-mode/cam/client.js'; import { init as initFDM } from '../kiri-mode/fdm/client.js'; import { init as initLaser } from '../kiri-mode/laser/client.js'; import { init as initSLA } from '../kiri-mode/sla/client.js'; let { CAM, SLA, FDM, LASER, DRAG, WJET, WEDM } = MODES, { consts, client, catalog, platform, selection } = api, LANG = api.language.current, WIN = self.window, DOC = self.document, STARTMODE = SETUP.sm && SETUP.sm.length === 1 ? SETUP.sm[0] : null, TWOD = [ LASER, DRAG, WJET, WEDM ], TWONED = [ LASER, DRAG, WJET ], THREED = [ FDM, CAM, SLA ], GCODE = [ FDM, CAM, ...TWOD ], CAM_LZR = [ CAM, ...TWOD ], FDM_LZN = [ FDM, ...TWONED ], NO_WEDM = [ FDM, CAM, SLA, LASER, DRAG, WJET ], FDM_CAM = [ FDM, CAM ], proto = location.protocol, platformColor, separator = true, hideable = true, inline = true, driven = true, trigger = true, units = true, ui = api.ui, uc = api.uc; function settings() { return api.conf.get(); } function updateTool(ev) { api.tool.update(ev); } 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.update_bounds(); 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 thinWallSave() { let opt = ui.sliceDetectThin; let level = opt.options[opt.selectedIndex]; if (level) { settings().process.sliceDetectThin = level.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; } client.config({ base: { clipperClean: rez } }); settings().controller.detail = level; api.conf.save(); } } function speedSave() { settings().controller.showSpeeds = ui.showSpeeds.checked; api.view.update_speeds(); } function zAnchorSave() { api.conf.update(); api.platform.update_top_z(); } function setThreaded(bool) { if (bool) { client.pool.start(); } else { client.pool.stop(); } return bool; } // upon restore, seed presets api.event.emit('preset', api.conf.dbo()); api.event.on("set.threaded", bool => setThreaded(bool)); function booleanSave() { let control = settings().controller; let doAlert = ui.ortho.checked !== control.ortho; if (control.assembly != ui.assembly.checked) { client.wasm(ui.assembly.checked); } if (control.antiAlias != ui.antiAlias.checked) { api.show.alert('Page Reload Required to Change Aliasing'); } control.shiny = ui.shiny.checked; control.drawer = ui.drawer.checked; control.scrolls = ui.scrolls.checked; control.showOrigin = ui.showOrigin.checked; control.showRulers = ui.showRulers.checked; control.autoLayout = ui.autoLayout.checked; control.freeLayout = ui.freeLayout.checked; control.spaceRandoX = ui.spaceRandoX.checked; control.autoSave = ui.autoSave.checked; control.antiAlias = ui.antiAlias.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.healMesh = ui.healMesh.checked; control.threaded = setThreaded(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(); updateStats(); updateDrawer(); api.event.emit('boolean.update'); if (doAlert) { api.show.alert("change requires page refresh"); } } function updateDrawer() { const { drawer, scrolls } = settings().controller; $c('app', drawer ? 'slideshow' : '', drawer ? '' : 'slideshow'); $c('app', scrolls ? '' : 'hide-scroll', scrolls ? 'hide-scroll' : ''); } function updateStats() { if (self.debug !== true) { return; } let { div, fps, rms, rnfo } = ui.stats; div.style.display = 'flex'; setInterval(() => { const nrms = space.view.getRMS().toFixed(1); const nfps = space.view.getFPS().toFixed(1); const rend = space.renderInfo(); const { memory, render } = rend; if (nfps !== fps.innerText) { fps.innerText = nfps; } if (nrms !== rms.innerText) { rms.innerText = nrms; } if (rnfo.offsetParent !== null) { rnfo.innerHTML = Object.entries({ ...memory, ...render, render_ms: nrms, frames_sec: nfps }).map(row => { return `
${row[0]}
` }).join(''); } }, 100); } function onBooleanClick(el) { // copy some ui elements to target settings let settings = api.conf.get(); settings.device.bedBelt = ui.deviceBelt.checked && api.mode.is_fdm(); settings.device.bedRound = ui.deviceRound.checked && api.mode.is_fdm(); settings.device.originCenter = ui.deviceOrigin.checked || ui.deviceRound.checked; settings.device.fwRetract = ui.fwRetract.checked; // refresh vars and other ui elements uc.refresh(); if (el === ui.camStockIndexed) { api.view.set_arrange(); } api.conf.update(); DOC.activeElement.blur(); api.event.emit("boolean.click"); api.devices.update_laser_state(); } 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 cca(c) { return c.charCodeAt(0); } function keyUpHandler(evt) { if (api.feature.on_key) { if (api.feature.on_key({up:evt})) return; } for (let handler of api.feature.on_key2) { if (handler({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(); // 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; } for (let handler of api.feature.on_key2) { if (handler({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(selection.meshes()); evt.preventDefault(); break; case 37: // left arrow if (inputHasFocus()) return false; if (deg) selection.rotate(0, 0, -deg); if (move > 0) selection.move(-move, 0, 0); evt.preventDefault(); break; case 39: // right arrow if (inputHasFocus()) return false; if (deg) selection.rotate(0, 0, deg); if (move > 0) 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) selection.rotate(deg, 0, 0); if (move > 0) 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) selection.rotate(-deg, 0, 0); if (move > 0) 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(); set_ctrl.sync.put(); } 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; } for (let handler of api.feature.on_key2) { if (handler({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(); WIN.location.reload(); } }); 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 api.function.slice(); break; case cca('P'): // prepare case cca('p'): // prepare if (api.mode.get() !== 'SLA') { // hidden in SLA mode api.function.prepare(); } break; case cca('X'): // export case cca('x'): // export 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 api.show.devices(); break; case cca('o'): // tools api.show.tools(); 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(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('a'): if (api.view.get() === VIEWS.ARRANGE) { // auto arrange items on platform platform.layout(); if (!api.conf.get().controller.spaceRandoX) { api.space.set_focus(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 duplicateSelection() { selection.duplicate(); } function mirrorSelection() { 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 { coord = (coord || '').split(','); let 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; selection.rotate(x, y, z); }); } function positionSelection() { if (selection.meshes().length === 0) { api.show.alert("select object to position"); return; } let current = settings(), { device, process} = current, center = process.ctOriginCenter || process.camOriginCenter || device.bedRound || device.originCenter, bounds = boundsSelection(); api.uc.prompt("Enter X,Y coordinates for selection","").then(coord => { coord = (coord || '').split(','); let x = parseFloat(coord[0] || 0.0), y = parseFloat(coord[1] || 0.0), z = parseFloat(coord[2] || 0.0); if (!center) { x = x - device.bedWidth/2 + (bounds.max.x - bounds.min.x)/2; y = y - device.bedDepth/2 + (bounds.max.y - bounds.min.y)/2 } 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 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(selection.export(format), name); }); } function workspaceNew() { uc.confirm("Clear Workspace?", {ok:true, cancel: false}).then(value => { if (value === true) { let proc = set_ctrl.proc(); proc.ops && (proc.ops.length = 0); proc.op2 && (proc.op2.length = 0); api.platform.clear(); } }); } function profileExport() { const opt = {pre: [ "
", "

Workspace Export

", " ", " ", "  Exclude meshes", "
" ]}; let suggestion = "workspace"; let file = api.widgets.all()[0]?.meta.file || ''; if (file) { suggestion = `${suggestion}_${file.split('.')[0]}`.replaceAll(' ','_'); }; uc.confirm("Filename", {ok:true, cancel: false}, suggestion, opt).then(name => { if (!name) return; let work = !$('excwork').checked; let json = api.conf.export({work, clear:true}); 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(); sync_put(); }; if (name) { saveAs(name); } else { uc.prompt("Save Settings As", cp ? lp || def : def).then(saveAs); } } function settingsLoad() { api.conf.show(); } function updateDeviceSize() { api.conf.update(); platform.update_size(); platform.update_origin(); } async function sync_put() { await set_ctrl.sync.put(); } 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); } if (files.length === 1) { api.platform.load_files(files); } else if (files.length > 1) { uc.confirm(`group ${files.length} files?`).then(yes => { api.platform.load_files(files, yes ? [] : undefined); }); } } 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 { api.uc.prompt(`rename file`, short).then(newname => { if (newname && newname !== short) { catalog.rename(name, `${newname}${ext}`, then => { api.modal.show('files'); }); } }); }; 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 = ''; 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 api.device.isBelt(); } function isNotBelt() { return !isBelt(); } // MAIN INITIALIZATION FUNCTION function init_one() { let { event, conf, view, show } = api, { bound, toInt, toFloat } = uc, { newBlank, newButton, newBoolean, newGroup, newInput } = uc, { newSelect, newLabel, newValue, newRow, newGCode, newDiv } = uc; event.emit('init.one'); // ensure we have settings from last session conf.restore(); let container = $('container'), gcode = $('dev-gcode'), tracker = $('tracker'), controller = settings().controller; WIN.addEventListener("resize", () => { event.emit('resize'); }); event.on('resize', () => { if (WIN.innerHeight < 800) { ui.modalBox.classList.add('mh85'); } else { ui.modalBox.classList.remove('mh85'); } view.update_slider(); }); space.sky.showGrid(false); space.sky.setColor(controller.dark ? 0 : 0xffffff); space.setAntiAlias(controller.antiAlias); space.init(container, function (delta) { let vars = api.var; if (vars.layer_max === 0 || !delta) return; if (controller.reverseZoom) delta = -delta; let same = vars.layer_hi === vars.layer_lo; let track = vars.layer_lo > 0; if (delta > 0) { vars.layer_hi = Math.max(same ? 0 : vars.layer_lo, vars.layer_hi - 1); if (track) { vars.layer_lo = Math.max(0, vars.layer_lo - 1); } } else if (delta < 0) { vars.layer_hi = Math.min(vars.layer_max, vars.layer_hi + 1); if (track) { vars.layer_lo = Math.min(vars.layer_hi, vars.layer_lo + 1); } } if (same) { vars.layer_lo = vars.layer_hi; } view.update_slider(); show.slices(); }, controller.ortho); space.platform.onMove(conf.save); space.platform.setRound(true); space.useDefaultKeys(api.feature.on_key === undefined || api.feature.on_key_defaults); updateDrawer(); // api augmentation with local functions api.device.export = deviceExport; let anim = ui.anim = {}; 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: $('app-help'), don8: $('app-don8'), mesh: $('app-mesh'), export: $('app-export') }, dev: { header: $('dev-header'), search: $('dev-search'), filter: $('dev-filter') }, mesh: { name: $('mesh-name'), points: $('mesh-points'), faces: $('mesh-faces'), }, stats: { fps: $('fps'), rms: $('rms'), div: $('stats'), rnfo: $('rnfo'), }, load: $('load-file'), speeds: $('speeds'), speedbar: $('speedbar'), context: $('context-menu'), ltsetup: $('lt-setup'), ltfile: $('lt-file'), ltview: $('lt-view'), ltact: $('lt-start'), edit: $('lt-tools'), nozzle: $('menu-nozzle'), modal: $('modal'), modalBox: $('modal-box'), modals: { help: $('mod-help'), setup: $('mod-setup'), prefs: $('mod-prefs'), files: $('mod-files'), saves: $('mod-saves'), tools: $('mod-tools'), xany: $('mod-x-any'), xsla: $('mod-x-sla'), xlaser: $('mod-x-laser'), local: $('mod-local'), don8: $('mod-don8'), any: $('mod-any'), }, catalogBody: $('catalogBody'), catalogList: $('catalogList'), devices: $('devices'), deviceAdd: $('device-add'), deviceDelete: $('device-del'), deviceRename: $('device-ren'), deviceExport: $('device-exp'), deviceSave: $('device-save'), toolsSave: $('tools-save'), toolsClose: $('tools-close'), toolsImport: $('tools-import'), toolsExport: $('tools-export'), toolSelect: $('tool-select'), toolAdd: $('tool-add'), toolCopy: $('tool-dup'), 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'), /** CAM Animation Bar */ _____: { _____: newDiv({ addto: $('layer-animate'), set:true }), row: newRow([ anim.replay = newButton(null,"anim.replay",{icon:'',title:"restart"}), anim.play = newButton(null,"anim.play",{icon:'',title:"play"}), anim.pause = newButton(null,"anim.pause",{icon:'',title:"pause"}), anim.step = newButton(null,"anim.step",{icon:'',title:"single step"}), anim.speed = newButton(null,"anim.fast",{icon:'',title:"toggle speed"}), anim.labspd = newValue(3, {class:"center padleft"}), anim.labx = newLabel("X", {class:"padleft"}), anim.valx = newValue(7, {class:"center"}), anim.laby = newLabel("Y", {class:"padleft"}), anim.valy = newValue(7, {class:"center"}), anim.labz = newLabel("Z", {class:"padleft"}), anim.valz = newValue(6, {class:"center"}), anim.laba = newLabel("A", {class:"padleft hide"}), anim.vala = newValue(6, {class:"center hide"}), anim.labpro = newLabel("%", {class:"padleft"}), anim.progress = newValue(5, {class:"center"}), anim.trans = newButton(null,"anim.trans",{icon:'',title:"transparency",class:"padleft"}), anim.model = newButton(null,"anim.model",{icon:'',title:"show model"}), anim.shade = newButton(null,"anim.stock",{icon:'',title:"stock box"}), ]) }, /** Device Browser / Editor */ _____: newDiv({ class: "f-col t-body t-inset", addto: $('dev-config'), set:true }), device: newGroup(LANG.dv_gr_dev, null, {group:"ddev", inline, class:"noshow"}), _____: newGroup("workspace", null, {group:"dext", inline}), bedWidth: newInput('X (width)', {title:LANG.dv_bedw_l, convert:toFloat, size:6, units, round:2, action:updateDeviceSize}), bedDepth: newInput('Y (depth)', {title:LANG.dv_bedw_l, convert:toFloat, size:6, units, round:2, action:updateDeviceSize}), maxHeight: newInput('Z (height)', {title:LANG.dv_bedw_l, convert:toFloat, size:6, units, round:2, action:updateDeviceSize}), resolutionX: newInput(LANG.dv_rezx_s, {title:LANG.dv_rezx_l, convert:toInt, size:6, modes:SLA}), resolutionY: newInput(LANG.dv_rezy_s, {title:LANG.dv_rezy_l, convert:toInt, size:6, modes:SLA}), _____: newDiv({ class: "f-col t-body t-inset", addto: $('dev-config'), set:true, modes:NO_WEDM }), _____: newGroup("firmware", null, {group:"dext", inline, modes:NO_WEDM}), fwRetract: newBoolean(LANG.dv_retr_s, onBooleanClick, {title:LANG.dv_retr_l, modes:FDM}), deviceOrigin: newBoolean(LANG.dv_orgc_s, onBooleanClick, {title:LANG.dv_orgc_l, modes:FDM_LZN, show:() => !ui.deviceRound.checked}), deviceRound: newBoolean(LANG.dv_bedc_s, onBooleanClick, {title:LANG.dv_bedc_l, modes:FDM, trigger, show:isNotBelt}), deviceBelt: newBoolean(LANG.dv_belt_s, onBooleanClick, {title:LANG.dv_belt_l, modes:FDM, trigger, show:() => !ui.deviceRound.checked}), separator: newBlank({class:"pop-sep", modes:FDM, driven}), spindleMax: newInput(LANG.dv_spmx_s, {title:LANG.dv_spmx_l, convert:toInt, size:5, modes:CAM, trigger}), deviceZMax: newInput(LANG.dv_zmax_s, {title:LANG.dv_zmax_l, convert:toInt, size:5, modes:FDM}), gcodeTime: newInput(LANG.dv_time_s, {title:LANG.dv_time_l, convert:toFloat, size:5, modes:FDM}), _____: newDiv({ class: "f-col t-body t-inset", addto: $('dev-config'), set:true, modes:FDM }), extruder: newGroup(LANG.dv_gr_ext, null, {group:"dext", inline}), extFilament: newInput(LANG.dv_fila_s, {title:LANG.dv_fila_l, convert:toFloat, modes:FDM}), extNozzle: newInput(LANG.dv_nozl_s, {title:LANG.dv_nozl_l, convert:toFloat, modes:FDM}), extOffsetX: newInput(LANG.dv_exox_s, {title:LANG.dv_exox_l, convert:toFloat, modes:FDM}), extOffsetY: newInput(LANG.dv_exoy_s, {title:LANG.dv_exoy_l, convert:toFloat, modes:FDM}), extPad: newBlank({class:"grow", modes:FDM}), separator: newBlank({class:"pop-sep", modes:FDM, driven}), extActions: newRow([ ui.extPrev = newButton(undefined, undefined, {icon:''}), ui.extAdd = newButton(undefined, undefined, {icon:''}), ui.extDel = newButton(undefined, undefined, {icon:''}), ui.extNext = newButton(undefined, undefined, {icon:''}) ], {class:"dev-buttons ext-buttons var-row", modes:FDM}), _____: newDiv({ class: "f-col t-body t-inset", addto: $('dev-config'), set:true, modes:CAM_LZR }), _____: newGroup(LANG.dv_gr_out, null, {group:"dgco", inline}), gcodeStrip: newBoolean(LANG.dv_strc_s, onBooleanClick, {title:LANG.dv_strc_l, modes:CAM}), gcodeSpace: newBoolean(LANG.dv_tksp_s, onBooleanClick, {title:LANG.dv_tksp_l, modes:CAM_LZR}), laserMaxPower: newInput(LANG.ou_maxp_s, {title:LANG.ou_maxp_l, modes:LASER, size:7, text:true}), useLaser: newBoolean(LANG.dv_lazr_s, onBooleanClick, {title:LANG.dv_lazr_l, modes:CAM}), gcodeFExt: newInput(LANG.dv_fext_s, {title:LANG.dv_fext_l, modes:CAM_LZR, size:7, text:true}), gcodeEd: newGroup(LANG.dv_gr_gco, $('dg'), {group:"dgcp", inline, modes:GCODE}), gcodeMacros: newRow([ (ui.gcodePre = newGCode(LANG.dv_head_s, {title:LANG.dv_head_l, modes:GCODE, area:gcode})).button, (ui.gcodePost = newGCode(LANG.dv_foot_s, {title:LANG.dv_foot_l, modes:GCODE, area:gcode})).button, (ui.gcodeLayer = newGCode(LANG.dv_layr_s, {title:LANG.dv_layr_l, modes:FDM, area:gcode})).button, (ui.gcodeTrack = newGCode(LANG.dv_prog_s, {title:LANG.dv_prog_l, modes:FDM, area:gcode})).button, (ui.gcodeFan = newGCode(LANG.dv_fanp_s, {title:LANG.dv_fanp_l, modes:FDM, area:gcode})).button, (ui.gcodeFeature = newGCode(LANG.dv_feat_s, {title:LANG.dv_feat_l, modes:FDM, area:gcode})).button, (ui.gcodeLaserOn = newGCode(LANG.dv_lzon_s, {title:LANG.dv_lzon_l, modes:LASER, area:gcode})).button, (ui.gcodeLaserOff = newGCode(LANG.dv_lzof_s, {title:LANG.dv_lzof_l, modes:LASER, area:gcode})).button, (ui.gcodeWaterOn = newGCode(LANG.dv_waon_s, {title:LANG.dv_waon_l, modes:WJET, area:gcode})).button, (ui.gcodeWaterOff = newGCode(LANG.dv_waof_s, {title:LANG.dv_waof_l, modes:WJET, area:gcode})).button, (ui.gcodeKnifeDn = newGCode(LANG.dv_dkon_s, {title:LANG.dv_dkon_l, modes:DRAG, area:gcode})).button, (ui.gcodeKnifeUp = newGCode(LANG.dv_dkof_s, {title:LANG.dv_dkof_l, modes:DRAG, area:gcode})).button, (ui.gcodeChange = newGCode(LANG.dv_tool_s, {title:LANG.dv_tool_l, modes:FDM_CAM, area:gcode})).button, (ui.gcodeDwell = newGCode(LANG.dv_dwll_s, {title:LANG.dv_dwll_l, modes:CAM, area:gcode})).button, (ui.gcodeSpindle = newGCode(LANG.dv_sspd_s, {title:LANG.dv_sspd_l, modes:CAM, area:gcode, show:() => ui.spindleMax.value > 0})).button ], {class:"ext-buttons f-row gcode-macros"}), /** Preferences Menu */ _____: newGroup(LANG.op_menu, $('prefs-gen1'), {inline}), antiAlias: newBoolean(LANG.op_anta_s, booleanSave, {title:LANG.op_anta_l}), reverseZoom: newBoolean(LANG.op_invr_s, booleanSave, {title:LANG.op_invr_l}), ortho: newBoolean(LANG.op_orth_s, booleanSave, {title:LANG.op_orth_l}), dark: newBoolean(LANG.op_dark_s, booleanSave, {title:LANG.op_dark_l}), drawer: newBoolean('slide out', booleanSave, {title:'slide out settings drawer'}), scrolls: newBoolean('scrollbars', booleanSave, {title:'show scrollbars'}), devel: newBoolean(LANG.op_devl_s, booleanSave, {title:LANG.op_devl_l}), _____: newGroup(LANG.op_disp, $('prefs-gen2'), {inline}), showOrigin: newBoolean(LANG.op_shor_s, booleanSave, {title:LANG.op_shor_l}), showRulers: newBoolean(LANG.op_shru_s, booleanSave, {title:LANG.op_shru_l}), showSpeeds: newBoolean(LANG.op_sped_s, speedSave, {title:LANG.op_sped_l}), shiny: newBoolean(LANG.op_shny_s, booleanSave, {title:LANG.op_shny_l, modes:FDM}), lineType: newSelect(LANG.op_line_s, {title: LANG.op_line_l, action: lineTypeSave, modes:FDM}, "linetype"), animesh: newSelect(LANG.op_anim_s, {title: LANG.op_anim_l, action: aniMeshSave, modes:CAM}, "animesh"), units: newSelect(LANG.op_unit_s, {title: LANG.op_unit_l, action: unitsSave, modes:CAM}, "units"), edgeangle: newInput(LANG.op_spoa_s, {title:LANG.op_spoa_l, convert:toFloat, size:3}), _____: newGroup(LANG.lo_menu, $('prefs-lay'), {inline}), autoSave: newBoolean(LANG.op_save_s, booleanSave, {title:LANG.op_save_l}), autoLayout: newBoolean(LANG.op_auto_s, booleanSave, {title:LANG.op_auto_l}), freeLayout: newBoolean(LANG.op_free_s, booleanSave, {title:LANG.op_free_l}), spaceRandoX: newBoolean(LANG.op_spcx_s, booleanSave, {title:LANG.op_spcx_l, show:isBelt}), spaceLayout: newInput(LANG.op_spcr_s, {title:LANG.op_spcr_l, convert:toFloat, size:3, units}), _____: newGroup(LANG.xp_menu, $('prefs-xpo'), {inline: true}), exportLocal: newBoolean(`Grid:Local`, booleanSave, {title:LANG.op_exgl_l}), exportGhost: newBoolean(`Grid:Host`, booleanSave, {title:LANG.op_exgh_l}), exportOcto: newBoolean(`OctoPrint`, booleanSave, {title:LANG.op_exop_l}), exportThumb: newBoolean(`Thumbnail`, booleanSave, {modes:FDM}), exportPreview: newBoolean(`Code Preview`, booleanSave), _____: newGroup(LANG.pt_menu, $('prefs-prt'), {inline}), detail: newSelect(LANG.pt_qual_s, {title: LANG.pt_qual_l, action: detailSave}, "detail"), healMesh: newBoolean(LANG.pt_heal_s, booleanSave, {title: LANG.pt_heal_l}), threaded: newBoolean(LANG.pt_thrd_s, booleanSave, {title: LANG.pt_thrd_l, modes:THREED}), assembly: newBoolean(LANG.pt_assy_s, booleanSave, {title: LANG.pt_assy_l, modes:THREED}), prefadd: uc.checkpoint($('prefs-add')), /** FDM Settings */ _____: newGroup(LANG.sl_menu, $('fdm-layers'), { modes:FDM, driven, hideable, separator, group:"fdm-layers" }), sliceHeight: newInput(LANG.sl_lahi_s, { title:LANG.sl_lahi_l, convert:toFloat }), sliceMinHeight: newInput(LANG.ad_minl_s, { title:LANG.ad_minl_l, convert:toFloat, bound:bound(0,3.0), show:() => ui.sliceAdaptive.checked }), sliceTopLayers: newInput(LANG.sl_ltop_s, { title:LANG.sl_ltop_l, convert:toInt }), sliceBottomLayers: newInput(LANG.sl_lbot_s, { title:LANG.sl_lbot_l, convert:toInt }), separator: newBlank({ class:"set-sep", driven }), sliceAdaptive: newBoolean(LANG.ad_adap_s, onBooleanClick, { title: LANG.ad_adap_l }), _____: newGroup(LANG.sw_menu, $('fdm-walls'), { modes:FDM, driven, hideable, separator, group:"fdm-walls" }), sliceShells: newInput(LANG.sl_shel_s, { title:LANG.sl_shel_l, convert:toFloat }), sliceLineWidth: newInput(LANG.sl_line_s, { title:LANG.sl_line_l, convert:toFloat, bound:bound(0,5) }), separator: newBlank({ class:"set-sep", driven }), sliceShellOrder: newSelect(LANG.sl_ordr_s, { title:LANG.sl_ordr_l}, "shell"), sliceDetectThin: newSelect(LANG.ad_thin_s, { title: LANG.ad_thin_l, action: thinWallSave }, "thin"), outputAlternating: newBoolean(LANG.ad_altr_s, onBooleanClick, {title:LANG.ad_altr_l}), sliceZInterleave: newBoolean(LANG.ad_zint_s, onBooleanClick, {title:LANG.ad_zint_l, show:zIntShow}), _____: newGroup(LANG.fi_menu, $('fdm-fill'), { modes:FDM, driven, hideable, separator, group:"fdm-fill" }), sliceFillType: newSelect(LANG.fi_type, {trigger}, "infill"), sliceFillSparse: newInput(LANG.fi_pcnt_s, {title:LANG.fi_pcnt_l, convert:toFloat, bound:bound(0.0,1.0), show:hasInfill}), sliceFillRepeat: newInput(LANG.fi_rept_s, {title:LANG.fi_rept_l, convert:toInt, bound:bound(1,10), show:fillIsLinear}), sliceFillOverlap: newInput(LANG.fi_over_s, {title:LANG.fi_over_l, convert:toFloat, bound:bound(0.0,2.0), show:hasInfill}), separator: newBlank({ class:"set-sep", driven }), sliceFillRate: newInput(LANG.ou_feed_s, {title:LANG.ou_feed_l, convert:toInt, bound:bound(0,500)}), sliceSolidRate: newInput(LANG.ou_fini_s, {title:LANG.ou_fini_l, convert:toInt, bound:bound(0,500)}), separator: newBlank({ class:"set-sep", driven }), sliceFillGrow: newInput(LANG.fi_grow_s, {title:LANG.fi_grow_l, convert:toFloat}), sliceFillAngle: newInput(LANG.fi_angl_s, {title:LANG.fi_angl_l, convert:toFloat}), _____: newGroup(LANG.fh_menu, $('fdm-heat'), { modes:FDM, driven, hideable, separator, group:"fdm-heat" }), outputTemp: newInput(LANG.ou_nozl_s, {title:LANG.ou_nozl_l, convert:toInt}), outputBedTemp: newInput(LANG.ou_bedd_s, {title:LANG.ou_bedd_l, convert:toInt}), _____: newGroup(LANG.fc_menu, $('fdm-cool'), { modes:FDM, driven, hideable, separator, group:"fdm-cool" }), outputFanLayer: newInput(LANG.ou_fanl_s, { title:LANG.ou_fanl_l, convert:toInt, bound:bound(0,255) }), outputFanSpeed: newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:toInt, bound:bound(0,255)}), _____: newGroup(LANG.sp_menu, $('fdm-support'), { modes:FDM, driven, hideable, separator, group:"fdm-supp" }), sliceSupportNozzle: newSelect(LANG.sp_nozl_s, {title:LANG.sp_nozl_l, show:isMultiHead}, "extruders"), sliceSupportDensity: newInput(LANG.sp_dens_s, {title:LANG.sp_dens_l, convert:toFloat, bound:bound(0.0,1.0)}), sliceSupportSize: newInput(LANG.sp_size_s, {title:LANG.sp_size_l, convert:toFloat, bound:bound(1.0,200.0)}), sliceSupportOffset: newInput(LANG.sp_offs_s, {title:LANG.sp_offs_l, convert:toFloat, bound:bound(0.0,200.0)}), sliceSupportGap: newInput(LANG.sp_gaps_s, {title:LANG.sp_gaps_l, convert:toInt, bound:bound(0,5)}), sliceSupportArea: newInput(LANG.sp_area_s, {title:LANG.sp_area_l, convert:toFloat, bound:bound(0.0,200.0)}), sliceSupportExtra: newInput(LANG.sp_xpnd_s, {title:LANG.sp_xpnd_l, convert:toFloat, bound:bound(0.0,10.0)}), sliceSupportGrow: newInput(LANG.sp_grow_s, {title:LANG.sp_grow_l, convert:toFloat, bound:bound(0.0,10.0)}), sliceSupportAngle: newInput(LANG.sp_angl_s, {title:LANG.sp_angl_l, convert:toFloat, bound:bound(0.0,90.0)}), sliceSupportSpan: newInput(LANG.sp_span_s, {title:LANG.sp_span_l, convert:toFloat, bound:bound(0.0,200.0), show:() => ui.sliceSupportEnable.checked }), separator: newBlank({ class:"set-sep", driven }), sliceSupportEnable: newBoolean(LANG.sp_auto_s, onBooleanClick, {title:LANG.sp_auto_l, show:isNotBelt}), sliceSupportOutline: newBoolean(LANG.sp_outl_s, onBooleanClick, {title:LANG.sp_outl_l}), separator: newBlank({ class:"set-sep", driven }), sliceSupportGen: newRow([ ui.ssaGen = newButton(LANG.sp_detect, onButtonClick, {class: "f-col grow a-center"}) ], { modes: FDM, class: "ext-buttons f-row grow" }), separator: newBlank({ class:"set-sep", driven }), sliceSupportManual: newRow([ (ui.ssmAdd = newButton(undefined, onButtonClick, {icon:''})), (ui.ssmDun = newButton(undefined, onButtonClick, {icon:''})), (ui.ssmClr = newButton(undefined, onButtonClick, {icon:''})) ], {class:"ext-buttons f-row"}), _____: newGroup(LANG.fl_menu, $('fdm-base'), { modes:FDM, driven, hideable, separator, group:"fdm-base" }), firstSliceHeight: newInput(LANG.fl_lahi_s, {title:LANG.fl_lahi_l, convert:toFloat, show:isNotBelt}), firstLayerNozzleTemp:newInput(LANG.fl_nozl_s, {title:LANG.fl_nozl_l, convert:toInt, show:isNotBelt}), firstLayerBedTemp: newInput(LANG.fl_bedd_s, {title:LANG.fl_bedd_l, convert:toInt, show:isNotBelt}), separator: newBlank({ class:"set-sep", driven }), firstLayerFanSpeed: newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:toInt, bound:bound(0,255), show:isBelt}), firstLayerYOffset: newInput(LANG.fl_zoff_s, {title:LANG.fl_zoff_l, convert:toFloat, show:isBelt}), firstLayerFlatten: newInput(LANG.fl_flat_s, {title:LANG.fl_flat_l, convert:toFloat, show:isBelt}), firstLayerRate: newInput(LANG.fl_rate_s, {title:LANG.fl_rate_l, convert:toFloat}), firstLayerFillRate: newInput(LANG.fl_frat_s, {title:LANG.fl_frat_l, convert:toFloat, show:isNotBelt}), separator: newBlank({ class:"set-sep", driven, show:isNotBelt }), firstLayerLineMult: newInput(LANG.fl_sfac_s, {title:LANG.fl_sfac_l, convert:toFloat, bound:bound(0.5,2), show:isNotBelt}), firstLayerPrintMult: newInput(LANG.fl_mult_s, {title:LANG.fl_mult_l, convert:toFloat}), separator: newBlank({ class:"set-sep", driven, show:isBelt }), firstLayerBrim: newInput(LANG.fl_brim_s, {title:LANG.fl_brim_l, convert:toInt, show:isBelt}), firstLayerBrimIn: newInput(LANG.fl_brin_s, {title:LANG.fl_brin_l, convert:toInt, show:isBelt}), firstLayerBrimTrig: newInput(LANG.fl_brmn_s, {title:LANG.fl_brmn_l, convert:toInt, show:isBelt}), firstLayerBrimGap: newInput(LANG.fl_brgp_s, {title:LANG.fl_brgp_l, convert:toFloat, show:isBelt}), separator: newBlank({ class:"set-sep", driven, show:isBelt }), firstLayerBeltLead: newInput(LANG.fl_bled_s, {title:LANG.fl_bled_l, convert:toFloat, show:isBelt}), firstLayerBeltBump: newInput(LANG.fl_blmp_s, {title:LANG.fl_blmp_l, convert:toFloat, bound:bound(0, 10), show:isBelt}), separator: newBlank({ class:"set-sep", driven, show:isNotBelt }), outputBrimCount: newInput(LANG.fl_skrt_s, {title:LANG.fl_skrt_l, convert:toInt, show:isNotBelt}), outputBrimOffset: newInput(LANG.fl_skro_s, {title:LANG.fl_skro_l, convert:toFloat, show:isNotBelt}), outputRaftSpacing: newInput(LANG.fr_spac_s, {title:LANG.fr_spac_l, convert:toFloat, bound:bound(0.0,3.0), show: () => ui.outputRaft.checked && isNotBelt() }), separator: newBlank({ class:"set-sep", driven, show:isNotBelt }), outputRaft: newBoolean(LANG.fr_nabl_s, onBooleanClick, {title:LANG.fr_nabl_l, trigger, show:() => isNotBelt()}), outputDraftShield: newBoolean(LANG.fr_draf_s, onBooleanClick, {title:LANG.fr_draf_l, trigger, show:() => isNotBelt()}), _____: newGroup(LANG.ou_menu, $('fdm-output'), { modes:FDM, driven, hideable, separator, group:"fdm-out" }), outputFeedrate: newInput(LANG.ou_feed_s, {title:LANG.ou_feed_l, convert:toInt}), outputFinishrate: newInput(LANG.ou_fini_s, {title:LANG.ou_fini_l, convert:toInt}), outputSeekrate: newInput(LANG.ou_move_s, {title:LANG.ou_move_l, convert:toInt}), separator: newBlank({ class:"set-sep", driven }), outputShellMult: newInput(LANG.ou_shml_s, {title:LANG.ou_exml_l, convert:toFloat, bound:bound(0.0,2.0)}), outputFillMult: newInput(LANG.ou_flml_s, {title:LANG.ou_exml_l, convert:toFloat, bound:bound(0.0,2.0)}), outputSparseMult: newInput(LANG.ou_spml_s, {title:LANG.ou_exml_l, convert:toFloat, bound:bound(0.0,2.0)}), separator: newBlank({ class:"set-sep", driven }), outputRetractDist: newInput(LANG.ad_rdst_s, {title:LANG.ad_rdst_l, convert:toFloat}), outputRetractSpeed: newInput(LANG.ad_rrat_s, {title:LANG.ad_rrat_l, convert:toInt}), outputRetractWipe: newInput(LANG.ad_wpln_s, {title:LANG.ad_wpln_l, convert:toFloat, bound:bound(0.0,10)}), separator: newBlank({ class:"set-sep", driven }), sliceLayerStart: newSelect(LANG.sl_strt_s, {title:LANG.sl_strt_l}, "start"), outputLayerRetract: newBoolean(LANG.ad_lret_s, onBooleanClick, {title:LANG.ad_lret_l}), outputAvoidGaps: newBoolean(LANG.ad_agap_s, onBooleanClick, {title:LANG.ad_agap_l}), separator: newBlank({ class:"set-sep", driven, show:isBelt }), outputBeltFirst: newBoolean(LANG.ad_lbir_s, onBooleanClick, {title:LANG.ad_lbir_l, show:isBelt}), _____: newGroup(LANG.ad_menu, $('fdm-expert'), { modes:FDM, driven, hideable, separator, group:"fdm-xprt" }), sliceAngle: newInput(LANG.sl_angl_s, {title:LANG.sl_angl_l, convert:toFloat, show:isBelt}), outputRetractDwell: newInput(LANG.ad_rdwl_s, {title:LANG.ad_rdwl_l, convert:toInt}), sliceSolidMinArea: newInput(LANG.ad_msol_s, {title:LANG.ad_msol_l, convert:toFloat}), outputMinSpeed: newInput(LANG.ad_mins_s, {title:LANG.ad_mins_l, convert:toFloat, bound:bound(1,200)}), outputShortPoly: newInput(LANG.ad_spol_s, {title:LANG.ad_spol_l, convert:toFloat, bound:bound(0,10000)}), outputCoastDist: newInput(LANG.ad_scst_s, {title:LANG.ad_scst_l, convert:toFloat, bound:bound(0.0,10)}), zHopDistance: newInput(LANG.ad_zhop_s, {title:LANG.ad_zhop_l, convert:toFloat, bound:bound(0,3.0)}), arcTolerance: newInput(LANG.ad_arct_s, {title:LANG.ad_arct_l, convert:toFloat, bound:bound(0,1.0), show:() => { return isNotBelt() }}), antiBacklash: newInput(LANG.ad_abkl_s, {title:LANG.ad_abkl_l, convert:toInt, bound:bound(0,3)}), outputLoops: newInput(LANG.ag_loop_s, {title:LANG.ag_loop_l, convert:toInt, bound:bound(-1,1000), show:isBelt}), outputPurgeTower: newInput(LANG.ad_purg_s, {title:LANG.ad_purg_l, convert:toInt, bound:bound(0,1000)}), fdmRanges: $('fdm-ranges'), /** CAM Settings */ _____: newGroup(LANG.ct_menu, $('cam-tabs'), { modes:CAM, marker:true, driven, separator }), camTabsWidth: newInput(LANG.ct_wdth_s, {title:LANG.ct_wdth_l, convert:toFloat, bound:bound(0.005,100), units}), camTabsHeight: newInput(LANG.ct_hght_s, {title:LANG.ct_hght_l, convert:toFloat, bound:bound(0.005,100), units}), camTabsDepth: newInput(LANG.ct_dpth_s, {title:LANG.ct_dpth_l, convert:toFloat, bound:bound(0.005,100), units}), separator: newBlank({ class:"set-sep", driven }), camTabsMidline: newBoolean(LANG.ct_midl_s, onBooleanClick, {title:LANG.ct_midl_l}), separator: newBlank({ class:"set-sep", driven }), camTabsManual: newRow([ (ui.tabAdd = newButton(undefined, onButtonClick, {icon:''})), (ui.tabDun = newButton(undefined, onButtonClick, {icon:''})), (ui.tabClr = newButton(undefined, onButtonClick, {icon:''})) ], {class:"ext-buttons f-row"}), _____: newGroup(LANG.cs_menu, $('cam-stock'), { modes:CAM, driven, separator }), camStockX: newInput(LANG.cs_wdth_s, {title:LANG.cs_wdth_l, convert:toFloat, bound:bound(0,9999), units}), camStockY: newInput(LANG.cs_dpth_s, {title:LANG.cs_dpth_l, convert:toFloat, bound:bound(0,9999), units}), camStockZ: newInput(LANG.cs_hght_s, {title:LANG.cs_hght_l, convert:toFloat, bound:bound(0,9999), units}), separator: newBlank({ class:"set-sep", driven }), camStockOffset: newBoolean(LANG.cs_offs_s, onBooleanClick, {title:LANG.cs_offs_l}), camStockClipTo: newBoolean(LANG.cs_clip_s, onBooleanClick, {title:LANG.cs_clip_l}), camStockIndexed: newBoolean(LANG.cs_indx_s, onBooleanClick, {title:LANG.cs_indx_l}), camStockIndexGrid: newBoolean(LANG.cs_ishg_s, onBooleanClick, {title:LANG.cs_ishg_l, show:() => ui.camStockIndexed.checked}), _____: newGroup(LANG.cc_menu, $('cam-limits'), { modes:CAM, driven, separator }), camZAnchor: newSelect(LANG.ou_zanc_s, {title: LANG.ou_zanc_l, action:zAnchorSave, show:() => !ui.camStockIndexed.checked}, "zanchor"), camZOffset: newInput(LANG.ou_ztof_s, {title:LANG.ou_ztof_l, convert:toFloat, units}), camZTop: newInput(LANG.ou_ztop_s, {title:LANG.ou_ztop_l, convert:toFloat, units, trigger}), camZBottom: newInput(LANG.ou_zbot_s, {title:LANG.ou_zbot_l, convert:toFloat, units, trigger}), camZThru: newInput(LANG.ou_ztru_s, {title:LANG.ou_ztru_l, convert:toFloat, bound:bound(0.0,100), units }), camZClearance: newInput(LANG.ou_zclr_s, {title:LANG.ou_zclr_l, convert:toFloat, bound:bound(0.01,100), units }), camFastFeedZ: newInput(LANG.cc_rzpd_s, {title:LANG.cc_rzpd_l, convert:toFloat, units}), camFastFeed: newInput(LANG.cc_rapd_s, {title:LANG.cc_rapd_l, convert:toFloat, units}), _____: newGroup(LANG.ou_menu, $('cam-output'), { modes:CAM, driven, separator, group:"cam-output" }), camConventional: newBoolean(LANG.ou_conv_s, onBooleanClick, {title:LANG.ou_conv_l}), camEaseDown: newBoolean(LANG.cr_ease_s, onBooleanClick, {title:LANG.cr_ease_l}), camDepthFirst: newBoolean(LANG.ou_depf_s, onBooleanClick, {title:LANG.ou_depf_l}), camToolInit: newBoolean(LANG.ou_toin_s, onBooleanClick, {title:LANG.ou_toin_l}), separator: newBlank({ class:"set-sep", driven }), camFirstZMax: newBoolean(LANG.ou_z1st_s, onBooleanClick, {title:LANG.ou_z1st_l}), camForceZMax: newBoolean(LANG.ou_forz_s, onBooleanClick, {title:LANG.ou_forz_l}), separator: newBlank({ class:"set-sep", driven }), camEaseAngle: newInput(LANG.ou_eang_s, {title:LANG.ou_eang_l, convert:toFloat, bound:bound(0.1,85), show:() => ui.camEaseDown.checked}), camFullEngage: newInput(LANG.ou_feng_s, {title:LANG.ou_feng_l, convert:toFloat, bound:bound(0.1,1.0)}), _____: newGroup(LANG.co_menu, $('cam-origin'), { modes:CAM, driven, separator }), camOriginTop: newBoolean(LANG.or_topp_s, onBooleanClick, {title:LANG.or_topp_l}), camOriginCenter: newBoolean(LANG.or_cntr_s, onBooleanClick, {title:LANG.or_cntr_l}), separator: newBlank({ class:"set-sep", driven }), camOriginOffX: newInput(LANG.co_offx_s, {title:LANG.co_offx_l, convert:toFloat, units}), camOriginOffY: newInput(LANG.co_offy_s, {title:LANG.co_offy_l, convert:toFloat, units}), camOriginOffZ: newInput(LANG.co_offz_s, {title:LANG.co_offz_l, convert:toFloat, units}), _____: newGroup(LANG.op_xprt_s, $('cam-expert'), { group:"cam_expert", modes:CAM, marker: false, driven, separator }), camExpertFast: newBoolean(LANG.cx_fast_s, onBooleanClick, {title:LANG.cx_fast_l, show: () => !ui.camTrueShadow.checked }), camTrueShadow: newBoolean(LANG.cx_true_s, onBooleanClick, {title:LANG.cx_true_l, show: () => !ui.camExpertFast.checked }), separator: newBlank({ class:"set-sep", driven }), camArcTolerance: newInput(LANG.cx_arct_s, {title:LANG.cx_arct_l, convert:toFloat, units, bound:bound(0,100)}), camArcResolution: newInput(LANG.cx_arcr_s, {title:LANG.cx_arcr_l, convert:toFloat, bound:bound(0,180), show:() => ui.camArcTolerance.value > 0}), /** LASER/DRAG/WJET/WEDM cut tool Settings */ _____: newGroup(LANG.sl_menu, $('lzr-slice'), { modes:TWOD, driven, separator }), ctSliceKerf: newInput(LANG.ls_offs_s, {title:LANG.ls_offs_l, convert:toFloat}), ctSliceHeight: newInput(LANG.ls_lahi_s, {title:LANG.ls_lahi_l, convert:toFloat, trigger}), ctSliceHeightMin: newInput(LANG.ls_lahm_s, {title:LANG.ls_lahm_l, convert:toFloat, show:() => ui.ctSliceHeight.value == 0 && !ui.ctSliceSingle.checked }), separator: newBlank({ class:"set-sep", driven }), ctSliceSingle: newBoolean(LANG.ls_sngl_s, onBooleanClick, {title:LANG.ls_sngl_l}), ctOmitInner: newBoolean(LANG.we_omit_s, onBooleanClick, {title:LANG.we_omit_l, modes:WEDM}), _____: newGroup('surfaces', $('lzr-surface'), { modes:[-1], driven, separator }), ctSurfaces: newRow([ (ui.faceAdd = newButton(undefined, onButtonClick, {icon:''})), (ui.faceDun = newButton(undefined, onButtonClick, {icon:''})), (ui.faceClr = newButton(undefined, onButtonClick, {icon:''})) ], {class:"ext-buttons f-row", modes:WEDM}), _____: newGroup(LANG.dk_menu, $('lzr-knife'), { modes:DRAG, marker:true, driven, separator }), ctOutKnifeDepth: newInput(LANG.dk_dpth_s, { title:LANG.dk_dpth_l, convert:toFloat, bound:bound(0.0,5.0) }), ctOutKnifePasses: newInput(LANG.dk_pass_s, { title:LANG.dk_pass_l, convert:toInt, bound:bound(0,5) }), ctOutKnifeTip: newInput(LANG.dk_offs_s, { title:LANG.dk_offs_l, convert:toFloat, bound:bound(0.0,10.0) }), _____: newGroup(LANG.lo_menu, $('lzr-layout'), { modes:TWOD, driven, separator }), ctOutTileSpacing: newInput(LANG.ou_spac_s, { title:LANG.ou_spac_l, convert:toInt }), ctOutMerged: newBoolean(LANG.ou_mrgd_s, onBooleanClick, {title:LANG.ou_mrgd_l, modes:TWONED, show:() => !ui.ctOutStack.checked }), ctOutGroup: newBoolean(LANG.ou_grpd_s, onBooleanClick, {title:LANG.ou_grpd_l, show:() => !(ui.ctOutMark.checked || ui.ctOutStack.checked) }), _____: newGroup(LANG.ou_menu, $('lzr-output'), { modes:TWOD, driven, separator, group:"lzr-output" }), ctOutPower: newInput(LANG.ou_powr_s, {title:LANG.ou_powr_l, convert:toInt, bound:bound(1,100), modes:TWONED }), ctOutSpeed: newInput(LANG.ou_sped_s, {title:LANG.ou_sped_l, convert:toInt }), ctAdaptive: newBoolean('adaptive speed', onBooleanClick, {modes:WEDM, title:'controller determines best cutting speed based on material feedback at runtime'}), separator: newBlank({ class:"set-sep", driven }), ctOriginBounds: newBoolean(LANG.or_bnds_s, onBooleanClick, { title:LANG.or_bnds_l, show:() => !ui.ctOriginCenter.checked }), ctOriginCenter: newBoolean(LANG.or_cntr_s, onBooleanClick, { title:LANG.or_cntr_l, show:() => !ui.ctOriginBounds.checked }), separator: newBlank({ class:"set-sep", driven, modes:WEDM, show:() => ui.ctOriginBounds.checked }), ctOriginOffX: newInput(LANG.or_offx_s, { title:LANG.or_offx_l, convert:toFloat, modes:WEDM, show:() => ui.ctOriginBounds.checked }), ctOriginOffY: newInput(LANG.or_offy_s, { title:LANG.or_offy_l, convert:toFloat, modes:WEDM, show:() => ui.ctOriginBounds.checked }), separator: newBlank({ class:"set-sep", driven, modes:TWONED }), ctOutZColor: newBoolean(LANG.ou_layo_s, onBooleanClick, { title:LANG.ou_layo_l, modes:TWONED, show:() => !ui.ctOutMerged.checked }), ctOutLayer: newBoolean(LANG.ou_layr_s, onBooleanClick, { title:LANG.ou_layr_l, modes:TWONED, show:() => !ui.ctOutStack.checked }), ctOutMark: newBoolean(LANG.ou_lays_s, onBooleanClick, { title:LANG.ou_lays_l, modes:TWONED, show:() => !ui.ctOutStack.checked }), separator: newBlank({ class:"set-sep", driven, modes:LASER }), ctOutInches: newBoolean(LANG.ou_inch_s, onBooleanClick, { title:LANG.ou_inch_l, modes:LASER }), ctOutStack: newBoolean(LANG.ou_stak_s, onBooleanClick, { title:LANG.ou_stak_l, modes:LASER }), ctOutShaper: newBoolean(LANG.ou_shap_s, onBooleanClick, { title:LANG.ou_shap_l, modes:LASER, show:() => ui.ctOutStack.checked }), /** SLA SETTINGS */ slaProc: newGroup(LANG.sa_menu, $('sla-slice'), { modes:SLA, group:"sla-slice", driven, separator }), slaSlice: newInput(LANG.sa_lahe_s, {title:LANG.sa_lahe_l, convert:toFloat}), slaShell: newInput(LANG.sa_shel_s, {title:LANG.sa_shel_l, convert:toFloat}), slaOpenTop: newBoolean(LANG.sa_otop_s, onBooleanClick, {title:LANG.sa_otop_l}), slaOpenBase: newBoolean(LANG.sa_obas_s, onBooleanClick, {title:LANG.sa_obas_l}), slaLayers: newGroup(LANG.sa_layr_m, $('sla-layers'), { modes:SLA, group:"sla-layers", driven, separator }), slaLayerOn: newInput(LANG.sa_lton_s, {title:LANG.sa_lton_l, convert:toFloat}), slaLayerOff: newInput(LANG.sa_ltof_s, {title:LANG.sa_ltof_l, convert:toFloat}), slaPeelDist: newInput(LANG.sa_pldi_s, {title:LANG.sa_pldi_l, convert:toFloat}), slaPeelLiftRate: newInput(LANG.sa_pllr_s, {title:LANG.sa_pllr_l, convert:toFloat}), slaPeelDropRate: newInput(LANG.sa_pldr_s, {title:LANG.sa_pldr_l, convert:toFloat}), slaBase: newGroup(LANG.sa_base_m, $('sla-base'), { modes:SLA, group:"sla-base", driven, separator }), slaBaseLayers: newInput(LANG.sa_balc_s, {title:LANG.sa_balc_l, convert:toInt}), slaBaseOn: newInput(LANG.sa_lton_s, {title:LANG.sa_bltn_l, convert:toFloat}), slaBaseOff: newInput(LANG.sa_ltof_s, {title:LANG.sa_bltf_l, convert:toFloat}), slaBasePeelDist: newInput(LANG.sa_pldi_s, {title:LANG.sa_pldi_l, convert:toFloat}), slaBasePeelLiftRate: newInput(LANG.sa_pllr_s, {title:LANG.sa_pllr_l, convert:toFloat}), slaFill: newGroup(LANG.sa_infl_m, $('sla-fill'), { modes:SLA, group:"sla-infill", driven, separator }), slaFillDensity: newInput(LANG.sa_ifdn_s, {title:LANG.sa_ifdn_l, convert:toFloat, bound:bound(0,1)}), slaFillLine: newInput(LANG.sa_iflw_s, {title:LANG.sa_iflw_l, convert:toFloat, bound:bound(0,5)}), slaSupport: newGroup(LANG.sa_supp_m, $('sla-support'), { modes:SLA, group:"sla-support", driven, separator }), slaSupportLayers: newInput(LANG.sa_slyr_s, {title:LANG.sa_slyr_l, convert:toInt, bound:bound(5,100)}), slaSupportGap: newInput(LANG.sa_slgp_s, {title:LANG.sa_slgp_l, convert:toInt, bound:bound(3,30)}), slaSupportDensity: newInput(LANG.sa_sldn_s, {title:LANG.sa_sldn_l, convert:toFloat, bound:bound(0.01,0.9)}), slaSupportSize: newInput(LANG.sa_slsz_s, {title:LANG.sa_slsz_l, convert:toFloat, bound:bound(0.1,1)}), slaSupportPoints: newInput(LANG.sa_slpt_s, {title:LANG.sa_slpt_l, convert:toInt, bound:bound(3,10)}), slaSupportEnable: newBoolean(LANG.enable, onBooleanClick, {title:LANG.sl_slen_l}), slaOutput: newGroup(LANG.sa_outp_m, $('sla-output'), { modes:SLA, driven, separator, group:"sla-output" }), slaFirstOffset: newInput(LANG.sa_opzo_s, {title:LANG.sa_opzo_l, convert:toFloat, bound:bound(0,1)}), slaAntiAlias: newSelect(LANG.sa_opaa_s, {title:LANG.sa_opaa_l}, "antialias"), layers: uc.setGroup($("layers")), settingsName: $('settingsName'), settingsSave: $('settingsSave'), }); // run client mode initializations right after UI is bound initFDM(); initCAM(); initLaser(); initSLA(); // override old style settings two-button menu ui.settingsSave.onclick = () => { settingsSave(undefined, ui.settingsName.value); }; function optSelected(sel) { let opt = sel.options[sel.selectedIndex]; return opt ? opt.value : undefined; } function hasInfill() { return optSelected(ui.sliceFillType) !== 'none' } function fillIsLinear() { return hasInfill() && optSelected(ui.sliceFillType) === 'linear'; } function spindleShow() { return settings().device.spindleMax > 0; } function zIntShow() { return settings().controller.devel; } // slider setup const mobile = space.info.mob; const slbar = mobile ? 80 : 30; const slbar2 = slbar * 2; const slider = ui.sliderRange; const drag = { }; if (mobile) { ui.slider.classList.add('slider-mobile'); ui.sliderLo.classList.add('slider-mobile'); ui.sliderHi.classList.add('slider-mobile'); // add css style for mobile devices DOC.body.classList.add('mobile'); } 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.ontouchstart = el.onmousedown = (ev) => { // el.classList.add('sli-drag-el'); tracker.style.display = 'block'; ev.stopPropagation(); let obj = (ev.touches ? ev.touches[0] : ev); drag.width = slider.clientWidth; drag.maxval = drag.width - slbar2; drag.start = obj.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.ontouchend = tracker.onmouseup = (ev) => { // el.classList.remove('sli-drag-el'); if (ev) { ev.stopPropagation(); ev.preventDefault(); } slider.onmousemove = undefined; tracker.style.display = 'none'; }; el.ontouchend = cancel_drag; el.ontouchmove = tracker.ontouchmove = tracker.onmousemove = (ev) => { ev.stopPropagation(); ev.preventDefault(); if (ev.buttons === 0) { return cancel_drag(); } if (delta) { let obj = (ev.touches ? ev.touches[0] : ev); delta(obj.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.load.onchange = function(event) { api.platform.load_files(event.target.files); ui.load.value = ''; // reset so you can re-import the same filee }; 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'); }; 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.event.addHandlers(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 ?? ui.scaleX.value) || 1, yv = parseFloat(ui.sizeY.was ?? ui.scaleY.value) || 1, zv = parseFloat(ui.sizeZ.was ?? ui.scaleZ.value) || 1, 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; } 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 ?? ui.scaleX.value) || 1, yv = parseFloat(ui.scaleY.was ?? ui.scaleY.value) || 1, zv = parseFloat(ui.scaleZ.was ?? ui.scaleY.value) || 1, 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); 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 val = parseFloat(e.target.value) || 0; e.target.value = val; } space.event.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, ui.toolTaperAngle, 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 = () => { 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; }; $('app-xpnd').onclick = () => { try { DOC.body.requestFullscreen(); } catch (e) { event.emit('resize'); moto.space.event.onResize(); } }; if (!DOC.body.requestFullscreen) { $('app-xpnd').style.display = 'none'; } 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; } api.platform.update_size(); function mouseOnHover(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'}); } function platformOnHover(int, event) { if (!api.feature.hover) return; if (int) api.event.emit('mouse.hover', {point: int, event, type: 'platform'}); } api.event.on("feature.hover", enable => { space.mouse.onHover(enable ? mouseOnHover : undefined); space.platform.onHover(enable ? platformOnHover : undefined); }); // 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.downSelect((int, event) => { if (api.feature.on_mouse_down) { if (int) { api.feature.on_mouse_down(int, event); return; } } if (api.feature.hover) { if (int) { return api.event.emit('mouse.hover.down', {int, point: int.point}); } else { return selection.meshes(); } } // 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)); 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 || selection.meshes(); } }); space.mouse.upSelect((object, event) => { if (api.feature.on_mouse_up) { if (event && object) { return api.feature.on_mouse_up(object, event); } else { return api.widgets.meshes(); } } 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, false); } } else { platform.deselect(); } } else { return api.feature.hovers || api.widgets.meshes(); } }); space.mouse.onDrag(function(delta, offset, up = false) { if (api.feature.hover) { return; } if (up) { api.event.emit('mouse.drag.done', offset); } 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; } selection.move(delta.x, delta.y, 0); api.event.emit('selection.drag', delta); } else { return 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'); // 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?${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?${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}); } // load workspace from url if (SETUP.wrk) { set_ctrl.import_url(`${proto}//${SETUP.wrk[0]}`, false); } // load an object from url if (SETUP.load) { console.log({load:SETUP}); api.platform.load_url(`${proto}//${SETUP.load[0]}`); } // bind this to UI so main can call it on settings import ui.sync = function() { const current = settings(); const control = current.controller; if (!control.devel) { // TODO: hide thin type 3 during development consts.LISTS.thin.length = 3; } platform.deselect(); catalog.addFileListener(updateCatalog); space.view.setZoom(control.reverseZoom, control.zoomSpeed); space.platform.setGridZOff(undefined); space.platform.setZOff(0.05); // 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.spaceRandoX.checked = control.spaceRandoX; ui.antiAlias.checked = control.antiAlias; ui.reverseZoom.checked = control.reverseZoom; ui.autoSave.checked = control.autoSave; ui.healMesh.checked = control.healMesh; ui.threaded.checked = setThreaded(control.threaded); ui.assembly.checked = control.assembly; ui.ortho.checked = control.ortho; ui.devel.checked = control.devel; lineTypeSave(); detailSave(); updateStats(); // 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 api.devices.refresh(); // update ui fields from settings api.conf.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 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 | ${version}`); // send init-done event api.event.emit('init-done', stats); // show gdpr if it's never been seen and we're not iframed const isLocal = LOCAL || WIN.location.host.split(':')[0] === 'localhost'; if (!sdb.gdpr && WIN.self === WIN.top && !SETUP.debug && !isLocal) { $('gdpr').style.display = 'flex'; } // warn of degraded functionality when SharedArrayBuffers are missing if (api.feature.work_alerts && !window.SharedArrayBuffer) { api.alerts.show("The security context of this", 10); api.alerts.show("Window blocks important functionality.", 10); api.alerts.show("Try a Chromium-base Browser instead", 10); } // 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(); }; // bind interface action elements $('mode-device').onclick = api.show.devices; $('mode-profile').onclick = settingsLoad; $('mode-fdm').onclick = () => api.mode.set('FDM'); $('mode-cam').onclick = () => api.mode.set('CAM'); $('mode-sla').onclick = () => api.mode.set('SLA'); $('mode-laser').onclick = () => api.mode.set('LASER'); $('mode-drag').onclick = () => api.mode.set('DRAG'); $('mode-wjet').onclick = () => api.mode.set('WJET'); $('mode-wedm').onclick = () => api.mode.set('WEDM'); $('set-device').onclick = (ev) => { ev.stopPropagation(); api.show.devices() }; $('set-profs').onclick = (ev) => { ev.stopPropagation(); api.conf.show() }; $('set-tools').onclick = (ev) => { ev.stopPropagation(); api.show.tools() }; $('set-prefs').onclick = (ev) => { ev.stopPropagation(); api.modal.show('prefs') }; ui.acct.help.onclick = (ev) => { ev.stopPropagation(); api.help.show() }; ui.acct.don8.onclick = (ev) => { ev.stopPropagation(); api.modal.show('don8') }; ui.acct.mesh.onclick = (ev) => { ev.stopPropagation(); WIN.location = "/mesh" }; ui.acct.export.onclick = (ev) => { ev.stopPropagation(); profileExport() }; ui.acct.export.title = LANG.acct_xpo; $('file-new').onclick = (ev) => { ev.stopPropagation(); workspaceNew() }; $('file-recent').onclick = () => { api.modal.show('files') }; $('file-import').onclick = (ev) => { api.event.import(ev); }; 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-front').onclick = space.view.front; $('view-back').onclick = space.view.back; $('view-left').onclick = space.view.left; $('view-right').onclick = space.view.right; $('unrotate').onclick = () => { api.widgets.for(w => w.unrotate()); selection.update_info(); }; // attach button handlers to support targets for (let btn of ["don8pt","don8gh","don8pp"]) { $(btn).onclick = (ev) => { window.open(ev.target.children[0].href); } } // rotation buttons let d = (Math.PI / 180); $('rot_x_lt').onclick = () => { selection.rotate(-d * $('rot_x').value,0,0) }; $('rot_x_gt').onclick = () => { selection.rotate( d * $('rot_x').value,0,0) }; $('rot_y_lt').onclick = () => { selection.rotate(0,-d * $('rot_y').value,0) }; $('rot_y_gt').onclick = () => { selection.rotate(0, d * $('rot_y').value,0) }; $('rot_z_lt').onclick = () => { selection.rotate(0,0, d * $('rot_z').value) }; $('rot_z_gt').onclick = () => { selection.rotate(0,0,-d * $('rot_z').value) }; // rendering options $('render-edges').onclick = () => { api.view.edges({ toggle: true }); api.conf.save() }; $('render-ghost').onclick = () => { api.view.wireframe(false, 0, api.view.is_arrange() ? 0.4 : 0.25); }; $('render-wire').onclick = () => { api.view.wireframe(true, 0, api.space.is_dark() ? 0.25 : 0.5); }; $('render-solid').onclick = () => { api.view.wireframe(false, 0, 1); }; $('mesh-export-stl').onclick = () => { objectsExport('stl') }; $('mesh-export-obj').onclick = () => { objectsExport('obj') }; $('mesh-merge').onclick = selection.merge; $('context-duplicate').onclick = duplicateSelection; $('context-mirror').onclick = mirrorSelection; $('context-layflat').onclick = () => { api.event.emit("tool.mesh.lay-flat") }; $('context-setfocus').onclick = () => { api.event.emit( "tool.camera.focus", ev => api.space.set_focus(undefined, ev.object.point) ); }; // ui.modal.onclick = api.modal.hide; ui.modalBox.onclick = (ev) => { ev.stopPropagation() }; // add app name hover info $('app-info').innerText = version; // show topline separator when iframed try { if (WIN.self !== WIN.top) $('top-sep').style.display = 'flex' } catch (e) { console.log(e) } // warn users they are running a beta release if (beta && beta > 0 && sdb.kiri_beta != beta) { api.show.alert("CAUTION"); api.show.alert("this is a development release"); api.show.alert("and may not function properly"); sdb.kiri_beta = beta; } // lift curtain $('curtain').style.display = 'none'; } // update static html elements with language overrides function lang_rewrite() { // 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}); } } } // init lang must happen before all other init functions function init_lang() { // if a language needs to load, the script is injected and loaded // first. once this loads, or doesn't, the initialization begins let lang = SETUP.ln ? SETUP.ln[0] : sdb.getItem('kiri-lang') || api.language.get(); api.event.emit('init.lang', lang); // inject language script if not english if (lang && lang !== 'en' && lang !== 'en-us') { let map = api.language.map(lang); let scr = DOC.createElement('script'); // scr.setAttribute('defer',true); scr.setAttribute('src',`/kiri/lang/${map}.js?${version}`); (DOC.body || DOC.head).appendChild(scr); stats.set('ll',lang); scr.onload = function() { api.language.set(map); lang_rewrite(); init_one(); }; scr.onerror = function(err) { console.log({language_load_error: err, lang}) api.language.set(); lang_rewrite(); init_one(); } } else { // set to browser default which will be overridden // by any future script loads (above) api.language.set(); lang_rewrite(); init_one(); } } export function run() { init_lang(); return api; }