convert bulk of root kiri packages to gapp.register() loading
This commit is contained in:
parent
64d825b48c
commit
57a2129dba
33 changed files with 3092 additions and 3030 deletions
2
app.js
2
app.js
|
|
@ -365,7 +365,6 @@ const script = {
|
||||||
"kiri/stats",
|
"kiri/stats",
|
||||||
"kiri/api",
|
"kiri/api",
|
||||||
"kiri/ui",
|
"kiri/ui",
|
||||||
"kiri/do",
|
|
||||||
"kiri/pack",
|
"kiri/pack",
|
||||||
"kiri/lang",
|
"kiri/lang",
|
||||||
"kiri/lang-en",
|
"kiri/lang-en",
|
||||||
|
|
@ -395,6 +394,7 @@ const script = {
|
||||||
"kiri/codec",
|
"kiri/codec",
|
||||||
"kiri/main",
|
"kiri/main",
|
||||||
"kiri/init",
|
"kiri/init",
|
||||||
|
"kiri/do",
|
||||||
"kiri/export",
|
"kiri/export",
|
||||||
"kiri/tools",
|
"kiri/tools",
|
||||||
"kiri/frame",
|
"kiri/frame",
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,10 @@
|
||||||
"geo/polygon",
|
"geo/polygon",
|
||||||
"geo/gyroid",
|
"geo/gyroid",
|
||||||
"geo/slicer",
|
"geo/slicer",
|
||||||
|
"data/local",
|
||||||
"load/obj",
|
"load/obj",
|
||||||
"load/stl",
|
"load/stl",
|
||||||
|
"kiri/consts",
|
||||||
"kiri/conf",
|
"kiri/conf",
|
||||||
"kiri/pack",
|
"kiri/pack",
|
||||||
"kiri/client",
|
"kiri/client",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ const { base, kiri } = self;
|
||||||
const { util, config } = base;
|
const { util, config } = base;
|
||||||
const { FDM } = kiri.driver;
|
const { FDM } = kiri.driver;
|
||||||
const { getRangeParameters } = FDM;
|
const { getRangeParameters } = FDM;
|
||||||
|
const debug = false;
|
||||||
|
|
||||||
FDM.export = function(print, online, ondone, ondebug) {
|
FDM.export = function(print, online, ondone, ondebug) {
|
||||||
const { settings, belty, tools } = print;
|
const { settings, belty, tools } = print;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
// dep: kiri.api
|
// dep: kiri.api
|
||||||
gapp.register("kiri.alerts", [], (root, exports) => {
|
gapp.register("kiri.alerts", [], (root, exports) => {
|
||||||
|
|
||||||
const { kiri } = self;
|
const { kiri } = root;
|
||||||
const { api } = kiri;
|
const { api } = kiri;
|
||||||
|
|
||||||
let alerts = [];
|
let alerts = [];
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
// use: kiri.settings
|
// use: kiri.settings
|
||||||
gapp.register("kiri.api", [], (root, exports) => {
|
gapp.register("kiri.api", [], (root, exports) => {
|
||||||
|
|
||||||
const { data, kiri, noop } = self;
|
const { data, kiri, noop } = root;
|
||||||
const { consts, utils } = kiri;
|
const { consts, utils } = kiri;
|
||||||
const { areEqual, parseOpt, encodeOpt, ajax, o2js, js2o } = utils;
|
const { areEqual, parseOpt, encodeOpt, ajax, o2js, js2o } = utils;
|
||||||
const { LISTS } = consts;
|
const { LISTS } = consts;
|
||||||
|
|
|
||||||
|
|
@ -6,387 +6,388 @@
|
||||||
// dep: geo.base
|
// dep: geo.base
|
||||||
gapp.register("kiri.codec", [], (root, exports) => {
|
gapp.register("kiri.codec", [], (root, exports) => {
|
||||||
|
|
||||||
const BASE = self.base, KIRI = self.kiri, handlers = {};
|
const { base, kiri } = root;
|
||||||
const freeMem = true;
|
const handlers = {};
|
||||||
const zeroOut = true;
|
const freeMem = true;
|
||||||
|
const zeroOut = true;
|
||||||
|
|
||||||
const codec = exports({
|
const codec = exports({
|
||||||
undef: undefined,
|
undef: undefined,
|
||||||
encode: encode,
|
encode: encode,
|
||||||
decode: decode,
|
decode: decode,
|
||||||
registerDecoder: registerDecoder,
|
registerDecoder: registerDecoder,
|
||||||
allocFloat32Array: allocFloat32Array,
|
allocFloat32Array: allocFloat32Array,
|
||||||
encodePointArray,
|
encodePointArray,
|
||||||
decodePointArray,
|
decodePointArray,
|
||||||
toCodable
|
toCodable
|
||||||
});
|
});
|
||||||
|
|
||||||
function toCodable(object) {
|
function toCodable(object) {
|
||||||
switch (typeof object) {
|
switch (typeof object) {
|
||||||
|
case 'function':
|
||||||
|
return undefined;
|
||||||
|
case 'string':
|
||||||
|
case 'number':
|
||||||
|
return object;
|
||||||
|
case 'object':
|
||||||
|
if (Array.isArray(object)) {
|
||||||
|
return object.map(v => toCodable(v));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let o = {};
|
||||||
|
for (let [key, val] of Object.entries(object)) {
|
||||||
|
switch (typeof val) {
|
||||||
case 'function':
|
case 'function':
|
||||||
return undefined;
|
break;
|
||||||
case 'string':
|
|
||||||
case 'number':
|
|
||||||
return object;
|
|
||||||
case 'object':
|
case 'object':
|
||||||
if (Array.isArray(object)) {
|
if (Array.isArray(val)) {
|
||||||
return object.map(v => toCodable(v));
|
val = val.map(v => toCodable(v));
|
||||||
|
} else {
|
||||||
|
val = toCodable(val);
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
o[key] = val;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let o = {};
|
|
||||||
for (let [key, val] of Object.entries(object)) {
|
|
||||||
switch (typeof val) {
|
|
||||||
case 'function':
|
|
||||||
break;
|
|
||||||
case 'object':
|
|
||||||
if (Array.isArray(val)) {
|
|
||||||
val = val.map(v => toCodable(v));
|
|
||||||
} else {
|
|
||||||
val = toCodable(val);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
o[key] = val;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
}
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
function allocFloat32Array(arg) {
|
function allocFloat32Array(arg) {
|
||||||
if (arg.byteLength) {
|
if (arg.byteLength) {
|
||||||
// already a float array
|
// already a float array
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
if (Array.isArray(arg)) {
|
if (Array.isArray(arg)) {
|
||||||
// create float array from array
|
// create float array from array
|
||||||
return new Float32Array(arg);
|
|
||||||
}
|
|
||||||
// usually a number (size) for array
|
|
||||||
return new Float32Array(arg);
|
return new Float32Array(arg);
|
||||||
}
|
}
|
||||||
|
// usually a number (size) for array
|
||||||
|
return new Float32Array(arg);
|
||||||
|
}
|
||||||
|
|
||||||
function encode(o, state) {
|
function encode(o, state) {
|
||||||
state = state || {};
|
state = state || {};
|
||||||
if (o === null) return null;
|
if (o === null) return null;
|
||||||
if (o === undefined) return undefined;
|
if (o === undefined) return undefined;
|
||||||
if (Array.isArray(o)) {
|
if (Array.isArray(o)) {
|
||||||
let arr = new Array(o.length), i=0;
|
let arr = new Array(o.length), i=0;
|
||||||
while (i < o.length) arr[i] = encode(o[i++], state);
|
while (i < o.length) arr[i] = encode(o[i++], state);
|
||||||
return arr;
|
return arr;
|
||||||
}
|
|
||||||
switch (typeof(o)) {
|
|
||||||
case 'string':
|
|
||||||
case 'number':
|
|
||||||
return o;
|
|
||||||
case 'object':
|
|
||||||
if (o.encode) return o.encode(state);
|
|
||||||
return genOEncode(o, state);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
switch (typeof(o)) {
|
||||||
function decode(o, state) {
|
case 'string':
|
||||||
state = state || {};
|
case 'number':
|
||||||
if (o === null) return null;
|
|
||||||
if (o === undefined) return undefined;
|
|
||||||
if (Array.isArray(o)) {
|
|
||||||
for (let i=0; i < o.length; i++) o[i] = decode(o[i],state);
|
|
||||||
return o;
|
return o;
|
||||||
}
|
case 'object':
|
||||||
switch (typeof(o)) {
|
if (o.encode) return o.encode(state);
|
||||||
case 'string':
|
return genOEncode(o, state);
|
||||||
case 'number':
|
|
||||||
return o;
|
|
||||||
case 'object':
|
|
||||||
if (o.type && handlers[o.type]) return handlers[o.type](o,state);
|
|
||||||
return genODecode(o, state);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function registerDecoder(type, handler) {
|
function decode(o, state) {
|
||||||
handlers[type] = handler;
|
state = state || {};
|
||||||
|
if (o === null) return null;
|
||||||
|
if (o === undefined) return undefined;
|
||||||
|
if (Array.isArray(o)) {
|
||||||
|
for (let i=0; i < o.length; i++) o[i] = decode(o[i],state);
|
||||||
|
return o;
|
||||||
}
|
}
|
||||||
|
switch (typeof(o)) {
|
||||||
function genOEncode(o, state) {
|
case 'string':
|
||||||
if (o instanceof Float32Array) return o;
|
case 'number':
|
||||||
let out = {};
|
return o;
|
||||||
for (let k in o) {
|
case 'object':
|
||||||
if (o.hasOwnProperty(k)) out[k] = encode(o[k], state);
|
if (o.type && handlers[o.type]) return handlers[o.type](o,state);
|
||||||
}
|
return genODecode(o, state);
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function genODecode(o, state) {
|
function registerDecoder(type, handler) {
|
||||||
if (o instanceof Float32Array) return o;
|
handlers[type] = handler;
|
||||||
let out = {};
|
}
|
||||||
for (let k in o) {
|
|
||||||
if (o.hasOwnProperty(k)) out[k] = decode(o[k], state);
|
function genOEncode(o, state) {
|
||||||
}
|
if (o instanceof Float32Array) return o;
|
||||||
return out;
|
let out = {};
|
||||||
|
for (let k in o) {
|
||||||
|
if (o.hasOwnProperty(k)) out[k] = encode(o[k], state);
|
||||||
}
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
/** ******************************************************************
|
function genODecode(o, state) {
|
||||||
* Object Class CODEC Functions
|
if (o instanceof Float32Array) return o;
|
||||||
******************************************************************* */
|
let out = {};
|
||||||
|
for (let k in o) {
|
||||||
|
if (o.hasOwnProperty(k)) out[k] = decode(o[k], state);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
KIRI.Widget.prototype.encode = function(state) {
|
/** ******************************************************************
|
||||||
const json = state._json_;
|
* Object Class CODEC Functions
|
||||||
const geo = this.getGeoVertices();
|
******************************************************************* */
|
||||||
const coded = {
|
|
||||||
type: 'widget',
|
kiri.Widget.prototype.encode = function(state) {
|
||||||
id: this.id,
|
const json = state._json_;
|
||||||
ver: 1, // for better future encodings
|
const geo = this.getGeoVertices();
|
||||||
json: json, // safe for JSON (float32array mess)
|
const coded = {
|
||||||
group: this.group.id,
|
type: 'widget',
|
||||||
track: this.track,
|
id: this.id,
|
||||||
geo: json ? Array.from(geo) : geo
|
ver: 1, // for better future encodings
|
||||||
};
|
json: json, // safe for JSON (float32array mess)
|
||||||
return coded;
|
group: this.group.id,
|
||||||
|
track: this.track,
|
||||||
|
geo: json ? Array.from(geo) : geo
|
||||||
};
|
};
|
||||||
|
return coded;
|
||||||
|
};
|
||||||
|
|
||||||
registerDecoder('widget', function(v, state) {
|
registerDecoder('widget', function(v, state) {
|
||||||
const id = v.id,
|
const id = v.id,
|
||||||
group = v.group || id,
|
group = v.group || id,
|
||||||
track = v.track || undefined,
|
track = v.track || undefined,
|
||||||
widget = KIRI.newWidget(id, KIRI.Widget.Groups.forid(group));
|
widget = kiri.newWidget(id, kiri.Widget.Groups.forid(group));
|
||||||
widget.loadVertices(v.json ? v.geo.toFloat32() : v.geo);
|
widget.loadVertices(v.json ? v.geo.toFloat32() : v.geo);
|
||||||
widget.saved = Date.now();
|
widget.saved = Date.now();
|
||||||
if (track && track.pos) {
|
if (track && track.pos) {
|
||||||
widget.track = track;
|
widget.track = track;
|
||||||
widget.move(track.pos.x, track.pos.y, track.pos.z, true);
|
widget.move(track.pos.x, track.pos.y, track.pos.z, true);
|
||||||
}
|
}
|
||||||
return widget;
|
return widget;
|
||||||
});
|
});
|
||||||
|
|
||||||
KIRI.Slice.prototype.encode = function(state) {
|
kiri.Slice.prototype.encode = function(state) {
|
||||||
const rv = {
|
const rv = {
|
||||||
type: 'slice',
|
type: 'slice',
|
||||||
z: this.z,
|
z: this.z,
|
||||||
index: this.index,
|
index: this.index,
|
||||||
layers: encode(this.layers, state)
|
layers: encode(this.layers, state)
|
||||||
};
|
|
||||||
// aggressively free memory
|
|
||||||
if (freeMem) this.layers = undefined;
|
|
||||||
return rv;
|
|
||||||
};
|
};
|
||||||
|
// aggressively free memory
|
||||||
|
if (freeMem) this.layers = undefined;
|
||||||
|
return rv;
|
||||||
|
};
|
||||||
|
|
||||||
registerDecoder('slice', function(v, state) {
|
registerDecoder('slice', function(v, state) {
|
||||||
let slice = KIRI.newSlice(v.z, state.mesh ? state.mesh.newGroup() : null);
|
let slice = kiri.newSlice(v.z, state.mesh ? state.mesh.newGroup() : null);
|
||||||
|
|
||||||
slice.index = v.index;
|
slice.index = v.index;
|
||||||
slice.layers = decode(v.layers, state)
|
slice.layers = decode(v.layers, state)
|
||||||
|
|
||||||
return slice;
|
return slice;
|
||||||
});
|
});
|
||||||
|
|
||||||
KIRI.Layers.prototype.encode = function(state) {
|
kiri.Layers.prototype.encode = function(state) {
|
||||||
let enc = {
|
let enc = {
|
||||||
type: 'layers',
|
type: 'layers',
|
||||||
layers: Object.keys(this.layers),
|
layers: Object.keys(this.layers),
|
||||||
data: Object.values(this.layers).map(layer => {
|
data: Object.values(this.layers).map(layer => {
|
||||||
const e = {
|
const e = {
|
||||||
polys: encode(layer.polys, state),
|
polys: encode(layer.polys, state),
|
||||||
lines: encodePointArray(layer.lines, state),
|
lines: encodePointArray(layer.lines, state),
|
||||||
faces: codec.allocFloat32Array(layer.faces),
|
faces: codec.allocFloat32Array(layer.faces),
|
||||||
cface: layer.cface || codec.undef,
|
cface: layer.cface || codec.undef,
|
||||||
color: layer.color,
|
color: layer.color,
|
||||||
paths: layer.paths.map(lp => {
|
paths: layer.paths.map(lp => {
|
||||||
const pe = {
|
const pe = {
|
||||||
z: lp.z,
|
|
||||||
index: lp.index,
|
|
||||||
faces: codec.allocFloat32Array(lp.faces)
|
|
||||||
};
|
|
||||||
if (zeroOut && state.zeros && pe.faces.length) {
|
|
||||||
state.zeros.push(pe.faces.buffer);
|
|
||||||
}
|
|
||||||
return pe;
|
|
||||||
}),
|
|
||||||
cpath: layer.cpath || codec.undef,
|
|
||||||
off: layer.off
|
|
||||||
};
|
|
||||||
if (zeroOut && state.zeros && e.faces.length) {
|
|
||||||
state.zeros.push(e.faces.buffer);
|
|
||||||
}
|
|
||||||
// console.log('-->',layer,e);
|
|
||||||
return e;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
// aggressively free memory
|
|
||||||
if (freeMem) this.init();
|
|
||||||
return enc;
|
|
||||||
};
|
|
||||||
|
|
||||||
registerDecoder('layers', function(v, state) {
|
|
||||||
let render = new KIRI.Layers();
|
|
||||||
for (let i=0; i<v.layers.length; i++) {
|
|
||||||
const d = render.layers[v.layers[i]] = {
|
|
||||||
polys: decode(v.data[i].polys, state),
|
|
||||||
cpoly: v.data[i].cpoly,
|
|
||||||
lines: decodePointArray(v.data[i].lines),
|
|
||||||
faces: codec.allocFloat32Array(v.data[i].faces),
|
|
||||||
cface: v.data[i].cface,
|
|
||||||
color: v.data[i].color,
|
|
||||||
paths: v.data[i].paths.map(lp => {
|
|
||||||
return {
|
|
||||||
z: lp.z,
|
z: lp.z,
|
||||||
index: lp.index,
|
index: lp.index,
|
||||||
faces: codec.allocFloat32Array(lp.faces)
|
faces: codec.allocFloat32Array(lp.faces)
|
||||||
};
|
};
|
||||||
|
if (zeroOut && state.zeros && pe.faces.length) {
|
||||||
|
state.zeros.push(pe.faces.buffer);
|
||||||
|
}
|
||||||
|
return pe;
|
||||||
}),
|
}),
|
||||||
cpath: v.data[i].cpath,
|
cpath: layer.cpath || codec.undef,
|
||||||
off: v.data[i].off
|
off: layer.off
|
||||||
};
|
};
|
||||||
// fixup null -> Infinity in material counts (JSON stringify sucks)
|
if (zeroOut && state.zeros && e.faces.length) {
|
||||||
if (d.cface) {
|
state.zeros.push(e.faces.buffer);
|
||||||
d.cface.forEach(rec => {
|
|
||||||
if (rec.count === null) {
|
|
||||||
rec.count = Infinity;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (d.cpoly) {
|
// console.log('-->',layer,e);
|
||||||
d.cpoly.forEach(rec => {
|
return e;
|
||||||
if (rec.count === null) {
|
})
|
||||||
rec.count = Infinity;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (d.cpath) {
|
|
||||||
d.cpath.forEach(rec => {
|
|
||||||
if (rec.count === null) {
|
|
||||||
rec.count = Infinity;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// console.log('<--',d);
|
|
||||||
}
|
|
||||||
return render;
|
|
||||||
});
|
|
||||||
|
|
||||||
KIRI.Top.prototype.encode = function(state) {
|
|
||||||
let obj = {
|
|
||||||
type: 'top',
|
|
||||||
poly: encode(this.poly, state)
|
|
||||||
};
|
|
||||||
if (state.full) {
|
|
||||||
obj.last = encode(this.last, state);
|
|
||||||
obj.shells = encode(this.shells, state);
|
|
||||||
obj.fill_off = encode(this.fill_off, state);
|
|
||||||
obj.fill_lines = encode(this.fill_lines, state);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
};
|
};
|
||||||
|
// aggressively free memory
|
||||||
|
if (freeMem) this.init();
|
||||||
|
return enc;
|
||||||
|
};
|
||||||
|
|
||||||
registerDecoder('top', function(v, state) {
|
registerDecoder('layers', function(v, state) {
|
||||||
let top = KIRI.newTop(decode(v.poly, state));
|
let render = new kiri.Layers();
|
||||||
if (state.full) {
|
for (let i=0; i<v.layers.length; i++) {
|
||||||
top.last = decode(v.last, state);
|
const d = render.layers[v.layers[i]] = {
|
||||||
top.shells = decode(v.shells, state);
|
polys: decode(v.data[i].polys, state),
|
||||||
top.fill_off = decode(v.fill_off, state);
|
cpoly: v.data[i].cpoly,
|
||||||
top.fill_lines = decode(v.fill_lines, state);
|
lines: decodePointArray(v.data[i].lines),
|
||||||
|
faces: codec.allocFloat32Array(v.data[i].faces),
|
||||||
|
cface: v.data[i].cface,
|
||||||
|
color: v.data[i].color,
|
||||||
|
paths: v.data[i].paths.map(lp => {
|
||||||
|
return {
|
||||||
|
z: lp.z,
|
||||||
|
index: lp.index,
|
||||||
|
faces: codec.allocFloat32Array(lp.faces)
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
cpath: v.data[i].cpath,
|
||||||
|
off: v.data[i].off
|
||||||
|
};
|
||||||
|
// fixup null -> Infinity in material counts (JSON stringify sucks)
|
||||||
|
if (d.cface) {
|
||||||
|
d.cface.forEach(rec => {
|
||||||
|
if (rec.count === null) {
|
||||||
|
rec.count = Infinity;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return top;
|
if (d.cpoly) {
|
||||||
|
d.cpoly.forEach(rec => {
|
||||||
|
if (rec.count === null) {
|
||||||
|
rec.count = Infinity;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (d.cpath) {
|
||||||
|
d.cpath.forEach(rec => {
|
||||||
|
if (rec.count === null) {
|
||||||
|
rec.count = Infinity;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// console.log('<--',d);
|
||||||
|
}
|
||||||
|
return render;
|
||||||
|
});
|
||||||
|
|
||||||
|
kiri.Top.prototype.encode = function(state) {
|
||||||
|
let obj = {
|
||||||
|
type: 'top',
|
||||||
|
poly: encode(this.poly, state)
|
||||||
|
};
|
||||||
|
if (state.full) {
|
||||||
|
obj.last = encode(this.last, state);
|
||||||
|
obj.shells = encode(this.shells, state);
|
||||||
|
obj.fill_off = encode(this.fill_off, state);
|
||||||
|
obj.fill_lines = encode(this.fill_lines, state);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
registerDecoder('top', function(v, state) {
|
||||||
|
let top = kiri.newTop(decode(v.poly, state));
|
||||||
|
if (state.full) {
|
||||||
|
top.last = decode(v.last, state);
|
||||||
|
top.shells = decode(v.shells, state);
|
||||||
|
top.fill_off = decode(v.fill_off, state);
|
||||||
|
top.fill_lines = decode(v.fill_lines, state);
|
||||||
|
}
|
||||||
|
return top;
|
||||||
|
});
|
||||||
|
|
||||||
|
function encodePointArray(points, state, z) {
|
||||||
|
if (!points) return null;
|
||||||
|
|
||||||
|
let array = codec.allocFloat32Array(points.length * 3),
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
|
points.forEach(function(point) {
|
||||||
|
if (state.rotate) {
|
||||||
|
if (state.centerz) {
|
||||||
|
point.z -= state.centerz;
|
||||||
|
}
|
||||||
|
let v = new THREE.Vector3(point.x, point.y, point.z);
|
||||||
|
v.applyMatrix4(state.rotate);
|
||||||
|
point = {x: v.x, y: v.y, z: v.z};
|
||||||
|
if (state.centerz) {
|
||||||
|
point.z += state.centerz;
|
||||||
|
}
|
||||||
|
if (state.movez) {
|
||||||
|
point.z -= state.movez;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
array[pos++] = point.x;
|
||||||
|
array[pos++] = point.y;
|
||||||
|
array[pos++] = z !== undefined ? z : point.z;
|
||||||
});
|
});
|
||||||
|
|
||||||
function encodePointArray(points, state, z) {
|
if (zeroOut && state.zeros) {
|
||||||
if (!points) return null;
|
state.zeros.push(array.buffer);
|
||||||
|
|
||||||
let array = codec.allocFloat32Array(points.length * 3),
|
|
||||||
pos = 0;
|
|
||||||
|
|
||||||
points.forEach(function(point) {
|
|
||||||
if (state.rotate) {
|
|
||||||
if (state.centerz) {
|
|
||||||
point.z -= state.centerz;
|
|
||||||
}
|
|
||||||
let v = new THREE.Vector3(point.x, point.y, point.z);
|
|
||||||
v.applyMatrix4(state.rotate);
|
|
||||||
point = {x: v.x, y: v.y, z: v.z};
|
|
||||||
if (state.centerz) {
|
|
||||||
point.z += state.centerz;
|
|
||||||
}
|
|
||||||
if (state.movez) {
|
|
||||||
point.z -= state.movez;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
array[pos++] = point.x;
|
|
||||||
array[pos++] = point.y;
|
|
||||||
array[pos++] = z !== undefined ? z : point.z;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (zeroOut && state.zeros) {
|
|
||||||
state.zeros.push(array.buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodePointArray(array) {
|
return array;
|
||||||
if (!array) return null;
|
}
|
||||||
|
|
||||||
let vid = 0,
|
function decodePointArray(array) {
|
||||||
pid = 0,
|
if (!array) return null;
|
||||||
points = new Array(array.length/3);
|
|
||||||
|
|
||||||
while (vid < array.length) {
|
let vid = 0,
|
||||||
points[pid++] = BASE.newPoint(array[vid++], array[vid++], array[vid++]);
|
pid = 0,
|
||||||
}
|
points = new Array(array.length/3);
|
||||||
|
|
||||||
return points;
|
while (vid < array.length) {
|
||||||
|
points[pid++] = base.newPoint(array[vid++], array[vid++], array[vid++]);
|
||||||
}
|
}
|
||||||
|
|
||||||
BASE.Polygon.prototype.encode = function(state) {
|
return points;
|
||||||
if (!state.poly) state.poly = {};
|
}
|
||||||
|
|
||||||
let cached = state.poly[this.id];
|
base.Polygon.prototype.encode = function(state) {
|
||||||
|
if (!state.poly) state.poly = {};
|
||||||
|
|
||||||
if (cached) {
|
let cached = state.poly[this.id];
|
||||||
return { type: 'poly', ref: this.id };
|
|
||||||
}
|
|
||||||
|
|
||||||
state.poly[this.id] = this;
|
if (cached) {
|
||||||
return {
|
return { type: 'poly', ref: this.id };
|
||||||
type: 'poly',
|
}
|
||||||
id: this.id,
|
|
||||||
array: encodePointArray(this.points, state, this.z),
|
state.poly[this.id] = this;
|
||||||
inner: encode(this.inner, state),
|
return {
|
||||||
parent: encode(this.parent, state),
|
type: 'poly',
|
||||||
depth: this.depth,
|
id: this.id,
|
||||||
color: this.color,
|
array: encodePointArray(this.points, state, this.z),
|
||||||
open: this.open
|
inner: encode(this.inner, state),
|
||||||
};
|
parent: encode(this.parent, state),
|
||||||
|
depth: this.depth,
|
||||||
|
color: this.color,
|
||||||
|
open: this.open
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
registerDecoder('poly', function(v, state) {
|
registerDecoder('poly', function(v, state) {
|
||||||
if (!state.poly) state.poly = {};
|
if (!state.poly) state.poly = {};
|
||||||
|
|
||||||
if (v.ref) return state.poly[v.ref];
|
if (v.ref) return state.poly[v.ref];
|
||||||
|
|
||||||
let poly = BASE.newPolygon(),
|
let poly = base.newPolygon(),
|
||||||
vid = 0;
|
vid = 0;
|
||||||
|
|
||||||
// if passed a normal array, convert to float32
|
// if passed a normal array, convert to float32
|
||||||
if (v.array.toFloat32) {
|
if (v.array.toFloat32) {
|
||||||
v.array = v.array.toFloat32();
|
v.array = v.array.toFloat32();
|
||||||
}
|
}
|
||||||
|
|
||||||
while (vid < v.array.length) {
|
while (vid < v.array.length) {
|
||||||
poly.push(BASE.newPoint(v.array[vid++], v.array[vid++], v.array[vid++]));
|
poly.push(base.newPoint(v.array[vid++], v.array[vid++], v.array[vid++]));
|
||||||
}
|
}
|
||||||
|
|
||||||
poly.id = v.id;
|
poly.id = v.id;
|
||||||
poly.open = v.open;
|
poly.open = v.open;
|
||||||
|
|
||||||
state.poly[v.id] = poly;
|
state.poly[v.id] = poly;
|
||||||
|
|
||||||
poly.inner = decode(v.inner, state);
|
poly.inner = decode(v.inner, state);
|
||||||
poly.parent = decode(v.parent, state);
|
poly.parent = decode(v.parent, state);
|
||||||
poly.depth = v.depth;
|
poly.depth = v.depth;
|
||||||
poly.color = v.color;
|
poly.color = v.color;
|
||||||
|
|
||||||
return poly;
|
return poly;
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
1408
src/kiri/conf.js
1408
src/kiri/conf.js
File diff suppressed because it is too large
Load diff
|
|
@ -2,9 +2,7 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function () {
|
gapp.register("kiri.consts", [], (root, exports) => {
|
||||||
|
|
||||||
const { kiri } = self;
|
|
||||||
|
|
||||||
const COLOR = {
|
const COLOR = {
|
||||||
wireframe: 0x444444,
|
wireframe: 0x444444,
|
||||||
|
|
@ -124,7 +122,7 @@ const PMODES = {
|
||||||
|
|
||||||
const SEED = 'kiri-seed';
|
const SEED = 'kiri-seed';
|
||||||
|
|
||||||
kiri.consts = {
|
exports({
|
||||||
PMODES,
|
PMODES,
|
||||||
COLOR,
|
COLOR,
|
||||||
LISTS,
|
LISTS,
|
||||||
|
|
@ -132,6 +130,6 @@ kiri.consts = {
|
||||||
VIEWS,
|
VIEWS,
|
||||||
SEED,
|
SEED,
|
||||||
beltfact: Math.cos(Math.PI / 4)
|
beltfact: Math.cos(Math.PI / 4)
|
||||||
};
|
});
|
||||||
|
|
||||||
})();
|
});
|
||||||
|
|
|
||||||
316
src/kiri/do.js
316
src/kiri/do.js
|
|
@ -1,171 +1,179 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
kiri.load(function(API) {
|
// dep: kiri.api
|
||||||
let stack = [];
|
// use: kiri.selection
|
||||||
let stpos = 0;
|
// use: moto.space
|
||||||
|
gapp.register("kiri.do", [], (root, events) => {
|
||||||
|
|
||||||
let event = API.event;
|
const { kiri, moto } = root;
|
||||||
let moved = {x:0, y:0};
|
const { api } = kiri;
|
||||||
let msgid;
|
const { space } = moto;
|
||||||
|
const { event } = api;
|
||||||
|
|
||||||
event.on("init-done", () => {
|
let stack = [];
|
||||||
$('undo').onclick = undo;
|
let stpos = 0;
|
||||||
$('redo').onclick = redo;
|
let moved = { x: 0, y: 0 };
|
||||||
});
|
let msgid;
|
||||||
|
|
||||||
let undo = API.doit.undo = function() {
|
event.on("init-done", () => {
|
||||||
if (stpos) {
|
$('undo').onclick = undo;
|
||||||
action(stack[--stpos].undo);
|
$('redo').onclick = redo;
|
||||||
} else {
|
});
|
||||||
message('nothing to undo');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let redo = API.doit.redo = function() {
|
let undo = api.doit.undo = function() {
|
||||||
if (stpos < stack.length) {
|
if (stpos) {
|
||||||
action(stack[stpos++].redo);
|
action(stack[--stpos].undo);
|
||||||
} else {
|
} else {
|
||||||
message('nothing to redo');
|
message('nothing to undo');
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let clear = API.doit.clear = function() {
|
|
||||||
stack = [];
|
|
||||||
stpos = 0;
|
|
||||||
moved = {x:0, y:0};
|
|
||||||
updateButtons();
|
|
||||||
};
|
|
||||||
|
|
||||||
function updateButtons() {
|
|
||||||
let isArrange = API.view.get() === kiri.consts.VIEWS.ARRANGE;
|
|
||||||
$('doit').style.display = isArrange && stack.length ? 'flex' : 'none';
|
|
||||||
$('undo').disabled = stpos === 0;
|
|
||||||
$('redo').disabled = stpos == stack.length;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function message(txt) {
|
let redo = api.doit.redo = function() {
|
||||||
if (msgid) {
|
if (stpos < stack.length) {
|
||||||
API.hide.alert(msgid);
|
action(stack[stpos++].redo);
|
||||||
}
|
} else {
|
||||||
msgid = API.show.alert(txt);
|
message('nothing to redo');
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function action(rec) {
|
let clear = api.doit.clear = function() {
|
||||||
switch (rec.type) {
|
stack = [];
|
||||||
case 'move':
|
stpos = 0;
|
||||||
for (let w of rec.widgets) {
|
moved = {x:0, y:0};
|
||||||
w._move(rec.dist.x, rec.dist.y, 0);
|
updateButtons();
|
||||||
}
|
};
|
||||||
break;
|
|
||||||
case 'rotate':
|
function updateButtons() {
|
||||||
for (let w of rec.widgets) {
|
let isArrange = api.view.get() === kiri.consts.VIEWS.ARRANGE;
|
||||||
w.rotate(rec.x, rec.y, rec.z);
|
$('doit').style.display = isArrange && stack.length ? 'flex' : 'none';
|
||||||
}
|
$('undo').disabled = stpos === 0;
|
||||||
break;
|
$('redo').disabled = stpos == stack.length;
|
||||||
case 'scale':
|
}
|
||||||
for (let w of rec.widgets) {
|
|
||||||
w._scale(rec.x, rec.y, rec.z);
|
function message(txt) {
|
||||||
}
|
if (msgid) {
|
||||||
API.selection.update_info();
|
api.hide.alert(msgid);
|
||||||
break;
|
|
||||||
}
|
|
||||||
API.space.update();
|
|
||||||
updateButtons();
|
|
||||||
}
|
}
|
||||||
|
msgid = api.show.alert(txt);
|
||||||
|
}
|
||||||
|
|
||||||
function pushActions(ur) {
|
function action(rec) {
|
||||||
stack.length = stpos++;
|
switch (rec.type) {
|
||||||
stack.push(ur);
|
case 'move':
|
||||||
updateButtons();
|
for (let w of rec.widgets) {
|
||||||
}
|
w._move(rec.dist.x, rec.dist.y, 0);
|
||||||
|
|
||||||
event.on([
|
|
||||||
'platform.layout',
|
|
||||||
'widget.add',
|
|
||||||
'widget.delete'
|
|
||||||
], (widget) => {
|
|
||||||
clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
event.on('view.set', mode => {
|
|
||||||
updateButtons();
|
|
||||||
});
|
|
||||||
|
|
||||||
// selection event (store position)
|
|
||||||
event.on('selection.rotate', (rec) => {
|
|
||||||
let type = "rotate";
|
|
||||||
let widgets = API.selection.widgets(true);
|
|
||||||
if (!widgets.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let {x, y, z} = rec;
|
|
||||||
if (!(x || y || z)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pushActions({
|
|
||||||
redo: {
|
|
||||||
type,
|
|
||||||
widgets,
|
|
||||||
x, y, z
|
|
||||||
},
|
|
||||||
undo: {
|
|
||||||
type,
|
|
||||||
widgets,
|
|
||||||
x: -x, y: -y, z: -z
|
|
||||||
}
|
}
|
||||||
})
|
break;
|
||||||
});
|
case 'rotate':
|
||||||
|
for (let w of rec.widgets) {
|
||||||
event.on('selection.scale', (rec) => {
|
w.rotate(rec.x, rec.y, rec.z);
|
||||||
let [x, y, z] = rec;
|
|
||||||
// ignore scale close to 1
|
|
||||||
if (Math.abs(1 - (x * y * z)) < 0.0001) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let widgets = API.selection.widgets(true);
|
|
||||||
if (!widgets.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let type = "scale";
|
|
||||||
pushActions({
|
|
||||||
redo: {
|
|
||||||
type,
|
|
||||||
widgets,
|
|
||||||
x, y, z
|
|
||||||
},
|
|
||||||
undo: {
|
|
||||||
type,
|
|
||||||
widgets,
|
|
||||||
x: 1/x, y: 1/y, z: 1/z
|
|
||||||
}
|
}
|
||||||
});
|
break;
|
||||||
});
|
case 'scale':
|
||||||
|
for (let w of rec.widgets) {
|
||||||
// selection moved (accumulator)
|
w._scale(rec.x, rec.y, rec.z);
|
||||||
event.on('selection.drag', (delta) => {
|
|
||||||
moved.x += delta.x;
|
|
||||||
moved.y += delta.y;
|
|
||||||
});
|
|
||||||
|
|
||||||
// move complete (store updated position)
|
|
||||||
event.on('mouse.drag.done', () => {
|
|
||||||
let widgets = API.selection.widgets(true);
|
|
||||||
if (!widgets.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let type = "move";
|
|
||||||
pushActions({
|
|
||||||
redo: {
|
|
||||||
type,
|
|
||||||
widgets,
|
|
||||||
dist: moved
|
|
||||||
},
|
|
||||||
undo: {
|
|
||||||
type,
|
|
||||||
widgets,
|
|
||||||
dist: {x: -moved.x, y: -moved.y}
|
|
||||||
}
|
}
|
||||||
});
|
api.selection.update_info();
|
||||||
moved = {x:0, y:0};
|
break;
|
||||||
|
}
|
||||||
|
space.update();
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
function pushActions(ur) {
|
||||||
|
stack.length = stpos++;
|
||||||
|
stack.push(ur);
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
event.on([
|
||||||
|
'platform.layout',
|
||||||
|
'widget.add',
|
||||||
|
'widget.delete'
|
||||||
|
], (widget) => {
|
||||||
|
clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
event.on('view.set', mode => {
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
|
||||||
|
// selection event (store position)
|
||||||
|
event.on('selection.rotate', (rec) => {
|
||||||
|
let type = "rotate";
|
||||||
|
let widgets = api.selection.widgets(true);
|
||||||
|
if (!widgets.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let {x, y, z} = rec;
|
||||||
|
if (!(x || y || z)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pushActions({
|
||||||
|
redo: {
|
||||||
|
type,
|
||||||
|
widgets,
|
||||||
|
x, y, z
|
||||||
|
},
|
||||||
|
undo: {
|
||||||
|
type,
|
||||||
|
widgets,
|
||||||
|
x: -x, y: -y, z: -z
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
event.on('selection.scale', (rec) => {
|
||||||
|
let [x, y, z] = rec;
|
||||||
|
// ignore scale close to 1
|
||||||
|
if (Math.abs(1 - (x * y * z)) < 0.0001) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let widgets = api.selection.widgets(true);
|
||||||
|
if (!widgets.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let type = "scale";
|
||||||
|
pushActions({
|
||||||
|
redo: {
|
||||||
|
type,
|
||||||
|
widgets,
|
||||||
|
x, y, z
|
||||||
|
},
|
||||||
|
undo: {
|
||||||
|
type,
|
||||||
|
widgets,
|
||||||
|
x: 1/x, y: 1/y, z: 1/z
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// selection moved (accumulator)
|
||||||
|
event.on('selection.drag', (delta) => {
|
||||||
|
moved.x += delta.x;
|
||||||
|
moved.y += delta.y;
|
||||||
|
});
|
||||||
|
|
||||||
|
// move complete (store updated position)
|
||||||
|
event.on('mouse.drag.done', () => {
|
||||||
|
let widgets = api.selection.widgets(true);
|
||||||
|
if (!widgets.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let type = "move";
|
||||||
|
pushActions({
|
||||||
|
redo: {
|
||||||
|
type,
|
||||||
|
widgets,
|
||||||
|
dist: moved
|
||||||
|
},
|
||||||
|
undo: {
|
||||||
|
type,
|
||||||
|
widgets,
|
||||||
|
dist: {x: -moved.x, y: -moved.y}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
moved = {x:0, y:0};
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
||||||
1296
src/kiri/export.js
1296
src/kiri/export.js
File diff suppressed because it is too large
Load diff
|
|
@ -2,221 +2,223 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function() {
|
// dep: main.kiri
|
||||||
|
// use: data.index
|
||||||
|
gapp.register("kiri.files", [], (root, exports) => {
|
||||||
|
|
||||||
const { kiri } = self;
|
const { kiri } = self;
|
||||||
|
|
||||||
class Files {
|
class Files {
|
||||||
constructor(indexdb, options) {
|
constructor(indexdb, options) {
|
||||||
let store = this;
|
let store = this;
|
||||||
this.db = indexdb;
|
this.db = indexdb;
|
||||||
this.files = {};
|
this.files = {};
|
||||||
this.listeners = [];
|
this.listeners = [];
|
||||||
this.options = options || {};
|
this.options = options || {};
|
||||||
this.deferredHandler = null;
|
this.deferredHandler = null;
|
||||||
this.concurrent = 0;
|
this.concurrent = 0;
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
|
||||||
|
|
||||||
setOptions(options) {
|
|
||||||
this.options = options;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
get index() {
|
|
||||||
return this.db;
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
let store = this;
|
|
||||||
store.db.get('files', function(files) {
|
|
||||||
if (files) {
|
|
||||||
store.files = files;
|
|
||||||
notifyFileListeners(store);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
wipe() {
|
|
||||||
let key, files = this.files;
|
|
||||||
for (key in files) {
|
|
||||||
if (files.hasOwnProperty(key)) this.deleteFile(key);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fileList() {
|
|
||||||
return this.files;
|
|
||||||
};
|
|
||||||
|
|
||||||
addFileListener(listener) {
|
|
||||||
if (!this.listeners.contains(listener)) {
|
|
||||||
this.listeners.push(listener);
|
|
||||||
listener(this.files);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
removeFileListener(listener) {
|
|
||||||
this.listeners.remove(listener);
|
|
||||||
};
|
|
||||||
|
|
||||||
decimate(vertices, callback) {
|
|
||||||
let options = this.options;
|
|
||||||
if (vertices.length < (options.threshold || 500000)) {
|
|
||||||
return callback(vertices);
|
|
||||||
}
|
|
||||||
kiri.client.decimate(vertices, this.options, function(reply) {
|
|
||||||
callback(reply);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
setDeferredHandler(handler) {
|
|
||||||
this.deferredHandler = handler;
|
|
||||||
};
|
|
||||||
|
|
||||||
putDeferred(name, mark) {
|
|
||||||
// triggers refresh callback
|
|
||||||
this.files[name] = {
|
|
||||||
deferred: mark
|
|
||||||
};
|
|
||||||
saveFileList(this);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {String} name
|
|
||||||
* @param {Float32Array} vertices
|
|
||||||
* @param {Function} [callback]
|
|
||||||
*/
|
|
||||||
putFile(name, vertices, callback) {
|
|
||||||
let mark = Date.now();
|
|
||||||
let store = this;
|
|
||||||
let pdb = store.db.promise();
|
|
||||||
store.concurrent++;
|
|
||||||
|
|
||||||
function ondone(error) {
|
|
||||||
if (callback) callback(error);
|
|
||||||
if (--store.concurrent === 0) {
|
|
||||||
notifyFileListeners(store);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pdb.put(`file-${name}`, vertices)
|
|
||||||
.then(() => {
|
|
||||||
store.files[name] = {
|
|
||||||
vertices: vertices.length / 3,
|
|
||||||
updated: new Date().getTime()
|
|
||||||
};
|
|
||||||
return pdb.put('files', store.files);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
return new Promise((resolve,reject) => {
|
|
||||||
store.decimate(vertices, resolve);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(decimated => {
|
|
||||||
return pdb.put(`fdec-${name}`, decimated)
|
|
||||||
})
|
|
||||||
.then(ondone)
|
|
||||||
.catch(ondone);
|
|
||||||
};
|
|
||||||
|
|
||||||
rename(name, newname, callback) {
|
|
||||||
if (!this.files[name]) return callback({error: 'no such file'});
|
|
||||||
if (!newname || newname == name) return callback({error: 'invalid new name'});
|
|
||||||
let done = 0;
|
|
||||||
let error = [];
|
|
||||||
let store = this;
|
|
||||||
function complete(ok, err) {
|
|
||||||
if (err) error.push(err);
|
|
||||||
if (++done === 2) {
|
|
||||||
store.files[newname] = store.files[name];
|
|
||||||
delete store.files[name];
|
|
||||||
saveFileList(store);
|
|
||||||
store.db.remove(`fdec-${name}`);
|
|
||||||
store.db.remove(`file-${name}`);
|
|
||||||
callback(error.length ? {error} : {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
store.db.get(`fdec-${name}`, (vertices) => {
|
|
||||||
if (!vertices) return complete(false, 'no decimation');
|
|
||||||
store.db.put(`fdec-${newname}`, vertices, complete);
|
|
||||||
});
|
|
||||||
store.db.get(`file-${name}`, (vertices) => {
|
|
||||||
if (!vertices) return complete(false, 'no raw file');
|
|
||||||
store.db.put(`file-${newname}`, vertices, complete);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {String} name
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
getFile(name, callback) {
|
|
||||||
let store = this,
|
|
||||||
rec = store.files[name];
|
|
||||||
if (rec && rec.deferred) {
|
|
||||||
if (store.deferredHandler) return store.deferredHandler(rec.deferred, name, callback);
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
this.db.get('fdec-'+name, function(vertices) {
|
|
||||||
if (vertices) {
|
|
||||||
callback(vertices);
|
|
||||||
} else {
|
|
||||||
store.db.get('file-'+name, function(vertices) {
|
|
||||||
if (vertices) {
|
|
||||||
store.decimate(vertices, function(decimated) {
|
|
||||||
store.db.put('fdec-'+name, decimated);
|
|
||||||
callback(vertices);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {String} name
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
deleteFile(name, callback) {
|
|
||||||
let store = this;
|
|
||||||
if (store.files[name]) {
|
|
||||||
delete store.files[name];
|
|
||||||
store.db.remove('fdec-'+name);
|
|
||||||
store.db.remove('file-'+name, function(ok) {
|
|
||||||
saveFileList(store);
|
|
||||||
if (callback) callback(ok);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (callback) callback(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteFilter(fn, from, to) {
|
|
||||||
this.db.keys(keys => {
|
|
||||||
for (let key of keys) {
|
|
||||||
if (fn(key)) {
|
|
||||||
this.db.remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, from, to);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveFileList(store) {
|
setOptions(options) {
|
||||||
store.db.put('files', store.files);
|
this.options = options;
|
||||||
notifyFileListeners(store);
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
function notifyFileListeners(store) {
|
get index() {
|
||||||
for (let i=0; i<store.listeners.length; i++) {
|
return this.db;
|
||||||
store.listeners[i](store.files);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
kiri.openFiles = function(indexdb, options) {
|
refresh() {
|
||||||
return new Files(indexdb, options);
|
let store = this;
|
||||||
|
store.db.get('files', function(files) {
|
||||||
|
if (files) {
|
||||||
|
store.files = files;
|
||||||
|
notifyFileListeners(store);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
wipe() {
|
||||||
|
let key, files = this.files;
|
||||||
|
for (key in files) {
|
||||||
|
if (files.hasOwnProperty(key)) this.deleteFile(key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fileList() {
|
||||||
|
return this.files;
|
||||||
|
};
|
||||||
|
|
||||||
|
addFileListener(listener) {
|
||||||
|
if (!this.listeners.contains(listener)) {
|
||||||
|
this.listeners.push(listener);
|
||||||
|
listener(this.files);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
removeFileListener(listener) {
|
||||||
|
this.listeners.remove(listener);
|
||||||
|
};
|
||||||
|
|
||||||
|
decimate(vertices, callback) {
|
||||||
|
let options = this.options;
|
||||||
|
if (vertices.length < (options.threshold || 500000)) {
|
||||||
|
return callback(vertices);
|
||||||
|
}
|
||||||
|
kiri.client.decimate(vertices, this.options, function(reply) {
|
||||||
|
callback(reply);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
setDeferredHandler(handler) {
|
||||||
|
this.deferredHandler = handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
putDeferred(name, mark) {
|
||||||
|
// triggers refresh callback
|
||||||
|
this.files[name] = {
|
||||||
|
deferred: mark
|
||||||
|
};
|
||||||
|
saveFileList(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String} name
|
||||||
|
* @param {Float32Array} vertices
|
||||||
|
* @param {Function} [callback]
|
||||||
|
*/
|
||||||
|
putFile(name, vertices, callback) {
|
||||||
|
let mark = Date.now();
|
||||||
|
let store = this;
|
||||||
|
let pdb = store.db.promise();
|
||||||
|
store.concurrent++;
|
||||||
|
|
||||||
|
function ondone(error) {
|
||||||
|
if (callback) callback(error);
|
||||||
|
if (--store.concurrent === 0) {
|
||||||
|
notifyFileListeners(store);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pdb.put(`file-${name}`, vertices)
|
||||||
|
.then(() => {
|
||||||
|
store.files[name] = {
|
||||||
|
vertices: vertices.length / 3,
|
||||||
|
updated: new Date().getTime()
|
||||||
|
};
|
||||||
|
return pdb.put('files', store.files);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return new Promise((resolve,reject) => {
|
||||||
|
store.decimate(vertices, resolve);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(decimated => {
|
||||||
|
return pdb.put(`fdec-${name}`, decimated)
|
||||||
|
})
|
||||||
|
.then(ondone)
|
||||||
|
.catch(ondone);
|
||||||
|
};
|
||||||
|
|
||||||
|
rename(name, newname, callback) {
|
||||||
|
if (!this.files[name]) return callback({error: 'no such file'});
|
||||||
|
if (!newname || newname == name) return callback({error: 'invalid new name'});
|
||||||
|
let done = 0;
|
||||||
|
let error = [];
|
||||||
|
let store = this;
|
||||||
|
function complete(ok, err) {
|
||||||
|
if (err) error.push(err);
|
||||||
|
if (++done === 2) {
|
||||||
|
store.files[newname] = store.files[name];
|
||||||
|
delete store.files[name];
|
||||||
|
saveFileList(store);
|
||||||
|
store.db.remove(`fdec-${name}`);
|
||||||
|
store.db.remove(`file-${name}`);
|
||||||
|
callback(error.length ? {error} : {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
store.db.get(`fdec-${name}`, (vertices) => {
|
||||||
|
if (!vertices) return complete(false, 'no decimation');
|
||||||
|
store.db.put(`fdec-${newname}`, vertices, complete);
|
||||||
|
});
|
||||||
|
store.db.get(`file-${name}`, (vertices) => {
|
||||||
|
if (!vertices) return complete(false, 'no raw file');
|
||||||
|
store.db.put(`file-${newname}`, vertices, complete);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String} name
|
||||||
|
* @param {Function} callback
|
||||||
|
*/
|
||||||
|
getFile(name, callback) {
|
||||||
|
let store = this,
|
||||||
|
rec = store.files[name];
|
||||||
|
if (rec && rec.deferred) {
|
||||||
|
if (store.deferredHandler) return store.deferredHandler(rec.deferred, name, callback);
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
this.db.get('fdec-'+name, function(vertices) {
|
||||||
|
if (vertices) {
|
||||||
|
callback(vertices);
|
||||||
|
} else {
|
||||||
|
store.db.get('file-'+name, function(vertices) {
|
||||||
|
if (vertices) {
|
||||||
|
store.decimate(vertices, function(decimated) {
|
||||||
|
store.db.put('fdec-'+name, decimated);
|
||||||
|
callback(vertices);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String} name
|
||||||
|
* @param {Function} callback
|
||||||
|
*/
|
||||||
|
deleteFile(name, callback) {
|
||||||
|
let store = this;
|
||||||
|
if (store.files[name]) {
|
||||||
|
delete store.files[name];
|
||||||
|
store.db.remove('fdec-'+name);
|
||||||
|
store.db.remove('file-'+name, function(ok) {
|
||||||
|
saveFileList(store);
|
||||||
|
if (callback) callback(ok);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (callback) callback(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
deleteFilter(fn, from, to) {
|
||||||
|
this.db.keys(keys => {
|
||||||
|
for (let key of keys) {
|
||||||
|
if (fn(key)) {
|
||||||
|
this.db.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, from, to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveFileList(store) {
|
||||||
|
store.db.put('files', store.files);
|
||||||
|
notifyFileListeners(store);
|
||||||
|
}
|
||||||
|
|
||||||
|
function notifyFileListeners(store) {
|
||||||
|
for (let i=0; i<store.listeners.length; i++) {
|
||||||
|
store.listeners[i](store.files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kiri.openFiles = function(indexdb, options) {
|
||||||
|
return new Files(indexdb, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,14 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function () {
|
// use: load.stl
|
||||||
|
// use: load.svg
|
||||||
|
// use: kiri.api
|
||||||
|
// use: kiri.feature
|
||||||
|
// use: kiri.platform
|
||||||
|
// use: kiri.settings
|
||||||
|
// use: kiri.widget
|
||||||
|
gapp.register("kiri.frame", [], (root, exports) => {
|
||||||
|
|
||||||
// add frame message api listener
|
// add frame message api listener
|
||||||
window.addEventListener('message', msg => {
|
window.addEventListener('message', msg => {
|
||||||
|
|
@ -113,4 +120,4 @@ window.addEventListener('message', msg => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
})();
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function () {
|
// dep: kiri.api
|
||||||
|
// dep: moto.space
|
||||||
|
gapp.register("kiri.function", [], (root, exports) => {
|
||||||
|
|
||||||
const { data, kiri, noop } = self;
|
const { kiri, noop } = root;
|
||||||
const { api, client, consts, utils } = kiri;
|
const { api, client, consts, utils } = kiri;
|
||||||
const { space } = moto;
|
const { space } = moto;
|
||||||
const { COLOR, PMODES } = consts;
|
const { COLOR, PMODES } = consts;
|
||||||
|
|
@ -59,6 +61,11 @@ function prepareSlices(callback, scale = 1, offset = 0) {
|
||||||
const slicing = api.widgets.all()
|
const slicing = api.widgets.all()
|
||||||
.slice()
|
.slice()
|
||||||
.filter(w => !w.track.ignore && !w.meta.disabled);
|
.filter(w => !w.track.ignore && !w.meta.disabled);
|
||||||
|
|
||||||
|
if (slicing.length === 0) {
|
||||||
|
return api.show.alert('nothing to slice');
|
||||||
|
}
|
||||||
|
|
||||||
const totalv = slicing.map(w => w.getVertices().count).reduce((a,v) => a + v);
|
const totalv = slicing.map(w => w.getVertices().count).reduce((a,v) => a + v);
|
||||||
const defvert = totalv / slicing.length;
|
const defvert = totalv / slicing.length;
|
||||||
const track = {};
|
const track = {};
|
||||||
|
|
@ -413,4 +420,4 @@ const functions = api.function = {
|
||||||
clear_progress() { complete = {} }
|
clear_progress() { complete = {} }
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,15 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function() {
|
// dep: geo.base
|
||||||
|
// dep: kiri.api
|
||||||
|
// dep: kiri.stats
|
||||||
|
// dep: kiri.consts
|
||||||
|
// dep: kiri.platform
|
||||||
|
// dep: kiri.selection
|
||||||
|
gapp.register("kiri.init", [], (root, exports) => {
|
||||||
|
|
||||||
if (self.kiri.init) return;
|
const { base, kiri } = root;
|
||||||
|
|
||||||
const { base, kiri, moto } = self;
|
|
||||||
const { api, catalog, conf, consts, space } = kiri;
|
const { api, catalog, conf, consts, space } = kiri;
|
||||||
const { sdb, stats, js2o, o2js, platform, selection, ui, uc } = api;
|
const { sdb, stats, js2o, o2js, platform, selection, ui, uc } = api;
|
||||||
const { VIEWS, MODES, SEED } = consts;
|
const { VIEWS, MODES, SEED } = consts;
|
||||||
|
|
@ -2287,11 +2291,9 @@
|
||||||
uc.hoverPop(ui.ltact, { group: hpops, target: $('pop-slice') });
|
uc.hoverPop(ui.ltact, { group: hpops, target: $('pop-slice') });
|
||||||
uc.hoverPop(ui.render, { group: hpops, target: $('pop-render'), sticky: false });
|
uc.hoverPop(ui.render, { group: hpops, target: $('pop-render'), sticky: false });
|
||||||
uc.hoverPop(ui.edit, { group: hpops, target: $('pop-tools'), sticky: false });
|
uc.hoverPop(ui.edit, { group: hpops, target: $('pop-tools'), sticky: false });
|
||||||
// uc.hoverPop(ui.rotate, { group: hpops, target: $('pop-rotate'), sticky: true });
|
|
||||||
// uc.hoverPop(ui.scale, { group: hpops, target: $('pop-scale'), sticky: true });
|
|
||||||
uc.hoverPop(ui.nozzle, { group: hpops, target: $('pop-nozzle'), sticky: true });
|
uc.hoverPop(ui.nozzle, { group: hpops, target: $('pop-nozzle'), sticky: true });
|
||||||
uc.hoverPop($('app-acct'), { group: hpops, target: $('acct-pop') } );
|
uc.hoverPop($('app-acct'), { group: hpops, target: $('acct-pop') } );
|
||||||
uc.hoverPop($('app-mode'), { group: hpops, target: $('mode-info') } );
|
// uc.hoverPop($('app-mode'), { group: hpops, target: $('mode-info') } );
|
||||||
uc.hoverPop($('app-name'), { group: hpops, target: $('app-info') } );
|
uc.hoverPop($('app-name'), { group: hpops, target: $('app-info') } );
|
||||||
|
|
||||||
uc.onBlur([
|
uc.onBlur([
|
||||||
|
|
@ -2925,4 +2927,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
});
|
||||||
|
|
|
||||||
143
src/kiri/lang.js
143
src/kiri/lang.js
|
|
@ -2,81 +2,80 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function() {
|
gapp.register("kiri.lang", [], (root, exports) => {
|
||||||
|
|
||||||
if (self.kiri.lang) return;
|
const { kiri } = root;
|
||||||
|
const LANG = kiri.lang = { current: {} };
|
||||||
|
const KDFL = 'en-us';
|
||||||
|
|
||||||
const KIRI = self.kiri, LANG = self.kiri.lang = { current: {} };
|
let lset = navigator.language.toLocaleLowerCase();
|
||||||
const KDFL = 'en-us';
|
|
||||||
|
|
||||||
let lset = navigator.language.toLocaleLowerCase();
|
LANG.map = function(key) {
|
||||||
|
if (!key) {
|
||||||
LANG.map = function(key) {
|
|
||||||
if (!key) {
|
|
||||||
return KDFL;
|
|
||||||
}
|
|
||||||
let tok = key.split('-');
|
|
||||||
switch (tok[0]) {
|
|
||||||
case 'da': return 'da-dk';
|
|
||||||
case 'de': return 'de-de';
|
|
||||||
case 'en': return 'en-us';
|
|
||||||
case 'es': return 'es-es';
|
|
||||||
case 'fr': return 'fr-fr';
|
|
||||||
case 'pl': return 'pl-pl';
|
|
||||||
case 'pt': return 'pt-pt';
|
|
||||||
case 'zh': return 'zh';
|
|
||||||
}
|
|
||||||
return KDFL;
|
return KDFL;
|
||||||
};
|
|
||||||
|
|
||||||
LANG.get = function() {
|
|
||||||
return lset;
|
|
||||||
};
|
|
||||||
|
|
||||||
LANG.set = function() {
|
|
||||||
let map, key, keys = [...arguments];
|
|
||||||
// provide default if none given
|
|
||||||
if (keys.length === 0) {
|
|
||||||
keys = [lset, lset.split('-')[0], 'en'];
|
|
||||||
}
|
|
||||||
for (let i=0; i<keys.length; i++)
|
|
||||||
{
|
|
||||||
key = keys[i]
|
|
||||||
if (!(map = LANG[key])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
lset = key;
|
|
||||||
let missing = [];
|
|
||||||
let invalid = [];
|
|
||||||
// default to EN values when missing
|
|
||||||
Object.keys(LANG.en).forEach(key => {
|
|
||||||
if (!map[key]) {
|
|
||||||
map[key] = LANG.en[key];
|
|
||||||
missing.push(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// update current map from chosen map
|
|
||||||
Object.keys(map).forEach(key => {
|
|
||||||
if (LANG.en[key]) {
|
|
||||||
let val = map[key];
|
|
||||||
// turn arrays into newline separated strings
|
|
||||||
if (Array.isArray(val)) {
|
|
||||||
val = val.join('\n');
|
|
||||||
}
|
|
||||||
LANG.current[key] = val;
|
|
||||||
} else {
|
|
||||||
invalid.push(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (missing.length) {
|
|
||||||
console.log(`language map "${key}" missing keys [${missing.length}]: ${missing.join(', ')}`);
|
|
||||||
}
|
|
||||||
if (invalid.length) {
|
|
||||||
console.log(`language map "${key}" invalid keys [${invalid.length}]: ${invalid.join(', ')}`);
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
let tok = key.split('-');
|
||||||
|
switch (tok[0]) {
|
||||||
|
case 'da': return 'da-dk';
|
||||||
|
case 'de': return 'de-de';
|
||||||
|
case 'en': return 'en-us';
|
||||||
|
case 'es': return 'es-es';
|
||||||
|
case 'fr': return 'fr-fr';
|
||||||
|
case 'pl': return 'pl-pl';
|
||||||
|
case 'pt': return 'pt-pt';
|
||||||
|
case 'zh': return 'zh';
|
||||||
|
}
|
||||||
|
return KDFL;
|
||||||
|
};
|
||||||
|
|
||||||
})();
|
LANG.get = function() {
|
||||||
|
return lset;
|
||||||
|
};
|
||||||
|
|
||||||
|
LANG.set = function() {
|
||||||
|
let map, key, keys = [...arguments];
|
||||||
|
// provide default if none given
|
||||||
|
if (keys.length === 0) {
|
||||||
|
keys = [lset, lset.split('-')[0], 'en'];
|
||||||
|
}
|
||||||
|
for (let i=0; i<keys.length; i++)
|
||||||
|
{
|
||||||
|
key = keys[i]
|
||||||
|
if (!(map = LANG[key])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
lset = key;
|
||||||
|
let missing = [];
|
||||||
|
let invalid = [];
|
||||||
|
// default to EN values when missing
|
||||||
|
Object.keys(LANG.en).forEach(key => {
|
||||||
|
if (!map[key]) {
|
||||||
|
map[key] = LANG.en[key];
|
||||||
|
missing.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// update current map from chosen map
|
||||||
|
Object.keys(map).forEach(key => {
|
||||||
|
if (LANG.en[key]) {
|
||||||
|
let val = map[key];
|
||||||
|
// turn arrays into newline separated strings
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
val = val.join('\n');
|
||||||
|
}
|
||||||
|
LANG.current[key] = val;
|
||||||
|
} else {
|
||||||
|
invalid.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (missing.length) {
|
||||||
|
console.log(`language map "${key}" missing keys [${missing.length}]: ${missing.join(', ')}`);
|
||||||
|
}
|
||||||
|
if (invalid.length) {
|
||||||
|
console.log(`language map "${key}" invalid keys [${invalid.length}]: ${invalid.join(', ')}`);
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,354 +2,359 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function() {
|
// dep: geo.base
|
||||||
let KIRI = self.kiri,
|
// dep: geo.polygon
|
||||||
BASE = self.base,
|
// dep: geo.polygons
|
||||||
POLY = BASE.polygons,
|
gapp.register("kiri.layers", [], (root, exports) => {
|
||||||
newPolygon = BASE.newPolygon;
|
|
||||||
|
|
||||||
class Layers {
|
const { base, kiri } = root;
|
||||||
constructor() {
|
const { polygons, newPolygon } = base;
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
const POLY = base.polygons;
|
||||||
this.layers = {};
|
|
||||||
this.profiles = {};
|
class Layers {
|
||||||
this.stats = {
|
constructor() {
|
||||||
contour: 0,
|
this.init();
|
||||||
flat_line: 0,
|
}
|
||||||
flat_poly: 0,
|
|
||||||
line_poly: 0,
|
init() {
|
||||||
line: 0
|
this.layers = {};
|
||||||
|
this.profiles = {};
|
||||||
|
this.stats = {
|
||||||
|
contour: 0,
|
||||||
|
flat_line: 0,
|
||||||
|
flat_poly: 0,
|
||||||
|
line_poly: 0,
|
||||||
|
line: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getLayer(layer) {
|
||||||
|
return this.layers[layer];
|
||||||
|
}
|
||||||
|
|
||||||
|
setLayer(layer, colors, off) {
|
||||||
|
let layers = this.layers;
|
||||||
|
if (typeof(colors) === 'number') {
|
||||||
|
colors = {
|
||||||
|
line: colors,
|
||||||
|
face: colors,
|
||||||
|
opacity: 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
this.current = layers[layer] = layers[layer] || {
|
||||||
|
off: off === true,
|
||||||
|
lines: [],
|
||||||
|
polys: [], // colors are an attribute on polygons
|
||||||
|
faces: [],
|
||||||
|
cface: undefined, // face color indices
|
||||||
|
paths: [],
|
||||||
|
cpath: undefined, // path colors indices
|
||||||
|
color: colors || {
|
||||||
|
check: 0,
|
||||||
|
line: 0,
|
||||||
|
face: 0,
|
||||||
|
opacity: 1
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
getLayer(layer) {
|
addLine(p1, p2) {
|
||||||
return this.layers[layer];
|
this.current.lines.push(p1, p2);
|
||||||
}
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
setLayer(layer, colors, off) {
|
addLines(lines, options) {
|
||||||
let layers = this.layers;
|
if (options) {
|
||||||
if (typeof(colors) === 'number') {
|
options.open = true;
|
||||||
colors = {
|
const polys = [];
|
||||||
line: colors,
|
|
||||||
face: colors,
|
|
||||||
opacity: 1
|
|
||||||
};
|
|
||||||
}
|
|
||||||
this.current = layers[layer] = layers[layer] || {
|
|
||||||
off: off === true,
|
|
||||||
lines: [],
|
|
||||||
polys: [], // colors are an attribute on polygons
|
|
||||||
faces: [],
|
|
||||||
cface: undefined, // face color indices
|
|
||||||
paths: [],
|
|
||||||
cpath: undefined, // path colors indices
|
|
||||||
color: colors || {
|
|
||||||
check: 0,
|
|
||||||
line: 0,
|
|
||||||
face: 0,
|
|
||||||
opacity: 1
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
addLine(p1, p2) {
|
|
||||||
this.current.lines.push(p1, p2);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
addLines(lines, options) {
|
|
||||||
if (options) {
|
|
||||||
options.open = true;
|
|
||||||
const polys = [];
|
|
||||||
for (let i=0; i<lines.length-1; i += 2) {
|
|
||||||
polys.push(new BASE.Polygon()
|
|
||||||
.append(lines[i])
|
|
||||||
.append(lines[i+1])
|
|
||||||
.setOpen());
|
|
||||||
}
|
|
||||||
return this.addPolys(polys, options);
|
|
||||||
}
|
|
||||||
for (let i=0; i<lines.length-1; i += 2) {
|
for (let i=0; i<lines.length-1; i += 2) {
|
||||||
this.addLine(lines[i], lines[i+1]);
|
polys.push(new base.Polygon()
|
||||||
this.stats.line++;
|
.append(lines[i])
|
||||||
|
.append(lines[i+1])
|
||||||
|
.setOpen());
|
||||||
}
|
}
|
||||||
|
return this.addPolys(polys, options);
|
||||||
|
}
|
||||||
|
for (let i=0; i<lines.length-1; i += 2) {
|
||||||
|
this.addLine(lines[i], lines[i+1]);
|
||||||
|
this.stats.line++;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
addPoly(poly, options) {
|
||||||
|
return this.addPolys([poly], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
addPolys(polys, options) {
|
||||||
|
if (polys.length === 0) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
if (options && options.clean) {
|
||||||
addPoly(poly, options) {
|
polys = polys.map(p => p.clean(true));
|
||||||
return this.addPolys([poly], options);
|
|
||||||
}
|
}
|
||||||
|
if (options && options.flat) {
|
||||||
addPolys(polys, options) {
|
return this.addFlats(polys, options);
|
||||||
if (polys.length === 0) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
if (options && options.clean) {
|
|
||||||
polys = polys.map(p => p.clean(true));
|
|
||||||
}
|
|
||||||
if (options && options.flat) {
|
|
||||||
return this.addFlats(polys, options);
|
|
||||||
}
|
|
||||||
if (options && !options.thin) {
|
|
||||||
return this.addPaths(polys, options);
|
|
||||||
}
|
|
||||||
polys = flat(polys);
|
|
||||||
if (options) {
|
|
||||||
polys.forEach(p => {
|
|
||||||
if (options.z !== undefined) {
|
|
||||||
p.setZ(options.z);
|
|
||||||
}
|
|
||||||
switch (typeof(options.color)) {
|
|
||||||
case 'number': p.color = options.color; break;
|
|
||||||
case 'object': p.color = options.color.line; break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.current.polys.appendAll(polys);
|
|
||||||
this.stats.line_poly += polys.length;
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
if (options && !options.thin) {
|
||||||
|
return this.addPaths(polys, options);
|
||||||
|
}
|
||||||
|
polys = flat(polys);
|
||||||
|
if (options) {
|
||||||
|
polys.forEach(p => {
|
||||||
|
if (options.z !== undefined) {
|
||||||
|
p.setZ(options.z);
|
||||||
|
}
|
||||||
|
switch (typeof(options.color)) {
|
||||||
|
case 'number': p.color = options.color; break;
|
||||||
|
case 'object': p.color = options.color.line; break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.current.polys.appendAll(polys);
|
||||||
|
this.stats.line_poly += polys.length;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
// z planar closed polygonal areas
|
// z planar closed polygonal areas
|
||||||
addAreas(polys, options) {
|
addAreas(polys, options) {
|
||||||
const faces = this.current.faces;
|
const faces = this.current.faces;
|
||||||
polys = Array.isArray(polys) ? polys : [ polys ];
|
polys = Array.isArray(polys) ? polys : [ polys ];
|
||||||
polys.forEach(poly => {
|
polys.forEach(poly => {
|
||||||
|
poly.earcut().forEach(ep => {
|
||||||
|
ep.forEachPoint(p => { faces.push(p.x, p.y, p.z) });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (options && options.outline) {
|
||||||
|
this.addPolys(polys.clone(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// misleading name. these are flat offset paths (old style rendering)
|
||||||
|
addFlats(polys, options) {
|
||||||
|
const opts = options || {};
|
||||||
|
const offset = opts.offset || 1;
|
||||||
|
polys = flat(polys);
|
||||||
|
if (!polys.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const z = polys[0].getZ(), faces = this.current.faces;
|
||||||
|
const off_opt = { z, flat: true };
|
||||||
|
polys.forEach(poly => {
|
||||||
|
let exp = off_opt.outs = [];
|
||||||
|
if (poly.isOpen()) {
|
||||||
|
// shorten line by offset
|
||||||
|
const pp = poly.points, pl = pp.length;
|
||||||
|
pp[0] = pp[0].offsetPointTo(pp[1], offset);
|
||||||
|
pp[pl-1] = pp[pl-1].offsetPointTo(pp[pl-2], offset);
|
||||||
|
exp.appendAll(POLY.expand_lines(poly, offset * 0.9, z));
|
||||||
|
// force re-nest if offsets cause ends to join
|
||||||
|
exp = POLY.flatten(exp,[],true);
|
||||||
|
this.stats.flat_line = 0;
|
||||||
|
} else if (offset) {
|
||||||
|
POLY.offset([poly], offset * 0.9, off_opt);
|
||||||
|
POLY.offset([poly], -offset * 0.9, off_opt);
|
||||||
|
this.stats.flat_poly = 0;
|
||||||
|
}
|
||||||
|
if (opts.outline) {
|
||||||
|
this.addPolys(exp.clone());
|
||||||
|
}
|
||||||
|
const faceidx = faces.length / 3;
|
||||||
|
POLY.nest(exp).forEach((poly,i) => {
|
||||||
poly.earcut().forEach(ep => {
|
poly.earcut().forEach(ep => {
|
||||||
ep.forEachPoint(p => { faces.push(p.x, p.y, p.z) });
|
ep.forEachPoint(p => { faces.push(p.x, p.y, p.z) });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if (options && options.outline) {
|
const cur = this.current;
|
||||||
this.addPolys(polys.clone(true));
|
const color = opts.color ?
|
||||||
|
(typeof(opts.color) === 'number' ? { line: opts.color, face: opts.color } : opts.color) :
|
||||||
|
cur.color;
|
||||||
|
if (!cur.cface) {
|
||||||
|
cur.cface = [ Object.assign({ start: faceidx, count: Infinity }, color) ];
|
||||||
|
} else {
|
||||||
|
// rewrite last color count if color or opacity have changed
|
||||||
|
const pc = cur.cface[cur.cface.length - 1];
|
||||||
|
if (pc.face !== color.face || pc.opacity !== color.opacity) {
|
||||||
|
pc.count = faceidx;
|
||||||
|
cur.cface.push(Object.assign({ start: faceidx, count: Infinity }, color));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
addPaths(polys, options) {
|
||||||
|
const opts = options || {};
|
||||||
|
const height = opts.height || 1;
|
||||||
|
const offset = opts.offset || 1;
|
||||||
|
polys = flat(polys);
|
||||||
|
if (!polys.length) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// misleading name. these are flat offset paths (old style rendering)
|
const profiles = this.profiles;
|
||||||
addFlats(polys, options) {
|
const prokey = `${offset}x${height}`;
|
||||||
const opts = options || {};
|
if (!profiles[prokey]) {
|
||||||
const offset = opts.offset || 1;
|
const profile = new THREE.Shape();
|
||||||
polys = flat(polys);
|
profile.moveTo(-offset, -height);
|
||||||
if (!polys.length) {
|
profile.lineTo(-offset, height);
|
||||||
return;
|
profile.lineTo( offset, height);
|
||||||
}
|
profile.lineTo( offset, -height);
|
||||||
const z = polys[0].getZ(), faces = this.current.faces;
|
profiles[prokey] = profile;
|
||||||
const off_opt = { z, flat: true };
|
}
|
||||||
polys.forEach(poly => {
|
const profile = profiles[prokey].clone();
|
||||||
let exp = off_opt.outs = [];
|
|
||||||
if (poly.isOpen()) {
|
polys.forEach(poly => {
|
||||||
// shorten line by offset
|
const contour = [];
|
||||||
const pp = poly.points, pl = pp.length;
|
poly = poly.debur(0.05);
|
||||||
pp[0] = pp[0].offsetPointTo(pp[1], offset);
|
if (!poly) return;
|
||||||
pp[pl-1] = pp[pl-1].offsetPointTo(pp[pl-2], offset);
|
poly = poly.miter();
|
||||||
exp.appendAll(POLY.expand_lines(poly, offset * 0.9, z));
|
poly.points.forEach(p => {
|
||||||
// force re-nest if offsets cause ends to join
|
contour.push(new THREE.Vector2(p.x, p.y));
|
||||||
exp = POLY.flatten(exp,[],true);
|
});
|
||||||
this.stats.flat_line = 0;
|
const {index, faces} = ProfiledContourGeometry(profile, contour, poly.isClosed());
|
||||||
} else if (offset) {
|
const cur = this.current;
|
||||||
POLY.offset([poly], offset * 0.9, off_opt);
|
const one = cur.paths[0];
|
||||||
POLY.offset([poly], -offset * 0.9, off_opt);
|
if (one) {
|
||||||
this.stats.flat_poly = 0;
|
// merge all contour geometry for massive speed gain
|
||||||
|
const add = one.faces.length / 3;
|
||||||
|
for (let i=0; i<index.length; i++) {
|
||||||
|
index[i] += add;
|
||||||
}
|
}
|
||||||
if (opts.outline) {
|
const feces = new Float32Array(one.faces.length + faces.length);
|
||||||
this.addPolys(exp.clone());
|
const indln = one.index.length;
|
||||||
}
|
feces.set(one.faces);
|
||||||
const faceidx = faces.length / 3;
|
feces.set(faces, one.faces.length);
|
||||||
POLY.nest(exp).forEach((poly,i) => {
|
one.faces = feces;
|
||||||
poly.earcut().forEach(ep => {
|
one.index.appendAll(index);
|
||||||
ep.forEachPoint(p => { faces.push(p.x, p.y, p.z) });
|
// allow changing colors
|
||||||
});
|
if (opts.color) {
|
||||||
});
|
if (!cur.cpath) {
|
||||||
const cur = this.current;
|
cur.cpath = [ Object.assign({ start: 0, count: indln - 1 }, cur.color) ];
|
||||||
const color = opts.color ?
|
}
|
||||||
(typeof(opts.color) === 'number' ? { line: opts.color, face: opts.color } : opts.color) :
|
|
||||||
cur.color;
|
|
||||||
if (!cur.cface) {
|
|
||||||
cur.cface = [ Object.assign({ start: faceidx, count: Infinity }, color) ];
|
|
||||||
} else {
|
|
||||||
// rewrite last color count if color or opacity have changed
|
// rewrite last color count if color or opacity have changed
|
||||||
const pc = cur.cface[cur.cface.length - 1];
|
const pc = cur.cpath[cur.cpath.length - 1];
|
||||||
if (pc.face !== color.face || pc.opacity !== color.opacity) {
|
if (pc.face !== opts.color.face || pc.opacity !== opts.color.opacity) {
|
||||||
pc.count = faceidx;
|
pc.count = indln;
|
||||||
cur.cface.push(Object.assign({ start: faceidx, count: Infinity }, color));
|
cur.cpath.push(Object.assign({ start: indln, count: Infinity }, opts.color));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
return this;
|
cur.paths.push({ index, faces, z: opts.z || poly.getZ() });
|
||||||
}
|
if (opts.color) {
|
||||||
|
cur.cpath = [ Object.assign({ start: 0, count: Infinity }, opts.color) ];
|
||||||
addPaths(polys, options) {
|
|
||||||
const opts = options || {};
|
|
||||||
const height = opts.height || 1;
|
|
||||||
const offset = opts.offset || 1;
|
|
||||||
polys = flat(polys);
|
|
||||||
if (!polys.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const profiles = this.profiles;
|
|
||||||
const prokey = `${offset}x${height}`;
|
|
||||||
if (!profiles[prokey]) {
|
|
||||||
const profile = new THREE.Shape();
|
|
||||||
profile.moveTo(-offset, -height);
|
|
||||||
profile.lineTo(-offset, height);
|
|
||||||
profile.lineTo( offset, height);
|
|
||||||
profile.lineTo( offset, -height);
|
|
||||||
profiles[prokey] = profile;
|
|
||||||
}
|
|
||||||
const profile = profiles[prokey].clone();
|
|
||||||
|
|
||||||
polys.forEach(poly => {
|
|
||||||
const contour = [];
|
|
||||||
poly = poly.debur(0.05);
|
|
||||||
if (!poly) return;
|
|
||||||
poly = poly.miter();
|
|
||||||
poly.points.forEach(p => {
|
|
||||||
contour.push(new THREE.Vector2(p.x, p.y));
|
|
||||||
});
|
|
||||||
const {index, faces} = ProfiledContourGeometry(profile, contour, poly.isClosed());
|
|
||||||
const cur = this.current;
|
|
||||||
const one = cur.paths[0];
|
|
||||||
if (one) {
|
|
||||||
// merge all contour geometry for massive speed gain
|
|
||||||
const add = one.faces.length / 3;
|
|
||||||
for (let i=0; i<index.length; i++) {
|
|
||||||
index[i] += add;
|
|
||||||
}
|
|
||||||
const feces = new Float32Array(one.faces.length + faces.length);
|
|
||||||
const indln = one.index.length;
|
|
||||||
feces.set(one.faces);
|
|
||||||
feces.set(faces, one.faces.length);
|
|
||||||
one.faces = feces;
|
|
||||||
one.index.appendAll(index);
|
|
||||||
// allow changing colors
|
|
||||||
if (opts.color) {
|
|
||||||
if (!cur.cpath) {
|
|
||||||
cur.cpath = [ Object.assign({ start: 0, count: indln - 1 }, cur.color) ];
|
|
||||||
}
|
|
||||||
// rewrite last color count if color or opacity have changed
|
|
||||||
const pc = cur.cpath[cur.cpath.length - 1];
|
|
||||||
if (pc.face !== opts.color.face || pc.opacity !== opts.color.opacity) {
|
|
||||||
pc.count = indln;
|
|
||||||
cur.cpath.push(Object.assign({ start: indln, count: Infinity }, opts.color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cur.paths.push({ index, faces, z: opts.z || poly.getZ() });
|
|
||||||
if (opts.color) {
|
|
||||||
cur.cpath = [ Object.assign({ start: 0, count: Infinity }, opts.color) ];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.stats.contour++;
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function flat(polys) {
|
|
||||||
if (Array.isArray(polys)) {
|
|
||||||
return POLY.flatten(polys.clone(true), [], true);
|
|
||||||
} else {
|
|
||||||
return POLY.flatten([polys.clone(true)], [], true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ProfiledContourGeometry(profileShape, contour, contourClosed) {
|
|
||||||
|
|
||||||
contourClosed = contourClosed !== undefined ? contourClosed : true;
|
|
||||||
|
|
||||||
const profileGeometry = new THREE.ShapeBufferGeometry(profileShape);
|
|
||||||
profileGeometry.rotateX(Math.PI * .5);
|
|
||||||
|
|
||||||
const profile = profileGeometry.attributes.position;
|
|
||||||
const faces = new Float32Array(profile.count * contour.length * 3);
|
|
||||||
|
|
||||||
for (let i = 0; i < contour.length; i++) {
|
|
||||||
const v1 = new THREE.Vector2().subVectors(contour[i - 1 < 0 ? contour.length - 1 : i - 1], contour[i]);
|
|
||||||
const v2 = new THREE.Vector2().subVectors(contour[i + 1 == contour.length ? 0 : i + 1], contour[i]);
|
|
||||||
const angle = v2.angle() - v1.angle();
|
|
||||||
const halfAngle = angle * .5;
|
|
||||||
let hA = halfAngle;
|
|
||||||
let tA = v2.angle() + Math.PI * .5;
|
|
||||||
|
|
||||||
if (!contourClosed){
|
|
||||||
if (i == 0 || i == contour.length - 1) {hA = Math.PI * .5;}
|
|
||||||
if (i == contour.length - 1) {tA = v1.angle() - Math.PI * .5;}
|
|
||||||
}
|
}
|
||||||
|
this.stats.contour++;
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const shift = Math.tan(hA - Math.PI * .5);
|
function flat(polys) {
|
||||||
const shiftMatrix = new THREE.Matrix4().set(
|
if (Array.isArray(polys)) {
|
||||||
1, 0, 0, 0,
|
return POLY.flatten(polys.clone(true), [], true);
|
||||||
-shift, 1, 0, 0,
|
} else {
|
||||||
0, 0, 1, 0,
|
return POLY.flatten([polys.clone(true)], [], true);
|
||||||
0, 0, 0, 1
|
}
|
||||||
);
|
}
|
||||||
|
|
||||||
const tempAngle = tA;
|
function ProfiledContourGeometry(profileShape, contour, contourClosed) {
|
||||||
const rotationMatrix = new THREE.Matrix4().set(
|
|
||||||
Math.cos(tempAngle), -Math.sin(tempAngle), 0, 0,
|
|
||||||
Math.sin(tempAngle), Math.cos(tempAngle), 0, 0,
|
|
||||||
0, 0, 1, 0,
|
|
||||||
0, 0, 0, 1
|
|
||||||
);
|
|
||||||
|
|
||||||
const translationMatrix = new THREE.Matrix4().set(
|
contourClosed = contourClosed !== undefined ? contourClosed : true;
|
||||||
1, 0, 0, contour[i].x,
|
|
||||||
0, 1, 0, contour[i].y,
|
|
||||||
0, 0, 1, 0,
|
|
||||||
0, 0, 0, 1,
|
|
||||||
);
|
|
||||||
|
|
||||||
const cloneProfile = profile.clone();
|
const profileGeometry = new THREE.ShapeBufferGeometry(profileShape);
|
||||||
cloneProfile.applyMatrix4(shiftMatrix);
|
profileGeometry.rotateX(Math.PI * .5);
|
||||||
cloneProfile.applyMatrix4(rotationMatrix);
|
|
||||||
cloneProfile.applyMatrix4(translationMatrix);
|
|
||||||
|
|
||||||
faces.set(cloneProfile.array, cloneProfile.count * i * 3);
|
const profile = profileGeometry.attributes.position;
|
||||||
|
const faces = new Float32Array(profile.count * contour.length * 3);
|
||||||
|
|
||||||
|
for (let i = 0; i < contour.length; i++) {
|
||||||
|
const v1 = new THREE.Vector2().subVectors(contour[i - 1 < 0 ? contour.length - 1 : i - 1], contour[i]);
|
||||||
|
const v2 = new THREE.Vector2().subVectors(contour[i + 1 == contour.length ? 0 : i + 1], contour[i]);
|
||||||
|
const angle = v2.angle() - v1.angle();
|
||||||
|
const halfAngle = angle * .5;
|
||||||
|
let hA = halfAngle;
|
||||||
|
let tA = v2.angle() + Math.PI * .5;
|
||||||
|
|
||||||
|
if (!contourClosed){
|
||||||
|
if (i == 0 || i == contour.length - 1) {hA = Math.PI * .5;}
|
||||||
|
if (i == contour.length - 1) {tA = v1.angle() - Math.PI * .5;}
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = [];
|
const shift = Math.tan(hA - Math.PI * .5);
|
||||||
const lastCorner = contourClosed == false ? contour.length - 1: contour.length;
|
const shiftMatrix = new THREE.Matrix4().set(
|
||||||
|
1, 0, 0, 0,
|
||||||
|
-shift, 1, 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1
|
||||||
|
);
|
||||||
|
|
||||||
for (let i = 0; i < lastCorner; i++) {
|
const tempAngle = tA;
|
||||||
for (let j = 0; j < profile.count; j++) {
|
const rotationMatrix = new THREE.Matrix4().set(
|
||||||
const currCorner = i;
|
Math.cos(tempAngle), -Math.sin(tempAngle), 0, 0,
|
||||||
const nextCorner = i + 1 == contour.length ? 0 : i + 1;
|
Math.sin(tempAngle), Math.cos(tempAngle), 0, 0,
|
||||||
const currPoint = j;
|
0, 0, 1, 0,
|
||||||
const nextPoint = j + 1 == profile.count ? 0 : j + 1;
|
0, 0, 0, 1
|
||||||
|
);
|
||||||
|
|
||||||
const a = nextPoint + profile.count * currCorner;
|
const translationMatrix = new THREE.Matrix4().set(
|
||||||
const b = currPoint + profile.count * currCorner;
|
1, 0, 0, contour[i].x,
|
||||||
const c = currPoint + profile.count * nextCorner;
|
0, 1, 0, contour[i].y,
|
||||||
const d = nextPoint + profile.count * nextCorner;
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1,
|
||||||
|
);
|
||||||
|
|
||||||
index.push(a, b, d);
|
const cloneProfile = profile.clone();
|
||||||
index.push(b, c, d);
|
cloneProfile.applyMatrix4(shiftMatrix);
|
||||||
}
|
cloneProfile.applyMatrix4(rotationMatrix);
|
||||||
}
|
cloneProfile.applyMatrix4(translationMatrix);
|
||||||
|
|
||||||
if (!contourClosed) {
|
faces.set(cloneProfile.array, cloneProfile.count * i * 3);
|
||||||
// cheating because we know the profile length is 4 (for now)
|
|
||||||
const p1 = 0 + profile.count * 0;
|
|
||||||
const p2 = 1 + profile.count * 0;
|
|
||||||
const p3 = 2 + profile.count * 0;
|
|
||||||
const p4 = 3 + profile.count * 0;
|
|
||||||
index.push(p1, p2, p3);
|
|
||||||
index.push(p1, p3, p4);
|
|
||||||
const lc = lastCorner;
|
|
||||||
const p5 = 0 + profile.count * lc;
|
|
||||||
const p6 = 1 + profile.count * lc;
|
|
||||||
const p7 = 2 + profile.count * lc;
|
|
||||||
const p8 = 3 + profile.count * lc;
|
|
||||||
index.push(p7, p6, p5);
|
|
||||||
index.push(p8, p7, p5);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {index, faces};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.kiri.Layers = Layers;
|
const index = [];
|
||||||
})();
|
const lastCorner = contourClosed == false ? contour.length - 1: contour.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < lastCorner; i++) {
|
||||||
|
for (let j = 0; j < profile.count; j++) {
|
||||||
|
const currCorner = i;
|
||||||
|
const nextCorner = i + 1 == contour.length ? 0 : i + 1;
|
||||||
|
const currPoint = j;
|
||||||
|
const nextPoint = j + 1 == profile.count ? 0 : j + 1;
|
||||||
|
|
||||||
|
const a = nextPoint + profile.count * currCorner;
|
||||||
|
const b = currPoint + profile.count * currCorner;
|
||||||
|
const c = currPoint + profile.count * nextCorner;
|
||||||
|
const d = nextPoint + profile.count * nextCorner;
|
||||||
|
|
||||||
|
index.push(a, b, d);
|
||||||
|
index.push(b, c, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!contourClosed) {
|
||||||
|
// cheating because we know the profile length is 4 (for now)
|
||||||
|
const p1 = 0 + profile.count * 0;
|
||||||
|
const p2 = 1 + profile.count * 0;
|
||||||
|
const p3 = 2 + profile.count * 0;
|
||||||
|
const p4 = 3 + profile.count * 0;
|
||||||
|
index.push(p1, p2, p3);
|
||||||
|
index.push(p1, p3, p4);
|
||||||
|
const lc = lastCorner;
|
||||||
|
const p5 = 0 + profile.count * lc;
|
||||||
|
const p6 = 1 + profile.count * lc;
|
||||||
|
const p7 = 2 + profile.count * lc;
|
||||||
|
const p8 = 3 + profile.count * lc;
|
||||||
|
index.push(p7, p6, p5);
|
||||||
|
index.push(p8, p7, p5);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {index, faces};
|
||||||
|
}
|
||||||
|
|
||||||
|
kiri.Layers = Layers;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function () {
|
gapp.register("kiri.main", [], (root, exports) => {
|
||||||
|
|
||||||
const { base, data, load, kiri, moto, noop } = self;
|
const { base, data, load, kiri, moto, noop } = root;
|
||||||
const { api, consts, lang, Widget, newWidget, utils, stats } = kiri;
|
const { api, consts, lang, Widget, newWidget, utils, stats } = kiri;
|
||||||
const { areEqual, parseOpt, encodeOpt, ajax, o2js, js2o, ls2o } = utils;
|
const { areEqual, parseOpt, encodeOpt, ajax, o2js, js2o, ls2o } = utils;
|
||||||
const { feature, platform, selection, settings } = api;
|
const { feature, platform, selection, settings } = api;
|
||||||
|
|
@ -943,4 +943,5 @@
|
||||||
|
|
||||||
// upon restore, seed presets
|
// upon restore, seed presets
|
||||||
api.event.emit('preset', api.conf.dbo());
|
api.event.emit('preset', api.conf.dbo());
|
||||||
})();
|
|
||||||
|
});
|
||||||
|
|
|
||||||
149
src/kiri/pack.js
149
src/kiri/pack.js
|
|
@ -7,24 +7,12 @@
|
||||||
* http://codeincomplete.com/posts/2011/5/7/bin_packing/
|
* http://codeincomplete.com/posts/2011/5/7/bin_packing/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function () {
|
gapp.register("kiri.pack", [], (root, exports) => {
|
||||||
|
|
||||||
let kiri = self.kiri = self.kiri || {};
|
const { kiri } = root;
|
||||||
if (kiri.Pack) return;
|
|
||||||
|
|
||||||
kiri.Pack = Packer;
|
class Packer {
|
||||||
|
constructor(w, h, spacing) {
|
||||||
kiri.Sort = function (a, b) {
|
|
||||||
let aa = (a.w * a.h);
|
|
||||||
let ab = (b.w * b.h);
|
|
||||||
if (Math.abs(aa-ab) < 1) {
|
|
||||||
return (b.w / b.h) - (a.w / a.h);
|
|
||||||
} else {
|
|
||||||
return ab - aa;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function Packer(w, h, spacing) {
|
|
||||||
this.root = { x: 0, y: 0, w: w, h: h };
|
this.root = { x: 0, y: 0, w: w, h: h };
|
||||||
this.max = { w: 0, h: 0 };
|
this.max = { w: 0, h: 0 };
|
||||||
this.packed = false;
|
this.packed = false;
|
||||||
|
|
@ -32,68 +20,77 @@
|
||||||
this.pad = this.spacing / 2;
|
this.pad = this.spacing / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
Packer.prototype = {
|
simple(blocks) {
|
||||||
|
let n = 0, block, x = 0, y = 0, mh = 0;
|
||||||
simple: function (blocks) {
|
while (n < blocks.length) {
|
||||||
let n = 0, block, x = 0, y = 0, mh = 0;
|
block = blocks[n++];
|
||||||
while (n < blocks.length) {
|
block.fit = { x, y };
|
||||||
block = blocks[n++];
|
this.max.w = Math.max(this.max.w, x + block.w);
|
||||||
block.fit = { x, y };
|
this.max.h = Math.max(this.max.h, y + block.h);
|
||||||
this.max.w = Math.max(this.max.w, x + block.w);
|
mh = Math.max(mh, block.h);
|
||||||
this.max.h = Math.max(this.max.h, y + block.h);
|
x += block.w + this.spacing;
|
||||||
mh = Math.max(mh, block.h);
|
if (x > this.root.w) {
|
||||||
x += block.w + this.spacing;
|
x = 0;
|
||||||
if (x > this.root.w) {
|
y += mh + this.spacing;
|
||||||
x = 0;
|
mh = 0;
|
||||||
y += mh + this.spacing;
|
|
||||||
mh = 0;
|
|
||||||
}
|
|
||||||
if (y > this.root.h) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.packed = true;
|
if (y > this.root.h) {
|
||||||
return this;
|
return this;
|
||||||
},
|
|
||||||
|
|
||||||
fit: function (blocks, simple) {
|
|
||||||
if (simple) {
|
|
||||||
return this.simple(blocks);
|
|
||||||
}
|
}
|
||||||
let n = 0, node, block, w, h;
|
|
||||||
while (n < blocks.length) {
|
|
||||||
block = blocks[n++];
|
|
||||||
w = block.w + this.spacing;
|
|
||||||
h = block.h + this.spacing;
|
|
||||||
if (node = this.findNode(this.root, w, h)) {
|
|
||||||
block.fit = this.splitNode(node, w, h);
|
|
||||||
} else {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.packed = true;
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
findNode: function (root, w, h) {
|
|
||||||
if (root.used) {
|
|
||||||
return this.findNode(root.right, w, h) || this.findNode(root.down, w, h);
|
|
||||||
} else if (w <= root.w && h <= root.h) {
|
|
||||||
return root;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
splitNode: function (node, w, h) {
|
|
||||||
node.used = true;
|
|
||||||
node.down = { x: node.x, y: node.y + h, w: node.w, h: node.h - h };
|
|
||||||
node.right = { x: node.x + w, y: node.y, w: node.w - w, h: h };
|
|
||||||
this.max.w = Math.max(this.max.w, node.x + w);
|
|
||||||
this.max.h = Math.max(this.max.h, node.y + h);
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
this.packed = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
fit(blocks, simple) {
|
||||||
|
if (simple) {
|
||||||
|
return this.simple(blocks);
|
||||||
|
}
|
||||||
|
let n = 0, node, block, w, h;
|
||||||
|
while (n < blocks.length) {
|
||||||
|
block = blocks[n++];
|
||||||
|
w = block.w + this.spacing;
|
||||||
|
h = block.h + this.spacing;
|
||||||
|
if (node = this.findNode(this.root, w, h)) {
|
||||||
|
block.fit = this.splitNode(node, w, h);
|
||||||
|
} else {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.packed = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
})();
|
findNode(root, w, h) {
|
||||||
|
if (root.used) {
|
||||||
|
return this.findNode(root.right, w, h) || this.findNode(root.down, w, h);
|
||||||
|
} else if (w <= root.w && h <= root.h) {
|
||||||
|
return root;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
splitNode(node, w, h) {
|
||||||
|
node.used = true;
|
||||||
|
node.down = { x: node.x, y: node.y + h, w: node.w, h: node.h - h };
|
||||||
|
node.right = { x: node.x + w, y: node.y, w: node.w - w, h: h };
|
||||||
|
this.max.w = Math.max(this.max.w, node.x + w);
|
||||||
|
this.max.h = Math.max(this.max.h, node.y + h);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// kiri.Sort = function (a, b) {
|
||||||
|
// let aa = (a.w * a.h);
|
||||||
|
// let ab = (b.w * b.h);
|
||||||
|
// if (Math.abs(aa - ab) < 1) {
|
||||||
|
// return (b.w / b.h) - (a.w / a.h);
|
||||||
|
// } else {
|
||||||
|
// return ab - aa;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
kiri.Pack = Packer;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,14 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function () {
|
// dep: moto.space
|
||||||
|
// dep: kiri.api
|
||||||
|
// dep: kiri.consts
|
||||||
|
// dep: kiri.utils
|
||||||
|
// dep: kiri.widget
|
||||||
|
gapp.register("kiri.platform", [], (root, exports) => {
|
||||||
|
|
||||||
const { kiri, moto, load } = self;
|
const { kiri, moto, load } = root;
|
||||||
const { api, consts, driver, utils, newWidget, Widget } = kiri;
|
const { api, consts, driver, utils, newWidget, Widget } = kiri;
|
||||||
const { ajax, js2o } = utils;
|
const { ajax, js2o } = utils;
|
||||||
const { space } = moto;
|
const { space } = moto;
|
||||||
|
|
@ -796,4 +801,4 @@ const platform = api.platform = {
|
||||||
clear() { api.space.clear(); api.space.save(true) }
|
clear() { api.space.clear(); api.space.save(true) }
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,12 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function() {
|
// dep: geo.base
|
||||||
|
// dep: geo.point
|
||||||
|
// dep: geo.polygon
|
||||||
|
// dep: geo.paths
|
||||||
|
// dep: kiri.consts
|
||||||
|
gapp.register("kiri.print", [], (root, evets) => {
|
||||||
|
|
||||||
const { base, kiri } = self;
|
const { base, kiri } = self;
|
||||||
const { paths, util, newPoint, Polygon } = base;
|
const { paths, util, newPoint, Polygon } = base;
|
||||||
|
|
@ -10,10 +15,6 @@ const { tip2tipEmit } = paths;
|
||||||
const { numOrDefault } = util;
|
const { numOrDefault } = util;
|
||||||
const { beltfact } = kiri.consts;
|
const { beltfact } = kiri.consts;
|
||||||
|
|
||||||
kiri.newPrint = function(settings, widgets, id) {
|
|
||||||
return new Print(settings, widgets, id);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Print {
|
class Print {
|
||||||
constructor(settings, widgets, id) {
|
constructor(settings, widgets, id) {
|
||||||
this.id = id || new Date().getTime().toString(36);
|
this.id = id || new Date().getTime().toString(36);
|
||||||
|
|
@ -523,7 +524,6 @@ class Print {
|
||||||
|
|
||||||
done({ output: scope.output });
|
done({ output: scope.output });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Output {
|
class Output {
|
||||||
|
|
@ -550,6 +550,13 @@ class Output {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kiri.Print = Print;
|
function newPrint(settings, widgets, id) {
|
||||||
|
return new Print(settings, widgets, id);
|
||||||
|
};
|
||||||
|
|
||||||
})();
|
gapp.overlay(kiri, {
|
||||||
|
Print,
|
||||||
|
newPrint
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,13 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function () {
|
// dep: moto.space
|
||||||
|
// dep: kiri.api
|
||||||
|
// dep: kiri.consts
|
||||||
|
// dep: kiri.utils
|
||||||
|
gapp.register("kiri.selection", [], (root, exports) => {
|
||||||
|
|
||||||
const { data, kiri, moto, noop } = self;
|
const { kiri, moto, noop } = self;
|
||||||
const { api, consts, utils } = kiri;
|
const { api, consts, utils } = kiri;
|
||||||
const { space } = moto;
|
const { space } = moto;
|
||||||
|
|
||||||
|
|
@ -293,4 +297,4 @@ const selection = api.selection = {
|
||||||
meshes() { return selectedMeshes.slice() },
|
meshes() { return selectedMeshes.slice() },
|
||||||
};
|
};
|
||||||
|
|
||||||
}());
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,12 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function () {
|
// dep: kiri.api
|
||||||
|
// dep: kiri.conf
|
||||||
|
// dep: kiri.utils
|
||||||
|
// dep: moto.space
|
||||||
|
// dep: data.local
|
||||||
|
gapp.register("kiri.settings", [], (root, exports) => {
|
||||||
|
|
||||||
const { data, kiri, moto, noop } = self;
|
const { data, kiri, moto, noop } = self;
|
||||||
const { api, conf, consts, utils } = kiri;
|
const { api, conf, consts, utils } = kiri;
|
||||||
|
|
@ -147,6 +152,7 @@ function updateSettings(opt = {}) {
|
||||||
|
|
||||||
$('mode-device').innerText = device.deviceName;
|
$('mode-device').innerText = device.deviceName;
|
||||||
$('mode-profile').innerText = `${cproc[mode]}${same ? '' : ' *'}`;
|
$('mode-profile').innerText = `${cproc[mode]}${same ? '' : ' *'}`;
|
||||||
|
$('mode-info').style.display = 'flex';
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSettingsFromFields(setrec, uirec = api.ui, changes) {
|
function updateSettingsFromFields(setrec, uirec = api.ui, changes) {
|
||||||
|
|
@ -544,7 +550,7 @@ function settingsImport(data, ask) {
|
||||||
if (isWork) {
|
if (isWork) {
|
||||||
api.platform.clear();
|
api.platform.clear();
|
||||||
kiri.codec.decode(data.work).forEach(widget => {
|
kiri.codec.decode(data.work).forEach(widget => {
|
||||||
platform.add(widget, 0, true);
|
api.platform.add(widget, 0, true);
|
||||||
});
|
});
|
||||||
if (data.view) {
|
if (data.view) {
|
||||||
space.view.load(data.view);
|
space.view.load(data.view);
|
||||||
|
|
@ -696,4 +702,4 @@ api.settings = {
|
||||||
mode() { return settings.mode },
|
mode() { return settings.mode },
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,30 +2,25 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function() {
|
// dep: geo.base
|
||||||
|
// dep: geo.polygons
|
||||||
|
gapp.register("kiri.slice", [], (root, exports) => {
|
||||||
|
|
||||||
const KIRI = self.kiri,
|
const { base, kiri } = root;
|
||||||
PRO = Slice.prototype,
|
const { key, polygons } = base;
|
||||||
BASE = self.base,
|
const { util } = base;
|
||||||
UTIL = BASE.util,
|
|
||||||
POLY = BASE.polygons,
|
|
||||||
NOKEY = BASE.key.NONE;
|
|
||||||
|
|
||||||
KIRI.Top = Top;
|
const POLY = base.polygons;
|
||||||
KIRI.Slice = Slice;
|
const NOKEY = key.NONE;
|
||||||
KIRI.newTop = newTop;
|
|
||||||
KIRI.newSlice = newSlice;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object encapsulates a z-slice from an object. This code is shared by the
|
* Object encapsulates a z-slice from an object. This code is shared by the
|
||||||
* client and the worker thread. As such, the view layers are ignored in the
|
* client and the worker thread. As such, the view layers are ignored in the
|
||||||
* worker code paths.
|
* worker code paths.
|
||||||
*
|
*/
|
||||||
* @param {number} z offset from ground
|
class Slice {
|
||||||
* @param {THREE.Group} [view] optional view parent object for layers
|
|
||||||
* @constructor
|
constructor (z, view) {
|
||||||
*/
|
|
||||||
function Slice(z, view) {
|
|
||||||
this.z = z; // z-index
|
this.z = z; // z-index
|
||||||
this.index = 0; // slice index
|
this.index = 0; // slice index
|
||||||
this.lines = null; // slice raw
|
this.lines = null; // slice raw
|
||||||
|
|
@ -38,63 +33,18 @@
|
||||||
this.layers = null; // will replace most of the layer output data
|
this.layers = null; // will replace most of the layer output data
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a top-level (outer) polygon in a slice. Slices may contain
|
|
||||||
* multiple tops each with nested structures. Top objects contain cached
|
|
||||||
* and computed objects for quick access for rendering and dependent computations.
|
|
||||||
*
|
|
||||||
* @param {Polygon} polygon
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
function Top(polygon) {
|
|
||||||
this.poly = polygon; // outline poly
|
|
||||||
}
|
|
||||||
|
|
||||||
Top.prototype.clone = function(deep) {
|
|
||||||
let top = new Top(this.poly.clone(deep));
|
|
||||||
return top;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return innermost traces under a given top. for FDM, this represents
|
|
||||||
* the outline shell that the fill touches. used by Print, Laser
|
|
||||||
*/
|
|
||||||
Top.prototype.innerShells = function() {
|
|
||||||
let shells = this.shells,
|
|
||||||
array = [];
|
|
||||||
if (shells) shells.forEach(function(p) {
|
|
||||||
if (p.inner) array.appendAll(p.inner);
|
|
||||||
});
|
|
||||||
return array;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns shell polygons of a given depth
|
|
||||||
* used by Print (FDM)
|
|
||||||
*
|
|
||||||
* @param {Polygon[]} out array to populate
|
|
||||||
* @returns {Polygon[]} array of top polygons
|
|
||||||
*/
|
|
||||||
Top.prototype.shellsAtDepth = function(depth) {
|
|
||||||
return this.shells ? this.shells.filter(poly => poly.depth === depth) : [];
|
|
||||||
};
|
|
||||||
|
|
||||||
/** ******************************************************************
|
|
||||||
* Slice Prototype Functions
|
|
||||||
******************************************************************* */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return Layers object for this slice. creates it if necessary.
|
* return Layers object for this slice. creates it if necessary.
|
||||||
*/
|
*/
|
||||||
PRO.output = function() {
|
output() {
|
||||||
if (this.layers) return this.layers;
|
if (this.layers) return this.layers;
|
||||||
return this.layers = new KIRI.Layers();
|
return this.layers = new kiri.Layers();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a cloned slice the option of a deep clone on the top polys
|
* returns a cloned slice the option of a deep clone on the top polys
|
||||||
*/
|
*/
|
||||||
PRO.clone = function(deep) {
|
clone(deep) {
|
||||||
const from = this, slice = newSlice(from.z, from.view);
|
const from = this, slice = newSlice(from.z, from.view);
|
||||||
from.tops.forEach(function(top) {
|
from.tops.forEach(function(top) {
|
||||||
slice.addTop(top.poly.clone(deep));
|
slice.addTop(top.poly.clone(deep));
|
||||||
|
|
@ -102,16 +52,16 @@
|
||||||
return slice;
|
return slice;
|
||||||
};
|
};
|
||||||
|
|
||||||
PRO.topPolys = function() {
|
topPolys() {
|
||||||
return this.tops.map(top => top.poly);
|
return this.tops.map(top => top.poly);
|
||||||
};
|
};
|
||||||
|
|
||||||
PRO.topSimples = function() {
|
topSimples() {
|
||||||
return this.tops.map(top => top.simple);
|
return this.tops.map(top => top.simple);
|
||||||
};
|
};
|
||||||
|
|
||||||
// FDM top intersect optimization
|
// FDM top intersect optimization
|
||||||
PRO.topPolysFlat = function() {
|
topPolysFlat() {
|
||||||
if (this.topFlatPolys) {
|
if (this.topFlatPolys) {
|
||||||
return this.topFlatPolys;
|
return this.topFlatPolys;
|
||||||
}
|
}
|
||||||
|
|
@ -119,7 +69,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
// FDM retract path routing using first shell
|
// FDM retract path routing using first shell
|
||||||
PRO.topRouteFlat = function() {
|
topRouteFlat() {
|
||||||
if (this.topFlatRoutes) {
|
if (this.topFlatRoutes) {
|
||||||
return this.topFlatRoutes;
|
return this.topFlatRoutes;
|
||||||
}
|
}
|
||||||
|
|
@ -128,27 +78,27 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// CAM only
|
// CAM only
|
||||||
PRO.topPolyInners = function() {
|
topPolyInners() {
|
||||||
return this.tops.map(top => top.poly.inner).flat().filter(poly => poly);
|
return this.tops.map(top => top.poly.inner).flat().filter(poly => poly);
|
||||||
};
|
};
|
||||||
|
|
||||||
// FDM / SLA only
|
// FDM / SLA only
|
||||||
PRO.topInners = function() {
|
topInners() {
|
||||||
return this.tops.map(top => top.last).flat().filter(poly => poly);
|
return this.tops.map(top => top.last).flat().filter(poly => poly);
|
||||||
};
|
};
|
||||||
|
|
||||||
// FDM / SLA only
|
// FDM / SLA only
|
||||||
PRO.topFillOff = function() {
|
topFillOff() {
|
||||||
return this.tops.map(top => top.fill_off).flat().filter(poly => poly);
|
return this.tops.map(top => top.fill_off).flat().filter(poly => poly);
|
||||||
};
|
};
|
||||||
|
|
||||||
// FDM only
|
// FDM only
|
||||||
PRO.topFill = function() {
|
topFill() {
|
||||||
return this.tops.map(top => top.fill_lines).flat().filter(poly => poly);
|
return this.tops.map(top => top.fill_lines).flat().filter(poly => poly);
|
||||||
};
|
};
|
||||||
|
|
||||||
// FDM only
|
// FDM only
|
||||||
PRO.topShells = function() {
|
topShells() {
|
||||||
return this.tops.map(top => top.shells).flat().filter(poly => poly);
|
return this.tops.map(top => top.shells).flat().filter(poly => poly);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -158,7 +108,7 @@
|
||||||
* vertical wall regions. this allows us to eliminate expensive diffs
|
* vertical wall regions. this allows us to eliminate expensive diffs
|
||||||
* and infill computation when we detect the layers are the same.
|
* and infill computation when we detect the layers are the same.
|
||||||
*/
|
*/
|
||||||
PRO.fingerprint = function() {
|
fingerprint() {
|
||||||
if (this.finger) {
|
if (this.finger) {
|
||||||
return this.finger;
|
return this.finger;
|
||||||
}
|
}
|
||||||
|
|
@ -168,18 +118,18 @@
|
||||||
/**
|
/**
|
||||||
* returns true if the layers' fingerprints are the same
|
* returns true if the layers' fingerprints are the same
|
||||||
*/
|
*/
|
||||||
PRO.fingerprintSame = function(slice) {
|
fingerprintSame(slice) {
|
||||||
return slice ? POLY.fingerprintCompare(this.fingerprint(), slice.fingerprint()) : false;
|
return slice ? POLY.fingerprintCompare(this.fingerprint(), slice.fingerprint()) : false;
|
||||||
};
|
};
|
||||||
|
|
||||||
PRO.addTops = function(polys) {
|
addTops(polys) {
|
||||||
polys.forEach(p => {
|
polys.forEach(p => {
|
||||||
this.addTop(p);
|
this.addTop(p);
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
PRO.addTop = function(poly) {
|
addTop(poly) {
|
||||||
if (poly.length) {
|
if (poly.length) {
|
||||||
// standard legacy polygon
|
// standard legacy polygon
|
||||||
let top = new Top(poly);
|
let top = new Top(poly);
|
||||||
|
|
@ -189,7 +139,7 @@
|
||||||
} else {
|
} else {
|
||||||
// frop top object passed back by slicePost()
|
// frop top object passed back by slicePost()
|
||||||
let top = new Top(poly.poly);
|
let top = new Top(poly.poly);
|
||||||
top.thin_fill = poly.thin_fill ? poly.thin_fill.map(p => BASE.newPoint(p.x,p.y,p.z)) : undefined;
|
top.thin_fill = poly.thin_fill ? poly.thin_fill.map(p => base.newPoint(p.x,p.y,p.z)) : undefined;
|
||||||
top.fill_lines = poly.fill_lines;
|
top.fill_lines = poly.fill_lines;
|
||||||
top.fill_off = poly.fill_off;
|
top.fill_off = poly.fill_off;
|
||||||
top.last = poly.last;
|
top.last = poly.last;
|
||||||
|
|
@ -202,7 +152,7 @@
|
||||||
throw "invalid top";
|
throw "invalid top";
|
||||||
};
|
};
|
||||||
|
|
||||||
PRO.findClosestPointTo = function(target) {
|
findClosestPointTo(target) {
|
||||||
let min, find;
|
let min, find;
|
||||||
|
|
||||||
if (this.tops.length) {
|
if (this.tops.length) {
|
||||||
|
|
@ -224,7 +174,7 @@
|
||||||
return min;
|
return min;
|
||||||
};
|
};
|
||||||
|
|
||||||
PRO.xray = function(dash = 3) {
|
xray(dash = 3) {
|
||||||
// console.log('xray', this);
|
// console.log('xray', this);
|
||||||
this.output().setLayer(`xp`, 0x888800).addPolys(this.topPolys());
|
this.output().setLayer(`xp`, 0x888800).addPolys(this.topPolys());
|
||||||
this.lines.forEach((line, i) => {
|
this.lines.forEach((line, i) => {
|
||||||
|
|
@ -232,19 +182,65 @@
|
||||||
const color = [ 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff ][group];
|
const color = [ 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff ][group];
|
||||||
this.output().setLayer(`xl-${group}`, color).addLine(line.p1, line.p2);
|
this.output().setLayer(`xl-${group}`, color).addLine(line.p1, line.p2);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
function newTop(poly) {
|
}
|
||||||
return new Top(poly);
|
|
||||||
|
/**
|
||||||
|
* Represents a top-level (outer) polygon in a slice. Slices may contain
|
||||||
|
* multiple tops each with nested structures. Top objects contain cached
|
||||||
|
* and computed objects for quick access for rendering and dependent computations.
|
||||||
|
*/
|
||||||
|
class Top {
|
||||||
|
|
||||||
|
constructor(polygon) {
|
||||||
|
this.poly = polygon; // outline poly
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(deep) {
|
||||||
|
let top = new Top(this.poly.clone(deep));
|
||||||
|
return top;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} z
|
* return innermost traces under a given top. for FDM, this represents
|
||||||
* @param {THREE.Group} view
|
* the outline shell that the fill touches. used by Print, Laser
|
||||||
* @returns {Slice}
|
|
||||||
*/
|
*/
|
||||||
function newSlice(z, view) {
|
innerShells() {
|
||||||
return new Slice(z, view);
|
let shells = this.shells,
|
||||||
|
array = [];
|
||||||
|
if (shells) shells.forEach(function(p) {
|
||||||
|
if (p.inner) array.appendAll(p.inner);
|
||||||
|
});
|
||||||
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
/**
|
||||||
|
* Returns shell polygons of a given depth
|
||||||
|
* used by Print (FDM)
|
||||||
|
*
|
||||||
|
* @param {Polygon[]} out array to populate
|
||||||
|
* @returns {Polygon[]} array of top polygons
|
||||||
|
*/
|
||||||
|
shellsAtDepth(depth) {
|
||||||
|
return this.shells ? this.shells.filter(poly => poly.depth === depth) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function newTop(poly) {
|
||||||
|
return new Top(poly);
|
||||||
|
}
|
||||||
|
|
||||||
|
function newSlice(z, view) {
|
||||||
|
return new Slice(z, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
gapp.overlay(kiri, {
|
||||||
|
Top,
|
||||||
|
Slice,
|
||||||
|
newTop,
|
||||||
|
newSlice
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,261 +2,263 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function() {
|
gapp.register("kiri.stack", [], (root, exports) => {
|
||||||
|
|
||||||
class Stack {
|
const { kiri } = root;
|
||||||
constructor(view, freeMem) {
|
|
||||||
this._view = view;
|
class Stack {
|
||||||
this.view = view.newGroup();
|
constructor(view, freeMem) {
|
||||||
this.slices = [];
|
this._view = view;
|
||||||
this.meshes = [];
|
this.view = view.newGroup();
|
||||||
this.freeMem = freeMem;
|
this.slices = [];
|
||||||
|
this.meshes = [];
|
||||||
|
this.freeMem = freeMem;
|
||||||
|
}
|
||||||
|
|
||||||
|
size() {
|
||||||
|
return this.slices.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.view.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this.view.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
rotate(set) {
|
||||||
|
this.view.rotation.x = -set.angle * (Math.PI/180);
|
||||||
|
this.view.position.y += -set.dy;
|
||||||
|
this.view.position.z += -set.dz;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this._view.remove(this.view);
|
||||||
|
THREE.dispose(this.view);
|
||||||
|
this.view = this.slices = this.meshes = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFreeMem(bool) {
|
||||||
|
this.freeMem = bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
setVisible(newMin, newMax) {
|
||||||
|
this.show();
|
||||||
|
for (let i=0, s=this.slices, len=s.length; i<len; i++) {
|
||||||
|
s[i].visible = i >= newMin && i <= newMax;
|
||||||
|
if (s[i].visible) this.lastVis = s[i];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size() {
|
setLastFraction(frac = 1) {
|
||||||
return this.slices.length;
|
if (this.lastVis)
|
||||||
|
for (let mesh of this.lastVis.children) {
|
||||||
|
let geo = mesh.geometry;
|
||||||
|
let pos = geo.attributes.position;
|
||||||
|
let len = frac === 1 ? Infinity : Math.round(pos.count * frac);
|
||||||
|
geo.setDrawRange(0, len);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hide() {
|
addLayers(layers) {
|
||||||
this.view.visible = false;
|
// each slice gets a group so the slice can be toggled efficiently
|
||||||
|
let group = this.view.newGroup();
|
||||||
|
this.slices.push(group);
|
||||||
|
let map = this.renderLayers(layers, group);
|
||||||
|
// by default release memory after layers are rendered
|
||||||
|
if (this.freeMem) {
|
||||||
|
layers.init();
|
||||||
}
|
}
|
||||||
|
// return map of layers to materials (so they can be toggled on/off)
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
show() {
|
renderLayers(layers, group) {
|
||||||
this.view.visible = true;
|
const map = {}
|
||||||
|
for (const [label, layer] of Object.entries(layers.layers)) {
|
||||||
|
map[label] = this.renderLayer(layer, group);
|
||||||
}
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
rotate(set) {
|
renderLayer(layer, group) {
|
||||||
this.view.rotation.x = -set.angle * (Math.PI/180);
|
function addPoly(vertices, poly) {
|
||||||
this.view.position.y += -set.dy;
|
const points = poly.points, len = points.length;
|
||||||
this.view.position.z += -set.dz;
|
for (let i=1; i<len; i++) {
|
||||||
}
|
const p1 = points[i-1], p2 = points[i];
|
||||||
|
vertices.push(new THREE.Vector3(p1.x, p1.y, p1.z));
|
||||||
destroy() {
|
vertices.push(new THREE.Vector3(p2.x, p2.y, p2.z));
|
||||||
this._view.remove(this.view);
|
}
|
||||||
THREE.dispose(this.view);
|
if (!poly.open) {
|
||||||
this.view = this.slices = this.meshes = null;
|
const p1 = points[len - 1], p2 = points[0];
|
||||||
}
|
vertices.push(new THREE.Vector3(p1.x, p1.y, p1.z));
|
||||||
|
vertices.push(new THREE.Vector3(p2.x, p2.y, p2.z));
|
||||||
setFreeMem(bool) {
|
|
||||||
this.freeMem = bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
setVisible(newMin, newMax) {
|
|
||||||
this.show();
|
|
||||||
for (let i=0, s=this.slices, len=s.length; i<len; i++) {
|
|
||||||
s[i].visible = i >= newMin && i <= newMax;
|
|
||||||
if (s[i].visible) this.lastVis = s[i];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setLastFraction(frac = 1) {
|
const { polys, lines, faces, cface, paths, cpath, color, off } = layer;
|
||||||
if (this.lastVis)
|
const meshes = [];
|
||||||
for (let mesh of this.lastVis.children) {
|
const defstate = !off;
|
||||||
let geo = mesh.geometry;
|
const mats = [];
|
||||||
let pos = geo.attributes.position;
|
mats.state = defstate;
|
||||||
let len = frac === 1 ? Infinity : Math.round(pos.count * frac);
|
|
||||||
geo.setDrawRange(0, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addLayers(layers) {
|
if (polys.length || lines.length) {
|
||||||
// each slice gets a group so the slice can be toggled efficiently
|
const vert = []; // vertexes
|
||||||
let group = this.view.newGroup();
|
const mat = []; // materials
|
||||||
this.slices.push(group);
|
const grp = []; // material groups
|
||||||
let map = this.renderLayers(layers, group);
|
const geo = new THREE.BufferGeometry();
|
||||||
// by default release memory after layers are rendered
|
// map all the poly and line colors for re-use
|
||||||
if (this.freeMem) {
|
const cmap = {}
|
||||||
layers.init();
|
let cidx = 0;
|
||||||
}
|
let last = undefined;
|
||||||
// return map of layers to materials (so they can be toggled on/off)
|
for (let i=0, il=polys.length; i<il; i++) {
|
||||||
return map;
|
const vl = vert.length;
|
||||||
}
|
const poly = polys[i];
|
||||||
|
addPoly(vert, poly);
|
||||||
renderLayers(layers, group) {
|
const pc = poly.color !== undefined ? { line: poly.color, opacity: color.opacity } : color;
|
||||||
const map = {}
|
const pk = pc.line;
|
||||||
for (const [label, layer] of Object.entries(layers.layers)) {
|
const cc = cmap[pk] = cmap[pk] || { idx: cidx++, mat: createLineMaterial(pc, mat) };
|
||||||
map[label] = this.renderLayer(layer, group);
|
if (last !== pk) {
|
||||||
}
|
if (grp.length) {
|
||||||
return map;
|
// rewrite counts for last group
|
||||||
}
|
const prev = grp[grp.length - 1]
|
||||||
|
prev[1] = vl;
|
||||||
renderLayer(layer, group) {
|
|
||||||
function addPoly(vertices, poly) {
|
|
||||||
const points = poly.points, len = points.length;
|
|
||||||
for (let i=1; i<len; i++) {
|
|
||||||
const p1 = points[i-1], p2 = points[i];
|
|
||||||
vertices.push(new THREE.Vector3(p1.x, p1.y, p1.z));
|
|
||||||
vertices.push(new THREE.Vector3(p2.x, p2.y, p2.z));
|
|
||||||
}
|
|
||||||
if (!poly.open) {
|
|
||||||
const p1 = points[len - 1], p2 = points[0];
|
|
||||||
vertices.push(new THREE.Vector3(p1.x, p1.y, p1.z));
|
|
||||||
vertices.push(new THREE.Vector3(p2.x, p2.y, p2.z));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { polys, lines, faces, cface, paths, cpath, color, off } = layer;
|
|
||||||
const meshes = [];
|
|
||||||
const defstate = !off;
|
|
||||||
const mats = [];
|
|
||||||
mats.state = defstate;
|
|
||||||
|
|
||||||
if (polys.length || lines.length) {
|
|
||||||
const vert = []; // vertexes
|
|
||||||
const mat = []; // materials
|
|
||||||
const grp = []; // material groups
|
|
||||||
const geo = new THREE.BufferGeometry();
|
|
||||||
// map all the poly and line colors for re-use
|
|
||||||
const cmap = {}
|
|
||||||
let cidx = 0;
|
|
||||||
let last = undefined;
|
|
||||||
for (let i=0, il=polys.length; i<il; i++) {
|
|
||||||
const vl = vert.length;
|
|
||||||
const poly = polys[i];
|
|
||||||
addPoly(vert, poly);
|
|
||||||
const pc = poly.color !== undefined ? { line: poly.color, opacity: color.opacity } : color;
|
|
||||||
const pk = pc.line;
|
|
||||||
const cc = cmap[pk] = cmap[pk] || { idx: cidx++, mat: createLineMaterial(pc, mat) };
|
|
||||||
if (last !== pk) {
|
|
||||||
if (grp.length) {
|
|
||||||
// rewrite counts for last group
|
|
||||||
const prev = grp[grp.length - 1]
|
|
||||||
prev[1] = vl;
|
|
||||||
}
|
|
||||||
grp.push([vl, Infinity, cc.idx]);
|
|
||||||
last = pk;
|
|
||||||
}
|
}
|
||||||
|
grp.push([vl, Infinity, cc.idx]);
|
||||||
|
last = pk;
|
||||||
}
|
}
|
||||||
// for now, line segments inherit the last color
|
|
||||||
for (let i=0, il=lines.length; i<il; i++) {
|
|
||||||
const p = lines[i];
|
|
||||||
vert.push(new THREE.Vector3(p.x, p.y, p.z));
|
|
||||||
}
|
|
||||||
// ensure at least one group using the default color settings
|
|
||||||
if (grp.length === 0) {
|
|
||||||
grp.push([0, Infinity, 0]);
|
|
||||||
mat.push(createLineMaterial(color));
|
|
||||||
}
|
|
||||||
mat.forEach(m => m.visible = defstate);
|
|
||||||
for (let i=0; i<grp.length; i++) {
|
|
||||||
const g = grp[i];
|
|
||||||
geo.addGroup(g[0], g[1], g[2]);
|
|
||||||
}
|
|
||||||
geo.setFromPoints(vert);
|
|
||||||
const segs = new THREE.LineSegments(geo, mat);
|
|
||||||
meshes.push(segs);
|
|
||||||
group.add(segs);
|
|
||||||
mats.appendAll(mat);
|
|
||||||
}
|
}
|
||||||
if (faces.length) {
|
// for now, line segments inherit the last color
|
||||||
const mat = [];
|
for (let i=0, il=lines.length; i<il; i++) {
|
||||||
if (cface) {
|
const p = lines[i];
|
||||||
cface.forEach(c => { mat.push(newMat(c, true)) });
|
vert.push(new THREE.Vector3(p.x, p.y, p.z));
|
||||||
} else {
|
}
|
||||||
mat.push(newMat(color, true));
|
// ensure at least one group using the default color settings
|
||||||
}
|
if (grp.length === 0) {
|
||||||
mat.forEach(m => m.visible = defstate);
|
grp.push([0, Infinity, 0]);
|
||||||
|
mat.push(createLineMaterial(color));
|
||||||
|
}
|
||||||
|
mat.forEach(m => m.visible = defstate);
|
||||||
|
for (let i=0; i<grp.length; i++) {
|
||||||
|
const g = grp[i];
|
||||||
|
geo.addGroup(g[0], g[1], g[2]);
|
||||||
|
}
|
||||||
|
geo.setFromPoints(vert);
|
||||||
|
const segs = new THREE.LineSegments(geo, mat);
|
||||||
|
meshes.push(segs);
|
||||||
|
group.add(segs);
|
||||||
|
mats.appendAll(mat);
|
||||||
|
}
|
||||||
|
if (faces.length) {
|
||||||
|
const mat = [];
|
||||||
|
if (cface) {
|
||||||
|
cface.forEach(c => { mat.push(newMat(c, true)) });
|
||||||
|
} else {
|
||||||
|
mat.push(newMat(color, true));
|
||||||
|
}
|
||||||
|
mat.forEach(m => m.visible = defstate);
|
||||||
|
const geo = new THREE.BufferGeometry();
|
||||||
|
if (faces.toFloat32) {
|
||||||
|
geo.setAttribute('position', new THREE.BufferAttribute(faces.toFloat32(), 3));
|
||||||
|
} else {
|
||||||
|
geo.setAttribute('position', new THREE.BufferAttribute(faces, 3));
|
||||||
|
}
|
||||||
|
// geo.computeFaceNormals();
|
||||||
|
geo.computeVertexNormals();
|
||||||
|
if (cface) {
|
||||||
|
cface.forEach((c, i) => geo.addGroup(c.start, c.count, i));
|
||||||
|
} else {
|
||||||
|
geo.addGroup(0, Infinity, 0);
|
||||||
|
}
|
||||||
|
const mesh = new THREE.Mesh(geo, mat);
|
||||||
|
mesh.castShadow = true;
|
||||||
|
mesh.receiveShadow = true;
|
||||||
|
meshes.push(mesh);
|
||||||
|
group.add(mesh);
|
||||||
|
mats.appendAll(mat);
|
||||||
|
}
|
||||||
|
if (paths.length) {
|
||||||
|
const mat = [];
|
||||||
|
if (cpath) {
|
||||||
|
cpath.forEach(c => { mat.push(newMat(c)) });
|
||||||
|
} else {
|
||||||
|
mat.push(newMat(color));
|
||||||
|
}
|
||||||
|
mat.forEach(m => m.visible = defstate);
|
||||||
|
// const mat = newMat(data);
|
||||||
|
paths.forEach((path, i) => {
|
||||||
|
const { index, faces, z, colors } = path;
|
||||||
const geo = new THREE.BufferGeometry();
|
const geo = new THREE.BufferGeometry();
|
||||||
if (faces.toFloat32) {
|
geo.setAttribute('position', new THREE.BufferAttribute(faces, 3));
|
||||||
geo.setAttribute('position', new THREE.BufferAttribute(faces.toFloat32(), 3));
|
geo.setIndex(index);
|
||||||
} else {
|
|
||||||
geo.setAttribute('position', new THREE.BufferAttribute(faces, 3));
|
|
||||||
}
|
|
||||||
// geo.computeFaceNormals();
|
// geo.computeFaceNormals();
|
||||||
geo.computeVertexNormals();
|
geo.computeVertexNormals();
|
||||||
if (cface) {
|
if (cpath) {
|
||||||
cface.forEach((c, i) => geo.addGroup(c.start, c.count, i));
|
cpath.forEach((c, i) => geo.addGroup(c.start, c.count, i));
|
||||||
} else {
|
} else {
|
||||||
geo.addGroup(0, Infinity, 0);
|
geo.addGroup(0, Infinity, 0);
|
||||||
}
|
}
|
||||||
const mesh = new THREE.Mesh(geo, mat);
|
const mesh = new THREE.Mesh(geo, mat);
|
||||||
|
mesh.position.z = z;
|
||||||
mesh.castShadow = true;
|
mesh.castShadow = true;
|
||||||
mesh.receiveShadow = true;
|
mesh.receiveShadow = true;
|
||||||
meshes.push(mesh);
|
meshes.push(mesh);
|
||||||
group.add(mesh);
|
group.add(mesh);
|
||||||
mats.appendAll(mat);
|
});
|
||||||
}
|
mats.appendAll(mat);
|
||||||
if (paths.length) {
|
|
||||||
const mat = [];
|
|
||||||
if (cpath) {
|
|
||||||
cpath.forEach(c => { mat.push(newMat(c)) });
|
|
||||||
} else {
|
|
||||||
mat.push(newMat(color));
|
|
||||||
}
|
|
||||||
mat.forEach(m => m.visible = defstate);
|
|
||||||
// const mat = newMat(data);
|
|
||||||
paths.forEach((path, i) => {
|
|
||||||
const { index, faces, z, colors } = path;
|
|
||||||
const geo = new THREE.BufferGeometry();
|
|
||||||
geo.setAttribute('position', new THREE.BufferAttribute(faces, 3));
|
|
||||||
geo.setIndex(index);
|
|
||||||
// geo.computeFaceNormals();
|
|
||||||
geo.computeVertexNormals();
|
|
||||||
if (cpath) {
|
|
||||||
cpath.forEach((c, i) => geo.addGroup(c.start, c.count, i));
|
|
||||||
} else {
|
|
||||||
geo.addGroup(0, Infinity, 0);
|
|
||||||
}
|
|
||||||
const mesh = new THREE.Mesh(geo, mat);
|
|
||||||
mesh.position.z = z;
|
|
||||||
mesh.castShadow = true;
|
|
||||||
mesh.receiveShadow = true;
|
|
||||||
meshes.push(mesh);
|
|
||||||
group.add(mesh);
|
|
||||||
});
|
|
||||||
mats.appendAll(mat);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.new_meshes = meshes;
|
|
||||||
this.meshes.appendAll(meshes);
|
|
||||||
mats.forEach(mat => mat.visible = defstate);
|
|
||||||
return mats;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.new_meshes = meshes;
|
||||||
|
this.meshes.appendAll(meshes);
|
||||||
|
mats.forEach(mat => mat.visible = defstate);
|
||||||
|
return mats;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.kiri.Stack = Stack;
|
kiri.Stack = Stack;
|
||||||
|
|
||||||
let shininess = 15,
|
let shininess = 15,
|
||||||
specular = 0x444444,
|
specular = 0x444444,
|
||||||
emissive = 0x101010,
|
emissive = 0x101010,
|
||||||
metalness = 0,
|
metalness = 0,
|
||||||
roughness = 0.3,
|
roughness = 0.3,
|
||||||
newMat = createPhongMaterial;
|
newMat = createPhongMaterial;
|
||||||
|
|
||||||
function createLineMaterial(color, array) {
|
function createLineMaterial(color, array) {
|
||||||
const opacity = color.lopacity || color.opacity || 1;
|
const opacity = color.lopacity || color.opacity || 1;
|
||||||
const mat = new THREE.LineBasicMaterial({
|
const mat = new THREE.LineBasicMaterial({
|
||||||
transparent: true,
|
transparent: true,
|
||||||
opacity: opacity,
|
opacity: opacity,
|
||||||
color: color.line
|
color: color.line
|
||||||
});
|
});
|
||||||
if (array) {
|
if (array) {
|
||||||
array.push(mat);
|
array.push(mat);
|
||||||
}
|
|
||||||
return mat;
|
|
||||||
}
|
}
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
function createStandardMaterial(color, flat) {
|
function createStandardMaterial(color, flat) {
|
||||||
return new THREE.MeshStandardMaterial({
|
return new THREE.MeshStandardMaterial({
|
||||||
emissive,
|
emissive,
|
||||||
roughness,
|
roughness,
|
||||||
metalness,
|
metalness,
|
||||||
transparent: color.opacity != 1,
|
transparent: color.opacity != 1,
|
||||||
opacity: color.opacity || 1,
|
opacity: color.opacity || 1,
|
||||||
color: color.face,
|
color: color.face,
|
||||||
side: flat ? THREE.DoubleSide : THREE.FrontSide
|
side: flat ? THREE.DoubleSide : THREE.FrontSide
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createPhongMaterial(color, flat) {
|
function createPhongMaterial(color, flat) {
|
||||||
return new THREE.MeshPhongMaterial({
|
return new THREE.MeshPhongMaterial({
|
||||||
shininess,
|
shininess,
|
||||||
specular,
|
specular,
|
||||||
transparent: color.opacity != 1,
|
transparent: color.opacity != 1,
|
||||||
opacity: color.opacity || 1,
|
opacity: color.opacity || 1,
|
||||||
color: color.face,
|
color: color.face,
|
||||||
side: flat ? THREE.DoubleSide : THREE.FrontSide
|
side: flat ? THREE.DoubleSide : THREE.FrontSide
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,168 +2,172 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function() {
|
// dep: kiri.stack
|
||||||
|
// use: kiri.api
|
||||||
|
// use: kiri.ui
|
||||||
|
gapp.register("kiri.stacks", [], (root, exports) => {
|
||||||
|
|
||||||
let KIRI = self.kiri,
|
const { kiri } = root;
|
||||||
freeMem = true,
|
|
||||||
stacks = {},
|
|
||||||
tallest = 0,
|
|
||||||
min = 0,
|
|
||||||
max = 0,
|
|
||||||
labels, API, UC, UI, DYN;
|
|
||||||
|
|
||||||
function init() {
|
let freeMem = true,
|
||||||
labels = $("layers");
|
stacks = {},
|
||||||
API = KIRI.api,
|
tallest = 0,
|
||||||
UC = API.uc,
|
min = 0,
|
||||||
UI = API.ui;
|
max = 0,
|
||||||
|
labels, API, UC, UI, DYN;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
labels = $("layers");
|
||||||
|
API = kiri.api,
|
||||||
|
UC = API.uc,
|
||||||
|
UI = API.ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFreeMem(bool) {
|
||||||
|
Object.values(stacks).forEach(stack => stack.obj.setFreeMem(bool));
|
||||||
|
freeMem = bool;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear() {
|
||||||
|
if (!API) {
|
||||||
|
init();
|
||||||
}
|
}
|
||||||
|
// remove stacks from their views
|
||||||
function setFreeMem(bool) {
|
for (const [stack, data] of Object.entries(stacks)) {
|
||||||
Object.values(stacks).forEach(stack => stack.obj.setFreeMem(bool));
|
data.clear();
|
||||||
freeMem = bool;
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
min = max = tallest = 0;
|
||||||
|
stacks = {};
|
||||||
|
DYN = UI.dyn = {};
|
||||||
|
|
||||||
function clear() {
|
// clear labels
|
||||||
if (!API) {
|
UC.setGroup(labels);
|
||||||
init();
|
labels.innerHTML = '';
|
||||||
}
|
}
|
||||||
// remove stacks from their views
|
|
||||||
for (const [stack, data] of Object.entries(stacks)) {
|
|
||||||
data.clear();
|
|
||||||
}
|
|
||||||
min = max = tallest = 0;
|
|
||||||
stacks = {};
|
|
||||||
DYN = UI.dyn = {};
|
|
||||||
|
|
||||||
// clear labels
|
function rotate(set) {
|
||||||
UC.setGroup(labels);
|
Object.values(stacks).forEach(stack => {
|
||||||
labels.innerHTML = '';
|
stack.obj.rotate(set);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function rotate(set) {
|
function getStack(name) {
|
||||||
Object.values(stacks).forEach(stack => {
|
return stacks[name];
|
||||||
stack.obj.rotate(set);
|
}
|
||||||
|
|
||||||
|
function getStacks() {
|
||||||
|
return Object.entries(stacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLabels() {
|
||||||
|
return Object.keys(DYN);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setVisible(label, bool) {
|
||||||
|
let ctrl = DYN[label];
|
||||||
|
if (ctrl) {
|
||||||
|
ctrl.toggle.checked = bool;
|
||||||
|
ctrl.group.forEach(mat => {
|
||||||
|
mat.visible = bool;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getStack(name) {
|
function create(name, view) {
|
||||||
|
if (stacks[name]) {
|
||||||
return stacks[name];
|
return stacks[name];
|
||||||
}
|
}
|
||||||
|
const stack = stacks[name] = {
|
||||||
function getStacks() {
|
layers: [ ],
|
||||||
return Object.entries(stacks);
|
obj: new kiri.Stack(view, freeMem),
|
||||||
}
|
add: function(layers) {
|
||||||
|
let lmap = layers.layers;
|
||||||
function getLabels() {
|
let map = stack.obj.addLayers(layers);
|
||||||
return Object.keys(DYN);
|
for (const [label, mats] of Object.entries(map)) {
|
||||||
}
|
if (!DYN[label]) {
|
||||||
|
let check = DYN[label] = {
|
||||||
function setVisible(label, bool) {
|
group: [],
|
||||||
let ctrl = DYN[label];
|
toggle: UC.newBoolean(label, (abc) => {
|
||||||
if (ctrl) {
|
ctrl.group.forEach(mat => {
|
||||||
ctrl.toggle.checked = bool;
|
mat.visible = ctrl.toggle.checked;
|
||||||
ctrl.group.forEach(mat => {
|
});
|
||||||
mat.visible = bool;
|
if (ctrl.toggle.checked) {
|
||||||
});
|
kiri.api.event.emit("stack.show", label);
|
||||||
}
|
} else {
|
||||||
}
|
kiri.api.event.emit("stack.hide", label)
|
||||||
|
}
|
||||||
function create(name, view) {
|
})
|
||||||
if (stacks[name]) {
|
};
|
||||||
return stacks[name];
|
let color = lmap[label].color.check;
|
||||||
}
|
if (color !== undefined) {
|
||||||
const stack = stacks[name] = {
|
let div = document.createElement('div');
|
||||||
layers: [ ],
|
div.setAttribute('class', 'color');
|
||||||
obj: new KIRI.Stack(view, freeMem),
|
div.style.backgroundColor = `#${color.toString(16).padStart(6,0)}`;
|
||||||
add: function(layers) {
|
check.toggle.parentNode.appendChild(div);
|
||||||
let lmap = layers.layers;
|
check.toggle.classList.add("color");
|
||||||
let map = stack.obj.addLayers(layers);
|
|
||||||
for (const [label, mats] of Object.entries(map)) {
|
|
||||||
if (!DYN[label]) {
|
|
||||||
let check = DYN[label] = {
|
|
||||||
group: [],
|
|
||||||
toggle: UC.newBoolean(label, (abc) => {
|
|
||||||
ctrl.group.forEach(mat => {
|
|
||||||
mat.visible = ctrl.toggle.checked;
|
|
||||||
});
|
|
||||||
if (ctrl.toggle.checked) {
|
|
||||||
KIRI.api.event.emit("stack.show", label);
|
|
||||||
} else {
|
|
||||||
KIRI.api.event.emit("stack.hide", label)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let color = lmap[label].color.check;
|
|
||||||
if (color !== undefined) {
|
|
||||||
let div = document.createElement('div');
|
|
||||||
div.setAttribute('class', 'color');
|
|
||||||
div.style.backgroundColor = `#${color.toString(16).padStart(6,0)}`;
|
|
||||||
check.toggle.parentNode.appendChild(div);
|
|
||||||
check.toggle.classList.add("color");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const ctrl = DYN[label];
|
|
||||||
ctrl.group.appendAll(mats);
|
|
||||||
ctrl.toggle.checked = mats.state;
|
|
||||||
}
|
}
|
||||||
tallest = Math.max(tallest, stack.obj.size());
|
const ctrl = DYN[label];
|
||||||
},
|
ctrl.group.appendAll(mats);
|
||||||
remove: function() {
|
ctrl.toggle.checked = mats.state;
|
||||||
view.remove(stack.obj.view);
|
|
||||||
THREE.dispose(stack.obj.view);
|
|
||||||
},
|
|
||||||
clear: function() {
|
|
||||||
view.remove(stack.obj.view);
|
|
||||||
THREE.dispose(stack.obj.view);
|
|
||||||
},
|
|
||||||
button: function(label, action) {
|
|
||||||
UC.newRow([ UC.newButton(label, action) ]);
|
|
||||||
}
|
}
|
||||||
};
|
tallest = Math.max(tallest, stack.obj.size());
|
||||||
return stack;
|
},
|
||||||
}
|
remove: function() {
|
||||||
|
view.remove(stack.obj.view);
|
||||||
function remove(name) {
|
THREE.dispose(stack.obj.view);
|
||||||
const stack = stacks[name];
|
},
|
||||||
if (stack) {
|
clear: function() {
|
||||||
stack.remove();
|
view.remove(stack.obj.view);
|
||||||
delete stacks[name];
|
THREE.dispose(stack.obj.view);
|
||||||
|
},
|
||||||
|
button: function(label, action) {
|
||||||
|
UC.newRow([ UC.newButton(label, action) ]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function getRange() {
|
|
||||||
return {min, max, tallest};
|
|
||||||
}
|
|
||||||
|
|
||||||
function setRange(newMin, newMax) {
|
|
||||||
Object.values(stacks).forEach(stack => {
|
|
||||||
stack.obj.setVisible(newMin,newMax);
|
|
||||||
});
|
|
||||||
min = newMin;
|
|
||||||
max = newMax;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setFraction(frac) {
|
|
||||||
Object.values(stacks).forEach(stack => {
|
|
||||||
stack.obj.setLastFraction(frac);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
KIRI.stacks = {
|
|
||||||
clear,
|
|
||||||
create,
|
|
||||||
rotate,
|
|
||||||
remove,
|
|
||||||
getStack,
|
|
||||||
getStacks,
|
|
||||||
getRange,
|
|
||||||
setRange,
|
|
||||||
getLabels,
|
|
||||||
setVisible,
|
|
||||||
setFraction,
|
|
||||||
setFreeMem
|
|
||||||
};
|
};
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
})();
|
function remove(name) {
|
||||||
|
const stack = stacks[name];
|
||||||
|
if (stack) {
|
||||||
|
stack.remove();
|
||||||
|
delete stacks[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRange() {
|
||||||
|
return {min, max, tallest};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRange(newMin, newMax) {
|
||||||
|
Object.values(stacks).forEach(stack => {
|
||||||
|
stack.obj.setVisible(newMin,newMax);
|
||||||
|
});
|
||||||
|
min = newMin;
|
||||||
|
max = newMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFraction(frac) {
|
||||||
|
Object.values(stacks).forEach(stack => {
|
||||||
|
stack.obj.setLastFraction(frac);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
kiri.stacks = {
|
||||||
|
clear,
|
||||||
|
create,
|
||||||
|
rotate,
|
||||||
|
remove,
|
||||||
|
getStack,
|
||||||
|
getStacks,
|
||||||
|
getRange,
|
||||||
|
setRange,
|
||||||
|
getLabels,
|
||||||
|
setVisible,
|
||||||
|
setFraction,
|
||||||
|
setFreeMem
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,12 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function () {
|
// dep: moto.broker
|
||||||
|
// dep: kiri.api
|
||||||
|
// dep: kiri.utils
|
||||||
|
gapp.register("kiri.stats", [], (root, exports) => {
|
||||||
|
|
||||||
const { data, kiri, gapp } = self;
|
const { data, kiri, gapp } = root;
|
||||||
const { broker } = gapp;
|
const { broker } = gapp;
|
||||||
const { api, utils } = kiri;
|
const { api, utils } = kiri;
|
||||||
const { js2o, o2js } = utils;
|
const { js2o, o2js } = utils;
|
||||||
|
|
@ -55,4 +58,4 @@ class Stats {
|
||||||
|
|
||||||
kiri.stats = new Stats();
|
kiri.stats = new Stats();
|
||||||
|
|
||||||
})();
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,100 +1,112 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
kiri.load(function(API) {
|
// dep: add.three
|
||||||
// circle
|
// dep: kiri.api
|
||||||
let pgeo = new THREE.CircleGeometry(8, 30);
|
gapp.register("kiri.tools", [], (root, exports) => {
|
||||||
let pmat = new THREE.MeshBasicMaterial({color: 0xff0000, opacity: 0.5, transparent: true});
|
|
||||||
let pmesh = new THREE.Mesh(pgeo, pmat);
|
const { kiri } = root;
|
||||||
// circle outline
|
const { api } = kiri;
|
||||||
let pi = pgeo.index.array;
|
|
||||||
let pp = pgeo.attributes.position.array;
|
// circle
|
||||||
let np = [];
|
let pgeo = new THREE.CircleGeometry(8, 30);
|
||||||
for (let i=0; i<pi.length; i++) {
|
let pmat = new THREE.MeshBasicMaterial({color: 0xff0000, opacity: 0.5, transparent: true});
|
||||||
if (pi[i] * pi[i+1]) {
|
let pmesh = new THREE.Mesh(pgeo, pmat);
|
||||||
np.push(pp[pi[i]*3+0]);
|
// circle outline
|
||||||
np.push(pp[pi[i]*3+1]);
|
let pi = pgeo.index.array;
|
||||||
np.push(pp[pi[i]*3+2]);
|
let pp = pgeo.attributes.position.array;
|
||||||
np.push(pp[pi[i+1]*3+0]);
|
let np = [];
|
||||||
np.push(pp[pi[i+1]*3+1]);
|
for (let i=0; i<pi.length; i++) {
|
||||||
np.push(pp[pi[i+1]*3+2]);
|
if (pi[i] * pi[i+1]) {
|
||||||
}
|
np.push(pp[pi[i]*3+0]);
|
||||||
|
np.push(pp[pi[i]*3+1]);
|
||||||
|
np.push(pp[pi[i]*3+2]);
|
||||||
|
np.push(pp[pi[i+1]*3+0]);
|
||||||
|
np.push(pp[pi[i+1]*3+1]);
|
||||||
|
np.push(pp[pi[i+1]*3+2]);
|
||||||
}
|
}
|
||||||
let wgeo = new THREE.BufferGeometry();
|
}
|
||||||
wgeo.setAttribute('position', new THREE.BufferAttribute(np.toFloat32(), 3));
|
|
||||||
let wmat = new THREE.LineBasicMaterial({ color: 0x883333 });
|
|
||||||
let wmesh = new THREE.LineSegments(wgeo, wmat);
|
|
||||||
pmesh.add(wmesh);
|
|
||||||
|
|
||||||
let alert;
|
let wgeo = new THREE.BufferGeometry();
|
||||||
let lastobj;
|
wgeo.setAttribute('position', new THREE.BufferAttribute(np.toFloat32(), 3));
|
||||||
let lastface;
|
|
||||||
let enabled = false;
|
|
||||||
|
|
||||||
function cleanup() {
|
let wmat = new THREE.LineBasicMaterial({ color: 0x883333 });
|
||||||
if (lastobj) {
|
let wmesh = new THREE.LineSegments(wgeo, wmat);
|
||||||
lastobj.remove(pmesh);
|
pmesh.add(wmesh);
|
||||||
lastobj = undefined;
|
|
||||||
}
|
let alert;
|
||||||
|
let lastobj;
|
||||||
|
let lastface;
|
||||||
|
let enabled = false;
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
if (lastobj) {
|
||||||
|
lastobj.remove(pmesh);
|
||||||
|
lastobj = undefined;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function endit() {
|
function endit() {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
API.hide.alert(alert);
|
api.hide.alert(alert);
|
||||||
$('lay-flat').classList.remove('selected');
|
$('lay-flat').classList.remove('selected');
|
||||||
API.feature.hover = false;
|
api.feature.hover = false;
|
||||||
enabled = false;
|
enabled = false;
|
||||||
alert = undefined;
|
alert = undefined;
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
API.event.on('key.esc', endit);
|
|
||||||
API.event.on('tool.mesh.lay-flat', () => {
|
|
||||||
if (enabled) {
|
|
||||||
endit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (API.feature.hover) {
|
|
||||||
console.log('lay flat cannot pre-empt hover');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$('lay-flat').classList.add('selected');
|
|
||||||
API.feature.hover = true;
|
|
||||||
enabled = true;
|
|
||||||
alert = API.show.alert('[ESC] to end lay-flat operation', 600000);
|
|
||||||
});
|
|
||||||
API.event.on('mouse.hover', (ev) => {
|
|
||||||
if (!enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cleanup();
|
cleanup();
|
||||||
let { int, ints, event, point, type } = ev;
|
}
|
||||||
if (type === 'widget') {
|
}
|
||||||
lastface = int.face;
|
|
||||||
let obj = lastobj = int.object;
|
api.event.on('key.esc', endit);
|
||||||
let norm = int.face.normal;
|
|
||||||
let opos = obj.widget.track.pos;
|
api.event.on('tool.mesh.lay-flat', () => {
|
||||||
obj.add(pmesh);
|
if (enabled) {
|
||||||
pmesh.position.x = point.x - opos.x + norm.x * 0.1;
|
|
||||||
pmesh.position.y = -point.z - opos.y + norm.y * 0.1;
|
|
||||||
pmesh.position.z = point.y + norm.z * 0.1;
|
|
||||||
let q = new THREE.Quaternion().setFromUnitVectors(
|
|
||||||
new THREE.Vector3(0,0,1),
|
|
||||||
new THREE.Vector3(norm.x,norm.y,norm.z)
|
|
||||||
);
|
|
||||||
pmesh.setRotationFromQuaternion(q);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
API.event.on('mouse.hover.up', (ev) => {
|
|
||||||
if (!enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let { int, point, object } = ev;
|
|
||||||
if (!object) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let q = new THREE.Quaternion().setFromUnitVectors(lastface.normal, new THREE.Vector3(0,0,-1));
|
|
||||||
API.selection.rotate(q);
|
|
||||||
endit();
|
endit();
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
|
if (api.feature.hover) {
|
||||||
|
console.log('lay flat cannot pre-empt hover');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('lay-flat').classList.add('selected');
|
||||||
|
api.feature.hover = true;
|
||||||
|
enabled = true;
|
||||||
|
alert = api.show.alert('[ESC] to end lay-flat operation', 600000);
|
||||||
|
});
|
||||||
|
|
||||||
|
api.event.on('mouse.hover', (ev) => {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
let { int, ints, event, point, type } = ev;
|
||||||
|
if (type === 'widget') {
|
||||||
|
lastface = int.face;
|
||||||
|
let obj = lastobj = int.object;
|
||||||
|
let norm = int.face.normal;
|
||||||
|
let opos = obj.widget.track.pos;
|
||||||
|
obj.add(pmesh);
|
||||||
|
pmesh.position.x = point.x - opos.x + norm.x * 0.1;
|
||||||
|
pmesh.position.y = -point.z - opos.y + norm.y * 0.1;
|
||||||
|
pmesh.position.z = point.y + norm.z * 0.1;
|
||||||
|
let q = new THREE.Quaternion().setFromUnitVectors(
|
||||||
|
new THREE.Vector3(0,0,1),
|
||||||
|
new THREE.Vector3(norm.x,norm.y,norm.z)
|
||||||
|
);
|
||||||
|
pmesh.setRotationFromQuaternion(q);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
api.event.on('mouse.hover.up', (ev) => {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let { int, point, object } = ev;
|
||||||
|
if (!object) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let q = new THREE.Quaternion().setFromUnitVectors(lastface.normal, new THREE.Vector3(0,0,-1));
|
||||||
|
api.selection.rotate(q);
|
||||||
|
endit();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function() {
|
gapp.register("kiri.ui", [], (root, exports) => {
|
||||||
|
|
||||||
if (kiri.ui) return;
|
if (kiri.ui) return;
|
||||||
|
|
||||||
|
|
@ -939,4 +939,4 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
// dep: data.local
|
// dep: data.local
|
||||||
// dep: main.kiri
|
// dep: main.kiri
|
||||||
// use: moto.ajax
|
// use: moto.ajax
|
||||||
(function () {
|
gapp.register("kiri.utils", [], (root, exports) => {
|
||||||
|
|
||||||
const { kiri, moto, data } = self;
|
const { kiri, moto, data } = root;
|
||||||
|
|
||||||
function parseOpt(ov) {
|
function parseOpt(ov) {
|
||||||
let opt = {}, kv, kva;
|
let opt = {}, kv, kva;
|
||||||
|
|
@ -96,7 +96,7 @@ function areEqual(o1, o2) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
kiri.utils = {
|
exports({
|
||||||
areEqual,
|
areEqual,
|
||||||
parseOpt,
|
parseOpt,
|
||||||
encodeOpt,
|
encodeOpt,
|
||||||
|
|
@ -107,6 +107,6 @@ kiri.utils = {
|
||||||
avgc,
|
avgc,
|
||||||
rgb,
|
rgb,
|
||||||
a2c
|
a2c
|
||||||
};
|
});
|
||||||
|
|
||||||
})();
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function() {
|
gapp.register("kiri.widget", [], (root, exports) => {
|
||||||
|
|
||||||
if (self.kiri.Widget) return;
|
const { base, kiri } = root;
|
||||||
|
|
||||||
const { base, kiri } = self;
|
|
||||||
const { api, driver, utils } = kiri;
|
const { api, driver, utils } = kiri;
|
||||||
const { config, util, polygons } = base;
|
const { config, util, polygons } = base;
|
||||||
const { Mesh, newPoint, verticesToPoints } = base;
|
const { Mesh, newPoint, verticesToPoints } = base;
|
||||||
|
|
@ -963,4 +961,4 @@ Widget.deleteFromState = function(id,ondone) {
|
||||||
kiri.Widget = Widget;
|
kiri.Widget = Widget;
|
||||||
kiri.newWidget = newWidget;
|
kiri.newWidget = newWidget;
|
||||||
|
|
||||||
})();
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
(function () {
|
gapp.register("kiri.widgets", [], (root, exports) => {
|
||||||
|
|
||||||
const { data, kiri, moto, noop } = self;
|
const { data, kiri, moto, noop } = root;
|
||||||
const { api, consts, utils, newWidget, Widget } = kiri;
|
const { api, consts, utils, newWidget, Widget } = kiri;
|
||||||
const { space } = moto;
|
const { space } = moto;
|
||||||
|
|
||||||
|
|
@ -110,4 +110,4 @@ const widgets = api.widgets = {
|
||||||
for(fn) { widgets.each(fn) },
|
for(fn) { widgets.each(fn) },
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
});
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,9 @@ a, a:hover, a:visited {
|
||||||
transform: translateX(-50%) translateY(-50%);
|
transform: translateX(-50%) translateY(-50%);
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
}
|
}
|
||||||
#app-name, #app-mode-name, #app-acct {
|
#app-name, #app-acct {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
#app-mode-name {
|
|
||||||
border-bottom: 2px dotted grey;
|
|
||||||
}
|
|
||||||
#app,div {
|
#app,div {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
@ -296,18 +293,16 @@ th, tr, td, label {
|
||||||
margin: 0 5px 0 5px;
|
margin: 0 5px 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#mode-info.hoverpop {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
#mode-info {
|
#mode-info {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
xposition: absolute;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-size: smaller;
|
font-size: smaller;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
left: 100%;
|
top: 50%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 1px 5px 1px 5px;
|
transform: translateY(-50%);
|
||||||
|
padding: 1px 0 1px 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@
|
||||||
<div id="top-sep"></div>
|
<div id="top-sep"></div>
|
||||||
<div id="top" class="f-row">
|
<div id="top" class="f-row">
|
||||||
<div id="app-mode">
|
<div id="app-mode">
|
||||||
<div id="app-mode-name"></div>
|
|
||||||
<div id="mode-info" class="f-row">
|
<div id="mode-info" class="f-row">
|
||||||
|
<label>Mode</label><div id="app-mode-name"></div>
|
||||||
<label>Device</label><div id="mode-device"></div>
|
<label>Device</label><div id="mode-device"></div>
|
||||||
<label>Profile</label><div id="mode-profile"></div>
|
<label>Profile</label><div id="mode-profile"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue