add mirroring, exclude normals from bounds, fix paint selection update
This commit is contained in:
parent
e682881374
commit
5d2f4ac02e
7 changed files with 88 additions and 7 deletions
|
|
@ -276,6 +276,7 @@ function space_init(data) {
|
|||
case 'KeyF':
|
||||
return selection.floor().focus();
|
||||
case 'KeyM':
|
||||
if (shiftKey) return api.tool.mirror();
|
||||
return api.tool.merge();
|
||||
case 'KeyA':
|
||||
return api.tool.analyze();
|
||||
|
|
|
|||
|
|
@ -140,6 +140,8 @@ let selection = {
|
|||
}
|
||||
// update saved selection id list
|
||||
prefs.save( prefs.map.space.select = selected.map(s => s.id) );
|
||||
// force repaint in case of idle
|
||||
space.update();
|
||||
return selection;
|
||||
},
|
||||
|
||||
|
|
@ -293,6 +295,9 @@ function fallback(models, strict) {
|
|||
let tool = {
|
||||
merge(models) {
|
||||
models = fallback(models);
|
||||
if (models.length <= 1) {
|
||||
return api.log.emit('nothing to merge');
|
||||
}
|
||||
api.log.emit(`merging ${models.length} models`).pin();
|
||||
worker.model_merge(models.map(m => {
|
||||
return { id: m.id, matrix: m.matrix }
|
||||
|
|
@ -313,6 +318,12 @@ let tool = {
|
|||
}
|
||||
},
|
||||
|
||||
mirror() {
|
||||
for (let m of selection.models()) {
|
||||
m.mirror();
|
||||
}
|
||||
},
|
||||
|
||||
invert() {
|
||||
for (let m of selection.models()) {
|
||||
m.invert(api.mode.get());
|
||||
|
|
|
|||
|
|
@ -267,6 +267,7 @@ function ui_build() {
|
|||
// h.div({ class: "vsep" }),
|
||||
h.button({ _: 'duplicate', onclick: tool.duplicate }),
|
||||
h.button({ _: 'merge', onclick: tool.merge }),
|
||||
h.button({ _: 'mirror', onclick: tool.mirror }),
|
||||
h.button({ _: 'split', onclick: call.edit_split }),
|
||||
h.button({ _: 'invert', onclick: tool.invert }),
|
||||
]),
|
||||
|
|
|
|||
|
|
@ -108,6 +108,13 @@ mesh.group = class MeshGroup extends mesh.object {
|
|||
return this.centerXY().floor();
|
||||
}
|
||||
|
||||
scale(x = 1, y = 1, z = 1) {
|
||||
for (let m of this.models) {
|
||||
m.scale(x, y, z);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// center objects to group bounds
|
||||
// dependent on first being added to world/scene
|
||||
centerModels() {
|
||||
|
|
@ -141,6 +148,11 @@ mesh.group = class MeshGroup extends mesh.object {
|
|||
model.select(...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
// ok, this is confusing, but necessary for now :/
|
||||
setSelected() {
|
||||
super.select();
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -108,16 +108,45 @@ mesh.model = class MeshModel extends mesh.object {
|
|||
return this.mesh.matrixWorld.elements;
|
||||
}
|
||||
|
||||
// override and translate mesh
|
||||
move(x = 0, y = 0, z = 0) {
|
||||
let arr = this.attributes.position.array;
|
||||
for (let i=0, l=arr.length; i<l; ) {
|
||||
arr[i] = arr[i++] + x;
|
||||
arr[i] = arr[i++] + y;
|
||||
arr[i] = arr[i++] + z;
|
||||
}
|
||||
this.reload(arr);
|
||||
return this;
|
||||
}
|
||||
|
||||
// override and translate mesh
|
||||
scale(x = 1, y = 1, z = 1) {
|
||||
let arr = this.attributes.position.array;
|
||||
for (let i=0, l=arr.length; i<l; ) {
|
||||
arr[i] = arr[i++] *= x;
|
||||
arr[i] = arr[i++] *= y;
|
||||
arr[i] = arr[i++] *= z;
|
||||
}
|
||||
this.reload(arr);
|
||||
return this;
|
||||
}
|
||||
|
||||
mirror() {
|
||||
return this.duplicate({ mirror: true });
|
||||
}
|
||||
|
||||
// return new group containing just this model in world coordinates
|
||||
duplicate() {
|
||||
duplicate(opt = {}) {
|
||||
worker.model_duplicate({
|
||||
matrix: this.matrix,
|
||||
id: this.id
|
||||
id: this.id,
|
||||
opt
|
||||
}).then(data => {
|
||||
mesh.api.group.new([new mesh.model({
|
||||
file: `${this.file}-dup`,
|
||||
file: `${this.file}`,
|
||||
mesh: data
|
||||
})]).select();
|
||||
})]).setSelected();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -357,7 +386,7 @@ mesh.model = class MeshModel extends mesh.object {
|
|||
this.group.add(model = new mesh.model({
|
||||
file: `${this.file}`,
|
||||
mesh: o2
|
||||
}).applyMatrix4(m4)).select();
|
||||
}).applyMatrix4(m4)).setSelected();
|
||||
if (o1.length) {
|
||||
// this becomes bottom
|
||||
this.reload(o1);
|
||||
|
|
|
|||
|
|
@ -103,6 +103,10 @@ let util = mesh.util = {
|
|||
// https://discourse.threejs.org/t/bounding-box-bigger-than-concave-object-extrudegeometry/26073/2
|
||||
// https://discourse.threejs.org/t/invalid-bounds-generated-for-some-orientations/33205
|
||||
box3expand(box3, object) {
|
||||
if (object._no_bounds) {
|
||||
return;
|
||||
}
|
||||
|
||||
let geometry = object.geometry;
|
||||
object.updateWorldMatrix(geometry ? true : false, false);
|
||||
|
||||
|
|
@ -183,6 +187,7 @@ let util = mesh.util = {
|
|||
this.size = size;
|
||||
this.type = 'FaceNormalsHelper';
|
||||
this.matrixAutoUpdate = false;
|
||||
this._no_bounds = true;
|
||||
this.update();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,8 +72,30 @@ let model = {
|
|||
|
||||
// return new vertices in world coordinates
|
||||
duplicate(data) {
|
||||
let { id, matrix } = data;
|
||||
return translate_encode(id, matrix);
|
||||
let { id, matrix, opt } = data;
|
||||
let array = translate_encode(id, matrix);
|
||||
if (opt.mirror) {
|
||||
// find max z and invert z
|
||||
let maxz = -Infinity;
|
||||
for (let i=2, l=array.length; i<l; i += 3) {
|
||||
maxz = Math.max(maxz, array[i]);
|
||||
array[i] = -array[i];
|
||||
}
|
||||
maxz *= 2;
|
||||
for (let i=0, l=array.length; i<l; i += 9) {
|
||||
// swap first two vertices in face to invert normals
|
||||
let v1 = array.slice(i, i+3);
|
||||
for (let j=0; j<3; j++) {
|
||||
array[i+j] = array[i+j+3];
|
||||
array[i+j+3] = v1[j];
|
||||
}
|
||||
// move part up in Z
|
||||
array[i+2] += maxz;
|
||||
array[i+5] += maxz;
|
||||
array[i+8] += maxz;
|
||||
}
|
||||
}
|
||||
return array;
|
||||
},
|
||||
|
||||
// merge several model vertices into a single array
|
||||
|
|
|
|||
Loading…
Reference in a new issue