kiri/app renames and reorgs

This commit is contained in:
Stewart Allen 2025-12-25 10:35:49 -05:00
commit 5bd67545dc
23 changed files with 486 additions and 499 deletions

5
.gitignore vendored
View file

@ -1,5 +1,7 @@
*.pem
.DS_Store
.bache
.bcache
.claude
.docusaurus
.env
alt
@ -16,4 +18,5 @@ src/ext/jszip-esm.js
src/ext/quickjs.js
src/ext/three.js
tmp
tmp_*/
web/boot/bundle*

View file

@ -9,7 +9,7 @@ import web from '../../moto/webui.js';
import { beta, version } from '../../moto/license.js';
import { broker } from '../../moto/broker.js';
import { client as work } from './client.js';
import { client as workers } from './workers.js';
import { consts, COLOR as color, LISTS as lists } from '../core/consts.js';
import { device, devices } from './devices.js';
import { functions } from './function.js';
@ -17,8 +17,8 @@ import { group as groupModule } from './groups.js';
import { help as helpModule } from './help.js';
import { image as imageModule } from './image.js';
import { Index } from '../../data/index.js';
import { LANG } from './lang.js';
import { local } from './local-storage.js';
import { LANG } from './language.js';
import { local } from './local.js';
import { local as dataLocal } from '../../data/local.js';
import { modal } from './modal.js';
import { mode as modeModule, process as processModule } from './mode.js';
@ -26,17 +26,17 @@ import { newWidget } from '../core/widget.js';
import { noop, ajax, o2js, js2o, utils } from '../core/utils.js';
import { openFiles } from './files.js';
import { platform } from './platform.js';
import { selection } from './select.js';
import { selection } from './selected.js';
import { settingsUI } from './config/dialog.js';
import { showDevices } from './devices.js';
import { showTools } from '../mode/cam/tools.js';
import { space as SPACE } from '../../moto/space.js';
import { stats } from './stats.js';
import { types as load } from '../../load/file.js';
import { UI } from './component.js';
import { UI } from './inputs.js';
import { updateTool } from '../mode/cam/tools.js';
import { util as utilModule } from './util.js';
import { view as viewModule } from './view-state.js';
import { view as viewModule } from './viewmode.js';
import { visuals } from './visuals.js';
import { widgets } from '../core/widgets.js';
import { workspace } from './workspace.js';
@ -74,7 +74,7 @@ export const api = {
dec() { api.event.emit("busy", --busyVal) }
},
catalog: FILES,
client: work,
client: workers,
clip(text) {
navigator.clipboard
.writeText(text)
@ -219,7 +219,7 @@ export const api = {
visuals,
web,
widgets,
work,
work: workers,
};
// allow widget to straddle client / worker FOR NOW

View file

@ -1,8 +1,8 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { $ } from '../../moto/webui.js';
import { api } from './api.js';
import { settings as set_ctrl } from './config/manager.js';
import { $ } from '../../../moto/webui.js';
import { api } from '../api.js';
import { settings as set_ctrl } from './manager.js';
function settings() {
return api.conf.get();

View file

@ -2,7 +2,7 @@
import { $ } from '../../moto/webui.js';
import { api } from './api.js';
import { client } from './client.js';
import { client } from './workers.js';
import { local } from '../../data/local.js';
import { util } from '../../geo/base.js';
import { MODES } from '../core/consts.js';

View file

@ -1,7 +1,7 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { api } from './api.js';
import { client } from './client.js';
import { client } from './workers.js';
import { codec } from '../core/codec.js';
import { space } from '../../moto/space.js';
import { COLOR, PMODES } from '../core/consts.js';

View file

@ -1,6 +1,6 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { selection } from './select.js';
import { selection } from './selected.js';
import { Widget } from '../core/widget.js';
function groupMerge() {

View file

@ -11,7 +11,7 @@ import { init as initLaser } from '../mode/laser/init-ui.js';
import { init as initSLA } from '../mode/sla/init-ui.js';
import { init as initWEDM } from '../mode/wedm/init-ui.js';
import { init as initWJET } from '../mode/wjet/init-ui.js';
import { interact } from './interact.js';
import { interact } from './mouse.js';
import { keyboard } from './keyboard.js';
import { local as sdb } from '../../data/local.js';
import { menu as menuCAM } from '../mode/cam/init-menu.js';
@ -20,9 +20,8 @@ import { menu as menuLaser } from '../mode/laser/init-menu.js';
import { menu as menuSLA } from '../mode/sla/init-menu.js';
import { modal } from './modal.js';
import { preferences } from './preferences.js';
import { selectionTools } from './selection-tools.js';
import { settings as set_ctrl } from './config/manager.js';
import { settingsOps } from './settings-ops.js';
import { settingsOps } from './config/settings.js';
import { slider } from './slider.js';
import { space } from '../../moto/space.js';
import { VIEWS, MODES, SEED } from '../core/consts.js';
@ -32,6 +31,7 @@ import STACKS from './stacks.js';
let { SETUP } = api.const,
{ CAM, SLA, FDM, LASER, DRAG, WJET, WEDM } = MODES,
{ catalog, platform, selection, stats } = api,
{ input_binding, input_position, input_rotate } = selection,
DOC = self.document,
WIN = self.window,
LANG = api.language.current,
@ -516,7 +516,7 @@ function init_one() {
VIEWS,
DOC,
WIN,
rotateInputSelection: selectionTools.rotateInputSelection,
rotateInputSelection: input_rotate,
settingsLoad: settingsOps.settingsLoad
});
@ -587,7 +587,7 @@ function init_one() {
]);
// Setup selection transformation bindings
selectionTools.setupSelectionBindings(ui);
input_binding(ui);
$('lab-axis').onclick = () => {
ui.lockX.checked =
@ -684,4 +684,4 @@ function init_one() {
});
};
export { onBooleanClick, updateTool, init_one };
export { onBooleanClick, init_one as init_input };

