diff --git a/src/main/mesh.js b/src/main/mesh.js index 68760c87..49ff9145 100644 --- a/src/main/mesh.js +++ b/src/main/mesh.js @@ -155,10 +155,12 @@ function space_init(data) { } let { shiftKey, metaKey, ctrlKey, code } = evt; switch (code) { + case 'KeyI': + return api.file.import(); + case 'KeyX': + return api.file.export(); case 'KeyD': - if (shiftKey) { - return selection.duplicate(); - } + if (shiftKey) return selection.duplicate(); break; case 'KeyC': return selection.centerXY().focus(); @@ -166,10 +168,10 @@ function space_init(data) { return selection.floor().focus(); case 'KeyM': return selection.merge(); - case 'KeyI': - return api.file.import(); - case 'KeyX': - return api.file.export(); + case 'KeyA': + return selection.analyze(); + case 'KeyR': + return selection.repair(); case 'KeyV': return selection.focus(); case 'KeyW': @@ -182,10 +184,6 @@ function space_init(data) { return selection.boundsBox({toggle:true}); case 'KeyH': return space.view.home(); - case 'KeyA': - return api.tool.analyze(); - case 'KeyR': - return api.tool.repair(); case 'KeyT': return space.view.top(); case 'KeyZ': diff --git a/src/mesh/api.js b/src/mesh/api.js index b1feee5a..8a6bed41 100644 --- a/src/mesh/api.js +++ b/src/mesh/api.js @@ -109,6 +109,14 @@ let selection = { } }, + analyze() { + tool.analyze(selection.models()); + }, + + repair() { + tool.repair(selection.models()); + }, + merge() { tool.merge(selection.models()); }, @@ -283,24 +291,33 @@ let tool = { }); }, - analyze() { + analyze(models) { let promises = []; 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 => { - // console.log({data}); + areas.appendAll(data.mapped.areas); + edges.appendAll(data.mapped.edges); }); promises.push(p); } 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(); }); }, - repair() { + repair(models) { let promises = []; 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 => { if (data) { m.reload( diff --git a/src/mesh/model.js b/src/mesh/model.js index 2bcc35f6..d8e36fc2 100644 --- a/src/mesh/model.js +++ b/src/mesh/model.js @@ -85,7 +85,7 @@ mesh.model = class MeshModel extends mesh.object { matrix: this.matrix, id: this.id }).then(data => { - return mesh.api.group.new([new mesh.model({ + mesh.api.group.new([new mesh.model({ file: `${this.file}-dup`, mesh: data })]); diff --git a/src/mesh/tool.js b/src/mesh/tool.js index 17bbf2ff..a0ce5cd7 100644 --- a/src/mesh/tool.js +++ b/src/mesh/tool.js @@ -91,23 +91,13 @@ mesh.tool = class MeshTool { } else { faces = faces.map(v => v*3); } + // unique vertex array this.vertices = vertices; + // unique face array. elements are offset into vertices this.faces = faces; 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. * construct ordered line maps with array of connected edges. @@ -212,10 +202,16 @@ mesh.tool = class MeshTool { 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; // lines belonging to only one face are on the edge of a hole let edges = this.edges = lines.filter(l => l.faces.length === 1); + + // arrays of connected edges that form a closed loop let loops = this.loops = []; // connect edge lines into closed loops @@ -383,22 +379,64 @@ mesh.tool = class MeshTool { this.areas.push(emitLoop(loop)); } - // track newly created faces when loop areas are merged - let faceCount = this.faces.length; + // auto-merge newly found enclosed areas if (opt.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; } - // 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() { for (let area of this.areas || []) { 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; + } }; })(); diff --git a/src/mesh/work.js b/src/mesh/work.js index 9de37325..e10287a2 100644 --- a/src/mesh/work.js +++ b/src/mesh/work.js @@ -38,7 +38,7 @@ function translate_encode(id, matrix) { return geo.attributes.position.array; } -function analyze(id) { +function analyze(id, opt = {}) { log(`${id} | indexing...`); let geo = cache[id].geo; let tool = new mesh.tool({ @@ -47,7 +47,7 @@ function analyze(id) { debug: false }); log(`${id} | analyzing...`); - tool.heal(); + tool.heal(opt); dbug.log(tool); return tool; } @@ -80,22 +80,20 @@ let model = { }, analyze(id) { - let tool = analyze(id); - let { stats, newFaces } = tool; + let tool = analyze(id, { mapped: true }); + let { stats, mapped } = tool; let { cull, dups, faces } = stats; log(`${id} | face count=${faces} bad=${cull} dup=${dups}`); log(`${id} | open loops=${tool.loops.length} edges=${tool.edges.length}`); - return { - new: newFaces - }; + return { stats, mapped }; }, heal(id) { let tool = analyze(id); log(`${id} | unrolling...`); - return true || tool.newFaces ? { + return { vertices: tool.unrolled().toFloat32(), - } : 0; + }; } };