2021-12-29 21:52:24 -05:00
|
|
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
|
|
|
|
|
// dep: ext.three
|
|
|
|
|
// dep: ext.three-bgu
|
2022-01-01 22:39:49 -05:00
|
|
|
gapp.register("mesh.api", [
|
2021-12-29 21:52:24 -05:00
|
|
|
"moto.license", // dep: moto.license
|
|
|
|
|
"moto.client", // dep: moto.client
|
2022-01-01 12:12:57 -05:00
|
|
|
"moto.broker", // dep: moto.broker
|
2021-12-29 21:52:24 -05:00
|
|
|
"moto.space", // dep: moto.space
|
2022-01-10 23:02:34 -05:00
|
|
|
"data.index", // dep: data.index
|
2021-12-29 21:52:24 -05:00
|
|
|
"mesh.tool", // dep: mesh.tool
|
2022-01-11 20:02:29 -05:00
|
|
|
"mesh.util", // dep: mesh.util
|
2021-12-29 21:52:24 -05:00
|
|
|
"add.array", // dep: add.array
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
let mesh = self.mesh = self.mesh || {};
|
|
|
|
|
if (mesh.api) return;
|
|
|
|
|
|
2022-01-12 11:46:10 -05:00
|
|
|
let { Matrix4, Vector3 } = THREE;
|
|
|
|
|
|
2021-12-29 21:52:24 -05:00
|
|
|
let space = moto.Space;
|
2022-01-09 14:43:20 -05:00
|
|
|
let worker = moto.client.fn;
|
2022-01-11 20:02:29 -05:00
|
|
|
let util = mesh.util;
|
|
|
|
|
|
2021-12-30 22:59:46 -05:00
|
|
|
let groups = [];
|
2021-12-31 14:12:42 -05:00
|
|
|
let selected = [];
|
2022-01-12 01:09:34 -05:00
|
|
|
let toggle = {
|
|
|
|
|
wire: false,
|
|
|
|
|
bound: false
|
|
|
|
|
};
|
2021-12-29 21:52:24 -05:00
|
|
|
|
2021-12-31 14:12:42 -05:00
|
|
|
let selection = {
|
2022-01-05 17:58:37 -05:00
|
|
|
// @returns {MeshObject[]} or all groups if not strict and no selection
|
|
|
|
|
list(strict = false) {
|
2022-01-16 00:55:28 -05:00
|
|
|
return selected.length || strict ? selected.slice() : groups.slice();
|
2021-12-31 14:12:42 -05:00
|
|
|
},
|
2021-12-29 21:52:24 -05:00
|
|
|
|
2022-01-12 23:02:08 -05:00
|
|
|
// return selected groups + groups from selected models
|
2022-01-05 17:58:37 -05:00
|
|
|
groups(strict) {
|
|
|
|
|
let all = selection.list(strict);
|
2022-01-01 13:43:35 -05:00
|
|
|
let grp = all.filter(s => s instanceof mesh.group);
|
|
|
|
|
let mdl = all.filter(s => s instanceof mesh.model);
|
|
|
|
|
for (let m of mdl) {
|
|
|
|
|
grp.addOnce(m.group);
|
|
|
|
|
}
|
|
|
|
|
return grp;
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-12 23:02:08 -05:00
|
|
|
// return selected models + models from selected groups
|
2022-01-05 17:58:37 -05:00
|
|
|
models(strict) {
|
|
|
|
|
let all = selection.list(strict);
|
2022-01-01 13:43:35 -05:00
|
|
|
let grp = all.filter(s => s instanceof mesh.group);
|
|
|
|
|
let mdl = all.filter(s => s instanceof mesh.model);
|
|
|
|
|
for (let g of grp) {
|
|
|
|
|
for (let m of g.models) {
|
|
|
|
|
mdl.addOnce(m);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return mdl;
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-02 16:30:05 -05:00
|
|
|
contains(object) {
|
|
|
|
|
return selected.contains(object);
|
|
|
|
|
},
|
|
|
|
|
|
2021-12-31 17:14:55 -05:00
|
|
|
// @param group {MeshObject[]}
|
2021-12-31 14:12:42 -05:00
|
|
|
set(objects) {
|
|
|
|
|
selected = objects;
|
2022-01-02 23:47:28 -05:00
|
|
|
util.defer(selection.update);
|
2021-12-31 14:12:42 -05:00
|
|
|
},
|
|
|
|
|
|
2021-12-31 17:14:55 -05:00
|
|
|
// @param group {MeshObject}
|
2021-12-31 14:12:42 -05:00
|
|
|
add(object) {
|
2022-01-02 19:52:56 -05:00
|
|
|
// pendantic code necessary to minimize re-entrant api calls
|
|
|
|
|
if (object.models) {
|
|
|
|
|
// if group, remove discrete selected members
|
|
|
|
|
for (let m of object.models) {
|
|
|
|
|
if (selected.contains(m)) {
|
|
|
|
|
selection.remove(m);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// if model, remove selcted group
|
|
|
|
|
if (selected.contains(object.group)) {
|
|
|
|
|
selection.remove(object.group);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-31 14:12:42 -05:00
|
|
|
selected.addOnce(object);
|
2022-01-02 23:47:28 -05:00
|
|
|
util.defer(selection.update);
|
2021-12-31 14:12:42 -05:00
|
|
|
},
|
|
|
|
|
|
2021-12-31 17:14:55 -05:00
|
|
|
// @param group {MeshObject}
|
2021-12-31 14:12:42 -05:00
|
|
|
remove(object) {
|
|
|
|
|
selected.remove(object);
|
2022-01-02 23:47:28 -05:00
|
|
|
util.defer(selection.update);
|
2021-12-31 14:12:42 -05:00
|
|
|
},
|
|
|
|
|
|
2022-01-02 19:52:56 -05:00
|
|
|
// @param group {MeshObject}
|
|
|
|
|
toggle(object) {
|
|
|
|
|
if (selected.contains(object)) {
|
|
|
|
|
selection.remove(object);
|
|
|
|
|
} else {
|
|
|
|
|
selection.add(object);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2021-12-31 14:12:42 -05:00
|
|
|
clear() {
|
2022-01-05 21:16:18 -05:00
|
|
|
for (let s of selection.list()) {
|
2022-01-02 23:47:28 -05:00
|
|
|
selection.remove(s);
|
|
|
|
|
}
|
2022-01-13 21:17:35 -05:00
|
|
|
for (let m of api.model.list()) {
|
2022-01-27 12:29:48 -05:00
|
|
|
m.clearSelections();
|
2022-01-13 21:17:35 -05:00
|
|
|
}
|
2021-12-31 17:14:55 -05:00
|
|
|
},
|
|
|
|
|
|
2022-01-12 00:28:33 -05:00
|
|
|
visible() {
|
|
|
|
|
for (let m of selection.models()) {
|
|
|
|
|
m.visible(...arguments);
|
|
|
|
|
}
|
|
|
|
|
util.defer(selection.update);
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-13 11:59:26 -05:00
|
|
|
// update selection and wireframe for all objects
|
2021-12-31 17:14:55 -05:00
|
|
|
update() {
|
|
|
|
|
for (let group of groups) {
|
2022-01-13 11:59:26 -05:00
|
|
|
group.select(false);
|
2022-01-14 14:14:13 -05:00
|
|
|
group.wireframe(prefs.map.space.wire || false);
|
|
|
|
|
group.normals(prefs.map.space.norm || false);
|
2021-12-31 17:14:55 -05:00
|
|
|
}
|
2022-01-02 16:30:05 -05:00
|
|
|
// prevent selection of model and its group
|
|
|
|
|
let mgsel = selected.filter(s => s instanceof mesh.model).map(m => m.group);
|
2022-01-28 19:18:45 -05:00
|
|
|
selected = selected.filter(sel => !mgsel.contains(sel)).filter(v => v);
|
2022-01-13 11:59:26 -05:00
|
|
|
// highlight selected
|
2021-12-31 17:14:55 -05:00
|
|
|
for (let object of selected) {
|
2022-01-13 11:59:26 -05:00
|
|
|
object.select(true);
|
2021-12-31 17:14:55 -05:00
|
|
|
}
|
2022-01-13 11:59:26 -05:00
|
|
|
// update saved selection id list
|
|
|
|
|
prefs.save( prefs.map.space.select = selected.map(s => s.id) );
|
2022-01-20 00:01:48 -05:00
|
|
|
// force repaint in case of idle
|
|
|
|
|
space.update();
|
2022-01-01 12:12:57 -05:00
|
|
|
return selection;
|
2021-12-31 14:12:42 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
move(dx = 0, dy = 0, dz = 0) {
|
2022-01-11 21:52:49 -05:00
|
|
|
for (let s of selection.groups()) {
|
2021-12-31 14:12:42 -05:00
|
|
|
s.move(dx, dy, dz);
|
2021-12-30 22:59:46 -05:00
|
|
|
}
|
2021-12-31 14:12:42 -05:00
|
|
|
return selection;
|
2021-12-30 22:59:46 -05:00
|
|
|
},
|
|
|
|
|
|
2021-12-31 14:12:42 -05:00
|
|
|
rotate(dx = 0, dy = 0, dz = 0) {
|
2022-01-11 21:52:49 -05:00
|
|
|
for (let s of selection.groups()) {
|
2021-12-31 16:12:37 -05:00
|
|
|
s.rotate(dx, dy, dz);
|
2021-12-31 14:12:42 -05:00
|
|
|
}
|
|
|
|
|
return selection;
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-01 10:44:06 -05:00
|
|
|
qrotate(q) {
|
2022-01-11 21:52:49 -05:00
|
|
|
for (let s of selection.groups()) {
|
2022-01-01 10:44:06 -05:00
|
|
|
s.qrotate(q);
|
|
|
|
|
}
|
|
|
|
|
return selection;
|
|
|
|
|
},
|
|
|
|
|
|
2021-12-31 14:12:42 -05:00
|
|
|
scale(dx = 0, dy = 0, dz = 0) {
|
2022-01-11 21:52:49 -05:00
|
|
|
for (let s of selection.groups()) {
|
2021-12-31 14:12:42 -05:00
|
|
|
let { x, y, z } = s.scale();
|
2022-01-10 18:10:57 -05:00
|
|
|
s.scale(x * dx, y * dy, z * dz);
|
2021-12-31 14:12:42 -05:00
|
|
|
}
|
|
|
|
|
return selection;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
floor() {
|
2022-01-11 21:52:49 -05:00
|
|
|
for (let s of selection.groups()) {
|
2021-12-31 14:12:42 -05:00
|
|
|
s.floor(...arguments);
|
|
|
|
|
}
|
|
|
|
|
return selection;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
centerXY() {
|
2022-01-11 21:52:49 -05:00
|
|
|
for (let s of selection.groups()) {
|
2021-12-31 16:59:38 -05:00
|
|
|
s.centerXY(...arguments);
|
2021-12-31 14:12:42 -05:00
|
|
|
}
|
|
|
|
|
return selection;
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-01 13:43:35 -05:00
|
|
|
boundsBox() {
|
|
|
|
|
for (let m of selection.groups()) {
|
|
|
|
|
m.showBounds(...arguments);
|
|
|
|
|
}
|
|
|
|
|
return selection;
|
|
|
|
|
},
|
|
|
|
|
|
2021-12-31 14:12:42 -05:00
|
|
|
home() {
|
|
|
|
|
return selection.centerXY().floor();
|
2021-12-31 16:59:38 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
focus() {
|
2022-01-07 14:39:32 -05:00
|
|
|
api.focus(selection.list());
|
2021-12-31 16:59:38 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
bounds() {
|
2022-01-05 17:58:37 -05:00
|
|
|
return util.bounds(selected.map(s => s.object));
|
2021-12-31 14:12:42 -05:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let group = {
|
|
|
|
|
// @returns {MeshGroup[]}
|
|
|
|
|
list() {
|
|
|
|
|
return groups.slice();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// @param group {MeshModel[]}
|
2022-01-11 21:04:24 -05:00
|
|
|
new(models, id, name) {
|
|
|
|
|
return group.add(new mesh.group(models, id, name));
|
2021-12-31 14:12:42 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// @param group {MeshGroup}
|
2022-01-01 23:48:36 -05:00
|
|
|
add(group) {
|
2021-12-31 14:12:42 -05:00
|
|
|
groups.addOnce(group);
|
2022-01-10 16:56:09 -05:00
|
|
|
space.world.add(group.object);
|
2022-01-13 11:59:26 -05:00
|
|
|
api.selection.update();
|
2021-12-31 14:12:42 -05:00
|
|
|
space.update();
|
|
|
|
|
return group;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// @param group {MeshGroup}
|
2022-01-01 23:48:36 -05:00
|
|
|
remove(group) {
|
2021-12-31 14:12:42 -05:00
|
|
|
groups.remove(group);
|
2022-01-10 16:56:09 -05:00
|
|
|
space.world.remove(group.object);
|
2021-12-31 14:12:42 -05:00
|
|
|
space.update();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-01-01 22:39:49 -05:00
|
|
|
let model = {
|
|
|
|
|
// @returns {MeshModel[]}
|
|
|
|
|
list() {
|
|
|
|
|
return groups.map(g => g.models).flat();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-01-06 20:22:50 -05:00
|
|
|
let file = {
|
|
|
|
|
import() {
|
|
|
|
|
// binding created in mesh.build
|
|
|
|
|
$('import').click();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
export() {
|
2022-01-08 21:00:19 -05:00
|
|
|
let recs = selection.models().map(m => { return {
|
|
|
|
|
id: m.id, matrix: m.matrix, file: m.file
|
|
|
|
|
} });
|
2022-01-08 22:06:50 -05:00
|
|
|
if (recs.length === 0) {
|
|
|
|
|
return api.log.emit(`no models to export`);
|
|
|
|
|
}
|
2022-01-10 21:06:38 -05:00
|
|
|
function doit(ext = 'obj') {
|
2022-01-09 14:43:20 -05:00
|
|
|
api.log.emit(`exporting ${recs.length} model(s)`);
|
|
|
|
|
let file = api.modal.bound.filename.value || "export.obj";
|
2022-01-10 21:06:38 -05:00
|
|
|
if (file.toLowerCase().indexOf(`.${ext}`) < 0) {
|
|
|
|
|
file = `${file}.${ext}`;
|
2022-01-08 22:06:50 -05:00
|
|
|
}
|
2022-01-09 14:43:20 -05:00
|
|
|
worker.file_export({
|
2022-01-10 21:06:38 -05:00
|
|
|
recs, format: ext
|
2022-01-09 14:43:20 -05:00
|
|
|
}).then(data => {
|
|
|
|
|
if (data.length) {
|
|
|
|
|
util.download(data, file);
|
|
|
|
|
}
|
|
|
|
|
}).finally( api.modal.hide );
|
|
|
|
|
}
|
|
|
|
|
api.modal.dialog({
|
|
|
|
|
title: `export ${recs.length} model(s)`,
|
|
|
|
|
body: [ h.div({ class: "export" }, [
|
|
|
|
|
h.div([
|
|
|
|
|
h.div("filename"),
|
|
|
|
|
h.input({ id: "filename", value: "mesh_export" })
|
|
|
|
|
]),
|
|
|
|
|
h.div([
|
2022-01-10 21:06:38 -05:00
|
|
|
h.button({ _: "download OBJ", onclick() { doit('obj') } }),
|
|
|
|
|
h.button({ _: "download STL", onclick() { doit('stl') } })
|
2022-01-09 14:43:20 -05:00
|
|
|
])
|
|
|
|
|
]) ]
|
2022-01-08 21:00:19 -05:00
|
|
|
});
|
2022-01-06 20:22:50 -05:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
2022-01-12 23:02:08 -05:00
|
|
|
// return selection if models are invalid. can happen when called from
|
|
|
|
|
// even bound to a button which delivers a PointEvent argument
|
2022-01-14 23:30:06 -05:00
|
|
|
function fallback(models, strict) {
|
|
|
|
|
return Array.isArray(models) ? models : selection.models(strict);
|
2022-01-12 23:02:08 -05:00
|
|
|
}
|
|
|
|
|
|
2022-01-06 20:22:50 -05:00
|
|
|
let tool = {
|
2022-01-11 16:29:54 -05:00
|
|
|
merge(models) {
|
2022-01-12 23:02:08 -05:00
|
|
|
models = fallback(models);
|
2022-01-20 00:01:48 -05:00
|
|
|
if (models.length <= 1) {
|
|
|
|
|
return api.log.emit('nothing to merge');
|
|
|
|
|
}
|
2022-01-11 16:29:54 -05:00
|
|
|
api.log.emit(`merging ${models.length} models`).pin();
|
|
|
|
|
worker.model_merge(models.map(m => {
|
|
|
|
|
return { id: m.id, matrix: m.matrix }
|
|
|
|
|
}))
|
|
|
|
|
.then(data => {
|
|
|
|
|
let group = api.group.new([new mesh.model({
|
|
|
|
|
file: `merged`,
|
|
|
|
|
mesh: data
|
2022-01-11 22:18:12 -05:00
|
|
|
})]).promote();
|
2022-01-11 16:29:54 -05:00
|
|
|
api.selection.set([group]);
|
|
|
|
|
api.log.emit('merge complete').unpin();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-30 21:56:53 -05:00
|
|
|
union(models) {
|
|
|
|
|
models = fallback(models);
|
|
|
|
|
api.log.emit(`union ${models.length} model(s)`).pin();
|
|
|
|
|
worker.model_union(models.map(m => {
|
|
|
|
|
return { id: m.id, matrix: m.matrix }
|
|
|
|
|
}))
|
|
|
|
|
.then(data => {
|
|
|
|
|
let group = api.group.new([new mesh.model({
|
|
|
|
|
file: `unioned`,
|
|
|
|
|
mesh: data
|
|
|
|
|
})]).promote();
|
|
|
|
|
api.selection.set([group]);
|
|
|
|
|
api.log.emit('union complete').unpin();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-13 17:01:35 -05:00
|
|
|
duplicate() {
|
|
|
|
|
for (let m of selection.models()) {
|
|
|
|
|
m.duplicate();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-20 00:01:48 -05:00
|
|
|
mirror() {
|
|
|
|
|
for (let m of selection.models()) {
|
|
|
|
|
m.mirror();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-14 14:30:07 -05:00
|
|
|
invert() {
|
2022-01-14 16:45:40 -05:00
|
|
|
for (let m of selection.models()) {
|
2022-01-14 14:30:07 -05:00
|
|
|
m.invert(api.mode.get());
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-20 17:40:10 -05:00
|
|
|
rename(models) {
|
|
|
|
|
models = fallback(models, true);
|
|
|
|
|
let model = models[0];
|
|
|
|
|
if (!model) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (models.length > 1) {
|
|
|
|
|
return api.log.emit('rename requires a single selection');
|
|
|
|
|
}
|
|
|
|
|
let onclick = onkeydown = (ev) => {
|
|
|
|
|
if (ev.code && ev.code !== 'Enter') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
model.rename( $('tempedit').value.trim() );
|
|
|
|
|
selection.update();
|
|
|
|
|
api.modal.hide();
|
|
|
|
|
};
|
|
|
|
|
let { tempedit } = api.modal.show(`rename model`, h.div({ class: "rename"}, [
|
|
|
|
|
h.input({ id: "tempedit", value: model.file, onkeydown }),
|
|
|
|
|
h.button({ _: 'ok', onclick })
|
|
|
|
|
]));
|
|
|
|
|
tempedit.setSelectionRange(0,1000);
|
|
|
|
|
tempedit.focus();
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-20 19:21:26 -05:00
|
|
|
regroup(models) {
|
|
|
|
|
models = fallback(models, true);
|
|
|
|
|
if (models.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
api.log.emit(`regrouping ${models.length} model(s)`);
|
|
|
|
|
let bounds = util.bounds(models);
|
2022-01-20 21:53:06 -05:00
|
|
|
let { mid } = bounds;
|
|
|
|
|
Promise.all(models.map(m => m.ungroup())).then(() => {
|
|
|
|
|
mesh.api.group.new(models)
|
|
|
|
|
.centerModels()
|
|
|
|
|
.position(mid.x, mid.y, mid.z)
|
2022-01-29 20:37:45 -05:00
|
|
|
.setSelected();
|
2022-01-20 21:53:06 -05:00
|
|
|
});
|
2022-01-20 19:21:26 -05:00
|
|
|
},
|
|
|
|
|
|
2022-01-18 13:41:11 -05:00
|
|
|
analyze(models, opt = { compound: true }) {
|
2022-01-12 23:02:08 -05:00
|
|
|
models = fallback(models);
|
2022-01-11 14:41:49 -05:00
|
|
|
api.log.emit('analyzing mesh(es)...').pin();
|
2022-01-11 21:04:24 -05:00
|
|
|
let promises = [];
|
|
|
|
|
let newmdl = [];
|
2022-01-12 11:46:10 -05:00
|
|
|
let mcore = new Matrix4().makeRotationX(Math.PI / 2);
|
2022-01-11 18:41:49 -05:00
|
|
|
for (let m of models) {
|
2022-01-12 09:32:13 -05:00
|
|
|
// todo - translate vertices with source model's matrix
|
2022-01-16 16:20:42 -05:00
|
|
|
let p = worker.model_analyze({ id: m.id, opt }).then(data => {
|
2022-01-11 21:04:24 -05:00
|
|
|
let { areas, edges } = data.mapped;
|
|
|
|
|
let nm = areas.map(area => new mesh.model({
|
|
|
|
|
file: (area.length/3).toString(),
|
|
|
|
|
mesh: area.toFloat32()
|
2022-01-13 21:17:35 -05:00
|
|
|
})).map( nm => nm.applyMatrix4(mcore.clone().multiply(m.mesh.matrixWorld)) );
|
2022-01-11 21:04:24 -05:00
|
|
|
newmdl.appendAll(nm);
|
2022-01-11 14:41:49 -05:00
|
|
|
});
|
2022-01-11 16:29:54 -05:00
|
|
|
promises.push(p);
|
2022-01-11 14:41:49 -05:00
|
|
|
}
|
2022-01-11 16:29:54 -05:00
|
|
|
Promise.all(promises).then(() => {
|
2022-01-11 21:52:49 -05:00
|
|
|
if (newmdl.length) {
|
2022-01-29 20:37:45 -05:00
|
|
|
mesh.api.group.new(newmdl, undefined, "patch").setSelected();
|
2022-01-11 21:52:49 -05:00
|
|
|
}
|
2022-01-11 16:29:54 -05:00
|
|
|
api.log.emit('analysis complete').unpin();
|
|
|
|
|
});
|
2022-01-06 20:22:50 -05:00
|
|
|
},
|
|
|
|
|
|
2022-01-11 22:42:43 -05:00
|
|
|
heal(models, opt = {}) {
|
2022-01-12 23:02:08 -05:00
|
|
|
models = fallback(models);
|
2022-01-11 21:04:24 -05:00
|
|
|
let promises = [];
|
2022-01-11 18:41:49 -05:00
|
|
|
for (let m of models) {
|
2022-01-11 22:42:43 -05:00
|
|
|
let p = worker.model_heal({
|
|
|
|
|
id: m.id, opt
|
|
|
|
|
}).then(data => {
|
2022-01-06 23:51:35 -05:00
|
|
|
if (data) {
|
|
|
|
|
m.reload(
|
|
|
|
|
data.vertices,
|
|
|
|
|
data.indices,
|
|
|
|
|
data.normals
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
2022-01-11 16:29:54 -05:00
|
|
|
promises.push(p);
|
2022-01-06 20:22:50 -05:00
|
|
|
}
|
2022-01-11 22:42:43 -05:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
Promise.all(promises).then(() => {
|
|
|
|
|
resolve();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
repair(models) {
|
2022-01-12 23:02:08 -05:00
|
|
|
models = fallback(models);
|
2022-01-11 22:42:43 -05:00
|
|
|
api.log.emit('repairing mesh(es)...').pin();
|
|
|
|
|
tool.heal(models, { merge: true }).then(() => {
|
2022-01-13 23:16:30 -05:00
|
|
|
api.log.emit('repair commplete').unpin();
|
2022-01-12 10:52:24 -05:00
|
|
|
api.selection.update();
|
2022-01-11 22:42:43 -05:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-26 21:06:30 -05:00
|
|
|
clean(models) {
|
2022-01-12 23:02:08 -05:00
|
|
|
models = fallback(models);
|
2022-01-11 22:42:43 -05:00
|
|
|
tool.heal(models, { merge: false }).then(() => {
|
2022-01-13 23:16:30 -05:00
|
|
|
api.log.emit('cleaning complete').unpin();
|
2022-01-12 10:52:24 -05:00
|
|
|
api.selection.update();
|
2022-01-11 16:29:54 -05:00
|
|
|
});
|
2022-01-26 21:06:30 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
rebuild(models) {
|
|
|
|
|
Promise.all(fallback(models).map(m => m.rebuild()));
|
2022-01-06 20:22:50 -05:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-01-13 17:01:35 -05:00
|
|
|
let modes = { object: "object", face: "face", line: "line", vertex: "vertex" };
|
|
|
|
|
|
|
|
|
|
// edit mode
|
|
|
|
|
let mode = {
|
|
|
|
|
set(mode) {
|
|
|
|
|
prefs.save(prefs.map.mode = mode);
|
|
|
|
|
for (let key of Object.values(modes)) {
|
|
|
|
|
$(`mode-${key}`).classList.remove('selected');
|
|
|
|
|
}
|
|
|
|
|
$(`mode-${mode}`).classList.add('selected');
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
get() {
|
|
|
|
|
return prefs.map.mode;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
object() {
|
|
|
|
|
mode.set(modes.object);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
face() {
|
|
|
|
|
mode.set(modes.face);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
line() {
|
|
|
|
|
mode.set(modes.line);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
vertex() {
|
|
|
|
|
mode.set(modes.vertex);
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
2022-01-18 16:34:42 -05:00
|
|
|
// cache pref signature so we know when it changes
|
|
|
|
|
let prefsig;
|
|
|
|
|
let prefsignew;
|
|
|
|
|
|
2022-01-10 23:02:34 -05:00
|
|
|
// persisted preference map
|
|
|
|
|
let prefs = {
|
2022-01-11 12:29:15 -05:00
|
|
|
map: {
|
2022-01-20 17:46:39 -05:00
|
|
|
info: {
|
|
|
|
|
group: "show",
|
|
|
|
|
span: "show"
|
|
|
|
|
},
|
2022-01-13 17:01:35 -05:00
|
|
|
mode: modes.object,
|
2022-01-20 17:01:56 -05:00
|
|
|
edit: {
|
|
|
|
|
scale_group_X: true,
|
|
|
|
|
scale_group_Y: true,
|
|
|
|
|
scale_group_Z: true
|
|
|
|
|
},
|
2022-01-11 12:29:15 -05:00
|
|
|
space: {
|
2022-01-20 23:29:35 -05:00
|
|
|
center: true,
|
|
|
|
|
floor: true,
|
2022-01-13 11:59:26 -05:00
|
|
|
wire: false,
|
2022-01-11 12:29:15 -05:00
|
|
|
grid: true,
|
2022-01-13 11:59:26 -05:00
|
|
|
dark: false,
|
|
|
|
|
select: []
|
2022-01-11 12:29:15 -05:00
|
|
|
},
|
|
|
|
|
},
|
2022-01-10 23:02:34 -05:00
|
|
|
|
2022-01-18 16:34:42 -05:00
|
|
|
// look for changes in pref signature
|
|
|
|
|
changed() {
|
|
|
|
|
prefsignew = JSON.stringify(prefs.map);
|
|
|
|
|
return prefsignew !== prefsig;
|
2022-01-10 23:02:34 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// persist to data store
|
2022-01-11 12:29:15 -05:00
|
|
|
save() {
|
2022-01-18 16:34:42 -05:00
|
|
|
if (prefs.changed()) {
|
|
|
|
|
mesh.db.admin.put("prefs", prefs.map);
|
|
|
|
|
prefsig = prefsignew;
|
|
|
|
|
}
|
2022-01-10 23:02:34 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// reload from data store
|
2022-01-11 12:29:15 -05:00
|
|
|
load() {
|
|
|
|
|
return mesh.db.admin.get("prefs").then(data => {
|
|
|
|
|
// handle/ignore incompatible older prefs
|
|
|
|
|
if (data) Object.assign(prefs.map, data);
|
2022-01-10 23:02:34 -05:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-01-08 21:00:19 -05:00
|
|
|
// api is augmented in mesh.build (log, modal, download)
|
2021-12-31 14:12:42 -05:00
|
|
|
let api = mesh.api = {
|
2022-01-09 10:13:09 -05:00
|
|
|
help() {
|
|
|
|
|
window.open("https://docs.grid.space/projects/mesh-tool");
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-01 23:48:36 -05:00
|
|
|
clear() {
|
2021-12-31 14:12:42 -05:00
|
|
|
for (let group of group.list()) {
|
|
|
|
|
group.remove(group);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-03 00:43:35 -05:00
|
|
|
// @param object {THREE.Object3D | THREE.Object3D[] | Point}
|
2022-01-01 23:48:36 -05:00
|
|
|
focus(object) {
|
2022-01-03 00:43:35 -05:00
|
|
|
let { center } = object.center ? object : util.bounds(object);
|
2021-12-31 17:20:41 -05:00
|
|
|
// when no valid objects supplied, set origin
|
2021-12-31 16:59:38 -05:00
|
|
|
if (isNaN(center.x * center.y * center.z)) {
|
2021-12-31 17:20:41 -05:00
|
|
|
center = { x: 0, y: 0, z: 0 };
|
2021-12-31 16:59:38 -05:00
|
|
|
}
|
2022-01-21 02:35:58 -05:00
|
|
|
let { normal } = object;
|
|
|
|
|
let left, up;
|
|
|
|
|
if (normal) {
|
|
|
|
|
let { x, y, z } = normal;
|
|
|
|
|
left = new Vector3(x,y,0).angleTo(new Vector3(0,-1,0));
|
|
|
|
|
up = new Vector3(0,y,z).angleTo(new Vector3(0,0,1));
|
|
|
|
|
if (x < 0) left = -left;
|
|
|
|
|
}
|
2021-12-31 17:20:41 -05:00
|
|
|
// sets "home" views (front, back, home, reset)
|
2022-01-01 13:43:35 -05:00
|
|
|
space.platform.setCenter(center.x, -center.y, center.z);
|
2021-12-31 17:20:41 -05:00
|
|
|
// sets camera focus
|
2022-01-21 02:35:58 -05:00
|
|
|
space.view.panTo(center.x, center.z, -center.y, left, up);
|
|
|
|
|
// space.view.setFocus(new Vector3(
|
|
|
|
|
// center.x, center.z, -center.y
|
|
|
|
|
// ));
|
2021-12-31 16:59:38 -05:00
|
|
|
},
|
|
|
|
|
|
2022-01-07 21:32:53 -05:00
|
|
|
grid(state = {toggle:true}) {
|
2022-01-11 12:29:15 -05:00
|
|
|
let { platform } = space;
|
|
|
|
|
let { map, save } = prefs;
|
2022-01-07 21:32:53 -05:00
|
|
|
if (state.toggle) {
|
2022-01-11 12:29:15 -05:00
|
|
|
platform.showGrid(!platform.isGridVisible());
|
2022-01-07 21:32:53 -05:00
|
|
|
} else {
|
2022-01-11 12:29:15 -05:00
|
|
|
platform.showGrid(state);
|
2022-01-07 21:32:53 -05:00
|
|
|
}
|
2022-01-11 12:29:15 -05:00
|
|
|
map.space.grid = platform.isGridVisible();
|
|
|
|
|
save();
|
2022-01-07 21:32:53 -05:00
|
|
|
},
|
|
|
|
|
|
2022-01-13 11:59:26 -05:00
|
|
|
wireframe(state = {toggle:true}, opt = { }) {
|
|
|
|
|
let wire = prefs.map.space.wire;
|
|
|
|
|
if (state.toggle) {
|
|
|
|
|
wire = !wire;
|
|
|
|
|
} else {
|
|
|
|
|
wire = state;
|
|
|
|
|
}
|
2022-01-14 14:14:13 -05:00
|
|
|
for (let m of model.list()) {
|
2022-01-13 11:59:26 -05:00
|
|
|
m.wireframe(wire, opt);
|
2022-01-06 09:38:04 -05:00
|
|
|
}
|
2022-01-13 11:59:26 -05:00
|
|
|
prefs.save( prefs.map.space.wire = wire );
|
2022-01-06 09:38:04 -05:00
|
|
|
},
|
|
|
|
|
|
2022-01-14 14:14:13 -05:00
|
|
|
normals(state = {toggle:true}) {
|
|
|
|
|
let norm = prefs.map.space.norm;
|
|
|
|
|
if (state.toggle) {
|
|
|
|
|
norm = !norm;
|
|
|
|
|
} else {
|
|
|
|
|
norm = state;
|
|
|
|
|
}
|
|
|
|
|
for (let m of model.list()) {
|
|
|
|
|
m.normals(norm);
|
|
|
|
|
}
|
|
|
|
|
prefs.save( prefs.map.space.norm = norm );
|
|
|
|
|
},
|
|
|
|
|
|
2022-01-13 17:01:35 -05:00
|
|
|
mode,
|
|
|
|
|
|
2022-01-13 20:23:48 -05:00
|
|
|
modes,
|
|
|
|
|
|
2021-12-31 14:12:42 -05:00
|
|
|
selection,
|
|
|
|
|
|
|
|
|
|
group,
|
|
|
|
|
|
2022-01-01 22:39:49 -05:00
|
|
|
model,
|
2022-01-01 00:04:35 -05:00
|
|
|
|
2022-01-06 20:22:50 -05:00
|
|
|
file,
|
|
|
|
|
|
|
|
|
|
tool,
|
2022-01-06 17:28:57 -05:00
|
|
|
|
2022-01-10 23:02:34 -05:00
|
|
|
prefs,
|
|
|
|
|
|
2022-01-01 23:48:36 -05:00
|
|
|
objects() {
|
2022-01-02 14:25:28 -05:00
|
|
|
// return model objects suitable for finding ray intersections
|
2022-01-10 16:56:09 -05:00
|
|
|
return group.list().map(o => o.models).flat().map(o => o.mesh);
|
2021-12-29 21:52:24 -05:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-01-01 22:39:49 -05:00
|
|
|
let broker = gapp.broker;
|
2022-01-01 23:48:36 -05:00
|
|
|
// publish messages when api functions are called
|
|
|
|
|
broker.wrapObject(selection, 'selection');
|
|
|
|
|
broker.wrapObject(model, 'model');
|
|
|
|
|
broker.wrapObject(group, 'group');
|
2022-01-01 22:39:49 -05:00
|
|
|
|
2022-01-10 23:02:34 -05:00
|
|
|
// optimize db writes by merging updates
|
2022-01-18 16:34:42 -05:00
|
|
|
prefs.save = util.deferWrap(prefs.save, 100);
|
2022-01-10 23:02:34 -05:00
|
|
|
|
2021-12-29 21:52:24 -05:00
|
|
|
})();
|