diff --git a/src/main/mesh.js b/src/main/mesh.js index 0d62db51..35b688e8 100644 --- a/src/main/mesh.js +++ b/src/main/mesh.js @@ -112,6 +112,23 @@ function ui_build() { let { grouplist } = bound; let api = mesh.api; + function grid(v1, v2, side = [ "pos", "rot"], top = [ "X", "Y", "Z" ]) { + return h.div({ class: "grid"}, [ + h.div({ _: "" }), + h.div({ _: top[0], class: "top" }), + h.div({ _: top[1], class: "top" }), + h.div({ _: top[2], class: "top" }), + h.div({ _: side[0], class: "side" }), + h.label({ _: v1[0] }), + h.label({ _: v1[1] }), + h.label({ _: v1[2] }), + h.div({ _: side[1], class: "side" }), + h.label({ _: v2[0] }), + h.label({ _: v2[1] }), + h.label({ _: v2[2] }), + ]); + } + function build() { let selHas = api.selection.contains; // map groups to divs @@ -134,25 +151,42 @@ function ui_build() { ) ])); h.bind(grouplist, groups); + let s_grp = api.selection.groups(true); + let s_mdl = api.selection.models(true); // map selection to divs - let sel_grp = api.selection.groups() - .map(g => h.div([ + let h_grp = s_grp.map(g => h.div([ h.button({ _: `group`, title: g.id, class: [ "group" ], }), - h.div({ class: "grid"}, [ - h.div({ _: "X" }), - h.div({ _: "Y" }), - h.div({ _: "Z" }), - h.div({ _: "pos" }), - h.div({ _: "rot" }), - h.label({ id: "gp-x" }), + grid([ + g.object.position.x.toFixed(2), + g.object.position.y.toFixed(2), + g.object.position.z.toFixed(2), + ],[ + g.object.rotation.x.toFixed(2), + g.object.rotation.y.toFixed(2), + g.object.rotation.z.toFixed(2), ]) ])); - let sel_mdl = api.selection.models(); - h.bind(selectlist, sel_grp); + let h_mdl = s_mdl.map(m => h.div([ + h.button({ + _: `model`, + title: m.id, + class: [ "model" ], + }), + grid([ + m.object.position.x.toFixed(2), + m.object.position.y.toFixed(2), + m.object.position.z.toFixed(2), + ],[ + m.object.rotation.x.toFixed(2), + m.object.rotation.y.toFixed(2), + m.object.rotation.z.toFixed(2), + ]) + ])); + h.bind(selectlist, [...h_grp, ...h_mdl]); } // listen for api calls @@ -264,7 +298,7 @@ function space_init(data) { break; case 'Backspace': case 'Delete': - for (let s of selection.list()) { + for (let s of selection.list(true)) { selection.remove(s); s.showBounds(false); s.remove(); diff --git a/src/mesh/api.js b/src/mesh/api.js index 4e5c6ebc..2c1c6615 100644 --- a/src/mesh/api.js +++ b/src/mesh/api.js @@ -23,13 +23,13 @@ let groups = []; let selected = []; let selection = { - // @returns {MeshObject[]} - list() { - return selected.length ? selected.slice() : groups.slice(); + // @returns {MeshObject[]} or all groups if not strict and no selection + list(strict = false) { + return selected.length || strict ? selected.slice() : groups.slice(); }, - groups() { - let all = selection.list(); + groups(strict) { + let all = selection.list(strict); let grp = all.filter(s => s instanceof mesh.group); let mdl = all.filter(s => s instanceof mesh.model); for (let m of mdl) { @@ -38,8 +38,8 @@ let selection = { return grp; }, - models() { - let all = selection.list(); + models(strict) { + let all = selection.list(strict); let grp = all.filter(s => s instanceof mesh.group); let mdl = all.filter(s => s instanceof mesh.model); for (let g of grp) { @@ -180,7 +180,7 @@ let selection = { }, bounds() { - return util.bounds(selected.map(s => s.object())); + return util.bounds(selected.map(s => s.object)); } }; @@ -248,7 +248,7 @@ let api = mesh.api = { objects() { // return model objects suitable for finding ray intersections - return group.list().map(o => o.models).flat().map(o => o.object()); + return group.list().map(o => o.models).flat().map(o => o.object); } }; @@ -295,10 +295,10 @@ let util = mesh.util = { let box = new THREE.Box3(); if (Array.isArray(object)) { for (let o of object) { - util.box3expand(box, o instanceof mesh.object ? o.object() : o); + util.box3expand(box, o instanceof mesh.object ? o.object : o); } } else if (object) { - util.box3expand(box, object instanceof mesh.object ? object.object() : object); + util.box3expand(box, object instanceof mesh.object ? object.object : object); } else { return box; } diff --git a/src/mesh/group.js b/src/mesh/group.js index 83c04044..9c4fe137 100644 --- a/src/mesh/group.js +++ b/src/mesh/group.js @@ -35,11 +35,11 @@ mesh.group = class MeshGroup extends mesh.object { } } - type() { + get type() { return "group"; } - object() { + get object() { return this.group; } @@ -92,7 +92,7 @@ mesh.group = class MeshGroup extends mesh.object { // center objects to group bounds // dependent on first being added to world/scene centerModels() { - let bounds = this.bounds(); + let bounds = this.bounds; for (let model of this.models) { model.center(bounds); } diff --git a/src/mesh/model.js b/src/mesh/model.js index 5f1dca53..deef7992 100644 --- a/src/mesh/model.js +++ b/src/mesh/model.js @@ -26,7 +26,7 @@ let materials = mesh.material = { side: THREE.DoubleSide, transparent: true, shininess: 100, - specular: 0x181818, + specular: 0x202020, color: 0xffff00, opacity: 1 }), @@ -34,7 +34,7 @@ let materials = mesh.material = { side: THREE.DoubleSide, transparent: true, shininess: 100, - specular: 0x181818, + specular: 0x202020, color: 0x00ff00, opacity: 1 }), @@ -68,11 +68,11 @@ mesh.model = class MeshModel extends mesh.object { mapp.db.space.put(this.id, { file, mesh }); } - type() { + get type() { return "model"; } - object() { + get object() { return this.mesh; } diff --git a/src/mesh/object.js b/src/mesh/object.js index 14473cc0..79ce6172 100644 --- a/src/mesh/object.js +++ b/src/mesh/object.js @@ -20,40 +20,40 @@ mesh.object = class MeshObject { constructor(id) { this.id = id || mesh.util.uuid(); - worker.object_create({id: this.id, type: this.type()}); + worker.object_create({id: this.id, type: this.type}); } - type() { + get type() { throw "type() requires implementation"; } // @returns {THREE.Object3D} - object() { + get object() { throw "object() requires implementation"; } - bounds() { - return mesh.util.bounds(this.object()); + get bounds() { + return mesh.util.bounds(this.object); } focus() { - mesh.api.focus(this.object()); + mesh.api.focus(this.object); } floor() { - let b = this.bounds(); + let b = this.bounds; this.move(0, 0, -b.min.z); return this; } center(bounds) { - let b = bounds || this.bounds(); - this.move(-b.center.x, b.center.y, -b.center.z); + let b = bounds || this.bounds; + this.move(-b.center.x, -b.center.y, -b.center.z); return this; } centerXY(bounds) { - let b = bounds || this.bounds(); + let b = bounds || this.bounds; this.move(-b.center.x, -b.center.y, 0); return this; } @@ -68,7 +68,7 @@ mesh.object = class MeshObject { } scale() { - let scale = this.object().scale; + let scale = this.object.scale; if (arguments.length === 0) { return scale; } @@ -80,9 +80,9 @@ mesh.object = class MeshObject { } rotate(x = 0, y = 0, z = 0) { - if (x) this.object().rotateOnWorldAxis(new THREE.Vector3(1,0,0), x); - if (y) this.object().rotateOnWorldAxis(new THREE.Vector3(0,1,0), y); - if (z) this.object().rotateOnWorldAxis(new THREE.Vector3(0,0,1), z); + if (x) this.object.rotateOnWorldAxis(new THREE.Vector3(1,0,0), x); + if (y) this.object.rotateOnWorldAxis(new THREE.Vector3(0,1,0), y); + if (z) this.object.rotateOnWorldAxis(new THREE.Vector3(0,0,1), z); this.updateBoundsBox(); worker.object_rotate({id: this.id, x, y, z}); space.update(); @@ -90,7 +90,7 @@ mesh.object = class MeshObject { } rotation() { - let rotation = this.object().rotation; + let rotation = this.object.rotation; if (arguments.length === 0) { return rotation; } @@ -103,7 +103,7 @@ mesh.object = class MeshObject { } qrotation(quaternion) { - this.object().setRotationFromQuaternion(quaternion); + this.object.setRotationFromQuaternion(quaternion); this.updateBoundsBox(); let { w, x, y, z } = quaternion; worker.object_qrotation({id: this.id, w, x, y, z}); @@ -112,7 +112,7 @@ mesh.object = class MeshObject { } position() { - let pos = this.object().position; + let pos = this.object.position; if (arguments.length === 0) { return pos; } @@ -137,7 +137,7 @@ mesh.object = class MeshObject { world.remove(helper); } if (this._showBounds) { - let { center, size } = this.bounds(); + let { center, size } = this.bounds; let b3 = new THREE.Box3().setFromCenterAndSize( new THREE.Vector3(center.x, center.y, center.z), new THREE.Vector3(size.x, size.y, size.z) diff --git a/src/moto/webui.js b/src/moto/webui.js index 9f92ad5b..c2ce2ab4 100644 --- a/src/moto/webui.js +++ b/src/moto/webui.js @@ -80,7 +80,7 @@ function build(data, context) { } html.appendAll(snips.flat()); } - if (text) { + if (text !== undefined) { html.push(text); } html.push(``); diff --git a/web/mesh/index.css b/web/mesh/index.css index 64ea553f..c2801a93 100644 --- a/web/mesh/index.css +++ b/web/mesh/index.css @@ -197,16 +197,53 @@ button:hover { /** for selection info bottom/right of workspace */ #selectlist { + font-size: smaller; position: absolute; margin: 0 3px 3px 0; bottom: 0; right: 0; + gap: 2px; } #selectlist > div { flex-direction: column; + gap: 2px; } -#selection .grid { - display: grid; +#selectlist label { + border-radius: 3px; + font-family: monospace; + text-align: right; + padding: 2px; + background-color: rgba(0,0,0,0.05); } + +#selectlist .top { + display: block; + font-weight: bold; + font-size: smaller; + text-align: center; +} + +#selectlist .side { + font-weight: bold; + font-size: smaller; + align-items: center; + justify-content: flex-end; +} + +#selectlist .grid { + background-color: rgba(255,255,255,0.9); + border: 1px solid rgba(0,0,0,0.15); + border-radius: 3px; + padding: 3px; + display: grid; + gap: 2px 2px; + grid: min-content 1fr 1fr / min-content 1fr 1fr 1fr; + grid-template-areas: + ". head head head" + "label data data data" + "label data data data"; +} + +#selectlist