merge manifold into csg
This commit is contained in:
parent
95edfc1ea9
commit
7daeaaba91
7 changed files with 141 additions and 126 deletions
2
notes.md
2
notes.md
|
|
@ -119,10 +119,10 @@
|
||||||
# Mesh:Tool
|
# Mesh:Tool
|
||||||
|
|
||||||
* add undo/redo
|
* add undo/redo
|
||||||
|
* click to repair normals based on a known good (selected) face
|
||||||
* send to Kiri:Moto workspace (or update model vertices in place)
|
* send to Kiri:Moto workspace (or update model vertices in place)
|
||||||
* better z snap using just vertexes from face intersected
|
* better z snap using just vertexes from face intersected
|
||||||
* add section view. local clip. raycast skip points above plane
|
* add section view. local clip. raycast skip points above plane
|
||||||
* add isolate op = separate bodies
|
|
||||||
* add decimate op = face reduction
|
* add decimate op = face reduction
|
||||||
* add flatten/crush op: for z bottoms (or surfaces?)
|
* add flatten/crush op: for z bottoms (or surfaces?)
|
||||||
* allow setting model/group origin for scale/rotate
|
* allow setting model/group origin for scale/rotate
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
Full docs @ https://docs.grid.space/projects/kiri-moto
|
Full docs @ https://docs.grid.space/projects/kiri-moto
|
||||||
|
|
||||||
|
# Release 3.7
|
||||||
|
|
||||||
|
* replace jscad/modeling with Manifold project for faster mesh boolean
|
||||||
|
|
||||||
|
|
||||||
# Release 3.6 (2022-10-22)
|
# Release 3.6 (2022-10-22)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
/** Copyright Stewart Allen -- All Rights Reserved */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const testing = false;
|
|
||||||
|
|
||||||
// dep: ext.manifold
|
|
||||||
gapp.register("add.manifold", [], (root, exports) => {
|
|
||||||
Module.onRuntimeInitialized = () => {
|
|
||||||
Module.setup();
|
|
||||||
|
|
||||||
if (testing) {
|
|
||||||
// let c = Module.cube([1,1,1], true);
|
|
||||||
let c = Module.sphere(10, 100);
|
|
||||||
let l = 1000;
|
|
||||||
let cm = c.getMesh();
|
|
||||||
let md = c.getMeshDirect();
|
|
||||||
console.log({ c, cm, md });
|
|
||||||
|
|
||||||
console.time('getMesh');
|
|
||||||
for (let i=0; i<l; i++) c.getMesh();
|
|
||||||
console.timeEnd('getMesh');
|
|
||||||
|
|
||||||
console.time(`getMeshDirect`);
|
|
||||||
for (let i=0; i<l; i++) c.getMeshDirect({ mode: 0 });
|
|
||||||
console.timeEnd(`getMeshDirect`);
|
|
||||||
|
|
||||||
console.time(`getMeshDirect`);
|
|
||||||
for (let i=0; i<l; i++) c.getMeshDirect({ mode: 1 });
|
|
||||||
console.timeEnd(`getMeshDirect`);
|
|
||||||
|
|
||||||
console.time(`getMeshDirect`);
|
|
||||||
for (let i=0; i<l; i++) c.getMeshDirect({ mode: 2 });
|
|
||||||
console.timeEnd(`getMeshDirect`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function indexVertices(pos) {
|
|
||||||
mesh.log(`indexing ${pos.length/3} vertices`);
|
|
||||||
let ipos = 0;
|
|
||||||
const index = [];
|
|
||||||
const vertices = [];
|
|
||||||
const cache = {};
|
|
||||||
const temp = { x: 0, y: 0, z: 0 };
|
|
||||||
for (let i=0, length = pos.length; i<length; ) {
|
|
||||||
temp.x = pos[i++];
|
|
||||||
temp.y = pos[i++];
|
|
||||||
temp.z = pos[i++];
|
|
||||||
let key = [
|
|
||||||
((temp.x * 100000) | 0),
|
|
||||||
((temp.y * 100000) | 0),
|
|
||||||
((temp.z * 100000) | 0)
|
|
||||||
].join('');
|
|
||||||
let ip = cache[key];
|
|
||||||
if (ip >= 0) {
|
|
||||||
index.push(ip);
|
|
||||||
} else {
|
|
||||||
index.push(ipos);
|
|
||||||
cache[key] = (ipos++);
|
|
||||||
vertices.push(temp.x, temp.y, temp.z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { index, vertices };
|
|
||||||
}
|
|
||||||
|
|
||||||
function pos2mesh(pos) {
|
|
||||||
const { index, vertices } = indexVertices(pos);
|
|
||||||
const mesh = {
|
|
||||||
vertPos: new Module.Vector_vec3(),
|
|
||||||
triVerts: new Module.Vector_ivec3(),
|
|
||||||
vertNormal: new Module.Vector_vec3(),
|
|
||||||
halfedgeTangent: new Module.Vector_vec4()
|
|
||||||
};
|
|
||||||
for (let i = 0, l = vertices.length; i < l; ) {
|
|
||||||
mesh.vertPos.push_back({
|
|
||||||
x: vertices[i++],
|
|
||||||
y: vertices[i++],
|
|
||||||
z: vertices[i++]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 0, l = index.length; i < l; ) {
|
|
||||||
mesh.triVerts.push_back([ index[i++], index[i++], index[i++] ]);
|
|
||||||
}
|
|
||||||
return mesh;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mesh2pos(mesh) {
|
|
||||||
const { vertPos, triVerts } = mesh;
|
|
||||||
const vertices = new Float32Array(triVerts.length * 9);
|
|
||||||
for (let i = 0, t = 0, l = triVerts.length; t < l; t++) {
|
|
||||||
let tri = triVerts[t];
|
|
||||||
let vert = vertPos[tri[0]]; // X
|
|
||||||
vertices[i++] = vert[0];
|
|
||||||
vertices[i++] = vert[1];
|
|
||||||
vertices[i++] = vert[2];
|
|
||||||
vert = vertPos[tri[1]]; // Y
|
|
||||||
vertices[i++] = vert[0];
|
|
||||||
vertices[i++] = vert[1];
|
|
||||||
vertices[i++] = vert[2];
|
|
||||||
vert = vertPos[tri[2]]; // Z
|
|
||||||
vertices[i++] = vert[0];
|
|
||||||
vertices[i++] = vert[1];
|
|
||||||
vertices[i++] = vert[2];
|
|
||||||
}
|
|
||||||
return vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
exports({
|
|
||||||
indexVertices,
|
|
||||||
pos2mesh,
|
|
||||||
mesh2pos
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
134
src/geo/csg.js
134
src/geo/csg.js
|
|
@ -3,7 +3,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// dep: geo.base
|
// dep: geo.base
|
||||||
// dep: add.manifold
|
// dep: ext.manifold
|
||||||
gapp.register("geo.csg", [], (root, exports) => {
|
gapp.register("geo.csg", [], (root, exports) => {
|
||||||
|
|
||||||
const { base } = root;
|
const { base } = root;
|
||||||
|
|
@ -16,10 +16,14 @@ const CSG = {
|
||||||
mesh.log(`manifold.union ${arguments.length} meshes`);
|
mesh.log(`manifold.union ${arguments.length} meshes`);
|
||||||
const args = [...arguments].map(a => new Module.Manifold(a));
|
const args = [...arguments].map(a => new Module.Manifold(a));
|
||||||
const result = Module.union(...args);
|
const result = Module.union(...args);
|
||||||
const output = result.getMesh();
|
const output = result.getMesh({ mode: 2 });
|
||||||
|
const errors = [ ...args, result ].map(m => m.status().value);
|
||||||
for (let m of [ ...args, result ]) {
|
for (let m of [ ...args, result ]) {
|
||||||
m.delete();
|
m.delete();
|
||||||
}
|
}
|
||||||
|
if (errors.reduce((a,b) => a+b)) {
|
||||||
|
throw `${errors}`;
|
||||||
|
}
|
||||||
return output;
|
return output;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -29,7 +33,7 @@ const CSG = {
|
||||||
mesh.log(`manifold.subtract ${arguments.length} meshes`);
|
mesh.log(`manifold.subtract ${arguments.length} meshes`);
|
||||||
const args = [...arguments].map(a => new Module.Manifold(a));
|
const args = [...arguments].map(a => new Module.Manifold(a));
|
||||||
const result = Module.difference(...args);
|
const result = Module.difference(...args);
|
||||||
const output = result.getMesh();
|
const output = result.getMesh({ mode: 2 });
|
||||||
for (let m of [ ...args, result ]) {
|
for (let m of [ ...args, result ]) {
|
||||||
m.delete();
|
m.delete();
|
||||||
}
|
}
|
||||||
|
|
@ -41,23 +45,137 @@ const CSG = {
|
||||||
mesh.log(`manifold.intersect ${arguments.length} meshes`);
|
mesh.log(`manifold.intersect ${arguments.length} meshes`);
|
||||||
const args = [...arguments].map(a => new Module.Manifold(a));
|
const args = [...arguments].map(a => new Module.Manifold(a));
|
||||||
const result = Module.intersection(...args);
|
const result = Module.intersection(...args);
|
||||||
const output = result.getMesh();
|
const output = result.getMesh({ mode: 2 });
|
||||||
for (let m of [ ...args, result ]) {
|
for (let m of [ ...args, result ]) {
|
||||||
m.delete();
|
m.delete();
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
},
|
},
|
||||||
|
|
||||||
toPositionArray(geom) {
|
toPositionArray(mesh) {
|
||||||
return add.manifold.mesh2pos(geom);
|
const { vertPos, triVerts, vertex, index } = mesh;
|
||||||
|
if (vertex) {
|
||||||
|
return vertex;
|
||||||
|
}
|
||||||
|
const vertices = new Float32Array(triVerts.length * 9);
|
||||||
|
for (let i = 0, t = 0, l = triVerts.length; t < l; t++) {
|
||||||
|
let tri = triVerts[t];
|
||||||
|
let vert = vertPos[tri[0]]; // X
|
||||||
|
vertices[i++] = vert[0];
|
||||||
|
vertices[i++] = vert[1];
|
||||||
|
vertices[i++] = vert[2];
|
||||||
|
vert = vertPos[tri[1]]; // Y
|
||||||
|
vertices[i++] = vert[0];
|
||||||
|
vertices[i++] = vert[1];
|
||||||
|
vertices[i++] = vert[2];
|
||||||
|
vert = vertPos[tri[2]]; // Z
|
||||||
|
vertices[i++] = vert[0];
|
||||||
|
vertices[i++] = vert[1];
|
||||||
|
vertices[i++] = vert[2];
|
||||||
|
}
|
||||||
|
return vertices;
|
||||||
},
|
},
|
||||||
|
|
||||||
fromPositionArray(array) {
|
fromPositionArray(pos) {
|
||||||
return add.manifold.pos2mesh(array);
|
const { index, vertices } = indexVertices(pos);
|
||||||
|
const mesh = {
|
||||||
|
vertPos: new Module.Vector_vec3(),
|
||||||
|
triVerts: new Module.Vector_ivec3(),
|
||||||
|
vertNormal: new Module.Vector_vec3(),
|
||||||
|
halfedgeTangent: new Module.Vector_vec4()
|
||||||
|
};
|
||||||
|
for (let i = 0, l = vertices.length; i < l; ) {
|
||||||
|
mesh.vertPos.push_back({
|
||||||
|
x: vertices[i++],
|
||||||
|
y: vertices[i++],
|
||||||
|
z: vertices[i++]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (let i = 0, l = index.length; i < l; ) {
|
||||||
|
mesh.triVerts.push_back([ index[i++], index[i++], index[i++] ]);
|
||||||
|
}
|
||||||
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function indexVertices(pos) {
|
||||||
|
mesh.log(`indexing ${pos.length/3} vertices`);
|
||||||
|
let ipos = 0;
|
||||||
|
const index = [];
|
||||||
|
const vertices = [];
|
||||||
|
const cache = {};
|
||||||
|
const temp = { x: 0, y: 0, z: 0 };
|
||||||
|
for (let i=0, length = pos.length; i<length; ) {
|
||||||
|
temp.x = pos[i++];
|
||||||
|
temp.y = pos[i++];
|
||||||
|
temp.z = pos[i++];
|
||||||
|
let key = [
|
||||||
|
((temp.x * 100000) | 0),
|
||||||
|
((temp.y * 100000) | 0),
|
||||||
|
((temp.z * 100000) | 0)
|
||||||
|
].join('');
|
||||||
|
let ip = cache[key];
|
||||||
|
if (ip >= 0) {
|
||||||
|
index.push(ip);
|
||||||
|
} else {
|
||||||
|
index.push(ipos);
|
||||||
|
cache[key] = (ipos++);
|
||||||
|
vertices.push(temp.x, temp.y, temp.z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { index, vertices };
|
||||||
|
}
|
||||||
|
|
||||||
|
function pos2mesh(pos) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Module.onRuntimeInitialized = () => {
|
||||||
|
Module.setup();
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
let c = Module.cube([1,1,1], true);
|
||||||
|
console.log({
|
||||||
|
c,
|
||||||
|
cm: c.getMesh(),
|
||||||
|
cm0: c.getMesh({ normal: false, mode: 0 }),
|
||||||
|
cm1: c.getMesh({ normal: false, mode: 1 }),
|
||||||
|
cm2: c.getMesh({ normal: false, mode: 2 }),
|
||||||
|
cmn0: c.getMesh({ normal: true, mode: 0 }),
|
||||||
|
cmn1: c.getMesh({ normal: true, mode: 1 }),
|
||||||
|
cmn2: c.getMesh({ normal: true, mode: 2 })
|
||||||
|
});
|
||||||
|
|
||||||
|
let l = 5000;
|
||||||
|
let s = Module.sphere(10, 100);
|
||||||
|
let sm = s.getMesh();
|
||||||
|
console.log({
|
||||||
|
s,
|
||||||
|
sm,
|
||||||
|
md0: s.getMesh({ mode: 0 }),
|
||||||
|
md1: s.getMesh({ mode: 1 }),
|
||||||
|
md2: s.getMesh({ mode: 2 }),
|
||||||
|
});
|
||||||
|
|
||||||
|
console.time('getMesh');
|
||||||
|
for (let i=0; i<l; i++) s.getMesh();
|
||||||
|
console.timeEnd('getMesh');
|
||||||
|
|
||||||
|
console.time(`getMeshDirect`);
|
||||||
|
for (let i=0; i<l; i++) s.getMesh({ mode: 0 });
|
||||||
|
console.timeEnd(`getMeshDirect`);
|
||||||
|
|
||||||
|
console.time(`getMeshDirect`);
|
||||||
|
for (let i=0; i<l; i++) s.getMesh({ mode: 1 });
|
||||||
|
console.timeEnd(`getMeshDirect`);
|
||||||
|
|
||||||
|
console.time(`getMeshDirect`);
|
||||||
|
for (let i=0; i<l; i++) s.getMesh({ mode: 2 });
|
||||||
|
console.timeEnd(`getMeshDirect`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
gapp.overlay(base, {
|
gapp.overlay(base, {
|
||||||
CSG
|
CSG
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -404,6 +404,11 @@ const tool = {
|
||||||
mesh: data
|
mesh: data
|
||||||
})]).promote();
|
})]).promote();
|
||||||
api.selection.set([group]);
|
api.selection.set([group]);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
api.log.emit(`union error: ${error}`);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
api.log.emit('union complete').unpin();
|
api.log.emit('union complete').unpin();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -126,11 +126,11 @@ const log = api.log = {
|
||||||
|
|
||||||
lines: 20, // max log lines before forced-age out
|
lines: 20, // max log lines before forced-age out
|
||||||
|
|
||||||
emit(msg) {
|
emit() {
|
||||||
let { age, data, lines, render } = log;
|
let { age, data, lines, render } = log;
|
||||||
let now = Date.now();
|
let now = Date.now();
|
||||||
data.push({
|
data.push({
|
||||||
text: `${dbug.since()} | ${msg}`,
|
text: `${dbug.since()} | ${[...arguments].join(' ')}`,
|
||||||
time: now
|
time: now
|
||||||
});
|
});
|
||||||
if (api.isDebug) {
|
if (api.isDebug) {
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,9 @@ const dispatch = exports({
|
||||||
send.done(data);
|
send.done(data);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.trace(error.stack);
|
if (error.stack) {
|
||||||
|
console.trace(error.stack);
|
||||||
|
}
|
||||||
send.error(error.toString());
|
send.error(error.toString());
|
||||||
// dispatch.send({ data: {error: error.toString()}, done: true });
|
// dispatch.send({ data: {error: error.toString()}, done: true });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue