add mesh edge selection to allow creating faces

This commit is contained in:
Stewart Allen 2024-07-21 22:36:37 -04:00
commit 4261d2eea9
8 changed files with 92 additions and 38 deletions

View file

@ -7,6 +7,7 @@
// dep: moto.broker
// dep: moto.space
// dep: mesh.util
// dep: mesh.edges
// dep: add.three
// use: add.array
gapp.register("mesh.api", [], (root, exports) => {
@ -24,6 +25,10 @@ let selected = [];
let tools = [];
const selection = {
count() {
return selected.length;
},
// @returns {MeshObject[]} or all groups if not strict and no selection
list(strict = false) {
return selected.length || strict ? selected.slice() : groups.slice();
@ -562,11 +567,11 @@ const tool = {
return { id: m.id, matrix: m.matrix }
}))
.then(data => {
api.selection.visible(false);
let group = api.group.new([new mesh.model({
file: `merged`,
mesh: data
})]).promote();
api.selection.visible(false);
api.selection.set([group]);
api.log.emit('merge complete').unpin();
});
@ -582,11 +587,11 @@ const tool = {
return { id: m.id, matrix: m.matrix }
}))
.then(data => {
api.selection.visible(false);
let group = api.group.new([new mesh.model({
file: `union`,
mesh: data
})]).promote();
api.selection.visible(false);
api.selection.set([group]);
})
.catch(error => {
@ -607,11 +612,11 @@ const tool = {
return { id: m.id, matrix: m.matrix }
}))
.then(data => {
api.selection.visible(false);
let group = api.group.new([new mesh.model({
file: `diff`,
mesh: data
})]).promote();
api.selection.visible(false);
api.selection.set([group]);
})
.catch(error => {
@ -857,6 +862,7 @@ const mode = {
}
$(`mode-${mode}`).classList.add('selected');
api.mode.check();
mesh.edges.end();
},
get() {
@ -909,6 +915,7 @@ const mode = {
selection.clear();
}
mode.set(modes.edge);
mesh.edges.start();
}
};
@ -1091,6 +1098,8 @@ const api = exports({
});
const { broker } = gapp;
const call = broker.send;
// publish messages when api functions are called
broker.wrapObject(selection, 'selection');
broker.wrapObject(model, 'model');

View file

@ -157,20 +157,21 @@ mesh.model = class MeshModel extends mesh.object {
// translated into world coordinates (rebuilt from rotation matrix)
// defaults to returning a new model in a new group
// options to mirror, re-use a group, or update model in-place
duplicate(opt = {}) {
duplicate(opt = { select: true }) {
return worker.model_duplicate({
matrix: this.matrix,
id: this.id,
opt: { mirror: opt.mirror}
}).then(data => {
if (opt.append) data = [...data, ...opt.append].toFloat32();
if (opt.reload) return this.reload(data);
let model = new mesh.model({ file: `${this.file}`, mesh: data });
let group = opt.group || mesh.api.group.new();
group.add(model);
group.setSelected();
if (opt.mirror) {
group.move(0, 0, group.bounds.dim.z);
}
model.wireframe(this.wireframe());
model.normals(this.normals());
if (opt.select) group.setSelected();
if (opt.mirror) group.move(0, 0, group.bounds.dim.z);
return model;
});
}
@ -209,7 +210,6 @@ mesh.model = class MeshModel extends mesh.object {
geo.setIndex(new BufferAttribute(indices, 1));
geo = geo.toNonIndexed();
}
let meh = this.mesh = new Mesh(geo, [
this.mats.normal,
this.mats.face

View file

@ -1,25 +1,32 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
/**
* toggle edit/split on Z tool
* presents a Z plane on hover
* click to split the selected mesh(es)
*/
"use strict";
// dep: moto.space
gapp.register("mesh.split", [], (root, exports) => {
const { Mesh, MeshPhongMaterial, PlaneGeometry, DoubleSide, Vector3 } = THREE;
const { broker } = gapp;
const { moto } = root;
const { space } = moto;
// toggle edit/split temporary mode (present plane on hover)
let temp_mode;
let isActive;
// split functions
let split = {
active() {
return temp_mode ? true : false;
return isActive ? true : false;
},
start() {
if (isActive) {
return;
}
let { api, util } = mesh;
// highlight button
let button = event.target;
@ -54,7 +61,7 @@ let split = {
let { dim, mid } = util.bounds(meshes);
let { point, face, object } = int;
let { x, y, z } = point;
// set appearance of split plane
mat.color.set(0x5555aa);
obj.visible = true;
if (event.shiftKey) {
@ -62,10 +69,11 @@ let split = {
}
// y is z in model space for the purposes of a split
state.plane = { z: y };
// ensure split plane exceeds bounds of split target
obj.scale.set(dim.x + 2, dim.y + 2, 1);
obj.position.set(mid.x, y, -mid.y);
});
temp_mode = split;
isActive = true;
},
select() {
@ -73,19 +81,22 @@ let split = {
let { models, plane } = split.state;
log.emit(`splitting ${models.length} model(s) at ${plane.z.round(3)}`).pin();
Promise.all(models.map(m => m.split(plane))).then(models => {
mesh.api.selection.set(models);
mesh.api.selection.set(models.filter(m => m));
log.emit('split complete').unpin();
split.end();
});
},
end() {
if (!isActive) {
return;
}
let space = moto.space;
let { button, obj } = split.state;
button.classList.remove('selected');
space.scene.remove(obj);
space.mouse.onHover(undefined);
temp_mode = split.state = undefined;
isActive = split.state = undefined;
mesh.api.selection.update();
},
@ -102,19 +113,6 @@ let split = {
}
}
function edit_split(event) {
if (temp_mode) {
temp_mode.end();
} else {
split.start();
}
}
exports(split);
// bind functions to topics
broker.listeners({
edit_split,
});
});