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 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;
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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([
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue