diff --git a/src/kiri/main.js b/src/kiri/main.js index 86acdb1d..9acaee49 100644 --- a/src/kiri/main.js +++ b/src/kiri/main.js @@ -3344,23 +3344,11 @@ let url = WIN.URL.createObjectURL(new Blob([data], {type: "octet/stream"})); $('mod-any').innerHTML = `x`; $('_dexport_').click(); - // fixes Chrome/iOS download, but loses name and breaks others - // let reader = new FileReader(); - // let out = new Blob([data], {type: 'octet/stream'}); - // reader.onload = function(e){ - // window.location.href = reader.result; - // } - // reader.readAsDataURL(out); } // prevent safari from exiting full screen mode DOC.onkeydown = function (evt) { if (evt.keyCode == 27) evt.preventDefault() } - // K3DB.onIdle(function() { - // upgrade on db idle. maybe delay init, too - // console.log("k3db idle"); - // }); - // run optional module functions NOW before kiri-init has run if (Array.isArray(self.kirimod)) { kirimod.forEach(function(mod) { mod(kiri.api) }); diff --git a/src/main/mesh.js b/src/main/mesh.js index 8e6c2d8a..16be54df 100644 --- a/src/main/mesh.js +++ b/src/main/mesh.js @@ -7,6 +7,7 @@ let broker = gapp.broker; let call = broker.send; let dbindex = [ "admin", "space" ]; +let worker = moto.client.fn; let { Quaternion, Vector3 } = THREE; // set below. called once the DOM readyState = complete @@ -160,13 +161,13 @@ function space_init(data) { case 'KeyF': return selection.floor().focus(); case 'KeyM': - return selection.merge(); + return api.tool.merge(); case 'KeyA': - return selection.analyze(); + return api.tool.analyze(); case 'KeyR': - return selection.repair(); + return api.tool.repair(); case 'KeyN': - return selection.clean(); + return api.tool.clean(); case 'KeyV': return selection.focus(); case 'KeyW': @@ -245,7 +246,7 @@ function space_init(data) { } ]); - // mouse hover/click handlers + // mouse hover/click handlers. required to enable model drag in space.js space.mouse.downSelect((int, event) => { return event && event.shiftKey ? api.objects() : undefined; }); @@ -256,17 +257,17 @@ function space_init(data) { if (model) { let group = model.group; let { ctrlKey, metaKey, shiftKey } = event; - if (metaKey) { + if (shiftKey && metaKey) { + // find faces adjacent to point/line clicked + model.select(int.point, int.face); + } else if (metaKey) { // set focus on intersected face let { x, y, z } = int.point; + // y,z swap due to world rotation for orbit controls api.focus({center: { x, y:-z, z:y }}); } else if (ctrlKey) { - // lay flat when ctrl clicking a selected face - let q = new Quaternion(); - // find intersecting point, look "up" on Z and rotate to face that - q.setFromUnitVectors(int.face.normal, new Vector3(0,0,-1)); - group.qrotation(q); - group.floor(); + // rotate selected face towawrd z "floor" + group.faceDown(int.face.normal); } else { selection.toggle(shiftKey ? model : model.group); } diff --git a/src/mesh/api.js b/src/mesh/api.js index a733326c..ca352fd2 100644 --- a/src/mesh/api.js +++ b/src/mesh/api.js @@ -39,6 +39,7 @@ let selection = { return selected.length || strict ? selected.slice() : groups.slice(); }, + // return selected groups + groups from selected models groups(strict) { let all = selection.list(strict); let grp = all.filter(s => s instanceof mesh.group); @@ -49,6 +50,7 @@ let selection = { return grp; }, + // return selected models + models from selected groups models(strict) { let all = selection.list(strict); let grp = all.filter(s => s instanceof mesh.group); @@ -125,22 +127,6 @@ let selection = { } }, - analyze() { - tool.analyze(selection.models()); - }, - - repair() { - tool.repair(selection.models()); - }, - - clean() { - tool.clean(selection.models()); - }, - - merge() { - tool.merge(selection.models()); - }, - // update material selections update() { for (let group of groups) { @@ -295,8 +281,15 @@ let file = { }, }; +// return selection if models are invalid. can happen when called from +// even bound to a button which delivers a PointEvent argument +function fallback(models) { + return Array.isArray(models) ? models : selection.models(); +} + let tool = { merge(models) { + models = fallback(models); api.log.emit(`merging ${models.length} models`).pin(); worker.model_merge(models.map(m => { return { id: m.id, matrix: m.matrix } @@ -312,6 +305,7 @@ let tool = { }, analyze(models) { + models = fallback(models); api.log.emit('analyzing mesh(es)...').pin(); let promises = []; let newmdl = []; @@ -337,6 +331,7 @@ let tool = { }, heal(models, opt = {}) { + models = fallback(models); let promises = []; for (let m of models) { let p = worker.model_heal({ @@ -360,6 +355,7 @@ let tool = { }, repair(models) { + models = fallback(models); api.log.emit('repairing mesh(es)...').pin(); tool.heal(models, { merge: true }).then(() => { api.log.emit('repair commplete').pin(); @@ -367,8 +363,8 @@ let tool = { }); }, - clean(models) { - api.log.emit('cleaning mesh(es)...').pin(); + clean(models = selection.models()) { + models = fallback(models); tool.heal(models, { merge: false }).then(() => { api.log.emit('cleaning complete').pin(); api.selection.update(); diff --git a/src/mesh/build.js b/src/mesh/build.js index 1edf9f98..e1a3db65 100644 --- a/src/mesh/build.js +++ b/src/mesh/build.js @@ -228,11 +228,11 @@ function ui_build() { h.button({ _: 'wireframe', onclick() { api.wireframe() } }), h.div({ class: "vsep" }), h.button({ _: 'duplicate', onclick: api.selection.duplicate }), - h.button({ _: 'merge', onclick: api.selection.merge }), + h.button({ _: 'merge', onclick: api.tool.merge }), h.div({ class: "vsep" }), - h.button({ _: 'analyze', onclick: api.selection.analyze }), - h.button({ _: 'repair', onclick: api.selection.repair }), - h.button({ _: 'clean', onclick: api.selection.clean }), + h.button({ _: 'analyze', onclick: api.tool.analyze }), + h.button({ _: 'repair', onclick: api.tool.repair }), + h.button({ _: 'clean', onclick: api.tool.clean }), ]), ]); diff --git a/src/mesh/group.js b/src/mesh/group.js index 396da67c..0a6d27aa 100644 --- a/src/mesh/group.js +++ b/src/mesh/group.js @@ -17,18 +17,19 @@ gapp.register("mesh.group", [ let mesh = self.mesh = self.mesh || {}; if (mesh.group) return; +let { Quaternion, Group, Vector3 } = THREE; let broker = gapp.broker; let call = broker.send; - let space = moto.Space; let worker = moto.client.fn; +let lookUp = new Vector3(0,0,-1); mesh.group = class MeshGroup extends mesh.object { // @param group {MeshModel[]} constructor(models = [], id, name) { super(id); - this.group3 = new THREE.Group(); + this.group3 = new Group(); this.models = []; this.name = name; for (let model of models) { @@ -88,6 +89,13 @@ mesh.group = class MeshGroup extends mesh.object { return this; } + // rotate model so face normal points toward floor + faceDown(normal) { + let q = new Quaternion().setFromUnitVectors(normal, lookUp); + this.qrotation(q); + this.floor(); + } + promote() { return this.centerModels().centerXY().floor(); } diff --git a/src/mesh/model.js b/src/mesh/model.js index 1aebea3f..0ca8d637 100644 --- a/src/mesh/model.js +++ b/src/mesh/model.js @@ -253,6 +253,21 @@ mesh.model = class MeshModel extends mesh.object { mesh.util.defer(this.group.deferUBB); } + // find adjacent faces to clicked point/line on a face + select(point, face) { + let geometry = new THREE.SphereGeometry( 0.5, 16, 16 ); + let material = new MeshPhongMaterial( { color: 0x777777, transparent: true, opacity: 0.25 } ); + let sphere = new Mesh( geometry, material ); + let { x, y, z } = point; + sphere.position.set(x,y,z); + space.scene.add(sphere); + worker.model_locate({ + id: this.id, x, y:-z, z:y, matrix: this.matrix + }).then(data => { + console.log('located', data); + }); + } + }; })(); diff --git a/src/mesh/work.js b/src/mesh/work.js index 2a5d2407..759ce8b1 100644 --- a/src/mesh/work.js +++ b/src/mesh/work.js @@ -33,6 +33,7 @@ function cacheUpdate(id, data) { Object.assign(cache[id], data); } +// translate original mesh vertices into UI world view (PI/2 rotation on X) function translate_encode(id, matrix) { let rec = cache[id]; let geo = rec.geo.clone(); @@ -97,6 +98,26 @@ let model = { return { vertices: tool.unrolled().toFloat32(), }; + }, + + // given model and point, locate matching vertices, lines, and faces + locate(data) { + let { id, x, y, z, matrix } = data; + let arr = translate_encode(id, matrix); + let eps = 2; + for (let i=0, l=arr.length; i