persist selection and wireframe. set up material groups for face selections
This commit is contained in:
parent
2a64dee448
commit
b2ef253e3f
5 changed files with 97 additions and 37 deletions
|
|
@ -59,12 +59,6 @@ function init() {
|
||||||
}, 100);
|
}, 100);
|
||||||
space.view.setZoom(zoomrev, zoomspd);
|
space.view.setZoom(zoomrev, zoomspd);
|
||||||
|
|
||||||
// restore preferences
|
|
||||||
api.prefs.load().then(() => {
|
|
||||||
let { map } = api.prefs;
|
|
||||||
api.grid(map.space.grid);
|
|
||||||
});
|
|
||||||
|
|
||||||
// start worker
|
// start worker
|
||||||
moto.client.start(`/code/mesh_work?${gapp.version}`);
|
moto.client.start(`/code/mesh_work?${gapp.version}`);
|
||||||
|
|
||||||
|
|
@ -80,6 +74,7 @@ function init() {
|
||||||
|
|
||||||
// restore space layout and view from previous session
|
// restore space layout and view from previous session
|
||||||
function restore_space() {
|
function restore_space() {
|
||||||
|
let { api } = mesh;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let space = moto.Space;
|
let space = moto.Space;
|
||||||
let mcache = {};
|
let mcache = {};
|
||||||
|
|
@ -115,7 +110,19 @@ function restore_space() {
|
||||||
// otherwise their setup will corrupt the cache for other restores
|
// otherwise their setup will corrupt the cache for other restores
|
||||||
matrixCache = mcache;
|
matrixCache = mcache;
|
||||||
store_matrices();
|
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
|
// hide loading curtain
|
||||||
$d('curtain','none');
|
$d('curtain','none');
|
||||||
}
|
}
|
||||||
|
|
@ -259,7 +266,7 @@ function space_init(data) {
|
||||||
let { ctrlKey, metaKey, shiftKey } = event;
|
let { ctrlKey, metaKey, shiftKey } = event;
|
||||||
if (shiftKey && metaKey) {
|
if (shiftKey && metaKey) {
|
||||||
// find faces adjacent to point/line clicked
|
// find faces adjacent to point/line clicked
|
||||||
model.select(int.point, int.face);
|
model.find(int.point, int.face);
|
||||||
// console.log(int);
|
// console.log(int);
|
||||||
} else if (metaKey) {
|
} else if (metaKey) {
|
||||||
// set focus on intersected face
|
// set focus on intersected face
|
||||||
|
|
|
||||||
|
|
@ -127,17 +127,21 @@ let selection = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// update material selections
|
// update selection and wireframe for all objects
|
||||||
update() {
|
update() {
|
||||||
for (let group of groups) {
|
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
|
// prevent selection of model and its group
|
||||||
let mgsel = selected.filter(s => s instanceof mesh.model).map(m => m.group);
|
let mgsel = selected.filter(s => s instanceof mesh.model).map(m => m.group);
|
||||||
selected = selected.filter(sel => !mgsel.contains(sel));
|
selected = selected.filter(sel => !mgsel.contains(sel));
|
||||||
|
// highlight selected
|
||||||
for (let object of 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;
|
return selection;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -219,6 +223,7 @@ let group = {
|
||||||
add(group) {
|
add(group) {
|
||||||
groups.addOnce(group);
|
groups.addOnce(group);
|
||||||
space.world.add(group.object);
|
space.world.add(group.object);
|
||||||
|
api.selection.update();
|
||||||
space.update();
|
space.update();
|
||||||
return group;
|
return group;
|
||||||
},
|
},
|
||||||
|
|
@ -377,8 +382,10 @@ let prefs = {
|
||||||
map: {
|
map: {
|
||||||
info: {},
|
info: {},
|
||||||
space: {
|
space: {
|
||||||
|
wire: false,
|
||||||
grid: true,
|
grid: true,
|
||||||
dark: false
|
dark: false,
|
||||||
|
select: []
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -440,10 +447,17 @@ let api = mesh.api = {
|
||||||
save();
|
save();
|
||||||
},
|
},
|
||||||
|
|
||||||
wireframe(state = {toggle:true}, opt = {opacity:0.15}) {
|
wireframe(state = {toggle:true}, opt = { }) {
|
||||||
for (let m of api.model.list()) {
|
let wire = prefs.map.space.wire;
|
||||||
m.wireframe(state, opt);
|
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,
|
selection,
|
||||||
|
|
|
||||||
|
|
@ -122,9 +122,9 @@ mesh.group = class MeshGroup extends mesh.object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
material() {
|
select() {
|
||||||
for (let model of this.models) {
|
for (let model of this.models) {
|
||||||
model.material(...arguments);
|
model.select(...arguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,8 @@ let { BufferGeometry, BufferAttribute, DoubleSide, Mesh } = THREE;
|
||||||
|
|
||||||
/** default materials **/
|
/** default materials **/
|
||||||
let materials = mesh.material = {
|
let materials = mesh.material = {
|
||||||
unselected: new MeshPhongMaterial({
|
// model unselected
|
||||||
|
normal: new MeshPhongMaterial({
|
||||||
side: DoubleSide,
|
side: DoubleSide,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
shininess: 100,
|
shininess: 100,
|
||||||
|
|
@ -33,7 +34,17 @@ let materials = mesh.material = {
|
||||||
color: 0xffff00,
|
color: 0xffff00,
|
||||||
opacity: 1
|
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,
|
side: DoubleSide,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
shininess: 100,
|
shininess: 100,
|
||||||
|
|
@ -64,6 +75,13 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
let dot = text.lastIndexOf('.');
|
let dot = text.lastIndexOf('.');
|
||||||
if (dot > 0) file = text.substring(0, dot);
|
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.file = file || 'unnamed';
|
||||||
this.load(mesh || vertices, indices, normals);
|
this.load(mesh || vertices, indices, normals);
|
||||||
// persist in db so it can be restored on page load
|
// 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));
|
geo.setAttribute('position', new BufferAttribute(vertices, 3));
|
||||||
if (indices) geo.setIndex(new BufferAttribute(indices, 1));
|
if (indices) geo.setIndex(new BufferAttribute(indices, 1));
|
||||||
if (!normals) geo.computeVertexNormals();
|
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.receiveShadow = true;
|
||||||
meh.castShadow = true;
|
meh.castShadow = true;
|
||||||
meh.renderOrder = 1;
|
meh.renderOrder = 1;
|
||||||
|
|
@ -162,6 +184,7 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
this.move(mid.x, mid.y, mid.z);
|
this.move(mid.x, mid.y, mid.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get, set, or toggle visibility of model and wireframe
|
||||||
visible(bool) {
|
visible(bool) {
|
||||||
if (bool === undefined) {
|
if (bool === undefined) {
|
||||||
return this.mesh.visible;
|
return this.mesh.visible;
|
||||||
|
|
@ -172,17 +195,27 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
this.mesh.visible = bool;
|
this.mesh.visible = bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
material(mat) {
|
// get, set, or toggle selection of model (coloring)
|
||||||
let op = this.opacity();
|
select(bool) {
|
||||||
this.mesh.material = mat = mat.clone();
|
if (bool === undefined) {
|
||||||
mat.opacity = op;
|
return this.mesh.material[0] === this.mats.normal;
|
||||||
if (op === 1) this.wireframe(false);
|
}
|
||||||
|
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 = {}) {
|
opacity(ov, opt = {}) {
|
||||||
let mat = this.mesh.material;
|
let mat = this.mesh.material;
|
||||||
if (ov === undefined) {
|
if (ov === undefined) {
|
||||||
return mat.opacity;
|
return mat[0].opacity;
|
||||||
}
|
}
|
||||||
if (ov.restore) {
|
if (ov.restore) {
|
||||||
ov = this._op;
|
ov = this._op;
|
||||||
|
|
@ -191,14 +224,16 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
} else {
|
} else {
|
||||||
this._op = ov;
|
this._op = ov;
|
||||||
}
|
}
|
||||||
if (ov <= 0.0) {
|
for (let m of Object.values(this.mats)) {
|
||||||
mat.transparent = false;
|
if (ov <= 0.0) {
|
||||||
mat.opacity = 1;
|
m.transparent = false;
|
||||||
mat.visible = false;
|
m.opacity = 1;
|
||||||
} else {
|
m.visible = false;
|
||||||
mat.transparent = true;
|
} else {
|
||||||
mat.opacity = ov;
|
m.transparent = true;
|
||||||
mat.visible = true;
|
m.opacity = ov;
|
||||||
|
m.visible = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
space.update();
|
space.update();
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +263,7 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
if (bool) {
|
if (bool) {
|
||||||
this._wire = new Mesh(this.mesh.geometry.shallowClone(), materials.wireframe);
|
this._wire = new Mesh(this.mesh.geometry.shallowClone(), materials.wireframe);
|
||||||
this.mesh.add(this._wire);
|
this.mesh.add(this._wire);
|
||||||
this.opacity({temp: opt.opacity || 0});
|
this.opacity({temp: opt.opacity || 0.15});
|
||||||
}
|
}
|
||||||
space.update();
|
space.update();
|
||||||
return was;
|
return was;
|
||||||
|
|
@ -254,7 +289,7 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
}
|
}
|
||||||
|
|
||||||
// find adjacent faces to clicked point/line on a face
|
// 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 geometry = new THREE.SphereGeometry( 0.5, 16, 16 );
|
||||||
let material = new MeshPhongMaterial( { color: 0x777777, transparent: true, opacity: 0.25 } );
|
let material = new MeshPhongMaterial( { color: 0x777777, transparent: true, opacity: 0.25 } );
|
||||||
let sphere = new Mesh( geometry, material );
|
let sphere = new Mesh( geometry, material );
|
||||||
|
|
|
||||||
|
|
@ -112,10 +112,13 @@ let model = {
|
||||||
// distance tolerance for click to vertex (rough distance)
|
// distance tolerance for click to vertex (rough distance)
|
||||||
let eps = 1;
|
let eps = 1;
|
||||||
let faces = [];
|
let faces = [];
|
||||||
|
let verts = [];
|
||||||
|
let edges = [];
|
||||||
for (let i=0, l=arr.length; i<l; ) {
|
for (let i=0, l=arr.length; i<l; ) {
|
||||||
// matches here are within radius of a vertex
|
// matches here are within radius of a vertex
|
||||||
// select all faces that share a matched vertex
|
// select all faces that share a matched vertex
|
||||||
let face = (i/9) | 0; // face index
|
let vert = i/3; // vertex index
|
||||||
|
let face = (i/9) | 0; // face index
|
||||||
let ax = arr[i++];
|
let ax = arr[i++];
|
||||||
let ay = arr[i++];
|
let ay = arr[i++];
|
||||||
let az = arr[i++];
|
let az = arr[i++];
|
||||||
|
|
@ -124,6 +127,7 @@ let model = {
|
||||||
let dz = Math.abs(az - z);
|
let dz = Math.abs(az - z);
|
||||||
if (dx < eps && dy < eps && dz < eps) {
|
if (dx < eps && dy < eps && dz < eps) {
|
||||||
faces.addOnce(face);
|
faces.addOnce(face);
|
||||||
|
verts.push(vert);
|
||||||
console.log(`match @ ${i-3} = ${face}`, ax, ay, az);
|
console.log(`match @ ${i-3} = ${face}`, ax, ay, az);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +141,7 @@ let model = {
|
||||||
if (faces.length === 0) {
|
if (faces.length === 0) {
|
||||||
faces.push(Math.min(a,b,c) / 3);
|
faces.push(Math.min(a,b,c) / 3);
|
||||||
}
|
}
|
||||||
return faces;
|
return { faces, edges, verts };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue