diff --git a/notes.md b/notes.md index 6cd11088..5e6e8912 100644 --- a/notes.md +++ b/notes.md @@ -119,10 +119,10 @@ # Mesh:Tool * 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) * better z snap using just vertexes from face intersected * add section view. local clip. raycast skip points above plane -* add isolate op = separate bodies * add decimate op = face reduction * add flatten/crush op: for z bottoms (or surfaces?) * allow setting model/group origin for scale/rotate diff --git a/release.md b/release.md index 7cbad9e4..f79be8e8 100644 --- a/release.md +++ b/release.md @@ -2,6 +2,10 @@ 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) diff --git a/src/add/manifold.js b/src/add/manifold.js deleted file mode 100644 index 77abbd66..00000000 --- a/src/add/manifold.js +++ /dev/null @@ -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= 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 - }); - -}); diff --git a/src/geo/csg.js b/src/geo/csg.js index 085095b3..a5a6ef2b 100644 --- a/src/geo/csg.js +++ b/src/geo/csg.js @@ -3,7 +3,7 @@ "use strict"; // dep: geo.base -// dep: add.manifold +// dep: ext.manifold gapp.register("geo.csg", [], (root, exports) => { const { base } = root; @@ -16,10 +16,14 @@ const CSG = { mesh.log(`manifold.union ${arguments.length} meshes`); const args = [...arguments].map(a => new Module.Manifold(a)); 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 ]) { m.delete(); } + if (errors.reduce((a,b) => a+b)) { + throw `${errors}`; + } return output; }, @@ -29,7 +33,7 @@ const CSG = { mesh.log(`manifold.subtract ${arguments.length} meshes`); const args = [...arguments].map(a => new Module.Manifold(a)); const result = Module.difference(...args); - const output = result.getMesh(); + const output = result.getMesh({ mode: 2 }); for (let m of [ ...args, result ]) { m.delete(); } @@ -41,23 +45,137 @@ const CSG = { mesh.log(`manifold.intersect ${arguments.length} meshes`); const args = [...arguments].map(a => new Module.Manifold(a)); const result = Module.intersection(...args); - const output = result.getMesh(); + const output = result.getMesh({ mode: 2 }); for (let m of [ ...args, result ]) { m.delete(); } return output; }, - toPositionArray(geom) { - return add.manifold.mesh2pos(geom); + toPositionArray(mesh) { + 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) { - return add.manifold.pos2mesh(array); + fromPositionArray(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 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= 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 { + api.log.emit(`union error: ${error}`); + }) + .finally(() => { api.log.emit('union complete').unpin(); }); }, diff --git a/src/mesh/build.js b/src/mesh/build.js index 00570d28..53255861 100644 --- a/src/mesh/build.js +++ b/src/mesh/build.js @@ -126,11 +126,11 @@ const log = api.log = { lines: 20, // max log lines before forced-age out - emit(msg) { + emit() { let { age, data, lines, render } = log; let now = Date.now(); data.push({ - text: `${dbug.since()} | ${msg}`, + text: `${dbug.since()} | ${[...arguments].join(' ')}`, time: now }); if (api.isDebug) { diff --git a/src/moto/worker.js b/src/moto/worker.js index 3e63c9e4..245815e5 100644 --- a/src/moto/worker.js +++ b/src/moto/worker.js @@ -84,7 +84,9 @@ const dispatch = exports({ send.done(data); } } catch (error) { - console.trace(error.stack); + if (error.stack) { + console.trace(error.stack); + } send.error(error.toString()); // dispatch.send({ data: {error: error.toString()}, done: true }); }