292
src/kiri/app/init-sync.js Normal file
View file

@ -0,0 +1,292 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { $ } from '../../moto/webui.js';
import { api } from './api.js';
import { beta, version } from '../../moto/license.js';
import { fileOps } from './file-ops.js';
import { local as sdb } from '../../data/local.js';
import { preferences } from './preferences.js';
import { settings as set_ctrl } from './config/manager.js';
import { settingsOps } from './config/settings.js';
import { space } from '../../moto/space.js';
import { VIEWS } from '../core/consts.js';
const DOC = self.document;
const WIN = self.window;
const proto = location.protocol;
const { SETUP, LOCAL } = api.const;
const { platform, client, catalog, stats } = api;
const LANG = api.language.current;
const ui = api.ui;
const STARTMODE = SETUP.sm && SETUP.sm.length === 1 ? SETUP.sm[0] : null;
function settings() {
return api.conf.get();
}
// SECOND STAGE INIT AFTER UI RESTORED
export async function init_sync() {
api.event.emit('init.two');
// load script extensions
if (SETUP.s) SETUP.s.forEach(function(lib) {
let scr = DOC.createElement('script');
scr.setAttribute('async', true);
scr.setAttribute('defer', true);
scr.setAttribute('src',`/code/${lib}.js`);
DOC.body.appendChild(scr);
stats.add('load_'+lib);
api.event.emit('load.lib', lib);
});
// override stored settings
if (SETUP.v) SETUP.v.forEach(function(kv) {
kv = kv.split('=');
sdb.setItem(kv[0],kv[1]);
});
// import octoprint settings
if (SETUP.ophost) {
let ohost = api.const.OCTO = {
host: SETUP.ophost[0],
apik: SETUP.opkey ? SETUP.opkey[0] : ''
};
sdb['octo-host'] = ohost.host;
sdb['octo-apik'] = ohost.apik;
console.log({octoprint:ohost});
}
// load workspace from url
if (SETUP.wrk) {
set_ctrl.import_url(`${proto}//${SETUP.wrk[0]}`, false);
}
// load an object from url
if (SETUP.load) {
console.log({load:SETUP});
api.platform.load_url(`${proto}//${SETUP.load[0]}`);
}
// bind this to UI so main can call it on settings import
ui.sync = ui_sync;
ui_sync();
// clear alerts as they build up
setInterval(api.event.alerts, 1000);
// add hide-alerts-on-alert-click
ui.alert.dialog.onclick = function() {
api.event.alerts(true);
};
// enable modal hiding
$('mod-x').onclick = api.modal.hide;
if (!SETUP.s) console.log(`kiri | init main | ${version}`);
// send init-done event
api.event.emit('init-done', stats);
// show gdpr if it's never been seen and we're not iframed
const isLocal = LOCAL || WIN.location.host.split(':')[0] === 'localhost';
if (!sdb.gdpr && WIN.self === WIN.top && !SETUP.debug && !isLocal) {
$('gdpr').style.display = 'flex';
}
// warn of degraded functionality when SharedArrayBuffers are missing
if (api.feature.work_alerts && !window.SharedArrayBuffer) {
api.alerts.show("The security context of this", 10);
api.alerts.show("Window blocks important functionality.", 10);
api.alerts.show("Try a Chromium-base Browser instead", 10);
}
// add keyboard focus handler (must use for iframes)
WIN.addEventListener('load', function () {
WIN.focus();
DOC.body.addEventListener('click', function() {
WIN.focus();
},false);
});
// dismiss gdpr alert
$('gotit').onclick = () => {
$('gdpr').style.display = 'none';
sdb.gdpr = Date.now();
};
// Setup navigation button bindings
setup_keybd_nav(ui, WIN, LANG);
// ui.modal.onclick = api.modal.hide;
ui.modalBox.onclick = (ev) => { ev.stopPropagation() };
// add app name hover info
$('app-info').innerText = version;
// show topline separator when iframed
try { if (WIN.self !== WIN.top) $('top-sep').style.display = 'flex' } catch (e) { console.log(e) }
// warn users they are running a beta release
if (beta && beta > 0 && sdb.kiri_beta != beta) {
api.show.alert("CAUTION");
api.show.alert("this is a development release");
api.show.alert("and may not function properly");
sdb.kiri_beta = beta;
}
// hide url params but preserve version root (when present)
let wlp = WIN.location.pathname;
let kio = wlp.indexOf('/kiri/');
if (kio >= 0) {
history.replaceState({}, '', wlp.substring(0,kio + 6));
}
// lift curtain
$('curtain').style.display = 'none';
}
function ui_sync() {
const current = settings();
const control = current.controller;
if (!control.devel) {
// TODO: hide thin type 3 during development
api.const.LISTS.thin.length = 3;
}
platform.deselect();
catalog.addFileListener(fileOps.updateCatalog);
space.view.setZoom(control.reverseZoom, control.zoomSpeed);
space.platform.setGridZOff(undefined);
space.platform.setZOff(0.05);
space.view.setProjection(control.ortho ? 'orthographic' : 'perspective');
// restore UI state from settings
ui.antiAlias.checked = control.antiAlias;
ui.assembly.checked = control.assembly;
ui.autoLayout.checked = control.autoLayout;
ui.autoSave.checked = control.autoSave;
ui.devel.checked = control.devel;
ui.freeLayout.checked = control.freeLayout;
ui.healMesh.checked = control.healMesh;
ui.manifold.checked = control.manifold;
ui.ortho.checked = control.ortho;
ui.reverseZoom.checked = control.reverseZoom;
ui.showOrigin.checked = control.showOrigin;
ui.showRulers.checked = control.showRulers;
ui.showSpeeds.checked = control.showSpeeds;
ui.spaceRandoX.checked = control.spaceRandoX;
// ui.threaded.checked = setThreaded(control.threaded);
ui.webGPU.checked = control.webGPU;
preferences.setThreaded(true);
preferences.lineTypeSave();
preferences.detailSave();
preferences.updateStats();
// optional set-and-lock mode (hides mode menu)
let SETMODE = SETUP.mode ? SETUP.mode[0] : null;
// optional set-and-lock device (hides device menu)
let DEVNAME = SETUP.dev ? SETUP.dev[0] : null;
// setup default mode and enable mode locking, if set
api.mode.set(SETMODE || STARTMODE || current.mode, SETMODE);
// fill device list
api.devices.refresh();
// update ui fields from settings
api.conf.update_fields();
// default to ARRANGE view mode
api.view.set(VIEWS.ARRANGE);
// add ability to override (todo: restore?)
// api.show.controls(api.feature.controls);
// update everything dependent on the platform size
platform.update_size();
// load wasm if indicated
client.wasm(control.assembly === true);
}
function setup_keybd_nav(ui, WIN, LANG) {
const { selection } = api;
// bind interface action elements
$('export-support-a').onclick = (ev) => { ev.stopPropagation(); api.modal.show('don8') };
$('mode-device').onclick = api.show.devices;
$('mode-profile').onclick = settingsOps.settingsLoad;
$('mode-fdm').onclick = () => api.mode.set('FDM');
$('mode-cam').onclick = () => api.mode.set('CAM');
$('mode-sla').onclick = () => api.mode.set('SLA');
$('mode-laser').onclick = () => api.mode.set('LASER');
$('mode-drag').onclick = () => api.mode.set('DRAG');
$('mode-wjet').onclick = () => api.mode.set('WJET');
$('mode-wedm').onclick = () => api.mode.set('WEDM');
$('set-device').onclick = (ev) => { ev.stopPropagation(); api.show.devices() };
$('set-profs').onclick = (ev) => { ev.stopPropagation(); api.conf.show() };
$('set-tools').onclick = (ev) => { ev.stopPropagation(); api.show.tools() };
$('set-prefs').onclick = (ev) => { ev.stopPropagation(); api.modal.show('prefs') };
ui.acct.help.onclick = (ev) => { ev.stopPropagation(); api.help.show() };
ui.acct.don8.onclick = (ev) => { ev.stopPropagation(); api.modal.show('don8') };
ui.acct.mesh.onclick = (ev) => { ev.stopPropagation(); WIN.location = "/mesh" };
ui.acct.export.onclick = (ev) => { ev.stopPropagation(); settingsOps.profileExport() };
ui.acct.export.title = LANG.acct_xpo;
$('file-new').onclick = (ev) => { ev.stopPropagation(); settingsOps.workspaceNew() };
$('file-recent').onclick = () => { api.modal.show('files') };
$('file-import').onclick = (ev) => { api.event.import(ev); };
ui.func.slice.onclick = (ev) => { ev.stopPropagation(); api.function.slice() };
ui.func.preview.onclick = (ev) => { ev.stopPropagation(); api.function.print() };
ui.func.animate.onclick = (ev) => { ev.stopPropagation(); api.function.animate() };
ui.func.export.onclick = (ev) => { ev.stopPropagation(); api.function.export() };
$('view-arrange').onclick = api.platform.layout;
$('view-top').onclick = space.view.top;
$('view-home').onclick = space.view.home;
$('view-front').onclick = space.view.front;
$('view-back').onclick = space.view.back;
$('view-left').onclick = space.view.left;
$('view-right').onclick = space.view.right;
$('unrotate').onclick = () => {
api.widgets.for(w => w.unrotate());
selection.update_info();
};
// attach button handlers to support targets
for (let btn of ["don8pt","don8gh","don8pp"]) {
$(btn).onclick = (ev) => {
window.open(ev.target.children[0].href);
}
}
// rotation buttons
let d = (Math.PI / 180);
$('rot_x_lt').onclick = () => { selection.rotate(-d * $('rot_x').value,0,0) };
$('rot_x_gt').onclick = () => { selection.rotate( d * $('rot_x').value,0,0) };
$('rot_y_lt').onclick = () => { selection.rotate(0,-d * $('rot_y').value,0) };
$('rot_y_gt').onclick = () => { selection.rotate(0, d * $('rot_y').value,0) };
$('rot_z_lt').onclick = () => { selection.rotate(0,0, d * $('rot_z').value) };
$('rot_z_gt').onclick = () => { selection.rotate(0,0,-d * $('rot_z').value) };
// rendering options
$('render-edges').onclick = () => { api.view.edges({ toggle: true }); api.conf.save() };
$('render-ghost').onclick = () => { api.view.wireframe(false, 0, api.view.is_arrange() ? 0.4 : 0.25); };
$('render-wire').onclick = () => { api.view.wireframe(true, 0, api.space.is_dark() ? 0.25 : 0.5); };
$('render-solid').onclick = () => { api.view.wireframe(false, 0, 1); };
$('mesh-export-stl').onclick = () => { settingsOps.objectsExport('stl') };
$('mesh-export-obj').onclick = () => { settingsOps.objectsExport('obj') };
$('mesh-merge').onclick = selection.merge;
$('mesh-split').onclick = selection.isolateBodies;
$('context-duplicate').onclick = selection.duplicate;
$('context-mirror').onclick = selection.mirror;
$('context-layflat').onclick = () => { api.event.emit("tool.mesh.lay-flat") };
$('context-lefty').onclick = () => { api.event.emit("tool.mesh.lefty") };
$('context-setfocus').onclick = () => {
api.event.emit(
"tool.camera.focus",
ev => api.space.set_focus(undefined, ev.object.point)
);
};
$('context-contents').onclick = () => { api.SPACE.view.fit() };
$('view-fit').onclick = () => { api.SPACE.view.fit() };
$('wassup').onmouseover = () => { $('suppopp').classList.remove('hide') };
}

