From b2ef253e3feb61f03590b5646417e9a0d06b0e4d Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Thu, 13 Jan 2022 11:59:26 -0500 Subject: [PATCH] persist selection and wireframe. set up material groups for face selections --- src/main/mesh.js | 23 +++++++++------ src/mesh/api.js | 28 +++++++++++++----- src/mesh/group.js | 4 +-- src/mesh/model.js | 73 +++++++++++++++++++++++++++++++++++------------ src/mesh/work.js | 8 ++++-- 5 files changed, 98 insertions(+), 38 deletions(-) diff --git a/src/main/mesh.js b/src/main/mesh.js index a2ff4ef1..5f30c24c 100644 --- a/src/main/mesh.js +++ b/src/main/mesh.js @@ -59,12 +59,6 @@ function init() { }, 100); space.view.setZoom(zoomrev, zoomspd); - // restore preferences - api.prefs.load().then(() => { - let { map } = api.prefs; - api.grid(map.space.grid); - }); - // start worker moto.client.start(`/code/mesh_work?${gapp.version}`); @@ -80,6 +74,7 @@ function init() { // restore space layout and view from previous session function restore_space() { + let { api } = mesh; let count = 0; let space = moto.Space; let mcache = {}; @@ -115,7 +110,19 @@ function restore_space() { // otherwise their setup will corrupt the cache for other restores matrixCache = mcache; store_matrices(); - }) + }).then(() => { + // restore preferences after models are restored + api.prefs.load().then(() => { + let { map } = api.prefs; + let { space } = map; + api.grid(space.grid); + // restore selected state + let selist = space.select; + let smodel = api.model.list().filter(m => selist.contains(m.id)); + let sgroup = api.group.list().filter(m => selist.contains(m.id)); + api.selection.set([...smodel, ...sgroup]); + }); + }); // hide loading curtain $d('curtain','none'); } @@ -259,7 +266,7 @@ function space_init(data) { let { ctrlKey, metaKey, shiftKey } = event; if (shiftKey && metaKey) { // find faces adjacent to point/line clicked - model.select(int.point, int.face); + model.find(int.point, int.face); // console.log(int); } else if (metaKey) { // set focus on intersected face diff --git a/src/mesh/api.js b/src/mesh/api.js index ca352fd2..bb45bcb6 100644 --- a/src/mesh/api.js +++ b/src/mesh/api.js @@ -127,17 +127,21 @@ let selection = { } }, - // update material selections + // update selection and wireframe for all objects update() { for (let group of groups) { - group.material(mesh.material.unselected); + group.select(false); + group.wireframe(prefs.map.space.wire); } // 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)); + // highlight selected for (let object of selected) { - object.material(mesh.material.selected); + object.select(true); } + // update saved selection id list + prefs.save( prefs.map.space.select = selected.map(s => s.id) ); return selection; }, @@ -219,6 +223,7 @@ let group = { add(group) { groups.addOnce(group); space.world.add(group.object); + api.selection.update(); space.update(); return group; }, @@ -377,8 +382,10 @@ let prefs = { map: { info: {}, space: { + wire: false, grid: true, - dark: false + dark: false, + select: [] }, }, @@ -440,10 +447,17 @@ let api = mesh.api = { save(); }, - wireframe(state = {toggle:true}, opt = {opacity:0.15}) { - for (let m of api.model.list()) { - m.wireframe(state, opt); + wireframe(state = {toggle:true}, opt = { }) { + let wire = prefs.map.space.wire; + if (state.toggle) { + wire = !wire; + } else { + wire = state; } + for (let m of api.model.list()) { + m.wireframe(wire, opt); + } + prefs.save( prefs.map.space.wire = wire ); }, selection, diff --git a/src/mesh/group.js b/src/mesh/group.js index 0a6d27aa..75517217 100644 --- a/src/mesh/group.js +++ b/src/mesh/group.js @@ -122,9 +122,9 @@ mesh.group = class MeshGroup extends mesh.object { } } - material() { + select() { for (let model of this.models) { - model.material(...arguments); + model.select(...arguments); } } }; diff --git a/src/mesh/model.js b/src/mesh/model.js index 233fa149..19a55b1b 100644 --- a/src/mesh/model.js +++ b/src/mesh/model.js @@ -25,7 +25,8 @@ let { BufferGeometry, BufferAttribute, DoubleSide, Mesh } = THREE; /** default materials **/ let materials = mesh.material = { - unselected: new MeshPhongMaterial({ + // model unselected + normal: new MeshPhongMaterial({ side: DoubleSide, transparent: true, shininess: 100, @@ -33,7 +34,17 @@ let materials = mesh.material = { color: 0xffff00, opacity: 1 }), - selected: new MeshPhongMaterial({ + // model selected + select: new MeshPhongMaterial({ + side: DoubleSide, + transparent: true, + shininess: 100, + specular: 0x202020, + color: 0x00ee00, + opacity: 1 + }), + // face selected (for groups ranges) + face: new MeshPhongMaterial({ side: DoubleSide, transparent: true, shininess: 100, @@ -64,6 +75,13 @@ mesh.model = class MeshModel extends mesh.object { let dot = text.lastIndexOf('.'); if (dot > 0) file = text.substring(0, dot); + // create local materials + this.mats = { + normal: materials.normal.clone(), + select: materials.select.clone(), + face: materials.face.clone() + }; + this.file = file || 'unnamed'; this.load(mesh || vertices, indices, normals); // persist in db so it can be restored on page load @@ -100,7 +118,11 @@ mesh.model = class MeshModel extends mesh.object { geo.setAttribute('position', new BufferAttribute(vertices, 3)); if (indices) geo.setIndex(new BufferAttribute(indices, 1)); if (!normals) geo.computeVertexNormals(); - let meh = this.mesh = new Mesh(geo, materials.unselected); + let meh = this.mesh = new Mesh(geo, [ + this.mats.normal, + this.mats.face + ]); + geo.addGroup(0, Infinity, 0); meh.receiveShadow = true; meh.castShadow = true; meh.renderOrder = 1; @@ -162,6 +184,7 @@ mesh.model = class MeshModel extends mesh.object { this.move(mid.x, mid.y, mid.z); } + // get, set, or toggle visibility of model and wireframe visible(bool) { if (bool === undefined) { return this.mesh.visible; @@ -172,17 +195,27 @@ mesh.model = class MeshModel extends mesh.object { this.mesh.visible = bool; } - material(mat) { - let op = this.opacity(); - this.mesh.material = mat = mat.clone(); - mat.opacity = op; - if (op === 1) this.wireframe(false); + // get, set, or toggle selection of model (coloring) + select(bool) { + if (bool === undefined) { + return this.mesh.material[0] === this.mats.normal; + } + if (bool.toggle) { + return this.select(!this.select()); + } + this.mesh.material[0] = bool ? this.mats.select : this.mats.normal; + return bool; + } + + // return selected state + selected() { + return this.select(); } opacity(ov, opt = {}) { let mat = this.mesh.material; if (ov === undefined) { - return mat.opacity; + return mat[0].opacity; } if (ov.restore) { ov = this._op; @@ -191,14 +224,16 @@ mesh.model = class MeshModel extends mesh.object { } else { this._op = ov; } - if (ov <= 0.0) { - mat.transparent = false; - mat.opacity = 1; - mat.visible = false; - } else { - mat.transparent = true; - mat.opacity = ov; - mat.visible = true; + for (let m of Object.values(this.mats)) { + if (ov <= 0.0) { + m.transparent = false; + m.opacity = 1; + m.visible = false; + } else { + m.transparent = true; + m.opacity = ov; + m.visible = true; + } } space.update(); } @@ -228,7 +263,7 @@ mesh.model = class MeshModel extends mesh.object { if (bool) { this._wire = new Mesh(this.mesh.geometry.shallowClone(), materials.wireframe); this.mesh.add(this._wire); - this.opacity({temp: opt.opacity || 0}); + this.opacity({temp: opt.opacity || 0.15}); } space.update(); return was; @@ -254,7 +289,7 @@ mesh.model = class MeshModel extends mesh.object { } // find adjacent faces to clicked point/line on a face - select(point, face) { + find(point, face) { let geometry = new THREE.SphereGeometry( 0.5, 16, 16 ); let material = new MeshPhongMaterial( { color: 0x777777, transparent: true, opacity: 0.25 } ); let sphere = new Mesh( geometry, material ); diff --git a/src/mesh/work.js b/src/mesh/work.js index 55830674..df0c0d96 100644 --- a/src/mesh/work.js +++ b/src/mesh/work.js @@ -112,10 +112,13 @@ let model = { // distance tolerance for click to vertex (rough distance) let eps = 1; let faces = []; + let verts = []; + let edges = []; for (let i=0, l=arr.length; i