add precision/factor for CSG ops to eliminate sharps. add difference op to CSG

This commit is contained in:
Stewart Allen 2024-07-20 09:59:22 -04:00
commit 41479e200b
5 changed files with 66 additions and 18 deletions

View file

@ -8,6 +8,8 @@ gapp.register("geo.csg", [], (root, exports) => {
const { base, ext } = root;
const debug = true;
const precision = 0.001;
const factor = 1/precision;
function log() {
if (root?.mesh?.log) {
@ -34,6 +36,11 @@ const CSG = {
return CSG.moduleOp('manifold.intersect', 'intersection', ...arguments);
},
// accepts 2 or more arguments with threejs vertex position arrays
difference() {
return CSG.moduleOp('manifold.difference', 'difference', ...arguments);
},
moduleOp(name, op) {
log(`${name} ${arguments.length} meshes`);
const args = [...arguments].slice(2).map(a => new Instance.Manifold(a));
@ -63,7 +70,7 @@ const CSG = {
vertices[p++] = vertPos[vi++];
vertices[p++] = vertPos[vi++];
}
return vertices;
return vertices.map(v => v/factor);
}
throw "mesh missing required fields";
},
@ -71,7 +78,7 @@ const CSG = {
fromPositionArray(pos) {
const { index, vertices } = indexVertices(pos);
let mesh = new Instance.Mesh({
vertProperties: vertices.toFloat32(),
vertProperties: vertices.map(v => (v*factor)|0).toFloat32(),
triVerts: index.toUint32()
});
return mesh;

View file

@ -235,10 +235,10 @@ const pattern = {
* @param {float[]} center [x,y,z]
*/
circle(count, center) {
async function doit(count, center = [0,0,0]) {
async function doit(count, center = [0,0]) {
api.modal.hide();
let models = selection.models();
let [ cx, cy, cz ] = center;
let [ cx, cy ] = center;
for (let model of models) {
await api.tool.regroup([ model ]);
let pos = model.group.position().clone();
@ -260,8 +260,8 @@ const 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.label('center x,y'),
h.input({ value: "0,0", size: 5, id: "_center" }),
h.button({ _: "create", onclick() {
doit(
parseInt(api.modal.bound._count.value) || 3,
@ -489,6 +489,7 @@ const tool = {
file: `merged`,
mesh: data
})]).promote();
api.selection.visible(false);
api.selection.set([group]);
api.log.emit('merge complete').unpin();
});
@ -508,6 +509,7 @@ const tool = {
file: `union`,
mesh: data
})]).promote();
api.selection.visible(false);
api.selection.set([group]);
})
.catch(error => {
@ -518,6 +520,31 @@ const tool = {
});
},
difference(models) {
models = fallback(models);
if (models.length < 2) {
return api.log.emit('nothing to diff');
}
api.log.emit(`diff ${models.length} models`).pin();
worker.model_difference(models.map(m => {
return { id: m.id, matrix: m.matrix }
}))
.then(data => {
let group = api.group.new([new mesh.model({
file: `diff`,
mesh: data
})]).promote();
api.selection.visible(false);
api.selection.set([group]);
})
.catch(error => {
api.log.emit(`diff error: ${error}`);
})
.finally(() => {
api.log.emit('diff complete').unpin();
});
},
intersect(models) {
models = fallback(models);
if (models.length < 2) {
@ -532,6 +559,7 @@ const tool = {
file: `intersect`,
mesh: data
})]).promote();
api.selection.visible(false);
api.selection.set([group]);
})
.catch(error => {
@ -556,6 +584,7 @@ const tool = {
file: `subtract`,
mesh: data
})]).promote();
api.selection.visible(false);
api.selection.set([group]);
})
.catch(error => {

View file

@ -396,6 +396,7 @@ function ui_build() {
h.button({ _: 'union', onclick: tool.union }),
h.button({ _: 'subtract', onclick: tool.subtract }),
h.button({ _: 'intersect', onclick: tool.intersect }),
h.button({ _: 'difference', onclick: tool.difference }),
])
]),
h.div([

View file

@ -153,13 +153,17 @@ mesh.model = class MeshModel extends mesh.object {
return this.duplicate({ mirror: true });
}
// return new group containing just this model in world coordinates
// return a model containing just this model
// 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 = {}) {
return worker.model_duplicate({
matrix: this.matrix,
id: this.id,
opt: { mirror: opt.mirror}
}).then(data => {
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);

View file

@ -24,7 +24,7 @@ const { Matrix4, Vector3, BufferGeometry, BufferAttribute, computeFaceNormal } =
const { base, mesh, moto } = root;
const { client, worker } = moto;
const { util } = mesh;
const { newPolygon } = base;
const { CSG, newPolygon } = base;
const cache = {};
// add scoped access to cache
@ -146,29 +146,36 @@ let model = {
union(recs) {
let arrays = recs.map(rec => translate_encode(rec.id, rec.matrix));
let solids = arrays.map(a => base.CSG.fromPositionArray(a));
let union = base.CSG.union(...solids);
return base.CSG.toPositionArray(union);
let solids = arrays.map(a => CSG.fromPositionArray(a));
let union = CSG.union(...solids);
return CSG.toPositionArray(union);
},
difference(recs) {
let arrays = recs.map(rec => translate_encode(rec.id, rec.matrix));
let solids = arrays.map(a => CSG.fromPositionArray(a));
let diff = CSG.difference(...solids);
return CSG.toPositionArray(diff);
},
intersect(recs) {
let arrays = recs.map(rec => translate_encode(rec.id, rec.matrix));
let solids = arrays.map(a => base.CSG.fromPositionArray(a));
let union = base.CSG.intersect(...solids);
return base.CSG.toPositionArray(union);
let solids = arrays.map(a => CSG.fromPositionArray(a));
let union = CSG.intersect(...solids);
return CSG.toPositionArray(union);
},
subtract(recs) {
let bases = recs.filter(rec => !rec.tool)
.map(rec => translate_encode(rec.id, rec.matrix))
.map(a => base.CSG.fromPositionArray(a));
.map(a => CSG.fromPositionArray(a));
let tools = recs.filter(rec => rec.tool)
.map(rec => translate_encode(rec.id, rec.matrix))
.map(a => base.CSG.fromPositionArray(a));
.map(a => CSG.fromPositionArray(a));
let subs = [];
for (let obj of bases) {
let sub = base.CSG.subtract(obj, ...tools);
subs.appendAll(base.CSG.toPositionArray(sub));
let sub = CSG.subtract(obj, ...tools);
subs.appendAll(CSG.toPositionArray(sub));
}
return subs.toFloat32();
},