Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…

// 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; | |
})(); |