add precision/factor for CSG ops to eliminate sharps. add difference op to CSG
This commit is contained in:
parent
8aae9885e8
commit
41479e200b
5 changed files with 66 additions and 18 deletions
|
|
@ -8,6 +8,8 @@ gapp.register("geo.csg", [], (root, exports) => {
|
||||||
|
|
||||||
const { base, ext } = root;
|
const { base, ext } = root;
|
||||||
const debug = true;
|
const debug = true;
|
||||||
|
const precision = 0.001;
|
||||||
|
const factor = 1/precision;
|
||||||
|
|
||||||
function log() {
|
function log() {
|
||||||
if (root?.mesh?.log) {
|
if (root?.mesh?.log) {
|
||||||
|
|
@ -34,6 +36,11 @@ const CSG = {
|
||||||
return CSG.moduleOp('manifold.intersect', 'intersection', ...arguments);
|
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) {
|
moduleOp(name, op) {
|
||||||
log(`${name} ${arguments.length} meshes`);
|
log(`${name} ${arguments.length} meshes`);
|
||||||
const args = [...arguments].slice(2).map(a => new Instance.Manifold(a));
|
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++];
|
||||||
vertices[p++] = vertPos[vi++];
|
vertices[p++] = vertPos[vi++];
|
||||||
}
|
}
|
||||||
return vertices;
|
return vertices.map(v => v/factor);
|
||||||
}
|
}
|
||||||
throw "mesh missing required fields";
|
throw "mesh missing required fields";
|
||||||
},
|
},
|
||||||
|
|
@ -71,7 +78,7 @@ const CSG = {
|
||||||
fromPositionArray(pos) {
|
fromPositionArray(pos) {
|
||||||
const { index, vertices } = indexVertices(pos);
|
const { index, vertices } = indexVertices(pos);
|
||||||
let mesh = new Instance.Mesh({
|
let mesh = new Instance.Mesh({
|
||||||
vertProperties: vertices.toFloat32(),
|
vertProperties: vertices.map(v => (v*factor)|0).toFloat32(),
|
||||||
triVerts: index.toUint32()
|
triVerts: index.toUint32()
|
||||||
});
|
});
|
||||||
return mesh;
|
return mesh;
|
||||||
|
|
|
||||||
|
|
@ -235,10 +235,10 @@ const pattern = {
|
||||||
* @param {float[]} center [x,y,z]
|
* @param {float[]} center [x,y,z]
|
||||||
*/
|
*/
|
||||||
circle(count, center) {
|
circle(count, center) {
|
||||||
async function doit(count, center = [0,0,0]) {
|
async function doit(count, center = [0,0]) {
|
||||||
api.modal.hide();
|
api.modal.hide();
|
||||||
let models = selection.models();
|
let models = selection.models();
|
||||||
let [ cx, cy, cz ] = center;
|
let [ cx, cy ] = center;
|
||||||
for (let model of models) {
|
for (let model of models) {
|
||||||
await api.tool.regroup([ model ]);
|
await api.tool.regroup([ model ]);
|
||||||
let pos = model.group.position().clone();
|
let pos = model.group.position().clone();
|
||||||
|
|
@ -260,8 +260,8 @@ const pattern = {
|
||||||
body: [ h.div({ class: "addgear" }, [
|
body: [ h.div({ class: "addgear" }, [
|
||||||
h.label('total count'),
|
h.label('total count'),
|
||||||
h.input({ value: 3, size: 5, id: "_count" }),
|
h.input({ value: 3, size: 5, id: "_count" }),
|
||||||
h.label('center x,y,z'),
|
h.label('center x,y'),
|
||||||
h.input({ value: "0,0,0", size: 5, id: "_center" }),
|
h.input({ value: "0,0", size: 5, id: "_center" }),
|
||||||
h.button({ _: "create", onclick() {
|
h.button({ _: "create", onclick() {
|
||||||
doit(
|
doit(
|
||||||
parseInt(api.modal.bound._count.value) || 3,
|
parseInt(api.modal.bound._count.value) || 3,
|
||||||
|
|
@ -489,6 +489,7 @@ const tool = {
|
||||||
file: `merged`,
|
file: `merged`,
|
||||||
mesh: data
|
mesh: data
|
||||||
})]).promote();
|
})]).promote();
|
||||||
|
api.selection.visible(false);
|
||||||
api.selection.set([group]);
|
api.selection.set([group]);
|
||||||
api.log.emit('merge complete').unpin();
|
api.log.emit('merge complete').unpin();
|
||||||
});
|
});
|
||||||
|
|
@ -508,6 +509,7 @@ const tool = {
|
||||||
file: `union`,
|
file: `union`,
|
||||||
mesh: data
|
mesh: data
|
||||||
})]).promote();
|
})]).promote();
|
||||||
|
api.selection.visible(false);
|
||||||
api.selection.set([group]);
|
api.selection.set([group]);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.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) {
|
intersect(models) {
|
||||||
models = fallback(models);
|
models = fallback(models);
|
||||||
if (models.length < 2) {
|
if (models.length < 2) {
|
||||||
|
|
@ -532,6 +559,7 @@ const tool = {
|
||||||
file: `intersect`,
|
file: `intersect`,
|
||||||
mesh: data
|
mesh: data
|
||||||
})]).promote();
|
})]).promote();
|
||||||
|
api.selection.visible(false);
|
||||||
api.selection.set([group]);
|
api.selection.set([group]);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
|
@ -556,6 +584,7 @@ const tool = {
|
||||||
file: `subtract`,
|
file: `subtract`,
|
||||||
mesh: data
|
mesh: data
|
||||||
})]).promote();
|
})]).promote();
|
||||||
|
api.selection.visible(false);
|
||||||
api.selection.set([group]);
|
api.selection.set([group]);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
|
|
||||||
|
|
@ -396,6 +396,7 @@ function ui_build() {
|
||||||
h.button({ _: 'union', onclick: tool.union }),
|
h.button({ _: 'union', onclick: tool.union }),
|
||||||
h.button({ _: 'subtract', onclick: tool.subtract }),
|
h.button({ _: 'subtract', onclick: tool.subtract }),
|
||||||
h.button({ _: 'intersect', onclick: tool.intersect }),
|
h.button({ _: 'intersect', onclick: tool.intersect }),
|
||||||
|
h.button({ _: 'difference', onclick: tool.difference }),
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
h.div([
|
h.div([
|
||||||
|
|
|
||||||
|
|
@ -153,13 +153,17 @@ mesh.model = class MeshModel extends mesh.object {
|
||||||
return this.duplicate({ mirror: true });
|
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 = {}) {
|
duplicate(opt = {}) {
|
||||||
return worker.model_duplicate({
|
return worker.model_duplicate({
|
||||||
matrix: this.matrix,
|
matrix: this.matrix,
|
||||||
id: this.id,
|
id: this.id,
|
||||||
opt: { mirror: opt.mirror}
|
opt: { mirror: opt.mirror}
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
|
if (opt.reload) return this.reload(data);
|
||||||
let model = new mesh.model({ file: `${this.file}`, mesh: data });
|
let model = new mesh.model({ file: `${this.file}`, mesh: data });
|
||||||
let group = opt.group || mesh.api.group.new();
|
let group = opt.group || mesh.api.group.new();
|
||||||
group.add(model);
|
group.add(model);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ const { Matrix4, Vector3, BufferGeometry, BufferAttribute, computeFaceNormal } =
|
||||||
const { base, mesh, moto } = root;
|
const { base, mesh, moto } = root;
|
||||||
const { client, worker } = moto;
|
const { client, worker } = moto;
|
||||||
const { util } = mesh;
|
const { util } = mesh;
|
||||||
const { newPolygon } = base;
|
const { CSG, newPolygon } = base;
|
||||||
const cache = {};
|
const cache = {};
|
||||||
|
|
||||||
// add scoped access to cache
|
// add scoped access to cache
|
||||||
|
|
@ -146,29 +146,36 @@ let model = {
|
||||||
|
|
||||||
union(recs) {
|
union(recs) {
|
||||||
let arrays = recs.map(rec => translate_encode(rec.id, rec.matrix));
|
let arrays = recs.map(rec => translate_encode(rec.id, rec.matrix));
|
||||||
let solids = arrays.map(a => base.CSG.fromPositionArray(a));
|
let solids = arrays.map(a => CSG.fromPositionArray(a));
|
||||||
let union = base.CSG.union(...solids);
|
let union = CSG.union(...solids);
|
||||||
return base.CSG.toPositionArray(union);
|
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) {
|
intersect(recs) {
|
||||||
let arrays = recs.map(rec => translate_encode(rec.id, rec.matrix));
|
let arrays = recs.map(rec => translate_encode(rec.id, rec.matrix));
|
||||||
let solids = arrays.map(a => base.CSG.fromPositionArray(a));
|
let solids = arrays.map(a => CSG.fromPositionArray(a));
|
||||||
let union = base.CSG.intersect(...solids);
|
let union = CSG.intersect(...solids);
|
||||||
return base.CSG.toPositionArray(union);
|
return CSG.toPositionArray(union);
|
||||||
},
|
},
|
||||||
|
|
||||||
subtract(recs) {
|
subtract(recs) {
|
||||||
let bases = recs.filter(rec => !rec.tool)
|
let bases = recs.filter(rec => !rec.tool)
|
||||||
.map(rec => translate_encode(rec.id, rec.matrix))
|
.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)
|
let tools = recs.filter(rec => rec.tool)
|
||||||
.map(rec => translate_encode(rec.id, rec.matrix))
|
.map(rec => translate_encode(rec.id, rec.matrix))
|
||||||
.map(a => base.CSG.fromPositionArray(a));
|
.map(a => CSG.fromPositionArray(a));
|
||||||
let subs = [];
|
let subs = [];
|
||||||
for (let obj of bases) {
|
for (let obj of bases) {
|
||||||
let sub = base.CSG.subtract(obj, ...tools);
|
let sub = CSG.subtract(obj, ...tools);
|
||||||
subs.appendAll(base.CSG.toPositionArray(sub));
|
subs.appendAll(CSG.toPositionArray(sub));
|
||||||
}
|
}
|
||||||
return subs.toFloat32();
|
return subs.toFloat32();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue