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
|
|
@ -5,7 +5,7 @@
|
|||
// dep: kiri.api
|
||||
gapp.register("kiri.alerts", [], (root, exports) => {
|
||||
|
||||
const { kiri } = self;
|
||||
const { kiri } = root;
|
||||
const { api } = kiri;
|
||||
|
||||
let alerts = [];
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
// use: kiri.settings
|
||||
gapp.register("kiri.api", [], (root, exports) => {
|
||||
|
||||
const { data, kiri, noop } = self;
|
||||
const { data, kiri, noop } = root;
|
||||
const { consts, utils } = kiri;
|
||||
const { areEqual, parseOpt, encodeOpt, ajax, o2js, js2o } = utils;
|
||||
const { LISTS } = consts;
|
||||
|
|
|
|||
|
|
@ -6,387 +6,388 @@
|
|||
// dep: geo.base
|
||||
gapp.register("kiri.codec", [], (root, exports) => {
|
||||
|
||||
const BASE = self.base, KIRI = self.kiri, handlers = {};
|
||||
const freeMem = true;
|
||||
const zeroOut = true;
|
||||
const { base, kiri } = root;
|
||||
const handlers = {};
|
||||
const freeMem = true;
|
||||
const zeroOut = true;
|
||||
|
||||
const codec = exports({
|
||||
undef: undefined,
|
||||
encode: encode,
|
||||
decode: decode,
|
||||
registerDecoder: registerDecoder,
|
||||
allocFloat32Array: allocFloat32Array,
|
||||
encodePointArray,
|
||||
decodePointArray,
|
||||
toCodable
|
||||
});
|
||||
const codec = exports({
|
||||
undef: undefined,
|
||||
encode: encode,
|
||||
decode: decode,
|
||||
registerDecoder: registerDecoder,
|
||||
allocFloat32Array: allocFloat32Array,
|
||||
encodePointArray,
|
||||
decodePointArray,
|
||||
toCodable
|
||||
});
|
||||
|
||||
function toCodable(object) {
|
||||
switch (typeof object) {
|
||||
function toCodable(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':
|
||||
return undefined;
|
||||
case 'string':
|
||||
case 'number':
|
||||
return object;
|
||||
break;
|
||||
case 'object':
|
||||
if (Array.isArray(object)) {
|
||||
return object.map(v => toCodable(v));
|
||||
if (Array.isArray(val)) {
|
||||
val = val.map(v => toCodable(v));
|
||||
} else {
|
||||
val = toCodable(val);
|
||||
}
|
||||
default:
|
||||
o[key] = val;
|
||||
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) {
|
||||
if (arg.byteLength) {
|
||||
// already a float array
|
||||
return arg;
|
||||
}
|
||||
if (Array.isArray(arg)) {
|
||||
// create float array from array
|
||||
return new Float32Array(arg);
|
||||
}
|
||||
// usually a number (size) for array
|
||||
function allocFloat32Array(arg) {
|
||||
if (arg.byteLength) {
|
||||
// already a float array
|
||||
return arg;
|
||||
}
|
||||
if (Array.isArray(arg)) {
|
||||
// create float array from array
|
||||
return new Float32Array(arg);
|
||||
}
|
||||
// usually a number (size) for array
|
||||
return new Float32Array(arg);
|
||||
}
|
||||
|
||||
function encode(o, state) {
|
||||
state = state || {};
|
||||
if (o === null) return null;
|
||||
if (o === undefined) return undefined;
|
||||
if (Array.isArray(o)) {
|
||||
let arr = new Array(o.length), i=0;
|
||||
while (i < o.length) arr[i] = encode(o[i++], state);
|
||||
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;
|
||||
function encode(o, state) {
|
||||
state = state || {};
|
||||
if (o === null) return null;
|
||||
if (o === undefined) return undefined;
|
||||
if (Array.isArray(o)) {
|
||||
let arr = new Array(o.length), i=0;
|
||||
while (i < o.length) arr[i] = encode(o[i++], state);
|
||||
return arr;
|
||||
}
|
||||
|
||||
function decode(o, state) {
|
||||
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);
|
||||
switch (typeof(o)) {
|
||||
case 'string':
|
||||
case 'number':
|
||||
return o;
|
||||
}
|
||||
switch (typeof(o)) {
|
||||
case 'string':
|
||||
case 'number':
|
||||
return o;
|
||||
case 'object':
|
||||
if (o.type && handlers[o.type]) return handlers[o.type](o,state);
|
||||
return genODecode(o, state);
|
||||
}
|
||||
return null;
|
||||
case 'object':
|
||||
if (o.encode) return o.encode(state);
|
||||
return genOEncode(o, state);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function registerDecoder(type, handler) {
|
||||
handlers[type] = handler;
|
||||
function decode(o, state) {
|
||||
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;
|
||||
}
|
||||
|
||||
function genOEncode(o, state) {
|
||||
if (o instanceof Float32Array) return o;
|
||||
let out = {};
|
||||
for (let k in o) {
|
||||
if (o.hasOwnProperty(k)) out[k] = encode(o[k], state);
|
||||
}
|
||||
return out;
|
||||
switch (typeof(o)) {
|
||||
case 'string':
|
||||
case 'number':
|
||||
return o;
|
||||
case 'object':
|
||||
if (o.type && handlers[o.type]) return handlers[o.type](o,state);
|
||||
return genODecode(o, state);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function genODecode(o, state) {
|
||||
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;
|
||||
function registerDecoder(type, handler) {
|
||||
handlers[type] = handler;
|
||||
}
|
||||
|
||||
function genOEncode(o, state) {
|
||||
if (o instanceof Float32Array) return o;
|
||||
let out = {};
|
||||
for (let k in o) {
|
||||
if (o.hasOwnProperty(k)) out[k] = encode(o[k], state);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** ******************************************************************
|
||||
* Object Class CODEC Functions
|
||||
******************************************************************* */
|
||||
function genODecode(o, state) {
|
||||
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_;
|
||||
const geo = this.getGeoVertices();
|
||||
const coded = {
|
||||
type: 'widget',
|
||||
id: this.id,
|
||||
ver: 1, // for better future encodings
|
||||
json: json, // safe for JSON (float32array mess)
|
||||
group: this.group.id,
|
||||
track: this.track,
|
||||
geo: json ? Array.from(geo) : geo
|
||||
};
|
||||
return coded;
|
||||
/** ******************************************************************
|
||||
* Object Class CODEC Functions
|
||||
******************************************************************* */
|
||||
|
||||
kiri.Widget.prototype.encode = function(state) {
|
||||
const json = state._json_;
|
||||
const geo = this.getGeoVertices();
|
||||
const coded = {
|
||||
type: 'widget',
|
||||
id: this.id,
|
||||
ver: 1, // for better future encodings
|
||||
json: json, // safe for JSON (float32array mess)
|
||||
group: this.group.id,
|
||||
track: this.track,
|
||||
geo: json ? Array.from(geo) : geo
|
||||
};
|
||||
return coded;
|
||||
};
|
||||
|
||||
registerDecoder('widget', function(v, state) {
|
||||
const id = v.id,
|
||||
group = v.group || id,
|
||||
track = v.track || undefined,
|
||||
widget = KIRI.newWidget(id, KIRI.Widget.Groups.forid(group));
|
||||
widget.loadVertices(v.json ? v.geo.toFloat32() : v.geo);
|
||||
widget.saved = Date.now();
|
||||
if (track && track.pos) {
|
||||
widget.track = track;
|
||||
widget.move(track.pos.x, track.pos.y, track.pos.z, true);
|
||||
}
|
||||
return widget;
|
||||
});
|
||||
registerDecoder('widget', function(v, state) {
|
||||
const id = v.id,
|
||||
group = v.group || id,
|
||||
track = v.track || undefined,
|
||||
widget = kiri.newWidget(id, kiri.Widget.Groups.forid(group));
|
||||
widget.loadVertices(v.json ? v.geo.toFloat32() : v.geo);
|
||||
widget.saved = Date.now();
|
||||
if (track && track.pos) {
|
||||
widget.track = track;
|
||||
widget.move(track.pos.x, track.pos.y, track.pos.z, true);
|
||||
}
|
||||
return widget;
|
||||
});
|
||||
|
||||
KIRI.Slice.prototype.encode = function(state) {
|
||||
const rv = {
|
||||
type: 'slice',
|
||||
z: this.z,
|
||||
index: this.index,
|
||||
layers: encode(this.layers, state)
|
||||
};
|
||||
// aggressively free memory
|
||||
if (freeMem) this.layers = undefined;
|
||||
return rv;
|
||||
kiri.Slice.prototype.encode = function(state) {
|
||||
const rv = {
|
||||
type: 'slice',
|
||||
z: this.z,
|
||||
index: this.index,
|
||||
layers: encode(this.layers, state)
|
||||
};
|
||||
// aggressively free memory
|
||||
if (freeMem) this.layers = undefined;
|
||||
return rv;
|
||||
};
|
||||
|
||||
registerDecoder('slice', function(v, state) {
|
||||
let slice = KIRI.newSlice(v.z, state.mesh ? state.mesh.newGroup() : null);
|
||||
registerDecoder('slice', function(v, state) {
|
||||
let slice = kiri.newSlice(v.z, state.mesh ? state.mesh.newGroup() : null);
|
||||
|
||||
slice.index = v.index;
|
||||
slice.layers = decode(v.layers, state)
|
||||
slice.index = v.index;
|
||||
slice.layers = decode(v.layers, state)
|
||||
|
||||
return slice;
|
||||
});
|
||||
return slice;
|
||||
});
|
||||
|
||||
KIRI.Layers.prototype.encode = function(state) {
|
||||
let enc = {
|
||||
type: 'layers',
|
||||
layers: Object.keys(this.layers),
|
||||
data: Object.values(this.layers).map(layer => {
|
||||
const e = {
|
||||
polys: encode(layer.polys, state),
|
||||
lines: encodePointArray(layer.lines, state),
|
||||
faces: codec.allocFloat32Array(layer.faces),
|
||||
cface: layer.cface || codec.undef,
|
||||
color: layer.color,
|
||||
paths: layer.paths.map(lp => {
|
||||
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 {
|
||||
kiri.Layers.prototype.encode = function(state) {
|
||||
let enc = {
|
||||
type: 'layers',
|
||||
layers: Object.keys(this.layers),
|
||||
data: Object.values(this.layers).map(layer => {
|
||||
const e = {
|
||||
polys: encode(layer.polys, state),
|
||||
lines: encodePointArray(layer.lines, state),
|
||||
faces: codec.allocFloat32Array(layer.faces),
|
||||
cface: layer.cface || codec.undef,
|
||||
color: layer.color,
|
||||
paths: layer.paths.map(lp => {
|
||||
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: v.data[i].cpath,
|
||||
off: v.data[i].off
|
||||
cpath: layer.cpath || codec.undef,
|
||||
off: layer.off
|
||||
};
|
||||
// fixup null -> Infinity in material counts (JSON stringify sucks)
|
||||
if (d.cface) {
|
||||
d.cface.forEach(rec => {
|
||||
if (rec.count === null) {
|
||||
rec.count = Infinity;
|
||||
}
|
||||
});
|
||||
if (zeroOut && state.zeros && e.faces.length) {
|
||||
state.zeros.push(e.faces.buffer);
|
||||
}
|
||||
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;
|
||||
// console.log('-->',layer,e);
|
||||
return e;
|
||||
})
|
||||
};
|
||||
// aggressively free memory
|
||||
if (freeMem) this.init();
|
||||
return enc;
|
||||
};
|
||||
|
||||
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);
|
||||
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,
|
||||
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 (!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;
|
||||
});
|
||||
|
||||
if (zeroOut && state.zeros) {
|
||||
state.zeros.push(array.buffer);
|
||||
}
|
||||
|
||||
return array;
|
||||
if (zeroOut && state.zeros) {
|
||||
state.zeros.push(array.buffer);
|
||||
}
|
||||
|
||||
function decodePointArray(array) {
|
||||
if (!array) return null;
|
||||
return array;
|
||||
}
|
||||
|
||||
let vid = 0,
|
||||
pid = 0,
|
||||
points = new Array(array.length/3);
|
||||
function decodePointArray(array) {
|
||||
if (!array) return null;
|
||||
|
||||
while (vid < array.length) {
|
||||
points[pid++] = BASE.newPoint(array[vid++], array[vid++], array[vid++]);
|
||||
}
|
||||
let vid = 0,
|
||||
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) {
|
||||
if (!state.poly) state.poly = {};
|
||||
return points;
|
||||
}
|
||||
|
||||
let cached = state.poly[this.id];
|
||||
base.Polygon.prototype.encode = function(state) {
|
||||
if (!state.poly) state.poly = {};
|
||||
|
||||
if (cached) {
|
||||
return { type: 'poly', ref: this.id };
|
||||
}
|
||||
let cached = state.poly[this.id];
|
||||
|
||||
state.poly[this.id] = this;
|
||||
return {
|
||||
type: 'poly',
|
||||
id: this.id,
|
||||
array: encodePointArray(this.points, state, this.z),
|
||||
inner: encode(this.inner, state),
|
||||
parent: encode(this.parent, state),
|
||||
depth: this.depth,
|
||||
color: this.color,
|
||||
open: this.open
|
||||
};
|
||||
if (cached) {
|
||||
return { type: 'poly', ref: this.id };
|
||||
}
|
||||
|
||||
state.poly[this.id] = this;
|
||||
return {
|
||||
type: 'poly',
|
||||
id: this.id,
|
||||
array: encodePointArray(this.points, state, this.z),
|
||||
inner: encode(this.inner, state),
|
||||
parent: encode(this.parent, state),
|
||||
depth: this.depth,
|
||||
color: this.color,
|
||||
open: this.open
|
||||
};
|
||||
};
|
||||
|
||||
registerDecoder('poly', function(v, state) {
|
||||
if (!state.poly) state.poly = {};
|
||||
registerDecoder('poly', function(v, state) {
|
||||
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(),
|
||||
vid = 0;
|
||||
let poly = base.newPolygon(),
|
||||
vid = 0;
|
||||
|
||||
// if passed a normal array, convert to float32
|
||||
if (v.array.toFloat32) {
|
||||
v.array = v.array.toFloat32();
|
||||
}
|
||||
// if passed a normal array, convert to float32
|
||||
if (v.array.toFloat32) {
|
||||
v.array = v.array.toFloat32();
|
||||
}
|
||||
|
||||
while (vid < v.array.length) {
|
||||
poly.push(BASE.newPoint(v.array[vid++], v.array[vid++], v.array[vid++]));
|
||||
}
|
||||
while (vid < v.array.length) {
|
||||
poly.push(base.newPoint(v.array[vid++], v.array[vid++], v.array[vid++]));
|
||||
}
|
||||
|
||||
poly.id = v.id;
|
||||
poly.open = v.open;
|
||||
poly.id = v.id;
|
||||
poly.open = v.open;
|
||||
|
||||
state.poly[v.id] = poly;
|
||||
state.poly[v.id] = poly;
|
||||
|
||||
poly.inner = decode(v.inner, state);
|
||||
poly.parent = decode(v.parent, state);
|
||||
poly.depth = v.depth;
|
||||
poly.color = v.color;
|
||||
poly.inner = decode(v.inner, state);
|
||||
poly.parent = decode(v.parent, state);
|
||||
poly.depth = v.depth;
|
||||
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";
|
||||
|
||||
(function () {
|
||||
|
||||
const { kiri } = self;
|
||||
gapp.register("kiri.consts", [], (root, exports) => {
|
||||
|
||||
const COLOR = {
|
||||
wireframe: 0x444444,
|
||||
|
|
@ -124,7 +122,7 @@ const PMODES = {
|
|||
|
||||
const SEED = 'kiri-seed';
|
||||
|
||||
kiri.consts = {
|
||||
exports({
|
||||
PMODES,
|
||||
COLOR,
|
||||
LISTS,
|
||||
|
|
@ -132,6 +130,6 @@ kiri.consts = {
|
|||
VIEWS,
|
||||
SEED,
|
||||
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 */
|
||||
|
||||
kiri.load(function(API) {
|
||||
let stack = [];
|
||||
let stpos = 0;
|
||||
// dep: kiri.api
|
||||
// use: kiri.selection
|
||||
// use: moto.space
|
||||
gapp.register("kiri.do", [], (root, events) => {
|
||||
|
||||
let event = API.event;
|
||||
let moved = {x:0, y:0};
|
||||
let msgid;
|
||||
const { kiri, moto } = root;
|
||||
const { api } = kiri;
|
||||
const { space } = moto;
|
||||
const { event } = api;
|
||||
|
||||
event.on("init-done", () => {
|
||||
$('undo').onclick = undo;
|
||||
$('redo').onclick = redo;
|
||||
});
|
||||
let stack = [];
|
||||
let stpos = 0;
|
||||
let moved = { x: 0, y: 0 };
|
||||
let msgid;
|
||||
|
||||
let undo = API.doit.undo = function() {
|
||||
if (stpos) {
|
||||
action(stack[--stpos].undo);
|
||||
} else {
|
||||
message('nothing to undo');
|
||||
}
|
||||
};
|
||||
event.on("init-done", () => {
|
||||
$('undo').onclick = undo;
|
||||
$('redo').onclick = redo;
|
||||
});
|
||||
|
||||
let redo = API.doit.redo = function() {
|
||||
if (stpos < stack.length) {
|
||||
action(stack[stpos++].redo);
|
||||
} else {
|
||||
message('nothing to redo');
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
let undo = api.doit.undo = function() {
|
||||
if (stpos) {
|
||||
action(stack[--stpos].undo);
|
||||
} else {
|
||||
message('nothing to undo');
|
||||
}
|
||||
};
|
||||
|
||||
function message(txt) {
|
||||
if (msgid) {
|
||||
API.hide.alert(msgid);
|
||||
}
|
||||
msgid = API.show.alert(txt);
|
||||
let redo = api.doit.redo = function() {
|
||||
if (stpos < stack.length) {
|
||||
action(stack[stpos++].redo);
|
||||
} else {
|
||||
message('nothing to redo');
|
||||
}
|
||||
};
|
||||
|
||||
function action(rec) {
|
||||
switch (rec.type) {
|
||||
case 'move':
|
||||
for (let w of rec.widgets) {
|
||||
w._move(rec.dist.x, rec.dist.y, 0);
|
||||
}
|
||||
break;
|
||||
case 'rotate':
|
||||
for (let w of rec.widgets) {
|
||||
w.rotate(rec.x, rec.y, rec.z);
|
||||
}
|
||||
break;
|
||||
case 'scale':
|
||||
for (let w of rec.widgets) {
|
||||
w._scale(rec.x, rec.y, rec.z);
|
||||
}
|
||||
API.selection.update_info();
|
||||
break;
|
||||
}
|
||||
API.space.update();
|
||||
updateButtons();
|
||||
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) {
|
||||
if (msgid) {
|
||||
api.hide.alert(msgid);
|
||||
}
|
||||
msgid = api.show.alert(txt);
|
||||
}
|
||||
|
||||
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
|
||||
function action(rec) {
|
||||
switch (rec.type) {
|
||||
case 'move':
|
||||
for (let w of rec.widgets) {
|
||||
w._move(rec.dist.x, rec.dist.y, 0);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
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
|
||||
break;
|
||||
case 'rotate':
|
||||
for (let w of rec.widgets) {
|
||||
w.rotate(rec.x, rec.y, rec.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}
|
||||
break;
|
||||
case 'scale':
|
||||
for (let w of rec.widgets) {
|
||||
w._scale(rec.x, rec.y, rec.z);
|
||||
}
|
||||
});
|
||||
moved = {x:0, y:0};
|
||||
api.selection.update_info();
|
||||
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";
|
||||
|
||||
(function() {
|
||||
// dep: main.kiri
|
||||
// use: data.index
|
||||
gapp.register("kiri.files", [], (root, exports) => {
|
||||
|
||||
const { kiri } = self;
|
||||
const { kiri } = self;
|
||||
|
||||
class Files {
|
||||
constructor(indexdb, options) {
|
||||
let store = this;
|
||||
this.db = indexdb;
|
||||
this.files = {};
|
||||
this.listeners = [];
|
||||
this.options = options || {};
|
||||
this.deferredHandler = null;
|
||||
this.concurrent = 0;
|
||||
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);
|
||||
}
|
||||
class Files {
|
||||
constructor(indexdb, options) {
|
||||
let store = this;
|
||||
this.db = indexdb;
|
||||
this.files = {};
|
||||
this.listeners = [];
|
||||
this.options = options || {};
|
||||
this.deferredHandler = null;
|
||||
this.concurrent = 0;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
function saveFileList(store) {
|
||||
store.db.put('files', store.files);
|
||||
notifyFileListeners(store);
|
||||
setOptions(options) {
|
||||
this.options = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
function notifyFileListeners(store) {
|
||||
for (let i=0; i<store.listeners.length; i++) {
|
||||
store.listeners[i](store.files);
|
||||
}
|
||||
get index() {
|
||||
return this.db;
|
||||
}
|
||||
|
||||
kiri.openFiles = function(indexdb, options) {
|
||||
return new Files(indexdb, options);
|
||||
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) {
|
||||
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";
|
||||
|
||||
(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
|
||||
window.addEventListener('message', msg => {
|
||||
|
|
@ -113,4 +120,4 @@ window.addEventListener('message', msg => {
|
|||
}
|
||||
});
|
||||
|
||||
})();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
"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 { space } = moto;
|
||||
const { COLOR, PMODES } = consts;
|
||||
|
|
@ -59,6 +61,11 @@ function prepareSlices(callback, scale = 1, offset = 0) {
|
|||
const slicing = api.widgets.all()
|
||||
.slice()
|
||||
.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 defvert = totalv / slicing.length;
|
||||
const track = {};
|
||||
|
|
@ -413,4 +420,4 @@ const functions = api.function = {
|
|||
clear_progress() { complete = {} }
|
||||
};
|
||||
|
||||
})();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,11 +2,15 @@
|
|||
|
||||
"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, moto } = self;
|
||||
const { base, kiri } = root;
|
||||
const { api, catalog, conf, consts, space } = kiri;
|
||||
const { sdb, stats, js2o, o2js, platform, selection, ui, uc } = api;
|
||||
const { VIEWS, MODES, SEED } = consts;
|
||||
|
|
@ -2287,11 +2291,9 @@
|
|||
uc.hoverPop(ui.ltact, { group: hpops, target: $('pop-slice') });
|
||||
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.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($('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.onBlur([
|
||||
|
|
@ -2925,4 +2927,4 @@
|
|||
}
|
||||
}
|
||||
|
||||
})();
|
||||
});
|
||||
|
|
|
|||
143
src/kiri/lang.js
143
src/kiri/lang.js
|
|
@ -2,81 +2,80 @@
|
|||
|
||||
"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: {} };
|
||||
const KDFL = 'en-us';
|
||||
let lset = navigator.language.toLocaleLowerCase();
|
||||
|
||||
let lset = navigator.language.toLocaleLowerCase();
|
||||
|
||||
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';
|
||||
}
|
||||
LANG.map = function(key) {
|
||||
if (!key) {
|
||||
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";
|
||||
|
||||
(function() {
|
||||
let KIRI = self.kiri,
|
||||
BASE = self.base,
|
||||
POLY = BASE.polygons,
|
||||
newPolygon = BASE.newPolygon;
|
||||
// dep: geo.base
|
||||
// dep: geo.polygon
|
||||
// dep: geo.polygons
|
||||
gapp.register("kiri.layers", [], (root, exports) => {
|
||||
|
||||
class Layers {
|
||||
constructor() {
|
||||
this.init();
|
||||
}
|
||||
const { base, kiri } = root;
|
||||
const { polygons, newPolygon } = base;
|
||||
|
||||
init() {
|
||||
this.layers = {};
|
||||
this.profiles = {};
|
||||
this.stats = {
|
||||
contour: 0,
|
||||
flat_line: 0,
|
||||
flat_poly: 0,
|
||||
line_poly: 0,
|
||||
line: 0
|
||||
const POLY = base.polygons;
|
||||
|
||||
class Layers {
|
||||
constructor() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
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) {
|
||||
return this.layers[layer];
|
||||
}
|
||||
addLine(p1, p2) {
|
||||
this.current.lines.push(p1, p2);
|
||||
return this;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
addLines(lines, options) {
|
||||
if (options) {
|
||||
options.open = true;
|
||||
const polys = [];
|
||||
for (let i=0; i<lines.length-1; i += 2) {
|
||||
this.addLine(lines[i], lines[i+1]);
|
||||
this.stats.line++;
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
addPoly(poly, options) {
|
||||
return this.addPolys([poly], options);
|
||||
if (options && options.clean) {
|
||||
polys = polys.map(p => p.clean(true));
|
||||
}
|
||||
|
||||
addPolys(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.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;
|
||||
}
|
||||
|
||||
// z planar closed polygonal areas
|
||||
addAreas(polys, options) {
|
||||
const faces = this.current.faces;
|
||||
polys = Array.isArray(polys) ? polys : [ polys ];
|
||||
polys.forEach(poly => {
|
||||
// z planar closed polygonal areas
|
||||
addAreas(polys, options) {
|
||||
const faces = this.current.faces;
|
||||
polys = Array.isArray(polys) ? polys : [ polys ];
|
||||
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 => {
|
||||
ep.forEachPoint(p => { faces.push(p.x, p.y, p.z) });
|
||||
});
|
||||
});
|
||||
if (options && options.outline) {
|
||||
this.addPolys(polys.clone(true));
|
||||
const cur = this.current;
|
||||
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)
|
||||
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;
|
||||
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;
|
||||
}
|
||||
if (opts.outline) {
|
||||
this.addPolys(exp.clone());
|
||||
}
|
||||
const faceidx = faces.length / 3;
|
||||
POLY.nest(exp).forEach((poly,i) => {
|
||||
poly.earcut().forEach(ep => {
|
||||
ep.forEachPoint(p => { faces.push(p.x, p.y, p.z) });
|
||||
});
|
||||
});
|
||||
const cur = this.current;
|
||||
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 {
|
||||
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.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));
|
||||
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));
|
||||
}
|
||||
}
|
||||
});
|
||||
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;
|
||||
}
|
||||
|
||||
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) ];
|
||||
}
|
||||
} 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);
|
||||
const shiftMatrix = new THREE.Matrix4().set(
|
||||
1, 0, 0, 0,
|
||||
-shift, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1
|
||||
);
|
||||
function flat(polys) {
|
||||
if (Array.isArray(polys)) {
|
||||
return POLY.flatten(polys.clone(true), [], true);
|
||||
} else {
|
||||
return POLY.flatten([polys.clone(true)], [], true);
|
||||
}
|
||||
}
|
||||
|
||||
const tempAngle = tA;
|
||||
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
|
||||
);
|
||||
function ProfiledContourGeometry(profileShape, contour, contourClosed) {
|
||||
|
||||
const translationMatrix = new THREE.Matrix4().set(
|
||||
1, 0, 0, contour[i].x,
|
||||
0, 1, 0, contour[i].y,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1,
|
||||
);
|
||||
contourClosed = contourClosed !== undefined ? contourClosed : true;
|
||||
|
||||
const cloneProfile = profile.clone();
|
||||
cloneProfile.applyMatrix4(shiftMatrix);
|
||||
cloneProfile.applyMatrix4(rotationMatrix);
|
||||
cloneProfile.applyMatrix4(translationMatrix);
|
||||
const profileGeometry = new THREE.ShapeBufferGeometry(profileShape);
|
||||
profileGeometry.rotateX(Math.PI * .5);
|
||||
|
||||
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 lastCorner = contourClosed == false ? contour.length - 1: contour.length;
|
||||
const shift = Math.tan(hA - Math.PI * .5);
|
||||
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++) {
|
||||
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 tempAngle = tA;
|
||||
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 a = nextPoint + profile.count * currCorner;
|
||||
const b = currPoint + profile.count * currCorner;
|
||||
const c = currPoint + profile.count * nextCorner;
|
||||
const d = nextPoint + profile.count * nextCorner;
|
||||
const translationMatrix = new THREE.Matrix4().set(
|
||||
1, 0, 0, contour[i].x,
|
||||
0, 1, 0, contour[i].y,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1,
|
||||
);
|
||||
|
||||
index.push(a, b, d);
|
||||
index.push(b, c, d);
|
||||
}
|
||||
}
|
||||
const cloneProfile = profile.clone();
|
||||
cloneProfile.applyMatrix4(shiftMatrix);
|
||||
cloneProfile.applyMatrix4(rotationMatrix);
|
||||
cloneProfile.applyMatrix4(translationMatrix);
|
||||
|
||||
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};
|
||||
faces.set(cloneProfile.array, cloneProfile.count * i * 3);
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
(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 { areEqual, parseOpt, encodeOpt, ajax, o2js, js2o, ls2o } = utils;
|
||||
const { feature, platform, selection, settings } = api;
|
||||
|
|
@ -943,4 +943,5 @@
|
|||
|
||||
// upon restore, seed presets
|
||||
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/
|
||||
*/
|
||||
|
||||
(function () {
|
||||
gapp.register("kiri.pack", [], (root, exports) => {
|
||||
|
||||
let kiri = self.kiri = self.kiri || {};
|
||||
if (kiri.Pack) return;
|
||||
const { kiri } = root;
|
||||
|
||||
kiri.Pack = Packer;
|
||||
|
||||
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) {
|
||||
class Packer {
|
||||
constructor(w, h, spacing) {
|
||||
this.root = { x: 0, y: 0, w: w, h: h };
|
||||
this.max = { w: 0, h: 0 };
|
||||
this.packed = false;
|
||||
|
|
@ -32,68 +20,77 @@
|
|||
this.pad = this.spacing / 2;
|
||||
}
|
||||
|
||||
Packer.prototype = {
|
||||
|
||||
simple: function (blocks) {
|
||||
let n = 0, block, x = 0, y = 0, mh = 0;
|
||||
while (n < blocks.length) {
|
||||
block = blocks[n++];
|
||||
block.fit = { x, y };
|
||||
this.max.w = Math.max(this.max.w, x + block.w);
|
||||
this.max.h = Math.max(this.max.h, y + block.h);
|
||||
mh = Math.max(mh, block.h);
|
||||
x += block.w + this.spacing;
|
||||
if (x > this.root.w) {
|
||||
x = 0;
|
||||
y += mh + this.spacing;
|
||||
mh = 0;
|
||||
}
|
||||
if (y > this.root.h) {
|
||||
return this;
|
||||
}
|
||||
simple(blocks) {
|
||||
let n = 0, block, x = 0, y = 0, mh = 0;
|
||||
while (n < blocks.length) {
|
||||
block = blocks[n++];
|
||||
block.fit = { x, y };
|
||||
this.max.w = Math.max(this.max.w, x + block.w);
|
||||
this.max.h = Math.max(this.max.h, y + block.h);
|
||||
mh = Math.max(mh, block.h);
|
||||
x += block.w + this.spacing;
|
||||
if (x > this.root.w) {
|
||||
x = 0;
|
||||
y += mh + this.spacing;
|
||||
mh = 0;
|
||||
}
|
||||
this.packed = true;
|
||||
return this;
|
||||
},
|
||||
|
||||
fit: function (blocks, simple) {
|
||||
if (simple) {
|
||||
return this.simple(blocks);
|
||||
if (y > this.root.h) {
|
||||
return this;
|
||||
}
|
||||
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";
|
||||
|
||||
(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 { ajax, js2o } = utils;
|
||||
const { space } = moto;
|
||||
|
|
@ -796,4 +801,4 @@ const platform = api.platform = {
|
|||
clear() { api.space.clear(); api.space.save(true) }
|
||||
};
|
||||
|
||||
})();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,12 @@
|
|||
|
||||
"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 { paths, util, newPoint, Polygon } = base;
|
||||
|
|
@ -10,10 +15,6 @@ const { tip2tipEmit } = paths;
|
|||
const { numOrDefault } = util;
|
||||
const { beltfact } = kiri.consts;
|
||||
|
||||
kiri.newPrint = function(settings, widgets, id) {
|
||||
return new Print(settings, widgets, id);
|
||||
};
|
||||
|
||||
class Print {
|
||||
constructor(settings, widgets, id) {
|
||||
this.id = id || new Date().getTime().toString(36);
|
||||
|
|
@ -523,7 +524,6 @@ class Print {
|
|||
|
||||
done({ output: scope.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";
|
||||
|
||||
(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 { space } = moto;
|
||||
|
||||
|
|
@ -293,4 +297,4 @@ const selection = api.selection = {
|
|||
meshes() { return selectedMeshes.slice() },
|
||||
};
|
||||
|
||||
}());
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,12 @@
|
|||
|
||||
"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 { api, conf, consts, utils } = kiri;
|
||||
|
|
@ -147,6 +152,7 @@ function updateSettings(opt = {}) {
|
|||
|
||||
$('mode-device').innerText = device.deviceName;
|
||||
$('mode-profile').innerText = `${cproc[mode]}${same ? '' : ' *'}`;
|
||||
$('mode-info').style.display = 'flex';
|
||||
}
|
||||
|
||||
function updateSettingsFromFields(setrec, uirec = api.ui, changes) {
|
||||
|
|
@ -544,7 +550,7 @@ function settingsImport(data, ask) {
|
|||
if (isWork) {
|
||||
api.platform.clear();
|
||||
kiri.codec.decode(data.work).forEach(widget => {
|
||||
platform.add(widget, 0, true);
|
||||
api.platform.add(widget, 0, true);
|
||||
});
|
||||
if (data.view) {
|
||||
space.view.load(data.view);
|
||||
|
|
@ -696,4 +702,4 @@ api.settings = {
|
|||
mode() { return settings.mode },
|
||||
};
|
||||
|
||||
})();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,30 +2,25 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
(function() {
|
||||
// dep: geo.base
|
||||
// dep: geo.polygons
|
||||
gapp.register("kiri.slice", [], (root, exports) => {
|
||||
|
||||
const KIRI = self.kiri,
|
||||
PRO = Slice.prototype,
|
||||
BASE = self.base,
|
||||
UTIL = BASE.util,
|
||||
POLY = BASE.polygons,
|
||||
NOKEY = BASE.key.NONE;
|
||||
const { base, kiri } = root;
|
||||
const { key, polygons } = base;
|
||||
const { util } = base;
|
||||
|
||||
KIRI.Top = Top;
|
||||
KIRI.Slice = Slice;
|
||||
KIRI.newTop = newTop;
|
||||
KIRI.newSlice = newSlice;
|
||||
const POLY = base.polygons;
|
||||
const NOKEY = key.NONE;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* worker code paths.
|
||||
*
|
||||
* @param {number} z offset from ground
|
||||
* @param {THREE.Group} [view] optional view parent object for layers
|
||||
* @constructor
|
||||
*/
|
||||
function Slice(z, view) {
|
||||
/**
|
||||
* 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
|
||||
* worker code paths.
|
||||
*/
|
||||
class Slice {
|
||||
|
||||
constructor (z, view) {
|
||||
this.z = z; // z-index
|
||||
this.index = 0; // slice index
|
||||
this.lines = null; // slice raw
|
||||
|
|
@ -38,63 +33,18 @@
|
|||
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.
|
||||
*/
|
||||
PRO.output = function() {
|
||||
output() {
|
||||
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
|
||||
*/
|
||||
PRO.clone = function(deep) {
|
||||
clone(deep) {
|
||||
const from = this, slice = newSlice(from.z, from.view);
|
||||
from.tops.forEach(function(top) {
|
||||
slice.addTop(top.poly.clone(deep));
|
||||
|
|
@ -102,16 +52,16 @@
|
|||
return slice;
|
||||
};
|
||||
|
||||
PRO.topPolys = function() {
|
||||
topPolys() {
|
||||
return this.tops.map(top => top.poly);
|
||||
};
|
||||
|
||||
PRO.topSimples = function() {
|
||||
topSimples() {
|
||||
return this.tops.map(top => top.simple);
|
||||
};
|
||||
|
||||
// FDM top intersect optimization
|
||||
PRO.topPolysFlat = function() {
|
||||
topPolysFlat() {
|
||||
if (this.topFlatPolys) {
|
||||
return this.topFlatPolys;
|
||||
}
|
||||
|
|
@ -119,7 +69,7 @@
|
|||
};
|
||||
|
||||
// FDM retract path routing using first shell
|
||||
PRO.topRouteFlat = function() {
|
||||
topRouteFlat() {
|
||||
if (this.topFlatRoutes) {
|
||||
return this.topFlatRoutes;
|
||||
}
|
||||
|
|
@ -128,27 +78,27 @@
|
|||
}
|
||||
|
||||
// CAM only
|
||||
PRO.topPolyInners = function() {
|
||||
topPolyInners() {
|
||||
return this.tops.map(top => top.poly.inner).flat().filter(poly => poly);
|
||||
};
|
||||
|
||||
// FDM / SLA only
|
||||
PRO.topInners = function() {
|
||||
topInners() {
|
||||
return this.tops.map(top => top.last).flat().filter(poly => poly);
|
||||
};
|
||||
|
||||
// FDM / SLA only
|
||||
PRO.topFillOff = function() {
|
||||
topFillOff() {
|
||||
return this.tops.map(top => top.fill_off).flat().filter(poly => poly);
|
||||
};
|
||||
|
||||
// FDM only
|
||||
PRO.topFill = function() {
|
||||
topFill() {
|
||||
return this.tops.map(top => top.fill_lines).flat().filter(poly => poly);
|
||||
};
|
||||
|
||||
// FDM only
|
||||
PRO.topShells = function() {
|
||||
topShells() {
|
||||
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
|
||||
* and infill computation when we detect the layers are the same.
|
||||
*/
|
||||
PRO.fingerprint = function() {
|
||||
fingerprint() {
|
||||
if (this.finger) {
|
||||
return this.finger;
|
||||
}
|
||||
|
|
@ -168,18 +118,18 @@
|
|||
/**
|
||||
* returns true if the layers' fingerprints are the same
|
||||
*/
|
||||
PRO.fingerprintSame = function(slice) {
|
||||
fingerprintSame(slice) {
|
||||
return slice ? POLY.fingerprintCompare(this.fingerprint(), slice.fingerprint()) : false;
|
||||
};
|
||||
|
||||
PRO.addTops = function(polys) {
|
||||
addTops(polys) {
|
||||
polys.forEach(p => {
|
||||
this.addTop(p);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
PRO.addTop = function(poly) {
|
||||
addTop(poly) {
|
||||
if (poly.length) {
|
||||
// standard legacy polygon
|
||||
let top = new Top(poly);
|
||||
|
|
@ -189,7 +139,7 @@
|
|||
} else {
|
||||
// frop top object passed back by slicePost()
|
||||
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_off = poly.fill_off;
|
||||
top.last = poly.last;
|
||||
|
|
@ -202,7 +152,7 @@
|
|||
throw "invalid top";
|
||||
};
|
||||
|
||||
PRO.findClosestPointTo = function(target) {
|
||||
findClosestPointTo(target) {
|
||||
let min, find;
|
||||
|
||||
if (this.tops.length) {
|
||||
|
|
@ -224,7 +174,7 @@
|
|||
return min;
|
||||
};
|
||||
|
||||
PRO.xray = function(dash = 3) {
|
||||
xray(dash = 3) {
|
||||
// console.log('xray', this);
|
||||
this.output().setLayer(`xp`, 0x888800).addPolys(this.topPolys());
|
||||
this.lines.forEach((line, i) => {
|
||||
|
|
@ -232,19 +182,65 @@
|
|||
const color = [ 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff ][group];
|
||||
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
|
||||
* @param {THREE.Group} view
|
||||
* @returns {Slice}
|
||||
* return innermost traces under a given top. for FDM, this represents
|
||||
* the outline shell that the fill touches. used by Print, Laser
|
||||
*/
|
||||
function newSlice(z, view) {
|
||||
return new Slice(z, view);
|
||||
innerShells() {
|
||||
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";
|
||||
|
||||
(function() {
|
||||
gapp.register("kiri.stack", [], (root, exports) => {
|
||||
|
||||
class Stack {
|
||||
constructor(view, freeMem) {
|
||||
this._view = view;
|
||||
this.view = view.newGroup();
|
||||
this.slices = [];
|
||||
this.meshes = [];
|
||||
this.freeMem = freeMem;
|
||||
const { kiri } = root;
|
||||
|
||||
class Stack {
|
||||
constructor(view, freeMem) {
|
||||
this._view = view;
|
||||
this.view = view.newGroup();
|
||||
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() {
|
||||
return this.slices.length;
|
||||
setLastFraction(frac = 1) {
|
||||
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() {
|
||||
this.view.visible = false;
|
||||
addLayers(layers) {
|
||||
// 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() {
|
||||
this.view.visible = true;
|
||||
renderLayers(layers, group) {
|
||||
const map = {}
|
||||
for (const [label, layer] of Object.entries(layers.layers)) {
|
||||
map[label] = this.renderLayer(layer, group);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
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];
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
setLastFraction(frac = 1) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
const { polys, lines, faces, cface, paths, cpath, color, off } = layer;
|
||||
const meshes = [];
|
||||
const defstate = !off;
|
||||
const mats = [];
|
||||
mats.state = defstate;
|
||||
|
||||
addLayers(layers) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
renderLayers(layers, group) {
|
||||
const map = {}
|
||||
for (const [label, layer] of Object.entries(layers.layers)) {
|
||||
map[label] = this.renderLayer(layer, group);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
// 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) {
|
||||
const mat = [];
|
||||
if (cface) {
|
||||
cface.forEach(c => { mat.push(newMat(c, true)) });
|
||||
} else {
|
||||
mat.push(newMat(color, true));
|
||||
}
|
||||
mat.forEach(m => m.visible = defstate);
|
||||
// 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) {
|
||||
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();
|
||||
if (faces.toFloat32) {
|
||||
geo.setAttribute('position', new THREE.BufferAttribute(faces.toFloat32(), 3));
|
||||
} else {
|
||||
geo.setAttribute('position', new THREE.BufferAttribute(faces, 3));
|
||||
}
|
||||
geo.setAttribute('position', new THREE.BufferAttribute(faces, 3));
|
||||
geo.setIndex(index);
|
||||
// geo.computeFaceNormals();
|
||||
geo.computeVertexNormals();
|
||||
if (cface) {
|
||||
cface.forEach((c, i) => geo.addGroup(c.start, c.count, i));
|
||||
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);
|
||||
}
|
||||
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;
|
||||
});
|
||||
mats.appendAll(mat);
|
||||
}
|
||||
|
||||
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,
|
||||
specular = 0x444444,
|
||||
emissive = 0x101010,
|
||||
metalness = 0,
|
||||
roughness = 0.3,
|
||||
newMat = createPhongMaterial;
|
||||
let shininess = 15,
|
||||
specular = 0x444444,
|
||||
emissive = 0x101010,
|
||||
metalness = 0,
|
||||
roughness = 0.3,
|
||||
newMat = createPhongMaterial;
|
||||
|
||||
function createLineMaterial(color, array) {
|
||||
const opacity = color.lopacity || color.opacity || 1;
|
||||
const mat = new THREE.LineBasicMaterial({
|
||||
transparent: true,
|
||||
opacity: opacity,
|
||||
color: color.line
|
||||
});
|
||||
if (array) {
|
||||
array.push(mat);
|
||||
}
|
||||
return mat;
|
||||
function createLineMaterial(color, array) {
|
||||
const opacity = color.lopacity || color.opacity || 1;
|
||||
const mat = new THREE.LineBasicMaterial({
|
||||
transparent: true,
|
||||
opacity: opacity,
|
||||
color: color.line
|
||||
});
|
||||
if (array) {
|
||||
array.push(mat);
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
function createStandardMaterial(color, flat) {
|
||||
return new THREE.MeshStandardMaterial({
|
||||
emissive,
|
||||
roughness,
|
||||
metalness,
|
||||
transparent: color.opacity != 1,
|
||||
opacity: color.opacity || 1,
|
||||
color: color.face,
|
||||
side: flat ? THREE.DoubleSide : THREE.FrontSide
|
||||
});
|
||||
}
|
||||
function createStandardMaterial(color, flat) {
|
||||
return new THREE.MeshStandardMaterial({
|
||||
emissive,
|
||||
roughness,
|
||||
metalness,
|
||||
transparent: color.opacity != 1,
|
||||
opacity: color.opacity || 1,
|
||||
color: color.face,
|
||||
side: flat ? THREE.DoubleSide : THREE.FrontSide
|
||||
});
|
||||
}
|
||||
|
||||
function createPhongMaterial(color, flat) {
|
||||
return new THREE.MeshPhongMaterial({
|
||||
shininess,
|
||||
specular,
|
||||
transparent: color.opacity != 1,
|
||||
opacity: color.opacity || 1,
|
||||
color: color.face,
|
||||
side: flat ? THREE.DoubleSide : THREE.FrontSide
|
||||
});
|
||||
}
|
||||
function createPhongMaterial(color, flat) {
|
||||
return new THREE.MeshPhongMaterial({
|
||||
shininess,
|
||||
specular,
|
||||
transparent: color.opacity != 1,
|
||||
opacity: color.opacity || 1,
|
||||
color: color.face,
|
||||
side: flat ? THREE.DoubleSide : THREE.FrontSide
|
||||
});
|
||||
}
|
||||
|
||||
})();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,168 +2,172 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
(function() {
|
||||
// dep: kiri.stack
|
||||
// use: kiri.api
|
||||
// use: kiri.ui
|
||||
gapp.register("kiri.stacks", [], (root, exports) => {
|
||||
|
||||
let KIRI = self.kiri,
|
||||
freeMem = true,
|
||||
stacks = {},
|
||||
tallest = 0,
|
||||
min = 0,
|
||||
max = 0,
|
||||
labels, API, UC, UI, DYN;
|
||||
const { kiri } = root;
|
||||
|
||||
function init() {
|
||||
labels = $("layers");
|
||||
API = KIRI.api,
|
||||
UC = API.uc,
|
||||
UI = API.ui;
|
||||
let freeMem = true,
|
||||
stacks = {},
|
||||
tallest = 0,
|
||||
min = 0,
|
||||
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();
|
||||
}
|
||||
|
||||
function setFreeMem(bool) {
|
||||
Object.values(stacks).forEach(stack => stack.obj.setFreeMem(bool));
|
||||
freeMem = bool;
|
||||
return this;
|
||||
// remove stacks from their views
|
||||
for (const [stack, data] of Object.entries(stacks)) {
|
||||
data.clear();
|
||||
}
|
||||
min = max = tallest = 0;
|
||||
stacks = {};
|
||||
DYN = UI.dyn = {};
|
||||
|
||||
function clear() {
|
||||
if (!API) {
|
||||
init();
|
||||
}
|
||||
// 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
|
||||
UC.setGroup(labels);
|
||||
labels.innerHTML = '';
|
||||
}
|
||||
|
||||
// clear labels
|
||||
UC.setGroup(labels);
|
||||
labels.innerHTML = '';
|
||||
}
|
||||
function rotate(set) {
|
||||
Object.values(stacks).forEach(stack => {
|
||||
stack.obj.rotate(set);
|
||||
});
|
||||
}
|
||||
|
||||
function rotate(set) {
|
||||
Object.values(stacks).forEach(stack => {
|
||||
stack.obj.rotate(set);
|
||||
function getStack(name) {
|
||||
return stacks[name];
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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 create(name, view) {
|
||||
if (stacks[name]) {
|
||||
return stacks[name];
|
||||
}
|
||||
const stack = stacks[name] = {
|
||||
layers: [ ],
|
||||
obj: new KIRI.Stack(view, freeMem),
|
||||
add: function(layers) {
|
||||
let lmap = layers.layers;
|
||||
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 stack = stacks[name] = {
|
||||
layers: [ ],
|
||||
obj: new kiri.Stack(view, freeMem),
|
||||
add: function(layers) {
|
||||
let lmap = layers.layers;
|
||||
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());
|
||||
},
|
||||
remove: function() {
|
||||
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) ]);
|
||||
const ctrl = DYN[label];
|
||||
ctrl.group.appendAll(mats);
|
||||
ctrl.toggle.checked = mats.state;
|
||||
}
|
||||
};
|
||||
return stack;
|
||||
}
|
||||
|
||||
function remove(name) {
|
||||
const stack = stacks[name];
|
||||
if (stack) {
|
||||
stack.remove();
|
||||
delete stacks[name];
|
||||
tallest = Math.max(tallest, stack.obj.size());
|
||||
},
|
||||
remove: function() {
|
||||
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) ]);
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
(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 { api, utils } = kiri;
|
||||
const { js2o, o2js } = utils;
|
||||
|
|
@ -55,4 +58,4 @@ class Stats {
|
|||
|
||||
kiri.stats = new Stats();
|
||||
|
||||
})();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,100 +1,112 @@
|
|||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||
|
||||
kiri.load(function(API) {
|
||||
// circle
|
||||
let pgeo = new THREE.CircleGeometry(8, 30);
|
||||
let pmat = new THREE.MeshBasicMaterial({color: 0xff0000, opacity: 0.5, transparent: true});
|
||||
let pmesh = new THREE.Mesh(pgeo, pmat);
|
||||
// circle outline
|
||||
let pi = pgeo.index.array;
|
||||
let pp = pgeo.attributes.position.array;
|
||||
let np = [];
|
||||
for (let i=0; i<pi.length; i++) {
|
||||
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]);
|
||||
}
|
||||
// dep: add.three
|
||||
// dep: kiri.api
|
||||
gapp.register("kiri.tools", [], (root, exports) => {
|
||||
|
||||
const { kiri } = root;
|
||||
const { api } = kiri;
|
||||
|
||||
// circle
|
||||
let pgeo = new THREE.CircleGeometry(8, 30);
|
||||
let pmat = new THREE.MeshBasicMaterial({color: 0xff0000, opacity: 0.5, transparent: true});
|
||||
let pmesh = new THREE.Mesh(pgeo, pmat);
|
||||
// circle outline
|
||||
let pi = pgeo.index.array;
|
||||
let pp = pgeo.attributes.position.array;
|
||||
let np = [];
|
||||
for (let i=0; i<pi.length; i++) {
|
||||
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 lastobj;
|
||||
let lastface;
|
||||
let enabled = false;
|
||||
let wgeo = new THREE.BufferGeometry();
|
||||
wgeo.setAttribute('position', new THREE.BufferAttribute(np.toFloat32(), 3));
|
||||
|
||||
function cleanup() {
|
||||
if (lastobj) {
|
||||
lastobj.remove(pmesh);
|
||||
lastobj = undefined;
|
||||
}
|
||||
let wmat = new THREE.LineBasicMaterial({ color: 0x883333 });
|
||||
let wmesh = new THREE.LineSegments(wgeo, wmat);
|
||||
pmesh.add(wmesh);
|
||||
|
||||
let alert;
|
||||
let lastobj;
|
||||
let lastface;
|
||||
let enabled = false;
|
||||
|
||||
function cleanup() {
|
||||
if (lastobj) {
|
||||
lastobj.remove(pmesh);
|
||||
lastobj = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function endit() {
|
||||
if (enabled) {
|
||||
API.hide.alert(alert);
|
||||
$('lay-flat').classList.remove('selected');
|
||||
API.feature.hover = false;
|
||||
enabled = false;
|
||||
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;
|
||||
}
|
||||
function endit() {
|
||||
if (enabled) {
|
||||
api.hide.alert(alert);
|
||||
$('lay-flat').classList.remove('selected');
|
||||
api.feature.hover = false;
|
||||
enabled = false;
|
||||
alert = undefined;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
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";
|
||||
|
||||
(function() {
|
||||
gapp.register("kiri.ui", [], (root, exports) => {
|
||||
|
||||
if (kiri.ui) return;
|
||||
|
||||
|
|
@ -939,4 +939,4 @@
|
|||
});
|
||||
}
|
||||
|
||||
})();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
// dep: data.local
|
||||
// dep: main.kiri
|
||||
// use: moto.ajax
|
||||
(function () {
|
||||
gapp.register("kiri.utils", [], (root, exports) => {
|
||||
|
||||
const { kiri, moto, data } = self;
|
||||
const { kiri, moto, data } = root;
|
||||
|
||||
function parseOpt(ov) {
|
||||
let opt = {}, kv, kva;
|
||||
|
|
@ -96,7 +96,7 @@ function areEqual(o1, o2) {
|
|||
return false;
|
||||
}
|
||||
|
||||
kiri.utils = {
|
||||
exports({
|
||||
areEqual,
|
||||
parseOpt,
|
||||
encodeOpt,
|
||||
|
|
@ -107,6 +107,6 @@ kiri.utils = {
|
|||
avgc,
|
||||
rgb,
|
||||
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";
|
||||
|
||||
(function() {
|
||||
gapp.register("kiri.widget", [], (root, exports) => {
|
||||
|
||||
if (self.kiri.Widget) return;
|
||||
|
||||
const { base, kiri } = self;
|
||||
const { base, kiri } = root;
|
||||
const { api, driver, utils } = kiri;
|
||||
const { config, util, polygons } = base;
|
||||
const { Mesh, newPoint, verticesToPoints } = base;
|
||||
|
|
@ -963,4 +961,4 @@ Widget.deleteFromState = function(id,ondone) {
|
|||
kiri.Widget = Widget;
|
||||
kiri.newWidget = newWidget;
|
||||
|
||||
})();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
"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 { space } = moto;
|
||||
|
||||
|
|
@ -110,4 +110,4 @@ const widgets = api.widgets = {
|
|||
for(fn) { widgets.each(fn) },
|
||||
};
|
||||
|
||||
})();
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue