2021-07-04 21:05:46 -04:00
|
|
|
console.log/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
2017-04-18 22:22:29 -04:00
|
|
|
|
2017-04-18 17:09:33 -04:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
|
|
|
|
|
if (self.kiri.Widget) return;
|
|
|
|
|
|
2020-06-03 22:39:30 -04:00
|
|
|
const KIRI = self.kiri,
|
2017-04-18 17:09:33 -04:00
|
|
|
DRIVERS = KIRI.driver,
|
|
|
|
|
BASE = self.base,
|
|
|
|
|
CONF = BASE.config,
|
|
|
|
|
UTIL = BASE.util,
|
|
|
|
|
POLY = BASE.polygons,
|
|
|
|
|
MATH = Math,
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO = Widget.prototype,
|
2020-04-13 17:42:40 -04:00
|
|
|
time = UTIL.time,
|
2020-06-03 22:39:30 -04:00
|
|
|
solid_opacity = 1.0;
|
|
|
|
|
|
|
|
|
|
let nextId = 0,
|
2020-04-07 19:56:30 -04:00
|
|
|
groups = [];
|
2017-04-18 17:09:33 -04:00
|
|
|
|
|
|
|
|
KIRI.Widget = Widget;
|
|
|
|
|
KIRI.newWidget = newWidget;
|
|
|
|
|
|
2020-04-07 19:56:30 -04:00
|
|
|
function newWidget(id,group) { return new Widget(id,group) }
|
|
|
|
|
|
|
|
|
|
/** ******************************************************************
|
|
|
|
|
* Group helpers
|
|
|
|
|
******************************************************************* */
|
|
|
|
|
|
|
|
|
|
let Group = Widget.Groups = {
|
|
|
|
|
|
2021-01-24 00:13:09 -05:00
|
|
|
list: () => {
|
|
|
|
|
return groups.slice()
|
|
|
|
|
},
|
|
|
|
|
|
2021-03-01 00:22:07 -05:00
|
|
|
merge: (widgets) => {
|
|
|
|
|
let grps = widgets.map(w => w.group).uniq();
|
|
|
|
|
if (grps.length > 1) {
|
|
|
|
|
let root = grps.shift();
|
2021-03-02 00:15:22 -05:00
|
|
|
let rpos = root[0].track.pos;
|
2021-03-01 00:22:07 -05:00
|
|
|
for (let grp of grps) {
|
|
|
|
|
for (let w of grp) {
|
2021-03-02 00:15:22 -05:00
|
|
|
let wpos = w.track.pos;
|
2021-03-01 00:22:07 -05:00
|
|
|
w.group = root;
|
2021-03-02 00:15:22 -05:00
|
|
|
w.moveMesh(rpos.x - wpos.x, rpos.y - wpos.y, rpos.z - wpos.z);
|
|
|
|
|
w._move(rpos.x, rpos.y, rpos.z, true);
|
2021-03-01 00:22:07 -05:00
|
|
|
root.push(w);
|
|
|
|
|
}
|
|
|
|
|
groups.splice(groups.indexOf(grp),1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
split: (widgets) => {
|
|
|
|
|
for (let group of widgets.map(w => w.group).uniq()) {
|
|
|
|
|
groups.splice(groups.indexOf(group),1);
|
|
|
|
|
for (let widget of group) {
|
|
|
|
|
let nugroup = Group.forid(widget.id);
|
|
|
|
|
nugroup.push(widget);
|
|
|
|
|
widget.group = nugroup;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2020-04-10 11:53:07 -04:00
|
|
|
forid: function(id) {
|
|
|
|
|
for (let i=0; i<groups.length; i++) {
|
|
|
|
|
if (groups[i].id === id) return groups[i];
|
|
|
|
|
}
|
|
|
|
|
let group = [];
|
|
|
|
|
group.id = id;
|
|
|
|
|
groups.push(group);
|
|
|
|
|
return group;
|
|
|
|
|
},
|
|
|
|
|
|
2020-04-07 19:56:30 -04:00
|
|
|
remove: function(widget) {
|
|
|
|
|
groups.slice().forEach(group => {
|
|
|
|
|
let pos = group.indexOf(widget);
|
|
|
|
|
if (pos >= 0) {
|
|
|
|
|
group.splice(pos,1);
|
|
|
|
|
}
|
|
|
|
|
if (group.length === 0) {
|
|
|
|
|
pos = groups.indexOf(group);
|
|
|
|
|
groups.splice(pos,1);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
blocks: function() {
|
|
|
|
|
return groups.map(group => {
|
|
|
|
|
return {
|
|
|
|
|
w: group[0].track.box.w,
|
|
|
|
|
h: group[0].track.box.h,
|
|
|
|
|
move: (x,y,z,abs) => {
|
|
|
|
|
group.forEach(widget => {
|
|
|
|
|
widget.mesh.material.visible = true;
|
|
|
|
|
widget._move(x, y, z, abs);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
loadDone: function() {
|
|
|
|
|
groups.forEach(group => {
|
|
|
|
|
if (!group.centered) {
|
|
|
|
|
group[0].center();
|
|
|
|
|
group.centered = true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
bounds: function(group) {
|
|
|
|
|
let bounds = null;
|
|
|
|
|
group.forEach(widget => {
|
|
|
|
|
let wb = widget.mesh.getBoundingBox(true);
|
|
|
|
|
if (bounds) {
|
|
|
|
|
bounds = bounds.union(wb);
|
|
|
|
|
} else {
|
|
|
|
|
bounds = wb;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return bounds;
|
2021-06-18 22:57:30 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
clear: function() {
|
|
|
|
|
groups.length = 0;
|
2020-04-07 19:56:30 -04:00
|
|
|
}
|
|
|
|
|
};
|
2017-04-18 17:09:33 -04:00
|
|
|
|
|
|
|
|
/** ******************************************************************
|
|
|
|
|
* Constructor
|
|
|
|
|
******************************************************************* */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @params {String} [id]
|
|
|
|
|
* @constructor
|
|
|
|
|
*/
|
2021-01-24 00:13:09 -05:00
|
|
|
function Widget(id, group) {
|
2017-04-18 17:09:33 -04:00
|
|
|
this.id = id || new Date().getTime().toString(36)+(nextId++);
|
2020-04-07 19:56:30 -04:00
|
|
|
this.group = group || [];
|
|
|
|
|
this.group.push(this);
|
2020-04-10 11:53:07 -04:00
|
|
|
if (!this.group.id) {
|
|
|
|
|
this.group.id = this.id;
|
|
|
|
|
}
|
2020-04-07 19:56:30 -04:00
|
|
|
if (groups.indexOf(this.group) < 0) {
|
|
|
|
|
groups.push(this.group);
|
|
|
|
|
}
|
2020-11-22 17:01:36 -05:00
|
|
|
// rotation stack (for undo)
|
2020-06-14 10:00:05 -04:00
|
|
|
this.roto = [];
|
2020-11-25 09:10:00 -05:00
|
|
|
// added meshes (supports, tabs, etc)
|
2020-11-22 20:06:11 -05:00
|
|
|
this.adds = [];
|
|
|
|
|
// THREE Mesh and points
|
2017-04-18 17:09:33 -04:00
|
|
|
this.mesh = null;
|
|
|
|
|
this.points = null;
|
|
|
|
|
// todo resolve use of this vs. mesh.bounds
|
|
|
|
|
this.bounds = null;
|
2020-11-22 20:06:11 -05:00
|
|
|
// wireframe
|
2017-04-18 17:09:33 -04:00
|
|
|
this.wire = null;
|
|
|
|
|
this.slices = null;
|
|
|
|
|
this.settings = null;
|
|
|
|
|
this.modified = true;
|
2020-04-07 19:56:30 -04:00
|
|
|
this.track = {
|
|
|
|
|
// box size for packer
|
|
|
|
|
box: {
|
|
|
|
|
w: 0,
|
|
|
|
|
h: 0,
|
|
|
|
|
d: 0
|
|
|
|
|
},
|
2017-04-18 17:09:33 -04:00
|
|
|
scale: {
|
|
|
|
|
x: 1.0,
|
|
|
|
|
y: 1.0,
|
|
|
|
|
z: 1.0
|
|
|
|
|
},
|
|
|
|
|
rot: {
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
z: 0
|
|
|
|
|
},
|
|
|
|
|
pos: {
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
z: 0
|
|
|
|
|
},
|
2020-12-22 15:05:50 -05:00
|
|
|
top: 0, // z top
|
2017-04-18 17:09:33 -04:00
|
|
|
mirror: false
|
|
|
|
|
},
|
2021-06-01 13:45:36 -04:00
|
|
|
this.cache = {};
|
2017-04-18 17:09:33 -04:00
|
|
|
this.stats = {
|
|
|
|
|
slice_time: 0,
|
|
|
|
|
load_time: 0,
|
|
|
|
|
progress: 0
|
|
|
|
|
};
|
2020-11-25 09:10:00 -05:00
|
|
|
this.meta = {
|
|
|
|
|
url: null,
|
|
|
|
|
file: null,
|
|
|
|
|
saved: false
|
|
|
|
|
};
|
|
|
|
|
// if this is a synthesized support widget
|
|
|
|
|
this.support = false;
|
2017-04-18 17:09:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** ******************************************************************
|
|
|
|
|
* Widget Class Functions
|
|
|
|
|
******************************************************************* */
|
|
|
|
|
|
|
|
|
|
Widget.loadFromCatalog = function(filename, ondone) {
|
|
|
|
|
KIRI.catalog.getFile(filename, function(data) {
|
2020-11-25 09:10:00 -05:00
|
|
|
let widget = newWidget().loadVertices(data);
|
|
|
|
|
widget.meta.file = filename;
|
|
|
|
|
ondone(widget);
|
2017-04-18 17:09:33 -04:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Widget.loadFromState = function(id, ondone, move) {
|
|
|
|
|
KIRI.odb.get('ws-save-'+id, function(data) {
|
|
|
|
|
if (data) {
|
2020-04-07 19:56:30 -04:00
|
|
|
let vertices = data.geo || data,
|
2020-04-10 11:53:07 -04:00
|
|
|
track = data.track || undefined,
|
|
|
|
|
group = data.group || id,
|
2020-06-14 20:03:00 -04:00
|
|
|
widget = newWidget(id, Group.forid(group)),
|
2020-11-25 09:10:00 -05:00
|
|
|
meta = data.meta || widget.meta,
|
2020-06-14 20:03:00 -04:00
|
|
|
ptr = widget.loadVertices(vertices);
|
2020-11-25 09:10:00 -05:00
|
|
|
widget.meta = meta;
|
2017-04-18 17:09:33 -04:00
|
|
|
// restore widget position if specified
|
2020-04-07 19:56:30 -04:00
|
|
|
if (move && track && track.pos) {
|
|
|
|
|
widget.track = track;
|
|
|
|
|
widget.move(track.pos.x, track.pos.y, track.pos.z, true);
|
2017-04-18 17:09:33 -04:00
|
|
|
}
|
2020-06-14 20:03:00 -04:00
|
|
|
ondone(ptr);
|
2017-04-18 17:09:33 -04:00
|
|
|
} else {
|
|
|
|
|
ondone(null);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Widget.deleteFromState = function(id,ondone) {
|
|
|
|
|
KIRI.odb.remove('ws-save-'+id, ondone);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** ******************************************************************
|
|
|
|
|
* Widget Prototype Functions
|
|
|
|
|
******************************************************************* */
|
|
|
|
|
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.saveToCatalog = function(filename) {
|
2020-04-07 19:56:30 -04:00
|
|
|
let widget = this;
|
|
|
|
|
let time = UTIL.time();
|
2020-11-25 09:10:00 -05:00
|
|
|
widget.meta.file = filename;
|
|
|
|
|
widget.meta.save = time;
|
2017-04-18 17:09:33 -04:00
|
|
|
KIRI.catalog.putFile(filename, this.getGeoVertices(), function(vertices) {
|
|
|
|
|
if (vertices && vertices.length) {
|
|
|
|
|
console.log("saving decimated mesh ["+vertices.length+"] time ["+(UTIL.time()-time)+"]");
|
|
|
|
|
widget.loadVertices(vertices);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return this;
|
|
|
|
|
};
|
|
|
|
|
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.saveState = function(ondone) {
|
2020-04-07 19:56:30 -04:00
|
|
|
let widget = this;
|
2020-04-10 11:53:07 -04:00
|
|
|
KIRI.odb.put('ws-save-'+this.id, {
|
2020-11-25 09:10:00 -05:00
|
|
|
geo: widget.getGeoVertices(),
|
|
|
|
|
track: widget.track,
|
|
|
|
|
group: this.group.id,
|
|
|
|
|
meta: this.meta
|
2020-04-10 11:53:07 -04:00
|
|
|
}, function(result) {
|
2020-11-25 09:10:00 -05:00
|
|
|
widget.meta.saved = time();
|
2017-04-18 17:09:33 -04:00
|
|
|
if (ondone) ondone();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param {Float32Array} vertices
|
|
|
|
|
* @returns {Widget}
|
|
|
|
|
*/
|
2021-08-28 19:20:37 -04:00
|
|
|
PRO.loadVertices = function(vertices, autoscale) {
|
|
|
|
|
if (autoscale === true) {
|
|
|
|
|
// onshape exports obj in meters by default :/
|
|
|
|
|
let maxv = 0;
|
|
|
|
|
for (let i=0; i<vertices.length; i++) {
|
|
|
|
|
maxv = Math.max(Math.abs(vertices[i]));
|
|
|
|
|
}
|
|
|
|
|
if (maxv < 1) {
|
|
|
|
|
for (let i=0; i<vertices.length; i++) {
|
|
|
|
|
vertices[i] *= 1000;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-18 17:09:33 -04:00
|
|
|
if (this.mesh) {
|
2020-03-16 23:57:14 -04:00
|
|
|
this.mesh.geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
|
2017-04-18 17:09:33 -04:00
|
|
|
this.mesh.geometry.computeFaceNormals();
|
|
|
|
|
this.mesh.geometry.computeVertexNormals();
|
|
|
|
|
this.points = null;
|
2020-11-25 09:10:00 -05:00
|
|
|
this.meta.vertices = vertices.length / 3;
|
2017-04-18 17:09:33 -04:00
|
|
|
return this;
|
|
|
|
|
} else {
|
2020-04-07 19:56:30 -04:00
|
|
|
let geometry = new THREE.BufferGeometry();
|
2020-03-16 23:57:14 -04:00
|
|
|
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
|
2020-11-25 09:10:00 -05:00
|
|
|
this.meta.vertices = vertices.length / 3;
|
2017-04-18 17:09:33 -04:00
|
|
|
return this.loadGeometry(geometry);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-01-26 20:07:17 -05:00
|
|
|
PRO.getVertices = function() {
|
|
|
|
|
return this.mesh.geometry.attributes.position;
|
|
|
|
|
};
|
|
|
|
|
|
2017-04-18 17:09:33 -04:00
|
|
|
/**
|
|
|
|
|
* @param {THREE.Geometry} geometry
|
|
|
|
|
* @returns {Widget}
|
|
|
|
|
*/
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.loadGeometry = function(geometry) {
|
2020-04-07 19:56:30 -04:00
|
|
|
let mesh = new THREE.Mesh(
|
2017-04-18 17:09:33 -04:00
|
|
|
geometry,
|
|
|
|
|
new THREE.MeshPhongMaterial({
|
|
|
|
|
color: 0xffff00,
|
|
|
|
|
specular: 0x181818,
|
|
|
|
|
shininess: 100,
|
|
|
|
|
transparent: true,
|
|
|
|
|
opacity: solid_opacity
|
|
|
|
|
})
|
|
|
|
|
);
|
2020-11-13 23:09:53 -05:00
|
|
|
mesh.renderOrder = 1;
|
2017-04-18 17:09:33 -04:00
|
|
|
// fix invalid normals
|
|
|
|
|
geometry.computeFaceNormals();
|
|
|
|
|
geometry.computeVertexNormals();
|
|
|
|
|
mesh.material.side = THREE.DoubleSide;
|
|
|
|
|
mesh.castShadow = true;
|
|
|
|
|
mesh.receiveShadow = true;
|
|
|
|
|
mesh.widget = this;
|
|
|
|
|
this.mesh = mesh;
|
|
|
|
|
// invalidates points cache (like any scale/rotation)
|
2020-04-07 19:56:30 -04:00
|
|
|
this.center(true);
|
2017-04-18 17:09:33 -04:00
|
|
|
return this;
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-07 19:56:30 -04:00
|
|
|
PRO.groupBounds = function() {
|
|
|
|
|
return Group.bounds(this.group);
|
|
|
|
|
};
|
|
|
|
|
|
2017-04-18 17:09:33 -04:00
|
|
|
/**
|
|
|
|
|
* @param {Point[]} points
|
|
|
|
|
* @returns {Widget}
|
|
|
|
|
*/
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.setPoints = function(points) {
|
2017-04-18 17:09:33 -04:00
|
|
|
this.points = points || null;
|
|
|
|
|
return this;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* remove slice data and their views
|
|
|
|
|
*/
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.clearSlices = function() {
|
2020-04-07 19:56:30 -04:00
|
|
|
let slices = this.slices,
|
2017-04-18 17:09:33 -04:00
|
|
|
mesh = this.mesh;
|
2020-11-30 22:36:06 -05:00
|
|
|
if (slices && mesh && mesh.remove) {
|
2017-04-18 17:09:33 -04:00
|
|
|
slices.forEach(function(slice) {
|
|
|
|
|
mesh.remove(slice.view);
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-11-30 18:01:32 -05:00
|
|
|
this.slices = null;
|
2017-04-18 17:09:33 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {number} color
|
|
|
|
|
*/
|
2020-04-10 23:17:22 -04:00
|
|
|
PRO.setColor = function(color,settings) {
|
|
|
|
|
if (Array.isArray(color)) {
|
|
|
|
|
color = color[this.getExtruder(settings) % color.length];
|
|
|
|
|
}
|
2020-04-07 19:56:30 -04:00
|
|
|
let material = this.mesh.material;
|
2017-04-18 17:09:33 -04:00
|
|
|
material.color.set(color);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {number} value
|
|
|
|
|
*/
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.setOpacity = function(value) {
|
2020-04-07 19:56:30 -04:00
|
|
|
let mesh = this.mesh;
|
2017-04-18 17:09:33 -04:00
|
|
|
if (value <= 0.0) {
|
|
|
|
|
mesh.material.transparent = solid_opacity < 1.0;
|
|
|
|
|
mesh.material.opacity = solid_opacity;
|
|
|
|
|
mesh.material.visible = false;
|
|
|
|
|
} else if (UTIL.inRange(value, 0.0, solid_opacity)) {
|
|
|
|
|
mesh.material.transparent = value < 1.0;
|
|
|
|
|
mesh.material.opacity = value;
|
|
|
|
|
mesh.material.visible = true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* center geometry bottom (on platform) at 0,0,0
|
|
|
|
|
*/
|
2020-04-07 19:56:30 -04:00
|
|
|
PRO.center = function(init) {
|
|
|
|
|
let bb = init ? this.mesh.getBoundingBox(true) : this.groupBounds(),
|
2017-04-18 17:09:33 -04:00
|
|
|
bm = bb.min.clone(),
|
|
|
|
|
bM = bb.max.clone(),
|
|
|
|
|
bd = bM.sub(bm).multiplyScalar(0.5),
|
2020-04-07 19:56:30 -04:00
|
|
|
dx = bm.x + bd.x,
|
|
|
|
|
dy = bm.y + bd.y,
|
|
|
|
|
dz = bm.z;
|
2021-01-11 12:26:19 -05:00
|
|
|
this.track.center = { dx, dy, dz };
|
2020-04-07 19:56:30 -04:00
|
|
|
// move mesh for each widget in group
|
|
|
|
|
if (!init) {
|
|
|
|
|
this.group.forEach(w => {
|
|
|
|
|
w.moveMesh(dx,dy,dz);
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-11-27 15:50:22 -05:00
|
|
|
return this;
|
2020-04-07 19:56:30 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* called by center() and Group.center()
|
|
|
|
|
*/
|
|
|
|
|
PRO.moveMesh = function(x, y, z) {
|
|
|
|
|
let gap = this.mesh.geometry.attributes.position,
|
2017-04-18 17:09:33 -04:00
|
|
|
pa = gap.array;
|
|
|
|
|
// center point array on 0,0,0
|
2020-04-07 19:56:30 -04:00
|
|
|
for (let i=0; i < pa.length; i += 3) {
|
|
|
|
|
pa[i ] -= x;
|
|
|
|
|
pa[i + 1] -= y;
|
|
|
|
|
pa[i + 2] -= z;
|
2017-04-18 17:09:33 -04:00
|
|
|
}
|
|
|
|
|
gap.needsUpdate = true;
|
2020-04-07 19:56:30 -04:00
|
|
|
let bb = this.groupBounds();
|
|
|
|
|
this.track.box = {
|
|
|
|
|
w: (bb.max.x - bb.min.x),
|
|
|
|
|
h: (bb.max.y - bb.min.y),
|
|
|
|
|
d: (bb.max.z - bb.min.z)
|
|
|
|
|
};
|
2017-04-18 17:09:33 -04:00
|
|
|
// for use with the packer
|
|
|
|
|
// invalidate cached points
|
|
|
|
|
this.points = null;
|
|
|
|
|
this.modified = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* moves top of widget to given Z
|
|
|
|
|
* used in CAM mode
|
|
|
|
|
*
|
|
|
|
|
* @param {number} z position
|
|
|
|
|
*/
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.setTopZ = function(z) {
|
2020-04-07 19:56:30 -04:00
|
|
|
let mesh = this.mesh,
|
2020-12-01 19:54:54 -05:00
|
|
|
pos = this.track.pos,
|
2020-12-22 15:05:50 -05:00
|
|
|
ltz = this.last_top_z || {},
|
|
|
|
|
mbz = mesh.getBoundingBox().max.z;
|
2017-04-18 17:09:33 -04:00
|
|
|
if (z) {
|
2020-12-22 15:05:50 -05:00
|
|
|
pos.z = mbz - z;
|
2017-04-18 17:09:33 -04:00
|
|
|
mesh.position.z = -pos.z - 0.01;
|
2020-12-22 15:05:50 -05:00
|
|
|
this.track.top = z;
|
2017-04-18 17:09:33 -04:00
|
|
|
} else {
|
|
|
|
|
pos.z = 0;
|
|
|
|
|
mesh.position.z = 0;
|
2020-12-22 15:05:50 -05:00
|
|
|
this.track.top = mbz;
|
2017-04-18 17:09:33 -04:00
|
|
|
}
|
2020-12-01 19:54:54 -05:00
|
|
|
let ntz = {
|
|
|
|
|
pz: pos.z,
|
|
|
|
|
mpz: mesh.position.z
|
|
|
|
|
};
|
|
|
|
|
this.modified |= (ltz.pz !== ntz.pz || ltz.mpz !== ntz.mpz);
|
|
|
|
|
this.last_top_z = ntz;
|
2017-04-18 17:09:33 -04:00
|
|
|
}
|
|
|
|
|
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.move = function(x, y, z, abs) {
|
2020-04-07 19:56:30 -04:00
|
|
|
this.group.forEach(w => {
|
|
|
|
|
w._move(x, y, z, abs);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PRO._move = function(x, y, z, abs) {
|
|
|
|
|
let mesh = this.mesh,
|
|
|
|
|
pos = this.track.pos;
|
2017-04-18 17:09:33 -04:00
|
|
|
// do not allow moves in pure slice view
|
|
|
|
|
if (!mesh.material.visible) return;
|
|
|
|
|
if (abs) {
|
|
|
|
|
mesh.position.set(x,y,z);
|
|
|
|
|
pos.x = (x || 0);
|
|
|
|
|
pos.y = (y || 0);
|
|
|
|
|
pos.z = (z || 0);
|
|
|
|
|
} else {
|
|
|
|
|
mesh.position.x += ( x || 0);
|
|
|
|
|
mesh.position.y += ( y || 0);
|
|
|
|
|
mesh.position.z += (-z || 0);
|
|
|
|
|
pos.x += (x || 0);
|
|
|
|
|
pos.y += (y || 0);
|
|
|
|
|
pos.z += (z || 0);
|
|
|
|
|
}
|
2020-03-28 14:39:24 -04:00
|
|
|
if (x || y || z) {
|
|
|
|
|
this.modified = true;
|
|
|
|
|
KIRI.api.event.emit('widget.move', {widget: this, pos});
|
|
|
|
|
}
|
2017-04-18 17:09:33 -04:00
|
|
|
};
|
|
|
|
|
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.scale = function(x, y, z) {
|
2020-04-07 19:56:30 -04:00
|
|
|
this.group.forEach(w => {
|
|
|
|
|
w._scale(x, y, z);
|
|
|
|
|
});
|
|
|
|
|
this.center();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PRO._scale = function(x, y, z) {
|
2020-03-16 17:26:12 -04:00
|
|
|
let mesh = this.mesh,
|
2020-04-07 19:56:30 -04:00
|
|
|
scale = this.track.scale;
|
2020-03-16 17:26:12 -04:00
|
|
|
this.bounds = null;
|
2017-04-18 17:09:33 -04:00
|
|
|
this.setWireframe(false);
|
|
|
|
|
this.clearSlices();
|
2020-03-16 23:57:14 -04:00
|
|
|
mesh.geometry.applyMatrix4(new THREE.Matrix4().makeScale(x, y, z));
|
2017-04-18 17:09:33 -04:00
|
|
|
scale.x *= (x || 1.0);
|
|
|
|
|
scale.y *= (y || 1.0);
|
|
|
|
|
scale.z *= (z || 1.0);
|
2020-12-01 19:54:54 -05:00
|
|
|
this.modified = true;
|
2017-04-18 17:09:33 -04:00
|
|
|
};
|
|
|
|
|
|
2021-01-24 00:13:09 -05:00
|
|
|
PRO.rotate = function(x, y, z, temp) {
|
2020-04-07 19:56:30 -04:00
|
|
|
this.group.forEach(w => {
|
2021-01-24 00:13:09 -05:00
|
|
|
w._rotate(x, y, z, temp);
|
2020-04-07 19:56:30 -04:00
|
|
|
});
|
2021-01-24 00:13:09 -05:00
|
|
|
this.center(false);
|
2020-04-07 19:56:30 -04:00
|
|
|
};
|
|
|
|
|
|
2021-04-27 21:53:21 -04:00
|
|
|
PRO.rotateRaw = function(x, y, z, temp) {
|
|
|
|
|
this.group.forEach(w => {
|
|
|
|
|
w._rotate(x, y, z, temp);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-06 14:04:43 -04:00
|
|
|
PRO._rotate = function(x, y, z, temp) {
|
|
|
|
|
if (!temp) {
|
|
|
|
|
this.bounds = null;
|
|
|
|
|
this.setWireframe(false);
|
|
|
|
|
this.clearSlices();
|
|
|
|
|
}
|
2020-03-30 14:10:36 -04:00
|
|
|
let m4 = new THREE.Matrix4();
|
|
|
|
|
let euler = typeof(x) === 'number';
|
|
|
|
|
if (euler) {
|
|
|
|
|
m4 = m4.makeRotationFromEuler(new THREE.Euler(x || 0, y || 0, z || 0));
|
|
|
|
|
} else {
|
|
|
|
|
m4 = m4.makeRotationFromQuaternion(x);
|
|
|
|
|
}
|
2020-06-14 10:00:05 -04:00
|
|
|
this.roto.push(m4);
|
|
|
|
|
this.mesh.geometry.applyMatrix4(m4);
|
2020-05-06 14:04:43 -04:00
|
|
|
if (!temp && euler) {
|
2020-04-07 19:56:30 -04:00
|
|
|
let rot = this.track.rot;
|
2020-03-30 14:10:36 -04:00
|
|
|
rot.x += (x || 0);
|
|
|
|
|
rot.y += (y || 0);
|
|
|
|
|
rot.z += (z || 0);
|
|
|
|
|
}
|
2020-12-01 19:54:54 -05:00
|
|
|
this.modified = true;
|
2017-04-18 17:09:33 -04:00
|
|
|
};
|
|
|
|
|
|
2020-06-13 22:52:46 -04:00
|
|
|
PRO.unrotate = function() {
|
2020-06-14 10:00:05 -04:00
|
|
|
this.roto.reverse().forEach(m => {
|
2021-03-31 11:29:58 -04:00
|
|
|
this.mesh.geometry.applyMatrix4(m.clone().invert());
|
2020-06-14 10:00:05 -04:00
|
|
|
});
|
|
|
|
|
this.roto = [];
|
|
|
|
|
this.center();
|
2020-12-01 19:54:54 -05:00
|
|
|
this.modified = true;
|
2020-06-13 22:52:46 -04:00
|
|
|
};
|
|
|
|
|
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.mirror = function() {
|
2020-04-07 19:56:30 -04:00
|
|
|
this.group.forEach(w => {
|
|
|
|
|
w._mirror();
|
|
|
|
|
});
|
|
|
|
|
this.center();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PRO._mirror = function() {
|
2017-04-18 17:09:33 -04:00
|
|
|
this.clearSlices();
|
2021-03-02 22:49:01 -05:00
|
|
|
this.setWireframe(false);
|
|
|
|
|
let geo = this.mesh.geometry, ot = this.track;
|
|
|
|
|
let pos = geo.attributes.position;
|
|
|
|
|
let arr = pos.array;
|
|
|
|
|
let count = pos.count;
|
|
|
|
|
// invert x
|
|
|
|
|
for (let i=0; i<count; i++) {
|
|
|
|
|
arr[i*3] = -arr[i*3];
|
2021-02-14 16:21:51 -05:00
|
|
|
}
|
2021-03-02 22:49:01 -05:00
|
|
|
// invert face vertices
|
|
|
|
|
for (let i=0; i<count; i+=3) {
|
|
|
|
|
let x = arr[i*3+0];
|
|
|
|
|
let y = arr[i*3+1];
|
|
|
|
|
let z = arr[i*3+2];
|
|
|
|
|
arr[i*3+0] = arr[i*3+6];
|
|
|
|
|
arr[i*3+1] = arr[i*3+7];
|
|
|
|
|
arr[i*3+2] = arr[i*3+8];
|
|
|
|
|
arr[i*3+6] = x;
|
|
|
|
|
arr[i*3+7] = y;
|
|
|
|
|
arr[i*3+8] = z;
|
|
|
|
|
}
|
|
|
|
|
pos.needsUpdate = true;
|
2017-04-18 17:09:33 -04:00
|
|
|
geo.computeFaceNormals();
|
|
|
|
|
geo.computeVertexNormals();
|
2021-03-02 22:49:01 -05:00
|
|
|
ot.mirror = !ot.mirror;
|
2020-12-01 19:54:54 -05:00
|
|
|
this.modified = true;
|
2021-03-02 22:49:01 -05:00
|
|
|
this.points = null;
|
2017-04-18 17:09:33 -04:00
|
|
|
};
|
|
|
|
|
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.getGeoVertices = function() {
|
2021-03-01 22:41:29 -05:00
|
|
|
let geo = this.mesh.geometry;
|
|
|
|
|
let pos = geo.getAttribute('position').array;
|
|
|
|
|
if (geo.index) {
|
|
|
|
|
let idx = geo.index.array;
|
|
|
|
|
let len = idx.length;
|
|
|
|
|
let p2 = new Float32Array(len * 3);
|
|
|
|
|
let inc = 0;
|
|
|
|
|
for (let i=0; i<len; i++) {
|
|
|
|
|
let iv = idx[i];
|
|
|
|
|
let ip = iv * 3;
|
|
|
|
|
p2[inc++] = pos[ip++];
|
|
|
|
|
p2[inc++] = pos[ip++];
|
|
|
|
|
p2[inc++] = pos[ip];
|
|
|
|
|
}
|
|
|
|
|
return p2;
|
|
|
|
|
} else {
|
|
|
|
|
return pos;
|
|
|
|
|
}
|
2017-04-18 17:09:33 -04:00
|
|
|
};
|
|
|
|
|
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.getPoints = function() {
|
2017-04-18 17:09:33 -04:00
|
|
|
if (!this.points) {
|
|
|
|
|
// convert and cache points from geometry vertices
|
2020-11-06 10:47:49 -05:00
|
|
|
this.points = BASE.verticesToPoints(this.getGeoVertices(), {
|
|
|
|
|
maxpass: 0 // disable decimation
|
|
|
|
|
});
|
2017-04-18 17:09:33 -04:00
|
|
|
}
|
|
|
|
|
return this.points;
|
|
|
|
|
};
|
|
|
|
|
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.getBoundingBox = function(refresh) {
|
2017-04-18 17:09:33 -04:00
|
|
|
if (!this.bounds || refresh) {
|
|
|
|
|
this.bounds = new THREE.Box3();
|
|
|
|
|
this.bounds.setFromPoints(this.getPoints());
|
|
|
|
|
}
|
|
|
|
|
return this.bounds;
|
|
|
|
|
};
|
|
|
|
|
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.isModified = function() {
|
2017-04-18 17:09:33 -04:00
|
|
|
return this.modified;
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-10 23:17:22 -04:00
|
|
|
PRO.getExtruder = function(settings) {
|
|
|
|
|
if (settings && settings.widget) {
|
|
|
|
|
let rec = settings.widget[this.id];
|
|
|
|
|
return rec && rec.extruder >= 0 ? rec.extruder : 0;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
};
|
|
|
|
|
|
2021-05-05 19:11:05 -04:00
|
|
|
// allow worker code to run in same memspace as client
|
|
|
|
|
PRO.setInWorker = function() {
|
|
|
|
|
this.inWorker = true;
|
|
|
|
|
return this;
|
|
|
|
|
};
|
|
|
|
|
|
2017-04-18 17:09:33 -04:00
|
|
|
/**
|
|
|
|
|
* processes points into facets, then into slices
|
|
|
|
|
*
|
|
|
|
|
* once upon a time there were multiple slicers. this was the fastest in most cases.
|
|
|
|
|
* lines are added to all the buckets they cross. then buckets are processed in order.
|
|
|
|
|
* buckets are contiguous ranges of z slicers. the advantage of this method is that
|
|
|
|
|
* as long as a large percentage of lines do not cross large z distances, this reduces
|
|
|
|
|
* the number of lines each slice has to consider thus improving speed.
|
|
|
|
|
*
|
|
|
|
|
* @params {Object} settings
|
|
|
|
|
* @params {Function} [ondone]
|
|
|
|
|
* @params {Function} [onupdate]
|
|
|
|
|
*/
|
2020-11-03 11:19:13 -05:00
|
|
|
PRO.slice = function(settings, ondone, onupdate) {
|
|
|
|
|
let widget = this;
|
|
|
|
|
let startTime = UTIL.time();
|
2017-04-18 17:09:33 -04:00
|
|
|
|
|
|
|
|
widget.settings = settings;
|
2020-03-26 20:45:04 -04:00
|
|
|
widget.clearSlices();
|
2017-04-18 17:09:33 -04:00
|
|
|
onupdate(0.0001, "slicing");
|
|
|
|
|
|
2021-05-05 19:11:05 -04:00
|
|
|
if (KIRI.client && !widget.inWorker) {
|
2020-03-26 20:45:04 -04:00
|
|
|
// in case result of slice is nothing, do not preserve previous
|
|
|
|
|
widget.slices = []
|
2017-04-18 17:09:33 -04:00
|
|
|
|
2017-11-17 13:11:13 -05:00
|
|
|
// executed from kiri.js
|
2020-11-03 11:19:13 -05:00
|
|
|
KIRI.client.slice(settings, this, function(reply) {
|
2017-04-18 17:09:33 -04:00
|
|
|
if (reply.update) {
|
|
|
|
|
onupdate(reply.update, reply.updateStatus);
|
|
|
|
|
}
|
2020-03-04 15:07:06 -05:00
|
|
|
if (reply.send_start) {
|
|
|
|
|
widget.xfer = {start: reply.send_start};
|
|
|
|
|
}
|
|
|
|
|
if (reply.stats) {
|
|
|
|
|
widget.stats = reply.stats;
|
|
|
|
|
}
|
|
|
|
|
if (reply.send_end) {
|
|
|
|
|
widget.stats.load_time = widget.xfer.start - reply.send_end;
|
|
|
|
|
}
|
|
|
|
|
if (reply.slice) {
|
|
|
|
|
widget.slices.push(KIRI.codec.decode(reply.slice, {mesh:widget.mesh}));
|
|
|
|
|
}
|
2020-12-04 19:44:31 -05:00
|
|
|
if (reply.rotinfo) {
|
|
|
|
|
widget.rotinfo = reply.rotinfo;
|
2017-12-31 12:57:26 -05:00
|
|
|
}
|
2018-03-08 10:42:26 -05:00
|
|
|
if (reply.done) {
|
2018-01-16 09:19:01 -05:00
|
|
|
ondone(true);
|
2017-04-18 17:09:33 -04:00
|
|
|
}
|
2020-12-04 19:44:31 -05:00
|
|
|
if (reply.error) {
|
|
|
|
|
ondone(false, reply.error);
|
|
|
|
|
}
|
2017-04-18 17:09:33 -04:00
|
|
|
});
|
2020-11-03 11:19:13 -05:00
|
|
|
}
|
2017-04-18 17:09:33 -04:00
|
|
|
|
2020-11-03 11:19:13 -05:00
|
|
|
if (KIRI.server) {
|
2017-11-17 13:11:13 -05:00
|
|
|
// executed from kiri-worker.js
|
2020-04-07 19:56:30 -04:00
|
|
|
let catchdone = function(error) {
|
2017-12-15 08:54:39 -05:00
|
|
|
if (error) {
|
|
|
|
|
return ondone(error);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-07 19:57:11 -05:00
|
|
|
onupdate(1.0, "transfer");
|
2017-12-15 08:54:39 -05:00
|
|
|
|
2017-04-18 17:09:33 -04:00
|
|
|
widget.stats.slice_time = UTIL.time() - startTime;
|
|
|
|
|
|
2017-12-15 08:54:39 -05:00
|
|
|
ondone();
|
2017-04-18 17:09:33 -04:00
|
|
|
};
|
|
|
|
|
|
2020-04-07 19:56:30 -04:00
|
|
|
let catchupdate = function(progress, message) {
|
2017-04-18 17:09:33 -04:00
|
|
|
onupdate(progress, message);
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-13 17:42:40 -04:00
|
|
|
let driver = DRIVERS[settings.mode.toUpperCase()];
|
2017-04-18 17:09:33 -04:00
|
|
|
|
|
|
|
|
if (driver) {
|
|
|
|
|
driver.slice(settings, widget, catchupdate, catchdone);
|
|
|
|
|
} else {
|
2021-07-04 21:05:46 -04:00
|
|
|
console.log('invalid mode: '+settings.mode);
|
2017-12-15 08:54:39 -05:00
|
|
|
ondone('invalid mode: '+settings.mode);
|
2017-04-18 17:09:33 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2020-11-08 10:55:36 -05:00
|
|
|
* render to provided stack
|
2017-04-18 17:09:33 -04:00
|
|
|
*/
|
2020-11-08 10:55:36 -05:00
|
|
|
PRO.render = function(stack) {
|
2020-11-07 19:57:11 -05:00
|
|
|
const mark = Date.now();
|
2020-11-08 10:55:36 -05:00
|
|
|
this.slices.forEach(slice => {
|
2020-11-30 22:36:06 -05:00
|
|
|
if (slice.layers) {
|
|
|
|
|
stack.add(slice.layers);
|
|
|
|
|
}
|
2017-04-18 17:09:33 -04:00
|
|
|
});
|
2020-11-08 10:55:36 -05:00
|
|
|
return Date.now() - mark;
|
2017-04-18 17:09:33 -04:00
|
|
|
};
|
|
|
|
|
|
2017-10-28 13:30:08 -04:00
|
|
|
PRO.setWireframe = function(set, color, opacity) {
|
2020-04-07 19:56:30 -04:00
|
|
|
let mesh = this.mesh,
|
2017-04-18 17:09:33 -04:00
|
|
|
widget = this;
|
|
|
|
|
if (this.wire) {
|
|
|
|
|
mesh.remove(this.wire);
|
|
|
|
|
this.wire = null;
|
|
|
|
|
this.setOpacity(solid_opacity);
|
|
|
|
|
}
|
|
|
|
|
if (set) {
|
|
|
|
|
widget.wire = base.render.wireframe(mesh, this.getPoints(), color);
|
2020-11-08 16:44:32 -05:00
|
|
|
}
|
|
|
|
|
if (opacity !== undefined) {
|
2017-04-18 17:09:33 -04:00
|
|
|
widget.setOpacity(opacity);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-10-30 17:36:27 -04:00
|
|
|
PRO.show = function() {
|
|
|
|
|
this.mesh.visible = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PRO.hide = function() {
|
|
|
|
|
this.mesh.visible = false;
|
|
|
|
|
};
|
|
|
|
|
|
2017-04-18 17:09:33 -04:00
|
|
|
})();
|