View file

@ -1,212 +0,0 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { $ } from '../../moto/webui.js';
import { api } from './api.js';
import { beta, version } from '../../moto/license.js';
import { fileOps } from './file-ops.js';
import { local as sdb } from '../../data/local.js';
import { navigation } from './navigation.js';
import { preferences } from './preferences.js';
import { settings as set_ctrl } from './config/manager.js';
import { space } from '../../moto/space.js';
import { VIEWS } from '../core/consts.js';
const DOC = self.document;
const WIN = self.window;
const proto = location.protocol;
const { SETUP, LOCAL } = api.const;
const { platform, client, catalog, stats } = api;
const LANG = api.language.current;
const ui = api.ui;
const STARTMODE = SETUP.sm && SETUP.sm.length === 1 ? SETUP.sm[0] : null;
function settings() {
return api.conf.get();
}
// SECOND STAGE INIT AFTER UI RESTORED
export async function init_two() {
api.event.emit('init.two');
// load script extensions
if (SETUP.s) SETUP.s.forEach(function(lib) {
let scr = DOC.createElement('script');
scr.setAttribute('async', true);
scr.setAttribute('defer', true);
scr.setAttribute('src',`/code/${lib}.js`);
DOC.body.appendChild(scr);
stats.add('load_'+lib);
api.event.emit('load.lib', lib);
});
// override stored settings
if (SETUP.v) SETUP.v.forEach(function(kv) {
kv = kv.split('=');
sdb.setItem(kv[0],kv[1]);
});
// import octoprint settings
if (SETUP.ophost) {
let ohost = api.const.OCTO = {
host: SETUP.ophost[0],
apik: SETUP.opkey ? SETUP.opkey[0] : ''
};
sdb['octo-host'] = ohost.host;
sdb['octo-apik'] = ohost.apik;
console.log({octoprint:ohost});
}
// load workspace from url
if (SETUP.wrk) {
set_ctrl.import_url(`${proto}//${SETUP.wrk[0]}`, false);
}
// load an object from url
if (SETUP.load) {
console.log({load:SETUP});
api.platform.load_url(`${proto}//${SETUP.load[0]}`);
}
// bind this to UI so main can call it on settings import
ui.sync = function() {
const current = settings();
const control = current.controller;
if (!control.devel) {
// TODO: hide thin type 3 during development
api.const.LISTS.thin.length = 3;
}
platform.deselect();
catalog.addFileListener(fileOps.updateCatalog);
space.view.setZoom(control.reverseZoom, control.zoomSpeed);
space.platform.setGridZOff(undefined);
space.platform.setZOff(0.05);
space.view.setProjection(control.ortho ? 'orthographic' : 'perspective');
// restore UI state from settings
ui.antiAlias.checked = control.antiAlias;
ui.assembly.checked = control.assembly;
ui.autoLayout.checked = control.autoLayout;
ui.autoSave.checked = control.autoSave;
ui.devel.checked = control.devel;
ui.freeLayout.checked = control.freeLayout;
ui.healMesh.checked = control.healMesh;
ui.manifold.checked = control.manifold;
ui.ortho.checked = control.ortho;
ui.reverseZoom.checked = control.reverseZoom;
ui.showOrigin.checked = control.showOrigin;
ui.showRulers.checked = control.showRulers;
ui.showSpeeds.checked = control.showSpeeds;
ui.spaceRandoX.checked = control.spaceRandoX;
// ui.threaded.checked = setThreaded(control.threaded);
ui.webGPU.checked = control.webGPU;
preferences.setThreaded(true);
preferences.lineTypeSave();
preferences.detailSave();
preferences.updateStats();
// optional set-and-lock mode (hides mode menu)
let SETMODE = SETUP.mode ? SETUP.mode[0] : null;
// optional set-and-lock device (hides device menu)
let DEVNAME = SETUP.dev ? SETUP.dev[0] : null;
// setup default mode and enable mode locking, if set
api.mode.set(SETMODE || STARTMODE || current.mode, SETMODE);
// fill device list
api.devices.refresh();
// update ui fields from settings
api.conf.update_fields();
// default to ARRANGE view mode
api.view.set(VIEWS.ARRANGE);
// add ability to override (todo: restore?)
// api.show.controls(api.feature.controls);
// update everything dependent on the platform size
platform.update_size();
// load wasm if indicated
client.wasm(control.assembly === true);
};
ui.sync();
// clear alerts as they build up
setInterval(api.event.alerts, 1000);
// add hide-alerts-on-alert-click
ui.alert.dialog.onclick = function() {
api.event.alerts(true);
};
// enable modal hiding
$('mod-x').onclick = api.modal.hide;
if (!SETUP.s) console.log(`kiri | init main | ${version}`);
// send init-done event
api.event.emit('init-done', stats);
// show gdpr if it's never been seen and we're not iframed
const isLocal = LOCAL || WIN.location.host.split(':')[0] === 'localhost';
if (!sdb.gdpr && WIN.self === WIN.top && !SETUP.debug && !isLocal) {
$('gdpr').style.display = 'flex';
}
// warn of degraded functionality when SharedArrayBuffers are missing
if (api.feature.work_alerts && !window.SharedArrayBuffer) {
api.alerts.show("The security context of this", 10);
api.alerts.show("Window blocks important functionality.", 10);
api.alerts.show("Try a Chromium-base Browser instead", 10);
}
// add keyboard focus handler (must use for iframes)
WIN.addEventListener('load', function () {
WIN.focus();
DOC.body.addEventListener('click', function() {
WIN.focus();
},false);
});
// dismiss gdpr alert
$('gotit').onclick = () => {
$('gdpr').style.display = 'none';
sdb.gdpr = Date.now();
};
// Setup navigation button bindings
navigation.setupNavigation(ui, WIN, LANG);
// ui.modal.onclick = api.modal.hide;
ui.modalBox.onclick = (ev) => { ev.stopPropagation() };
// add app name hover info
$('app-info').innerText = version;
// show topline separator when iframed
try { if (WIN.self !== WIN.top) $('top-sep').style.display = 'flex' } catch (e) { console.log(e) }
// warn users they are running a beta release
if (beta && beta > 0 && sdb.kiri_beta != beta) {
api.show.alert("CAUTION");
api.show.alert("this is a development release");
api.show.alert("and may not function properly");
sdb.kiri_beta = beta;
}
// hide url params but preserve version root (when present)
let wlp = WIN.location.pathname;
let kio = wlp.indexOf('/kiri/');
if (kio >= 0) {
history.replaceState({}, '', wlp.substring(0,kio + 6));
}
// lift curtain
$('curtain').style.display = 'none';
}

