2020-11-08 10:55:36 -05:00
|
|
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
|
|
|
|
|
const KIRI = self.kiri,
|
|
|
|
|
shininess = 15,
|
|
|
|
|
specular = 0x444444,
|
|
|
|
|
emissive = 0x101010,
|
|
|
|
|
metalness = 0,
|
|
|
|
|
roughness = 0.3,
|
2020-11-29 00:16:37 -05:00
|
|
|
newMat = createPhongMaterial;
|
2020-11-08 10:55:36 -05:00
|
|
|
|
|
|
|
|
let stacks = {},
|
2020-11-29 00:16:37 -05:00
|
|
|
freeMem = true,
|
2020-11-08 10:55:36 -05:00
|
|
|
tallest = 0,
|
|
|
|
|
min = 0,
|
|
|
|
|
max = 0,
|
|
|
|
|
labels, API, UC, UI, DYN;
|
|
|
|
|
|
|
|
|
|
function init() {
|
|
|
|
|
labels = $("layers");
|
|
|
|
|
API = KIRI.api,
|
|
|
|
|
UC = API.uc,
|
2020-11-09 13:57:18 -05:00
|
|
|
UI = API.ui;
|
2020-11-08 10:55:36 -05:00
|
|
|
}
|
|
|
|
|
|
2020-11-29 00:16:37 -05:00
|
|
|
function setFreeMem(bool) {
|
|
|
|
|
freeMem = bool;
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-08 10:55:36 -05:00
|
|
|
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 = {};
|
2020-11-09 13:57:18 -05:00
|
|
|
DYN = UI.dyn = {};
|
2020-11-08 10:55:36 -05:00
|
|
|
|
|
|
|
|
// clear labels
|
|
|
|
|
UC.setGroup(labels);
|
|
|
|
|
labels.innerHTML = '';
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-14 12:07:05 -05:00
|
|
|
function getStack(name) {
|
|
|
|
|
return stacks[name];
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-08 10:55:36 -05:00
|
|
|
function create(name, view) {
|
|
|
|
|
if (stacks[name]) {
|
|
|
|
|
return stacks[name];
|
|
|
|
|
}
|
|
|
|
|
const stack = stacks[name] = {
|
|
|
|
|
layers: [ ],
|
|
|
|
|
view: view.newGroup(),
|
|
|
|
|
add: function(layer) {
|
2020-11-15 14:43:22 -05:00
|
|
|
const lview = stack.view.newGroup();
|
|
|
|
|
stack.layers.push(lview);
|
|
|
|
|
render(layer, lview, name);
|
2020-11-08 10:55:36 -05:00
|
|
|
tallest = Math.max(tallest, stack.layers.length);
|
|
|
|
|
},
|
2020-11-15 14:43:22 -05:00
|
|
|
remove: function() {
|
|
|
|
|
view.remove(stack.view);
|
|
|
|
|
},
|
2020-11-08 10:55:36 -05:00
|
|
|
clear: function() {
|
|
|
|
|
view.remove(stack.view);
|
2020-11-23 21:55:46 -05:00
|
|
|
},
|
|
|
|
|
button: function(label, action) {
|
|
|
|
|
UC.newRow([ UC.newButton(label, action) ]);
|
2020-11-08 10:55:36 -05:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
return stack;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-15 14:43:22 -05:00
|
|
|
function remove(name) {
|
|
|
|
|
const stack = stacks[name];
|
|
|
|
|
if (stack) {
|
|
|
|
|
stack.remove();
|
|
|
|
|
delete stacks[name];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-08 10:55:36 -05:00
|
|
|
function getRange() {
|
|
|
|
|
return {min, max, tallest};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setRange(newMin, newMax) {
|
|
|
|
|
for (const [stack, data] of Object.entries(stacks)) {
|
|
|
|
|
const layers = data.layers, len = layers.length;
|
|
|
|
|
for (let i=0; i<len; i++) {
|
|
|
|
|
layers[i].visible = i >= newMin && i <= newMax;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
min = newMin;
|
|
|
|
|
max = newMax;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-13 20:45:38 -05:00
|
|
|
function render(render, group, name) {
|
2020-11-08 10:55:36 -05:00
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const [label, data] of Object.entries(render.layers)) {
|
|
|
|
|
if (!DYN[label]) {
|
|
|
|
|
DYN[label] = {
|
|
|
|
|
group: [],
|
|
|
|
|
toggle: UC.newBoolean(label, (abc) => {
|
|
|
|
|
ctrl.group.forEach(mat => {
|
|
|
|
|
mat.visible = ctrl.toggle.checked;
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
};
|
|
|
|
|
}
|
2020-11-09 09:08:13 -05:00
|
|
|
const defstate = !data.off;
|
2020-11-08 10:55:36 -05:00
|
|
|
const ctrl = DYN[label];
|
2020-11-09 09:08:13 -05:00
|
|
|
|
|
|
|
|
ctrl.toggle.checked = defstate;
|
2020-11-08 10:55:36 -05:00
|
|
|
|
2020-11-13 20:45:38 -05:00
|
|
|
const { polys, lines, faces, cface, paths, cpath } = data;
|
2020-11-08 10:55:36 -05:00
|
|
|
if (polys.length || lines.length) {
|
2020-11-09 20:12:58 -05:00
|
|
|
const vert = []; // vertexes
|
|
|
|
|
const mat = []; // materials
|
|
|
|
|
const grp = []; // material groups
|
2020-11-09 09:46:27 -05:00
|
|
|
const geo = new THREE.BufferGeometry();
|
2020-11-13 16:07:43 -05:00
|
|
|
// map all the poly and line colors for re-use
|
2020-11-13 20:45:38 -05:00
|
|
|
const cmap = {}
|
2020-11-09 20:12:58 -05:00
|
|
|
let cidx = 0;
|
|
|
|
|
let last = undefined;
|
2020-11-08 10:55:36 -05:00
|
|
|
for (let i=0, il=polys.length; i<il; i++) {
|
2020-11-09 20:12:58 -05:00
|
|
|
const vl = vert.length;
|
|
|
|
|
const poly = polys[i];
|
|
|
|
|
addPoly(vert, poly);
|
2020-11-13 13:52:35 -05:00
|
|
|
const pc = poly.color !== undefined ? { line: poly.color, opacity: data.color.opacity } : data.color;
|
2020-11-09 20:12:58 -05:00
|
|
|
const pk = pc.line;
|
2020-11-13 20:45:38 -05:00
|
|
|
const cc = cmap[pk] = cmap[pk] || { idx: cidx++, mat: createLineMaterial(pc, mat) };
|
|
|
|
|
if (last !== pk) {
|
2020-11-09 20:12:58 -05:00
|
|
|
if (grp.length) {
|
|
|
|
|
// rewrite counts for last group
|
|
|
|
|
const prev = grp[grp.length - 1]
|
2020-11-13 16:07:43 -05:00
|
|
|
prev[1] = vl;
|
2020-11-09 20:12:58 -05:00
|
|
|
}
|
2020-11-13 13:52:35 -05:00
|
|
|
grp.push([vl, Infinity, cc.idx]);
|
2020-11-09 20:12:58 -05:00
|
|
|
last = pk;
|
|
|
|
|
}
|
2020-11-08 10:55:36 -05:00
|
|
|
}
|
2020-11-09 20:12:58 -05:00
|
|
|
// for now, line segments inherit the last color
|
2020-11-08 10:55:36 -05:00
|
|
|
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));
|
|
|
|
|
}
|
2020-11-09 20:12:58 -05:00
|
|
|
// ensure at least one group using the default color settings
|
|
|
|
|
if (grp.length === 0) {
|
|
|
|
|
grp.push([0, Infinity, 0]);
|
|
|
|
|
mat.push(createLineMaterial(data.color));
|
2020-11-08 10:55:36 -05:00
|
|
|
}
|
2020-11-09 20:12:58 -05:00
|
|
|
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]);
|
|
|
|
|
}
|
2020-11-09 09:46:27 -05:00
|
|
|
geo.setFromPoints(vert);
|
2020-11-13 20:45:38 -05:00
|
|
|
const segs = new THREE.LineSegments(geo, mat);
|
|
|
|
|
group.add(segs);
|
2020-11-09 10:11:24 -05:00
|
|
|
ctrl.group.appendAll(mat);
|
2020-11-08 10:55:36 -05:00
|
|
|
}
|
|
|
|
|
if (faces.length) {
|
2020-11-13 13:52:35 -05:00
|
|
|
const mat = [];
|
|
|
|
|
if (cface) {
|
|
|
|
|
cface.forEach(c => { mat.push(newMat(c)) });
|
|
|
|
|
} else {
|
|
|
|
|
mat.push(newMat(data.color));
|
|
|
|
|
}
|
|
|
|
|
mat.forEach(m => m.visible = defstate);
|
2020-11-08 10:55:36 -05:00
|
|
|
const geo = new THREE.BufferGeometry();
|
2020-11-13 13:52:35 -05:00
|
|
|
if (faces.toFloat32) {
|
|
|
|
|
geo.setAttribute('position', new THREE.BufferAttribute(faces.toFloat32(), 3));
|
|
|
|
|
} else {
|
|
|
|
|
geo.setAttribute('position', new THREE.BufferAttribute(faces, 3));
|
|
|
|
|
}
|
2020-11-08 10:55:36 -05:00
|
|
|
geo.computeFaceNormals();
|
|
|
|
|
geo.computeVertexNormals();
|
2020-11-13 13:52:35 -05:00
|
|
|
if (cface) {
|
|
|
|
|
cface.forEach((c, i) => geo.addGroup(c.start, c.count, i));
|
|
|
|
|
} else {
|
|
|
|
|
geo.addGroup(0, Infinity, 0);
|
|
|
|
|
}
|
2020-11-08 10:55:36 -05:00
|
|
|
const mesh = new THREE.Mesh(geo, mat);
|
|
|
|
|
mesh.castShadow = true;
|
|
|
|
|
mesh.receiveShadow = true;
|
|
|
|
|
group.add(mesh);
|
2020-11-16 11:00:52 -05:00
|
|
|
ctrl.group.appendAll(mat);
|
2020-11-08 10:55:36 -05:00
|
|
|
}
|
|
|
|
|
if (paths.length) {
|
2020-11-09 09:46:27 -05:00
|
|
|
const mat = [];
|
2020-11-09 00:00:57 -05:00
|
|
|
if (cpath) {
|
2020-11-09 20:12:58 -05:00
|
|
|
cpath.forEach(c => { mat.push(newMat(c)) });
|
2020-11-09 00:00:57 -05:00
|
|
|
} else {
|
2020-11-09 20:12:58 -05:00
|
|
|
mat.push(newMat(data.color));
|
2020-11-09 00:00:57 -05:00
|
|
|
}
|
2020-11-09 09:08:13 -05:00
|
|
|
mat.forEach(m => m.visible = defstate);
|
2020-11-09 00:00:57 -05:00
|
|
|
// const mat = newMat(data);
|
2020-11-08 10:55:36 -05:00
|
|
|
paths.forEach((path, i) => {
|
2020-11-09 00:00:57 -05:00
|
|
|
const { index, faces, z, colors } = path;
|
2020-11-08 10:55:36 -05:00
|
|
|
const geo = new THREE.BufferGeometry();
|
|
|
|
|
geo.setAttribute('position', new THREE.BufferAttribute(faces, 3));
|
|
|
|
|
geo.setIndex(index);
|
|
|
|
|
geo.computeFaceNormals();
|
|
|
|
|
geo.computeVertexNormals();
|
2020-11-09 00:00:57 -05:00
|
|
|
if (cpath) {
|
|
|
|
|
cpath.forEach((c, i) => geo.addGroup(c.start, c.count, i));
|
|
|
|
|
} else {
|
|
|
|
|
geo.addGroup(0, Infinity, 0);
|
|
|
|
|
}
|
2020-11-08 10:55:36 -05:00
|
|
|
const mesh = new THREE.Mesh(geo, mat);
|
|
|
|
|
mesh.position.z = z;
|
|
|
|
|
mesh.castShadow = true;
|
|
|
|
|
mesh.receiveShadow = true;
|
|
|
|
|
group.add(mesh);
|
|
|
|
|
});
|
2020-11-09 00:00:57 -05:00
|
|
|
ctrl.group.appendAll(mat);
|
2020-11-08 10:55:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
2020-11-26 08:48:19 -05:00
|
|
|
if (freeMem) {
|
|
|
|
|
render.init();
|
|
|
|
|
}
|
2020-11-08 10:55:36 -05:00
|
|
|
};
|
|
|
|
|
|
2020-11-09 20:12:58 -05:00
|
|
|
function createLineMaterial(color, array) {
|
2020-11-13 20:45:38 -05:00
|
|
|
const opacity = color.opacity || 1;
|
2020-11-09 20:12:58 -05:00
|
|
|
const mat = new THREE.LineBasicMaterial({
|
2020-11-13 20:45:38 -05:00
|
|
|
transparent: true,
|
|
|
|
|
opacity: opacity,
|
2020-11-09 20:12:58 -05:00
|
|
|
color: color.line
|
2020-11-09 09:46:27 -05:00
|
|
|
});
|
2020-11-09 20:12:58 -05:00
|
|
|
if (array) {
|
|
|
|
|
array.push(mat);
|
|
|
|
|
}
|
|
|
|
|
return mat;
|
2020-11-09 09:46:27 -05:00
|
|
|
}
|
|
|
|
|
|
2020-11-09 20:12:58 -05:00
|
|
|
function createStandardMaterial(color) {
|
2020-11-08 10:55:36 -05:00
|
|
|
return new THREE.MeshStandardMaterial({
|
|
|
|
|
emissive,
|
|
|
|
|
roughness,
|
|
|
|
|
metalness,
|
2020-11-09 20:12:58 -05:00
|
|
|
transparent: color.opacity != 1,
|
|
|
|
|
opacity: color.opacity || 1,
|
|
|
|
|
color: color.face,
|
2020-11-08 10:55:36 -05:00
|
|
|
side: THREE.DoubleSide
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-09 20:12:58 -05:00
|
|
|
function createPhongMaterial(color) {
|
2020-11-08 10:55:36 -05:00
|
|
|
return new THREE.MeshPhongMaterial({
|
|
|
|
|
shininess,
|
|
|
|
|
specular,
|
2020-11-09 20:12:58 -05:00
|
|
|
transparent: color.opacity != 1,
|
|
|
|
|
opacity: color.opacity || 1,
|
|
|
|
|
color: color.face,
|
2020-11-08 10:55:36 -05:00
|
|
|
side: THREE.DoubleSide
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
KIRI.stacks = {
|
|
|
|
|
clear,
|
|
|
|
|
create,
|
2020-11-15 14:43:22 -05:00
|
|
|
remove,
|
2020-11-14 12:07:05 -05:00
|
|
|
getStack,
|
2020-11-08 10:55:36 -05:00
|
|
|
getRange,
|
2020-11-29 00:16:37 -05:00
|
|
|
setRange,
|
|
|
|
|
setFreeMem
|
2020-11-08 10:55:36 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
})();
|