begin vertex / line / face selector

This commit is contained in:
Stewart Allen 2022-01-12 23:02:08 -05:00
commit dd38b0f128
7 changed files with 77 additions and 48 deletions

View file

@ -3344,23 +3344,11 @@
let url = WIN.URL.createObjectURL(new Blob([data], {type: "octet/stream"}));
$('mod-any').innerHTML = `<a id="_dexport_" href="${url}" download="${filename}">x</a>`;
$('_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) });

View file

@ -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);
}

View file

@ -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();

View file

@ -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 }),
]),
]);

View file

@ -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();
}

View file

@ -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);
});
}
};
})();

View file

@ -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<l; ) {
let face = (i/9) | 0;
let ax = arr[i++];
let ay = arr[i++];
let az = arr[i++];
let dx = Math.abs(ax - x);
let dy = Math.abs(ay - y);
let dz = Math.abs(az - z);
if (dz < eps && dy < eps && dz < eps) {
console.log(`match @ ${face}`, ax, ay, az);
}
}
return {};
}
};