analysis returns actionable data for visualization

This commit is contained in:
Stewart Allen 2022-01-11 18:41:49 -05:00
commit ae0a884088
5 changed files with 93 additions and 42 deletions

View file

@ -155,10 +155,12 @@ function space_init(data) {
} }
let { shiftKey, metaKey, ctrlKey, code } = evt; let { shiftKey, metaKey, ctrlKey, code } = evt;
switch (code) { switch (code) {
case 'KeyI':
return api.file.import();
case 'KeyX':
return api.file.export();
case 'KeyD': case 'KeyD':
if (shiftKey) { if (shiftKey) return selection.duplicate();
return selection.duplicate();
}
break; break;
case 'KeyC': case 'KeyC':
return selection.centerXY().focus(); return selection.centerXY().focus();
@ -166,10 +168,10 @@ function space_init(data) {
return selection.floor().focus(); return selection.floor().focus();
case 'KeyM': case 'KeyM':
return selection.merge(); return selection.merge();
case 'KeyI': case 'KeyA':
return api.file.import(); return selection.analyze();
case 'KeyX': case 'KeyR':
return api.file.export(); return selection.repair();
case 'KeyV': case 'KeyV':
return selection.focus(); return selection.focus();
case 'KeyW': case 'KeyW':
@ -182,10 +184,6 @@ function space_init(data) {
return selection.boundsBox({toggle:true}); return selection.boundsBox({toggle:true});
case 'KeyH': case 'KeyH':
return space.view.home(); return space.view.home();
case 'KeyA':
return api.tool.analyze();
case 'KeyR':
return api.tool.repair();
case 'KeyT': case 'KeyT':
return space.view.top(); return space.view.top();
case 'KeyZ': case 'KeyZ':

View file

@ -109,6 +109,14 @@ let selection = {
} }
}, },
analyze() {
tool.analyze(selection.models());
},
repair() {
tool.repair(selection.models());
},
merge() { merge() {
tool.merge(selection.models()); tool.merge(selection.models());
}, },
@ -283,24 +291,33 @@ let tool = {
}); });
}, },
analyze() { analyze(models) {
let promises = []; let promises = [];
api.log.emit('analyzing mesh(es)...').pin(); api.log.emit('analyzing mesh(es)...').pin();
for (let m of selection.models()) { let areas = [];
let edges = [];
for (let m of models) {
let p = worker.model_analyze(m.id).then(data => { let p = worker.model_analyze(m.id).then(data => {
// console.log({data}); areas.appendAll(data.mapped.areas);
edges.appendAll(data.mapped.edges);
}); });
promises.push(p); promises.push(p);
} }
Promise.all(promises).then(() => { Promise.all(promises).then(() => {
let newmdl = areas.map(area => new mesh.model({
file: (area.length/3).toString(),
mesh: area.toFloat32()
}));
mesh.api.group.new(newmdl);
console.log({ areas, edges, newmdl });
api.log.emit('analysis complete').unpin(); api.log.emit('analysis complete').unpin();
}); });
}, },
repair() { repair(models) {
let promises = []; let promises = [];
api.log.emit('repairing mesh(es)...').pin(); api.log.emit('repairing mesh(es)...').pin();
for (let m of selection.models()) { for (let m of models) {
let p = worker.model_heal(m.id).then(data => { let p = worker.model_heal(m.id).then(data => {
if (data) { if (data) {
m.reload( m.reload(

View file

@ -85,7 +85,7 @@ mesh.model = class MeshModel extends mesh.object {
matrix: this.matrix, matrix: this.matrix,
id: this.id id: this.id
}).then(data => { }).then(data => {
return mesh.api.group.new([new mesh.model({ mesh.api.group.new([new mesh.model({
file: `${this.file}-dup`, file: `${this.file}-dup`,
mesh: data mesh: data
})]); })]);

View file

@ -91,23 +91,13 @@ mesh.tool = class MeshTool {
} else { } else {
faces = faces.map(v => v*3); faces = faces.map(v => v*3);
} }
// unique vertex array
this.vertices = vertices; this.vertices = vertices;
// unique face array. elements are offset into vertices
this.faces = faces; this.faces = faces;
return this; return this;
} }
// return non-indexed vertex list (for compatibility)
unrolled() {
let out = [];
let vertices = this.vertices;
for (let face of this.faces) {
out.push(vertices[face]);
out.push(vertices[face+1]);
out.push(vertices[face+2]);
}
return out;
}
/** /**
* 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.
@ -212,10 +202,16 @@ mesh.tool = class MeshTool {
let l3 = getLine(v3, v1).addFace(i); let l3 = getLine(v3, v1).addFace(i);
} }
// array of unique line segment records with vertices
// and a list of faces. one = edge, two = inside
// all lines should be inside or object is not manifold
// but does not preclude self-intersecting geometries
this.lines = lines; this.lines = lines;
// lines belonging to only one face are on the edge of a hole // lines belonging to only one face are on the edge of a hole
let edges = this.edges = lines.filter(l => l.faces.length === 1); let edges = this.edges = lines.filter(l => l.faces.length === 1);
// arrays of connected edges that form a closed loop
let loops = this.loops = []; let loops = this.loops = [];
// connect edge lines into closed loops // connect edge lines into closed loops
@ -383,22 +379,64 @@ mesh.tool = class MeshTool {
this.areas.push(emitLoop(loop)); this.areas.push(emitLoop(loop));
} }
// track newly created faces when loop areas are merged // auto-merge newly found enclosed areas
let faceCount = this.faces.length;
if (opt.merge) { if (opt.merge) {
this.merge(); this.merge();
} }
this.newFaces = this.faces.length - faceCount;
// de-reference loops and edges
if (opt.mapped) {
this.mapped = {
edges: this.expandLoop(this.edges),
areas: this.areas.map(a => this.expandArray(a))
};
}
return this; return this;
} }
// merge poly areas into faces // add vertex data from given index into array
appendVertex(index, array = []) {
let vertx = this.vertices;
array.push(vertx[index++]);
array.push(vertx[index++]);
array.push(vertx[index]);
return array;
}
// add vertices from a vertex index list to an array
expandArray(indices, array = []) {
for (let i of indices) {
this.appendVertex(i, array);
}
return array;
}
// turn loop into XYZ vertex array
expandLoop(loop, array = []) {
for (let line of loop) {
this.appendVertex(line.v1, array);
this.appendVertex(line.v2, array);
}
return array;
}
// merge generated poly areas into faces
merge() { merge() {
for (let area of this.areas || []) { for (let area of this.areas || []) {
this.faces.appendAll(area); this.faces.appendAll(area);
} }
} }
// return non-indexed vertex list (for compatibility)
unrolled() {
let out = [];
let vertices = this.vertices;
for (let face of this.faces) {
this.appendVertex(face, out);
}
return out;
}
}; };
})(); })();

View file

@ -38,7 +38,7 @@ function translate_encode(id, matrix) {
return geo.attributes.position.array; return geo.attributes.position.array;
} }
function analyze(id) { function analyze(id, opt = {}) {
log(`${id} | indexing...`); log(`${id} | indexing...`);
let geo = cache[id].geo; let geo = cache[id].geo;
let tool = new mesh.tool({ let tool = new mesh.tool({
@ -47,7 +47,7 @@ function analyze(id) {
debug: false debug: false
}); });
log(`${id} | analyzing...`); log(`${id} | analyzing...`);
tool.heal(); tool.heal(opt);
dbug.log(tool); dbug.log(tool);
return tool; return tool;
} }
@ -80,22 +80,20 @@ let model = {
}, },
analyze(id) { analyze(id) {
let tool = analyze(id); let tool = analyze(id, { mapped: true });
let { stats, newFaces } = tool; let { stats, mapped } = tool;
let { cull, dups, faces } = stats; let { cull, dups, faces } = stats;
log(`${id} | face count=${faces} bad=${cull} dup=${dups}`); log(`${id} | face count=${faces} bad=${cull} dup=${dups}`);
log(`${id} | open loops=${tool.loops.length} edges=${tool.edges.length}`); log(`${id} | open loops=${tool.loops.length} edges=${tool.edges.length}`);
return { return { stats, mapped };
new: newFaces
};
}, },
heal(id) { heal(id) {
let tool = analyze(id); let tool = analyze(id);
log(`${id} | unrolling...`); log(`${id} | unrolling...`);
return true || tool.newFaces ? { return {
vertices: tool.unrolled().toFloat32(), vertices: tool.unrolled().toFloat32(),
} : 0; };
} }
}; };