complete body separation code

This commit is contained in:
Stewart Allen 2022-04-05 09:40:25 -07:00
commit 406cacc453
5 changed files with 72 additions and 9 deletions

View file

@ -14,6 +14,7 @@ Full docs @ https://docs.grid.space/projects/kiri-moto
## Mesh:Tool ## Mesh:Tool
* `C` multi-body identification and isolation
* `-` parameterize svg and image import * `-` parameterize svg and image import

View file

@ -400,6 +400,26 @@ const tool = {
}); });
}, },
isolate(models) {
models = fallback(models);
api.log.emit('isolating bodies').pin();
let promises = [];
let mcore = new Matrix4().makeRotationX(Math.PI / 2);
for (let m of models) {
let p = worker.model_isolate({ id: m.id }).then(bodies => {
bodies = bodies.map(vert => new mesh.model({
file: m.file,
mesh: vert.toFloat32()
})).map( nm => nm.applyMatrix4(mcore.clone().multiply(m.mesh.matrixWorld)) );
mesh.api.group.new(bodies, undefined, "isolate").setSelected();
});
promises.push(p);
}
Promise.all(promises).then(() => {
api.log.emit('isolation complete').unpin();
});
},
mapFaces(models) { mapFaces(models) {
models = fallback(models); models = fallback(models);
api.log.emit('mapping faces').pin(); api.log.emit('mapping faces').pin();

View file

@ -377,6 +377,7 @@ function ui_build() {
h.div({ _: "repair", class: "head" }), h.div({ _: "repair", class: "head" }),
// h.div({ class: "vsep" }), // h.div({ class: "vsep" }),
h.button({ _: 'analyze', onclick: tool.analyze }), h.button({ _: 'analyze', onclick: tool.analyze }),
h.button({ _: 'isolate', onclick: tool.isolate }),
h.button({ _: 'patch', onclick: tool.repair }), h.button({ _: 'patch', onclick: tool.repair }),
// h.button({ _: 'rebuild', onclick: tool.rebuild }), // h.button({ _: 'rebuild', onclick: tool.rebuild }),
h.button({ _: 'clean', onclick: tool.clean }), h.button({ _: 'clean', onclick: tool.clean }),

View file

@ -101,7 +101,7 @@ mesh.tool = class MeshTool {
} }
generateFaceMap(vertices) { generateFaceMap(vertices) {
this.checkVertices(vertices, 3); this.vertices = this.checkVertices(vertices, 3);
const normals = this.normals = []; const normals = this.normals = [];
const sides = this.sides = []; const sides = this.sides = [];
const _va = new THREE.Vector3(); const _va = new THREE.Vector3();
@ -158,12 +158,13 @@ mesh.tool = class MeshTool {
return sides.map(side => this.side2face[side].filter(v => v != face)).flat(); return sides.map(side => this.side2face[side].filter(v => v != face)).flat();
} }
findConnectedSurface(faces, radians, filterZ) { // depends on generateFaceMap() being run first
const found = {}; findConnectedSurface(faces, radians, filterZ, found = {}) {
const norms = this.normals; const norms = this.normals;
if (filterZ) { if (filterZ) {
faces = faces.filter(f => norms[f][2] >= filterZ); faces = faces.filter(f => norms[f][2] >= filterZ);
} }
const checked = {};
const check = faces.slice(); const check = faces.slice();
for (let face of faces) { for (let face of faces) {
found[face] = 1; found[face] = 1;
@ -173,11 +174,17 @@ mesh.tool = class MeshTool {
while (check.length) { while (check.length) {
const face = check.shift(); const face = check.shift();
const norm = norms[face]; const norm = norms[face];
// if (!norm || norm.length < 3) {
// throw `invalid face ${face}`;
// }
if (filterZ !== undefined && norm[2] < filterZ) { if (filterZ !== undefined && norm[2] < filterZ) {
continue; continue;
} }
const fadj = this.getAdjacentFaces(face).filter(f => !found[f]); const fadj = this.getAdjacentFaces(face);
for (let f of fadj) { for (let f of fadj) {
if (found[f] || checked[f]) {
continue;
}
// #1 most accurate // #1 most accurate
// const nf = this.normals[f]; // const nf = this.normals[f];
// _v1.set(nf[0], nf[1], nf[2]); // _v1.set(nf[0], nf[1], nf[2]);
@ -195,6 +202,7 @@ mesh.tool = class MeshTool {
if (fn <= radians) { if (fn <= radians) {
faces.push(f); faces.push(f);
check.push(f) check.push(f)
checked[f] = 1;
found[f] = 1; found[f] = 1;
} }
} }
@ -202,6 +210,29 @@ mesh.tool = class MeshTool {
return faces; return faces;
} }
// depends on generateFaceMap() being run first
isolateBodies() {
const verts = this.checkVertices(this.vertices);
const bodies = [];
const used = {};
for (let i=0, l=this.normals.length-1; i<l; i++) {
if (used[i]) {
continue;
}
const body = this.findConnectedSurface([i], Infinity, undefined, used);
if (body.length) {
// todo: pre-allocate known length
// then copy array regions from verts to bverts
const bverts = [];
for (let f of body) {
bverts.push(...verts.slice(f*9, f*9+9));
}
bodies.push(bverts);
}
}
return bodies;
}
/** /**
* finds edge lines which are line segments on a single face. * finds edge lines which are line segments on a single face.
* construct ordered line maps with array of connected edges. * construct ordered line maps with array of connected edges.

View file

@ -76,16 +76,21 @@ function analyze(id, opt = {}) {
return tool; return tool;
} }
function mapFaces(id, opt = {}) { function isolateBodies(id) {
let tool = mapFaces(id);
log(`${id} | isolating bodies`);
return tool.isolateBodies();
}
function mapFaces(id) {
let rec = cache[id]; let rec = cache[id];
let { geo, tool } = rec; let { geo, tool } = rec;
if (!tool) { if (!tool) {
tool = rec.tool = new mesh.tool(); tool = rec.tool = new mesh.tool();
} }
const vertices = geo.attributes.position.array;
if (!tool.normals) { if (!tool.normals) {
log(`${id} | generating face map`); log(`${id} | generating face map`);
tool.generateFaceMap(vertices); tool.generateFaceMap(geo.attributes.position.array);
} }
return tool; return tool;
} }
@ -275,11 +280,16 @@ let model = {
}, },
mapFaces(data) { mapFaces(data) {
let { id, opt } = data; let { id } = data;
let tool = mapFaces(id, opt); let tool = mapFaces(id);
return { mapped: true }; return { mapped: true };
}, },
isolate(data) {
let { id } = data;
return isolateBodies(id);
},
// given model and point, locate matching vertices, lines, and faces // given model and point, locate matching vertices, lines, and faces
select(data) { select(data) {
let { id, x, y, z, a, b, c, matrix, surface } = data; let { id, x, y, z, a, b, c, matrix, surface } = data;