begin vertex / line / face selector
This commit is contained in:
parent
03bbb22263
commit
dd38b0f128
7 changed files with 77 additions and 48 deletions
|
|
@ -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) });
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 }),
|
||||
]),
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue