add mesh patterning

This commit is contained in:
Stewart Allen 2024-07-20 02:24:40 -04:00
commit 8aae9885e8
4 changed files with 79 additions and 15 deletions

View file

@ -12,7 +12,7 @@
gapp.register("mesh.api", [], (root, exports) => { gapp.register("mesh.api", [], (root, exports) => {
const { Matrix4, Vector3 } = THREE; const { Matrix4, Vector3 } = THREE;
const { mesh, moto } = root; const { base, mesh, moto } = root;
const { space } = moto; const { space } = moto;
const { util } = mesh; const { util } = mesh;
@ -229,6 +229,58 @@ const selection = {
} }
}; };
const pattern = {
/**
* @param {int} count number of entities
* @param {float[]} center [x,y,z]
*/
circle(count, center) {
async function doit(count, center = [0,0,0]) {
api.modal.hide();
let models = selection.models();
let [ cx, cy, cz ] = center;
for (let model of models) {
await api.tool.regroup([ model ]);
let pos = model.group.position().clone();
let stepr = (Math.PI * 2) / count;
let modnu = [ model ];
for (let i=1; i<count; i++) {
let clone = await model.duplicate();
let [x, y] = base.util.rotate(pos.x - cx, pos.y - cy, stepr * i);
clone.group.rotate(0, 0, stepr * i);
clone.group.position(x + cx, y + cy, pos.z);
modnu.push(clone);
}
await api.tool.regroup(modnu);
}
}
if (!count) {
api.modal.dialog({
title: "object pattern",
body: [ h.div({ class: "addgear" }, [
h.label('total count'),
h.input({ value: 3, size: 5, id: "_count" }),
h.label('center x,y,z'),
h.input({ value: "0,0,0", size: 5, id: "_center" }),
h.button({ _: "create", onclick() {
doit(
parseInt(api.modal.bound._count.value) || 3,
api.modal.bound._center.value.split(',').map(v => parseFloat(v))
);
} })
]) ]
});
api.modal.bound._count.focus();
} else {
doit(count, origin);
}
},
grid() {
}
};
const group = { const group = {
// @returns {MeshGroup[]} // @returns {MeshGroup[]}
list() { list() {
@ -565,8 +617,8 @@ const tool = {
api.log.emit(`regrouping ${models.length} model(s)`); api.log.emit(`regrouping ${models.length} model(s)`);
let bounds = util.bounds(models); let bounds = util.bounds(models);
let { mid } = bounds; let { mid } = bounds;
Promise.all(models.map(m => m.ungroup())).then(() => { return Promise.all(models.map(m => m.ungroup())).then(() => {
mesh.api.group.new(models) return mesh.api.group.new(models)
.centerModels() .centerModels()
.position(mid.x, mid.y, mid.z) .position(mid.x, mid.y, mid.z)
.setSelected(); .setSelected();
@ -592,7 +644,7 @@ const tool = {
}); });
promises.push(p); promises.push(p);
} }
Promise.all(promises).then(() => { return Promise.all(promises).then(() => {
api.log.emit('analysis complete').unpin(); api.log.emit('analysis complete').unpin();
}); });
}, },
@ -613,7 +665,7 @@ const tool = {
}); });
promises.push(p); promises.push(p);
} }
Promise.all(promises).then(() => { return Promise.all(promises).then(() => {
api.log.emit('isolation complete').unpin(); api.log.emit('isolation complete').unpin();
// api.log.emit(`... isolate time = ${Date.now() - mark}`); // api.log.emit(`... isolate time = ${Date.now() - mark}`);
}); });
@ -902,6 +954,8 @@ const api = exports({
selection, selection,
pattern,
group, group,
model, model,

View file

@ -398,6 +398,14 @@ function ui_build() {
h.button({ _: 'intersect', onclick: tool.intersect }), h.button({ _: 'intersect', onclick: tool.intersect }),
]) ])
]), ]),
h.div([
h.div({ _: "pattern", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'circular', onclick() { api.pattern.circle() } }),
h.button({ _: 'grid', onclick() { api.pattern.grid } }),
])
]),
h.div([ h.div([
h.div({ _: "models", class: "head" }), h.div({ _: "models", class: "head" }),
h.div({ class: "back"}), h.div({ class: "back"}),

View file

@ -155,18 +155,19 @@ mesh.model = class MeshModel extends mesh.object {
// return new group containing just this model in world coordinates // return new group containing just this model in world coordinates
duplicate(opt = {}) { duplicate(opt = {}) {
worker.model_duplicate({ return worker.model_duplicate({
matrix: this.matrix, matrix: this.matrix,
id: this.id, id: this.id,
opt opt: { mirror: opt.mirror}
}).then(data => { }).then(data => {
let group = mesh.api.group.new([new mesh.model({ let model = new mesh.model({ file: `${this.file}`, mesh: data });
file: `${this.file}`, let group = opt.group || mesh.api.group.new();
mesh: data group.add(model);
})]).setSelected(); group.setSelected();
if (opt.mirror) { if (opt.mirror) {
group.move(0, 0, group.bounds.dim.z); group.move(0, 0, group.bounds.dim.z);
} }
return model;
}); });
} }
@ -460,9 +461,10 @@ mesh.model = class MeshModel extends mesh.object {
// release from a group but remain in memory and storage // release from a group but remain in memory and storage
// so it can be re-assigned to another group // so it can be re-assigned to another group
ungroup() { ungroup() {
if (this.group) {
this.group.remove(this, { free: false }); this.group.remove(this, { free: false });
this.group = undefined; this.group = undefined;
}
return worker.model_duplicate({ return worker.model_duplicate({
matrix: this.matrix, matrix: this.matrix,
id: this.id, id: this.id,

View file

@ -230,7 +230,7 @@ button:hover {
} }
.addgear { .addgear {
gap: 5px; gap: 5px 10px;
display: grid; display: grid;
grid-template-columns: 1fr 50px; grid-template-columns: 1fr 50px;
} }