diff --git a/release.md b/release.md index 1fec24d9..7a90de0b 100644 --- a/release.md +++ b/release.md @@ -33,7 +33,7 @@ ## Mesh:Tool * `C` add preferences for normal length and color -* `-` add preferences for face selection and matching +* `C` add preferences for face selection and surface matching * `P` add svg and image import conversion (created shared load. libs) * `C` replace triangulation algorithm that was causing some union failures * `C` add version chooser diff --git a/src/main/mesh.js b/src/main/mesh.js index c0a0b782..e6cfa506 100644 --- a/src/main/mesh.js +++ b/src/main/mesh.js @@ -105,7 +105,7 @@ function restore_space() { mesh.db.space.iterate({ map: true }).then(cached => { for (let [id, data] of Object.entries(cached)) { if (count++ === 0) { - mesh.api.log.emit(`restoring workspace...`); + mesh.api.log.emit(`restoring workspace`); } // restore group if (Array.isArray(data)) { @@ -359,6 +359,8 @@ function space_init(data) { if (mode !== api.modes.object) { for (let m of selection.models()) { m.deleteSelections(mode); + // re-gen face index + api.mode.check(); } } else { for (let s of selection.list(true)) { @@ -427,17 +429,21 @@ function space_init(data) { selection.update(); } else { let { modes } = api; + let radians = 0; switch(api.mode.get()) { case modes.object: selection.toggle(shiftKey ? model : model.group); break; + case modes.surface: + radians = api.prefs.map.surface.radians; case modes.face: case modes.line: case modes.vertex: // find faces adjacent to point/line clicked model.find(int, altKey ? { toggle: true } : - shiftKey ? { clear: true } : { select: true }); + shiftKey ? { clear: true } : { select: true }, + radians); break; } } @@ -546,6 +552,12 @@ function set_normals_color(color) { prefs.save(); } +function set_surface_radians(radians) { + let { prefs } = mesh.api; + prefs.map.surface.radians = radians || 0.1; + prefs.save(); +} + // bind functions to topics broker.listeners({ edit_split, @@ -557,6 +569,7 @@ broker.listeners({ set_darkmode, set_normals_color, set_normals_length, + set_surface_radians }); // remove version cache bust from url diff --git a/src/mesh/api.js b/src/mesh/api.js index 9e0f5097..8978d31e 100644 --- a/src/mesh/api.js +++ b/src/mesh/api.js @@ -378,7 +378,7 @@ const tool = { analyze(models, opt = { compound: true }) { models = fallback(models); - api.log.emit('analyzing mesh(es)...').pin(); + api.log.emit('analyzing mesh(es)').pin(); let promises = []; let mcore = new Matrix4().makeRotationX(Math.PI / 2); for (let m of models) { @@ -400,6 +400,21 @@ const tool = { }); }, + mapFaces(models) { + models = fallback(models); + api.log.emit('mapping faces').pin(); + let promises = []; + for (let m of models) { + let p = worker.model_mapFaces({ id: m.id }).then(data => { + // console.log({map_info: data}); + }); + promises.push(p); + } + Promise.all(promises).then(() => { + api.log.emit('mapping complete').unpin(); + }); + }, + heal(models, opt = {}) { models = fallback(models); let promises = []; @@ -426,7 +441,7 @@ const tool = { repair(models) { models = fallback(models); - api.log.emit('repairing mesh(es)...').pin(); + api.log.emit('repairing mesh(es)').pin(); tool.heal(models, { merge: true }).then(() => { api.log.emit('repair commplete').unpin(); api.selection.update(); @@ -462,12 +477,19 @@ const mode = { $(`mode-${key}`).classList.remove('selected'); } $(`mode-${mode}`).classList.add('selected'); + api.mode.check(); }, get() { return prefs.map.mode; }, + check() { + if (prefs.map.mode === modes.surface) { + tool.mapFaces(); + } + }, + object() { mode.set(modes.object); }, @@ -518,6 +540,9 @@ const prefs = { length: 0.5, color_lite: 0xff0000, color_dark: 0x00ffff + }, + surface: { + radians: 0.1 } }, diff --git a/src/mesh/build.js b/src/mesh/build.js index 1aa29224..a6da5668 100644 --- a/src/mesh/build.js +++ b/src/mesh/build.js @@ -214,7 +214,7 @@ api.welcome = function(version = "unknown") { api.settings = function() { const { util } = mesh; const { prefs } = api; - const { normals, space } = prefs.map; + const { surface, normals, space } = prefs.map; const { dark } = space; const col1 = h.div([ @@ -249,7 +249,18 @@ api.settings = function() { }), ]); - modal.show('settings', h.div({ class: "settings" }, [ col1, col2 ] )); + const col3 = h.div([ + h.label(''), + h.label('surface'), + h.label('radians'), + h.input({ type: "text", size: 5, value: surface.radians, + onchange: ev => call.set_surface_radians(ev.target.value) + }), + h.label(' '), + h.label(' ') + ]); + + modal.show('settings', h.div({ class: "settings" }, [ col1, col2, col3 ] )); } // create html elements diff --git a/src/mesh/model.js b/src/mesh/model.js index cd0841b4..3ec7a24a 100644 --- a/src/mesh/model.js +++ b/src/mesh/model.js @@ -586,12 +586,12 @@ mesh.model = class MeshModel extends mesh.object { } // find adjacent faces to clicked point/line on a face - find(int, action) { + find(int, action, radians = 0) { let { point, face } = int; let { x, y, z } = point; let { a, b, c } = face; worker.model_select({ - id: this.id, x, y:-z, z:y, a, b, c, matrix: this.matrix + id: this.id, x, y:-z, z:y, a, b, c, matrix: this.matrix, radians }).then(data => { let { faces, edges, verts, point } = data; // console.log({data}); diff --git a/src/mesh/tool.js b/src/mesh/tool.js index 416c7101..d003cb8c 100644 --- a/src/mesh/tool.js +++ b/src/mesh/tool.js @@ -16,19 +16,27 @@ const { geom } = mesh; * to base.util.triagulate */ mesh.tool = class MeshTool { - constructor(params = {}, opt = { clean: true }) { + constructor(params = {}) { this.precision = Math.pow(10, params.precision || 6); - if (params.vertices) { - this.setVertices(params.vertices, opt); + } + + checkVertices(vertices, mod) { + if (!vertices) { + throw "missing vertices"; } + if (mod && vertices.length % mod !== 0) { + throw "invalid vertices"; + } + return vertices; } /** - * @param {number[]} vertices + * @param {number[]} vertices non-indexed */ - setVertices(vertices, opt = {}) { - this.checkVertices(vertices); + generateFaces(vertices, opt = {}) { + this.checkVertices(vertices, 3); let doClean = opt.clean !== false; + let doDedup = opt.dedup !== false; let faces = []; let fcac = {}; // seen face hash let fnew = []; // accumulate vertex triplets @@ -65,13 +73,17 @@ mesh.tool = class MeshTool { cull++; continue; } - let key = fnew.slice().sort().join('-'); - // drop duplicate faces (sort handles reverse order) - if (!fcac[key]) { - faces.appendAll(fnew); - fcac[key] = key; + if (doDedup) { + let key = fnew.slice().sort().join('-'); + // drop duplicate faces (sort handles reverse order) + if (!fcac[key]) { + faces.appendAll(fnew); + fcac[key] = key; + } else { + dups++; + } } else { - dups++; + faces.appendAll(fnew); } fnew = []; } @@ -79,29 +91,18 @@ mesh.tool = class MeshTool { faces.push(vpos); } } - vertices = nuvt; - this.stats = {cull, dups, faces:faces.length/3}; + this.stats = {cull, dups, faces: faces.length/3}; // unique vertex array - this.vertices = vertices; + this.uvert = nuvt; // unique face array. elements are offset into vertices this.faces = faces; return this; } - checkVertices(v) { - const vertices = v || this.vertices; - if (!vertices) { - throw "missing vertices"; - } - if (vertices.length % 3 !== 0) { - throw "invalid vertices"; - } - return vertices; - } - - generateFaceNormals() { - const vertices = this.checkVertices(this.vertices); + generateFaceMap(vertices) { + this.checkVertices(vertices, 3); const normals = this.normals = []; + const sides = this.sides = []; const _va = new THREE.Vector3(); const _vb = new THREE.Vector3(); const _vc = new THREE.Vector3(); @@ -109,19 +110,80 @@ mesh.tool = class MeshTool { _va.set(vertices[i++], vertices[i++], vertices[i++]); _vb.set(vertices[i++], vertices[i++], vertices[i++]); _vc.set(vertices[i++], vertices[i++], vertices[i++]); - let vn = THREE.computeFaceNormal(_va, _vb, _vc); - normals.push(vn.x, vn.y, vn.z); + const vn = THREE.computeFaceNormal(_va, _vb, _vc); + normals.push([vn.x, vn.y, vn.z]); } - return normals; + const prec = this.precision; + const vround = vertices.map(v => (v * prec) | 0); + const sidekeyid = {}; + const side2face = this.side2face = []; + for (let i=0, side=0, face=0, l=vround.length; i