complete body separation code
This commit is contained in:
parent
d5114f11a7
commit
406cacc453
5 changed files with 72 additions and 9 deletions
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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 }),
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue