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

2029 lines
64 KiB
JavaScript
Raw Normal View History

2020-06-03 22:39:30 -04:00
/** 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
(function () {
2018-01-10 20:04:51 -05:00
let iOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent),
2017-04-18 17:09:33 -04:00
autoDecimate = true,
// ---------------
MOTO = moto,
KIRI = self.kiri,
BASE = self.base,
UTIL = BASE.util,
DBUG = BASE.debug,
LANG = KIRI.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,
2020-06-13 11:59:49 -04:00
SDB = MOTO.KV,
ODB = KIRI.odb = new MOTO.Storage(SETUP.d ? SETUP.d[0] : 'kiri'),
2017-04-18 17:09:33 -04:00
SPACE = KIRI.space = MOTO.Space,
WIDGETS = KIRI.widgets = [],
CATALOG = KIRI.catalog = KIRI.openCatalog(ODB,autoDecimate),
STATS = new Stats(SDB),
SEED = 'kiri-seed',
// ---------------
2020-04-09 14:52:43 -04:00
CONF = KIRI.conf,
MODES = CONF.MODES,
VIEWS = CONF.VIEWS,
2020-04-13 17:42:40 -04:00
clone = Object.clone,
2020-04-09 14:52:43 -04:00
settings = clone(CONF.template),
settingsDefault = clone(settings),
2020-03-09 11:02:09 -04:00
// ---------------
Widget = kiri.Widget,
2017-04-18 17:09:33 -04:00
newWidget = kiri.newWidget,
// ---------------
UI = {},
2020-06-05 23:21:53 -04:00
UC = KIRI.ui.prefix('kiri').inputAction(updateSettings).hideAction(updateDialogLeft),
2017-04-18 17:09:33 -04:00
MODE = MODES.FDM,
DRIVER = undefined,
2017-04-18 17:09:33 -04:00
onEvent = {},
currentPrint = null,
selectedMeshes = [],
localFilterKey ='kiri-gcode-filters',
localFilters = js2o(SDB.getItem(localFilterKey)) || [],
// ---------------
renderMode = 4,
viewMode = VIEWS.ARRANGE,
layoutOnAdd = true,
local = SETUP.local,
camStock = null,
2019-04-12 09:45:52 -04:00
camTopZ = 0,
topZ = 0,
showFavorites = SDB.getItem('dev-favorites') === 'true',
alerts = [],
grouping = false;
2017-04-18 17:09:33 -04:00
2020-03-17 19:46:42 -04:00
if (SETUP.rm) renderMode = parseInt(SETUP.rm[0]);
2017-04-18 17:09:33 -04:00
DBUG.enable();
2020-06-01 23:23:47 -04:00
// remove version, preserve other settings
WIN.history.replaceState({},'','/kiri/' + encodeOpt(SETUP) + LOC.hash);
2020-03-10 14:23:42 -04:00
// add show() to catalog for API
CATALOG.show = showCatalog;
const feature = {
seed: true,
controls: true
};
2020-03-09 11:02:09 -04:00
const selection = {
opacity: setOpacity,
move: moveSelection,
scale: scaleSelection,
rotate: rotateSelection,
meshes: function() { return selectedMeshes.slice() },
widgets: function() { return selectedMeshes.slice().map(m => m.widget) },
for_groups: forSelectedGroups,
2020-03-09 11:02:09 -04:00
for_meshes: forSelectedMeshes,
for_widgets: forSelectedWidgets
};
const platform = {
add: platformAdd,
delete: platformDelete,
layout: platformLayout,
load: platformLoad,
load_stl: platformLoadSTL,
deselect: platformDeselect,
select: platformSelect,
select_all: platformSelectAll,
selected_count: platformSelectedCount,
compute_max_z: platformComputeMaxZ,
update_origin: platformUpdateOrigin,
update_bounds: platformUpdateBounds,
update_stock: platformUpdateStock,
update_size: platformUpdateSize,
2020-03-09 11:02:09 -04:00
update_top_z: platformUpdateTopZ,
update_selected: platformUpdateSelected,
2020-04-07 21:45:21 -04:00
load_files: platformLoadFiles,
group: platformGroup,
group_done: platformGroupDone
};
2017-04-18 17:09:33 -04:00
const color = {
2020-03-28 14:17:00 -04:00
wireframe: 0x444444,
wireframe_opacity: 0.25,
selected: [ 0xbbff00, 0xbbee00, 0xbbdd00 ],
deselected: [ 0xffff00, 0xffdd00, 0xffbb00 ],
2020-03-28 14:17:00 -04:00
slicing: 0xffaaaa,
preview_opacity: 0.0,
model_opacity: 1.0,
slicing_opacity: 0.5,
sliced_opacity: 0.0,
cam_preview: 0x0055bb,
cam_preview_opacity: 0.25,
cam_sliced_opacity: 0.25
};
const lists = {
infill: [
{ name: "vase" },
{ name: "hex" },
{ name: "grid" },
{ name: "gyroid" },
2020-04-15 22:19:18 -04:00
{ name: "triangle" },
2020-04-20 21:04:52 -04:00
{ name: "linear" },
{ name: "bubbles" }
],
2020-04-18 22:12:50 -04:00
units: [
{ name: "mm" },
{ name: "in" }
],
2020-04-18 22:12:50 -04:00
antialias: [
{ name: "1", id: 1 },
{ name: "2", id: 2 },
{ name: "4", id: 4 },
{ name: "8", id: 8 }
]
};
2020-03-09 11:02:09 -04:00
const API = KIRI.api = {
ui: UI,
uc: UC,
sdb: SDB,
o2js: o2js,
js2o: js2o,
ajax: ajax,
2020-04-09 14:52:43 -04:00
clone: clone,
focus: () => {},
2020-03-09 11:02:09 -04:00
stats: STATS,
catalog: CATALOG,
conf: {
get: getSettings,
put: putSettings,
2020-03-29 20:23:48 -04:00
load: loadSettings,
2020-03-09 11:02:09 -04:00
save: saveSettings,
show: showSettings,
2020-03-29 20:23:48 -04:00
update: updateSettings,
2020-04-07 21:45:21 -04:00
restore: restoreSettings,
export: settingsExport,
import: settingsImport
2020-03-09 11:02:09 -04:00
},
color,
2020-03-09 11:02:09 -04:00
const: {
SEED,
LANG,
LOCAL,
MODES,
VIEWS,
2020-06-10 18:56:55 -04:00
SETUP,
SECURE
2020-03-09 11:02:09 -04:00
},
var: {
2020-06-07 23:02:05 -04:00
layer_lo: 0,
layer_hi: 0,
layer_max: 0
},
2020-03-09 11:02:09 -04:00
device: {
2020-03-17 16:32:43 -04:00
get: currentDeviceName,
2020-07-03 13:04:47 -04:00
set: undefined, // set during init
clone: undefined // set during init
2020-03-09 11:02:09 -04:00
},
dialog: {
2020-06-06 11:17:04 -04:00
show: showModal,
hide: hideModal
2020-03-09 11:02:09 -04:00
},
help: {
show: showHelp,
file: showHelpFile
},
event: {
on: addOnEvent,
emit: sendOnEvent,
import: loadFile,
alerts: updateAlerts,
settings: triggerSettingsEvent
},
feature,
2020-03-09 11:02:09 -04:00
function: {
slice: prepareSlices,
print: preparePrint,
2020-03-10 10:40:57 -04:00
export: function() { KIRI.export() },
cancel: cancelWorker,
2020-03-09 11:02:09 -04:00
clear: clearWidgetCache
},
2020-03-09 11:12:11 -04:00
hide: {
alert: function(rec) { alert2cancel(rec) },
2020-06-09 00:30:11 -04:00
import: function() { }
2020-03-09 11:12:11 -04:00
},
2020-03-17 09:36:36 -04:00
language: KIRI.lang,
lists,
2020-03-09 11:02:09 -04:00
modal: {
show: showModal,
hide: hideModal,
visible: modalShowing
},
mode: {
get_lower: getModeLower,
get_id: function() { return MODE },
get: getMode,
set: setMode,
switch: switchMode,
set_expert: setExpert
2020-03-09 11:02:09 -04:00
},
2020-03-10 10:40:57 -04:00
print: {
get: function() { return currentPrint },
clear: clearPrint
},
2020-03-09 11:02:09 -04:00
probe: {
grid : function() { return false },
2020-03-10 14:23:42 -04:00
local : function() { return false }
2020-03-09 11:02:09 -04:00
},
platform,
selection,
show: {
alert: alert2,
2020-03-17 16:32:43 -04:00
devices: undefined, // 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,
import: function() { UI.import.style.display = '' }
2020-03-09 11:02:09 -04:00
},
space: {
reload: reload,
2020-03-09 11:02:09 -04:00
restore: restoreWorkspace,
clear: clearWorkspace,
save: saveWorkspace,
},
2020-06-10 18:56:55 -04:00
util: {
isSecure
},
2020-03-09 11:02:09 -04:00
view: {
get: function() { return viewMode },
set: setViewMode,
update_slider: updateSlider,
update_fields: updateFields,
2020-03-10 14:23:42 -04:00
wireframe: toggleWireframe,
snapshot: null
2020-03-09 11:02:09 -04:00
},
widgets: {
new: newWidget,
all: function() { return WIDGETS.slice() },
for: forAllWidgets,
load: Widget.loadFromCatalog,
meshes: meshArray,
opacity: setOpacity
2020-03-09 11:02:09 -04:00
},
work: KIRI.work
2017-04-18 17:09:33 -04:00
};
function reload() {
API.event.emit('reload');
// allow time for async saves to complete
setTimeout(() => { LOC.reload() }, 50);
}
2017-04-18 17:09:33 -04:00
/** ******************************************************************
* Stats accumulator
******************************************************************* */
function Stats(db) {
this.db = db;
2020-03-07 13:14:22 -05:00
this.obj = js2o(this.db['stats'] || '{}');
2020-04-09 14:52:43 -04:00
let o = this.obj, k;
2017-04-18 17:09:33 -04:00
for (k in o) {
if (!o.hasOwnProperty(k)) continue;
2020-03-07 13:14:22 -05:00
if (k === 'dn' || k.indexOf('-') > 0 || k.indexOf('_') > 0) {
delete o[k];
}
2017-04-18 17:09:33 -04:00
}
}
2020-03-07 12:33:26 -05:00
Stats.prototype.save = function(quiet) {
2017-04-18 17:09:33 -04:00
this.db['stats'] = o2js(this.obj);
2020-03-07 12:33:26 -05:00
if (!quiet) {
API.event.emit('stats', this.obj);
2020-03-07 12:33:26 -05:00
}
2017-04-18 17:09:33 -04:00
return this;
};
2020-03-07 12:33:26 -05:00
2017-04-18 17:09:33 -04:00
Stats.prototype.get = function(k) {
return this.obj[k];
2020-03-07 12:33:26 -05:00
};
Stats.prototype.set = function(k,v,quiet) {
2017-04-18 17:09:33 -04:00
this.obj[k] = v;
2020-03-07 12:33:26 -05:00
this.save(quiet);
2017-04-18 17:09:33 -04:00
return this;
};
2020-03-07 12:33:26 -05:00
Stats.prototype.add = function(k,v,quiet) {
2017-04-18 17:09:33 -04:00
this.obj[k] = (this.obj[k] || 0) + (v || 1);
2020-03-07 12:33:26 -05:00
this.save(quiet);
2017-04-18 17:09:33 -04:00
return this;
};
2020-03-07 12:33:26 -05:00
Stats.prototype.del = function(k, quiet) {
2017-04-18 17:09:33 -04:00
delete this.obj[k];
2020-03-07 12:33:26 -05:00
this.save(quiet);
2017-04-18 17:09:33 -04:00
return this;
2020-03-07 12:33:26 -05:00
};
2017-04-18 17:09:33 -04:00
2020-03-07 15:57:45 -05:00
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);
STATS.set('init', inits);
2017-04-18 17:09:33 -04:00
STATS.set('kiri', kiri.version);
/** ******************************************************************
* Utility Functions
******************************************************************* */
function unitScale() {
return MODE === MODES.CAM &&
settings.controller.units === 'in' ? 25.4 : 1;
}
2020-03-07 12:33:26 -05:00
function alert2(message, time) {
if (message === undefined) {
return updateAlerts(true);
}
let rec = [message, Date.now(), time, true];
alerts.push(rec);
updateAlerts();
return rec;
}
function alert2cancel(rec) {
rec[3] = false;
2020-03-07 12:33:26 -05:00
updateAlerts();
}
function updateAlerts(clear) {
if (clear) {
alerts = [];
}
let now = Date.now();
// filter out by age and active flag
2020-03-07 12:33:26 -05:00
alerts = alerts.filter(alert => {
return alert[3] && (now - alert[1]) < ((alert[2] || 5) * 1000);
2020-03-07 12:33:26 -05:00
});
// limit to 5 showing
while (alerts.length > 5) {
alerts.shift();
}
2020-03-17 09:36:36 -04:00
// return if called before UI configured
if (!UI.alert) {
return;
}
2020-03-07 12:33:26 -05:00
if (alerts.length > 0) {
UI.alert.text.innerHTML = alerts.map(v => ['<p>',v[0],'</p>'].join('')).join('');
UI.alert.dialog.style.display = 'flex';
} else {
UI.alert.dialog.style.display = 'none';
}
}
2020-03-09 11:02:09 -04:00
function getShowFavorites(bool) {
if (bool !== undefined) {
SDB.setItem('dev-favorites', bool);
showFavorites = bool;
return bool;
2020-03-09 11:02:09 -04:00
}
return showFavorites;
}
2020-03-07 12:33:26 -05:00
function sendOnEvent(name, data) {
if (name && onEvent[name]) onEvent[name].forEach(function(fn) {
fn(data);
});
}
function addOnEvent(name, handler) {
if (name && typeof(name) === 'string' && typeof(handler) === 'function') {
onEvent[name] = onEvent[name] || [];
onEvent[name].push(handler);
}
2020-07-03 14:53:08 -04:00
return API.event;
2020-03-07 12:33:26 -05:00
}
function triggerSettingsEvent() {
API.event.emit('settings', settings);
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;
}
function parseOpt(ov) {
2020-04-09 14:52:43 -04:00
let opt = {}, kv, kva;
2020-03-20 09:53:44 -04:00
// handle kiri legacy and proper url encoding better
ov.replace(/&/g,',').split(',').forEach(function(el) {
2020-03-20 09:53:44 -04:00
kv = decodeURIComponent(el).split(':');
2017-04-18 17:09:33 -04:00
if (kv.length === 2) {
kva = opt[kv[0]] = opt[kv[0]] || [];
kva.push(decodeURIComponent(kv[1]));
}
});
return opt;
}
2020-06-01 23:23:47 -04:00
function encodeOpt(opt) {
let out = [];
Object.keys(opt).forEach(key => {
if (key === 'ver') return;
let val = opt[key];
out.push(encodeURIComponent(key) + ":" + encodeURIComponent(val));
2020-06-01 23:23:47 -04:00
});
return out.length ? '?' + out.join(',') : '';
2020-06-01 23:23:47 -04:00
}
2017-04-18 17:09:33 -04:00
function ajax(url, fn, rt, po, hd) {
return new MOTO.Ajax(fn, rt).request(url, po, hd);
}
function o2js(o,def) {
return o ? JSON.stringify(o) : def || null;
}
function js2o(s,def) {
try {
return s ? JSON.parse(s) : def || null;
} catch (e) {
console.log({malformed_json:s});
return def || null;
}
}
function ls2o(key,def) {
return js2o(SDB.getItem(key),def);
}
function setProgress(value, msg) {
if (value) {
value = UTIL.round(value*100,4);
UI.loading.display = 'block';
UI.progress.width = value+'%';
if (msg) UI.prostatus.innerHTML = msg;
} else {
UI.loading.display = 'none';
}
}
function bound(v,min,max) {
return Math.max(min,Math.min(max,v));
}
2020-06-07 23:02:05 -04:00
function updateSlider() {
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
});
}
function setVisibleLayer(h, l) {
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 meshArray() {
2020-04-09 14:52:43 -04:00
let out = [];
2019-05-07 14:35:39 -04:00
forAllWidgets(function(widget) {
2017-04-18 17:09:33 -04:00
out.push(widget.mesh);
});
return out;
}
2019-05-07 14:35:39 -04:00
function forAllWidgets(f) {
2017-04-18 17:09:33 -04:00
WIDGETS.slice().forEach(function(widget) {
f(widget);
});
}
function forSelectedGroups(f) {
let m = selectedMeshes;
if (m.length === 0 && WIDGETS.length === 1) m = [ WIDGETS[0].mesh ];
let v = [];
m.slice().forEach(function (mesh) {
if (v.indexOf(mesh.widget.group) < 0) f(mesh.widget);
v.push(mesh.widget.group);
});
}
function forSelectedWidgets(f,noauto) {
let m = selectedMeshes;
if (m.length === 0 && WIDGETS.length === 1) {
m = noauto ? [] : [ WIDGETS[0].mesh ];
}
2017-04-18 17:09:33 -04:00
m.slice().forEach(function (mesh) { f(mesh.widget) });
}
function forSelectedMeshes(f) {
selectedMeshes.slice().forEach(function (mesh) { f(mesh) });
}
function toggleWireframe(color, opacity) {
2019-05-07 14:35:39 -04:00
forAllWidgets(function(w) { w.toggleWireframe(color, opacity) });
2017-04-18 17:09:33 -04:00
SPACE.update();
}
function updateSliderMax(set) {
2020-04-09 14:52:43 -04:00
let max = 0;
2017-04-18 17:09:33 -04:00
if (viewMode === VIEWS.PREVIEW && currentPrint) {
max = currentPrint.getLayerCount();
} else {
2019-05-07 14:35:39 -04:00
forAllWidgets(function(widget) {
2017-04-18 17:09:33 -04:00
if (!widget.slices) return;
2017-11-03 15:56:32 -04:00
max = Math.max(max, widget.slices.length);
2017-04-18 17:09:33 -04:00
});
}
2017-11-03 15:56:32 -04:00
max = Math.max(0, max - 1);
2020-06-07 23:02:05 -04: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-04-09 14:52:43 -04:00
let showing = false;
2020-03-28 14:17:00 -04:00
setOpacity(color.model_opacity);
2019-05-07 14:35:39 -04:00
forAllWidgets(function(widget) {
2017-04-18 17:09:33 -04:00
widget.setWireframe(false);
showing = widget.hideSlices() || showing;
});
clearPrint();
return showing;
}
/**
* 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 {
2020-06-07 23:02:05 -04:00
layer = API.var.layer_hi;
2017-04-18 17:09:33 -04:00
}
layer = bound(layer, 0, API.var.layer_max);
if (layer < API.var.layer_lo) API.var.layer_lo = layer;
2020-06-07 23:02:05 -04:00
API.var.layer_hi = layer;
API.event.emit("slider.label");
2017-04-18 17:09:33 -04:00
2020-06-07 23:02:05 -04:00
let print = UI.layerPrint.checked,
moves = UI.layerMoves.checked,
cam = MODE === MODES.CAM,
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();
2017-04-18 17:09:33 -04:00
2019-05-07 14:35:39 -04:00
forAllWidgets(function(widget) {
2017-04-18 17:09:33 -04:00
if (print) return widget.hideSlices();
2020-06-07 23:02:05 -04:00
let slices = widget.slices;
2017-04-18 17:09:33 -04:00
if (!slices) return;
2020-06-07 23:02:05 -04:00
for (let j = 0; j < slices.length; j++) {
let slice = slices[j];
slice.view.visible = j >= lo && j <= hi;
let layers = slice.layers;
layers.outline.setVisible(cam ?
UI.layerOutline.checked :
2020-06-07 23:02:05 -04:00
UI.layerOutline.checked
2017-04-18 17:09:33 -04:00
);
2020-06-07 23:02:05 -04:00
layers.trace.setVisible(cam ?
UI.layerRough.checked :
UI.layerTrace.checked
2017-04-18 17:09:33 -04:00
);
2020-06-07 23:02:05 -04:00
layers.bridge.setVisible(cam ?
UI.layerFinishX.checked :
UI.layerDelta.checked
2017-04-18 17:09:33 -04:00
);
2020-06-07 23:02:05 -04:00
layers.flat.setVisible(cam ?
UI.layerFinishY.checked :
UI.layerDelta.checked
2017-04-18 17:09:33 -04:00
);
2020-06-07 23:02:05 -04:00
layers.solid.setVisible(cam ?
UI.layerFinish.checked :
UI.layerSolid.checked
2017-04-18 17:09:33 -04:00
);
2020-06-07 23:02:05 -04:00
layers.fill.setVisible(cam ?
UI.layerFacing.checked :
UI.layerFill.checked
2017-04-18 17:09:33 -04:00
);
layers.sparse.setVisible(UI.layerSparse.checked);
layers.support.setVisible(UI.layerSupport.checked);
}
});
if (currentPrint) {
let len = currentPrint.getLayerCount();
2020-06-07 23:02:05 -04:00
for (let j = 0; j < len; j++) {
currentPrint.showLayer(j, print && j >= lo && j <= hi, moves);
2017-04-18 17:09:33 -04:00
}
}
2020-06-07 23:02:05 -04:00
UI.layerPrint.parentNode.style.display = currentPrint ? '' : 'none';
UI.layerMoves.parentNode.style.display = currentPrint ? '' : 'none';
2017-04-18 17:09:33 -04:00
SPACE.update();
}
2019-09-17 08:36:20 -04:00
function loadCode(code, type) {
2017-04-18 17:09:33 -04:00
setViewMode(VIEWS.PREVIEW);
clearPrint();
setOpacity(0);
currentPrint = kiri.newPrint(settings, []);
let center = settings.process.outputOriginCenter;
let origin = settings.origin;
let offset = {
x: origin.x,
y: -origin.y,
z: origin.z
2019-09-17 08:36:20 -04:00
};
switch (type) {
case 'svg':
currentPrint.parseSVG(code, offset);
break;
default:
currentPrint.parseGCode(code, offset);
break;
}
2017-04-18 17:09:33 -04:00
currentPrint.render();
SPACE.platform.add(currentPrint.group);
SPACE.update();
UI.layerPrint.checked = true;
updateSliderMax(true);
showSlices();
}
function preparePrint(callback) {
if (viewMode === VIEWS.PREVIEW) return;
2020-06-07 23:02:05 -04:00
hideSlider(true);
2017-04-18 17:09:33 -04:00
// kick off slicing it hasn't been done already
2020-04-09 14:52:43 -04:00
for (let i=0; i < WIDGETS.length; i++) {
2017-04-18 17:09:33 -04:00
if (!WIDGETS[i].slices || WIDGETS[i].isModified()) {
prepareSlices(function() {
if (!WIDGETS[i].slices || WIDGETS[i].isModified()) {
2019-01-18 16:46:17 -05:00
alert2("nothing to print");
} else {
preparePrint(callback);
}
});
2017-04-18 17:09:33 -04:00
return;
}
}
2020-07-03 13:04:47 -04:00
let isCam = MODE === MODES.CAM, pMode = getMode();
2017-10-30 14:21:23 -04:00
setViewMode(VIEWS.PREVIEW);
2017-04-18 17:09:33 -04:00
clearPrint();
2020-03-29 20:23:48 -04:00
API.conf.save();
2020-07-03 13:04:47 -04:00
API.event.emit('preview.begin', pMode);
if (isCam) {
2020-03-28 14:17:00 -04:00
setOpacity(color.cam_preview_opacity);
2019-05-07 14:35:39 -04:00
forAllWidgets(function(widget) {
2020-03-28 14:17:00 -04:00
widget.setColor(color.cam_preview);
2017-04-18 17:09:33 -04:00
});
} else {
2020-03-28 14:17:00 -04:00
setOpacity(color.preview_opacity);
2017-04-18 17:09:33 -04:00
}
currentPrint = kiri.newPrint(settings, WIDGETS);
2017-11-17 13:11:13 -05:00
currentPrint.setup(true, function(update, status) {
setProgress(update, status);
2017-04-18 17:09:33 -04:00
}, function() {
2020-04-19 16:29:13 -04:00
if (!currentPrint) {
return setViewMode(VIEWS.ARRANGE);
}
2017-04-18 17:09:33 -04:00
setProgress(0);
if (!isCam) setOpacity(0);
2017-04-18 17:09:33 -04:00
currentPrint.render();
2020-07-03 13:04:47 -04:00
API.event.emit('print', pMode);
API.event.emit('preview.end', pMode);
2017-04-18 17:09:33 -04:00
SPACE.platform.add(currentPrint.group);
SPACE.update();
UI.layerPrint.checked = true;
updateSliderMax(true);
showSlices();
2020-03-10 10:40:57 -04:00
if (typeof(callback) === 'function') {
callback();
2017-04-18 17:09:33 -04:00
}
});
}
function cancelWorker() {
if (KIRI.work.isSlicing()) KIRI.work.restart();
}
2017-04-18 17:09:33 -04:00
function clearWidgetCache() {
hideSlices();
clearSlices();
clearPrint();
}
function clearPrint() {
if (currentPrint) {
SPACE.platform.remove(currentPrint.group);
currentPrint = null;
}
UI.layerPrint.checked = false;
}
function clearSlices() {
2019-05-07 14:35:39 -04:00
forAllWidgets(function(widget) {
2017-04-18 17:09:33 -04:00
widget.slices = null;
});
}
2020-06-07 23:02:05 -04:00
function showSlider() {
UI.layers.style.display = 'flex';
UI.slider.style.display = 'flex';
// UI.setMenu.style.display = 'none';
2020-06-07 23:02:05 -04:00
}
function hideSlider(andmenu) {
UI.layers.style.display = 'none';
UI.slider.style.display = 'none';
// UI.setMenu.style.display = andmenu ? 'none' : 'flex';
2020-06-07 23:02:05 -04:00
}
2017-04-18 17:09:33 -04:00
/**
* incrementally slice all meshes then incrementally update them
*
* @param {Function} callback
*/
function prepareSlices(callback) {
2017-10-30 14:21:23 -04:00
if (viewMode == VIEWS.ARRANGE) {
2020-03-10 14:23:42 -04:00
let snap = SPACE.screenshot();
API.view.snapshot = snap.substring(snap.indexOf(",")+1);
KIRI.work.snap(API.view.snapshot);
2017-10-30 14:21:23 -04:00
}
2020-06-09 18:12:59 -04:00
if (MODE === MODES.SLA && !callback) {
callback = preparePrint;
}
2017-10-30 14:21:23 -04:00
2020-06-07 23:02:05 -04:00
hideSlider(true);
2017-04-18 17:09:33 -04:00
clearPrint();
platform.deselect();
setViewMode(VIEWS.SLICE);
2020-07-03 13:04:47 -04:00
API.conf.save();
2020-07-03 13:04:47 -04:00
API.event.emit('slice.begin', getMode());
2017-04-18 17:09:33 -04:00
2020-04-09 14:52:43 -04:00
let firstMesh = true,
2017-04-18 17:09:33 -04:00
countdown = WIDGETS.length,
preserveMax = API.var.layer_max,
2020-06-07 23:02:05 -04:00
preserveLayer = API.var.layer_hi,
2017-04-18 17:09:33 -04:00
totalProgress,
track = {},
mode = settings.mode,
now = UTIL.time();
2017-04-18 17:09:33 -04:00
// require topo be sent back from worker for local printing
2017-11-17 13:11:13 -05:00
settings.synth.sendTopo = false;
2017-04-18 17:09:33 -04:00
2020-03-28 14:17:00 -04:00
setOpacity(color.slicing_opacity);
2017-04-18 17:09:33 -04:00
// for each widget, slice
2019-05-07 14:35:39 -04:00
forAllWidgets(function(widget) {
2020-04-09 14:52:43 -04:00
let segtimes = {},
2017-04-18 17:09:33 -04:00
segNumber = 0,
errored = false,
2017-04-18 17:09:33 -04:00
startTime,
lastMsg,
camOrLaser = mode === 'CAM' || mode === 'LASER';
2017-04-18 17:09:33 -04:00
widget.stats.progress = 0;
2020-03-28 14:17:00 -04:00
widget.setColor(color.slicing);
widget.slice(settings, function(sliced, error) {
2020-04-09 14:52:43 -04:00
let mark = UTIL.time();
// on done
widget.render();
// clear wireframe
widget.setWireframe(false, color.wireframe, color.wireframe_opacity);
widget.setOpacity(camOrLaser ? color.cam_sliced_opacity : color.sliced_opacity);
widget.setColor(color.deselected);
2017-04-18 17:09:33 -04:00
// update UI info
if (sliced) {
// update segment time
if (lastMsg) segtimes[segNumber+"_"+lastMsg] = mark - startTime;
segtimes.total = UTIL.time() - now;
2017-04-18 17:09:33 -04:00
DBUG.log(segtimes);
2020-04-18 11:34:54 -04:00
API.event.emit('slice', getMode());
2017-04-18 17:09:33 -04:00
updateSliderMax(true);
if (preserveMax != API.var.layer_max) {
preserveLayer = API.var.layer_max;
2017-04-18 17:09:33 -04:00
}
firstMesh = false;
}
// on the last exit, update ui and call the callback
if (--countdown === 0 || error || errored) {
setProgress(0);
showSlices(preserveLayer);
SPACE.scene.active();
2020-07-03 13:04:47 -04:00
API.event.emit('slice.end', getMode());
if (callback && typeof callback === 'function') {
callback();
2020-03-10 10:40:57 -04:00
}
2017-04-18 17:09:33 -04:00
}
// handle slicing errors
if (error && !errored) {
errored = true;
setViewMode(VIEWS.ARRANGE);
2020-03-28 14:17:00 -04:00
setOpacity(color.model_opacity);
platform.deselect();
2019-01-18 16:46:17 -05:00
alert2(error);
}
2017-04-18 17:09:33 -04:00
}, function(update, msg) {
if (msg !== lastMsg) {
2020-04-09 14:52:43 -04:00
let mark = UTIL.time();
2017-04-18 17:09:33 -04:00
if (lastMsg) segtimes[segNumber+"_"+lastMsg] = mark - startTime;
lastMsg = msg;
startTime = mark;
segNumber++;
}
// on update
track[widget.id] = update;
totalProgress = 0;
2019-05-07 14:35:39 -04:00
forAllWidgets(function(w) {
2017-04-18 17:09:33 -04:00
totalProgress += (track[w.id] || 0);
});
setProgress((totalProgress / WIDGETS.length), msg);
2017-11-17 13:11:13 -05:00
}, true);
2017-04-18 17:09:33 -04:00
});
}
/** ******************************************************************
* Selection Functions
******************************************************************* */
2020-04-07 21:45:21 -04:00
function updateSelectedInfo() {
let bounds = new THREE.Box3(), track;
2020-04-07 21:45:21 -04:00
forSelectedMeshes(mesh => {
bounds = bounds.union(mesh.getBoundingBox());
track = mesh.widget.track;
2020-04-07 21:45:21 -04:00
});
if (bounds.min.x === Infinity) {
2017-04-18 17:09:33 -04:00
if (selectedMeshes.length === 0) {
2020-06-07 11:41:03 -04:00
UI.sizeX.value = 0;
UI.sizeY.value = 0;
UI.sizeZ.value = 0;
UI.scaleX.value = 1;
UI.scaleY.value = 1;
UI.scaleZ.value = 1;
2017-04-18 17:09:33 -04:00
}
2020-04-07 21:45:21 -04:00
return;
2017-04-18 17:09:33 -04:00
}
2020-04-07 21:45:21 -04:00
let dx = bounds.max.x - bounds.min.x,
dy = bounds.max.y - bounds.min.y,
dz = bounds.max.z - bounds.min.z,
scale = unitScale();
2020-06-07 11:41:03 -04:00
UI.sizeX.value = UI.sizeX.was = UTIL.round(dx/scale,2);
UI.sizeY.value = UI.sizeY.was = UTIL.round(dy/scale,2);
UI.sizeZ.value = UI.sizeZ.was = UTIL.round(dz/scale,2);
UI.scaleX.value = UI.scaleX.was = track.scale.x;
UI.scaleY.value = UI.scaleY.was = track.scale.y;
UI.scaleZ.value = UI.scaleZ.was = track.scale.z;
2017-04-18 17:09:33 -04:00
}
function setOpacity(value) {
2019-05-07 14:35:39 -04:00
forAllWidgets(function (w) { w.setOpacity(value) });
2020-06-05 23:21:53 -04:00
// UI.modelOpacity.value = value * 100;
2017-04-18 17:09:33 -04:00
SPACE.update();
}
function moveSelection(x, y, z, abs) {
forSelectedGroups(function (w) { w.move(x, y, z, abs) });
platform.update_stock();
2017-04-18 17:09:33 -04:00
SPACE.update();
}
2020-04-09 22:42:40 -04:00
function scaleSelection() {
let args = arguments;
forSelectedGroups(function (w) {
w.scale(...args);
});
// skip update if last argument is strictly 'false'
if ([...arguments].pop() === false) {
return;
}
updateSelectedInfo();
platform.compute_max_z();
platform.update_stock(true);
SPACE.update();
2020-04-09 22:42:40 -04:00
}
2017-04-18 17:09:33 -04:00
function rotateSelection(x, y, z) {
forSelectedGroups(function (w) {
w.rotate(x, y, z);
});
2020-04-07 21:45:21 -04:00
updateSelectedInfo();
platform.compute_max_z();
platform.update_stock(true);
2017-04-18 17:09:33 -04:00
SPACE.update();
}
/** ******************************************************************
* Platform Functions
******************************************************************* */
2017-04-18 17:09:33 -04:00
function platformUpdateOrigin() {
2020-03-12 17:09:44 -04:00
platform.update_bounds();
let dev = settings.device;
let proc = settings.process;
let x = 0;
let y = 0;
let z = 0;
if (MODE === MODES.CAM && proc.camOriginTop) {
z = camTopZ + 0.01;
if (!camStock) {
z += proc.camZTopOffset;
}
}
if (!proc.outputOriginCenter) {
if (camStock) {
x = (-camStock.scale.x / 2) + camStock.position.x;
y = (camStock.scale.y / 2) - camStock.position.y;
} else {
2020-03-12 17:09:44 -04:00
if (MODE === MODES.LASER && proc.outputOriginBounds) {
let b = settings.bounds;
x = b.min.x,
y = -b.min.y
} else {
x = -dev.bedWidth / 2;
y = dev.bedDepth / 2;
}
}
} else if (camStock) {
x = camStock.position.x;
y = -camStock.position.y;
}
settings.origin = {x, y, z};
if (settings.controller.showOrigin && MODE !== MODES.SLA) {
SPACE.platform.setOrigin(x,y,z);
} else {
SPACE.platform.setOrigin();
}
}
2017-04-18 17:09:33 -04:00
function platformUpdateTopZ() {
let alignTopOk = WIDGETS.length > 1 && settings.controller.alignTop;
let camz = (MODE === MODES.CAM) && (settings.stock.z || alignTopOk);
let ztop = camz ? camTopZ - settings.process.camZTopOffset : 0;
forAllWidgets(function(widget) {
widget.setTopZ(ztop);
});
}
function platformUpdateSize() {
2020-04-15 15:20:02 -04:00
let frozen = SPACE.scene.freeze(true);
2020-04-09 14:52:43 -04:00
let dev = settings.device,
width, depth,
height = Math.round(Math.max(dev.bedHeight, dev.bedWidth/100, dev.bedDepth/100));
SPACE.platform.setRound(dev.bedRound);
SPACE.platform.setSize(
width = parseInt(dev.bedWidth),
depth = parseInt(dev.bedDepth),
height = parseFloat(dev.bedHeight)
);
let ctrl = settings.controller,
unitMM = ctrl.units === 'mm',
gridMajor = unitMM ? 25 : 25.4,
gridMinor = unitMM ? 5 : 25.4 / 10;
if (ctrl.dark) {
SPACE.platform.setGrid(gridMajor, gridMinor, 0x999999, 0x333333);
SPACE.platform.opacity(0.8);
DOC.body.classList.add('dark');
} else {
SPACE.platform.setGrid(gridMajor, gridMinor, 0x999999, 0xcccccc);
SPACE.platform.opacity(0.3);
DOC.body.classList.remove('dark');
}
2020-04-15 15:20:02 -04:00
SPACE.platform.setGZOff(height/2 - 0.1);
platform.update_origin();
2020-04-15 15:20:02 -04:00
SPACE.scene.freeze(frozen);
2017-04-18 17:09:33 -04:00
}
function platformUpdateBounds() {
2020-04-09 14:52:43 -04:00
let bounds = new THREE.Box3();
forAllWidgets(function(widget) {
let wp = widget.track.pos;
2020-03-12 17:09:44 -04:00
let wb = widget.mesh.getBoundingBox().clone();
2020-05-01 10:29:50 -04:00
wb.min.x += wp.x;
wb.max.x += wp.x;
wb.min.y += wp.y;
wb.max.y += wp.y;
2020-03-12 17:09:44 -04:00
bounds.union(wb);
});
return settings.bounds = bounds;
2017-04-18 17:09:33 -04:00
}
function platformSelect(widget, shift) {
2020-06-07 23:02:05 -04:00
if (viewMode !== VIEWS.ARRANGE) {
return;
}
2020-04-09 14:52:43 -04:00
let mesh = widget.mesh,
2017-04-18 17:09:33 -04:00
sel = (selectedMeshes.indexOf(mesh) >= 0);
if (sel) {
if (shift) {
platform.deselect(widget)
2017-04-18 17:09:33 -04:00
} else if (selectedMeshes.length > 1) {
platform.deselect();
platform.select(widget, false);
2017-04-18 17:09:33 -04:00
}
} else {
// prevent selection in slice view
if (!mesh.material.visible) return;
if (!shift) platform.deselect();
2017-04-18 17:09:33 -04:00
selectedMeshes.push(mesh);
API.event.emit('widget.select', widget);
widget.setColor(color.selected, settings);
2020-04-07 21:45:21 -04:00
updateSelectedInfo();
2017-04-18 17:09:33 -04:00
}
2020-03-10 14:23:42 -04:00
platformUpdateSelected();
2017-04-18 17:09:33 -04:00
SPACE.update();
}
function platformSelectedCount() {
2017-04-18 17:09:33 -04:00
return viewMode === VIEWS.ARRANGE ? selectedMeshes.length : 0;
}
2020-03-10 14:23:42 -04:00
function platformUpdateSelected() {
let selcount = platform.selected_count();
2020-06-07 23:02:05 -04:00
let extruders = settings.device.extruders;
let menu_show = selcount ? 'flex': '';
if (selcount) {
UI.scale.classList.add('lt-enabled');
UI.rotate.classList.add('lt-enabled');
UI.nozzle.classList.add('lt-enabled');
} else {
UI.scale.classList.remove('lt-enabled');
UI.rotate.classList.remove('lt-enabled');
UI.nozzle.classList.remove('lt-enabled');
}
UI.nozzle.style.display = extruders && extruders.length > 1 ? 'flex' : '';
if (extruders) {
for (let i=0; i<extruders.length; i++) {
let b = $(`sel-ext-${i}`);
2020-06-07 23:02:05 -04:00
if (b) b.classList.remove('pop-sel');
}
forSelectedWidgets(w => {
2020-06-07 23:02:05 -04:00
w.setColor(color.selected, settings);
let ext = (settings.widget[w.id] || {}).extruder || 0;
let b = $(`sel-ext-${ext}`);
2020-06-07 23:02:05 -04:00
if (b) b.classList.add('pop-sel');
}, true);
}
2020-03-10 14:23:42 -04:00
}
function platformDeselect(widget) {
2020-03-11 13:24:09 -04:00
if (viewMode !== VIEWS.ARRANGE) {
// don't de-select and re-color widgets in,
// for example, sliced or preview modes
return;
}
2017-04-18 17:09:33 -04:00
if (!widget) {
2019-05-07 14:35:39 -04:00
forAllWidgets(function(widget) {
platform.deselect(widget);
2017-04-18 17:09:33 -04:00
});
return;
}
2020-04-09 14:52:43 -04:00
let mesh = widget.mesh,
2017-04-18 17:09:33 -04:00
si = selectedMeshes.indexOf(mesh),
sel = (si >= 0);
if (sel) {
selectedMeshes.splice(si,1);
API.event.emit('widget.deselect', widget);
}
widget.setColor(color.deselected, settings);
2020-03-10 14:23:42 -04:00
platformUpdateSelected();
2017-04-18 17:09:33 -04:00
SPACE.update();
2020-04-07 21:45:21 -04:00
updateSelectedInfo();
2017-04-18 17:09:33 -04:00
}
function platformLoad(url, onload) {
if (url.toLowerCase().indexOf(".stl") > 0) {
platform.load_stl(url, onload);
} else {
ajax(url, function(vertices) {
vertices = js2o(vertices).toFloat32();
platform.add(newWidget().loadVertices(vertices));
if (onload) onload(vertices);
});
}
}
function platformLoadSTL(url, onload) {
new MOTO.STL().load(url, function(vertices) {
platform.add(newWidget().loadVertices(vertices));
if (onload) onload(vertices);
})
}
function platformComputeMaxZ() {
topZ = 0;
forAllWidgets(function(widget) {
topZ = Math.max(topZ, widget.mesh.getBoundingBox().max.z);
});
SPACE.platform.setMaxZ(topZ);
}
function platformGroup() {
grouping = true;
}
// called after all new widgets are loaded to update group positions
function platformGroupDone(skipLayout) {
grouping = false;
Widget.Groups.loadDone();
if (layoutOnAdd && !skipLayout) platform.layout();
}
function platformAdd(widget, shift, nolayout) {
2020-04-10 11:53:07 -04:00
if (!settings.widget[widget.id]) {
settings.widget[widget.id] = {extruder: 0};
}
WIDGETS.push(widget);
SPACE.platform.add(widget.mesh);
platform.select(widget, shift);
platform.compute_max_z();
API.event.emit('widget.add', widget);
if (nolayout) return;
if (!grouping) {
platformGroupDone();
2020-04-10 11:53:07 -04:00
} else if (layoutOnAdd) {
platform.layout();
}
}
function platformDelete(widget) {
2020-03-09 11:02:09 -04:00
if (!widget) {
return;
}
if (Array.isArray(widget)) {
2020-04-09 14:52:43 -04:00
let mc = widget.slice(), i;
for (i=0; i<mc.length; i++) {
platform.delete(mc[i].widget);
}
return;
}
KIRI.work.clear(widget);
2020-04-09 22:42:40 -04:00
delete settings.widget[widget.id];
WIDGETS.remove(widget);
Widget.Groups.remove(widget);
SPACE.platform.remove(widget.mesh);
selectedMeshes.remove(widget.mesh);
updateSliderMax();
platform.compute_max_z();
if (MODE !== MODES.FDM) platform.layout();
SPACE.update();
2020-03-10 14:23:42 -04:00
platformUpdateSelected();
2020-04-10 11:53:07 -04:00
if (layoutOnAdd) platform.layout();
API.event.emit('widget.delete', widget);
}
function platformSelectAll() {
forAllWidgets(function(w) { platform.select(w, true) })
}
function platformLayout(event, space) {
2020-04-09 14:52:43 -04:00
let auto = UI.autoLayout.checked,
2018-03-25 19:21:07 -04:00
proc = settings.process,
oldmode = viewMode,
layout = (viewMode === VIEWS.ARRANGE && auto),
topZ = MODE === MODES.CAM ? camTopZ - proc.camZTopOffset : 0;
2017-04-18 17:09:33 -04:00
2018-03-25 19:21:07 -04:00
switch (MODE) {
2020-04-24 21:02:36 -04:00
case MODES.SLA:
space = space || (proc.slaSupportLayers && proc.slaSupportDensity ? 2 : 1);
break;
2018-03-25 19:21:07 -04:00
case MODES.CAM:
case MODES.LASER:
space = space || proc.outputTileSpacing || 1;
break;
case MODES.FDM:
space = space || ((proc.sliceSupportExtra || 0) * 2) + 1;
2018-03-25 19:21:07 -04:00
break;
}
2017-04-18 17:09:33 -04:00
setViewMode(VIEWS.ARRANGE);
hideSlices();
// only auto-layout when in arrange mode
if (oldmode !== VIEWS.ARRANGE) {
return SPACE.update();
}
2017-04-18 17:09:33 -04:00
// do not layout when switching back from slice view
if (!auto || (!space && !layout)) {
return SPACE.update();
}
2017-04-18 17:09:33 -04:00
2020-04-09 14:52:43 -04:00
let gap = space;
2018-03-25 19:21:07 -04:00
2017-04-18 17:09:33 -04:00
// in CNC mode with >1 widget, force layout with spacing @ 1.5x largest tool diameter
if (MODE === MODES.CAM && WIDGETS.length > 1) {
2020-04-09 14:52:43 -04:00
let spacing = space || 1, CAM = KIRI.driver.CAM;
2017-04-18 17:09:33 -04:00
if (proc.roughingOn) spacing = Math.max(spacing, CAM.getToolDiameter(settings, proc.roughingTool));
if (proc.finishingOn || proc.finishingXOn || proc.finishingYOn) spacing = Math.max(spacing, CAM.getToolDiameter(settings, proc.finishingTool));
gap = spacing * 1.5;
}
2020-04-09 14:52:43 -04:00
let i, m, sz = SPACE.platform.size(),
2017-04-18 17:09:33 -04:00
mp = [sz.x, sz.y],
ms = [mp[0] / 2, mp[1] / 2],
mi = mp[0] > mp[1] ? [(mp[0] / mp[1]) * 10, 10] : [10, (mp[1] / mp[1]) * 10],
c = Widget.Groups.blocks().sort(function (a, b) { return (b.w * b.h) - (a.w * a.h) }),
2017-04-18 17:09:33 -04:00
p = new MOTO.Pack(ms[0], ms[1], gap).fit(c);
2018-03-25 19:21:07 -04:00
2017-04-18 17:09:33 -04:00
while (!p.packed) {
ms[0] += mi[0];
ms[1] += mi[1];
p = new MOTO.Pack(ms[0], ms[1], gap).fit(c);
}
2018-03-25 19:21:07 -04:00
2017-04-18 17:09:33 -04:00
for (i = 0; i < c.length; i++) {
m = c[i];
m.fit.x += m.w / 2 + p.pad;
m.fit.y += m.h / 2 + p.pad;
m.move(p.max.w / 2 - m.fit.x, p.max.h / 2 - m.fit.y, 0, true);
// m.material.visible = true;
2017-04-18 17:09:33 -04:00
}
if (MODE === MODES.CAM) {
platform.update_stock(true);
}
2020-03-12 17:09:44 -04:00
platform.update_origin();
2017-04-18 17:09:33 -04:00
SPACE.update();
}
function platformUpdateStock(refresh) {
let sd = settings.process;
let offset = UI.camStockOffset.checked;
2020-05-17 00:02:51 -04:00
let stockSet = offset || (sd.camStockX && sd.camStockY && sd.camStockZ > 0);
let scale = unitScale();
settings.stock = { };
camTopZ = topZ;
// create/inject cam stock if stock size other than default
if (MODE === MODES.CAM && stockSet && WIDGETS.length) {
2020-06-05 23:21:53 -04:00
// UI.stock.style.display = offset ? 'inline-block' : 'none';
let csx = sd.camStockX * scale;
let csy = sd.camStockY * scale;
let csz = sd.camStockZ * scale;
let csox = 0;
let csoy = 0;
if (offset) {
let min = { x: Infinity, y: Infinity, z: 0 };
let max = { x: -Infinity, y: -Infinity, z: -Infinity };
forAllWidgets(function(widget) {
let wbnd = widget.getBoundingBox(refresh);
let wpos = widget.track.pos;
min = {
x: Math.min(min.x, wpos.x + wbnd.min.x),
y: Math.min(min.y, wpos.y + wbnd.min.y),
z: 0
};
max = {
x: Math.max(max.x, wpos.x + wbnd.max.x),
y: Math.max(max.y, wpos.y + wbnd.max.y),
z: Math.max(max.z, wbnd.max.z)
};
});
csx += max.x - min.x;
csy += max.y - min.y;
csz += max.z - min.z;
csox = min.x + ((max.x - min.x) / 2);
csoy = min.y + ((max.y - min.y) / 2);
}
if (!camStock) {
2020-04-09 14:52:43 -04:00
let geo = new THREE.BoxGeometry(1, 1, 1);
let mat = new THREE.MeshBasicMaterial({ color: 0x777777, opacity: 0.2, transparent: true, side:THREE.DoubleSide });
let cube = new THREE.Mesh(geo, mat);
SPACE.platform.add(cube);
camStock = cube;
}
settings.stock = {
x: csx,
y: csy,
z: csz
};
camStock.scale.x = csx;
camStock.scale.y = csy;
camStock.scale.z = csz;
camStock.position.x = csox;
camStock.position.y = csoy;
camStock.position.z = csz / 2;
camStock.material.visible = settings.mode === 'CAM';
camTopZ = csz;
} else if (camStock) {
// UI.stock.style.display = 'none';
SPACE.platform.remove(camStock);
camStock = null;
camTopZ = topZ;
2017-04-18 17:09:33 -04:00
}
platform.update_top_z();
platform.update_origin();
SPACE.update();
2017-04-18 17:09:33 -04:00
}
/** ******************************************************************
* Settings Functions
******************************************************************* */
2017-04-18 17:09:33 -04:00
2020-04-09 14:52:43 -04:00
// given a settings region, update values of matching bound UI fields
2017-04-18 17:09:33 -04:00
function updateFieldsFromSettings(scope) {
if (!scope) return console.trace("missing scope");
2020-04-09 14:52:43 -04:00
for (let key in scope) {
2017-04-18 17:09:33 -04:00
if (!scope.hasOwnProperty(key)) continue;
2020-04-09 14:52:43 -04:00
let val = scope[key];
2017-04-18 17:09:33 -04:00
if (UI.hasOwnProperty(key)) {
2020-04-09 14:52:43 -04:00
let uie = UI[key], typ = uie ? uie.type : null;
2017-04-18 17:09:33 -04:00
if (typ === 'text') {
uie.value = val;
} else if (typ === 'checkbox') {
uie.checked = val;
} else if (typ === 'select-one') {
2020-04-15 21:58:04 -04:00
uie.innerHTML = '';
2020-04-09 14:52:43 -04:00
let source = uie.parentNode.getAttribute('source'),
list = settings[source] || lists[source],
2020-04-09 14:52:43 -04:00
chosen = null;
if (list) list.forEach(function(tool, index) {
2019-05-02 15:09:54 -04:00
let id = tool.id || tool.name;
if (val == id) {
chosen = index;
2019-05-02 15:09:54 -04:00
}
2020-04-09 14:52:43 -04:00
let opt = DOC.createElement('option');
2017-04-18 17:09:33 -04:00
opt.appendChild(DOC.createTextNode(tool.name));
2019-05-02 15:09:54 -04:00
opt.setAttribute('value', id);
2017-04-18 17:09:33 -04:00
uie.appendChild(opt);
});
if (chosen) uie.selectedIndex = chosen;
} else if (typ === 'textarea') {
if (Array.isArray(val)) {
uie.value = val.join('\n');
2020-04-19 19:09:13 -04:00
} else {
uie.value = '';
}
2017-04-18 17:09:33 -04:00
}
}
}
}
/**
* @returns {Object}
*/
function updateSettingsFromFields(scope) {
if (!scope) return console.trace("missing scope");
2020-04-09 14:52:43 -04:00
let key, changed = false;
2017-04-18 17:09:33 -04:00
// for each key in scope object
for (key in scope) {
2020-04-09 14:52:43 -04:00
if (!scope.hasOwnProperty(key)) {
continue;
}
2017-04-18 17:09:33 -04:00
if (UI.hasOwnProperty(key)) {
2020-04-09 14:52:43 -04:00
let nval = null, uie = UI[key];
2017-04-18 17:09:33 -04:00
// skip empty UI values
2020-04-09 14:52:43 -04:00
if (!uie || uie === '') {
continue;
}
2017-04-18 17:09:33 -04:00
if (uie.type === 'text') {
nval = UI[key].convert();
} else if (uie.type === 'checkbox') {
nval = UI[key].checked;
} else if (uie.type === 'select-one') {
if (uie.selectedIndex >= 0) {
2019-05-02 15:09:54 -04:00
nval = uie.options[uie.selectedIndex].value;
let src = uie.parentNode.getAttribute('source');
2019-05-02 15:09:54 -04:00
if (src === 'tools') {
nval = parseInt(nval);
}
} else {
nval = scope[key];
2017-04-18 17:09:33 -04:00
}
2020-04-10 13:12:13 -04:00
} else if (uie.type === 'textarea') {
2020-04-19 19:09:13 -04:00
nval = uie.value.trim().split('\n').filter(v => v !== '');
2020-04-09 14:52:43 -04:00
} else {
continue;
2017-04-18 17:09:33 -04:00
}
if (scope[key] != nval) {
scope[key] = nval;
}
}
}
return settings;
}
function updateFields() {
updateFieldsFromSettings(settings.device);
updateFieldsFromSettings(settings.process);
updateFieldsFromSettings(settings.layers);
updateFieldsFromSettings(settings.controller);
2020-04-10 12:27:08 -04:00
updateExtruderFields(settings.device);
}
function updateExtruderFields(device) {
2020-04-09 14:52:43 -04:00
if (device.extruders && device.extruders[device.internal]) {
updateFieldsFromSettings(device.extruders[device.internal]);
UI.extruder.firstChild.innerText = `${LANG.dv_gr_ext} [${device.internal+1}/${device.extruders.length}]`;
2020-04-10 12:27:08 -04:00
UI.extPrev.disabled = device.internal === 0;
UI.extPrev.onclick = function() {
device.internal--;
updateExtruderFields(device);
};
UI.extNext.disabled = device.internal === device.extruders.length - 1;
UI.extNext.onclick = function() {
device.internal++;
updateExtruderFields(device);
};
2020-04-10 13:12:13 -04:00
UI.extDel.disabled = device.extruders.length < 2;
UI.extDel.onclick = function() {
device.extruders.splice(device.internal,1);
2020-04-10 13:24:45 -04:00
device.internal = Math.min(device.internal, device.extruders.length-1);
2020-04-10 13:12:13 -04:00
updateExtruderFields(device);
};
UI.extAdd.onclick = function() {
let copy = clone(device.extruders[device.internal]);
copy.extSelect = [`T${device.extruders.length}`];
device.extruders.push(copy);
2020-04-10 13:12:13 -04:00
device.internal = device.extruders.length - 1;
updateExtruderFields(device);
};
2020-04-09 14:52:43 -04:00
}
2017-04-18 17:09:33 -04:00
}
function updateSettings() {
updateSettingsFromFields(settings.controller);
2017-04-18 17:09:33 -04:00
updateSettingsFromFields(settings.device);
updateSettingsFromFields(settings.process);
updateSettingsFromFields(settings.layers);
2020-04-09 14:52:43 -04:00
let device = settings.device;
if (device.extruders && device.extruders[device.internal]) {
updateSettingsFromFields(device.extruders[device.internal]);
}
2020-03-29 20:23:48 -04:00
API.conf.save();
platform.update_stock();
2020-06-08 11:54:57 -04:00
$('mode-device').innerText = settings.device.deviceName;
$('mode-profile').innerText = settings.process.processName;
2017-04-18 17:09:33 -04:00
}
function saveSettings() {
2020-03-29 20:23:48 -04:00
let view = SPACE.view.save();
2020-04-13 17:42:40 -04:00
if (view.left || view.up) {
settings.controller.view = view;
}
2017-04-18 17:09:33 -04:00
SDB.setItem('ws-settings', JSON.stringify(settings));
API.event.emit('settings.saved', settings);
2017-04-18 17:09:33 -04:00
}
2020-04-07 21:45:21 -04:00
function settingsImport(data, ask) {
if (typeof(data) === 'string') {
try {
data = JSON.parse(atob(data));
} catch (e) {
UC.alert('invalid settings format');
2020-04-09 14:52:43 -04:00
console.log('data',data);
2020-04-07 21:45:21 -04:00
return;
}
}
2020-04-09 14:52:43 -04:00
if (!(data.settings && data.version && data.time)) {
UC.alert('invalid settings format');
2020-04-09 14:52:43 -04:00
console.log('data',data);
2020-04-07 21:45:21 -04:00
return;
}
function doit() {
settings = CONF.normalize(data.settings);
API.conf.save();
API.space.reload();
}
if (ask) {
UC.confirm(`Import settings made in Kiri:Moto version ${data.version} on<br>${new Date(data.time)}?`).then((yes) => {
if (yes) doit();
});
} else {
doit();
2020-04-07 21:45:21 -04:00
}
}
function settingsExport() {
return btoa(JSON.stringify({
settings: settings,
version: KIRI.version,
moto: MOTO.id,
init: SDB.getItem('kiri-init'),
time: Date.now()
}));
}
function platformLoadFiles(files,group) {
let loaded = files.length;
platform.group();
for (let i=0; i<files.length; i++) {
let reader = new FileReader(),
2019-09-17 08:36:20 -04:00
lower = files[i].name.toLowerCase(),
2020-02-26 20:23:32 -05:00
israw = lower.indexOf(".raw") > 0 || lower.indexOf('.') < 0,
2017-04-18 17:09:33 -04:00
isstl = lower.indexOf(".stl") > 0,
2019-09-17 08:36:20 -04:00
issvg = lower.indexOf(".svg") > 0,
2020-04-07 21:45:21 -04:00
isgcode = lower.indexOf(".gcode") > 0 || lower.indexOf(".nc") > 0,
isset = lower.indexOf(".b64") > 0;
2017-04-18 17:09:33 -04:00
reader.file = files[i];
reader.onloadend = function (e) {
if (israw) platform.add(
newWidget(undefined,group)
.loadVertices(JSON.parse(e.target.result).toFloat32())
2020-02-14 09:28:51 -05:00
);
2020-04-12 14:23:35 -04:00
if (isstl) {
if (API.feature.on_add_stl) {
API.feature.on_add_stl(e.target.result);
} else {
platform.add(
newWidget(undefined,group)
.loadVertices(new MOTO.STL().parse(e.target.result))
.saveToCatalog(e.target.file.name)
);
}
}
2019-09-17 08:36:20 -04:00
if (isgcode) loadCode(e.target.result, 'gcode');
if (issvg) loadCode(e.target.result, 'svg');
2020-04-07 21:45:21 -04:00
if (isset) settingsImport(e.target.result, true);
if (--loaded === 0) platform.group_done(isgcode);
2017-04-18 17:09:33 -04:00
};
reader.readAsBinaryString(reader.file);
}
}
function loadFile() {
$('load-file').onchange = function(event) {
DBUG.log(event);
2020-04-07 21:45:21 -04:00
platformLoadFiles(event.target.files);
2017-04-18 17:09:33 -04:00
};
$('load-file').click();
2019-01-18 16:46:17 -05:00
// alert2("drag/drop STL files onto platform to import\nreload page to return to last saved state");
2017-04-18 17:09:33 -04:00
}
2020-03-10 10:40:57 -04:00
function saveWorkspace() {
2020-03-29 20:23:48 -04:00
API.conf.save();
2020-04-09 14:52:43 -04:00
let newWidgets = [],
2020-03-10 10:40:57 -04:00
oldWidgets = js2o(SDB.getItem('ws-widgets'), []);
forAllWidgets(function(widget) {
newWidgets.push(widget.id);
oldWidgets.remove(widget.id);
widget.saveState();
});
SDB.setItem('ws-widgets', o2js(newWidgets));
oldWidgets.forEach(function(wid) {
Widget.deleteFromState(wid);
});
2020-05-26 11:05:21 -04:00
// eliminate dangling saved widgets
ODB.keys(keys => {
keys.forEach(key => {
if (newWidgets.indexOf(key.substring(8)) < 0) {
ODB.remove(key);
}
})
}, "ws-save-" ,"ws-savf");
2020-03-10 10:40:57 -04:00
alert2("workspace saved", 1);
2017-04-18 17:09:33 -04:00
}
2020-03-29 20:23:48 -04:00
function restoreSettings(save) {
2020-04-15 09:51:21 -04:00
let newset = ls2o('ws-settings') || settings;
2017-04-18 17:09:33 -04:00
2020-04-15 09:51:21 -04:00
settings = CONF.normalize(newset);
// override camera from settings
if (settings.controller.view) {
SDB.removeItem('ws-camera');
2017-04-18 17:09:33 -04:00
}
2020-04-15 09:51:21 -04:00
// merge custom filters from localstorage into settings
localFilters.forEach(function(fname) {
let fkey = "gcode-filter-"+fname, ov = ls2o(fkey);
if (ov) settings.devices[fname] = ov;
SDB.removeItem(fkey)
});
SDB.removeItem(localFilterKey);
// save updated settings
if (save) API.conf.save();
2020-04-13 17:42:40 -04:00
return newset;
2020-03-29 20:23:48 -04:00
}
function restoreWorkspace(ondone, skip_widget_load) {
2020-04-13 17:42:40 -04:00
let newset = restoreSettings(true),
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;
2017-04-18 17:09:33 -04:00
updateFields();
platform.update_size();
platform.update_stock();
2017-04-18 17:09:33 -04:00
2020-04-15 15:20:02 -04:00
let fz = SPACE.scene.freeze(true);
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();
}
SPACE.scene.freeze(fz);
2017-04-18 17:09:33 -04:00
2020-03-17 16:32:43 -04:00
if (skip_widget_load) return;
2017-04-18 17:09:33 -04:00
2020-03-17 16:32:43 -04:00
// remove any widgets from platform
2019-05-07 14:35:39 -04:00
forAllWidgets(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);
}
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_top_z();
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.work.clear();
2020-03-09 11:02:09 -04:00
platform.select_all();
platform.delete(selectedMeshes);
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() {
API.event.emit('modal.show', which);
}
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-10 10:40:57 -04:00
function getSettings() {
return settings;
}
function putSettings(newset) {
2020-04-09 14:52:43 -04:00
settings = CONF.normalize(newset);
2020-03-29 20:23:48 -04:00
API.conf.save()
API.space.restore(null, true);
2020-03-10 10:40:57 -04:00
}
2020-03-29 20:23:48 -04:00
function editSettings(e) {
2020-04-09 14:52:43 -04:00
let mode = getMode(),
name = e.target.getAttribute("name"),
load = settings.sproc[mode][name],
edit = prompt(`settings for "${name}"`, JSON.stringify(load));
if (edit) {
try {
settings.sproc[mode][name] = JSON.parse(edit);
2019-07-23 21:12:40 -04:00
if (name === settings.process.processName) {
2020-03-29 20:23:48 -04:00
API.conf.load(null, name);
2019-07-23 21:12:40 -04:00
}
2020-03-29 20:23:48 -04:00
API.conf.save();
} catch (e) {
UC.alert('malformed settings object');
}
}
}
2020-03-29 20:23:48 -04:00
function loadSettings(e, named) {
2020-04-09 14:52:43 -04:00
let mode = getMode(),
2019-07-23 21:12:40 -04:00
name = e ? e.target.getAttribute("load") : named || settings.cproc[mode],
load = settings.sproc[mode][name];
2020-06-12 23:06:37 -04:00
2017-04-18 17:09:33 -04:00
if (!load) return;
2019-07-23 21:12:40 -04:00
2020-06-12 23:06:37 -04:00
// clone loaded process into settings
settings.process = clone(load);
2020-06-12 23:06:37 -04:00
// update process name
2017-04-18 17:09:33 -04:00
settings.process.processName = name;
2020-06-12 23:06:37 -04:00
// set currenet process name for this mode
2018-02-14 20:47:35 -05:00
settings.cproc[mode] = name;
2020-06-12 23:06:37 -04:00
// save named process with the current device
settings.devproc[currentDeviceName()] = name;
2020-06-12 23:06:37 -04:00
// update settings hover-pop with current process name
UI.setNow.innerText = named;
// allow mode driver to take any necessary actions
API.event.emit("settings.load", settings);
2020-06-12 23:06:37 -04:00
// update UI fields to reflect current settings
2020-04-09 14:52:43 -04:00
updateFields();
2020-03-29 20:23:48 -04:00
API.conf.update();
2020-06-12 23:06:37 -04:00
2017-04-18 17:09:33 -04:00
if (e) triggerSettingsEvent();
}
2020-03-29 20:23:48 -04:00
function deleteSettings(e) {
2020-04-09 14:52:43 -04:00
let name = e.target.getAttribute("del");
2017-04-18 17:09:33 -04:00
delete settings.sproc[getMode()][name];
updateSettingsList();
2020-03-29 20:23:48 -04:00
API.conf.save();
2017-04-18 17:09:33 -04:00
triggerSettingsEvent();
}
function updateSettingsList() {
2020-04-09 14:52:43 -04:00
let list = [], s = settings, 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.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'),
2017-04-18 17:09:33 -04:00
del = DOC.createElement('button'),
name = sk;
load.setAttribute('load', sk);
load.onclick = (ev) => {
API.conf.load(undefined, sk);
updateSettingsList();
}
2017-04-18 17:09:33 -04:00
load.appendChild(DOC.createTextNode(sk));
2017-12-14 20:41:19 -05:00
if (sk == settings.process.processName) {
load.setAttribute('class', 'selected')
}
2017-04-18 17:09:33 -04:00
del.setAttribute('del', sk);
del.setAttribute('title', "remove '"+sk+"'");
2020-03-29 20:23:48 -04:00
del.onclick = deleteSettings;
2017-04-18 17:09:33 -04:00
del.appendChild(DOC.createTextNode('x'));
edit.innerHTML = '&uarr;';
edit.setAttribute('name', sk);
edit.setAttribute('title', 'edit');
2020-03-29 20:23:48 -04:00
edit.onclick = editSettings;
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(del);
table.appendChild(row);
});
}
function showSettings() {
updateSettingsList();
2020-06-07 23:02:05 -04:00
showModal("saves");
2017-04-18 17:09:33 -04:00
}
2020-03-09 11:02:09 -04:00
function updateDialogLeft() {
2020-06-05 23:21:53 -04:00
return console.log('TODO updateDialogLeft');
2020-03-09 11:02:09 -04:00
let left = UI.ctrlLeft.getBoundingClientRect();
let right = UI.ctrlRight.getBoundingClientRect();
2017-04-18 17:09:33 -04:00
UI.catalog.style.left = (left.width + 5) + 'px';
UI.devices.style.left = (left.width + 5) + 'px';
UI.tools.style.left = (left.width + 5) + 'px';
UI.settings.style.right = (right.width + 5) + 'px';
}
2020-03-09 11:02:09 -04:00
function showHelp() {
2020-06-10 00:41:08 -04:00
showHelpFile(`/kiri/lang/${KIRI.lang.get()}-help.html?${KIRI.version}`);
2017-04-18 17:09:33 -04:00
}
2020-03-09 11:02:09 -04:00
function showHelpFile(local) {
2017-04-18 17:09:33 -04:00
if (!local) {
WIN.open("//wiki.grid.space/wiki/Kiri:Moto", "_help");
return;
}
ajax(local, function(html) {
UI.help.innerHTML = html;
try {
$('kiri-version').innerHTML = `${LANG.version} ${KIRI.version}`;
} catch (e) { }
2017-04-18 17:09:33 -04:00
showModal('help');
});
2020-04-18 11:34:54 -04:00
API.event.emit('help.show', local);
2017-04-18 17:09:33 -04:00
}
function showLocal() {
showModal('local');
fetch("/api/grid_local")
.then(r => r.json())
.then(j => {
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>`);
for (let k in j) {
let r = j[k].stat;
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-04-09 14:52:43 -04:00
let oldMode = viewMode;
2017-04-18 17:09:33 -04:00
viewMode = mode;
platform.deselect();
2020-04-07 21:45:21 -04:00
updateSelectedInfo();
2017-04-18 17:09:33 -04:00
switch (mode) {
case VIEWS.ARRANGE:
2020-06-08 14:21:10 -04:00
$('lt-back').style.display = '';
KIRI.work.clear();
2020-06-07 23:02:05 -04:00
hideSlider();
clearWidgetCache();
2017-04-18 17:09:33 -04:00
updateSliderMax();
break;
case VIEWS.SLICE:
2020-06-08 14:21:10 -04:00
$('lt-back').style.display = 'flex';
2017-04-18 17:09:33 -04:00
updateSliderMax();
break;
case VIEWS.PREVIEW:
2020-06-08 14:21:10 -04:00
$('lt-back').style.display = 'flex';
2017-04-18 17:09:33 -04:00
break;
default:
DBUG.log("invalid view mode: "+mode);
return;
}
DOC.activeElement.blur();
2017-04-18 17:09:33 -04:00
}
function setExpert(bool) {
2020-06-07 23:02:05 -04:00
UC.setExpert(UI.expert.checked = settings.controller.expert = bool);
}
2017-04-18 17:09:33 -04:00
function getMode() {
return settings.mode;
}
2020-03-07 13:14:22 -05:00
function getModeLower() {
return getMode().toLowerCase();
}
2020-02-08 21:01:00 -05:00
function switchMode(mode) {
setMode(mode, 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]) {
DBUG.log("invalid mode: "+mode);
mode = 'FDM';
}
// change mode constants
2017-04-18 17:09:33 -04:00
settings.mode = mode;
2020-06-09 18:12:59 -04:00
MODE = MODES[mode];
DRIVER = KIRI.driver[mode];
// update mode display
$('app-mode-name').innerHTML = mode;
// highlight relevant device mode button
["fdm","sla","cam","laser"].forEach(dev => {
let cl = $(`mode-${dev}`).classList;
if (dev === mode.toLowerCase()) {
cl.add("dev-sel");
} else {
cl.remove("dev-sel");
}
});
2017-04-18 17:09:33 -04:00
// restore cached device profile for this mode
if (settings.cdev[mode]) {
settings.device = clone(settings.cdev[mode]);
2020-04-18 13:20:00 -04:00
API.event.emit('device.set', currentDeviceName());
}
// really belongs in CAM driver (lots of work / abstraction needed)
if (camStock) {
camStock.material.visible = settings.mode === 'CAM';
2020-06-09 18:12:59 -04:00
}
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
2020-03-29 20:23:48 -04:00
API.conf.load();
API.conf.save();
// other housekeeping
2017-04-18 17:09:33 -04:00
clearWidgetCache();
triggerSettingsEvent();
platformUpdateSelected();
2020-04-11 16:33:10 -04:00
updateFields();
// because device dialog, if showing, needs to be updated
if (modalShowing()) {
API.show.devices();
}
API.space.restore(null, true);
2020-04-18 11:34:54 -04:00
API.event.emit("mode.set", mode);
if (then) {
then();
}
2017-04-18 17:09:33 -04:00
}
function currentDeviceName() {
return settings.filter[getMode()];
}
2017-04-18 17:09:33 -04:00
function setControlsVisible(show) {
2020-06-07 23:02:05 -04:00
$('mid-lcol').style.display = show ? 'flex' : 'none';
$('mid-rcol').style.display = show ? 'flex' : 'none';
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
// run optional module functions NOW before kiri-init has run
2017-04-18 17:09:33 -04:00
if (Array.isArray(self.kirimod)) {
kirimod.forEach(function(mod) { mod(kiri.api) });
}
2017-04-18 17:09:33 -04:00
})();