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
|
|
|
|
2021-12-26 20:01:56 -05:00
|
|
|
(function() {
|
|
|
|
|
|
2021-12-29 00:19:22 -05:00
|
|
|
let broker = gapp.broker;
|
2022-01-02 16:30:05 -05:00
|
|
|
let call = broker.send;
|
2021-12-26 20:01:56 -05:00
|
|
|
|
2021-12-26 13:59:02 -05:00
|
|
|
function init() {
|
2022-01-03 17:13:50 -05:00
|
|
|
let stores = data.open('mesh', { stores:[ "admin", "cache", "space" ] }).init(),
|
|
|
|
|
moto = self.moto,
|
2022-01-01 00:04:35 -05:00
|
|
|
api = mesh.api,
|
2021-12-27 16:24:39 -05:00
|
|
|
sky = false,
|
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,
|
2021-12-26 20:01:56 -05:00
|
|
|
space = moto.Space,
|
2022-01-03 17:13:50 -05:00
|
|
|
platform = space.platform,
|
|
|
|
|
db = mesh.db = {
|
|
|
|
|
admin: stores.promise('admin'),
|
|
|
|
|
space: stores.promise('space'),
|
|
|
|
|
cache: stores.promise('cache')
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 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-01 22:39:49 -05:00
|
|
|
space.useDefaultKeys(false);
|
2021-12-28 13:02:01 -05:00
|
|
|
space.sky.set({
|
|
|
|
|
grid: sky,
|
|
|
|
|
color: dark ? 0 : 0xffffff
|
|
|
|
|
});
|
2021-12-26 20:01:56 -05:00
|
|
|
space.init($('container'), delta => { }, ortho);
|
2021-12-28 13:02:01 -05:00
|
|
|
platform.set({
|
|
|
|
|
volume: false,
|
|
|
|
|
round: false,
|
2022-01-02 21:25:39 -05:00
|
|
|
zOffset: 0.1,
|
|
|
|
|
opacity: 0.1,
|
|
|
|
|
color: 0xdddddd,
|
2021-12-28 13:02:01 -05:00
|
|
|
zoom: { reverse: true, speed: 1 },
|
2022-01-02 23:47:28 -05:00
|
|
|
size: { width: 2000, depth: 2000, height: 1, maxz: 300 },
|
2022-01-03 17:13:50 -05:00
|
|
|
grid: { major: 25, minor: 5,
|
|
|
|
|
colorMajor: 0xcccccc, colorMinor: 0xeeeeee,
|
|
|
|
|
colorX: 0xffaaaa, colorY: 0xaaaaff },
|
2021-12-28 13:02:01 -05:00
|
|
|
});
|
2021-12-26 20:01:56 -05:00
|
|
|
space.view.setZoom(zoomrev, zoomspd);
|
2021-12-26 19:43:48 -05:00
|
|
|
|
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-03 17:13:50 -05:00
|
|
|
// reload stored space
|
|
|
|
|
moto.client.on('ready', restore_space);
|
|
|
|
|
|
2022-01-02 16:30:05 -05:00
|
|
|
// hide loading curtain
|
|
|
|
|
$d('curtain','none');
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-03 17:13:50 -05:00
|
|
|
function restore_space() {
|
|
|
|
|
mesh.db.space.iterate({ map: true }).then(cached => {
|
|
|
|
|
for (let [id, data] of Object.entries(cached)) {
|
|
|
|
|
// restore group
|
|
|
|
|
if (Array.isArray(data)) {
|
|
|
|
|
let models = data.map(id => {
|
|
|
|
|
let md = cached[id];
|
|
|
|
|
return new mesh.model(md, id);
|
|
|
|
|
});
|
|
|
|
|
mesh.api.group.new(models, id)
|
|
|
|
|
.centerModels()
|
|
|
|
|
.centerXY()
|
|
|
|
|
.floor()
|
|
|
|
|
.focus();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-02 16:30:05 -05:00
|
|
|
function ui_build() {
|
2021-12-29 00:19:22 -05:00
|
|
|
// set app version
|
|
|
|
|
$h('app-name', "Mesh:Tool");
|
|
|
|
|
$h('app-vers', gapp.version);
|
|
|
|
|
|
2022-01-02 16:30:05 -05:00
|
|
|
let bound = h.bind($('app-body'), [
|
|
|
|
|
h.div({id: 'grouplist'})
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
let { grouplist } = bound;
|
|
|
|
|
let api = mesh.api;
|
|
|
|
|
|
2022-01-03 10:50:59 -05:00
|
|
|
function build() {
|
|
|
|
|
// map groups to divs
|
|
|
|
|
let groups = api.group.list()
|
|
|
|
|
.map(g => h.div([
|
|
|
|
|
h.button({
|
|
|
|
|
_: `group`,
|
|
|
|
|
title: g.id,
|
|
|
|
|
class: [ "group",
|
|
|
|
|
api.selection.contains(g) ? 'selected' : undefined
|
|
|
|
|
],
|
|
|
|
|
onclick() { api.selection.toggle(g); }
|
|
|
|
|
}),
|
|
|
|
|
h.div({ class: "vsep" }),
|
|
|
|
|
h.div({ class: "models"},
|
|
|
|
|
// map models to buttons
|
|
|
|
|
g.models.map(m => h.button({
|
|
|
|
|
_: m.file || m.id,
|
|
|
|
|
class: api.selection.contains(m) ? [ 'selected' ] : [],
|
|
|
|
|
onclick() { api.selection.toggle(m); }
|
|
|
|
|
}))
|
|
|
|
|
)
|
|
|
|
|
]));
|
|
|
|
|
h.bind(grouplist, groups);
|
|
|
|
|
}
|
2022-01-02 16:30:05 -05:00
|
|
|
|
|
|
|
|
function update(object, topic) {
|
|
|
|
|
console.log({update: object, topic});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// listen for api calls
|
2022-01-03 10:50:59 -05:00
|
|
|
// create a deferred wrapper to merge multiple rapid events
|
|
|
|
|
let deferred = mesh.util.deferWrap(build);
|
2022-01-02 16:30:05 -05:00
|
|
|
broker.listeners({
|
2022-01-03 10:50:59 -05:00
|
|
|
model_add: deferred,
|
|
|
|
|
group_add: deferred,
|
|
|
|
|
model_remove: deferred,
|
|
|
|
|
group_remove: deferred,
|
|
|
|
|
selection_update: deferred,
|
2022-01-02 16:30:05 -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 { space, platform } = data;
|
|
|
|
|
let platcolor = 0x00ff00;
|
2021-12-31 14:12:42 -05:00
|
|
|
let api = mesh.api;
|
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);
|
|
|
|
|
platform.setColor(platcolor);
|
2021-12-29 23:52:48 -05:00
|
|
|
load.File.load([...evt.dataTransfer.files])
|
2021-12-28 15:52:28 -05:00
|
|
|
.then(data => {
|
2022-01-02 16:30:05 -05:00
|
|
|
call.space_load(data);
|
2021-12-28 15:52:28 -05:00
|
|
|
})
|
2021-12-29 00:19:22 -05:00
|
|
|
.catch(error => {
|
|
|
|
|
dbug.error(error);
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
|
|
|
|
// hide spinner
|
|
|
|
|
});
|
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;
|
|
|
|
|
},
|
2021-12-29 00:19:22 -05:00
|
|
|
'dragleave', evt => {
|
2021-12-28 15:52:28 -05:00
|
|
|
platform.setColor(platcolor);
|
2021-12-31 14:12:42 -05:00
|
|
|
},
|
|
|
|
|
'keypress', evt => {
|
2022-01-02 14:25:28 -05:00
|
|
|
let { shiftKey, metaKey, ctrlKey, code } = evt;
|
|
|
|
|
switch (code) {
|
2022-01-01 13:43:35 -05:00
|
|
|
case 'KeyC':
|
2022-01-02 00:40:43 -05:00
|
|
|
selection.centerXY().focus();
|
2022-01-01 13:43:35 -05:00
|
|
|
break;
|
2021-12-31 16:59:38 -05:00
|
|
|
case 'KeyV':
|
2022-01-02 00:40:43 -05:00
|
|
|
selection.focus();
|
2021-12-31 16:59:38 -05:00
|
|
|
break;
|
2022-01-01 13:43:35 -05:00
|
|
|
case 'KeyW':
|
2022-01-02 23:47:28 -05:00
|
|
|
selection.wireframe({toggle:true}, {opacity:0.15});
|
2022-01-01 13:43:35 -05:00
|
|
|
break;
|
|
|
|
|
case 'KeyB':
|
2022-01-02 00:40:43 -05:00
|
|
|
selection.boundsBox({toggle:true});
|
|
|
|
|
break;
|
2022-01-01 13:43:35 -05:00
|
|
|
case 'KeyH':
|
|
|
|
|
space.view.home();
|
|
|
|
|
break;
|
2022-01-01 15:04:24 -05:00
|
|
|
case 'KeyF':
|
|
|
|
|
space.view.front();
|
|
|
|
|
break;
|
2022-01-01 13:43:35 -05:00
|
|
|
case 'KeyT':
|
|
|
|
|
space.view.top();
|
|
|
|
|
break;
|
|
|
|
|
case 'KeyZ':
|
|
|
|
|
space.view.reset();
|
|
|
|
|
break;
|
2021-12-31 16:59:38 -05:00
|
|
|
}
|
2021-12-31 14:12:42 -05:00
|
|
|
},
|
|
|
|
|
'keydown', evt => {
|
2021-12-31 16:59:38 -05:00
|
|
|
let rv = Math.PI / 16;
|
2022-01-01 17:28:47 -05:00
|
|
|
let { shiftKey, metaKey, ctrlKey, code } = evt;
|
|
|
|
|
switch (code) {
|
2022-01-01 15:28:59 -05:00
|
|
|
case 'KeyA':
|
2022-01-01 17:28:47 -05:00
|
|
|
if (metaKey || ctrlKey) {
|
2022-01-02 00:40:43 -05:00
|
|
|
selection.set(api.group.list());
|
2022-01-01 15:28:59 -05:00
|
|
|
estop(evt);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2022-01-01 17:28:47 -05:00
|
|
|
case 'KeyD':
|
2022-01-02 14:25:28 -05:00
|
|
|
if (shiftKey) {
|
|
|
|
|
for (let m of selection.models()) {
|
|
|
|
|
m.debug();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2022-01-02 16:30:05 -05:00
|
|
|
call.space_debug();
|
2022-01-02 14:25:28 -05:00
|
|
|
}
|
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();
|
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-02 00:40:43 -05:00
|
|
|
for (let s of selection.list()) {
|
2022-01-03 17:13:50 -05:00
|
|
|
selection.remove(s);
|
2022-01-01 15:04:24 -05:00
|
|
|
s.showBounds(false);
|
|
|
|
|
s.remove();
|
|
|
|
|
}
|
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-02 00:40:43 -05:00
|
|
|
selection.rotate(-rv,0,0).floor();
|
2021-12-31 14:12:42 -05:00
|
|
|
break;
|
|
|
|
|
case 'ArrowDown':
|
2022-01-02 00:40:43 -05:00
|
|
|
selection.rotate(rv,0,0).floor();
|
2021-12-31 14:12:42 -05:00
|
|
|
break;
|
|
|
|
|
case 'ArrowLeft':
|
2022-01-01 17:28:47 -05:00
|
|
|
if (shiftKey) {
|
2022-01-02 00:40:43 -05:00
|
|
|
selection.rotate(0,-rv,0).floor();
|
2022-01-01 17:28:47 -05:00
|
|
|
} else {
|
2022-01-02 00:40:43 -05:00
|
|
|
selection.rotate(0,0,rv).floor();
|
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-02 00:40:43 -05:00
|
|
|
selection.rotate(0,rv,0).floor();
|
2022-01-01 17:28:47 -05:00
|
|
|
} else {
|
2022-01-02 00:40:43 -05:00
|
|
|
selection.rotate(0,0,-rv).floor();
|
2022-01-01 17:28:47 -05:00
|
|
|
}
|
2021-12-31 14:12:42 -05:00
|
|
|
break;
|
|
|
|
|
}
|
2021-12-28 15:52:28 -05:00
|
|
|
}
|
|
|
|
|
]);
|
2022-01-01 00:04:35 -05:00
|
|
|
|
|
|
|
|
// mouse hover/click handlers
|
|
|
|
|
space.mouse.downSelect((int, event) => {
|
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) => {
|
|
|
|
|
if (event && event.target.nodeName === "CANVAS") {
|
|
|
|
|
let model = int && int.object.model ? int.object.model : undefined;
|
|
|
|
|
if (model) {
|
|
|
|
|
let group = model.group;
|
2022-01-02 19:52:56 -05:00
|
|
|
let { ctrlKey, metaKey, shiftKey } = event;
|
2022-01-03 00:43:35 -05:00
|
|
|
if (metaKey) {
|
|
|
|
|
// set focus on intersected face
|
|
|
|
|
let { x, y, z } = int.point;
|
|
|
|
|
api.focus({center: { x, y:-z, z:y }});
|
|
|
|
|
} else if (ctrlKey) {
|
|
|
|
|
// lay flat when ctrl clicking a selected face
|
2022-01-01 00:04:35 -05:00
|
|
|
let q = new THREE.Quaternion();
|
|
|
|
|
// find intersecting point, look "up" on Z and rotate to face that
|
|
|
|
|
q.setFromUnitVectors(int.face.normal, new THREE.Vector3(0,0,-1));
|
2022-01-01 15:19:39 -05:00
|
|
|
group.qrotation(q);
|
2022-01-01 10:44:06 -05:00
|
|
|
group.floor();
|
2022-01-01 00:04:35 -05:00
|
|
|
} else {
|
2022-01-02 21:25:39 -05:00
|
|
|
selection.toggle(shiftKey ? model : model.group);
|
2022-01-01 00:04:35 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return api.objects();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
space.mouse.onDrag((delta, offset, up = false) => {
|
2022-01-02 19:52:56 -05:00
|
|
|
if (delta && delta.event.shiftKey) {
|
2022-01-02 00:40:43 -05:00
|
|
|
selection.move(delta.x, delta.y, 0);
|
2022-01-01 00:04:35 -05:00
|
|
|
} else {
|
|
|
|
|
return api.objects().length > 0;
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-01-02 16:30:05 -05:00
|
|
|
}
|
2021-12-28 15:52:28 -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) {
|
2021-12-31 15:30:21 -05:00
|
|
|
mesh.api.group.new(data.flat().map(el => new mesh.model(el)))
|
|
|
|
|
.centerModels()
|
|
|
|
|
.centerXY()
|
2021-12-31 16:59:38 -05:00
|
|
|
.floor()
|
|
|
|
|
.focus();
|
2022-01-02 16:30:05 -05:00
|
|
|
}
|
2021-12-26 13:59:02 -05:00
|
|
|
|
2022-01-01 17:28:47 -05:00
|
|
|
// on debug key press
|
2022-01-02 16:30:05 -05:00
|
|
|
function space_debug() {
|
2022-01-01 17:28:47 -05:00
|
|
|
for (let g of mesh.api.group.list()) {
|
|
|
|
|
let { center, size } = g.bounds();
|
2022-01-02 14:25:28 -05:00
|
|
|
console.group(g.id);
|
|
|
|
|
console.log({
|
2022-01-01 17:28:47 -05:00
|
|
|
center,
|
|
|
|
|
size,
|
|
|
|
|
pos: g.group.position
|
|
|
|
|
});
|
|
|
|
|
for (let m of g.models) {
|
2022-01-02 14:25:28 -05:00
|
|
|
console.log(m.id, {
|
2022-01-01 17:28:47 -05:00
|
|
|
box: m.mesh.getBoundingBox(),
|
|
|
|
|
pos: m.mesh.position
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
console.groupEnd();
|
|
|
|
|
}
|
2022-01-02 14:25:28 -05:00
|
|
|
moto.client.fn.debug();
|
2022-01-02 16:30:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// bind functions to topics
|
|
|
|
|
broker.listeners({
|
|
|
|
|
ui_build,
|
|
|
|
|
space_init,
|
|
|
|
|
space_load,
|
|
|
|
|
space_debug,
|
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
|
|
|
|
2021-12-29 00:19:22 -05:00
|
|
|
// finalize modules
|
|
|
|
|
gapp.finalize("main.mesh", [
|
2021-12-29 16:31:17 -05:00
|
|
|
"moto.license", // dep: moto.license
|
2021-12-29 12:55:45 -05:00
|
|
|
"moto.webui", // dep: moto.webui
|
2021-12-29 10:37:17 -05:00
|
|
|
"moto.client", // dep: moto.client
|
2021-12-29 07:54:16 -05:00
|
|
|
"moto.broker", // dep: moto.broker
|
2021-12-29 16:31:17 -05:00
|
|
|
"moto.space", // dep: moto.space
|
2022-01-03 17:13:50 -05:00
|
|
|
"data.index", // dep: data.index
|
2021-12-29 21:52:24 -05:00
|
|
|
"mesh.api", // dep: mesh.api
|
|
|
|
|
"mesh.model", // dep: mesh.model
|
2021-12-29 16:49:15 -05:00
|
|
|
"load.file", // dep: load.file
|
2021-12-29 00:19:22 -05:00
|
|
|
]);
|
|
|
|
|
|
2021-12-26 20:01:56 -05:00
|
|
|
})();
|