Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
407 lines (323 sloc) 12.1 KB
// namespace
var SLAcer = SLAcer || {};
;(function() {
// global settings
var globalSettings = {
color: 0xffff00
};
// -------------------------------------------------------------------------
function Point(p) {
this.x = p.x;
this.y = p.y;
this.s = p.x + ':' + p.y;
}
function Line(p1, p2) {
this.p1 = new Point(p1);
this.p2 = new Point(p2);
}
function isSameValue(v1, v2) {
return Math.abs(v1 - v2) <= Number.EPSILON;
}
function isSamePoint(p1, p2) {
return isSameValue(p1.x, p2.x) && isSameValue(p1.y, p2.y);
}
function linesToPolygons(lines) {
var polygons = [];
var polygon = [];
var firstLine, lastPoint, i, line;
function getNextPoint() {
var found = false;
for (i = 0; i < lines.length; i++) {
line = lines[i];
if (isSamePoint(lastPoint, line.p1)) {
lines.splice(i, 1);
if (isSamePoint(firstLine.p1, line.p2)) {
//console.log('closed loop');
break;
}
polygon.push(line.p2);
lastPoint = line.p2;
found = true;
}
else if (isSamePoint(lastPoint, line.p2)) {
lines.splice(i, 1);
if (isSamePoint(firstLine.p1, line.p1)) {
//console.log('closed loop');
break;
}
polygon.push(line.p1);
lastPoint = line.p1;
found = true;
}
}
return found;
}
while (lines.length) {
firstLine = lines.shift();
lastPoint = firstLine.p2;
polygon.push(firstLine.p1);
polygon.push(firstLine.p2);
while (getNextPoint()) {}
polygons.push(polygon);
polygon = [];
}
return polygons;
}
function pointInPolygon(point, polygon) {
// ray-casting algorithm based on
// https://github.com/substack/point-in-polygon/blob/master/index.js
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
var inside = false;
var il, j, pi, pj, intersect;
for (i = 0, il = polygon.length, j = il - 1; i < il; j = i++) {
pi = polygon[i];
pj = polygon[j];
(((pi.y > point.y) != (pj.y > point.y))
&& (point.x < (pj.x - pi.x) * (point.y - pi.y) / (pj.y - pi.y) + pi.x))
&& (inside = !inside);
}
return inside;
};
function makeNodes(polygons) {
// single polygon...
if (polygons.length == 1) {
return { 0: { parents: [], isHole: false } };
}
// nodes collection
var nodes = {};
// variables
var i, il, point, y, yl;
// for each polygon extract parents and childs polygons
for (i = 0, il = polygons.length; i < il; i++) {
// only check for first point in polygon
point = polygons[i][0];
// not enough faces ( !!! investigation required !!!)
if (polygons[i].length < 3) {
//console.log('--------------------');
//console.log(il, i, polygons[i]);
continue;
}
// for each polygons
for (y = 0, yl = il; y < yl; y++) {
// do not check self intersection
if (i == y) continue;
// create default node
nodes[i] || (nodes[i] = { parents: [], isHole: false });
nodes[y] || (nodes[y] = { parents: [], isHole: false });
// check if point in poylgon
if (pointInPolygon(point, polygons[y])) {
// push parent and child
nodes[i].parents.push(y);
// odd parents number ==> hole
nodes[i].isHole = !! (nodes[i].parents.length % 2);
nodes[y].isHole = !! (nodes[y].parents.length % 2);
}
}
}
// return nodes collection
return nodes;
}
function polygonsToShapes(polygons) {
// shapes collection
var shapes = [];
// make the nodes collection
var nodes = makeNodes(polygons);
//console.log('nodes:', nodes);
// variables
var key, node, i, il, parentKey;
// make base collection
for (key in nodes) {
node = nodes[key];
if (! node.isHole) {
shapes[key] = new THREE.Shape(polygons[key]);
}
}
// push holes
for (key in nodes) {
node = nodes[key];
if (node.isHole) {
for (i = 0, il = node.parents.length; i < il; i++) {
parentKey = node.parents[i];
if ((il - 1) == nodes[parentKey].parents.length) {
shapes[parentKey].holes.push(new THREE.Path(polygons[key]));
}
}
}
}
// return shapes collection
return shapes;
}
// -------------------------------------------------------------------------
// Constructor
function Slicer(settings) {
// settings
this.settings = _.defaults({}, settings || {}, Slicer.globalSettings);
// faces collection
this.faces = [];
// position
this.zHeight = 0;
this.zOffset = 0;
// plane
this.mesh = null;
this.plane = null;
this.slice = null;
}
// -------------------------------------------------------------------------
Slicer.prototype.loadMesh = function(mesh) {
// mesh
this.mesh = mesh;
// slice
this.slice = mesh.clone();
// bounding box
var box = mesh.geometry.boundingBox.clone();
// mesh size
var size = box.size();
// z height
this.zHeight = size.z;
this.zOffset = box.min.z;
// min/max faces (z)
this.facesMinMax = [];
var i, length, face, v1, v2, v3;
var geometry = this.slice.geometry;
for (i = 0, length = geometry.faces.length; i < length; i++) {
face = geometry.faces[i];
v1 = geometry.vertices[face.a];
v2 = geometry.vertices[face.b];
v3 = geometry.vertices[face.c];
this.facesMinMax.push({
min: Math.min(v1.z, v2.z, v3.z),
max: Math.max(v1.z, v2.z, v3.z)
});
}
// plane
this.plane = new SLAcer.Mesh(
new THREE.PlaneGeometry(size.x, size.y, 1),
new THREE.MeshBasicMaterial({
color: this.settings.color, side: THREE.DoubleSide
})
);
};
Slicer.prototype.getFaces = function(zPosition) {
var time = Date.now();
zPosition += this.zOffset;
var source = this.slice.geometry;
var geometry = new THREE.Geometry();
var plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), -zPosition);
var i, length, minMax, face, vertices, v1, v2, v3, index, normal;
var lines = [];
var line, top, bot;
function addLine(p1, p2) {
lines.push(new Line(p1, p2));
}
for (i = 0, length = source.faces.length; i < length; i++) {
minMax = this.facesMinMax[i];
if (minMax.min <= zPosition && minMax.max >= zPosition) {
face = source.faces[i];
vertices = source.vertices;
v1 = vertices[face.a].clone();
v2 = vertices[face.b].clone();
v3 = vertices[face.c].clone();
geometry.vertices.push(v1, v2, v3);
index = geometry.vertices.length;
normal = face.normal.clone();
geometry.faces.push(new THREE.Face3(
index-3, index-2, index-1, normal
));
// slice...
t1 = isSameValue(v1.z, zPosition);
t2 = isSameValue(v2.z, zPosition);
t3 = isSameValue(v3.z, zPosition);
touch = 0;
t1 && touch++;
t2 && touch++;
t3 && touch++;
// all points on plane
if (touch == 3) {
// skip since is shared with two points case
continue;
}
// two points on plane
if (touch == 2) {
if (t1 && t2 && !t3) addLine(v1, v2);
else if (!t1 && t2 && t3) addLine(v2, v3);
else addLine(v3, v1);
continue;
}
// one point on plane
if (touch == 1) {
// test if faces intersect the plane
if (t1 && ((v2.z > zPosition && v3.z < zPosition) || (v2.z < zPosition && v3.z > zPosition))) {
addLine(v1, plane.intersectLine(new THREE.Line3(v2, v3)));
}
else if (t2 && ((v3.z > zPosition && v1.z < zPosition) || (v3.z < zPosition && v1.z > zPosition))) {
addLine(v2, plane.intersectLine(new THREE.Line3(v3, v1)));
}
else if (t3 && ((v1.z > zPosition && v2.z < zPosition) || (v1.z < zPosition && v2.z > zPosition))) {
addLine(v3, plane.intersectLine(new THREE.Line3(v1, v2)));
}
// no intersection!
// skip since is shared with two points case
continue;
}
// no points on plane (need intersection)
if (touch == 0) {
top = [];
bot = [];
v1.z > zPosition && top.push(v1) || bot.push(v1);
v2.z > zPosition && top.push(v2) || bot.push(v2);
v3.z > zPosition && top.push(v3) || bot.push(v3);
if (top.length == 1) {
addLine(
plane.intersectLine(new THREE.Line3(top[0], bot[0])),
plane.intersectLine(new THREE.Line3(top[0], bot[1]))
);
}
else {
addLine(
plane.intersectLine(new THREE.Line3(top[0], bot[0])),
plane.intersectLine(new THREE.Line3(top[1], bot[0]))
);
}
}
}
}
var polygons = linesToPolygons(lines);
var shapes = polygonsToShapes(polygons);
var meshes = [];
for (key in shapes) {
try {
var color = this.settings.color;
//var color = ((1<<24)*Math.random()|0);
var geo = new THREE.ShapeGeometry(shapes[key]);
if (!geo.faces.length || !geo.vertices.length) {
delete shapes[key];
continue;
}
meshes.push(new THREE.Mesh(
geo,
new THREE.MeshBasicMaterial({
color: color, side: THREE.DoubleSide
})
));
}
catch(e) {
console.error(e);
console.log(shapes[key]);
}
}
// remove empty shapes...
shapes = shapes.filter(function(n){ return n != undefined });
return {
time : Date.now() - time,
geometry: geometry,
polygons: polygons,
shapes : shapes,
meshes : meshes
};
};
// -------------------------------------------------------------------------
// global settings
Slicer.globalSettings = globalSettings;
// export module
SLAcer.Slicer = Slicer;
})();