split util from api

This commit is contained in:
Stewart Allen 2022-01-11 20:02:29 -05:00
commit c64d6570a3
5 changed files with 159 additions and 136 deletions

View file

@ -13,6 +13,7 @@ gapp.register("mesh.api", [
"moto.space", // dep: moto.space
"data.index", // dep: data.index
"mesh.tool", // dep: mesh.tool
"mesh.util", // dep: mesh.util
"add.array", // dep: add.array
]);
@ -21,6 +22,8 @@ if (mesh.api) return;
let space = moto.Space;
let worker = moto.client.fn;
let util = mesh.util;
let groups = [];
let selected = [];
@ -427,142 +430,6 @@ let api = mesh.api = {
}
};
let deferFn = [];
let boundsCache = {};
// util functions augmented in build (download)
let util = mesh.util = {
uuid(segs = 1) {
let uid = [];
while (segs-- > 0) {
uid.push(Math.round(Math.random() * 0xffffffff).toString(36));
}
return uid.join('-');
},
// merge repeated function calls like updates
// that importantly take no arguments
defer(fn, time = 50) {
for (let rec of deferFn) {
if (rec.fn === fn) {
clearTimeout(rec.timer);
deferFn.remove(rec);
break;
}
}
let rec = { fn };
rec.timer = setTimeout(() => {
deferFn.remove(rec);
fn();
});
deferFn.push(rec);
},
// return a function wrapper which can be re-used
deferWrap(fn, time) {
return function() {
util.defer(fn, time);
}
},
// @param object {THREE.Object3D | THREE.Object3D[] | MeshObject | MeshObject[]}
// @returns bounds modified for moto.Space
bounds(object) {
let box = new THREE.Box3();
if (Array.isArray(object)) {
for (let o of object) {
util.box3expand(box, o instanceof mesh.object ? o.object : o);
}
} else if (object) {
util.box3expand(box, object instanceof mesh.object ? object.object : object);
} else {
return box;
}
let bnd = {
min: {
x: box.min.x,
y: box.min.z,
z: box.min.y
},
max: {
x: box.max.x,
y: box.max.z,
z: box.max.y
}
};
bnd.size = bnd.dim = {
x: bnd.max.x - bnd.min.x,
y: bnd.max.y - bnd.min.y,
z: bnd.max.z - bnd.min.z
};
bnd.center = bnd.mid = {
x: (bnd.max.x + bnd.min.x) / 2,
y: -(bnd.max.y + bnd.min.y) / 2,
z: (bnd.max.z + bnd.min.z) / 2
};
return bnd;
},
// bounding box workaround adapted from:
// https://discourse.threejs.org/t/bounding-box-bigger-than-concave-object-extrudegeometry/26073/2
// https://discourse.threejs.org/t/invalid-bounds-generated-for-some-orientations/33205
box3expand(box3, object) {
let geometry = object.geometry;
object.updateWorldMatrix(geometry ? true : false, false);
if (geometry) {
let matrix = object.matrixWorld;
let bkey = [matrix.elements.map(v => v.round(5))].join(',')
let cached = boundsCache[object.id];
if (!cached || cached.bkey !== bkey) {
let position = geometry.attributes.position.clone();
position.applyMatrix4(new THREE.Matrix4().extractRotation(matrix));
let bounds = new THREE.Box3().setFromBufferAttribute(position);
// let scale = new THREE.Vector3().setFromMatrixScale(matrix);
// bounds.min.multiply(scale);
// bounds.max.multiply(scale);
cached = boundsCache[object.id] = { bkey, bounds };
}
let bt = cached.bounds.clone();
let m4 = new THREE.Matrix4();
m4.setPosition(new THREE.Vector3().setFromMatrixPosition(object.matrixWorld));
bt.applyMatrix4(m4);
box3.union(bt);
}
let children = object.children;
for (let i = 0, l = children.length; i < l; i++) {
util.box3expand(box3, children[i]);
}
},
extract(object, opt = {}) {
let field = opt.fields || ['x', 'y', 'z'];
let array = [];
for (let k of field) {
let v = object[k] || 0;
if (opt.round !== undefined) v = v.round(opt.round);
if (opt.fixed !== undefined) v = v.toFixed(opt.fixed);
array.push(v);
}
return array;
},
average(array, opt = {}) {
let fields = opt.fields || ['x', 'y', 'z'];
let avg = {};
for (let e of array) {
for (let f of fields) {
avg[f] = (avg[f] || 0) + e[f];
}
}
for (let f of fields) {
avg[f] = (avg[f] || 0) / array.length;
}
return avg;
}
};
let broker = gapp.broker;
// publish messages when api functions are called
broker.wrapObject(selection, 'selection');

View file

@ -6,6 +6,7 @@
gapp.register("mesh.build",[
"moto.broker", // dep: moto.broker
"mesh.util", // dep: mesh.util
"mesh.api", // dep: mesh.api
]);

