2021-12-27 10:14:21 -05:00
|
|
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
|
|
|
|
|
|
|
|
|
"use strict";
|
2021-12-26 13:59:02 -05:00
|
|
|
|
2022-02-20 12:56:43 -05:00
|
|
|
// dep: moto.license
|
|
|
|
|
// dep: moto.webui
|
|
|
|
|
// dep: moto.client
|
|
|
|
|
// dep: moto.broker
|
|
|
|
|
// dep: moto.space
|
|
|
|
|
// dep: data.index
|
|
|
|
|
// dep: mesh.api
|
2024-07-21 13:48:18 -04:00
|
|
|
// dep: mesh.split
|
2024-07-21 23:27:50 -04:00
|
|
|
// dep: mesh.edges
|
2022-02-20 12:56:43 -05:00
|
|
|
// dep: mesh.model
|
|
|
|
|
// dep: mesh.build
|
|
|
|
|
// dep: load.file
|
2022-06-16 18:24:49 -04:00
|
|
|
// use: geo.polygons
|
2022-02-20 12:56:43 -05:00
|
|
|
gapp.main("main.mesh", [], (root) => {
|
|
|
|
|
|
2024-07-21 13:48:18 -04:00
|
|
|
const { Quaternion } = THREE;
|
2022-02-20 12:56:43 -05:00
|
|
|
const { broker } = gapp;
|
2024-07-23 13:08:17 -04:00
|
|
|
const { mesh, moto } = root;
|
2022-02-20 12:56:43 -05:00
|
|
|
const { space } = moto;
|
|
|
|
|
|
2024-07-23 22:46:38 -04:00
|
|
|
const version = mesh.version = '1.4.4';
|
2022-02-20 12:56:43 -05:00
|
|
|
const call = broker.send;
|
|
|
|
|
const dbindex = [ "admin", "space" ];
|
2021-12-26 20:01:56 -05:00
|
|
|
|
2022-01-12 15:55:09 -05:00
|
|
|
// set below. called once the DOM readyState = complete
|
|
|
|
|
// this is the main() entrypoint called after all dependents load
|
2021-12-26 13:59:02 -05:00
|
|
|
function init() {
|
2022-01-12 10:52:24 -05:00
|
|
|
let stores = data.open('mesh', { stores: dbindex, version: 4 }).init(),
|
2021-12-26 13:59:02 -05:00
|
|
|
dark = false,
|
|
|
|
|
ortho = false,
|
2021-12-27 16:24:39 -05:00
|
|
|
zoomrev = true,
|
2021-12-26 13:59:02 -05:00
|
|
|
zoomspd = 1,
|
2022-01-03 17:13:50 -05:00
|
|
|
platform = space.platform,
|
|
|
|
|
db = mesh.db = {
|
|
|
|
|
admin: stores.promise('admin'),
|
2022-01-12 10:52:24 -05:00
|
|
|
space: stores.promise('space')
|
2022-01-03 17:13:50 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// mark init time and use count
|
|
|
|
|
db.admin.put("init", Date.now());
|
|
|
|
|
db.admin.get("uses").then(v => db.admin.put("uses", (v||0) + 1));
|
2021-12-26 13:59:02 -05:00
|
|
|
|
2021-12-26 17:17:34 -05:00
|
|
|
// setup default workspace
|
2022-01-28 16:54:53 -05:00
|
|
|
space.setAntiAlias(true);
|
2022-01-01 22:39:49 -05:00
|
|
|
space.useDefaultKeys(false);
|
2022-01-19 14:16:26 -05:00
|
|
|
space.init($('container'), delta => { }, ortho);
|
2021-12-28 13:02:01 -05:00
|
|
|
space.sky.set({
|
2022-01-07 22:07:49 -05:00
|
|
|
grid: false,
|
2021-12-28 13:02:01 -05:00
|
|
|
color: dark ? 0 : 0xffffff
|
|
|
|
|
});
|
|
|
|
|
platform.set({
|
|
|
|
|
volume: false,
|
|
|
|
|
round: false,
|
2022-01-15 22:24:54 -05:00
|
|
|
zOffset: 0,
|
2022-01-07 21:13:31 -05:00
|
|
|
opacity: 0,
|
2022-01-02 21:25:39 -05:00
|
|
|
color: 0xdddddd,
|
2021-12-28 13:02:01 -05:00
|
|
|
zoom: { reverse: true, speed: 1 },
|
2022-01-19 22:06:55 -05:00
|
|
|
size: { width: 2000, depth: 2000, height: 0.05, maxz: 2000 },
|
2022-01-16 16:20:42 -05:00
|
|
|
grid: { major: 25, minor: 5, zOffset: 0,
|
2022-01-03 17:13:50 -05:00
|
|
|
colorMajor: 0xcccccc, colorMinor: 0xeeeeee,
|
2022-01-03 19:58:49 -05:00
|
|
|
colorX: 0xff7777, colorY: 0x7777ff },
|
2021-12-28 13:02:01 -05:00
|
|
|
});
|
2022-01-04 09:48:36 -05:00
|
|
|
platform.onMove(() => {
|
|
|
|
|
// save last location and focus
|
|
|
|
|
db.admin.put('camera', {
|
|
|
|
|
place: space.view.save(),
|
|
|
|
|
focus: space.view.getFocus()
|
|
|
|
|
});
|
|
|
|
|
}, 100);
|
2021-12-26 20:01:56 -05:00
|
|
|
space.view.setZoom(zoomrev, zoomspd);
|
2021-12-26 19:43:48 -05:00
|
|
|
|
2022-01-18 13:41:11 -05:00
|
|
|
// reload stored space when worker is ready
|
|
|
|
|
moto.client.on('ready', restore_space);
|
|
|
|
|
|
2021-12-29 00:19:22 -05:00
|
|
|
// start worker
|
|
|
|
|
moto.client.start(`/code/mesh_work?${gapp.version}`);
|
|
|
|
|
|
2022-01-02 16:30:05 -05:00
|
|
|
// trigger space event binding
|
|
|
|
|
call.space_init({ space, platform });
|
|
|
|
|
|
|
|
|
|
// trigger ui building
|
|
|
|
|
call.ui_build();
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-04 09:48:36 -05:00
|
|
|
// restore space layout and view from previous session
|
2022-11-15 23:45:45 -05:00
|
|
|
async function restore_space() {
|
|
|
|
|
const { api } = mesh;
|
|
|
|
|
const space = moto.space;
|
|
|
|
|
const db_admin = mesh.db.admin;
|
|
|
|
|
const db_space = mesh.db.space;
|
|
|
|
|
// let mcache = {};
|
|
|
|
|
await db_admin.get("camera")
|
2022-01-04 09:48:36 -05:00
|
|
|
.then(saved => {
|
2022-01-04 11:08:54 -05:00
|
|
|
if (saved) {
|
|
|
|
|
space.view.load(saved.place);
|
|
|
|
|
space.view.setFocus(saved.focus);
|
|
|
|
|
}
|
2022-01-04 09:48:36 -05:00
|
|
|
});
|
2022-11-15 23:45:45 -05:00
|
|
|
const mcache = await db_admin.get("matrices") || {};
|
|
|
|
|
let count = 0;
|
|
|
|
|
await db_space.iterate({ map: true }).then(cached => {
|
2022-10-25 18:23:00 -04:00
|
|
|
const keys = [];
|
|
|
|
|
const claimed = [];
|
2022-01-03 17:13:50 -05:00
|
|
|
for (let [id, data] of Object.entries(cached)) {
|
2024-07-24 12:29:55 -04:00
|
|
|
// console.log({ id, data });
|
2022-10-25 18:23:00 -04:00
|
|
|
keys.push(id);
|
2022-01-08 22:06:50 -05:00
|
|
|
if (count++ === 0) {
|
2022-03-31 15:24:11 -04:00
|
|
|
mesh.api.log.emit(`restoring workspace`);
|
2022-01-08 22:06:50 -05:00
|
|
|
}
|
2024-07-24 12:29:55 -04:00
|
|
|
// restore object based on type
|
|
|
|
|
// group arrays load models they contain
|
|
|
|
|
// sketches are loaded by type since they're not grouped
|
2022-01-03 17:13:50 -05:00
|
|
|
if (Array.isArray(data)) {
|
2022-10-25 18:23:00 -04:00
|
|
|
claimed.push(id);
|
2022-01-09 21:05:14 -05:00
|
|
|
let models = data
|
2022-10-25 18:23:00 -04:00
|
|
|
.map(id => {
|
|
|
|
|
claimed.push(id);
|
|
|
|
|
return { id, md: cached[id] }
|
|
|
|
|
})
|
2022-01-09 21:05:14 -05:00
|
|
|
.filter(r => r.md) // filter cache misses
|
2022-01-11 10:54:31 -05:00
|
|
|
.map(r => new mesh.model(r.md, r.id).applyMatrix(mcache[r.id]));
|
2022-01-20 19:21:26 -05:00
|
|
|
if (models.length) {
|
|
|
|
|
mesh.api.log.emit(`restored ${models.length} model(s)`);
|
|
|
|
|
mesh.api.group.new(models, id).applyMatrix(mcache[id]);
|
|
|
|
|
} else {
|
|
|
|
|
mesh.api.log.emit(`removed empty group ${id}`);
|
2022-11-15 23:45:45 -05:00
|
|
|
db_space.remove(id);
|
2022-01-20 19:21:26 -05:00
|
|
|
}
|
2024-07-24 12:29:55 -04:00
|
|
|
} else if (data.type === 'sketch') {
|
2024-07-24 15:57:32 -04:00
|
|
|
claimed.push(id);
|
|
|
|
|
mesh.api.add.sketch({ id, ...data });
|
2022-01-03 17:13:50 -05:00
|
|
|
}
|
|
|
|
|
}
|
2022-10-25 18:23:00 -04:00
|
|
|
for (let id of claimed) {
|
|
|
|
|
keys.remove(id);
|
|
|
|
|
}
|
|
|
|
|
if (keys.length) {
|
|
|
|
|
mesh.api.log.emit(`removing ${keys.length} unclaimed meshes`);
|
|
|
|
|
}
|
|
|
|
|
// clear out meshes left in the space db along with their matrices
|
|
|
|
|
for (let id of keys) {
|
2022-11-15 23:45:45 -05:00
|
|
|
db_space.remove(id);
|
2022-10-25 18:23:00 -04:00
|
|
|
delete mcache[id];
|
|
|
|
|
}
|
2022-01-11 10:54:31 -05:00
|
|
|
// restore global cache only after objects are restored
|
|
|
|
|
// otherwise their setup will corrupt the cache for other restores
|
|
|
|
|
matrixCache = mcache;
|
|
|
|
|
store_matrices();
|
2022-01-13 11:59:26 -05:00
|
|
|
}).then(() => {
|
|
|
|
|
// restore preferences after models are restored
|
2022-01-18 16:34:42 -05:00
|
|
|
return api.prefs.load().then(() => {
|
2022-01-13 11:59:26 -05:00
|
|
|
let { map } = api.prefs;
|
2022-01-13 17:01:35 -05:00
|
|
|
let { space, mode } = map;
|
2022-01-13 11:59:26 -05:00
|
|
|
api.grid(space.grid);
|
|
|
|
|
// restore selected state
|
2022-01-14 16:45:40 -05:00
|
|
|
let selist = space.select || [];
|
2022-01-13 11:59:26 -05:00
|
|
|
let smodel = api.model.list().filter(m => selist.contains(m.id));
|
|
|
|
|
let sgroup = api.group.list().filter(m => selist.contains(m.id));
|
2022-04-20 16:53:28 -04:00
|
|
|
let tolist = space.tools || [];
|
|
|
|
|
let tmodel = api.model.list().filter(m => tolist.contains(m.id));
|
|
|
|
|
let tgroup = api.group.list().filter(m => tolist.contains(m.id));
|
2024-07-24 15:57:32 -04:00
|
|
|
let sklist = api.sketch.list().filter(s => selist.contains(s.id));
|
|
|
|
|
api.selection.set([...smodel, ...sgroup, ...sklist], [...tmodel, ...tgroup]);
|
2022-01-13 17:01:35 -05:00
|
|
|
// restore edit mode
|
2024-07-21 22:36:37 -04:00
|
|
|
api.mode[mode]();
|
2022-01-18 16:34:42 -05:00
|
|
|
// restore dark mode
|
|
|
|
|
set_darkmode(map.space.dark);
|
2022-01-13 11:59:26 -05:00
|
|
|
});
|
2022-01-18 16:34:42 -05:00
|
|
|
}).finally(() => {
|
|
|
|
|
// hide loading curtain
|
|
|
|
|
$d('curtain','none');
|
2022-02-25 15:09:05 -05:00
|
|
|
if (api.prefs.map.info.welcome !== false) {
|
|
|
|
|
api.welcome(version);
|
|
|
|
|
}
|
2022-01-13 11:59:26 -05:00
|
|
|
});
|
2022-01-03 17:13:50 -05:00
|
|
|
}
|
|
|
|
|
|
2021-12-29 00:19:22 -05:00
|
|
|
// add space event bindings
|
2022-01-02 16:30:05 -05:00
|
|
|
function space_init(data) {
|
2021-12-29 00:19:22 -05:00
|
|
|
let platcolor = 0x00ff00;
|
2024-07-21 13:48:18 -04:00
|
|
|
let { space, platform } = data;
|
|
|
|
|
let { api } = mesh;
|
2022-01-02 00:40:43 -05:00
|
|
|
let { selection } = api;
|
2022-01-01 00:04:35 -05:00
|
|
|
|
2021-12-28 15:52:28 -05:00
|
|
|
// add file drop handler
|
|
|
|
|
space.event.addHandlers(self, [
|
|
|
|
|
'drop', (evt) => {
|
|
|
|
|
estop(evt);
|
2022-01-14 23:30:06 -05:00
|
|
|
platform.set({ opacity: 0, color: platcolor });
|
2022-01-06 17:28:57 -05:00
|
|
|
call.load_files([...evt.dataTransfer.files]);
|
2021-12-28 15:52:28 -05:00
|
|
|
},
|
2021-12-29 00:19:22 -05:00
|
|
|
'dragover', evt => {
|
2021-12-28 15:52:28 -05:00
|
|
|
estop(evt);
|
|
|
|
|
evt.dataTransfer.dropEffect = 'copy';
|
|
|
|
|
let color = platform.setColor(0x00ff00);
|
|
|
|
|
if (color !== 0x00ff00) platcolor = color;
|
2022-01-14 23:30:06 -05:00
|
|
|
platform.set({ opacity: 0.1 });
|
2021-12-28 15:52:28 -05:00
|
|
|
},
|
2021-12-29 00:19:22 -05:00
|
|
|
'dragleave', evt => {
|
2022-01-14 23:30:06 -05:00
|
|
|
platform.set({ opacity: 0, color: platcolor });
|
2021-12-31 14:12:42 -05:00
|
|
|
},
|
|
|
|
|
'keypress', evt => {
|
2022-01-06 15:10:03 -05:00
|
|
|
if (api.modal.showing) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-01-18 16:34:42 -05:00
|
|
|
if (evt.key === '?') {
|
2022-02-25 15:09:05 -05:00
|
|
|
return api.welcome(version);
|
2022-01-18 16:34:42 -05:00
|
|
|
}
|
2022-01-02 14:25:28 -05:00
|
|
|
let { shiftKey, metaKey, ctrlKey, code } = evt;
|
|
|
|
|
switch (code) {
|
2022-01-18 16:34:42 -05:00
|
|
|
case 'KeyQ':
|
|
|
|
|
return api.settings();
|
2022-01-11 18:41:49 -05:00
|
|
|
case 'KeyI':
|
2024-07-21 22:36:37 -04:00
|
|
|
return shiftKey ? api.tool.invert() : api.file.import();
|
2022-01-11 18:41:49 -05:00
|
|
|
case 'KeyX':
|
|
|
|
|
return api.file.export();
|
2022-01-08 21:00:19 -05:00
|
|
|
case 'KeyD':
|
2022-04-19 16:51:55 -04:00
|
|
|
return shiftKey && api.tool.duplicate();
|
2022-01-08 21:00:19 -05:00
|
|
|
case 'KeyC':
|
|
|
|
|
return selection.centerXY().focus();
|
2022-01-11 10:54:31 -05:00
|
|
|
case 'KeyF':
|
|
|
|
|
return selection.floor().focus();
|
2022-01-11 16:29:54 -05:00
|
|
|
case 'KeyM':
|
2022-04-19 16:51:55 -04:00
|
|
|
return shiftKey ? api.tool.merge() : api.tool.mirror();
|
2022-01-30 21:56:53 -05:00
|
|
|
case 'KeyU':
|
|
|
|
|
return shiftKey && api.tool.union();
|
2022-01-11 18:41:49 -05:00
|
|
|
case 'KeyA':
|
2024-07-21 22:36:37 -04:00
|
|
|
if (api.mode.is([ api.modes.edge ])) return mesh.edges.add();
|
2022-04-19 16:51:55 -04:00
|
|
|
return shiftKey && api.tool.analyze();
|
2022-01-11 18:41:49 -05:00
|
|
|
case 'KeyR':
|
2022-04-19 16:51:55 -04:00
|
|
|
return shiftKey ? api.tool.rebuild() : api.tool.repair();
|
2022-01-14 14:14:13 -05:00
|
|
|
case 'KeyE':
|
2022-01-12 23:02:08 -05:00
|
|
|
return api.tool.clean();
|
2021-12-31 16:59:38 -05:00
|
|
|
case 'KeyV':
|
2024-07-20 18:47:47 -04:00
|
|
|
return shiftKey ? selection.show() : selection.focus();
|
2022-01-14 14:14:13 -05:00
|
|
|
case 'KeyN':
|
2022-04-19 16:51:55 -04:00
|
|
|
return shiftKey ? estop(evt, api.tool.rename()) : api.normals();
|
2022-01-01 13:43:35 -05:00
|
|
|
case 'KeyW':
|
2022-01-08 21:00:19 -05:00
|
|
|
return api.wireframe();
|
2022-01-07 23:30:19 -05:00
|
|
|
case 'KeyG':
|
2022-04-19 16:51:55 -04:00
|
|
|
return shiftKey ? api.tool.regroup() : api.grid();
|
2022-01-08 22:06:50 -05:00
|
|
|
case 'KeyL':
|
2022-03-31 20:45:49 -04:00
|
|
|
return api.log.toggle({ spinner: false });
|
2022-01-12 00:28:33 -05:00
|
|
|
case 'KeyS':
|
2024-07-21 22:36:37 -04:00
|
|
|
if (!api.mode.is([ api.modes.object ])) return;
|
|
|
|
|
return shiftKey ? selection.visible({toggle:true}) : mesh.split.start();
|
2022-01-01 13:43:35 -05:00
|
|
|
case 'KeyB':
|
2022-01-08 21:00:19 -05:00
|
|
|
return selection.boundsBox({toggle:true});
|
2022-01-01 13:43:35 -05:00
|
|
|
case 'KeyH':
|
2024-07-21 13:48:18 -04:00
|
|
|
return shiftKey ? selection.hide() : space.view.home();
|
2022-01-01 13:43:35 -05:00
|
|
|
case 'KeyT':
|
2022-01-08 21:00:19 -05:00
|
|
|
return space.view.top();
|
2022-01-01 13:43:35 -05:00
|
|
|
case 'KeyZ':
|
2022-01-08 21:00:19 -05:00
|
|
|
return space.view.reset();
|
2021-12-31 16:59:38 -05:00
|
|
|
}
|
2021-12-31 14:12:42 -05:00
|
|
|
},
|
|
|
|
|
'keydown', evt => {
|
2022-01-01 17:28:47 -05:00
|
|
|
let { shiftKey, metaKey, ctrlKey, code } = evt;
|
2022-01-20 15:42:28 -05:00
|
|
|
let rv = (Math.PI / 12);
|
2022-01-06 15:10:03 -05:00
|
|
|
if (api.modal.showing) {
|
|
|
|
|
if (code === 'Escape') {
|
2022-01-20 15:42:28 -05:00
|
|
|
api.modal.cancel();
|
2022-01-06 15:10:03 -05:00
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-01-20 23:35:09 -05:00
|
|
|
let rot, floor = api.prefs.map.space.floor !== false;
|
2022-01-01 17:28:47 -05:00
|
|
|
switch (code) {
|
2022-01-01 15:28:59 -05:00
|
|
|
case 'KeyA':
|
2022-01-01 17:28:47 -05:00
|
|
|
if (metaKey || ctrlKey) {
|
2024-07-21 22:36:37 -04:00
|
|
|
api.mode.object();
|
2022-01-02 00:40:43 -05:00
|
|
|
selection.set(api.group.list());
|
2022-01-01 15:28:59 -05:00
|
|
|
estop(evt);
|
|
|
|
|
}
|
2022-01-01 17:28:47 -05:00
|
|
|
break;
|
2022-01-01 15:28:59 -05:00
|
|
|
case 'Escape':
|
2022-01-02 00:40:43 -05:00
|
|
|
selection.clear();
|
2024-07-21 22:36:37 -04:00
|
|
|
mesh.edges.clear();
|
|
|
|
|
mesh.split.end();
|
2022-01-01 15:28:59 -05:00
|
|
|
estop(evt);
|
|
|
|
|
break;
|
2022-01-01 15:04:24 -05:00
|
|
|
case 'Backspace':
|
|
|
|
|
case 'Delete':
|
2022-01-13 20:23:48 -05:00
|
|
|
let mode = api.mode.get();
|
2022-04-20 16:53:28 -04:00
|
|
|
if ([api.modes.object, api.modes.tool].contains(mode)) {
|
|
|
|
|
selection.delete();
|
|
|
|
|
} else {
|
2022-01-13 20:23:48 -05:00
|
|
|
for (let m of selection.models()) {
|
|
|
|
|
m.deleteSelections(mode);
|
2022-03-31 15:42:41 -04:00
|
|
|
space.refresh()
|
2022-01-13 20:23:48 -05:00
|
|
|
}
|
2022-01-01 15:04:24 -05:00
|
|
|
}
|
2022-01-01 15:28:59 -05:00
|
|
|
estop(evt);
|
2022-01-01 15:04:24 -05:00
|
|
|
break;
|
2021-12-31 14:12:42 -05:00
|
|
|
case 'ArrowUp':
|
2022-01-11 10:54:31 -05:00
|
|
|
rot = selection.rotate(-rv,0,0);
|
2021-12-31 14:12:42 -05:00
|
|
|
break;
|
|
|
|
|
case 'ArrowDown':
|
2022-01-11 10:54:31 -05:00
|
|
|
rot = selection.rotate(rv,0,0);
|
2021-12-31 14:12:42 -05:00
|
|
|
break;
|
|
|
|
|
case 'ArrowLeft':
|
2022-01-01 17:28:47 -05:00
|
|
|
if (shiftKey) {
|
2022-01-11 10:54:31 -05:00
|
|
|
rot = selection.rotate(0,-rv,0);
|
2022-01-01 17:28:47 -05:00
|
|
|
} else {
|
2022-01-20 17:06:07 -05:00
|
|
|
floor = false;
|
2022-01-11 10:54:31 -05:00
|
|
|
rot = selection.rotate(0,0,rv);
|
2022-01-01 17:28:47 -05:00
|
|
|
}
|
2021-12-31 14:12:42 -05:00
|
|
|
break;
|
|
|
|
|
case 'ArrowRight':
|
2022-01-01 17:28:47 -05:00
|
|
|
if (shiftKey) {
|
2022-01-11 10:54:31 -05:00
|
|
|
rot = selection.rotate(0,rv,0);
|
2022-01-01 17:28:47 -05:00
|
|
|
} else {
|
2022-01-20 17:06:07 -05:00
|
|
|
floor = false;
|
2022-01-11 10:54:31 -05:00
|
|
|
rot = selection.rotate(0,0,-rv);
|
2022-01-01 17:28:47 -05:00
|
|
|
}
|
2021-12-31 14:12:42 -05:00
|
|
|
break;
|
|
|
|
|
}
|
2022-01-20 17:06:07 -05:00
|
|
|
if (rot && floor) {
|
2022-01-11 10:54:31 -05:00
|
|
|
// todo future pref to auto-floor or not
|
|
|
|
|
rot.floor(mesh.group);
|
|
|
|
|
}
|
2021-12-28 15:52:28 -05:00
|
|
|
}
|
|
|
|
|
]);
|
2022-01-01 00:04:35 -05:00
|
|
|
|
2022-01-12 23:02:08 -05:00
|
|
|
// mouse hover/click handlers. required to enable model drag in space.js
|
2022-01-01 00:04:35 -05:00
|
|
|
space.mouse.downSelect((int, event) => {
|
2024-07-24 15:57:32 -04:00
|
|
|
if (int?.object?.sketch) {
|
|
|
|
|
int.object.sketch.drag({ start: int.object });
|
|
|
|
|
}
|
2022-01-02 21:25:39 -05:00
|
|
|
return event && event.shiftKey ? api.objects() : undefined;
|
2022-01-01 00:04:35 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
space.mouse.upSelect((int, event) => {
|
2024-07-24 15:57:32 -04:00
|
|
|
if (int?.object?.sketch) {
|
|
|
|
|
int.object.sketch.drag({ end: int.object });
|
|
|
|
|
}
|
2022-01-01 00:04:35 -05:00
|
|
|
if (event && event.target.nodeName === "CANVAS") {
|
2024-07-24 13:28:15 -04:00
|
|
|
const model = int?.object?.model;
|
|
|
|
|
const sketch = int?.object?.sketch;
|
|
|
|
|
const { altKey, ctrlKey, metaKey, shiftKey } = event;
|
2024-07-21 13:48:18 -04:00
|
|
|
if (mesh.split.active()) {
|
|
|
|
|
return mesh.split.select(model);
|
2022-01-15 13:47:03 -05:00
|
|
|
}
|
2022-01-01 00:04:35 -05:00
|
|
|
if (model) {
|
2022-04-20 16:53:28 -04:00
|
|
|
const group = model.group;
|
2022-01-13 17:01:35 -05:00
|
|
|
if (metaKey) {
|
2022-01-03 00:43:35 -05:00
|
|
|
// set focus on intersected face
|
2022-04-20 16:53:28 -04:00
|
|
|
const { x, y, z } = int.point;
|
|
|
|
|
const q = new Quaternion().setFromRotationMatrix(group.object.matrix);
|
2022-01-21 02:35:58 -05:00
|
|
|
// rotate normal using group's matrix
|
2022-10-25 00:33:31 -04:00
|
|
|
const normal = shiftKey ? int.face.normal.applyQuaternion(q) : undefined;
|
2022-01-12 23:02:08 -05:00
|
|
|
// y,z swap due to world rotation for orbit controls
|
2022-01-21 02:35:58 -05:00
|
|
|
api.focus({center: { x, y:-z, z:y }, normal});
|
2022-01-03 00:43:35 -05:00
|
|
|
} else if (ctrlKey) {
|
2022-01-12 23:02:08 -05:00
|
|
|
// rotate selected face towawrd z "floor"
|
|
|
|
|
group.faceDown(int.face.normal);
|
2022-01-20 17:06:07 -05:00
|
|
|
selection.update();
|
2022-01-01 00:04:35 -05:00
|
|
|
} else {
|
2022-04-20 16:53:28 -04:00
|
|
|
const { modes } = api;
|
|
|
|
|
const { surface } = api.prefs.map;
|
|
|
|
|
const opt = { radians: 0, radius: surface.radius };
|
|
|
|
|
const mode = api.mode.get();
|
|
|
|
|
switch(mode) {
|
2024-07-24 16:57:42 -04:00
|
|
|
case modes.sketch:
|
|
|
|
|
api.mode.object();
|
2022-01-13 17:01:35 -05:00
|
|
|
case modes.object:
|
2022-04-20 16:53:28 -04:00
|
|
|
case modes.tool:
|
|
|
|
|
selection.toggle(shiftKey ? model : model.group, mode === modes.tool);
|
2022-01-13 17:01:35 -05:00
|
|
|
break;
|
2022-03-31 15:24:11 -04:00
|
|
|
case modes.surface:
|
2022-04-01 10:21:02 -04:00
|
|
|
opt.radians = surface.radians;
|
2022-01-13 17:01:35 -05:00
|
|
|
case modes.face:
|
|
|
|
|
// find faces adjacent to point/line clicked
|
2022-01-20 17:19:15 -05:00
|
|
|
model.find(int,
|
|
|
|
|
altKey ? { toggle: true } :
|
2022-03-31 15:24:11 -04:00
|
|
|
shiftKey ? { clear: true } : { select: true },
|
2022-04-01 10:21:02 -04:00
|
|
|
opt);
|
2022-01-13 17:01:35 -05:00
|
|
|
break;
|
2024-07-21 22:36:37 -04:00
|
|
|
case modes.edge:
|
|
|
|
|
mesh.edges.select();
|
|
|
|
|
break;
|
2022-01-13 17:01:35 -05:00
|
|
|
}
|
2022-01-01 00:04:35 -05:00
|
|
|
}
|
2024-07-24 13:28:15 -04:00
|
|
|
} else if (sketch) {
|
|
|
|
|
if (metaKey) {
|
|
|
|
|
// set focus on intersected face
|
|
|
|
|
const { x, y, z } = int.point;
|
|
|
|
|
// rotate normal using group's matrix
|
|
|
|
|
const normal = shiftKey ? int.face.normal : undefined;
|
|
|
|
|
// y,z swap due to world rotation for orbit controls
|
|
|
|
|
api.focus({center: { x, y:-z, z:y }, normal});
|
|
|
|
|
} else {
|
|
|
|
|
selection.toggle(sketch);
|
|
|
|
|
}
|
2022-01-01 00:04:35 -05:00
|
|
|
}
|
|
|
|
|
} else {
|
2022-01-16 00:55:28 -05:00
|
|
|
return api.objects().filter(o => o.visible);
|
2022-01-01 00:04:35 -05:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
space.mouse.onDrag((delta, offset, up = false) => {
|
2022-04-21 16:53:31 -04:00
|
|
|
const { mode, modes } = api;
|
2022-01-02 19:52:56 -05:00
|
|
|
if (delta && delta.event.shiftKey) {
|
2024-07-21 22:36:37 -04:00
|
|
|
selection.count() && selection.move(delta.x, delta.y, 0);
|
2022-04-21 16:53:31 -04:00
|
|
|
} else if (mode.is([ modes.object, modes.tool ])) {
|
2022-01-01 00:04:35 -05:00
|
|
|
return api.objects().length > 0;
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-01-02 16:30:05 -05:00
|
|
|
}
|
2021-12-28 15:52:28 -05:00
|
|
|
|
2022-01-06 17:28:57 -05:00
|
|
|
function load_files(files) {
|
2022-01-07 14:39:32 -05:00
|
|
|
mesh.api.log.emit(`loading file...`);
|
2022-05-11 23:18:34 -04:00
|
|
|
let api = mesh.api;
|
|
|
|
|
let has_image = false;
|
2022-10-30 21:23:20 -04:00
|
|
|
let has_svg = false;
|
2022-05-11 23:18:34 -04:00
|
|
|
for (let file of files) {
|
|
|
|
|
has_image = has_image || file.type === 'image/png';
|
2022-10-30 21:23:20 -04:00
|
|
|
has_svg = has_svg || file.name.toLowerCase().indexOf(".svg") > 0;
|
2022-05-11 23:18:34 -04:00
|
|
|
}
|
2022-10-30 21:23:20 -04:00
|
|
|
if (has_svg) {
|
2022-05-11 23:18:34 -04:00
|
|
|
api.modal.dialog({
|
2022-10-30 21:23:20 -04:00
|
|
|
title: `svg import`,
|
|
|
|
|
body: [ h.div({ class: "image-import" }, [
|
|
|
|
|
h.div([
|
|
|
|
|
h.label("extrude"),
|
|
|
|
|
h.input({ id: "extrude_height", value: 5, size: 4 })
|
|
|
|
|
]),
|
|
|
|
|
h.div([
|
|
|
|
|
h.label("repair"),
|
|
|
|
|
h.input({ id: "svg_repair", type: "checkbox", checked: true })
|
|
|
|
|
]),
|
|
|
|
|
h.div([
|
|
|
|
|
h.button({ _: "import", onclick() {
|
|
|
|
|
let { svg_repair, extrude_height } = api.modal.bound;
|
|
|
|
|
load_files_opt(files, {
|
|
|
|
|
soup: svg_repair.checked,
|
|
|
|
|
depth: extrude_height
|
|
|
|
|
});
|
|
|
|
|
api.modal.hide();
|
|
|
|
|
} }),
|
|
|
|
|
])
|
|
|
|
|
]) ]
|
|
|
|
|
});
|
|
|
|
|
} else if (has_image) {
|
|
|
|
|
api.modal.dialog({
|
|
|
|
|
title: `image import`,
|
2022-05-11 23:18:34 -04:00
|
|
|
body: [ h.div({ class: "image-import" }, [
|
|
|
|
|
h.div([
|
|
|
|
|
h.label("invert pixels"),
|
|
|
|
|
h.input({ id: "inv_image", type: "checkbox" })
|
|
|
|
|
]),
|
|
|
|
|
h.div([
|
|
|
|
|
h.label("invert alpha"),
|
|
|
|
|
h.input({ id: "inv_alpha", type: "checkbox" })
|
|
|
|
|
]),
|
|
|
|
|
h.div([
|
|
|
|
|
h.label("border size"),
|
2022-05-11 23:20:36 -04:00
|
|
|
h.input({ id: "img_border", value: 0, size: 4 })
|
2022-05-11 23:18:34 -04:00
|
|
|
]),
|
|
|
|
|
h.div([
|
|
|
|
|
h.label("blur pixels"),
|
2022-05-11 23:20:36 -04:00
|
|
|
h.input({ id: "img_blur", value: 0, size: 4 })
|
2022-05-11 23:18:34 -04:00
|
|
|
]),
|
|
|
|
|
h.div([
|
|
|
|
|
h.label("base pixels"),
|
2022-05-11 23:20:36 -04:00
|
|
|
h.input({ id: "img_base", value: 0, size: 4 })
|
2022-05-11 23:18:34 -04:00
|
|
|
]),
|
|
|
|
|
h.div([
|
2022-10-30 21:23:20 -04:00
|
|
|
h.button({ _: "import", onclick() {
|
2022-05-11 23:18:34 -04:00
|
|
|
let { inv_image, inv_alpha, img_border, img_blur, img_base } = api.modal.bound;
|
|
|
|
|
load_files_opt(files, {
|
|
|
|
|
inv_image: inv_image.checked,
|
|
|
|
|
inv_alpha: inv_alpha.checked,
|
|
|
|
|
border: parseInt(img_border.value || 0),
|
|
|
|
|
blur: parseInt(img_blur.value || 0),
|
|
|
|
|
base: parseInt(img_base.value || 0),
|
|
|
|
|
});
|
|
|
|
|
api.modal.hide();
|
|
|
|
|
} }),
|
|
|
|
|
])
|
|
|
|
|
]) ]
|
|
|
|
|
});
|
|
|
|
|
} else {
|
2022-05-13 15:05:21 -04:00
|
|
|
load_files_opt(files);
|
2022-05-11 23:18:34 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function load_files_opt(files, opt) {
|
|
|
|
|
load.File.load([...files], opt)
|
2022-01-06 17:28:57 -05:00
|
|
|
.then(data => {
|
|
|
|
|
call.space_load(data);
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
dbug.error(error);
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
2022-01-07 14:39:32 -05:00
|
|
|
mesh.api.log.hide();
|
2022-01-06 17:28:57 -05:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-29 00:19:22 -05:00
|
|
|
// add object loader
|
2022-01-02 16:30:05 -05:00
|
|
|
function space_load(data) {
|
2022-06-05 19:17:43 -04:00
|
|
|
if (data && data.length && (data = data.flat()).length)
|
|
|
|
|
mesh.api.group.new(data.map(el => new mesh.model(el)))
|
2022-01-11 22:18:12 -05:00
|
|
|
.promote()
|
2021-12-31 16:59:38 -05:00
|
|
|
.focus();
|
2022-01-02 16:30:05 -05:00
|
|
|
}
|
2021-12-26 13:59:02 -05:00
|
|
|
|
2022-01-09 21:05:14 -05:00
|
|
|
let matrixCache = {};
|
|
|
|
|
|
|
|
|
|
// todo deferred with util
|
|
|
|
|
function store_matrices() {
|
|
|
|
|
mesh.db.admin.put("matrices", matrixCache);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// cache model matrices for page restores
|
|
|
|
|
function object_matrix(data) {
|
|
|
|
|
let { id, matrix } = data;
|
|
|
|
|
matrixCache[id] = matrix.elements;
|
|
|
|
|
store_matrices();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function object_destroy(id) {
|
|
|
|
|
delete matrixCache[id];
|
|
|
|
|
store_matrices();
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-18 16:34:42 -05:00
|
|
|
// listen for changes like dark mode toggle
|
|
|
|
|
function set_darkmode(dark) {
|
2024-07-24 13:28:15 -04:00
|
|
|
let { prefs, model } = mesh.api;
|
2022-02-20 12:56:43 -05:00
|
|
|
let { sky, platform } = moto.space;
|
2022-01-18 20:58:46 -05:00
|
|
|
prefs.map.space.dark = dark;
|
2022-01-18 16:34:42 -05:00
|
|
|
if (dark) {
|
2022-01-19 17:48:29 -05:00
|
|
|
mesh.material.wireframe.color.set(0xaaaaaa);
|
2022-01-19 14:04:04 -05:00
|
|
|
$('app').classList.add('dark');
|
2022-01-18 16:34:42 -05:00
|
|
|
} else {
|
|
|
|
|
mesh.material.wireframe.color.set(0,0,0);
|
2022-01-19 14:04:04 -05:00
|
|
|
$('app').classList.remove('dark');
|
2022-01-18 16:34:42 -05:00
|
|
|
}
|
2022-01-19 14:16:26 -05:00
|
|
|
sky.set({
|
|
|
|
|
color: dark ? 0 : 0xffffff,
|
2022-01-23 20:04:32 -05:00
|
|
|
ambient: { intensity: dark ? 0.55 : 1.1 }
|
2022-01-19 14:16:26 -05:00
|
|
|
});
|
2022-01-18 16:34:42 -05:00
|
|
|
platform.set({
|
2022-01-23 14:06:27 -05:00
|
|
|
light: dark ? 0.08 : 0.08,
|
2022-01-18 16:34:42 -05:00
|
|
|
grid: dark ? {
|
|
|
|
|
colorMajor: 0x666666,
|
|
|
|
|
colorMinor: 0x333333,
|
|
|
|
|
} : {
|
|
|
|
|
colorMajor: 0xcccccc,
|
|
|
|
|
colorMinor: 0xeeeeee,
|
|
|
|
|
},
|
|
|
|
|
});
|
2022-04-15 14:25:36 -04:00
|
|
|
mesh.api.updateFog();
|
2022-01-19 20:42:22 -05:00
|
|
|
platform.setSize();
|
2022-01-18 20:58:46 -05:00
|
|
|
for (let m of model.list()) {
|
|
|
|
|
m.normals({ refresh: true });
|
|
|
|
|
}
|
2022-01-18 16:34:42 -05:00
|
|
|
prefs.save();
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-25 13:56:09 -05:00
|
|
|
function set_normals_length(length) {
|
|
|
|
|
let { prefs } = mesh.api;
|
|
|
|
|
prefs.map.normals.length = length || 1;
|
|
|
|
|
prefs.save();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function set_normals_color(color) {
|
|
|
|
|
let { prefs } = mesh.api;
|
|
|
|
|
let { map } = prefs;
|
|
|
|
|
if (map.space.dark) {
|
|
|
|
|
map.normals.color_dark = color || 0;
|
|
|
|
|
} else {
|
|
|
|
|
map.normals.color_lite = color || 0;
|
|
|
|
|
}
|
|
|
|
|
prefs.save();
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-31 15:24:11 -04:00
|
|
|
function set_surface_radians(radians) {
|
|
|
|
|
let { prefs } = mesh.api;
|
2022-04-01 10:21:02 -04:00
|
|
|
prefs.map.surface.radians = parseFloat(radians || 0.1);
|
|
|
|
|
prefs.save();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function set_surface_radius(radius) {
|
|
|
|
|
let { prefs } = mesh.api;
|
|
|
|
|
prefs.map.surface.radius = parseFloat(radius || 0.2);
|
2022-03-31 15:24:11 -04:00
|
|
|
prefs.save();
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-18 15:49:52 -04:00
|
|
|
function set_wireframe_opacity(opacity) {
|
|
|
|
|
let { prefs } = mesh.api;
|
|
|
|
|
prefs.map.wireframe.opacity = parseFloat(opacity || 0.15);
|
|
|
|
|
prefs.save();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function set_wireframe_fog(fogx) {
|
|
|
|
|
let { prefs } = mesh.api;
|
|
|
|
|
prefs.map.wireframe.fog = parseFloat(fogx || 3);
|
|
|
|
|
prefs.save();
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-02 16:30:05 -05:00
|
|
|
// bind functions to topics
|
|
|
|
|
broker.listeners({
|
2022-01-06 17:28:57 -05:00
|
|
|
load_files,
|
2022-01-09 21:05:14 -05:00
|
|
|
object_matrix,
|
|
|
|
|
object_destroy,
|
2022-02-25 13:56:09 -05:00
|
|
|
space_init,
|
|
|
|
|
space_load,
|
2022-01-18 16:34:42 -05:00
|
|
|
set_darkmode,
|
2022-02-25 13:56:09 -05:00
|
|
|
set_normals_color,
|
|
|
|
|
set_normals_length,
|
2022-04-01 10:21:02 -04:00
|
|
|
set_surface_radians,
|
2022-04-18 15:49:52 -04:00
|
|
|
set_surface_radius,
|
|
|
|
|
set_wireframe_opacity,
|
|
|
|
|
set_wireframe_fog
|
2022-01-01 17:28:47 -05:00
|
|
|
});
|
|
|
|
|
|
2021-12-26 20:01:56 -05:00
|
|
|
// remove version cache bust from url
|
2021-12-29 00:19:22 -05:00
|
|
|
window.history.replaceState({},'','/mesh/');
|
2021-12-26 20:01:56 -05:00
|
|
|
|
|
|
|
|
// setup init() trigger when dom + scripts complete
|
2021-12-29 00:19:22 -05:00
|
|
|
document.onreadystatechange = function() {
|
|
|
|
|
if (document.readyState === 'complete') {
|
2021-12-26 13:59:02 -05:00
|
|
|
init();
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-26 20:01:56 -05:00
|
|
|
|
2022-02-20 12:56:43 -05:00
|
|
|
});
|