diff --git a/app.js b/app.js index e1de4117..04758404 100644 --- a/app.js +++ b/app.js @@ -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", diff --git a/src/cli/kiri-source.json b/src/cli/kiri-source.json index 5b98a0f1..d3bff2fa 100644 --- a/src/cli/kiri-source.json +++ b/src/cli/kiri-source.json @@ -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" ] diff --git a/src/kiri-mode/cam/animate.js b/src/kiri-mode/cam/animate.js index 1c7c281c..b376da81 100644 --- a/src/kiri-mode/cam/animate.js +++ b/src/kiri-mode/cam/animate.js @@ -2,7 +2,7 @@ "use strict"; -kiri.load(function() { +self.kiri.load(function() { let KIRI = self.kiri, CAM = KIRI.driver.CAM, diff --git a/src/kiri-mode/fdm/slice.js b/src/kiri-mode/fdm/slice.js index 827666d2..6fa2026b 100644 --- a/src/kiri-mode/fdm/slice.js +++ b/src/kiri-mode/fdm/slice.js @@ -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; diff --git a/src/kiri/cli.js b/src/kiri/cli.js index bc11d31e..a12a2c17 100644 --- a/src/kiri/cli.js +++ b/src/kiri/cli.js @@ -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}); diff --git a/src/kiri/main.js b/src/kiri/main.js index 0f22475d..bec5700b 100644 --- a/src/kiri/main.js +++ b/src/kiri/main.js @@ -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"); diff --git a/src/kiri/platform.js b/src/kiri/platform.js index af6210e6..253e57ce 100644 --- a/src/kiri/platform.js +++ b/src/kiri/platform.js @@ -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) } }; })(); diff --git a/src/kiri/utils.js b/src/kiri/utils.js index 1a8ea049..c84a282a 100644 --- a/src/kiri/utils.js +++ b/src/kiri/utils.js @@ -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 }; })(); diff --git a/src/kiri/widget.js b/src/kiri/widget.js index 1062de94..0f0c83d3 100644 --- a/src/kiri/widget.js +++ b/src/kiri/widget.js @@ -4,145 +4,29 @@ console.log/** Copyright Stewart Allen -- 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 { - 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- All Rights Reserved */ for (let i=0; i -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 -- 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 { + 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; })();