View file

@ -10,6 +10,7 @@ gapp.register("mesh.model", [
"moto.license", // dep: moto.license
"mesh.object", // dep: mesh.object
"mesh.group", // dep: mesh.group
"mesh.util", // dep: mesh.util
"mesh.api", // dep: mesh.api
]);

View file

@ -8,6 +8,7 @@ gapp.register("mesh.object", [
"add.three", // dep: add.three
"moto.license", // dep: moto.license
"moto.broker", // dep: moto.broker
"mesh.util", // dep: mesh.util
"mesh.api", // dep: mesh.api
]);

153
src/mesh/util.js Normal file
View file

@ -0,0 +1,153 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
"use strict";
(function() {
// dep: ext.three
// dep: ext.three-bgu
gapp.register("mesh.util", [
"moto.license", // dep: moto.license
"add.array", // dep: add.array
]);
let mesh = self.mesh = self.mesh || {};
if (mesh.util) return;
let deferFn = [];
let boundsCache = {};
// util functions augmented in build (download)
let util = mesh.util = {
uuid(segs = 1) {
let uid = [];
while (segs-- > 0) {
uid.push(Math.round(Math.random() * 0xffffffff).toString(36));
}
return uid.join('-');
},
// merge repeated function calls like updates
// that importantly take no arguments
defer(fn, time = 50) {
for (let rec of deferFn) {
if (rec.fn === fn) {
clearTimeout(rec.timer);
deferFn.remove(rec);
break;
}
}
let rec = { fn };
rec.timer = setTimeout(() => {
deferFn.remove(rec);
fn();
});
deferFn.push(rec);
},
// return a function wrapper which can be re-used
deferWrap(fn, time) {
return function() {
util.defer(fn, time);
}
},
// @param object {THREE.Object3D | THREE.Object3D[] | MeshObject | MeshObject[]}
// @returns bounds modified for moto.Space
bounds(object) {
let box = new THREE.Box3();
if (Array.isArray(object)) {
for (let o of object) {
util.box3expand(box, o instanceof mesh.object ? o.object : o);
}
} else if (object) {
util.box3expand(box, object instanceof mesh.object ? object.object : object);
} else {
return box;
}
let bnd = {
min: {
x: box.min.x,
y: box.min.z,
z: box.min.y
},
max: {
x: box.max.x,
y: box.max.z,
z: box.max.y
}
};
bnd.size = bnd.dim = {
x: bnd.max.x - bnd.min.x,
y: bnd.max.y - bnd.min.y,
z: bnd.max.z - bnd.min.z
};
bnd.center = bnd.mid = {
x: (bnd.max.x + bnd.min.x) / 2,
y: -(bnd.max.y + bnd.min.y) / 2,
z: (bnd.max.z + bnd.min.z) / 2
};
return bnd;
},
// bounding box workaround adapted from:
// https://discourse.threejs.org/t/bounding-box-bigger-than-concave-object-extrudegeometry/26073/2
// https://discourse.threejs.org/t/invalid-bounds-generated-for-some-orientations/33205
box3expand(box3, object) {
let geometry = object.geometry;
object.updateWorldMatrix(geometry ? true : false, false);
if (geometry) {
let matrix = object.matrixWorld;
let bkey = [matrix.elements.map(v => v.round(5))].join(',')
let cached = boundsCache[object.id];
if (!cached || cached.bkey !== bkey) {
let position = geometry.attributes.position.clone();
position.applyMatrix4(new THREE.Matrix4().extractRotation(matrix));
let bounds = new THREE.Box3().setFromBufferAttribute(position);
// let scale = new THREE.Vector3().setFromMatrixScale(matrix);
// bounds.min.multiply(scale);
// bounds.max.multiply(scale);
cached = boundsCache[object.id] = { bkey, bounds };
}
let bt = cached.bounds.clone();
let m4 = new THREE.Matrix4();
m4.setPosition(new THREE.Vector3().setFromMatrixPosition(object.matrixWorld));
bt.applyMatrix4(m4);
box3.union(bt);
}
let children = object.children;
for (let i = 0, l = children.length; i < l; i++) {
util.box3expand(box3, children[i]);
}
},
extract(object, opt = {}) {
let field = opt.fields || ['x', 'y', 'z'];
let array = [];
for (let k of field) {
let v = object[k] || 0;
if (opt.round !== undefined) v = v.round(opt.round);
if (opt.fixed !== undefined) v = v.toFixed(opt.fixed);
array.push(v);
}
return array;
},
average(array, opt = {}) {
let fields = opt.fields || ['x', 'y', 'z'];
let avg = {};
for (let e of array) {
for (let f of fields) {
avg[f] = (avg[f] || 0) + e[f];
}
}
for (let f of fields) {
avg[f] = (avg[f] || 0) / array.length;
}
return avg;
}
};
})();