From 5532cf28e4a9a60de8ddef6005f8a974e48adb4d Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Thu, 10 Jul 2025 16:54:02 -0400 Subject: [PATCH 01/28] add cam origin selector. fix wasm newpoly --- app.js | 90 ++++++++++---------------- src/geo/polygon.js | 8 +++ src/geo/wasm.js | 4 +- src/kiri/core/api.js | 2 +- src/kiri/core/client.js | 11 ++-- src/kiri/core/init.js | 68 +++----------------- src/kiri/mode/cam/cl-ops.js | 1 + src/kiri/mode/cam/cl-origin.js | 114 +++++++++++++++++++++++++++++++++ src/kiri/mode/cam/cl-trace.js | 6 +- src/kiri/mode/cam/client.js | 10 +-- src/kiri/mode/cam/init-menu.js | 92 ++++++++++++++++++++++++++ src/kiri/mode/cam/slice.js | 3 +- 12 files changed, 277 insertions(+), 132 deletions(-) create mode 100644 src/kiri/mode/cam/cl-origin.js create mode 100644 src/kiri/mode/cam/init-menu.js diff --git a/app.js b/app.js index aa58c139..a93473d8 100644 --- a/app.js +++ b/app.js @@ -72,11 +72,6 @@ function init(mod) { dversion = debug ? `_${version}` : version; forceUseCache = ENV.cache ? true : false; - const approot = PATH.join("main","gapp"); - const refcache = {}; - const callstack = []; - let xxxx = false; - if (!ENV.electron) { generateDevices(); } @@ -116,8 +111,6 @@ function init(mod) { }); } mod.add(rewriteHtmlVersion); - // example of how to use the new middleware - // mod.add(appendContent('/kiri/index.html', '')); mod.static("/lib/", "src"); mod.static("/obj/", "web/obj"); mod.static("/font/", "web/font"); @@ -126,8 +119,8 @@ function init(mod) { mod.static("/moto/", "web/moto"); mod.static("/kiri/", "web/kiri"); + // module loader function load_modules(root, force) { - // load modules lastmod(`${dir}/${root}`) && fs.readdirSync(`${dir}/${root}`).forEach(mdir => { const modpath = `${root}/${mdir}`; if (dir.charAt(0) === '.' && !ENV.single) return; @@ -152,7 +145,7 @@ function init(mod) { // load optional local modules load_modules('mods'); - // run loads injected by modules + // run load functions injected by modules while (load.length) { try { load.shift()(); @@ -161,7 +154,7 @@ function init(mod) { } } - // minify appends + // minify appends in production mode if (!debug) { for (let key of Object.keys(append)) { append[key] = minify(append[key]); @@ -189,14 +182,14 @@ function initModule(mod, file, dir) { // express functions added here show up at "/api/" url root api: api, adm: { - setver: (ver) => { oversion = ver }, - crossOrigin: (bool) => { crossOrigin = bool } + setver(ver) { oversion = ver }, + crossOrigin(bool) { crossOrigin = bool } }, events, const: { args: {}, meta: mod.meta, - debug: debug, + debug, moddir: dir, rootdir: mod.dir, version: oversion || version @@ -209,35 +202,21 @@ function initModule(mod, file, dir) { }, mod: mods, util: { + guid, + time, log: logger.log, - time: time, - guid: guid, + logger: log.new, mkdirs: util.mkdir, - isfile: util.isfile, confdir: util.confdir, datadir: util.datadir, lastmod: lastmod, - obj2string: obj2string, - string2obj: string2obj, + isfile: util.isfile, getCookieValue: cookieValue, - logger: log.new - }, - inject: (code, file, opt = {}) => { - const path = mod.dir + '/' + dir + '/' + file; - try { - const body = fs.readFileSync(path); - // console.log({ inject: code, file, opt }); - if (opt.first) { - append[code] = body.toString() + '\n' + append[code]; - } else { - append[code] += body.toString() + '\n'; - } - } catch (e) { - console.log({ missing_file: path, dir, mod }); - } + obj2string(o) { return JSON.stringify(o) }, + string2obj(s) { return JSON.parse(s) }, }, path: { - any: arg => { mod.add(arg) }, + any(arg) { mod.add(arg) }, code() { const [ path, file ] = [ ...arguments ]; if (lastmod(file)) { @@ -251,13 +230,13 @@ function initModule(mod, file, dir) { console.log({ MISSING_CODE: path, file }); } }, - full: arg => { mod.add(fullpath(arg)) }, - map: arg => { mod.add(fixedmap(arg)) }, - pre: arg => { mod.add(prepath(arg)) }, + full(arg) { mod.add(fullpath(arg)) }, + map(arg) { mod.add(fixedmap(arg)) }, + pre(arg) { mod.add(prepath(arg)) }, redir: redir, remap: remap, - setup: fn => { setupFn = fn }, - static: (root, pre) => { + setup(fn) { setupFn = fn }, + static(root, pre) { mod.static(pre || "/", root); }, }, @@ -271,22 +250,29 @@ function initModule(mod, file, dir) { ws: { register: mod.wss }, - onload: (fn) => { + inject(code, file, opt = {}) { + const path = mod.dir + '/' + dir + '/' + file; + try { + const body = fs.readFileSync(path); + // console.log({ inject: code, file, opt }); + if (opt.first) { + append[code] = body.toString() + '\n' + append[code]; + } else { + append[code] += body.toString() + '\n'; + } + } catch (e) { + console.log({ missing_file: path, dir, mod }); + } + }, + onload(fn) { load.push(fn); }, - onexit: (fn) => { + onexit(fn) { mod.on.exit(fn); } }); } -// prevent caching of specified modules -const cachever = {}; - -function promise(resolve, reject) { - return new Promise(resolve, reject); -} - function rval() { return (Math.round(Math.random()*0xffffffff)).toString(36); } @@ -299,14 +285,6 @@ function time() { return Date.now(); } -function obj2string(o) { - return JSON.stringify(o); -} - -function string2obj(s) { - return JSON.parse(s); -} - function handleSetup(req, res, next) { if (setupFn) { setupFn(req, res, next); diff --git a/src/geo/polygon.js b/src/geo/polygon.js index 6e665522..395e0351 100644 --- a/src/geo/polygon.js +++ b/src/geo/polygon.js @@ -1117,6 +1117,14 @@ export class Polygon { return this.first().isEqual(this.last()); } + fixClosed() { + if (this.appearsClosed()) { + this.points.pop(); + this.open = false; + } + return this; + } + setClockwise() { if (!this.isClockwise()) this.reverse(); return this; diff --git a/src/geo/wasm.js b/src/geo/wasm.js index 87ec5cdd..72c7578f 100644 --- a/src/geo/wasm.js +++ b/src/geo/wasm.js @@ -1,9 +1,9 @@ /** Copyright Stewart Allen -- All Rights Reserved */ import { base } from '../geo/base.js'; +import { newPolygon } from './polygon.js'; const { config } = base; - const factor = config.clipper; export const wasm_ctrl = { @@ -54,7 +54,7 @@ function writePoly(view, poly, inner) { function readPoly(view, z) { let points = view.readU16(true); if (points === 0) return; - let poly = base.newPolygon(); + let poly = newPolygon(); while (points-- > 0) { poly.add(view.readI32(true)/factor, view.readI32(true)/factor, z || 0); } diff --git a/src/kiri/core/api.js b/src/kiri/core/api.js index c81a6954..c66292a3 100644 --- a/src/kiri/core/api.js +++ b/src/kiri/core/api.js @@ -6,7 +6,7 @@ import STACKS from './stacks.js'; import { broker } from '../../moto/broker.js'; import { client as work } from './client.js'; -import { consts, COLOR as color, LISTS as lists, MODES, VIEWS, PATHS } from './consts.js'; +import { consts, COLOR as color, LISTS as lists } from './consts.js'; import { device, devices } from './devices.js'; import { catalog, dialog, event, group, help, hide, image } from './main.js'; import { modal, mode, probe, process, show, space, util, view } from './main.js'; diff --git a/src/kiri/core/client.js b/src/kiri/core/client.js index 363161fb..a6a5a116 100644 --- a/src/kiri/core/client.js +++ b/src/kiri/core/client.js @@ -2,6 +2,9 @@ import { api } from './api.js'; import { noop } from './utils.js'; +import { codec } from './codec.js'; +import { Widget } from './widget.js'; +import { newPrint } from './print.js'; // this code runs in kiri's main loop let debug = self.debug === true, @@ -199,7 +202,7 @@ export const client = { send("rotate", { settings }, reply => { if (reply.group) { // collect post-rotation data for slice/preview renders - for (let widget of kiri.Widget.Groups.forid(reply.group)) { + for (let widget of Widget.Groups.forid(reply.group)) { widget.belt = reply.belt; } } else if (callback) { @@ -280,10 +283,10 @@ export const client = { y: -origin.y, z: origin.z }; - const output = kiri.newPrint().parseSVG(code, offset); + const output = newPrint().parseSVG(code, offset); send("parse_svg", output, reply => { if (reply.parsed) { - done(kiri.codec.decode(reply.parsed)); + done(codec.decode(reply.parsed)); } }); return; @@ -293,7 +296,7 @@ export const client = { progress(reply.progress); } if (reply.parsed) { - done(kiri.codec.decode(reply.parsed), reply.maxSpeed, reply.minSpeed); + done(codec.decode(reply.parsed), reply.maxSpeed, reply.minSpeed); } }); }, diff --git a/src/kiri/core/init.js b/src/kiri/core/init.js index c7829d66..df797448 100644 --- a/src/kiri/core/init.js +++ b/src/kiri/core/init.js @@ -16,6 +16,7 @@ import { init as initLaser } from '../mode/laser/client.js'; import { init as initSLA } from '../mode/sla/client.js'; import { init as initWEDM } from '../mode/wedm/client.js'; import { init as initWJET } from '../mode/wjet/client.js'; +import { menu as menuCAM } from '../mode/cam/init-menu.js'; let { CAM, SLA, FDM, LASER, DRAG, WJET, WEDM } = MODES, { client, catalog, platform, selection, stats } = api, @@ -123,11 +124,6 @@ function speedSave() { api.view.update_speeds(); } -function zAnchorSave() { - api.conf.update(); - api.platform.update_top_z(); -} - function setThreaded(bool) { if (bool) { client.pool.start(); @@ -234,6 +230,8 @@ function onBooleanClick(el) { api.devices.update_laser_state(); } +api.event.on('click.boolean', onBooleanClick); + function onButtonClick(ev) { let target = ev.target; while (target && target.tagName !== 'BUTTON') { @@ -242,6 +240,8 @@ function onButtonClick(ev) { api.event.emit("button.click", target); } +api.event.on('click.button', onButtonClick); + function inputHasFocus() { let active = DOC.activeElement; return active && (active.nodeName === "INPUT" || active.nodeName === "TEXTAREA"); @@ -1229,61 +1229,7 @@ function init_one() { 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}), + ...menuCAM(), /** LASER/DRAG/WJET/WEDM cut tool Settings */ @@ -1367,6 +1313,8 @@ function init_one() { }); // run client mode initializations right after UI is bound + // MUST run after main ui menu inits since they rely on passed state + // that createPopOps disrupts (should be refactored) initCAM(); initDRAG(); initFDM(); diff --git a/src/kiri/mode/cam/cl-ops.js b/src/kiri/mode/cam/cl-ops.js index d4c63b6d..d3559a2d 100644 --- a/src/kiri/mode/cam/cl-ops.js +++ b/src/kiri/mode/cam/cl-ops.js @@ -95,6 +95,7 @@ export function createPopOp(type, map) { return op.rec === rec; }, bind: (ev) => { + console.log({ bind: type, map, op }); api.util.ui2rec(op.rec, op.inputs); const settings = conf.get(); diff --git a/src/kiri/mode/cam/cl-origin.js b/src/kiri/mode/cam/cl-origin.js new file mode 100644 index 00000000..073a7f08 --- /dev/null +++ b/src/kiri/mode/cam/cl-origin.js @@ -0,0 +1,114 @@ +/** Copyright Stewart Allen -- All Rights Reserved */ + +import { api } from '../../core/api.js'; +import { env, clearPops } from './client.js'; +import { CAM } from './driver-fe.js'; +import { config } from '../../../geo/base.js'; +import { addbox, delbox } from '../../core/boxes.js'; + +export let originSelectOn = false; + +let { world } = api.SPACE; +let alert, lastHover, lastPoints; + +export function originSelect() { + if (originSelectOn) { + return originSelectDone(); + } + + clearPops(); + + alert = api.show.alert("analyzing parts...", 1000); + originSelectOn = true; + + api.feature.hover = true; + api.feature.hoverAdds = true; + env.hover = pointHover; + env.hoverUp = pointHoverUp; + + CAM.traces((ids) => { + api.hide.alert(alert); + alert = api.show.alert("select origin
[esc] to cancel", 1000); + let points = {}; + let color = new THREE.Color( 0xff0000 ); + api.widgets.for(widget => { + let { pos, tzoff } = widget.track; + let { adds, traces } = widget; + let polys = traces.map(trace => { + return trace.fixClosed().clone().move(pos).move({ x:0, y:0, z: -tzoff }); + }); + for (let poly of polys) { + if (poly.circularity() >= config.hint_min_circ) { + let center = poly.calcCircleCenter(); + if (points[center.key]) { + continue; + } + let box = addbox(center, color, center.key, undefined, { opacity: 1 }); + adds.push(box); + world.add(box); + box._origin = center; + points[center.key] = { box, center }; + } + } + }); + lastPoints = Object.values(points); + }); +} + +export function originSelectDone() { + if (!originSelectOn) { + return; + } + api.hide.alert(alert); + api.feature.hover = false; + api.feature.hoverAdds = false; + api.widgets.for(widget => { + for (let box of widget.adds) { + world.remove(box); + } + widget.adds.length = 0; + }); + originSelectOn = false; +} + +function pointHover(data) { + if (lastHover) { + let { color, colorSave } = lastHover.material[0] || lastHover.material; + color.r = colorSave.r; + color.g = colorSave.g; + color.b = colorSave.b; + } + lastHover = null; + if (data.type === 'platform') { + return; + } + if (!data.int.object._origin) { + return; + } + lastHover = data.int.object; + let material = lastHover.material[0] || lastHover.material; + let color = material.color; + material.colorSave = color.clone(); + color.setHex(0x00ff00); +} + +function pointHoverUp(int, ev) { + if (!int?.object?._origin) return; + let { _origin } = int.object; + let { process, stock } = api.conf.get(); + let { x, y, z } = _origin; + let { camOriginTop, camOriginCenter } = process; + if (camOriginTop) { + z = z - stock.z; + } + if (!camOriginCenter) { + x += stock.x / 2; + y += stock.y / 2; + } + process.camOriginOffX = x; + process.camOriginOffY = y; + process.camOriginOffZ = z; + api.settings.update_fields(); + api.platform.update_origin(); + originSelectDone(); +} diff --git a/src/kiri/mode/cam/cl-trace.js b/src/kiri/mode/cam/cl-trace.js index aef93434..25755670 100644 --- a/src/kiri/mode/cam/cl-trace.js +++ b/src/kiri/mode/cam/cl-trace.js @@ -1,7 +1,7 @@ /** Copyright Stewart Allen -- All Rights Reserved */ import { api } from '../../core/api.js'; -import { env, clearPops } from './client.js'; +import { env, clearPops, isDark } from './client.js'; import { CAM } from './driver-fe.js'; import { Layers } from '../../core/layers.js'; import { Stack } from '../../core/stack.js'; @@ -123,7 +123,7 @@ export function traceHover(data) { let material = lastTrace.material[0] || lastTrace.material; let color = material.color; material.colorSave = color.clone(); - color.setHex(env.isDark() ? 0x0066ff : 0x0000ff); + color.setHex(isDark() ? 0x0066ff : 0x0000ff); } export function traceHoverUp(int, ev) { @@ -157,7 +157,7 @@ export function traceToggle(obj, skip) { colorSave = material.colorSave = color.clone(); } if (obj.selected) { - color.setHex(env.isDark() ? 0xdd0011 : 0xff0033); + color.setHex(isDark() ? 0xdd0011 : 0xff0033); colorSave.r = color.r; colorSave.g = color.g; colorSave.b = color.b; diff --git a/src/kiri/mode/cam/client.js b/src/kiri/mode/cam/client.js index e163978c..6e7b9cc8 100644 --- a/src/kiri/mode/cam/client.js +++ b/src/kiri/mode/cam/client.js @@ -1,6 +1,6 @@ /** Copyright Stewart Allen -- All Rights Reserved */ -import { $, h } from '../../../moto/webui.js'; +import { $ } from '../../../moto/webui.js'; import { api } from '../../core/api.js'; import { load } from '../../../load/file.js'; import { animate as anim_2d, animate_clear as anim_2d_clear } from './anim-2d-fe.js'; @@ -14,13 +14,14 @@ import { tabAdd, tabDone, tabClear, restoreTabs, updateTabs } from './cl-tab.js' import { traceOn, traceDone, unselectTraces } from './cl-trace.js'; import { holeSelOn, selectHolesDone, clearHolesRec } from './cl-hole.js'; import { surfaceOn, surfaceDone } from './cl-surface.js'; +import { originSelectDone } from './cl-origin.js'; const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; const hasSharedArrays = self.SharedArrayBuffer ? true : false; const { VIEWS, STACKS } = api.const; -const { noop, space } = api; +const { noop } = api; const { ui: UI, uc: UC } = api; const { widgets: WIDGETS } = api; @@ -54,10 +55,11 @@ export function isDark() { export function clearPops() { if (env.func.unpop) env.func.unpop(); + originSelectDone(); + selectHolesDone(); + surfaceDone(); tabDone(); traceDone(); - surfaceDone(); - selectHolesDone(); } function animFn() { diff --git a/src/kiri/mode/cam/init-menu.js b/src/kiri/mode/cam/init-menu.js new file mode 100644 index 00000000..d9972c13 --- /dev/null +++ b/src/kiri/mode/cam/init-menu.js @@ -0,0 +1,92 @@ + +import { api } from '../../core/api.js'; +import { originSelect } from './cl-origin.js'; + +let LANG = api.language.current; +let { CAM } = api.const.MODES, + { $ } = api.web, + { uc, ui } = api, + { bound, toInt, toFloat } = uc, + { newBlank, newButton, newBoolean, newGroup, newInput } = uc, + { newSelect, newLabel, newValue, newRow, newGCode, newDiv } = uc, + driven = true, + separator = true, + trigger = true, + units = true + ; + +function onBooleanClick(el) { + api.event.emit('click.boolean', el); +} + +function onButtonClick(el) { + api.event.emit('click.button', el); +} + +function zAnchorSave() { + api.conf.update(); + api.platform.update_top_z(); +} + +export function menu() { + return { + + _____: 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}), + separator: newBlank({ class:"set-sep", driven }), + camOriginSelect: newRow([ newButton("select", originSelect)], { class: "ext-buttons f-row" }), + _____: 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}), + + }; +}; diff --git a/src/kiri/mode/cam/slice.js b/src/kiri/mode/cam/slice.js index 9df46bdc..174bdf23 100644 --- a/src/kiri/mode/cam/slice.js +++ b/src/kiri/mode/cam/slice.js @@ -398,7 +398,7 @@ export function addDogbones(poly, dist, reverse) { } }; -export async function traces(settings, widget, single) { +export async function traces(settings, widget) { if (widget.traces) { return false; } @@ -534,7 +534,6 @@ export async function holes(settings, widget, individual, rec, onProgress) { // keeps the z bottom relative to the part when z align changes zBottom = isIndexed ? camZBottom : camZBottom - zbOff; - let slicerOpts = { flatoff: 0.001 } let slicer = new cam_slicer(widget, slicerOpts); let zFlats = Object.keys(slicer.zFlat).map(Number).map(z => [z, z - 0.002]).flat() From 4197377d8576a4f21e8cf7b16eaef73eeeed9a9c Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Thu, 10 Jul 2025 19:01:00 -0400 Subject: [PATCH 02/28] add origin offset reset button --- src/kiri/core/boxes.js | 10 +++++++++- src/kiri/core/settings.js | 10 +++++++++- src/kiri/core/ui.js | 1 + src/kiri/mode/cam/cl-origin.js | 19 ++++++++++++++++--- src/kiri/mode/cam/cl-tab.js | 4 +++- src/kiri/mode/cam/init-menu.js | 7 +++++-- 6 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/kiri/core/boxes.js b/src/kiri/core/boxes.js index ac18430e..7e6cbaed 100644 --- a/src/kiri/core/boxes.js +++ b/src/kiri/core/boxes.js @@ -5,6 +5,10 @@ const boxes = []; let lastBox; +export function clearboxes() { + boxes.length = 0; +} + export function getlastbox() { return lastBox; } @@ -19,7 +23,7 @@ export function delbox(name) { export function addbox(point, color, name, dim = {x:1,y:1,z:1,rz:0}, opt = {}) { delbox(name); - const box = boxes[name] = new Mesh( + const box = new Mesh( new BoxGeometry(dim.x, dim.y, dim.z), new MeshPhongMaterial({ transparent: true, @@ -28,6 +32,10 @@ export function addbox(point, color, name, dim = {x:1,y:1,z:1,rz:0}, opt = {}) { }) ); + if (name) { + boxes[name] = box; + } + box.position.x = point.x; box.position.y = point.y; box.position.z = point.z; diff --git a/src/kiri/core/settings.js b/src/kiri/core/settings.js index db9d40ae..ec98c819 100644 --- a/src/kiri/core/settings.js +++ b/src/kiri/core/settings.js @@ -221,7 +221,7 @@ function updateSettingsFromFields(setrec, uirec = api.ui, changes) { } // given a settings region, update values of matching bound UI fields -function updateFieldsFromSettings(setrec, uirec = api.ui, trace) { +function updateFieldsFromSettings(setrec, uirec = api.ui, opt = {}) { if (!setrec) { updateFieldsFromSettings(settings.device); updateFieldsFromSettings(settings.process); @@ -229,6 +229,9 @@ function updateFieldsFromSettings(setrec, uirec = api.ui, trace) { updateExtruderFields(settings.device); return; } + if (opt.trace) { + console.log({ setrec, uirec }); + } for (let key in setrec) { if (!setrec.hasOwnProperty(key)) { continue; @@ -238,6 +241,9 @@ function updateFieldsFromSettings(setrec, uirec = api.ui, trace) { continue; } let uie = uirec[key], typ = uie ? uie.type : null; + if (opt.trace) { + console.log({ key, uie, typ, val }); + } if (typ === 'text') { if (uie.setv) { uie.setv(val); @@ -271,6 +277,8 @@ function updateFieldsFromSettings(setrec, uirec = api.ui, trace) { } else { uie.value = ''; } + } else if (opt.trace) { + console.log('skipped', { key, val, typ }); } } } diff --git a/src/kiri/core/ui.js b/src/kiri/core/ui.js index a13ce209..bb665d31 100644 --- a/src/kiri/core/ui.js +++ b/src/kiri/core/ui.js @@ -672,6 +672,7 @@ function addUnits(input, round) { } else { input.value = value; } + // console.log({ value }, input.real, input.value ); return input.real; }; return input; diff --git a/src/kiri/mode/cam/cl-origin.js b/src/kiri/mode/cam/cl-origin.js index 073a7f08..e971a560 100644 --- a/src/kiri/mode/cam/cl-origin.js +++ b/src/kiri/mode/cam/cl-origin.js @@ -4,7 +4,7 @@ import { api } from '../../core/api.js'; import { env, clearPops } from './client.js'; import { CAM } from './driver-fe.js'; import { config } from '../../../geo/base.js'; -import { addbox, delbox } from '../../core/boxes.js'; +import { addbox, clearboxes } from '../../core/boxes.js'; export let originSelectOn = false; @@ -43,7 +43,7 @@ export function originSelect() { if (points[center.key]) { continue; } - let box = addbox(center, color, center.key, undefined, { opacity: 1 }); + let box = addbox(center, color, undefined, undefined, { opacity: 1 }); adds.push(box); world.add(box); box._origin = center; @@ -55,6 +55,17 @@ export function originSelect() { }); } +export function originReset() { + let { process} = api.conf.get(); + process.camOriginOffX = 0; + process.camOriginOffY = 0; + process.camOriginOffZ = 0; + api.settings.update_fields(process); + api.platform.update_origin(); + api.conf.save(); + originSelectDone(); +} + export function originSelectDone() { if (!originSelectOn) { return; @@ -68,6 +79,7 @@ export function originSelectDone() { } widget.adds.length = 0; }); + clearboxes(); originSelectOn = false; } @@ -108,7 +120,8 @@ function pointHoverUp(int, ev) { process.camOriginOffX = x; process.camOriginOffY = y; process.camOriginOffZ = z; - api.settings.update_fields(); + api.settings.update_fields(process); api.platform.update_origin(); + api.conf.save(); originSelectDone(); } diff --git a/src/kiri/mode/cam/cl-tab.js b/src/kiri/mode/cam/cl-tab.js index b65df0d5..6012ae0d 100644 --- a/src/kiri/mode/cam/cl-tab.js +++ b/src/kiri/mode/cam/cl-tab.js @@ -2,7 +2,7 @@ import { api } from '../../core/api.js'; import { env, isDark } from './client.js'; -import { addbox, delbox } from '../../core/boxes.js'; +import { addbox, delbox, clearboxes } from '../../core/boxes.js'; import { traceDone } from './cl-trace.js'; let showTab, lastTab, tab, iw, ic; @@ -107,6 +107,7 @@ export function clearTabs(widget, skiprec) { if (!skiprec) { delete api.widgets.annotate(widget.id).tab; } + clearboxes(); } export function updateTabs() { @@ -198,6 +199,7 @@ export function tabAdd() { export function tabDone() { delbox('tabb'); + clearboxes(); api.hide.alert(alert); api.feature.hover = false; if (lastTab) { diff --git a/src/kiri/mode/cam/init-menu.js b/src/kiri/mode/cam/init-menu.js index d9972c13..e628c58f 100644 --- a/src/kiri/mode/cam/init-menu.js +++ b/src/kiri/mode/cam/init-menu.js @@ -1,6 +1,6 @@ import { api } from '../../core/api.js'; -import { originSelect } from './cl-origin.js'; +import { originReset, originSelect } from './cl-origin.js'; let LANG = api.language.current; let { CAM } = api.const.MODES, @@ -80,7 +80,10 @@ export function menu() { 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}), separator: newBlank({ class:"set-sep", driven }), - camOriginSelect: newRow([ newButton("select", originSelect)], { class: "ext-buttons f-row" }), + camOriginSelect: newRow([ + newButton("select", originSelect), + newButton("reset", originReset), + ], { class: "ext-buttons f-row" }), _____: 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 }), From b57d815bfe56345ac3c181225bc06c80852ca8b4 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Thu, 10 Jul 2025 19:52:11 -0400 Subject: [PATCH 03/28] add 3-way trace intersections to origin candidates --- src/geo/polygon.js | 7 ++++++- src/kiri/mode/cam/cl-origin.js | 31 ++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/geo/polygon.js b/src/geo/polygon.js index 395e0351..4aece2c3 100644 --- a/src/geo/polygon.js +++ b/src/geo/polygon.js @@ -1560,7 +1560,12 @@ export class Polygon { * @returns {number} 0.0 - 1.0 from flat to perfectly circular */ circularity() { - return (4 * Math.PI * this.area()) / util.sqr(this.perimeter()); + try { + return (4 * Math.PI * this.area()) / util.sqr(this.perimeter()); + } catch (e) { + console.log(this.perimeter(), e); + return 0; + } } circularityDeep() { diff --git a/src/kiri/mode/cam/cl-origin.js b/src/kiri/mode/cam/cl-origin.js index e971a560..5b63f8ac 100644 --- a/src/kiri/mode/cam/cl-origin.js +++ b/src/kiri/mode/cam/cl-origin.js @@ -3,7 +3,6 @@ import { api } from '../../core/api.js'; import { env, clearPops } from './client.js'; import { CAM } from './driver-fe.js'; -import { config } from '../../../geo/base.js'; import { addbox, clearboxes } from '../../core/boxes.js'; export let originSelectOn = false; @@ -29,25 +28,39 @@ export function originSelect() { CAM.traces((ids) => { api.hide.alert(alert); alert = api.show.alert("select origin
[esc] to cancel", 1000); + let ends = {}; let points = {}; let color = new THREE.Color( 0xff0000 ); + function incpoint(widget, point) { + let rec = ends[point.key]; + if (rec && rec.count++ === 2) { + addpoint(widget, point); + } else if (!rec) { + rec = ends[point.key] = { count: 1 }; + } + } + function addpoint(widget, center) { + let box = addbox(center, color, undefined, undefined, { opacity: 1 }); + widget.adds.push(box); + world.add(box); + box._origin = center; + points[center.key] = { box, center }; + } api.widgets.for(widget => { let { pos, tzoff } = widget.track; - let { adds, traces } = widget; - let polys = traces.map(trace => { + let polys = widget.traces.map(trace => { return trace.fixClosed().clone().move(pos).move({ x:0, y:0, z: -tzoff }); }); for (let poly of polys) { - if (poly.circularity() >= config.hint_min_circ) { + if (poly.circularity() >= 0.99) { let center = poly.calcCircleCenter(); if (points[center.key]) { continue; } - let box = addbox(center, color, undefined, undefined, { opacity: 1 }); - adds.push(box); - world.add(box); - box._origin = center; - points[center.key] = { box, center }; + addpoint(widget, center); + } else if (poly.isOpen()) { + incpoint(widget, poly.first()); + incpoint(widget, poly.last()); } } }); From 4bba10717a399a6a413e3a5308e896900214c47a Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Thu, 10 Jul 2025 21:07:50 -0400 Subject: [PATCH 04/28] need to revisit adaptive plunge with contour --- src/kiri/core/function.js | 3 +-- src/kiri/core/print.js | 9 +++++++-- src/kiri/mode/cam/prepare.js | 20 ++++++++++++++++---- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/kiri/core/function.js b/src/kiri/core/function.js index a8462a66..26e4620b 100644 --- a/src/kiri/core/function.js +++ b/src/kiri/core/function.js @@ -452,8 +452,7 @@ function cancelWorker() { } function parseCode(code, type) { - const { conf, event, show, widgets, view } = api; - const { stacks } = kiri; + const { conf, event, show, stacks, widgets, view } = api; const settings = conf.get(); event.emit("code.load", {code, type}); diff --git a/src/kiri/core/print.js b/src/kiri/core/print.js index 3fc4b559..6be1665d 100644 --- a/src/kiri/core/print.js +++ b/src/kiri/core/print.js @@ -613,10 +613,15 @@ class Print { scope.belt = belt; if (scope.debugE) { - console.log({ bounds, print_time: time.round(2) }); + console.log({ + bounds, + minf, + maxf, + print_time: time.round(2), + output: scope.output + }); } - console.log(scope.output) done({ output: scope.output }); } } diff --git a/src/kiri/mode/cam/prepare.js b/src/kiri/mode/cam/prepare.js index 76c92ed4..cf7b5ea4 100644 --- a/src/kiri/mode/cam/prepare.js +++ b/src/kiri/mode/cam/prepare.js @@ -177,6 +177,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { } feedRate = feed || feedRate || plunge; plungeRate = Math.min(feedRate || plunge, plunge || plungeRate || feedRate); + console.log({ setTool: toolID, feed, plunge, plungeRate }); } function setLasering(bool, power = 0) { @@ -461,16 +462,27 @@ export function prepEach(widget, settings, print, firstPoint, update) { } // set new plunge rate - if (!lasering && deltaZ < -tolerance && !isLathe) { + if (!lasering && !isLathe && deltaZ < -tolerance) { let threshold = Math.min(deltaXY / 2, absDeltaZ), modifier = threshold / absDeltaZ; if (synthPlunge && threshold && modifier && deltaXY > tolerance) { // use modifier to speed up long XY move plunge rates // console.log('modifier', modifier); - rate = Math.round(plungeRate + ((feedRate - plungeRate) * modifier)); - cut = 1; + rate = Math.max( + plungeRate, + Math.round(plungeRate + ((feedRate - plungeRate) * modifier)) + ); } else { - rate = 1 / Math.hypot(deltaXY / feedRate, absDeltaZ / plungeRate); + let L = Math.hypot(deltaXY, absDeltaZ); + let limXY = feedRate * L / deltaXY; + let limZ = plungeRate * L / Math.abs(deltaZ); + rate = Math.min(feedRate, limXY, limZ); + // let zps = len / absDeltaZ; + // rate = Math.max( + // plungeRate, + // 1 / Math.hypot(deltaXY / feedRate, absDeltaZ / plungeRate) + // ); + // console.log({ rate, deltaXY, deltaZ }); } } From 806fc79eea468e7514f5af8a707deab9d38202f2 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Thu, 10 Jul 2025 22:21:17 -0400 Subject: [PATCH 05/28] improve error handling for netdb create --- app.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index a93473d8..b903136a 100644 --- a/app.js +++ b/app.js @@ -46,9 +46,13 @@ netdb.create = async function(map = {}) { Object.assign(map, JSON.parse(fs.readFileSync(map.conf))); } const client = new netdb(); - if (map.host && map.port) await client.open(map.host, map.port); - if (map.user && map.pass) await client.auth(map.user, map.pass); - if (map.base) await client.use(map.base); + try { + if (map.host && map.port) await client.open(map.host, map.port); + if (map.user && map.pass) await client.auth(map.user, map.pass); + if (map.base) await client.use(map.base); + } catch (e) { + logger.log({ netdb_setup_error: e }); + } logger.log({ netdb: map.host, user: map.user, base: map.base }); return client; }; From cb9781c3cd417bb41f008aaa9cb9325e6abc9db1 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Fri, 11 Jul 2025 11:28:06 -0400 Subject: [PATCH 06/28] pull more ui init code into mode directories --- src/kiri/core/init.js | 257 +------------------------------ src/kiri/mode/cam/init-menu.js | 34 +++- src/kiri/mode/fdm/init-menu.js | 188 ++++++++++++++++++++++ src/kiri/mode/laser/init-menu.js | 74 +++++++++ src/kiri/mode/sla/init-menu.js | 58 +++++++ 5 files changed, 359 insertions(+), 252 deletions(-) create mode 100644 src/kiri/mode/fdm/init-menu.js create mode 100644 src/kiri/mode/laser/init-menu.js create mode 100644 src/kiri/mode/sla/init-menu.js diff --git a/src/kiri/core/init.js b/src/kiri/core/init.js index df797448..1b96bfad 100644 --- a/src/kiri/core/init.js +++ b/src/kiri/core/init.js @@ -17,6 +17,9 @@ import { init as initSLA } from '../mode/sla/client.js'; import { init as initWEDM } from '../mode/wedm/client.js'; import { init as initWJET } from '../mode/wjet/client.js'; import { menu as menuCAM } from '../mode/cam/init-menu.js'; +import { menu as menuFDM } from '../mode/fdm/init-menu.js'; +import { menu as menuLaser } from '../mode/laser/init-menu.js'; +import { menu as menuSLA } from '../mode/sla/init-menu.js'; let { CAM, SLA, FDM, LASER, DRAG, WJET, WEDM } = MODES, { client, catalog, platform, selection, stats } = api, @@ -91,15 +94,6 @@ function lineTypeSave() { } } -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) { @@ -769,11 +763,6 @@ function updateCatalog(files) { } } -function isMultiHead() { - let dev = api.conf.get().device; - return isNotBelt() && dev.extruders && dev.extruders.length > 1; -} - function isBelt() { return api.device.isBelt(); } @@ -845,7 +834,6 @@ function init_one() { // api augmentation with local functions api.device.export = deviceExport; - let anim = ui.anim = {}; Object.assign(ui, { tracker: tracker, @@ -982,33 +970,6 @@ function init_one() { 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 }), @@ -1110,201 +1071,16 @@ function init_one() { 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'), + ...menuFDM(), /** CAM Settings */ ...menuCAM(), /** 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 }), + ...menuLaser(), /** 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"), + ...menuSLA(), layers: uc.setGroup($("layers")), @@ -1328,27 +1104,6 @@ function init_one() { 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; diff --git a/src/kiri/mode/cam/init-menu.js b/src/kiri/mode/cam/init-menu.js index e628c58f..0b62759b 100644 --- a/src/kiri/mode/cam/init-menu.js +++ b/src/kiri/mode/cam/init-menu.js @@ -1,3 +1,4 @@ +/** Copyright Stewart Allen -- All Rights Reserved */ import { api } from '../../core/api.js'; import { originReset, originSelect } from './cl-origin.js'; @@ -8,7 +9,7 @@ let { CAM } = api.const.MODES, { uc, ui } = api, { bound, toInt, toFloat } = uc, { newBlank, newButton, newBoolean, newGroup, newInput } = uc, - { newSelect, newLabel, newValue, newRow, newGCode, newDiv } = uc, + { newSelect, newLabel, newValue, newRow, newDiv } = uc, driven = true, separator = true, trigger = true, @@ -29,8 +30,39 @@ function zAnchorSave() { } export function menu() { + let anim = ui.anim = {}; + return { + /** 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"}), + ]) + }, + + /** Left Side Menu */ + _____: 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}), diff --git a/src/kiri/mode/fdm/init-menu.js b/src/kiri/mode/fdm/init-menu.js new file mode 100644 index 00000000..bb34b97f --- /dev/null +++ b/src/kiri/mode/fdm/init-menu.js @@ -0,0 +1,188 @@ +/** Copyright Stewart Allen -- All Rights Reserved */ + +import { api } from '../../core/api.js'; + +let LANG = api.language.current; +let { FDM } = api.const.MODES, + { $ } = api.web, + { uc, ui } = api, + { bound, toInt, toFloat } = uc, + { newBlank, newButton, newBoolean, newGroup, newInput, newSelect, newRow } = uc, + driven = true, + hideable = true, + separator = true, + trigger = true + ; + +function onBooleanClick(el) { + api.event.emit('click.boolean', el); +} + +function onButtonClick(el) { + api.event.emit('click.button', el); +} + +function thinWallSave() { + let opt = ui.sliceDetectThin; + let level = opt.options[opt.selectedIndex]; + if (level) { + api.conf.get().process.sliceDetectThin = level.value; + api.conf.save(); + } +} + +function isMultiHead() { + let dev = api.conf.get().device; + return isNotBelt() && dev.extruders && dev.extruders.length > 1; +} + +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 zIntShow() { + return settings().controller.devel; +} + +function isBelt() { + return api.device.isBelt(); +} + +function isNotBelt() { + return !isBelt(); +} + +export function menu() { + + return { + + /** Left Side Menu */ + + _____: 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'), + + }; +} diff --git a/src/kiri/mode/laser/init-menu.js b/src/kiri/mode/laser/init-menu.js new file mode 100644 index 00000000..14d40eee --- /dev/null +++ b/src/kiri/mode/laser/init-menu.js @@ -0,0 +1,74 @@ +/** Copyright Stewart Allen -- All Rights Reserved */ + +import { api } from '../../core/api.js'; + +let LANG = api.language.current; +let { LASER, DRAG, WJET, WEDM } = api.const.MODES, + TWOD = [ LASER, DRAG, WJET, WEDM ], + TWONED = [ LASER, DRAG, WJET ], + { $ } = api.web, + { uc, ui } = api, + { bound, toInt, toFloat } = uc, + { newBlank, newButton, newBoolean, newGroup, newInput, newRow } = uc, + driven = true, + separator = true, + trigger = true + ; + +function onBooleanClick(el) { + api.event.emit('click.boolean', el); +} + +function onButtonClick(el) { + api.event.emit('click.button', el); +} + +export function menu() { + + return { + + /** Left Side Menu */ + + _____: 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 }), + + }; + +} diff --git a/src/kiri/mode/sla/init-menu.js b/src/kiri/mode/sla/init-menu.js new file mode 100644 index 00000000..fa45d671 --- /dev/null +++ b/src/kiri/mode/sla/init-menu.js @@ -0,0 +1,58 @@ +/** Copyright Stewart Allen -- All Rights Reserved */ + +import { api } from '../../core/api.js'; + +let LANG = api.language.current; +let { SLA } = api.const.MODES, + { $ } = api.web, + { uc, ui } = api, + { bound, toInt, toFloat } = uc, + { newBoolean, newGroup, newInput, newSelect } = uc, + driven = true, + separator = true + ; + +function onBooleanClick(el) { + api.event.emit('click.boolean', el); +} + +export function menu() { + + return { + + /** Left Side Menu */ + + 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"), + + }; + +} From 083e874f2623a6ee831cb12fd1af68857c90bdcc Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Fri, 11 Jul 2025 18:00:06 -0400 Subject: [PATCH 07/28] allow for referer version testing for manifest request which does not receive cookies --- app.js | 16 +++++++++++++++- web/kiri/manifest.json | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index b903136a..7c151c95 100644 --- a/app.js +++ b/app.js @@ -81,7 +81,7 @@ function init(mod) { } mod.on.test((req) => { - let cookie = cookieValue(req.headers.cookie, "version") || undefined; + let cookie = cookieValue(req.headers.cookie, "version") || ''; let vmatch = mod.meta.version || "*"; if (!Array.isArray(vmatch)) { vmatch = [ vmatch ]; @@ -92,6 +92,17 @@ function init(mod) { return vmatch.indexOf(cookie) >= 0; }); + mod.on.testv((version) => { + let vmatch = mod.meta.version || "*"; + if (!Array.isArray(vmatch)) { + vmatch = [ vmatch ]; + } + if (vmatch.indexOf("*") >= 0) { + return true; + } + return vmatch.indexOf(version) >= 0; + }); + mod.add(handleSetup); mod.add(handleOptions); mod.add(serveWasm); @@ -174,6 +185,9 @@ function loadModule(mod, dir) { if (lastmod(`${mod.dir}/${dir}/.ignore`)) { return; } + // either load the module, or if it's not a module + // directory, serve the module as static content + // bound to the root lastmod(`${mod.dir}/${dir}/init.js`) ? initModule(mod, `./${dir}/init.js`, dir) : mod.static("/", `${mod.dir}/${dir}`); diff --git a/web/kiri/manifest.json b/web/kiri/manifest.json index 661bb6d5..92a74d65 100644 --- a/web/kiri/manifest.json +++ b/web/kiri/manifest.json @@ -1,5 +1,5 @@ { - "name": "Kiri:Moto 4.2.0", + "name": "Kiri:Moto 4.3.0", "short_name": "Kiri:Moto", "description": "Slicer for 3D printers, CNC mills, laser cutters and more", "start_url": "/kiri/", From e967fd3b2fb996ee6548e91d989a2dae323b2708 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Fri, 11 Jul 2025 18:42:28 -0400 Subject: [PATCH 08/28] update app-server dep --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2d3d5567..ac9f6edf 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ }, "dependencies": { "@fortawesome/fontawesome-free": "^6.1.1", - "@gridspace/app-server": "^0.0.17", + "@gridspace/app-server": "^0.0.18", "@gridspace/basic-ftp": "^5.0.5", "@gridspace/net-level-client": "^0.2.3", "@tracespace/parser": "^5.0.0-next.0", From 7d3b210ed996f422ca4cc4ff54e34b3ca01e8971 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sat, 12 Jul 2025 14:09:35 -0400 Subject: [PATCH 09/28] fix nested trace elements when clearing (poly area bug), fix shadowZ poly union, fix hidden cam animate --- src/geo/polygon.js | 2 ++ src/geo/polygons.js | 2 +- src/kiri/core/ui.js | 9 +++++++-- src/kiri/mode/cam/cl-ops.js | 1 - src/kiri/mode/cam/init-menu.js | 2 +- src/kiri/mode/cam/op-trace.js | 27 ++++++++++++++++++++++----- src/kiri/mode/cam/prepare.js | 2 +- src/kiri/mode/cam/slice.js | 2 +- 8 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/geo/polygon.js b/src/geo/polygon.js index 4aece2c3..8cc0f14d 100644 --- a/src/geo/polygon.js +++ b/src/geo/polygon.js @@ -843,6 +843,7 @@ export class Polygon { * scale polygon around origin */ scale(scale, round) { + this.area2 = undefined; let x, y, z; if (typeof(scale) === 'number') { x = y = z = scale; @@ -1073,6 +1074,7 @@ export class Polygon { * append point to polygon and return point */ push(p) { + this.area2 = undefined; // clone any point belonging to another polygon if (p.poly) p = p.clone(); p.poly = this; diff --git a/src/geo/polygons.js b/src/geo/polygons.js index 07cea123..6d4d8128 100644 --- a/src/geo/polygons.js +++ b/src/geo/polygons.js @@ -359,7 +359,7 @@ export function subtract(setA, setB, outA, outB, z, minArea, opt = {}) { function filter(from, to = []) { from.forEach(function(poly) { - if (poly.area() >= min) { + if (poly.area() >= min && poly.length > 2) { to.push(poly); out.push(poly); } diff --git a/src/kiri/core/ui.js b/src/kiri/core/ui.js index bb665d31..a31e41ec 100644 --- a/src/kiri/core/ui.js +++ b/src/kiri/core/ui.js @@ -421,8 +421,13 @@ function newDiv(opt = {}) { (opt.addto || addTo).appendChild(div); if (opt.addto) lastDiv = addTo = div; if (opt.class) div.setAttribute('class', opt.class); - lastGroup?.push(div); - div._group = groupName; + if (opt.group !== false) { + lastGroup?.push(div); + div._group = groupName; + } else { + lastGroup = undefined; + groupName = undefined; + } return div; } diff --git a/src/kiri/mode/cam/cl-ops.js b/src/kiri/mode/cam/cl-ops.js index d3559a2d..d4c63b6d 100644 --- a/src/kiri/mode/cam/cl-ops.js +++ b/src/kiri/mode/cam/cl-ops.js @@ -95,7 +95,6 @@ export function createPopOp(type, map) { return op.rec === rec; }, bind: (ev) => { - console.log({ bind: type, map, op }); api.util.ui2rec(op.rec, op.inputs); const settings = conf.get(); diff --git a/src/kiri/mode/cam/init-menu.js b/src/kiri/mode/cam/init-menu.js index 0b62759b..40faf9f7 100644 --- a/src/kiri/mode/cam/init-menu.js +++ b/src/kiri/mode/cam/init-menu.js @@ -37,7 +37,7 @@ export function menu() { /** Animation Bar */ _____: { - _____: newDiv({ addto: $('layer-animate'), set:true }), + _____: newDiv({ addto: $('layer-animate'), set:true, group:false }), row: newRow([ anim.replay = newButton(null,"anim.replay",{icon:'',title:"restart"}), anim.play = newButton(null,"anim.play",{icon:'',title:"play"}), diff --git a/src/kiri/mode/cam/op-trace.js b/src/kiri/mode/cam/op-trace.js index ab83409e..6ec46dba 100644 --- a/src/kiri/mode/cam/op-trace.js +++ b/src/kiri/mode/cam/op-trace.js @@ -107,15 +107,28 @@ class OpTrace extends CamOp { for (let z of zs) { let clip = [], shadow; shadow = shadowAt(z); - POLY.subtract([ poly ], shadow, clip, undefined, undefined, 0); + // for cases where the shadow IS the poly like + // with lettering without a bounding frame, clip + // will fail and we need to restore the matching poly + let subshadow = true; + for (let spo of shadow) { + if (poly.isInside(spo, 0.01)) { + subshadow = false; + clip = [ poly ]; + break; + } + } + if (subshadow) { + POLY.subtract([ poly ], shadow, clip, undefined, undefined, 0); + } if (op.outline) { POLY.clearInner(clip); } if (clip.length === 0) { continue; } - let slice = newSliceOut(z); let count = 999; + let slice = newSliceOut(z); slice.camTrace = { tool, rate, plunge }; if (toolDiam) { const offs = [ -toolDiam / 2, -toolOver ]; @@ -132,12 +145,15 @@ class OpTrace extends CamOp { slice.camLines = POLY.flatten(slice.camLines, null, true); } POLY.setWinding(slice.camLines, cutdir, false); - slice.output() - .setLayer("trace", {line: color}, false) - .addPolys(slice.camLines) if (debug && shadow) slice.output() .setLayer("trace shadow", {line: 0xff8811}, false) .addPolys(shadow) + if (debug) slice.output() + .setLayer("trace poly", {line: 0x1188ff}, false) + .addPolys([ poly ]) + slice.output() + .setLayer("trace", {line: color}, false) + .addPolys(slice.camLines) progress(zpro, "trace"); zpro += zinc; addSlices(slice); @@ -254,6 +270,7 @@ class OpTrace extends CamOp { case "clear": const zbo = widget.track.top - widget.track.box.d; let zmap = {}; + polys = POLY.nest(polys); for (let poly of polys) { let z = minZ(poly.minZ()); if (offover) { diff --git a/src/kiri/mode/cam/prepare.js b/src/kiri/mode/cam/prepare.js index cf7b5ea4..b54b9feb 100644 --- a/src/kiri/mode/cam/prepare.js +++ b/src/kiri/mode/cam/prepare.js @@ -177,7 +177,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { } feedRate = feed || feedRate || plunge; plungeRate = Math.min(feedRate || plunge, plunge || plungeRate || feedRate); - console.log({ setTool: toolID, feed, plunge, plungeRate }); + // console.log({ setTool: toolID, feed, plunge, plungeRate }); } function setLasering(bool, power = 0) { diff --git a/src/kiri/mode/cam/slice.js b/src/kiri/mode/cam/slice.js index 174bdf23..c23b1786 100644 --- a/src/kiri/mode/cam/slice.js +++ b/src/kiri/mode/cam/slice.js @@ -801,7 +801,7 @@ export function computeShadowAt(widget, z, ztop) { .add(a[2].x, a[2].y, a[2].z); }); - polys = POLY.union(polys, 0.001, true); + polys = POLY.union(polys, 0, true); return polys; } From 65dd5391493a7d8561179b07346a39049ef5a856 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sat, 12 Jul 2025 14:34:45 -0400 Subject: [PATCH 10/28] fix jszip esm wrapper --- src/ext/jszip.esm.js | 2 +- src/kiri/core/settings.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ext/jszip.esm.js b/src/ext/jszip.esm.js index 58a5a1de..298be08f 100644 --- a/src/ext/jszip.esm.js +++ b/src/ext/jszip.esm.js @@ -4,4 +4,4 @@ import * as JSZip from "./jszip.js"; //export object attached to the window object -export default self.JSZip; +export default JSZip; diff --git a/src/kiri/core/settings.js b/src/kiri/core/settings.js index ec98c819..65c36834 100644 --- a/src/kiri/core/settings.js +++ b/src/kiri/core/settings.js @@ -9,6 +9,7 @@ import { space } from '../../moto/space.js'; import { local } from '../../data/local.js'; import { utils } from './utils.js'; import { version } from '../../moto/license.js'; +import JSZip from '../../ext/jszip.esm.js'; const { areEqual, ls2o, js2o } = utils; const { COLOR } = consts; From 4496f1dc78d5fcb5917683632c29ec91b958df8e Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sat, 12 Jul 2025 15:43:51 -0400 Subject: [PATCH 11/28] topo4 fixups --- src/kiri/mode/cam/op-lathe.js | 1 + src/kiri/mode/cam/topo4.js | 22 ++++++++++++++-------- src/kiri/run/minion.js | 5 +++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/kiri/mode/cam/op-lathe.js b/src/kiri/mode/cam/op-lathe.js index 8bdf0361..b1b5349a 100644 --- a/src/kiri/mode/cam/op-lathe.js +++ b/src/kiri/mode/cam/op-lathe.js @@ -2,6 +2,7 @@ import { CamOp } from './op.js'; import { Tool } from './tool.js'; +import { Topo4 } from './topo4.js'; import { newPoint } from '../../../geo/point.js'; function createFilter(op) { diff --git a/src/kiri/mode/cam/topo4.js b/src/kiri/mode/cam/topo4.js index 4c5e5388..2a430dce 100644 --- a/src/kiri/mode/cam/topo4.js +++ b/src/kiri/mode/cam/topo4.js @@ -4,6 +4,7 @@ import { codec } from '../../core/codec.js'; import { newPoint } from '../../../geo/point.js'; import { newPolygon } from '../../../geo/polygon.js'; import { sliceConnect } from '../../../geo/slicer.js'; +import { newSlice } from '../../core/slice.js'; import { Tool } from './tool.js'; import { Slicer as topo_slicer } from './slicer_topo.js'; @@ -16,7 +17,7 @@ function scale(fn, factor = 1, base = 0) { } } -class Topo4 { +class Topo { constructor() { } async generate(opt = {}) { @@ -132,7 +133,8 @@ class Topo4 { slices.push(slice); // console.log({ shards, range, step, slices }); - if (kiri.minions.running > 1) { + const { minions } = self.kiri_worker; + if (minions?.running > 1) { return await this.sliceMinions(onupdate); } else { return await this.sliceWorker(onupdate); @@ -202,7 +204,8 @@ class Topo4 { } async lathe(onupdate) { - if (kiri.minions.running > 1) { + const { minions } = self.kiri_worker; + if (minions?.running > 1) { return await this.latheMinions(onupdate); } else { return await this.latheWorker(onupdate); @@ -444,24 +447,27 @@ class Topo4 { } putCache(key, data) { - kiri.worker.putCache({ key, data }, { done: data => { } }); + const { dispatch } = self.kiri_worker; + dispatch.putCache({ key, data }, { done: data => { } }); } clearCache() { - kiri.worker.clearCache({}, { done: data => { } }); + const { dispatch } = self.kiri_worker; + dispatch.clearCache({}, { done: data => { } }); } queue(cmd, params) { + const { minions } = self.kiri_worker; return new Promise(resolve => { - kiri.minions.queue({ cmd, ...params }, resolve); + minions.queue({ cmd, ...params }, resolve); }); } } -function rotatePoints(lines, rot) { +export function rotatePoints(lines, rot) { new THREE.BufferAttribute(lines, 3).applyMatrix4(rot); } export function Topo4(opt) { - return new Topo4().generate(opt); + return new Topo().generate(opt); }; diff --git a/src/kiri/run/minion.js b/src/kiri/run/minion.js index 58c25f9d..6d97d0c4 100644 --- a/src/kiri/run/minion.js +++ b/src/kiri/run/minion.js @@ -11,10 +11,11 @@ import { codec, encode, encodePointArray } from '../core/codec.js'; import { doTopShells } from '../mode/fdm/post.js'; import { newPoint } from '../../geo/point.js'; import { polygons as POLY } from '../../geo/polygons.js'; -import { sliceZ } from '../../geo/slicer.js'; +import { sliceZ, sliceConnect } from '../../geo/slicer.js'; import { Slicer as cam_slicer } from '../mode/cam/slicer.js'; import { Slicer as topo_slicer } from '../mode/cam/slicer_topo.js'; import { Probe, Trace, raster_slice } from '../mode/cam/topo3.js'; +import { Topo4, rotatePoints } from '../mode/cam/topo4.js'; import { wasm_ctrl } from '../../geo/wasm.js'; const clib = self.ClipperLib; @@ -271,7 +272,7 @@ const funcs = self.minion = { topo4_slice(data, seq) { const { slice, resolution } = data; const vertices = cache.vertices; - const recs = new kiri.topo_slicer(slice.index) + const recs = new topo_slicer(slice.index) .setFromArray(vertices, slice) .slice(resolution) .map(rec => { From f10024c28d607ba6b2d2b77af06a410add2e1dd6 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sat, 12 Jul 2025 16:08:34 -0400 Subject: [PATCH 12/28] fix slice progress for topo --- src/kiri/mode/cam/slice.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/kiri/mode/cam/slice.js b/src/kiri/mode/cam/slice.js index c23b1786..6abf19f5 100644 --- a/src/kiri/mode/cam/slice.js +++ b/src/kiri/mode/cam/slice.js @@ -265,10 +265,14 @@ export async function cam_slice(settings, widget, onupdate, ondone) { // silently preface op list with OpShadow if (isIndexed) { - if (activeOps.length === 0 || activeOps[0].type !== 'index') + if (activeOps.length === 0 || activeOps[0].type !== 'index') { opList.push(new OPS.index(state, { type: "index", index: 0 })); + opTot++; + + } } else { opList.push(new OPS.shadow(state, { type: "shadow", silent: true })); + opTot++; } // determing # of steps and step weighting for progress bar From 41ecf2951fd457199f78a6861c0d2ade823cfe91 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sat, 12 Jul 2025 18:51:26 -0400 Subject: [PATCH 13/28] restore indexed output, fix ops weight in slicing progress --- src/kiri/mode/cam/op-index.js | 4 ++++ src/kiri/mode/cam/op-shadow.js | 4 ++++ src/kiri/mode/cam/prepare.js | 2 +- src/kiri/mode/cam/slice.js | 4 ++-- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/kiri/mode/cam/op-index.js b/src/kiri/mode/cam/op-index.js index becd0441..d226aac0 100644 --- a/src/kiri/mode/cam/op-index.js +++ b/src/kiri/mode/cam/op-index.js @@ -7,6 +7,10 @@ export class OpIndex extends CamOp { super(state, op); } + weight() { + return 0.1; + } + async slice() { let { op, state } = this; if (!state.isIndexed) { diff --git a/src/kiri/mode/cam/op-shadow.js b/src/kiri/mode/cam/op-shadow.js index 91c6f91a..5c782636 100644 --- a/src/kiri/mode/cam/op-shadow.js +++ b/src/kiri/mode/cam/op-shadow.js @@ -20,6 +20,10 @@ class OpShadow extends CamOp { super(state, op); } + weight() { + return 0.1; + } + async slice(progress) { let state = this.state; let { ops, slicer, widget, unsafe, addSlices, shadowAt } = state; diff --git a/src/kiri/mode/cam/prepare.js b/src/kiri/mode/cam/prepare.js index b54b9feb..4ca6e2fc 100644 --- a/src/kiri/mode/cam/prepare.js +++ b/src/kiri/mode/cam/prepare.js @@ -318,7 +318,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { p.x + wmx, p.y + wmy, p.z + zadd - ) + ).setA(p.a); } /** diff --git a/src/kiri/mode/cam/slice.js b/src/kiri/mode/cam/slice.js index 6abf19f5..b117d74a 100644 --- a/src/kiri/mode/cam/slice.js +++ b/src/kiri/mode/cam/slice.js @@ -267,12 +267,12 @@ export async function cam_slice(settings, widget, onupdate, ondone) { if (isIndexed) { if (activeOps.length === 0 || activeOps[0].type !== 'index') { opList.push(new OPS.index(state, { type: "index", index: 0 })); - opTot++; + opTot += opList.peek().weight(); } } else { opList.push(new OPS.shadow(state, { type: "shadow", silent: true })); - opTot++; + opTot += opList.peek().weight(); } // determing # of steps and step weighting for progress bar From f314e3b40c17191ada7b351fc29773afc12677b7 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sat, 12 Jul 2025 19:11:39 -0400 Subject: [PATCH 14/28] fix topo4 and linear lathe with workers --- src/kiri/mode/cam/op-lathe.js | 4 ++-- src/kiri/mode/cam/topo3.js | 2 +- src/kiri/mode/cam/topo4.js | 33 ++++++++++++++++++++++----------- src/kiri/run/minion.js | 8 ++++++-- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/kiri/mode/cam/op-lathe.js b/src/kiri/mode/cam/op-lathe.js index b1b5349a..275b6906 100644 --- a/src/kiri/mode/cam/op-lathe.js +++ b/src/kiri/mode/cam/op-lathe.js @@ -2,7 +2,7 @@ import { CamOp } from './op.js'; import { Tool } from './tool.js'; -import { Topo4 } from './topo4.js'; +import { generate as topo4_generate } from './topo4.js'; import { newPoint } from '../../../geo/point.js'; function createFilter(op) { @@ -39,7 +39,7 @@ class OpLathe extends CamOp { let { addSlices } = state; let filter = createFilter(op); - this.topo = await Topo4({ + this.topo = await topo4_generate({ op, state, onupdate: (pct, msg) => { diff --git a/src/kiri/mode/cam/topo3.js b/src/kiri/mode/cam/topo3.js index 4116ac21..d45ae043 100644 --- a/src/kiri/mode/cam/topo3.js +++ b/src/kiri/mode/cam/topo3.js @@ -11,7 +11,7 @@ import { Tool } from './tool.js'; const RAD2DEG = 180 / Math.PI; const DEG2RAD = Math.PI / 180; -class Topo { +export class Topo { constructor() { } async generate(opt = {}) { diff --git a/src/kiri/mode/cam/topo4.js b/src/kiri/mode/cam/topo4.js index 2a430dce..7a4c39c6 100644 --- a/src/kiri/mode/cam/topo4.js +++ b/src/kiri/mode/cam/topo4.js @@ -17,7 +17,7 @@ function scale(fn, factor = 1, base = 0) { } } -class Topo { +export class Topo { constructor() { } async generate(opt = {}) { @@ -296,7 +296,7 @@ class Topo { } async latheWorker(onupdate) { - const { sliced, tool, zoff, leave } = this; + const { sliced, tool, zoff, leave, linear } = this; const rota = this.angle * DEG2RAD; const steps = (Math.PI * 2) / rota; @@ -319,19 +319,32 @@ class Topo { onupdate(count / steps); } - count = recs[0].heights.length / 3; + count = linear ? recs.length : recs[0].heights.length / 3; + // count = recs[0].heights.length / 3; while (count-- > 0) { let slice = newSlice(count); slice.camLines = [ newPolygon().setOpen() ]; paths.push(slice); } - for (let rec of recs) { - const { degrees, heights } = rec; - [...heights].group(3).forEach((a,i) => { - // progress each path 360 degrees to prevent A rolling backwards - paths[i].camLines[0].push( newPoint(a[0], a[1], a[2] + leave).setA(degrees + i * -360) ); + if (linear) { + recs.forEach((rec,i) => { + const { degrees, heights } = rec; + [...heights].group(3).forEach((a) => { + paths[i].camLines[0].push( newPoint(a[0], a[1], a[2] + leave).setA(degrees) ); + }); + if (i % 2 === 1) { + paths[i].camLines[0].reverse(); + } }); + } else { + for (let rec of recs) { + const { degrees, heights } = rec; + [...heights].group(3).forEach((a,i) => { + // progress each path 360 degrees to prevent A rolling backwards + paths[i].camLines[0].push( newPoint(a[0], a[1], a[2] + leave).setA(degrees + i * -360) ); + }); + } } for (let slice of paths) { @@ -395,8 +408,6 @@ class Topo { await Promise.all(promises); recs.sort((a, b) => { return b.angle - a.angle }); - // let linear = true; - count = linear ? recs.length : recs[0].heights.length / 3; while (count-- > 0) { let slice = newSlice(count); @@ -468,6 +479,6 @@ export function rotatePoints(lines, rot) { new THREE.BufferAttribute(lines, 3).applyMatrix4(rot); } -export function Topo4(opt) { +export async function generate(opt) { return new Topo().generate(opt); }; diff --git a/src/kiri/run/minion.js b/src/kiri/run/minion.js index 6d97d0c4..ae1c2920 100644 --- a/src/kiri/run/minion.js +++ b/src/kiri/run/minion.js @@ -15,7 +15,7 @@ import { sliceZ, sliceConnect } from '../../geo/slicer.js'; import { Slicer as cam_slicer } from '../mode/cam/slicer.js'; import { Slicer as topo_slicer } from '../mode/cam/slicer_topo.js'; import { Probe, Trace, raster_slice } from '../mode/cam/topo3.js'; -import { Topo4, rotatePoints } from '../mode/cam/topo4.js'; +import { Topo as Topo4, rotatePoints } from '../mode/cam/topo4.js'; import { wasm_ctrl } from '../../geo/wasm.js'; const clib = self.ClipperLib; @@ -54,9 +54,13 @@ function debug() { // console.log(`[${name}]`, ...arguments); } +let invalid = 0; const funcs = self.minion = { invalid(data, seq, cmd) { - console.error({ invalid_minion_command: cmd, data }); + if (invalid++ < 20) { + // prevent flooding console and killing session + console.error({ invalid_minion_command: cmd, data }); + } reply({ seq, error: `invalid command (${cmd})` }); }, From 57819b8c91fcac57d918e655d101c81695900ed8 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sun, 13 Jul 2025 12:30:17 -0400 Subject: [PATCH 15/28] notes update, requested ulio brix changes --- notes.md | 27 ++++----------------------- src/kiri/dev/fdm/ULIO.3D.BRIX.json | 6 +++--- src/kiri/dev/fdm/ULIO.3D.STEAM.json | 6 +++--- 3 files changed, 10 insertions(+), 29 deletions(-) diff --git a/notes.md b/notes.md index acd628ab..a7036e77 100644 --- a/notes.md +++ b/notes.md @@ -2,8 +2,6 @@ ## `C` cosmetic, `F` functional, `P` performance, `B` bug fix -* `F` experiment with structuredClone(), SharedArrayBuffer, createImageBitmap() -* `F` test #private fields, array.at() for negative indices * `F` option to hide platform when viewing from below # Kiri:Moto @@ -14,7 +12,6 @@ * `P` refactor vertex replacement and widget update matrix tracking * `P` duplicate objects should share same slice data unless rotated or scaled -* `P` allow selection to be decimated on demand (context menu?) * `P` move all persisted / workspace settings/data to IndexedDB (LS limitations) * `P` faster ray intersect https://github.com/gkjohnson/three-mesh-bvh/ * `P` try material clipping planes for slice range selection @@ -26,7 +23,7 @@ * `F` complete and expose grouping feature * `F` add svgnest-like arrange algorithm * `F` date column and sorting in recent files list -* `F` warn if part hanging in negative Z space or off bed in general +* `F` highlight part outside workspace bounds (like can neg Z shader) # FDM @@ -34,6 +31,7 @@ * `B` multi-extruder rendering of raft fails to offset the rest of the print * `B` multi-extruder purge blocks fail to generate properly for rafts +* `F` better thin-wall (like arachne) * `F` add scarf seams * `F` add 'random' and 'seam' start point options * `F` convert ranges to z offsets while continuing to show layer # @@ -52,12 +50,6 @@ * `P` extruder + filament max flow rate cap in planner * `P` solid fill the tops of supports for down facing flats -# FDM - SLA - -# `P` common support area detection (fork new fdm code) -* `P` prioritize supports by length of unsupported span. mandatory when circularity > X -* or % area of inner to outer poly is high (making it a thin shell) - # FDM - BELT * `B` auto bed resizing leaves the origin in the wrong place at first @@ -69,7 +61,6 @@ # CAM -* `B` roughing leave stock should also increase Z slightly (sqrt(2) * leave) * `B` rapid moves should be max of terrain zmax and last cut layer height (roughing) * `B` feed rate for next tool set before tool change (push/pop feed rates?) * `B` tab cuts cause loss of direction, parenting, depth, and it's hard to fix @@ -84,18 +75,14 @@ * `F` add `match faces` option in `outline` operation * `F` add {progress} substitution and maybe {time-remaining} if can be calc'd * `F` import and follow 2D paths (conformed like pocket contours) -* `F` add user-defined origin (issue #28) -* `F` intelligently turn circle hole pocket clear into spiral down * `F` add `plunge max` to contouring that can override z feed limit * `F` add lead-in milling (requires adding clamp / no go areas) * `F` add linear clearing strategy * `F` add adaptive clearing strategy -* `F` add ease-down support to trace op * `F` add support for tapered ball mills * `F` change color of line selection in trace op when not a closed poly * `F` limit cut depth to flute length of selected tool (or warn) * `F` validate muti-part layout and spacing exceeds largest outside tool diameter -* `F` parameterize dropping close points in prep.js. ensure long segments remain straight * `F` trapezoidal tabs in Z * `F` ease-in and ease-out especially on tab cut-out start/stop * `F` maintain several part orientations + op chains in a single profile @@ -104,14 +91,8 @@ * `P` outer corner arc moves * `P` log Z interpolation for contour XYZ moves * `P` option to start with the smallest poly by area on layer change -* `P` refactor slicing engine to add minion threading -* `P` port arc code from FDM export to CAM export * `P` redo all path route / planning in prepare to account for terrain before camOut -# SLA - -* `P` blit layers w/ 3JS ortho camera to canvas - # Laser * `F` add PLT / HP-GL output format (https://en.wikipedia.org/wiki/HP-GL) @@ -124,8 +105,9 @@ # Mesh:Tool -* font to path with https://github.com/paulzi/svg-text-to-path * add undo/redo +* add TextGeometry +* font to path with https://github.com/paulzi/svg-text-to-path * click to repair normals based on a known good (selected) face * send to Kiri:Moto workspace (or update model vertices in place) * better z snap using just vertexes from face intersected @@ -134,6 +116,5 @@ * add flatten/crush op: for z bottoms (or surfaces?) * allow setting model/group origin for scale/rotate * fix mirror to work with groups (just models currently) -* bounding box toggle should be global, not selection * add analyze results dialog * remove/hide auto-repair function diff --git a/src/kiri/dev/fdm/ULIO.3D.BRIX.json b/src/kiri/dev/fdm/ULIO.3D.BRIX.json index 113f20a7..0a1aadf9 100644 --- a/src/kiri/dev/fdm/ULIO.3D.BRIX.json +++ b/src/kiri/dev/fdm/ULIO.3D.BRIX.json @@ -104,16 +104,16 @@ "sliceFillWidth": 1, "sliceFillRate": 0, "sliceSupportEnable": false, - "firstSliceHeight": 0.25, + "firstSliceHeight": 0.5, "firstLayerFillRate": 20, "firstLayerLineMult": 1, "firstLayerNozzleTemp": 0, "firstLayerBedTemp": 0, "firstLayerBrimTrig": 0, "firstLayerBrimGap": 0, - "outputRaft": true, + "outputRaft": false, "outputRaftSpacing": 0.2, - "outputBrimCount": 5, + "outputBrimCount": 0, "outputBrimOffset": 2, "outputPurgeTower": 0, "outputInvertX": false, diff --git a/src/kiri/dev/fdm/ULIO.3D.STEAM.json b/src/kiri/dev/fdm/ULIO.3D.STEAM.json index b6239564..5270560e 100644 --- a/src/kiri/dev/fdm/ULIO.3D.STEAM.json +++ b/src/kiri/dev/fdm/ULIO.3D.STEAM.json @@ -104,16 +104,16 @@ "sliceFillWidth": 1, "sliceFillRate": 0, "sliceSupportEnable": false, - "firstSliceHeight": 0.25, + "firstSliceHeight": 0.5, "firstLayerFillRate": 20, "firstLayerLineMult": 1, "firstLayerNozzleTemp": 0, "firstLayerBedTemp": 0, "firstLayerBrimTrig": 0, "firstLayerBrimGap": 0, - "outputRaft": true, + "outputRaft": false, "outputRaftSpacing": 0.2, - "outputBrimCount": 5, + "outputBrimCount": 0, "outputBrimOffset": 2, "outputPurgeTower": 0, "outputInvertX": false, From cb98a056f867b2402dad7e2603cc17fcb1aed027 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sun, 13 Jul 2025 14:20:16 -0400 Subject: [PATCH 16/28] add work/pool url options in engine constructor --- src/kiri/run/engine.js | 12 +++++++++--- web/kiri/engine.html | 9 ++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/kiri/run/engine.js b/src/kiri/run/engine.js index 2b14bbe0..abd69b2a 100644 --- a/src/kiri/run/engine.js +++ b/src/kiri/run/engine.js @@ -10,7 +10,7 @@ import { load } from '../../load/file.js'; import { newWidget } from '../core/widget.js'; class Engine { - constructor() { + constructor({ workURL, poolURL } = {}) { this.widget = newWidget(); this.settings = { mode: "FDM", @@ -22,7 +22,13 @@ class Engine { widget: { [this.widget.id]: {} } }; this.listener = () => { }; - client.start(); + try { + client.setWorkPath(workURL); + client.setPoolPath(poolURL); + client.restart(); + } catch (error) { + console.log({ error }); + } } load(url) { @@ -190,7 +196,7 @@ class Engine { } export function newEngine() { - return new Engine(); + return new Engine(...arguments); } export { Engine }; diff --git a/web/kiri/engine.html b/web/kiri/engine.html index 4f298f3d..da417539 100644 --- a/web/kiri/engine.html +++ b/web/kiri/engine.html @@ -88,8 +88,7 @@ - +