View file

@ -5,7 +5,7 @@ import { api } from './api.js';
import { consts } from '../core/consts.js';
import { modal } from './modal.js';
import { platform } from './platform.js';
import { selection } from './select.js';
import { selection } from './selected.js';
import { settings } from './config/manager.js';
const { MODES, VIEWS } = consts;

View file

@ -1,89 +0,0 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { $ } from '../../moto/webui.js';
import { api } from './api.js';
import { space } from '../../moto/space.js';
import { settingsOps } from './settings-ops.js';
const { selection } = api;
function setupNavigation(ui, WIN, LANG) {
// bind interface action elements
$('export-support-a').onclick = (ev) => { ev.stopPropagation(); api.modal.show('don8') };
$('mode-device').onclick = api.show.devices;
$('mode-profile').onclick = settingsOps.settingsLoad;
$('mode-fdm').onclick = () => api.mode.set('FDM');
$('mode-cam').onclick = () => api.mode.set('CAM');
$('mode-sla').onclick = () => api.mode.set('SLA');
$('mode-laser').onclick = () => api.mode.set('LASER');
$('mode-drag').onclick = () => api.mode.set('DRAG');
$('mode-wjet').onclick = () => api.mode.set('WJET');
$('mode-wedm').onclick = () => api.mode.set('WEDM');
$('set-device').onclick = (ev) => { ev.stopPropagation(); api.show.devices() };
$('set-profs').onclick = (ev) => { ev.stopPropagation(); api.conf.show() };
$('set-tools').onclick = (ev) => { ev.stopPropagation(); api.show.tools() };
$('set-prefs').onclick = (ev) => { ev.stopPropagation(); api.modal.show('prefs') };
ui.acct.help.onclick = (ev) => { ev.stopPropagation(); api.help.show() };
ui.acct.don8.onclick = (ev) => { ev.stopPropagation(); api.modal.show('don8') };
ui.acct.mesh.onclick = (ev) => { ev.stopPropagation(); WIN.location = "/mesh" };
ui.acct.export.onclick = (ev) => { ev.stopPropagation(); settingsOps.profileExport() };
ui.acct.export.title = LANG.acct_xpo;
$('file-new').onclick = (ev) => { ev.stopPropagation(); settingsOps.workspaceNew() };
$('file-recent').onclick = () => { api.modal.show('files') };
$('file-import').onclick = (ev) => { api.event.import(ev); };
ui.func.slice.onclick = (ev) => { ev.stopPropagation(); api.function.slice() };
ui.func.preview.onclick = (ev) => { ev.stopPropagation(); api.function.print() };
ui.func.animate.onclick = (ev) => { ev.stopPropagation(); api.function.animate() };
ui.func.export.onclick = (ev) => { ev.stopPropagation(); api.function.export() };
$('view-arrange').onclick = api.platform.layout;
$('view-top').onclick = space.view.top;
$('view-home').onclick = space.view.home;
$('view-front').onclick = space.view.front;
$('view-back').onclick = space.view.back;
$('view-left').onclick = space.view.left;
$('view-right').onclick = space.view.right;
$('unrotate').onclick = () => {
api.widgets.for(w => w.unrotate());
selection.update_info();
};
// attach button handlers to support targets
for (let btn of ["don8pt","don8gh","don8pp"]) {
$(btn).onclick = (ev) => {
window.open(ev.target.children[0].href);
}
}
// rotation buttons
let d = (Math.PI / 180);
$('rot_x_lt').onclick = () => { selection.rotate(-d * $('rot_x').value,0,0) };
$('rot_x_gt').onclick = () => { selection.rotate( d * $('rot_x').value,0,0) };
$('rot_y_lt').onclick = () => { selection.rotate(0,-d * $('rot_y').value,0) };
$('rot_y_gt').onclick = () => { selection.rotate(0, d * $('rot_y').value,0) };
$('rot_z_lt').onclick = () => { selection.rotate(0,0, d * $('rot_z').value) };
$('rot_z_gt').onclick = () => { selection.rotate(0,0,-d * $('rot_z').value) };
// rendering options
$('render-edges').onclick = () => { api.view.edges({ toggle: true }); api.conf.save() };
$('render-ghost').onclick = () => { api.view.wireframe(false, 0, api.view.is_arrange() ? 0.4 : 0.25); };
$('render-wire').onclick = () => { api.view.wireframe(true, 0, api.space.is_dark() ? 0.25 : 0.5); };
$('render-solid').onclick = () => { api.view.wireframe(false, 0, 1); };
$('mesh-export-stl').onclick = () => { settingsOps.objectsExport('stl') };
$('mesh-export-obj').onclick = () => { settingsOps.objectsExport('obj') };
$('mesh-merge').onclick = selection.merge;
$('mesh-split').onclick = selection.isolateBodies;
$('context-duplicate').onclick = selection.duplicate;
$('context-mirror').onclick = selection.mirror;
$('context-layflat').onclick = () => { api.event.emit("tool.mesh.lay-flat") };
$('context-lefty').onclick = () => { api.event.emit("tool.mesh.lefty") };
$('context-setfocus').onclick = () => {
api.event.emit(
"tool.camera.focus",
ev => api.space.set_focus(undefined, ev.object.point)
);
};
$('context-contents').onclick = () => { api.SPACE.view.fit() };
$('view-fit').onclick = () => { api.SPACE.view.fit() };
$('wassup').onmouseover = () => { $('suppopp').classList.remove('hide') };
}
export const navigation = {
setupNavigation
};

