add simple mesh/group list

This commit is contained in:
Stewart Allen 2022-01-02 16:30:05 -05:00
commit 930e591c44
7 changed files with 149 additions and 29 deletions

View file

@ -5,6 +5,7 @@
(function() { (function() {
let broker = gapp.broker; let broker = gapp.broker;
let call = broker.send;
function init() { function init() {
let moto = self.moto, let moto = self.moto,
@ -36,22 +37,78 @@ function init() {
}); });
space.view.setZoom(zoomrev, zoomspd); space.view.setZoom(zoomrev, zoomspd);
// trigger space event binding
broker.send.space_init({ space, platform });
// start worker // start worker
moto.client.start(`/code/mesh_work?${gapp.version}`); moto.client.start(`/code/mesh_work?${gapp.version}`);
// set app version // trigger space event binding
$h('app-name', "Mesh:Tool"); call.space_init({ space, platform });
$h('app-vers', gapp.version);
// trigger ui building
call.ui_build();
// hide loading curtain // hide loading curtain
$d('curtain','none'); $d('curtain','none');
} }
function ui_build() {
// set app version
$h('app-name', "Mesh:Tool");
$h('app-vers', gapp.version);
let bound = h.bind($('app-body'), [
h.div({id: 'grouplist'})
]);
let { grouplist } = bound;
let api = mesh.api;
let maker = {
update() {
clearTimeout(maker.timer);
maker.timer = setTimeout(maker.do, 10);
},
do() {
let groups = api.group.list()
// map groups to divs
.map(g => h.div([
h.button({
_: 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.id,
class: api.selection.contains(m) ? [ 'selected' ] : [],
onclick() { api.selection.toggle(m); }
}))
)
]));
h.bind(grouplist, groups);
}
};
function update(object, topic) {
console.log({update: object, topic});
}
// listen for api calls
broker.listeners({
model_add: maker.update,
group_add: maker.update,
model_remove: maker.update,
group_remove: maker.update,
selection_update: maker.update,
})
}
// add space event bindings // add space event bindings
broker.subscribe('space_init', data => { function space_init(data) {
let { space, platform } = data; let { space, platform } = data;
let platcolor = 0x00ff00; let platcolor = 0x00ff00;
let api = mesh.api; let api = mesh.api;
@ -64,7 +121,7 @@ broker.subscribe('space_init', data => {
platform.setColor(platcolor); platform.setColor(platcolor);
load.File.load([...evt.dataTransfer.files]) load.File.load([...evt.dataTransfer.files])
.then(data => { .then(data => {
broker.send.space_load(data); call.space_load(data);
}) })
.catch(error => { .catch(error => {
dbug.error(error); dbug.error(error);
@ -127,7 +184,7 @@ broker.subscribe('space_init', data => {
m.debug(); m.debug();
} }
} else { } else {
broker.send.space_debug(); call.space_debug();
} }
break; break;
case 'Escape': case 'Escape':
@ -199,20 +256,19 @@ broker.subscribe('space_init', data => {
return api.objects().length > 0; return api.objects().length > 0;
} }
}); });
}
});
// add object loader // add object loader
broker.subscribe('space_load', data => { function space_load(data) {
mesh.api.group.new(data.flat().map(el => new mesh.model(el))) mesh.api.group.new(data.flat().map(el => new mesh.model(el)))
.centerModels() .centerModels()
.centerXY() .centerXY()
.floor() .floor()
.focus(); .focus();
}); }
// on debug key press // on debug key press
broker.subscribe('space_debug', () => { function space_debug() {
for (let g of mesh.api.group.list()) { for (let g of mesh.api.group.list()) {
let { center, size } = g.bounds(); let { center, size } = g.bounds();
console.group(g.id); console.group(g.id);
@ -230,6 +286,14 @@ broker.subscribe('space_debug', () => {
console.groupEnd(); console.groupEnd();
} }
moto.client.fn.debug(); moto.client.fn.debug();
}
// bind functions to topics
broker.listeners({
ui_build,
space_init,
space_load,
space_debug,
}); });
// remove version cache bust from url // remove version cache bust from url

View file

@ -50,6 +50,10 @@ let selection = {
return mdl; return mdl;
}, },
contains(object) {
return selected.contains(object);
},
// @param group {MeshObject[]} // @param group {MeshObject[]}
set(objects) { set(objects) {
selected = objects; selected = objects;
@ -82,6 +86,9 @@ let selection = {
for (let group of groups) { for (let group of groups) {
group.material(mesh.material.unselected); group.material(mesh.material.unselected);
} }
// prevent selection of model and its group
let mgsel = selected.filter(s => s instanceof mesh.model).map(m => m.group);
selected = selected.filter(sel => !mgsel.contains(sel));
for (let object of selected) { for (let object of selected) {
object.material(mesh.material.selected); object.material(mesh.material.selected);
} }

View file

@ -8,6 +8,7 @@ gapp.register("mesh.group", [
"add.array", // dep: add.array "add.array", // dep: add.array
"add.three", // dep: add.three "add.three", // dep: add.three
"moto.license", // dep: moto.license "moto.license", // dep: moto.license
"moto.broker", // dep: moto.broker
"mesh.object", // dep: mesh.object "mesh.object", // dep: mesh.object
"mesh.model", // dep: mesh.model "mesh.model", // dep: mesh.model
"mesh.api", // dep: mesh.api "mesh.api", // dep: mesh.api
@ -16,12 +17,15 @@ gapp.register("mesh.group", [
let mesh = self.mesh = self.mesh || {}; let mesh = self.mesh = self.mesh || {};
if (mesh.group) return; if (mesh.group) return;
let broker = gapp.broker;
let call = broker.send;
let space = moto.Space; let space = moto.Space;
let worker = moto.client.fn; let worker = moto.client.fn;
mesh.group = class MeshGroup extends mesh.object { mesh.group = class MeshGroup extends mesh.object {
// @param group {mesh.model[]} // @param group {MeshModel[]}
constructor(models = []) { constructor(models = []) {
super(); super();
this.group = new THREE.Group(); this.group = new THREE.Group();
@ -45,6 +49,7 @@ mesh.group = class MeshGroup extends mesh.object {
this.models.addOnce(model); this.models.addOnce(model);
this.group.add(model.mesh); this.group.add(model.mesh);
space.update(); space.update();
call.model_add({model, group:this});
worker.group_add({id: this.id, model:model.id}); worker.group_add({id: this.id, model:model.id});
return this; return this;
} }
@ -61,6 +66,8 @@ mesh.group = class MeshGroup extends mesh.object {
model.group = undefined; model.group = undefined;
this.models.remove(model); this.models.remove(model);
this.group.remove(model.mesh); this.group.remove(model.mesh);
// create message for listeners
call.model_remove({model, group:this});
// trigger sync with worker // trigger sync with worker
worker.group_remove({id: this.id, model:model.id}); worker.group_remove({id: this.id, model:model.id});
model.remove(true); model.remove(true);

View file

@ -18,7 +18,7 @@ gapp.finalize("mesh.work", [
]); ]);
// compensation for space/world/platform rotation // compensation for space/world/platform rotation
let space_rotation = new THREE.Matrix4().makeRotationX(Math.PI / 2); let core_matrix = new THREE.Matrix4().makeRotationX(Math.PI / 2);
let { client, worker } = moto; let { client, worker } = moto;
let cache = {}; let cache = {};
@ -38,14 +38,14 @@ let model = {
let geo = new THREE.BufferGeometry(); let geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3)); geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
geo.computeVertexNormals(); geo.computeVertexNormals();
cacheUpdate(id, { name, geo }); cacheUpdate(id, { name, geo, matrix: core_matrix.clone() });
}, },
debug(data) { debug(data) {
let { matrix, id } = data; let { matrix, id } = data;
let rec = cache[id]; let rec = cache[id];
let geo = rec.geo.clone(); let geo = rec.geo.clone();
let m4 = space_rotation.clone().multiply( new THREE.Matrix4().fromArray(matrix) ); let m4 = core_matrix.clone().multiply( new THREE.Matrix4().fromArray(matrix) );
geo.applyMatrix4(m4); geo.applyMatrix4(m4);
// for debugging state / matrix ops // for debugging state / matrix ops
return geo.attributes.position.array; return geo.attributes.position.array;
@ -59,11 +59,6 @@ let group = {
remove(data) { remove(data) {
let { id, model } = data; let { id, model } = data;
},
create(data) {
let { id, type } = data;
cache[id] = { id, type };
} }
}; };
@ -78,23 +73,23 @@ let object = {
}, },
move(data) { move(data) {
let { id, type, x, y, z } = data; let { id, x, y, z } = data;
}, },
position(data) { position(data) {
let { id, type, x, y, z } = data; let { id, x, y, z } = data;
}, },
rotate(data) { rotate(data) {
let { id, type, x, y, z } = data; let { id, x, y, z } = data;
}, },
rotation(data) { rotation(data) {
let { id, type, x, y, z } = data; let { id, x, y, z } = data;
}, },
qrotation(data) { qrotation(data) {
let { id, type, w, x, y, z } = data; let { id, w, x, y, z } = data;
} }
}; };

View file

@ -23,6 +23,20 @@ gapp.broker = new class Broker {
return Object.keys(this.topics); return Object.keys(this.topics);
} }
// create subscriptions for all functions in an object
// a complement wrapObject on the subscription side
listeners(object, root) {
for (let [key, fn] of Object.entries(object)) {
if (typeof fn === 'function') {
key = root ? `${root}_${key}` : key;
this.subscribe(key, fn);
}
}
}
// attach function to a topic
// creates topic if new topic
// creates a send function if new topic
subscribe(topic, listener) { subscribe(topic, listener) {
if (Array.isArray(topic)) { if (Array.isArray(topic)) {
for (let t of topic) { for (let t of topic) {

View file

@ -58,8 +58,9 @@ function build(data, context) {
func[key] = val; func[key] = val;
elid = elid || `_${nextid++}`; elid = elid || `_${nextid++}`;
} else if (tov === 'object') { } else if (tov === 'object') {
html.push(` ${key}="${val.join(' ')}"`); if (val.length)
} else { html.push(` ${key}="${val.filter(v => v !== undefined).join(' ')}"`);
} else if (tov !== 'undefined') {
html.push(` ${key}="${val}"`); html.push(` ${key}="${val}"`);
} }
} }

View file

@ -140,3 +140,35 @@ body, div {
margin-right: -14px !important; margin-right: -14px !important;
margin-bottom: -14px !important; margin-bottom: -14px !important;
} }
#grouplist {
position: absolute;
margin: 3px 3px 0 0;
right: 0;
top: 0;
}
#grouplist, #grouplist div {
flex-direction: column;
background-color: transparent;
}
#grouplist > div {
border: 1px solid gray;
border-radius: 3px;
padding: 3px;
}
#grouplist .models button {
margin-top: 1px;
}
#grouplist .selected {
background-color: rgba(0,255,0,0.75)
}
#grouplist .vsep {
height: 1px;
margin: 2px 0 1px 0;
background-color: gray;
}