refactor widget as class. patch engine, frame, cli

This commit is contained in:
Stewart Allen 2022-02-18 12:08:08 -05:00
commit 74d3d8752c
9 changed files with 300 additions and 306 deletions

4
app.js
View file

@ -404,6 +404,7 @@ const script = {
"geo/mesh",
// "moto/broker",
"kiri/pack",
"kiri/utils",
"kiri/slice",
"kiri/layers",
"kiri-mode/fdm/fill",
@ -453,6 +454,7 @@ const script = {
"geo/slicer",
"kiri-mode/fdm/driver",
"kiri-mode/fdm/slice",
"kiri/utils",
"kiri/slice",
"kiri/layers",
"kiri/widget",
@ -472,6 +474,7 @@ const script = {
"geo/base",
"geo/point",
"geo/points",
"geo/paths",
"geo/slope",
"geo/line",
"geo/bounds",
@ -481,6 +484,7 @@ const script = {
"load/obj",
"load/stl",
"kiri/conf",
"kiri/utils",
"kiri/client",
"kiri/slice",
"kiri/layers",

View file

@ -1,6 +1,7 @@
[
"main/gapp",
"moto/license",
"moto/broker",
"main/kiri",
"ext/three",
"ext/pngjs",
@ -13,18 +14,22 @@
"geo/base",
"geo/point",
"geo/points",
"geo/paths",
"geo/slope",
"geo/line",
"geo/bounds",
"geo/polygons",
"geo/polygon",
"geo/gyroid",
"geo/slicer",
"load/obj",
"load/stl",
"kiri/conf",
"kiri/pack",
"kiri/client",
"kiri/engine",
"kiri/utils",
"kiri/slice",
"kiri/slicer",
"kiri/layers",
"kiri-mode/fdm/fill",
"kiri-mode/fdm/driver",
@ -48,5 +53,4 @@
"kiri/print",
"kiri/codec",
"kiri/worker",
"load/stl"
]

View file

@ -2,7 +2,7 @@
"use strict";
kiri.load(function() {
self.kiri.load(function() {
let KIRI = self.kiri,
CAM = KIRI.driver.CAM,

View file

@ -4,7 +4,7 @@
(function() {
const { base, kiri } = self;
const { base, kiri, noop } = self;
const { driver, fill, fill_fixed, newSlice, utils } = kiri;
const { config, polygons, util, newPoint } = base;
const { fillArea } = polygons;

View file

@ -56,9 +56,8 @@ let exports_save = exports,
gapp = {},
geo = {},
self = this.self = {
THREE,
gapp,
kiri: { driver: {}, loader: [] },
THREE,
location: { hostname: 'local', port: 0, protocol: 'fake' },
postMessage: (msg) => {
self.kiri.client.onmessage({data:msg});

View file

@ -399,7 +399,7 @@
}
}
if (data.load) {
platformLoad(data.load, (verts, widget) => {
api.platform.load(data.load, (verts, widget) => {
send({event: "loaded", data: [ widget.id ]});
})
};
@ -2044,7 +2044,7 @@
function loadFile() {
$('load-file').onchange = function(event) {
console.log(event);
platformLoadFiles(event.target.files);
api.platform.load_files(event.target.files);
};
$('load-file').click();
// alert2("drag/drop STL files onto platform to import\nreload page to return to last saved state");

View file

@ -793,7 +793,7 @@ const platform = api.platform = {
show_axes: space.platform.showAxes,
show_volume: space.platform.showVolume,
top_z() { return topZ },
clear() { clearWorkspace(); saveWorkspace(true) }
clear() { api.space.clear(); api.space.save(true) }
};
})();

View file

@ -46,6 +46,26 @@ function js2o(s,def) {
}
}
// split 24 bit color into [ r, g, b ]
function rgb(c) {
return [ (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff ];
}
// return 24 bit color from [ r, g, b ] array
function a2c(a) {
return (a[0] << 16) | (a[1] << 8) | a[0];
}
// average two color arrays with weighting @ return 24 bit color
function avgc(c1, c2, w = 3) {
let r1 = rgb(c1);
let r2 = rgb(c2);
let d = (w + 2);
return ((r1[0] * w + r2[0]) / d) << 16
| ((r1[1] * w + r2[1]) / d) << 8
| ((r1[2] * w + r2[2]) / d);
}
function areEqual(o1, o2) {
if (o1 == o2) return true;
if (Array.isArray(o1) && Array.isArray(o2)) {
@ -76,6 +96,9 @@ kiri.utils = {
ajax,
o2js,
js2o,
avgc,
rgb,
a2c
};
})();

View file

@ -4,145 +4,29 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
(function() {
if (self.kiri.Widget) return;
if (self.kiri.Widget) return;
const { base, kiri } = self;
const { driver } = kiri;
const { config, util, polygons } = base;
const { Mesh, newPoint, verticesToPoints } = base;
const { inRange, time } = util;
const { base, kiri } = self;
const { api, driver, utils } = kiri;
const { config, util, polygons } = base;
const { Mesh, newPoint, verticesToPoints } = base;
const { inRange, time } = util;
const { rgb, avgc } = utils;
const POLY = polygons;
const PRO = Widget.prototype;
const solid_opacity = 1.0;
const POLY = polygons;
const solid_opacity = 1.0;
const groups = [];
let nextId = 0,
groups = [];
let nextId = 0;
kiri.Widget = Widget;
kiri.newWidget = newWidget;
function newWidget(id,group) { return new Widget(id,group) }
function newWidget(id,group) { return new Widget(id,group) }
function catalog() { return kiri.catalog }
function catalog() { return kiri.catalog }
function index() { return catalog().index }
function index() { return catalog().index }
/** ******************************************************************
* Group helpers
******************************************************************* */
const Group = Widget.Groups = {
list: () => {
return groups.slice()
},
merge: (widgets) => {
let grps = widgets.map(w => w.group).uniq();
if (grps.length > 1) {
let root = grps.shift();
let rpos = root[0].track.pos;
for (let grp of grps) {
for (let w of grp) {
let wpos = w.track.pos;
w.group = root;
w.moveMesh(rpos.x - wpos.x, rpos.y - wpos.y, rpos.z - wpos.z);
w._move(rpos.x, rpos.y, rpos.z, true);
root.push(w);
}
groups.splice(groups.indexOf(grp),1);
}
}
},
split: (widgets) => {
for (let group of widgets.map(w => w.group).uniq()) {
groups.splice(groups.indexOf(group),1);
for (let widget of group) {
let nugroup = Group.forid(widget.id);
nugroup.push(widget);
widget.group = nugroup;
}
}
},
forid: function(id) {
for (let i=0; i<groups.length; i++) {
if (groups[i].id === id) return groups[i];
}
let group = [];
group.id = id;
groups.push(group);
return group;
},
remove: function(widget) {
groups.slice().forEach(group => {
let pos = group.indexOf(widget);
if (pos >= 0) {
group.splice(pos,1);
}
if (group.length === 0) {
pos = groups.indexOf(group);
groups.splice(pos,1);
}
});
},
blocks: function() {
return groups.map(group => {
return {
w: group[0].track.box.w,
h: group[0].track.box.h,
move: (x,y,z,abs) => {
group.forEach(widget => {
widget.mesh.material.visible = true;
widget._move(x, y, z, abs);
});
}
};
});
},
loadDone: function() {
groups.forEach(group => {
if (!group.centered) {
group[0].center();
group.centered = true;
}
});
},
bounds: function(group) {
let bounds = null;
group.forEach(widget => {
let wb = widget.mesh.getBoundingBox(true);
if (bounds) {
bounds = bounds.union(wb);
} else {
bounds = wb;
}
});
return bounds;
},
clear: function() {
groups.length = 0;
}
};
// class Widget {
// constructor(id, group) {
//
// }
// }
/**
* @params {String} [id]
* @constructor
*/
function Widget(id, group) {
class Widget {
constructor(id, group) {
this.id = id || new Date().getTime().toString(36)+(nextId++);
this.grouped = group ? true : false;
this.group = group || [];
@ -209,69 +93,24 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
this.support = false;
}
/** ******************************************************************
* Widget Class Functions
******************************************************************* */
Widget.loadFromCatalog = function(filename, ondone) {
catalog().getFile(filename, function(data) {
let widget = newWidget().loadVertices(data);
widget.meta.file = filename;
ondone(widget);
});
};
Widget.loadFromState = function(id, ondone, move) {
index().get('ws-save-'+id, function(data) {
if (data) {
let vertices = data.geo || data,
indices = data.ind || undefined,
track = data.track || undefined,
group = data.group || id,
anno = data.anno || undefined,
widget = newWidget(id, Group.forid(group)),
meta = data.meta || widget.meta,
ptr = widget.loadVertices({vertices, indices});
widget.meta = meta;
widget.anno = anno || widget.anno;
// restore widget position if specified
if (move && track && track.pos) {
widget.track = track;
widget.move(track.pos.x, track.pos.y, track.pos.z, true);
}
ondone(ptr);
} else {
ondone(null);
}
});
};
Widget.deleteFromState = function(id,ondone) {
index().remove('ws-save-'+id, ondone);
};
/** ******************************************************************
* Widget Prototype Functions
******************************************************************* */
PRO.saveToCatalog = function(filename) {
saveToCatalog(filename) {
if (this.grouped) {
return this;
}
let widget = this;
let time = time();
const widget = this;
const mark = time();
widget.meta.file = filename;
widget.meta.save = time;
catalog().putFile(filename, this.getGeoVertices(true), function(vertices) {
widget.meta.save = mark;
catalog().putFile(filename, this.getGeoVertices(true), vertices => {
if (vertices && vertices.length) {
console.log("saving decimated mesh ["+vertices.length+"] time ["+(time()-time)+"]");
console.log("saving decimated mesh ["+vertices.length+"] time ["+(time()-mark)+"]");
widget.loadVertices(vertices);
}
});
return this;
};
}
PRO.saveState = function(ondone) {
saveState(ondone) {
if (!ondone) {
clearTimeout(this._save_timer);
this._save_timer = setTimeout(() => {
@ -280,7 +119,7 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
}, 1500);
return;
}
let widget = this;
const widget = this;
index().put('ws-save-'+this.id, {
geo: widget.getGeoVertices(false),
ind: widget.getGeoIndices(),
@ -288,13 +127,13 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
group: this.group.id,
meta: this.meta,
anno: this.annotations()
}, function(result) {
}, result => {
widget.meta.saved = time();
if (ondone) ondone();
});
};
}
PRO.annotations = function() {
annotations() {
let o = Object.clone(this.anno);
if (o.support) {
// clear out THREE.Box temps
@ -303,14 +142,14 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
}
}
return o;
};
}
/**
*
* @param {Float32Array} vertices
* @returns {Widget}
*/
PRO.loadVertices = function(data, options = { index: false }) {
loadVertices(data, options = { index: false }) {
let vertices,
indices,
autoscale = false;
@ -369,19 +208,21 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
this.points = null;
return this.loadGeometry(geo);
}
};
}
PRO.loadData = PRO.loadVertices;
loadData() {
return this.loadVertices(...arguments);
}
PRO.setModified = function() {
setModified() {
this.modified = true;
if (this.mesh && this.mesh.geometry) {
// this fixes ray intersections after the mesh is modified
this.mesh.geometry.boundingSphere = null;
}
};
}
PRO.indexGeo = function() {
indexGeo() {
let indices = this.getGeoIndices();
if (!indices) {
let mesh = new Mesh({vertices: this.getGeoVertices()});
@ -389,9 +230,9 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
indices = Uint32Array.from(mesh.faces.map(v => v/3));
this.loadData({vertices, indices});
}
};
}
PRO.heal = function(debug, refresh) {
heal(debug, refresh) {
if (debug) {
let mesh = this.debugMesh().heal();
let verts = mesh.vertices;
@ -437,22 +278,22 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
resolve(this.modified);
}, refresh);
});
};
}
PRO.debugMesh = function(precision) {
debugMesh(precision) {
return new base.Mesh({precision, vertices: this.getVertices().array});
};
}
PRO.getVertices = function() {
getVertices() {
return this.mesh.geometry.attributes.position;
};
}
/**
* @param {THREE.Geometry} geometry
* @returns {Widget}
*/
PRO.loadGeometry = function(geometry) {
let mesh = new THREE.Mesh(
loadGeometry(geometry) {
const mesh = new THREE.Mesh(
geometry,
new THREE.MeshPhongMaterial({
color: 0xffff00,
@ -474,25 +315,25 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
// invalidates points cache (like any scale/rotation)
this.center(true);
return this;
};
}
PRO.groupBounds = function() {
groupBounds() {
return Group.bounds(this.group);
};
}
/**
* @param {Point[]} points
* @returns {Widget}
*/
PRO.setPoints = function(points) {
setPoints(points) {
this.points = points || null;
return this;
};
}
/**
* remove slice data and their views
*/
PRO.clearSlices = function() {
clearSlices() {
let slices = this.slices,
mesh = this.mesh;
if (slices && mesh && mesh.remove) {
@ -501,25 +342,12 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
});
}
this.slices = null;
};
function rgb(c) {
return [ c >> 16, (c >> 8) & 0xff , c & 0xff ];
}
function avgc(c1, c2, w) {
let r1 = rgb(c1);
let r2 = rgb(c2);
let d = (w + 2);
return ((r1[0] * w + r2[0]) / d) << 16
| ((r1[1] * w + r2[1]) / d) << 8
| ((r1[2] * w + r2[2]) / d);
}
/**
* @param {number} color
*/
PRO.setColor = function(color, settings, save = true) {
setColor(color, settings, save = true) {
if (settings) {
console.trace('legacy call with settings');
}
@ -531,17 +359,17 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
}
let material = this.mesh.material;
material.color.set(this.meta.disabled ? avgc(0x888888, color, 3) : color);
};
}
PRO.getColor = function() {
getColor() {
return this.color;
};
}
/**
* @param {number} value
*/
PRO.setOpacity = function(value) {
let mesh = this.mesh;
setOpacity(value) {
const mesh = this.mesh;
if (value <= 0.0) {
mesh.material.transparent = solid_opacity < 1.0;
mesh.material.opacity = solid_opacity;
@ -551,12 +379,12 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
mesh.material.opacity = value;
mesh.material.visible = true;
}
};
}
/**
* center geometry bottom (on platform) at 0,0,0
*/
PRO.center = function(init) {
center(init) {
let bb = init ? this.mesh.getBoundingBox(true) : this.groupBounds(),
bm = bb.min.clone(),
bM = bb.max.clone(),
@ -572,13 +400,13 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
});
}
return this;
};
}
/**
* called by center() and Group.center()
* todo use new prototype.moveMesh()
*/
PRO.moveMesh = function(x, y, z) {
moveMesh(x, y, z) {
let gap = this.mesh.geometry.attributes.position,
pa = gap.array;
// center point array on 0,0,0
@ -599,14 +427,14 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
// invalidate cached points
this.points = null;
this.setModified();
};
}
/**
* moves top of widget to given Z
*
* @param {number} z position
*/
PRO.setTopZ = function(z) {
setTopZ(z) {
let mesh = this.mesh,
pos = this.track.pos,
ltz = this.last_top_z || {},
@ -626,20 +454,20 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
};
this.modified |= (ltz.pz !== ntz.pz || ltz.mpz !== ntz.mpz);
this.last_top_z = ntz;
};
}
PRO.cutZ = function(dist) {
cutZ(dist) {
this.track.zcut = dist;
this.setTopZ();
};
}
PRO.move = function(x, y, z, abs) {
move(x, y, z, abs) {
this.group.forEach(w => {
w._move(x, y, z, abs);
});
};
}
PRO._move = function(x, y, z, abs) {
_move(x, y, z, abs) {
let mesh = this.mesh,
pos = this.track.pos,
zcut = this.track.zcut || 0;
@ -660,18 +488,18 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
}
if (x || y || z) {
this.setModified();
kiri.api.event.emit('widget.move', {widget: this, pos});
api.event.emit('widget.move', {widget: this, pos});
}
};
}
PRO.scale = function(x, y, z) {
scale(x, y, z) {
this.group.forEach(w => {
w._scale(x, y, z);
});
this.center();
};
}
PRO._scale = function(x, y, z) {
_scale(x, y, z) {
let mesh = this.mesh,
scale = this.track.scale;
this.bounds = null;
@ -682,22 +510,22 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
scale.y *= (y || 1.0);
scale.z *= (z || 1.0);
this.setModified();
};
}
PRO.rotate = function(x, y, z, temp) {
rotate(x, y, z, temp) {
this.group.forEach(w => {
w._rotate(x, y, z, temp);
});
this.center(false);
};
}
PRO.rotateRaw = function(x, y, z, temp) {
rotateRaw(x, y, z, temp) {
this.group.forEach(w => {
w._rotate(x, y, z, temp);
});
};
}
PRO._rotate = function(x, y, z, temp) {
_rotate(x, y, z, temp) {
if (!temp) {
this.bounds = null;
this.setWireframe(false);
@ -719,25 +547,25 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
rot.z += (z || 0);
}
this.setModified();
};
}
PRO.unrotate = function() {
unrotate() {
this.roto.reverse().forEach(m => {
this.mesh.geometry.applyMatrix4(m.clone().invert());
});
this.roto = [];
this.center();
this.setModified();
};
}
PRO.mirror = function() {
mirror() {
this.group.forEach(w => {
w._mirror();
});
this.center();
};
}
PRO._mirror = function() {
_mirror() {
this.clearSlices();
this.setWireframe(false);
let geo = this.mesh.geometry, ot = this.track;
@ -748,7 +576,7 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
for (let i=0; i<count; i++) {
arr[i*3] = -arr[i*3];
}
// invert face vertices
// invert face vertex order
for (let i=0; i<count; i+=3) {
let x = arr[i*3+0];
let y = arr[i*3+1];
@ -766,9 +594,9 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
ot.mirror = !ot.mirror;
this.setModified();
this.points = null;
};
}
PRO.getGeoVertices = function(unroll) {
getGeoVertices(unroll) {
let geo = this.mesh.geometry;
let pos = geo.getAttribute('position').array;
if (geo.index && unroll !== false) {
@ -787,14 +615,14 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
} else {
return pos;
}
};
}
PRO.getGeoIndices = function() {
getGeoIndices() {
let indices = this.mesh.geometry.index;
return indices ? indices.array : undefined;
};
}
PRO.iterPoints = function() {
iterPoints() {
let verts = this.getGeoVertices();
let index = 0;
let count = verts.length;
@ -807,9 +635,9 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
}
} }
};
};
}
PRO.getPoints = function() {
getPoints() {
if (!this.points) {
// convert and cache points from geometry vertices
this.points = verticesToPoints(this.getGeoVertices(), {
@ -817,17 +645,17 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
});
}
return this.points;
};
}
PRO.getBoundingBox = function(refresh) {
getBoundingBox(refresh) {
if (!this.bounds || refresh) {
this.bounds = new THREE.Box3();
this.bounds.setFromPoints(this.getPoints());
}
return this.bounds;
};
}
PRO.getPositionBox = function() {
getPositionBox() {
let bounds = this.getBoundingBox().clone();
let pos = this.track.pos;
bounds.min.x += pos.x;
@ -835,29 +663,24 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
bounds.min.y += pos.y;
bounds.max.y += pos.y;
return bounds;
};
}
PRO.isModified = function() {
isModified() {
return this.modified;
};
}
PRO.getExtruder = function(settings) {
getExtruder(settings) {
if (settings) {
console.trace('legacy call with settings');
}
return this.anno.extruder || 0;
// if (settings && settings.widget) {
// let rec = settings.widget[this.id];
// return rec && rec.extruder >= 0 ? rec.extruder : 0;
// }
// return 0;
};
}
// allow worker code to run in same memspace as client
PRO.setInWorker = function() {
setInWorker() {
this.inWorker = true;
return this;
};
}
/**
* processes points into facets, then into slices
@ -872,7 +695,7 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
* @params {Function} [ondone]
* @params {Function} [onupdate]
*/
PRO.slice = function(settings, ondone, onupdate) {
slice(settings, ondone, onupdate) {
let widget = this;
let startTime = time();
@ -943,12 +766,12 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
// discard point cache
widget.points = undefined;
};
}
/**
* render to provided stack
*/
PRO.render = function(stack) {
render(stack) {
const mark = Date.now();
this.slices.forEach(slice => {
if (slice.layers) {
@ -956,10 +779,10 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
}
});
return Date.now() - mark;
};
}
PRO.setWireframe = function(set, color, opacity) {
if (!(kiri.api && kiri.api.conf)) {
setWireframe(set, color, opacity) {
if (!(api && api.conf)) {
// missing api features in engine mode
return;
}
@ -971,7 +794,7 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
this.wire = null;
}
if (set) {
let dark = kiri.api.conf.get().controller.dark;
let dark = api.space.is_dark();
let mat = new THREE.MeshBasicMaterial({
wireframe: true,
color: dark ? 0xaaaaaa : 0,
@ -981,7 +804,7 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
let wire = widget.wire = new THREE.Mesh(mesh.geometry.shallowClone(), mat);
mesh.add(wire);
}
if (kiri.api.view.get() === kiri.consts.VIEWS.ARRANGE) {
if (api.view.is_arrange()) {
this.setColor(this.color);
} else {
this.setColor(0x888888,undefined,false);
@ -989,14 +812,155 @@ console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
if (opacity !== undefined) {
widget.setOpacity(opacity);
}
};
}
PRO.show = function() {
show() {
this.mesh.visible = true;
};
}
PRO.hide = function() {
hide() {
this.mesh.visible = false;
};
}
}
// Widget Grouping API
const Group = Widget.Groups = {
list() {
return groups.slice()
},
merge(widgets) {
let grps = widgets.map(w => w.group).uniq();
if (grps.length > 1) {
let root = grps.shift();
let rpos = root[0].track.pos;
for (let grp of grps) {
for (let w of grp) {
let wpos = w.track.pos;
w.group = root;
w.moveMesh(rpos.x - wpos.x, rpos.y - wpos.y, rpos.z - wpos.z);
w._move(rpos.x, rpos.y, rpos.z, true);
root.push(w);
}
groups.splice(groups.indexOf(grp),1);
}
}
},
split(widgets) {
for (let group of widgets.map(w => w.group).uniq()) {
groups.splice(groups.indexOf(group),1);
for (let widget of group) {
let nugroup = Group.forid(widget.id);
nugroup.push(widget);
widget.group = nugroup;
}
}
},
forid(id) {
for (let i=0; i<groups.length; i++) {
if (groups[i].id === id) return groups[i];
}
let group = [];
group.id = id;
groups.push(group);
return group;
},
remove(widget) {
groups.slice().forEach(group => {
let pos = group.indexOf(widget);
if (pos >= 0) {
group.splice(pos,1);
}
if (group.length === 0) {
pos = groups.indexOf(group);
groups.splice(pos,1);
}
});
},
blocks() {
return groups.map(group => {
return {
w: group[0].track.box.w,
h: group[0].track.box.h,
move: (x,y,z,abs) => {
group.forEach(widget => {
widget.mesh.material.visible = true;
widget._move(x, y, z, abs);
});
}
};
});
},
loadDone() {
groups.forEach(group => {
if (!group.centered) {
group[0].center();
group.centered = true;
}
});
},
bounds(group) {
let bounds = null;
group.forEach(widget => {
let wb = widget.mesh.getBoundingBox(true);
if (bounds) {
bounds = bounds.union(wb);
} else {
bounds = wb;
}
});
return bounds;
},
clear() {
groups.length = 0;
}
};
Widget.loadFromCatalog = function(filename, ondone) {
catalog().getFile(filename, function(data) {
let widget = newWidget().loadVertices(data);
widget.meta.file = filename;
ondone(widget);
});
};
Widget.loadFromState = function(id, ondone, move) {
index().get('ws-save-'+id, function(data) {
if (data) {
let vertices = data.geo || data,
indices = data.ind || undefined,
track = data.track || undefined,
group = data.group || id,
anno = data.anno || undefined,
widget = newWidget(id, Group.forid(group)),
meta = data.meta || widget.meta,
ptr = widget.loadVertices({vertices, indices});
widget.meta = meta;
widget.anno = anno || widget.anno;
// restore widget position if specified
if (move && track && track.pos) {
widget.track = track;
widget.move(track.pos.x, track.pos.y, track.pos.z, true);
}
ondone(ptr);
} else {
ondone(null);
}
});
};
Widget.deleteFromState = function(id,ondone) {
index().remove('ws-save-'+id, ondone);
};
kiri.Widget = Widget;
kiri.newWidget = newWidget;
})();