View file

@ -9,6 +9,10 @@ import { encode as stlEncode } from '../../load/stl.js';
const selectedMeshes = [];
function updateTool(ev) {
api.tool.update(ev);
}
function for_groups(fn) {
let groups = widgets(true).map(w => w.group).uniq();
for (let group of groups) {
@ -47,6 +51,20 @@ function move(x, y, z, abs) {
api.space.auto_save();
}
function get_bounds() {
// Helper to compute bounds of selected meshes
const THREE = self.THREE;
let bounds = new THREE.Box3();
selection.for_meshes(mesh => {
let box = mesh.getBoundingBox().clone();
let pos = mesh.widget.mesh.position;
box.min.add(pos);
box.max.add(pos);
bounds = bounds.union(box);
});
return bounds;
}
function update_info() {
const ui = api.ui;
let bounds = new THREE.Box3(), track;
@ -299,19 +317,155 @@ function setDisabled(bool) {
api.platform.update_selected();
}
function settings() {
return api.conf.get();
}
function input_rotate() {
if (selection.meshes().length === 0) {
api.show.alert("select object to rotate");
return;
}
api.uc.prompt("Enter X,Y,Z degrees of rotation","").then(coord => {
coord = (coord || '').split(',');
let prod = Math.PI / 180,
x = parseFloat(coord[0] || 0.0) * prod,
y = parseFloat(coord[1] || 0.0) * prod,
z = parseFloat(coord[2] || 0.0) * prod;
selection.rotate(x, y, z);
});
}
function input_position() {
if (selection.meshes().length === 0) {
api.show.alert("select object to position");
return;
}
let current = settings(),
{ device, process} = current,
center = process.ctOriginCenter || process.camOriginCenter || device.bedRound || device.originCenter,
bounds = selection.get_bounds();
api.uc.prompt("Enter X,Y coordinates for selection","").then(coord => {
coord = (coord || '').split(',');
let x = parseFloat(coord[0] || 0.0),
y = parseFloat(coord[1] || 0.0),
z = parseFloat(coord[2] || 0.0);
if (!center) {
x = x - device.bedWidth/2 + (bounds.max.x - bounds.min.x)/2;
y = y - device.bedDepth/2 + (bounds.max.y - bounds.min.y)/2
}
selection.move(x, y, z, true);
});
}
function input_resize(e, ui) {
let dv = parseFloat(e.target.value || 1),
pv = parseFloat(e.target.was || 1),
ra = dv / pv,
xv = parseFloat(ui.sizeX.was ?? ui.scaleX.value) || 1,
yv = parseFloat(ui.sizeY.was ?? ui.scaleY.value) || 1,
zv = parseFloat(ui.sizeZ.was ?? ui.scaleZ.value) || 1,
ta = e.target,
xc = ui.lockX.checked,
yc = ui.lockY.checked,
zc = ui.lockZ.checked,
xt = ta === ui.sizeX,
yt = ta === ui.sizeY,
zt = ta === ui.sizeZ,
tl = (xt && xc) || (yt && yc) || (zt && zc),
xr = ((tl && xc) || (!tl && xt) ? ra : 1),
yr = ((tl && yc) || (!tl && yt) ? ra : 1),
zr = ((tl && zc) || (!tl && zt) ? ra : 1);
// prevent null scale
if (xv * xr < 0.1 || yv * yr < 0.1 || zv * zr < 0.1) {
api.alerts.show('invalid scale value');
return;
}
selection.scale(xr,yr,zr);
ui.sizeX.was = ui.sizeX.value = xv * xr;
ui.sizeY.was = ui.sizeY.value = yv * yr;
ui.sizeZ.was = ui.sizeZ.value = zv * zr;
}
function input_scale(e, ui) {
let dv = parseFloat(e.target.value || 1),
pv = parseFloat(e.target.was || 1),
ra = dv / pv,
xv = parseFloat(ui.scaleX.was ?? ui.scaleX.value) || 1,
yv = parseFloat(ui.scaleY.was ?? ui.scaleY.value) || 1,
zv = parseFloat(ui.scaleZ.was ?? ui.scaleY.value) || 1,
ta = e.target,
xc = ui.lockX.checked,
yc = ui.lockY.checked,
zc = ui.lockZ.checked,
xt = ta === ui.scaleX,
yt = ta === ui.scaleY,
zt = ta === ui.scaleZ,
tl = (xt && xc) || (yt && yc) || (zt && zc),
xr = ((tl && xc) || (!tl && xt) ? ra : 1),
yr = ((tl && yc) || (!tl && yt) ? ra : 1),
zr = ((tl && zc) || (!tl && zt) ? ra : 1);
// prevent null scale
if (xv * xr < 0.1 || yv * yr < 0.1 || zv * zr < 0.1) {
api.alerts.show('invalid scale value');
return;
}
selection.scale(xr,yr,zr);
ui.scaleX.was = ui.scaleX.value = xv * xr;
ui.scaleY.was = ui.scaleY.value = yv * yr;
ui.scaleZ.was = ui.scaleZ.value = zv * zr;
}
function parse_as_float(e) {
e.target.value = parseFloat(e.target.value) || 0;
}
function input_binding(ui) {
// on enter but not on blur
space.event.onEnterKey([
ui.scaleX, (e) => input_scale(e, ui),
ui.scaleY, (e) => input_scale(e, ui),
ui.scaleZ, (e) => input_scale(e, ui),
ui.sizeX, (e) => input_resize(e, ui),
ui.sizeY, (e) => input_resize(e, ui),
ui.sizeZ, (e) => input_resize(e, ui),
]);
// on enter and blur
space.event.onEnterKey([
ui.toolName, updateTool,
ui.toolNum, updateTool,
ui.toolFluteDiam, updateTool,
ui.toolFluteLen, updateTool,
ui.toolShaftDiam, updateTool,
ui.toolShaftLen, updateTool,
ui.toolTaperTip, updateTool,
ui.toolTaperAngle, updateTool,
$('rot_x'), parse_as_float,
$('rot_y'), parse_as_float,
$('rot_z'), parse_as_float
], true);
}
// extend API (api.selection)
export const selection = {
move,
merge,
isolateBodies,
scale,
rotate,
mirror,
duplicate,
for_groups,
for_meshes,
for_status,
for_widgets,
get_bounds,
input_binding,
input_position,
input_rotate,
isolateBodies,
merge,
mirror,
move,
rotate,
scale,
update_bounds,
update_info,
widgets,

View file

@ -1,161 +0,0 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { $ } from '../../moto/webui.js';
import { api } from './api.js';
import { space } from '../../moto/space.js';
import { updateTool } from '../mode/cam/tools.js';
const { selection } = api;
function settings() {
return api.conf.get();
}
function boundsSelection() {
// Helper to compute bounds of selected meshes
const THREE = self.THREE;
let bounds = new THREE.Box3();
selection.for_meshes(mesh => {
let box = mesh.getBoundingBox().clone();
let pos = mesh.widget.mesh.position;
box.min.add(pos);
box.max.add(pos);
bounds = bounds.union(box);
});
return bounds;
}
function rotateInputSelection() {
if (selection.meshes().length === 0) {
api.show.alert("select object to rotate");
return;
}
api.uc.prompt("Enter X,Y,Z degrees of rotation","").then(coord => {
coord = (coord || '').split(',');
let prod = Math.PI / 180,
x = parseFloat(coord[0] || 0.0) * prod,
y = parseFloat(coord[1] || 0.0) * prod,
z = parseFloat(coord[2] || 0.0) * prod;
selection.rotate(x, y, z);
});
}
function positionSelection() {
if (selection.meshes().length === 0) {
api.show.alert("select object to position");
return;
}
let current = settings(),
{ device, process} = current,
center = process.ctOriginCenter || process.camOriginCenter || device.bedRound || device.originCenter,
bounds = boundsSelection();
api.uc.prompt("Enter X,Y coordinates for selection","").then(coord => {
coord = (coord || '').split(',');
let x = parseFloat(coord[0] || 0.0),
y = parseFloat(coord[1] || 0.0),
z = parseFloat(coord[2] || 0.0);
if (!center) {
x = x - device.bedWidth/2 + (bounds.max.x - bounds.min.x)/2;
y = y - device.bedDepth/2 + (bounds.max.y - bounds.min.y)/2
}
selection.move(x, y, z, true);
});
}
function selectionSize(e, ui) {
let dv = parseFloat(e.target.value || 1),
pv = parseFloat(e.target.was || 1),
ra = dv / pv,
xv = parseFloat(ui.sizeX.was ?? ui.scaleX.value) || 1,
yv = parseFloat(ui.sizeY.was ?? ui.scaleY.value) || 1,
zv = parseFloat(ui.sizeZ.was ?? ui.scaleZ.value) || 1,
ta = e.target,
xc = ui.lockX.checked,
yc = ui.lockY.checked,
zc = ui.lockZ.checked,
xt = ta === ui.sizeX,
yt = ta === ui.sizeY,
zt = ta === ui.sizeZ,
tl = (xt && xc) || (yt && yc) || (zt && zc),
xr = ((tl && xc) || (!tl && xt) ? ra : 1),
yr = ((tl && yc) || (!tl && yt) ? ra : 1),
zr = ((tl && zc) || (!tl && zt) ? ra : 1);
// prevent null scale
if (xv * xr < 0.1 || yv * yr < 0.1 || zv * zr < 0.1) {
api.alerts.show('invalid scale value');
return;
}
selection.scale(xr,yr,zr);
ui.sizeX.was = ui.sizeX.value = xv * xr;
ui.sizeY.was = ui.sizeY.value = yv * yr;
ui.sizeZ.was = ui.sizeZ.value = zv * zr;
}
function selectionScale(e, ui) {
let dv = parseFloat(e.target.value || 1),
pv = parseFloat(e.target.was || 1),
ra = dv / pv,
xv = parseFloat(ui.scaleX.was ?? ui.scaleX.value) || 1,
yv = parseFloat(ui.scaleY.was ?? ui.scaleY.value) || 1,
zv = parseFloat(ui.scaleZ.was ?? ui.scaleY.value) || 1,
ta = e.target,
xc = ui.lockX.checked,
yc = ui.lockY.checked,
zc = ui.lockZ.checked,
xt = ta === ui.scaleX,
yt = ta === ui.scaleY,
zt = ta === ui.scaleZ,
tl = (xt && xc) || (yt && yc) || (zt && zc),
xr = ((tl && xc) || (!tl && xt) ? ra : 1),
yr = ((tl && yc) || (!tl && yt) ? ra : 1),
zr = ((tl && zc) || (!tl && zt) ? ra : 1);
// prevent null scale
if (xv * xr < 0.1 || yv * yr < 0.1 || zv * zr < 0.1) {
api.alerts.show('invalid scale value');
return;
}
selection.scale(xr,yr,zr);
ui.scaleX.was = ui.scaleX.value = xv * xr;
ui.scaleY.was = ui.scaleY.value = yv * yr;
ui.scaleZ.was = ui.scaleZ.value = zv * zr;
}
function selectionRotate(e) {
let val = parseFloat(e.target.value) || 0;
e.target.value = val;
}
function setupSelectionBindings(ui) {
// on enter but not on blur
space.event.onEnterKey([
ui.scaleX, (e) => selectionScale(e, ui),
ui.scaleY, (e) => selectionScale(e, ui),
ui.scaleZ, (e) => selectionScale(e, ui),
ui.sizeX, (e) => selectionSize(e, ui),
ui.sizeY, (e) => selectionSize(e, ui),
ui.sizeZ, (e) => selectionSize(e, ui),
]);
// on enter and blur
space.event.onEnterKey([
ui.toolName, updateTool,
ui.toolNum, updateTool,
ui.toolFluteDiam, updateTool,
ui.toolFluteLen, updateTool,
ui.toolShaftDiam, updateTool,
ui.toolShaftLen, updateTool,
ui.toolTaperTip, updateTool,
ui.toolTaperAngle, updateTool,
$('rot_x'), selectionRotate,
$('rot_y'), selectionRotate,
$('rot_z'), selectionRotate
], true);
}
export const selectionTools = {
rotateInputSelection,
positionSelection,
setupSelectionBindings
};

View file

@ -4,7 +4,7 @@ import { $ } from '../../moto/webui.js';
import { api } from './api.js';
import { consts } from '../core/consts.js';
import { platform } from './platform.js';
import { selection } from './select.js';
import { selection } from './selected.js';
import { settings } from './config/manager.js';
import STACKS from './stacks.js';

View file

@ -5,7 +5,7 @@ import { api } from './api.js';
import { local as SDB } from '../../data/local.js';
import { openFiles } from './files.js';
import { platform } from './platform.js';
import { selection } from './select.js';
import { selection } from './selected.js';
import { settings } from './config/manager.js';
import { space as SPACE } from '../../moto/space.js';
import { utils } from '../core/utils.js';

View file

@ -4,7 +4,7 @@ import { $ } from '../../../moto/webui.js';
import { api } from '../../app/api.js';
import { conf } from '../../app/config/defaults.js';
import { util } from '../../../geo/base.js';
import { client } from '../../app/client.js';
import { client } from '../../app/workers.js';
import { addbox, delbox, getlastbox } from '../../core/boxes.js';
import { Layers } from '../../app/layers.js';
import { Stack } from '../../app/stack.js';

View file

@ -5,7 +5,7 @@ import '../../add/class.js';
import '../../add/three.js';
import { api } from '../app/api.js';
import { conf } from '../app/config/defaults.js';
import { client } from '../app/client.js';
import { client } from '../app/workers.js';
import { load } from '../../load/file.js';
import { newWidget } from '../core/widget.js';

View file

@ -3,13 +3,13 @@
import '../add/array.js';
import '../add/class.js';
import '../add/three.js';
import '../kiri/app/lang.js';
import '../kiri/app/language.js';
import '../kiri/core/lang-en.js';
import { api } from '../kiri/app/api.js';
import { init_lang } from '../kiri/app/init-lang.js';
import { init_one } from '../kiri/app/init-one.js';
import { init_two } from '../kiri/app/init-two.js';
import { init_input } from '../kiri/app/init-input.js';
import { init_sync } from '../kiri/app/init-sync.js';
let traceload = location.search.indexOf('traceload') > 0;
let load = [];
@ -34,8 +34,8 @@ async function checkReady() {
{
api.client.start();
await init_lang();
await init_one();
await init_two();
await init_input();
await init_sync();
}
for (let fn of load) {
safeExec(fn);