grid-apps-cmms/src/kiri/main.js

1014 lines
33 KiB
JavaScript
Raw Normal View History

/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
2017-04-18 17:09:33 -04:00
2020-06-03 22:39:30 -04:00
"use strict";
2017-04-18 17:09:33 -04:00
// dep: main.kiri
// dep: moto.license
// dep: geo.base
// dep: kiri.ui
// dep: kiri.api
// dep: kiri.conf
// dep: kiri.lang
// dep: kiri.utils
// dep: kiri.files
// dep: kiri.consts
// dep: kiri.widget
// dep: kiri.stats
// dep: kiri.stacks
// dep: kiri.function
// dep: kiri.platform
// dep: kiri.selection
// dep: kiri.settings
// use: kiri.alerts
// use: kiri.files
// use: kiri.frame
2022-02-22 11:32:35 -05:00
// use: moto.ajax
gapp.register("kiri.main", [], (root, exports) => {
2018-01-10 20:04:51 -05:00
const { base, data, kiri, moto, noop } = root;
2022-02-19 12:58:17 -05:00
const { api, consts, lang, Widget, newWidget, utils, stats } = kiri;
2022-02-18 18:42:08 -05:00
const { areEqual, parseOpt, encodeOpt, ajax, o2js, js2o, ls2o } = utils;
const { feature, platform, selection, settings } = api;
2022-02-18 10:52:49 -05:00
const { COLOR, MODES, PMODES, VIEWS } = consts;
2022-02-17 14:40:51 -05:00
const LANG = lang.current,
WIN = self.window,
DOC = self.document,
LOC = self.location,
HOST = LOC.host.split(':'),
SETUP = parseOpt(LOC.search.substring(1)),
SECURE = isSecure(LOC.protocol),
LOCAL = self.debug && !SETUP.remote,
2022-02-17 14:40:51 -05:00
EVENT = kiri.broker = gapp.broker,
2022-02-18 10:52:49 -05:00
SDB = data.local,
SPACE = kiri.space = moto.space,
2022-02-18 11:30:00 -05:00
FILES = kiri.catalog = kiri.openFiles(new data.Index(SETUP.d ? SETUP.d[0] : 'kiri')),
2022-02-17 14:40:51 -05:00
CONF = kiri.conf,
clone = Object.clone;
2022-02-18 18:42:08 -05:00
let UI = {},
UC = kiri.ui.prefix('kiri').inputAction(api.conf.update),
2017-04-18 17:09:33 -04:00
MODE = MODES.FDM,
2022-02-17 14:40:51 -05:00
STACKS = kiri.stacks,
DRIVER = undefined,
2017-04-18 17:09:33 -04:00
viewMode = VIEWS.ARRANGE,
local = SETUP.local,
busy = 0,
showFavorites = SDB.getItem('dev-favorites') === 'true',
saveTimer = null,
version = kiri.version = gapp.version;
2017-04-18 17:09:33 -04:00
2020-03-10 14:23:42 -04:00
// add show() to catalog for API
2022-02-18 11:30:00 -05:00
FILES.show = showCatalog;
2020-03-10 14:23:42 -04:00
2021-12-26 13:09:07 -05:00
// patch broker for api backward compatibility
EVENT.on = (topic, listener) => {
EVENT.subscribe(topic, listener);
return EVENT;
};
2022-02-17 14:40:51 -05:00
// augment api
Object.assign(api, {
2020-03-09 11:02:09 -04:00
ui: UI,
uc: UC,
2022-02-19 12:58:17 -05:00
stats,
2022-02-17 14:40:51 -05:00
focus: noop,
2022-02-18 11:30:00 -05:00
catalog: FILES,
busy: {
2022-02-17 14:40:51 -05:00
val() { return busy },
inc() { kiri.api.event.emit("busy", ++busy) },
dec() { kiri.api.event.emit("busy", --busy) }
},
color: COLOR,
2020-03-09 11:02:09 -04:00
const: {
LANG,
LOCAL,
2020-06-10 18:56:55 -04:00
SETUP,
2020-11-09 01:42:17 -05:00
SECURE,
STACKS,
2020-03-09 11:02:09 -04:00
},
device: {
code: currentDeviceCode,
2020-03-17 16:32:43 -04:00
get: currentDeviceName,
set: noop, // set during init
clone: noop // set during init
2020-03-09 11:02:09 -04:00
},
dialog: {
2020-06-06 11:17:04 -04:00
show: showModal,
2022-02-18 18:42:08 -05:00
hide: hideModal,
update_process_list: updateProcessList
2020-03-09 11:02:09 -04:00
},
help: {
show: showHelp,
file: showHelpFile
},
event: {
2022-02-17 14:40:51 -05:00
on(t,l) { return EVENT.on(t,l) },
emit(t,m,o) { return EVENT.publish(t,m,o) },
bind(t,m,o) { return EVENT.bind(t,m,o) },
2022-02-25 15:09:05 -05:00
alerts(clr) { api.alerts.update(clr) },
2020-03-09 11:02:09 -04:00
import: loadFile,
settings: triggerSettingsEvent
},
2021-03-01 00:22:07 -05:00
group: {
merge: groupMerge,
split: groupSplit,
},
2020-03-09 11:12:11 -04:00
hide: {
2022-02-19 13:44:15 -05:00
alert(rec, recs) { api.alerts.hide(...arguments) },
2022-02-17 14:40:51 -05:00
import: noop,
2020-11-24 00:48:45 -05:00
slider: hideSlider
2020-03-09 11:12:11 -04:00
},
2022-02-17 21:52:54 -05:00
image: {
dialog: loadImageDialog,
convert: loadImageConvert
},
2022-02-17 14:40:51 -05:00
language: kiri.lang,
2020-03-09 11:02:09 -04:00
modal: {
show: showModal,
hide: hideModal,
visible: modalShowing
},
mode: {
2022-02-17 14:40:51 -05:00
get_id() { return MODE },
2020-03-09 11:02:09 -04:00
get_lower: getModeLower,
get: getMode,
set: setMode,
switch: switchMode,
2022-02-18 14:56:59 -05:00
set_expert: noop,
is_fdm() { return MODE === MODES.FDM },
is_cam() { return MODE === MODES.CAM },
is_sla() { return MODE === MODES.SLA },
is_drag() { return MODE === MODES.DRAG },
is_wedm() { return MODE === MODES.WEDM },
is_wjet() { return MODE === MODES.WJET },
is_laser() { return MODE === MODES.LASER },
is_2d() { return
api.mode.is_drag() ||
api.mode.is_wedm() ||
api.mode.is_wjet() ||
api.mode.is_laser()
}
2020-03-09 11:02:09 -04:00
},
probe: {
2022-02-17 14:40:51 -05:00
live: "https://live.grid.space",
grid: noop,
local: noop
2020-03-09 11:02:09 -04:00
},
process: {
code: currentProcessCode,
get: currentProcessName
},
2020-03-09 11:02:09 -04:00
show: {
2022-02-19 13:44:15 -05:00
alert() { return api.alerts.show(...arguments) },
devices: noop, // set during init
2020-03-09 11:02:09 -04:00
progress: setProgress,
controls: setControlsVisible,
favorites: getShowFavorites,
slices: showSlices,
layer: setVisibleLayer,
2020-03-09 11:12:11 -04:00
local: showLocal,
tools: noop, // set during init
2020-03-09 11:12:11 -04:00
import: function() { UI.import.style.display = '' }
2020-03-09 11:02:09 -04:00
},
space: {
2022-02-17 21:52:54 -05:00
reload,
auto_save,
2020-03-09 11:02:09 -04:00
restore: restoreWorkspace,
clear: clearWorkspace,
save: saveWorkspace,
set_focus: setFocus,
update: SPACE.update,
2022-02-18 18:42:08 -05:00
is_dark() { return settings.ctrl().dark }
2020-03-09 11:02:09 -04:00
},
2020-06-10 18:56:55 -04:00
util: {
2020-12-23 14:27:18 -05:00
isSecure,
download: downloadBlob,
ui2rec() { api.conf.update_from(...arguments) },
rec2ui() { api.conf.update_fields(...arguments) },
2022-02-17 14:40:51 -05:00
b64enc(obj) { return base64js.fromByteArray(new TextEncoder().encode(JSON.stringify(obj))) },
b64dec(obj) { return JSON.parse(new TextDecoder().decode(base64js.toByteArray(obj))) }
2020-06-10 18:56:55 -04:00
},
2020-03-09 11:02:09 -04:00
view: {
get() { return viewMode },
2022-02-17 14:40:51 -05:00
set() { setViewMode(...arguments) },
set_arrange() { api.view.set(VIEWS.ARRANGE) },
set_slice() { api.view.set(VIEWS.SLICE) },
set_preview() { api.view.set(VIEWS.PREVIEW) },
set_animate() { api.view.set(VIEWS.ANIMATE) },
2022-02-17 14:40:51 -05:00
is_arrange() { return viewMode === VIEWS.ARRANGE },
is_slice() { return viewMode === VIEWS.SLICE },
is_preview() { return viewMode === VIEWS.PREVIEW },
is_animate() { return viewMode === VIEWS.ANIMATE },
2022-02-19 14:13:35 -05:00
update_stack_labels: updateStackLabelState,
2022-02-17 21:52:54 -05:00
update_slider_max: updateSliderMax,
update_slider: updateSlider,
update_speeds: updateSpeeds,
hide_slices: hideSlices,
2020-11-14 11:12:12 -05:00
snapshot: null,
2024-09-06 10:22:46 -04:00
outline: setOutline,
unit_scale: unitScale,
2022-02-17 21:52:54 -05:00
wireframe: setWireframe,
2020-03-09 11:02:09 -04:00
},
work: kiri.client
2022-02-17 14:40:51 -05:00
});
2017-04-18 17:09:33 -04:00
2022-02-19 14:13:35 -05:00
function updateStackLabelState() {
const settings = api.conf.get();
const { stacks } = kiri;
// match label checkboxes to preference
for (let label of stacks.getLabels()) {
let check = `${settings.mode}-${api.view.get()}-${label}`;
stacks.setVisible(label, settings.labels[check] !== false);
}
}
2022-02-18 13:53:41 -05:00
function setFocus(sel, point) {
2021-04-21 22:30:42 -04:00
if (point) {
SPACE.platform.setCenter(point.x, point.z, point.y);
SPACE.view.setFocus(new THREE.Vector3(point.x, point.y, point.z));
return;
}
2022-02-18 13:53:41 -05:00
if (sel === undefined) {
sel = api.widgets.all();
} else if (!Array.isArray) {
2022-02-18 13:53:41 -05:00
sel = [ sel ];
} else if (sel.length === 0) {
sel = api.widgets.all();
}
let pos = { x:0, y:0, z:0 };
2022-02-18 13:53:41 -05:00
for (let widget of sel) {
pos.x += widget.track.pos.x;
pos.y += widget.track.pos.y;
pos.z += widget.track.pos.z;
}
2022-02-18 13:53:41 -05:00
if (sel.length) {
pos.x /= sel.length;
pos.y /= sel.length;
pos.z /= sel.length;
2021-01-23 10:20:31 -05:00
}
let cam_index = api.conf.get().process.camStockIndexed || false;
let focus_z = cam_index ? 0 : platform.top_z() / 2;
SPACE.platform.setCenter(pos.x, -pos.y, focus_z);
SPACE.view.setFocus(new THREE.Vector3(pos.x, focus_z, -pos.y));
}
function reload() {
2022-02-17 14:40:51 -05:00
api.event.emit('reload');
do_reload(100);
}
function do_reload(time) {
// allow time for async saves to complete and busy to to to zero
setTimeout(() => {
if (busy === 0) {
LOC.reload();
} else {
console.log(`reload deferred on busy=${busy}`);
do_reload(250);
}
}, time || 100);
}
function auto_save() {
2022-02-18 18:42:08 -05:00
if (!settings.ctrl().autoSave) {
return;
}
clearTimeout(saveTimer);
saveTimer = setTimeout(() => {
2022-02-17 14:40:51 -05:00
api.space.save(true);
}, 1000);
}
2022-02-19 12:58:17 -05:00
// update version and init count
let inits = parseInt(SDB.getItem('kiri-init') || stats.get('init') || 0) + 1;
2020-03-07 14:00:41 -05:00
SDB.setItem('kiri-init', inits);
2022-02-19 12:58:17 -05:00
stats.set('init', inits);
stats.set('kiri', kiri.version);
2017-04-18 17:09:33 -04:00
2020-12-31 23:22:39 -05:00
// remove version from url, preserve other settings
WIN.history.replaceState({},'','/kiri/' + encodeOpt(SETUP) + LOC.hash);
2017-04-18 17:09:33 -04:00
/** ******************************************************************
* Utility Functions
******************************************************************* */
2021-12-09 23:12:35 -05:00
function unitScale() {
return api.mode.is_cam() && settings.ctrl().units === 'in' ? 25.4 : 1;
2021-12-09 23:12:35 -05:00
}
function getShowFavorites(bool) {
if (bool !== undefined) {
SDB.setItem('dev-favorites', bool);
showFavorites = bool;
return bool;
}
return showFavorites;
}
function triggerSettingsEvent() {
2022-02-18 21:25:25 -05:00
api.event.emit('settings', settings.get());
2021-12-09 23:12:35 -05:00
}
2020-03-07 12:33:26 -05:00
2017-04-18 17:09:33 -04:00
function isSecure(proto) {
return proto.toLowerCase().indexOf("https") === 0;
}
2024-05-23 10:42:06 -04:00
function setProgress(value = 0, msg) {
value = (value * 100).round(4);
UI.progress.width = value+'%';
if (self.debug) {
// console.log(msg, value.round(2));
UI.prostatus.style.display = 'flex';
if (msg) {
UI.prostatus.innerHTML = msg;
} else {
UI.prostatus.innerHTML = '';
}
}
2017-04-18 17:09:33 -04:00
}
function bound(v,min,max) {
return Math.max(min,Math.min(max,v));
}
2022-02-17 21:52:54 -05:00
function updateSpeeds(maxSpeed, minSpeed) {
const { ui } = api;
ui.speeds.style.display =
maxSpeed &&
settings.mode !== 'SLA' &&
settings.mode !== 'LASER' &&
viewMode === VIEWS.PREVIEW &&
ui.showSpeeds.checked ? 'block' : '';
if (maxSpeed) {
const colors = [];
for (let i = 0; i <= maxSpeed; i += maxSpeed / 20) {
colors.push(Math.round(Math.max(i, 1)));
}
kiri.client.colors(colors, maxSpeed, speedColors => {
const list = [];
Object.keys(speedColors).map(v => parseInt(v)).sort((a, b) => b - a).forEach(speed => {
const color = speedColors[speed];
const hex = color.toString(16).padStart(6, 0);
const r = (color >> 16) & 0xff;
const g = (color >> 8) & 0xff;
const b = (color >> 0) & 0xff;
const style = `background-color:#${hex}`;
list.push(`<label style="${style}">${speed}</label>`);
});
ui.speedbar.innerHTML = list.join('');
});
api.event.emit('preview.speeds', {
min: minSpeed,
max: maxSpeed
});
}
}
2020-06-07 23:02:05 -04:00
function updateSlider() {
2022-02-17 14:40:51 -05:00
api.event.emit("slider.set", {
start: (api.var.layer_lo / api.var.layer_max),
end: (api.var.layer_hi / api.var.layer_max)
2020-06-07 23:02:05 -04:00
});
api.conf.update_fields_from_range();
2020-06-07 23:02:05 -04:00
}
function setVisibleLayer(h, l) {
2022-02-17 14:40:51 -05:00
h = h >= 0 ? h : api.var.layer_hi;
l = l >= 0 ? l : api.var.layer_lo;
api.var.layer_hi = bound(h, 0, api.var.layer_max);
api.var.layer_lo = bound(l, 0, h);
api.event.emit("slider.label");
2020-06-07 23:02:05 -04:00
updateSlider();
showSlices();
2017-04-18 17:09:33 -04:00
}
function setWireframe(bool, color, opacity) {
2022-02-18 13:53:41 -05:00
api.widgets.each(function(w) { w.setWireframe(bool, color, opacity) });
SPACE.update();
}
2024-09-06 10:22:46 -04:00
function setOutline(bool) {
api.widgets.each(w => w.setOutline(bool));
SPACE.update();
}
2017-04-18 17:09:33 -04:00
function updateSliderMax(set) {
let max = STACKS.getRange().tallest - 1;
2022-02-17 14:40:51 -05:00
api.var.layer_max = UI.sliderMax.innerText = max;
if (set || max < api.var.layer_hi) {
api.var.layer_hi = api.var.layer_max;
api.event.emit("slider.label");
2020-06-07 23:02:05 -04:00
updateSlider();
2017-04-18 17:09:33 -04:00
}
}
function hideSlices() {
2020-11-08 10:55:36 -05:00
STACKS.clear();
2022-02-18 13:53:41 -05:00
api.widgets.opacity(COLOR.model_opacity);
api.widgets.each(function(widget) {
2017-04-18 17:09:33 -04:00
widget.setWireframe(false);
});
}
function setWidgetVisibility(bool) {
2022-02-18 13:53:41 -05:00
api.widgets.each(w => {
if (bool) {
w.show();
} else {
w.hide();
}
});
}
2017-04-18 17:09:33 -04:00
/**
* hide or show slice-layers and their sub-elements
*
* @param {number} [layer]
*/
function showSlices(layer) {
2020-06-08 12:23:35 -04:00
if (viewMode === VIEWS.ARRANGE) {
return;
}
2020-06-07 23:02:05 -04:00
showSlider();
2017-04-18 17:09:33 -04:00
if (typeof(layer) === 'string' || typeof(layer) === 'number') {
layer = parseInt(layer);
} else {
2022-02-17 14:40:51 -05:00
layer = api.var.layer_hi;
2017-04-18 17:09:33 -04:00
}
2022-02-17 14:40:51 -05:00
layer = bound(layer, 0, api.var.layer_max);
if (layer < api.var.layer_lo) api.var.layer_lo = layer;
api.var.layer_hi = layer;
api.event.emit("slider.label");
2017-04-18 17:09:33 -04:00
let cam = api.mode.is_cam(),
sla = api.mode.is_sla(),
2022-02-17 14:40:51 -05:00
hi = cam ? api.var.layer_max - api.var.layer_lo : api.var.layer_hi,
lo = cam ? api.var.layer_max - api.var.layer_hi : api.var.layer_lo;
2017-04-18 17:09:33 -04:00
2020-06-07 23:02:05 -04:00
updateSlider();
2022-02-17 14:40:51 -05:00
STACKS.setRange(api.var.layer_lo, api.var.layer_hi);
2020-06-07 23:02:05 -04:00
2017-04-18 17:09:33 -04:00
SPACE.update();
}
2020-06-07 23:02:05 -04:00
function showSlider() {
UI.layers.style.display = 'flex';
UI.slider.style.display = 'flex';
}
2022-02-18 14:56:59 -05:00
function hideSlider() {
2020-06-07 23:02:05 -04:00
UI.layers.style.display = 'none';
UI.slider.style.display = 'none';
2020-11-24 00:48:45 -05:00
UI.speeds.style.display = 'none';
2020-06-07 23:02:05 -04:00
}
2021-01-18 15:11:18 -05:00
function loadImageDialog(image, name, force) {
if (!force && image.byteLength > 2500000) {
return UC.confirm("Large images may fail to import<br>Consider resizing under 1000 x 1000<br>Proceed with import?").then(ok => {
if (ok) {
loadImageDialog(image, name, true);
}
});
}
const opt = {pre: [
"<div class='f-col a-center'>",
" <h3>Image Conversion</h3>",
" <p class='t-just' style='width:300px;line-height:1.5em'>",
" This will create a 3D model from a 2D PNG image. Photos must",
" be blurred to be usable. Values from 0=off to 50=high are suggested.",
" Higher values incur more processing time.",
" </p>",
" <div class='f-row t-right'><table>",
" <tr><th>blur value</th><td><input id='png-blur' value='0' size='3'></td>",
" <th>&nbsp;invert image</th><td><input id='png-inv' type='checkbox'></td></tr>",
" <tr><th>base size</th><td><input id='png-base' value='0' size='3'></td>",
" <th>&nbsp;invert alpha</th><td><input id='alpha-inv' type='checkbox'></td></tr>",
2020-11-27 15:03:25 -05:00
" <tr><th>border size</th><td><input id='png-border' value='0' size='3'></td>",
" <th></th><td></td></tr>",
" </table></div>",
"</div>"
]};
UC.confirm(undefined, {convert:true, cancel:false}, undefined, opt).then((ok) => {
if (ok) {
loadImage(image, {
file: name,
blur: parseInt($('png-blur').value) || 0,
base: parseInt($('png-base').value) || 0,
2020-11-27 15:03:25 -05:00
border: parseInt($('png-border').value) || 0,
inv_image: $('png-inv').checked,
inv_alpha: $('alpha-inv').checked
});
}
});
}
function loadImage(image, opt = {}) {
const info = Object.assign({settings: settings.get(), png:image}, opt);
2022-02-17 14:40:51 -05:00
kiri.client.image2mesh(info, progress => {
api.show.progress(progress, "converting");
}, vertices => {
2022-02-17 14:40:51 -05:00
api.show.progress(0);
const widget = newWidget().loadVertices(vertices);
widget.meta.file = opt.file;
platform.add(widget);
2020-11-26 23:18:28 -05:00
});
}
/** ******************************************************************
* Selection Functions
******************************************************************* */
2021-03-01 00:22:07 -05:00
function groupMerge() {
2022-02-18 13:27:26 -05:00
Widget.Groups.merge(selection.widgets(true));
2021-03-01 00:22:07 -05:00
}
function groupSplit() {
2022-02-18 13:27:26 -05:00
Widget.Groups.split(selection.widgets(false));
}
/** ******************************************************************
* Settings Functions
******************************************************************* */
2017-04-18 17:09:33 -04:00
// convert any image type to png
2021-02-23 10:08:04 -05:00
function loadImageConvert(res, name) {
let url = URL.createObjectURL(new Blob([res]));
$('mod-any').innerHTML = `<img id="xsrc" src="${url}"><canvas id="xdst"></canvas>`;
let img = $('xsrc');
let can = $('xdst');
img.onload = () => {
can.width = img.width;
can.height = img.height;
let ctx = can.getContext('2d');
ctx.drawImage(img, 0, 0);
fetch(can.toDataURL()).then(r => r.arrayBuffer()).then(data => {
loadImageDialog(data, name);
});
};
}
2022-10-26 21:47:31 -04:00
function loadFile(ev) {
// use modern Filesystem api when available
2022-10-26 21:47:31 -04:00
if (false && window.showOpenFilePicker) {
window.showOpenFilePicker().then(files => {
return Promise.all(files.map(fh => fh.getFile()))
}).then(files => {
if (files.length) {
api.platform.load_files(files);
}
}).catch(e => { /* ignore cancel */ });
return;
}
2022-10-26 21:47:31 -04:00
api.ui.load.click();
2017-04-18 17:09:33 -04:00
}
function saveWorkspace(quiet) {
2022-02-17 14:40:51 -05:00
api.conf.save();
2022-02-18 11:30:00 -05:00
const newWidgets = [];
const oldWidgets = js2o(SDB.getItem('ws-widgets'), []);
2022-02-18 13:53:41 -05:00
api.widgets.each(function(widget) {
2021-01-24 00:13:09 -05:00
if (widget.synth) return;
2020-03-10 10:40:57 -04:00
newWidgets.push(widget.id);
oldWidgets.remove(widget.id);
widget.saveState();
2022-02-17 14:40:51 -05:00
let ann = api.widgets.annotate(widget.id);
2020-11-25 09:35:28 -05:00
ann.file = widget.meta.file;
ann.url = widget.meta.url;
2020-03-10 10:40:57 -04:00
});
SDB.setItem('ws-widgets', o2js(newWidgets));
2024-06-05 17:36:35 -07:00
oldWidgets.forEach(wid => {
2020-03-10 10:40:57 -04:00
Widget.deleteFromState(wid);
});
2020-05-26 11:05:21 -04:00
// eliminate dangling saved widgets
2022-02-18 11:30:00 -05:00
FILES.deleteFilter(key => newWidgets.indexOf(key.substring(8)) < 0, "ws-save-", "ws-savf");
if (!quiet) {
2022-02-19 13:44:15 -05:00
api.show.alert("workspace saved", 1);
}
2017-04-18 17:09:33 -04:00
}
2020-03-29 20:23:48 -04:00
function restoreWorkspace(ondone, skip_widget_load) {
2022-02-18 18:42:08 -05:00
let newset = api.conf.restore(false),
2020-04-13 17:42:40 -04:00
camera = newset.controller.view,
2020-03-29 20:23:48 -04:00
toload = ls2o('ws-widgets',[]),
2020-04-13 17:42:40 -04:00
loaded = 0,
2020-03-29 20:23:48 -04:00
position = true;
api.conf.update_fields();
platform.update_size();
2017-04-18 17:09:33 -04:00
SPACE.view.reset();
2020-04-15 15:20:02 -04:00
if (camera) {
SPACE.view.load(camera);
} else {
SPACE.view.home();
}
2017-04-18 17:09:33 -04:00
if (skip_widget_load) {
if (ondone) {
ondone();
}
return;
}
2017-04-18 17:09:33 -04:00
2020-03-17 16:32:43 -04:00
// remove any widgets from platform
2022-02-18 13:53:41 -05:00
api.widgets.each(function(widget) {
platform.delete(widget);
2017-04-18 17:09:33 -04:00
});
2020-03-17 16:32:43 -04:00
// load any widget by name that was saved to the workspace
2017-04-18 17:09:33 -04:00
toload.forEach(function(widgetid) {
Widget.loadFromState(widgetid, function(widget) {
if (widget) {
platform.add(widget, 0, position, true);
2022-02-17 14:40:51 -05:00
let ann = api.widgets.annotate(widgetid);
2020-11-25 23:28:41 -05:00
widget.meta.file = ann.file;
widget.meta.url = ann.url;
}
2017-04-18 17:09:33 -04:00
if (++loaded === toload.length) {
platform.deselect();
if (ondone) {
ondone();
2020-03-17 16:32:43 -04:00
setTimeout(() => {
platform.update_bounds();
2020-03-17 16:32:43 -04:00
SPACE.update();
}, 1);
}
2017-04-18 17:09:33 -04:00
}
}, position);
2017-04-18 17:09:33 -04:00
});
return toload.length > 0;
}
function clearWorkspace() {
// free up worker cache/mem
kiri.client.clear();
2020-03-09 11:02:09 -04:00
platform.select_all();
2022-02-18 13:27:26 -05:00
platform.delete(selection.meshes());
2017-04-18 17:09:33 -04:00
}
function modalShowing() {
2020-06-06 11:17:04 -04:00
return UI.modal.style.display === 'flex';
2017-04-18 17:09:33 -04:00
}
function showModal(which) {
2020-06-06 11:17:04 -04:00
let mod = UI.modal,
style = mod.style,
visible = modalShowing(),
info = { pct: 0 };
["help","setup","tools","prefs","saves","files","print","local","any"].forEach(function(name) {
2020-06-06 11:17:04 -04:00
UI[name].style.display = name === which ? 'flex' : '';
2017-04-18 17:09:33 -04:00
});
2020-06-06 11:17:04 -04:00
function ondone() {
2022-02-17 14:40:51 -05:00
api.event.emit('modal.show', which);
2020-06-06 11:17:04 -04:00
}
if (visible) {
return ondone();
}
style.height = '0';
style.display = 'flex';
new TWEEN.Tween(info).
easing(TWEEN.Easing.Quadratic.InOut).
to({ pct: 100 }, 100).
onUpdate(() => { style.height = `${info.pct}%` }).
onComplete(ondone).
start();
2017-04-18 17:09:33 -04:00
}
2020-06-06 11:17:04 -04:00
function hideModal() {
if (!modalShowing()) {
2018-02-02 09:02:07 -05:00
return;
}
2020-06-06 11:17:04 -04:00
let mod = UI.modal, style = mod.style, info={pct:100};
new TWEEN.Tween(info).
easing(TWEEN.Easing.Quadratic.InOut).
to({pct:0}, 100).
onUpdate(() => { style.height = `${info.pct}%` }).
onComplete(() => { style.display = '' }).
start();
}
2017-04-18 17:09:33 -04:00
function showCatalog() {
showModal("files");
2017-04-18 17:09:33 -04:00
}
2020-03-29 20:23:48 -04:00
function editSettings(e) {
2022-02-18 18:42:08 -05:00
let current = settings.get(),
mode = getMode(),
name = e.target.getAttribute("name"),
2022-02-18 18:42:08 -05:00
load = current.sproc[mode][name],
loadstr = JSON.stringify(load,null,4).split('\n');
UC.prompt(`settings for "${name}"`, loadstr).then(edit => {
if (edit) {
try {
current.sproc[mode][name] = JSON.parse(edit);
if (name === settings.proc().processName) {
api.conf.load(null, name);
}
api.conf.save();
api.settings.sync.put();
} catch (e) {
console.log({ malformed_settings: e });
UC.alert('malformed settings object');
2019-07-23 21:12:40 -04:00
}
}
});
}
function exportSettings(e) {
2022-02-18 18:42:08 -05:00
let current = settings.get(),
mode = getMode(),
name = e.target.getAttribute("name"),
2022-02-17 14:40:51 -05:00
data = api.util.b64enc({
2022-02-18 18:42:08 -05:00
process: current.sproc[mode][name],
2022-02-17 14:40:51 -05:00
version: kiri.version,
moto: moto.id,
time: Date.now(),
mode,
name
});
UC.prompt("Export Process Filename", name).then(name => {
if (name) {
2022-02-17 14:40:51 -05:00
api.util.download(data, `${name}.km`);
}
});
}
2020-03-29 20:23:48 -04:00
function deleteSettings(e) {
2022-02-18 18:42:08 -05:00
let current = settings.get();
2020-04-09 14:52:43 -04:00
let name = e.target.getAttribute("del");
2022-02-18 18:42:08 -05:00
delete current.sproc[getMode()][name];
2024-05-16 18:53:38 -04:00
api.settings.sync.put();
2022-02-18 18:42:08 -05:00
updateProcessList();
2022-02-17 14:40:51 -05:00
api.conf.save();
2017-04-18 17:09:33 -04:00
triggerSettingsEvent();
}
2022-02-18 18:42:08 -05:00
function updateProcessList() {
let current = settings.get();
let list = [], s = current, sp = s.sproc[getMode()] || {}, table = UI.settingsList;
2017-04-18 17:09:33 -04:00
table.innerHTML = '';
2020-04-09 14:52:43 -04:00
for (let k in sp) {
2017-04-18 17:09:33 -04:00
if (sp.hasOwnProperty(k)) list.push(k);
}
list.filter(n => n !=='default').sort().forEach(function(sk) {
2020-04-09 14:52:43 -04:00
let row = DOC.createElement('div'),
2017-04-18 17:09:33 -04:00
load = DOC.createElement('button'),
edit = DOC.createElement('button'),
xprt = DOC.createElement('button'),
2017-04-18 17:09:33 -04:00
del = DOC.createElement('button'),
name = sk;
load.setAttribute('load', sk);
load.onclick = (ev) => {
2022-02-17 14:40:51 -05:00
api.conf.load(undefined, sk);
2022-02-18 18:42:08 -05:00
updateProcessList();
hideModal();
}
2017-04-18 17:09:33 -04:00
load.appendChild(DOC.createTextNode(sk));
2022-02-18 18:42:08 -05:00
if (sk == settings.proc().processName) {
2017-12-14 20:41:19 -05:00
load.setAttribute('class', 'selected')
}
2022-02-18 18:42:08 -05:00
UI.settingsName.value = settings.proc().processName;
2017-04-18 17:09:33 -04:00
del.setAttribute('del', sk);
del.setAttribute('title', "remove '"+sk+"'");
del.innerHTML = '<i class="far fa-trash-alt"></i>';
2020-03-29 20:23:48 -04:00
del.onclick = deleteSettings;
2017-04-18 17:09:33 -04:00
edit.setAttribute('name', sk);
edit.setAttribute('title', 'edit');
edit.innerHTML = '<i class="far fa-edit"></i>';
2020-03-29 20:23:48 -04:00
edit.onclick = editSettings;
xprt.setAttribute('name', sk);
xprt.setAttribute('title', 'export');
xprt.innerHTML = '<i class="fas fa-download"></i>';
xprt.onclick = exportSettings;
2017-04-18 17:09:33 -04:00
row.setAttribute("class", "flow-row");
row.appendChild(edit);
2017-04-18 17:09:33 -04:00
row.appendChild(load);
row.appendChild(xprt);
2017-04-18 17:09:33 -04:00
row.appendChild(del);
table.appendChild(row);
});
}
2020-03-09 11:02:09 -04:00
function showHelp() {
2021-10-26 19:35:57 -04:00
showHelpFile(`local`,() => {});
2017-04-18 17:09:33 -04:00
}
2020-11-16 15:04:58 -05:00
function showHelpFile(local,then) {
2017-04-18 17:09:33 -04:00
if (!local) {
2021-10-26 19:35:57 -04:00
WIN.open("//docs.grid.space/", "_help");
2017-04-18 17:09:33 -04:00
return;
}
2022-02-17 14:40:51 -05:00
$('kiri-version').innerHTML = `${LANG.version} ${kiri.version}`;
2021-10-26 19:35:57 -04:00
showModal('help');
2022-02-17 14:40:51 -05:00
api.event.emit('help.show', local);
2017-04-18 17:09:33 -04:00
}
function showLocal() {
showModal('local');
2022-02-17 14:40:51 -05:00
api.probe.local((err,data) => {
let devc = 0;
let bind = [];
let html = ['<table>'];
html.push(`<thead><tr><th>device</th><th>type</th><th>status</th><th></th></tr></thead>`);
html.push(`<tbody>`);
2021-06-02 17:14:07 -04:00
let recs = [];
for (let k in data) {
2021-06-02 17:14:07 -04:00
recs.push(data[k].stat);
}
recs.sort((a,b) => {
return a.device.name < b.device.name ? -1 : 1;
});
for (let r of recs) {
bind.push({uuid: r.device.uuid, host: r.device.addr[0], port: r.device.port});
html.push(`<tr>`);
html.push(`<td>${r.device.name}</td>`);
html.push(`<td>${r.device.mode}</td>`);
html.push(`<td>${r.state}</td>`);
html.push(`<td><button id="${r.device.uuid}">admin</button></td>`);
html.push(`</tr>`);
devc++;
}
html.push(`</tbody>`);
html.push(`</table>`);
if (devc) {
$('mod-local').innerHTML = html.join('');
} else {
$('mod-local').innerHTML = `<br><b>no local devices</b>`;
}
bind.forEach(rec => {
$(rec.uuid).onclick = () => {
window.open(`http://${rec.host}:${rec.port||4080}/`);
};
});
});
}
2017-04-18 17:09:33 -04:00
function setViewMode(mode) {
2020-11-11 08:13:40 -05:00
const oldMode = viewMode;
2022-02-18 18:42:08 -05:00
const isCAM = settings.mode() === 'CAM';
2017-04-18 17:09:33 -04:00
viewMode = mode;
platform.deselect();
2022-02-18 13:27:26 -05:00
selection.update_info();
// clear any bounds forced by, for example, WireEDM previews
api.platform.set_bounds();
// disable clear in non-arrange modes
['view-arrange','act-slice','act-preview','act-animate'].forEach(el => {
$(el).classList.remove('selected')
});
2024-06-04 22:06:02 -07:00
$('render-tools').classList.add('hide');
2017-04-18 17:09:33 -04:00
switch (mode) {
case VIEWS.ARRANGE:
$('view-arrange').classList.add('selected');
2024-06-04 22:06:02 -07:00
$('render-tools').classList.remove('hide');
2022-02-18 14:56:59 -05:00
api.function.clear_progress();
2022-02-17 14:40:51 -05:00
kiri.client.clear();
2020-11-08 17:35:40 -05:00
STACKS.clear();
2020-06-07 23:02:05 -04:00
hideSlider();
updateSpeeds();
2020-11-09 00:21:35 -05:00
setVisibleLayer();
setWidgetVisibility(true);
2022-02-18 13:53:41 -05:00
api.widgets.opacity(1);
2017-04-18 17:09:33 -04:00
break;
case VIEWS.SLICE:
$('act-slice').classList.add('selected');
updateSpeeds();
2017-04-18 17:09:33 -04:00
updateSliderMax();
setWidgetVisibility(true);
2024-09-06 10:22:46 -04:00
!isCAM && api.view.outline(false);
2017-04-18 17:09:33 -04:00
break;
case VIEWS.PREVIEW:
$('act-preview').classList.add('selected');
setWidgetVisibility(true);
2024-09-06 10:22:46 -04:00
!isCAM && api.view.outline(false);
2017-04-18 17:09:33 -04:00
break;
case VIEWS.ANIMATE:
$('act-animate').classList.add('selected');
2024-09-06 10:22:46 -04:00
!isCAM && api.view.outline(false);
break;
2017-04-18 17:09:33 -04:00
default:
2021-07-04 21:05:46 -04:00
console.log("invalid view mode: "+mode);
2017-04-18 17:09:33 -04:00
return;
}
2022-02-17 14:40:51 -05:00
api.event.emit('view.set', mode);
DOC.activeElement.blur();
2017-04-18 17:09:33 -04:00
}
function getMode() {
2022-02-18 18:42:08 -05:00
return settings.mode();
2017-04-18 17:09:33 -04:00
}
2020-03-07 13:14:22 -05:00
function getModeLower() {
return getMode().toLowerCase();
}
2020-02-08 21:01:00 -05:00
function switchMode(mode) {
2023-12-11 16:24:45 -05:00
setMode(mode, null, platform.update_size);
2020-02-08 21:01:00 -05:00
}
2017-04-18 17:09:33 -04:00
function setMode(mode, lock, then) {
if (!MODES[mode]) {
2021-07-04 21:05:46 -04:00
console.log("invalid mode: "+mode);
2017-04-18 17:09:33 -04:00
mode = 'FDM';
}
2022-02-18 18:42:08 -05:00
const current = settings.get();
// change mode constants
2022-02-18 18:42:08 -05:00
current.mode = mode;
2020-06-09 18:12:59 -04:00
MODE = MODES[mode];
2022-02-17 14:40:51 -05:00
DRIVER = kiri.driver[mode];
kiri.driver.current = DRIVER;
2024-06-01 00:19:10 -04:00
// gcode edit area for any non-SLA mode
api.uc.setVisible($('gcode-edit'), mode !== 'SLA');
// highlight selected mode menu item
["FDM","CAM","SLA","LASER","DRAG","WJET","WEDM"].forEach(sm => {
const cl = $(`mode-${sm.toLowerCase()}`).classList;
if (sm === mode) {
cl.add('selected');
} else {
cl.remove('selected');
}
});
2017-04-18 17:09:33 -04:00
// restore cached device profile for this mode
2022-02-18 18:42:08 -05:00
if (current.cdev[mode]) {
current.device = clone(current.cdev[mode]);
api.event.emit('device.select', currentDeviceName());
}
2024-05-20 22:36:20 -04:00
// hide/show
api.uc.setVisible($('set-tools'), mode === 'CAM');
2020-03-17 16:32:43 -04:00
// updates right-hand menu by enabling/disabling fields
setViewMode(VIEWS.ARRANGE);
2017-04-18 17:09:33 -04:00
UC.setMode(MODE);
// sanitize and persist settings
2022-02-17 14:40:51 -05:00
api.conf.load();
api.conf.save();
// other housekeeping
2017-04-18 17:09:33 -04:00
triggerSettingsEvent();
2022-02-17 21:52:54 -05:00
platform.update_selected();
2022-02-18 13:53:41 -05:00
selection.update_bounds(api.widgets.all());
api.conf.update_fields();
// because device dialog, if showing, needs to be updated
if (modalShowing()) {
2022-02-17 14:40:51 -05:00
api.show.devices();
}
2022-02-17 14:40:51 -05:00
api.space.restore(null, true);
api.event.emit("mode.set", mode);
if (then) {
then();
}
2017-04-18 17:09:33 -04:00
}
function currentDeviceName() {
2022-02-18 18:42:08 -05:00
return settings.get().filter[getMode()];
}
function currentDeviceCode() {
2022-02-18 18:42:08 -05:00
return settings.get().devices[currentDeviceName()];
}
function currentProcessName() {
2022-02-18 18:42:08 -05:00
return settings.get().cproc[getMode()];
}
function currentProcessCode() {
2022-02-18 18:42:08 -05:00
return settings.get().sproc[getMode()][currentProcessName()];
}
2017-04-18 17:09:33 -04:00
function setControlsVisible(show) {
2024-05-28 18:02:52 -04:00
// TODO fix
// $('mid-left').style.display = show ? 'flex' : 'none';
// $('mid-right').style.display = show ? 'flex' : 'none';
2017-04-18 17:09:33 -04:00
}
function downloadBlob(data, filename) {
let url = WIN.URL.createObjectURL(new Blob([data], {type: "octet/stream"}));
$('mod-any').innerHTML = `<a id="_dexport_" href="${url}" download="${filename}">x</a>`;
$('_dexport_').click();
}
2017-04-18 17:09:33 -04:00
// prevent safari from exiting full screen mode
2020-03-09 11:02:09 -04:00
DOC.onkeydown = function (evt) { if (evt.keyCode == 27) evt.preventDefault() }
2017-04-18 17:09:33 -04:00
// complete module loading
// kiri.load_exec();
2021-02-27 17:26:24 -05:00
// upon restore, seed presets
2022-02-17 14:40:51 -05:00
api.event.emit('preset', api.conf.dbo());
});