created abstraction for clipper js

This commit is contained in:
casperlamboo 2015-05-12 11:29:01 +02:00
parent c63f50075f
commit 9740c254c2
11 changed files with 528 additions and 507 deletions

3
.gitignore vendored
View File

@ -16,3 +16,6 @@ src/script/.DS_Store
src/.DS_Store src/.DS_Store
bower_components/ bower_components/
src/oldcode.js
test.html

445
build/d3d.js vendored
View File

@ -89,61 +89,6 @@ Array.prototype.clone = function () {
return array; return array;
}; };
function applyMouseControls (renderer, camera, center, maxDistance) {
"use strict";
//TODO
//impliment touch controls
//windows mouse wheel fix
var distance = 20;
var rotX = 0;
var rotY = 0;
var moveCamera = false;
function updateCamera () {
camera.position.set(
Math.cos(rotY)*Math.sin(rotX)*distance,
Math.sin(rotY)*distance,
Math.cos(rotY)*Math.cos(rotX)*distance
).add(center);
camera.lookAt(center);
}
$(renderer.domElement).on("mousedown", function (e) {
moveCamera = true;
}).on("wheel", function (e) {
var event = e.originalEvent;
event.preventDefault();
distance = THREE.Math.clamp(distance - event.wheelDelta, 1, maxDistance);
updateCamera();
});
$(window).on("mouseup", function (e) {
moveCamera = false;
}).on("mousemove", function (e) {
var event = e.originalEvent;
if (moveCamera === true) {
rotX = (rotX - event.webkitMovementX/100) % (2*Math.PI);
rotY = THREE.Math.clamp(rotY + event.webkitMovementY/100, -Math.PI/2, Math.PI/2);
updateCamera();
}
});
updateCamera();
}
var requestAnimFrame = (function () {
"use strict";
return requestAnimationFrame || webkitRequestAnimationFrame || mozRequestAnimationFrame || function (callback) {
setTimeout(callback, 1000/60);
};
})();
/****************************************************** /******************************************************
* *
* Box * Box
@ -617,6 +562,159 @@ D3D.Printer.prototype.subsituteVariables = function (gcode) {
}; };
/****************************************************** /******************************************************
* *
* Path
*
* Abstraction layer for annoying clipper js
*
******************************************************/
D3D.Path = function (path, closed) {
"use strict";
this.path = path || [];
this.closed = (closed !== undefined) ? closed : true;
};
D3D.Path.prototype.setPath = function (path) {
"use strict";
this.path = path;
return this;
};
D3D.Path.prototype.union = function (path) {
"use strict";
var solution = new ClipperLib.Paths();
var clipper = new ClipperLib.Clipper();
clipper.AddPaths(this.path, ClipperLib.PolyType.ptSubject, this.closed);
clipper.AddPaths(path.path, ClipperLib.PolyType.ptClip, path.closed);
clipper.Execute(ClipperLib.ClipType.ctUnion, solution);
return new D3D.Path(solution, this.closed);
};
D3D.Path.prototype.difference = function (path) {
"use strict";
var solution = new ClipperLib.Paths();
var clipper = new ClipperLib.Clipper();
clipper.AddPaths(this.path, ClipperLib.PolyType.ptSubject, this.closed);
clipper.AddPaths(path.path, ClipperLib.PolyType.ptClip, path.closed);
clipper.Execute(ClipperLib.ClipType.ctDifference, solution);
return new D3D.Path(solution, this.closed);
};
D3D.Path.prototype.intersect = function (path) {
"use strict";
var solution = new ClipperLib.Paths();
var clipper = new ClipperLib.Clipper();
clipper.AddPaths(this.path, ClipperLib.PolyType.ptSubject, this.closed);
clipper.AddPaths(path.path, ClipperLib.PolyType.ptClip, path.closed);
clipper.Execute(ClipperLib.ClipType.ctIntersection, solution);
return new D3D.Path(solution, this.closed);
};
D3D.Path.prototype.xor = function () {
"use strict";
var solution = new ClipperLib.Paths();
var clipper = new ClipperLib.Clipper();
clipper.AddPaths(this.path, ClipperLib.PolyType.ptSubject, this.closed);
clipper.AddPaths(path.path, ClipperLib.PolyType.ptClip, path.closed);
clipper.Execute(ClipperLib.ClipType.ctXor, solution);
return new D3D.Path(solution, this.closed);
};
D3D.Path.prototype.offset = function (offset) {
"use strict";
var solution = new ClipperLib.Paths();
var co = new ClipperLib.ClipperOffset(1, 1);
co.AddPaths(this.path, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);
co.Execute(solution, offset);
return new D3D.Path(solution, this.closed);
};
D3D.Path.prototype.scaleUp = function (factor) {
"use strict";
var path = ClipperLib.JS.ScaleUpPaths(this.path, factor);
return this;
};
D3D.Path.prototype.scaleDown = function (factor) {
"use strict";
var path = ClipperLib.JS.ScaleDownPaths(this.path, factor);
return this;
};
D3D.Path.prototype.tresholdArea = function (minArea) {
"use strict";
for (var i = 0; i < this.path.length; i ++) {
var shape = this.path[i];
var area = ClipperLib.Clipper.Area(shape);
if (area < minArea) {
this.path.splice(i, 1);
i --;
}
}
return areas;
};
D3D.Path.prototype.area = function () {
"use strict";
var areas = [];
for (var i = 0; i < this.path.length; i ++) {
var shape = this.path[i];
areas.push(ClipperLib.Clipper.Area(shape))
}
return areas;
};
D3D.Path.prototype.join = function (path) {
"use strict";
this.path = this.path.concat(path.path);
return this;
}
D3D.Path.prototype.clone = function () {
"use strict";
var path = ClipperLib.JS.Clone(this.path);
return new D3D.Path(path, this.closed);
}
D3D.Path.prototype.draw = function (context, color) {
"use strict";
context.strokeStyle = color;
for (var i = 0; i < this.path.length; i ++) {
var shape = this.path[i];
context.beginPath();
var length = this.closed ? (shape.length + 1) : shape.length;
for (var j = 0; j < length; j ++) {
var point = shape[j % shape.length];
context.lineTo(point.X*2, point.Y*2);
}
context.stroke();
}
};
/******************************************************
*
* Slicer * Slicer
* *
* TODO (optimalisatie) * TODO (optimalisatie)
@ -732,8 +830,8 @@ D3D.Slicer.prototype.slice = function (height, step) {
var line = this.lines[index].line; var line = this.lines[index].line;
var alpha = (y - line.start.y) / (line.end.y - line.start.y); var alpha = (y - line.start.y) / (line.end.y - line.start.y);
var x = line.start.x * alpha + line.end.x * (1 - alpha); var x = line.end.x * alpha + line.start.x * (1 - alpha);
var z = line.start.z * alpha + line.end.z * (1 - alpha); var z = line.end.z * alpha + line.start.z * (1 - alpha);
intersections[index] = new THREE.Vector2(x, z); intersections[index] = new THREE.Vector2(x, z);
} }
@ -807,7 +905,8 @@ D3D.Slicer.prototype.slice = function (height, step) {
//stop when ther are no intersects //stop when ther are no intersects
if (slice.length > 0) { if (slice.length > 0) {
slices.push(slice); slices.push(new D3D.Path(slice, true));
//slices.push(slice);
} }
else { else {
break; break;
@ -816,20 +915,64 @@ D3D.Slicer.prototype.slice = function (height, step) {
return slices; return slices;
}; };
D3D.Slicer.prototype.getInset = function (slice, offset) { D3D.Slicer.prototype.slicesToData = function (slices, printer) {
"use strict"; "use strict";
var solution = new ClipperLib.Paths(); var scale = 100;
var co = new ClipperLib.ClipperOffset(1, 1);
co.AddPaths(slice, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);
co.Execute(solution, -offset);
return solution; var layerHeight = printer.config["printer.layerHeight"] * scale;
var dimensionsZ = printer.config["printer.dimensions.z"] * scale;
var wallThickness = printer.config["printer.wallThickness"] * scale;
var shellThickness = printer.config["printer.shellThickness"] * scale;
var fillSize = printer.config["printer.fillSize"] * scale;
var brimOffset = printer.config["printer.brimOffset"] * scale;
var skinCount = Math.ceil(shellThickness/layerHeight);
var data = [];
var lowFillTemplate = this.getFillTemplate(dimensionsZ, fillSize, true, true);
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
var outerLayer = slice.clone();
outerLayer.scaleUp(scale);
var insets = new D3D.Path();
for (var offset = wallThickness; offset <= shellThickness; offset += wallThickness) {
var inset = outerLayer.offset(-offset);
insets.join(inset);
}
var fillArea = (inset || outerLayer).offset(-wallThickness/2);
var downFill = (layer - skinCount >= 0) ? slices[layer - skinCount] : new D3D.Path();
var upFill = (layer + skinCount < slices.length) ? slices[layer + skinCount] : new D3D.Path();
var highFillArea = fillArea.difference(downFill.intersect(upFill).scaleUp(scale));
var lowFillArea = fillArea.difference(highFillArea);
var fill = new D3D.Path([], false);
fill.join(lowFillTemplate.intersect(lowFillArea));
if (highFillArea.path.length > 0) {
var highFillTemplate = this.getFillTemplate(dimensionsZ, wallThickness, (layer % 2 === 0), (layer % 2 === 1));
fill.join(highFillTemplate.intersect(highFillArea));
}
data.push({
outerLayer: outerLayer.scaleDown(scale),
insets: insets.scaleDown(scale),
fill: fill.scaleDown(scale)
});
}
return data;
}; };
D3D.Slicer.prototype.getFillTemplate = function (dimension, size, even, uneven) { D3D.Slicer.prototype.getFillTemplate = function (dimension, size, even, uneven) {
"use strict"; "use strict";
var paths = new ClipperLib.Paths(); var paths = [];
if (even) { if (even) {
for (var length = 0; length <= dimension; length += size) { for (var length = 0; length <= dimension; length += size) {
@ -842,125 +985,8 @@ D3D.Slicer.prototype.getFillTemplate = function (dimension, size, even, uneven)
} }
} }
return paths; //return paths;
}; return new D3D.Path(paths, false);
D3D.Slicer.prototype.slicesToData = function (slices, printer) {
"use strict";
//scale because of clipper crap
var scale = 100;
var layerHeight = printer.config["printer.layerHeight"] * scale;
var dimensionsZ = printer.config["printer.dimensions.z"] * scale;
var wallThickness = printer.config["printer.wallThickness"] * scale;
var shellThickness = printer.config["printer.shellThickness"] * scale;
var fillSize = printer.config["printer.fillSize"] * scale;
var brimOffset = printer.config["printer.brimOffset"] * scale;
var data = [];
var lowFillTemplate = this.getFillTemplate(dimensionsZ, fillSize, true, true);
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
//var outerLayer = ClipperLib.JS.Clean(slice, 1.0);
var outerLayer = slice.clone();
ClipperLib.JS.ScaleUpPaths(outerLayer, scale);
var innerLayer = [];
for (var i = wallThickness; i < shellThickness; i += wallThickness) {
var inset = this.getInset(outerLayer, i);
innerLayer = innerLayer.concat(inset);
}
//moet fillArea wel kleiner?
//var fillArea = this.getInset((inset || outerLayer), wallThickness);
var fillArea = (inset || outerLayer);
var fillAbove = false;
//for (var i = 1; i < shellThickness/layerHeight; i ++) {
for (var i = 1; i < shellThickness/layerHeight; i ++) {
var newLayer = ClipperLib.JS.Clone(slices[layer + i]);
ClipperLib.JS.ScaleUpPaths(newLayer, scale);
if (newLayer.length === 0 || (fillAbove && fillAbove.length === 0)) {
fillAbove = [];
break;
}
else if (fillAbove === false) {
fillAbove = newLayer;
}
else {
var c = new ClipperLib.Clipper();
var solution = new ClipperLib.Paths();
c.AddPaths(newLayer, ClipperLib.PolyType.ptSubject, true);
c.AddPaths(fillAbove, ClipperLib.PolyType.ptClip, true);
c.Execute(ClipperLib.ClipType.ctIntersection, solution);
fillAbove = solution;
}
}
//kijkt alleen nog naar boven
//omliggende lagen hebben inhoud van lowFill;
//inset moet opgevult worden;
//verschill tussen lowFill en inset moet vol, rest is raster
var clipper = new ClipperLib.Clipper();
var highFillArea = new ClipperLib.Paths();
clipper.AddPaths(fillArea, ClipperLib.PolyType.ptSubject, true);
clipper.AddPaths(fillAbove, ClipperLib.PolyType.ptClip, true);
clipper.Execute(ClipperLib.ClipType.ctDifference, highFillArea);
var clipper = new ClipperLib.Clipper();
var lowFillArea = new ClipperLib.Paths();
clipper.AddPaths(fillArea, ClipperLib.PolyType.ptSubject, true);
clipper.AddPaths(highFillArea, ClipperLib.PolyType.ptClip, true);
clipper.Execute(ClipperLib.ClipType.ctDifference, lowFillArea);
var fill = [];
var clipper = new ClipperLib.Clipper();
var lowFillStrokes = new ClipperLib.Paths();
clipper.AddPaths(lowFillTemplate, ClipperLib.PolyType.ptSubject, false);
clipper.AddPaths(lowFillArea, ClipperLib.PolyType.ptClip, true);
clipper.Execute(ClipperLib.ClipType.ctIntersection, lowFillStrokes);
fill = fill.concat(lowFillStrokes);
//optimize
//make as big as bounding box of highFillArea
var highFillTemplate = this.getFillTemplate(dimensionsZ, wallThickness, (layer % 2 === 0), (layer % 2 === 1));
var clipper = new ClipperLib.Clipper();
var highFillStrokes = new ClipperLib.Paths();
clipper.AddPaths(highFillTemplate, ClipperLib.PolyType.ptSubject, false);
clipper.AddPaths(highFillArea, ClipperLib.PolyType.ptClip, true);
clipper.Execute(ClipperLib.ClipType.ctIntersection, highFillStrokes);
fill = fill.concat(highFillStrokes);
//create brim
/*if (layer === 0) {
var brim = this.getInset(outerLayer, -brimOffset);
outerLayer = brim.concat(outerLayer);
}*/
ClipperLib.JS.ScaleDownPaths(outerLayer, scale);
ClipperLib.JS.ScaleDownPaths(innerLayer, scale);
ClipperLib.JS.ScaleDownPaths(fill, scale);
data.push({
outerLayer: outerLayer,
innerLayer: innerLayer,
fill: fill
});
}
return data;
}; };
D3D.Slicer.prototype.dataToGcode = function (data, printer) { D3D.Slicer.prototype.dataToGcode = function (data, printer) {
"use strict"; "use strict";
@ -982,8 +1008,8 @@ D3D.Slicer.prototype.dataToGcode = function (data, printer) {
function sliceToGcode (slice) { function sliceToGcode (slice) {
var gcode = []; var gcode = [];
for (var i = 0; i < slice.length; i ++) { for (var i = 0; i < slice.path.length; i ++) {
var shape = slice[i]; var shape = slice.path[i];
var previousPoint; var previousPoint;
@ -1057,54 +1083,20 @@ D3D.Slicer.prototype.dataToGcode = function (data, printer) {
var z = ((layer + 1) * layerHeight).toFixed(3); var z = ((layer + 1) * layerHeight).toFixed(3);
gcode = gcode.concat(sliceToGcode(slice.outerLayer)); gcode = gcode.concat(sliceToGcode(slice.outerLayer));
gcode = gcode.concat(sliceToGcode(slice.innerLayer)); gcode = gcode.concat(sliceToGcode(slice.insets));
gcode = gcode.concat(sliceToGcode(slice.fill)); gcode = gcode.concat(sliceToGcode(slice.fill));
} }
gcode = gcode.concat(printer.getEndCode()); gcode = gcode.concat(printer.getEndCode());
return gcode; return gcode;
}; };
//only for debug purposes
D3D.Slicer.prototype.drawPaths = function (printer, min, max) { D3D.Slicer.prototype.drawPaths = function (printer, min, max) {
"use strict"; "use strict";
var layerHeight = printer.config["printer.layerHeight"]; var layerHeight = printer.config["printer.layerHeight"];
var dimensionsZ = printer.config["printer.dimensions.z"]; var dimensionsZ = printer.config["printer.dimensions.z"];
function drawLines (paths, color) {
context.fillStyle = color;
context.strokeStyle = color;
context.beginPath();
for (var i = 0; i < paths.length; i ++) {
var path = paths[i];
context.moveTo((path[0].X * 2), (path[0].Y * 2));
for (var j = 0; j < path.length; j ++) {
var point = path[j];
context.lineTo((point.X * 2), (point.Y * 2));
}
//context.closePath();
}
context.stroke();
}
function drawVertexes (paths, color) {
context.fillStyle = color;
context.strokeStyle = color;
for (var i = 0; i < paths.length; i ++) {
var path = paths[i];
for (var j = 0; j < path.length; j ++) {
var point = path[j];
context.beginPath();
context.arc(point.X * 2, point.Y * 2, 1, 0, Math.PI*2, false);
context.stroke();
}
}
}
var slices = this.slice(dimensionsZ, layerHeight); var slices = this.slice(dimensionsZ, layerHeight);
var data = this.slicesToData(slices, printer); var data = this.slicesToData(slices, printer);
@ -1114,14 +1106,13 @@ D3D.Slicer.prototype.drawPaths = function (printer, min, max) {
canvas.height = 400; canvas.height = 400;
var context = canvas.getContext("2d"); var context = canvas.getContext("2d");
for (var layer = min; layer < max; layer ++) { for (var layer = min; layer < max; layer ++) {
var slice = data[layer % data.length]; var slice = data[layer % data.length];
drawLines(slice.outerLayer, "red"); slice.insets.draw(context, "blue");
drawLines(slice.innerLayer, "green"); slice.outerLayer.draw(context, "green");
drawLines(slice.fill, "blue"); slice.fill.draw(context, "red");
drawVertexes(slice.outerLayer, "green");
} }
return canvas; return canvas;

2
build/d3d.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -10,6 +10,7 @@ var files = [
"src/utils.js", "src/utils.js",
"src/box.js", "src/box.js",
"src/printer.js", "src/printer.js",
"src/path.js",
"src/slicer.js" "src/slicer.js"
]; ];

BIN
models/cubes.stl Normal file

Binary file not shown.

BIN
models/overhang_test.stl Normal file

Binary file not shown.

View File

@ -11,6 +11,7 @@
<script src="src/utils.js"></script> <script src="src/utils.js"></script>
<script src="src/box.js"></script> <script src="src/box.js"></script>
<script src="src/printer.js"></script> <script src="src/printer.js"></script>
<script src="src/path.js"></script>
<script src="src/slicer.js"></script> <script src="src/slicer.js"></script>
<script src="gcode/testgcode.js"></script> <script src="gcode/testgcode.js"></script>
@ -43,9 +44,8 @@ var printerConfig = {
"printer.heatup.bed.temperature": 70, "printer.heatup.bed.temperature": 70,
"printer.heatup.enabled": true, "printer.heatup.enabled": true,
"printer.heatup.temperature": 180, "printer.heatup.temperature": 180,
"printer.layerHeight": 0.3,
"printer.retraction.amount": 3, "printer.retraction.amount": 3,
"printer.retraction.enabled": true, "printer.retraction.enabled": false,
"printer.retraction.minDistance": 5, "printer.retraction.minDistance": 5,
"printer.retraction.speed": 50, "printer.retraction.speed": 50,
"printer.screenToMillimeterScale": 0.3, //???? "printer.screenToMillimeterScale": 0.3, //????
@ -55,13 +55,16 @@ var printerConfig = {
"printer.travelSpeed": 200, "printer.travelSpeed": 200,
"printer.type": "ultimaker", "printer.type": "ultimaker",
"printer.useSubLayers": true, //wat is dit? "printer.useSubLayers": true, //wat is dit?
"printer.wallThickness": 0.4,
"printer.wallThickness": 0.4, //nozzle
"printer.layerHeight": 0.3,
//variabele toevoegen; //variabele toevoegen;
//-snelheid, retraction etc voor verschillende types (outerlayer, innerlayer, fill) //-snelheid, retraction etc voor verschillende types (outerlayer, innerlayer, fill)
"printer.shellThickness": 0.8, "printer.shellThickness": 0.4,
"printer.fillSize": 5, //dit is het raster aan de binnen kant van de geometry "printer.fillSize": 5, //dit is het raster aan de binnen kant van de geometry
"printer.brimOffset": 5 "printer.brimOffset": 5,
"printer.bottomTopThickness": 0.8
}; };
var printer = new D3D.Printer(printerConfig); var printer = new D3D.Printer(printerConfig);
@ -104,53 +107,30 @@ var geometry = (function () {
var material = new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true}); var material = new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true});
//var geometry = new THREE.TorusGeometry(20, 10, 30, 30); //var geometry = new THREE.TorusGeometry(20, 10, 30, 30);
var geometry = new THREE.BoxGeometry(20, 20, 20, 1, 1, 1); var geometry = new THREE.BoxGeometry(10, 10, 10, 1, 1, 1);
//var geometry = new THREE.SphereGeometry(10, 10, 10); //var geometry = new THREE.SphereGeometry(10, 10, 10);
var mesh = new THREE.Mesh(geometry, material); var mesh = new THREE.Mesh(geometry, material);
mesh.position.x = 100; mesh.position.x = 100;
mesh.position.z = 100; mesh.position.z = 100;
scene.add(mesh); //scene.add(mesh);
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var slicer = new D3D.Slicer().setMesh(mesh);
gcode = slicer.getGcode(printer);
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var layer = 0;
var img = slicer.drawPaths(printer, layer, layer + 1);
context.drawImage(img, 0, 0);
/*
var loader = new THREE.STLLoader(); var loader = new THREE.STLLoader();
loader.load('stl/overhang_test.stl', function (geometry) { loader.load("models/cubes.stl", function (geometry) {
var matrix = new THREE.Matrix4();
matrix.makeRotationX(Math.PI*1.5);
geometry.applyMatrix(matrix);
var material = new THREE.MeshLambertMaterial({color: 0x000000, wireframe: true});
var mesh = new THREE.Mesh(geometry, material); var mesh = new THREE.Mesh(geometry, material);
mesh.position.x = 100;
mesh.position.z = 100;
mesh.rotation.x = -Math.PI/2;
mesh.scale.x = mesh.scale.y = mesh.scale.z = 3;
//mesh is lifted a little bit...
mesh.position.y = -0.6552429199218741;
scene.add(mesh); scene.add(mesh);
var canvas = document.getElementById("canvas"); var slicer = new D3D.Slicer().setMesh(mesh);
var context = canvas.getContext("2d");
var slicer = new D3D.Slicer().setMesh(geometry); gcode = slicer.getGcode(printer);
var layer = 149;
var img = slicer.drawPaths(printer, layer, layer + 1);
context.drawImage(img, 0, 0);
var gcode = slicer.getGcode(printer);
console.log(gcode);
}); });
*/
(function animate () { (function animate () {
"use strict"; "use strict";
@ -158,6 +138,53 @@ loader.load('stl/overhang_test.stl', function (geometry) {
requestAnimationFrame(animate); requestAnimationFrame(animate);
renderer.render(scene, camera); renderer.render(scene, camera);
})(); })();
function applyMouseControls (renderer, camera, center, maxDistance) {
"use strict";
//TODO
//impliment touch controls
//windows mouse wheel fix
var distance = 20;
var rotX = 0;
var rotY = 0;
var moveCamera = false;
function updateCamera () {
camera.position.set(
Math.cos(rotY)*Math.sin(rotX)*distance,
Math.sin(rotY)*distance,
Math.cos(rotY)*Math.cos(rotX)*distance
).add(center);
camera.lookAt(center);
}
$(renderer.domElement).on("mousedown", function (e) {
moveCamera = true;
}).on("wheel", function (e) {
var event = e.originalEvent;
event.preventDefault();
distance = THREE.Math.clamp(distance - event.wheelDelta, 1, maxDistance);
updateCamera();
});
$(window).on("mouseup", function (e) {
moveCamera = false;
}).on("mousemove", function (e) {
var event = e.originalEvent;
if (moveCamera === true) {
rotX = (rotX - event.webkitMovementX/100) % (2*Math.PI);
rotY = THREE.Math.clamp(rotY + event.webkitMovementY/100, -Math.PI/2, Math.PI/2);
updateCamera();
}
});
updateCamera();
}
</script> </script>
</body> </body>

153
src/path.js Normal file
View File

@ -0,0 +1,153 @@
/******************************************************
*
* Path
*
* Abstraction layer for annoying clipper js
*
******************************************************/
D3D.Path = function (path, closed) {
"use strict";
this.path = path || [];
this.closed = (closed !== undefined) ? closed : true;
};
D3D.Path.prototype.setPath = function (path) {
"use strict";
this.path = path;
return this;
};
D3D.Path.prototype.union = function (path) {
"use strict";
var solution = new ClipperLib.Paths();
var clipper = new ClipperLib.Clipper();
clipper.AddPaths(this.path, ClipperLib.PolyType.ptSubject, this.closed);
clipper.AddPaths(path.path, ClipperLib.PolyType.ptClip, path.closed);
clipper.Execute(ClipperLib.ClipType.ctUnion, solution);
return new D3D.Path(solution, this.closed);
};
D3D.Path.prototype.difference = function (path) {
"use strict";
var solution = new ClipperLib.Paths();
var clipper = new ClipperLib.Clipper();
clipper.AddPaths(this.path, ClipperLib.PolyType.ptSubject, this.closed);
clipper.AddPaths(path.path, ClipperLib.PolyType.ptClip, path.closed);
clipper.Execute(ClipperLib.ClipType.ctDifference, solution);
return new D3D.Path(solution, this.closed);
};
D3D.Path.prototype.intersect = function (path) {
"use strict";
var solution = new ClipperLib.Paths();
var clipper = new ClipperLib.Clipper();
clipper.AddPaths(this.path, ClipperLib.PolyType.ptSubject, this.closed);
clipper.AddPaths(path.path, ClipperLib.PolyType.ptClip, path.closed);
clipper.Execute(ClipperLib.ClipType.ctIntersection, solution);
return new D3D.Path(solution, this.closed);
};
D3D.Path.prototype.xor = function () {
"use strict";
var solution = new ClipperLib.Paths();
var clipper = new ClipperLib.Clipper();
clipper.AddPaths(this.path, ClipperLib.PolyType.ptSubject, this.closed);
clipper.AddPaths(path.path, ClipperLib.PolyType.ptClip, path.closed);
clipper.Execute(ClipperLib.ClipType.ctXor, solution);
return new D3D.Path(solution, this.closed);
};
D3D.Path.prototype.offset = function (offset) {
"use strict";
var solution = new ClipperLib.Paths();
var co = new ClipperLib.ClipperOffset(1, 1);
co.AddPaths(this.path, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);
co.Execute(solution, offset);
return new D3D.Path(solution, this.closed);
};
D3D.Path.prototype.scaleUp = function (factor) {
"use strict";
var path = ClipperLib.JS.ScaleUpPaths(this.path, factor);
return this;
};
D3D.Path.prototype.scaleDown = function (factor) {
"use strict";
var path = ClipperLib.JS.ScaleDownPaths(this.path, factor);
return this;
};
D3D.Path.prototype.tresholdArea = function (minArea) {
"use strict";
for (var i = 0; i < this.path.length; i ++) {
var shape = this.path[i];
var area = ClipperLib.Clipper.Area(shape);
if (area < minArea) {
this.path.splice(i, 1);
i --;
}
}
return areas;
};
D3D.Path.prototype.area = function () {
"use strict";
var areas = [];
for (var i = 0; i < this.path.length; i ++) {
var shape = this.path[i];
areas.push(ClipperLib.Clipper.Area(shape))
}
return areas;
};
D3D.Path.prototype.join = function (path) {
"use strict";
this.path = this.path.concat(path.path);
return this;
}
D3D.Path.prototype.clone = function () {
"use strict";
var path = ClipperLib.JS.Clone(this.path);
return new D3D.Path(path, this.closed);
}
D3D.Path.prototype.draw = function (context, color) {
"use strict";
context.strokeStyle = color;
for (var i = 0; i < this.path.length; i ++) {
var shape = this.path[i];
context.beginPath();
var length = this.closed ? (shape.length + 1) : shape.length;
for (var j = 0; j < length; j ++) {
var point = shape[j % shape.length];
context.lineTo(point.X*2, point.Y*2);
}
context.stroke();
}
};

View File

@ -23,12 +23,11 @@ D3D.Slicer.prototype.setMesh = function (mesh) {
mesh.updateMatrix(); mesh.updateMatrix();
var geometry = mesh.geometry.clone(); var geometry = mesh.geometry.clone();
geometry.mergeVertices();
geometry.applyMatrix(mesh.matrix);
if (geometry instanceof THREE.BufferGeometry) { if (geometry instanceof THREE.BufferGeometry) {
geometry = new THREE.Geometry().fromBufferGeometry(geometry); geometry = new THREE.Geometry().fromBufferGeometry(geometry);
} }
geometry.mergeVertices();
geometry.applyMatrix(mesh.matrix);
this.geometry = geometry; this.geometry = geometry;
@ -55,7 +54,8 @@ D3D.Slicer.prototype.createLines = function () {
self.lines.push({ self.lines.push({
line: new THREE.Line3(self.geometry.vertices[a], self.geometry.vertices[b]), line: new THREE.Line3(self.geometry.vertices[a], self.geometry.vertices[b]),
connects: [], connects: [],
normals: [] normals: [],
ignore: 0
}); });
} }
@ -80,6 +80,12 @@ D3D.Slicer.prototype.createLines = function () {
this.lines[a].normals.push(normal); this.lines[a].normals.push(normal);
this.lines[b].normals.push(normal); this.lines[b].normals.push(normal);
this.lines[c].normals.push(normal); this.lines[c].normals.push(normal);
if (face.normal.y === 1 || face.normal.y === -1) {
this.lines[a].ignore ++;
this.lines[b].ignore ++;
this.lines[c].ignore ++;
}
} }
}; };
D3D.Slicer.prototype.slice = function (height, step) { D3D.Slicer.prototype.slice = function (height, step) {
@ -93,6 +99,7 @@ D3D.Slicer.prototype.slice = function (height, step) {
var min = Math.ceil(Math.min(line.line.start.y, line.line.end.y) / step); var min = Math.ceil(Math.min(line.line.start.y, line.line.end.y) / step);
var max = Math.floor(Math.max(line.line.start.y, line.line.end.y) / step); var max = Math.floor(Math.max(line.line.start.y, line.line.end.y) / step);
if (line.ignore < 2) {
for (var layerIndex = min; layerIndex <= max; layerIndex ++) { for (var layerIndex = min; layerIndex <= max; layerIndex ++) {
if (layerIndex >= 0) { if (layerIndex >= 0) {
if (layersIntersections[layerIndex] === undefined) { if (layersIntersections[layerIndex] === undefined) {
@ -102,6 +109,7 @@ D3D.Slicer.prototype.slice = function (height, step) {
} }
} }
} }
}
var slices = []; var slices = [];
@ -115,8 +123,8 @@ D3D.Slicer.prototype.slice = function (height, step) {
var line = this.lines[index].line; var line = this.lines[index].line;
var alpha = (y - line.start.y) / (line.end.y - line.start.y); var alpha = (y - line.start.y) / (line.end.y - line.start.y);
var x = line.start.x * alpha + line.end.x * (1 - alpha); var x = line.end.x * alpha + line.start.x * (1 - alpha);
var z = line.start.z * alpha + line.end.z * (1 - alpha); var z = line.end.z * alpha + line.start.z * (1 - alpha);
intersections[index] = new THREE.Vector2(x, z); intersections[index] = new THREE.Vector2(x, z);
} }
@ -190,7 +198,8 @@ D3D.Slicer.prototype.slice = function (height, step) {
//stop when ther are no intersects //stop when ther are no intersects
if (slice.length > 0) { if (slice.length > 0) {
slices.push(slice); slices.push(new D3D.Path(slice, true));
//slices.push(slice);
} }
else { else {
break; break;
@ -199,20 +208,65 @@ D3D.Slicer.prototype.slice = function (height, step) {
return slices; return slices;
}; };
D3D.Slicer.prototype.getInset = function (slice, offset) { D3D.Slicer.prototype.slicesToData = function (slices, printer) {
"use strict"; "use strict";
var solution = new ClipperLib.Paths(); var scale = 100;
var co = new ClipperLib.ClipperOffset(1, 1);
co.AddPaths(slice, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);
co.Execute(solution, -offset);
return solution; var layerHeight = printer.config["printer.layerHeight"] * scale;
var dimensionsZ = printer.config["printer.dimensions.z"] * scale;
var wallThickness = printer.config["printer.wallThickness"] * scale;
var shellThickness = printer.config["printer.shellThickness"] * scale;
var fillSize = printer.config["printer.fillSize"] * scale;
var brimOffset = printer.config["printer.brimOffset"] * scale;
var skinCount = Math.ceil(shellThickness/layerHeight);
var data = [];
var lowFillTemplate = this.getFillTemplate(dimensionsZ, fillSize, true, true);
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
var outerLayer = slice.clone();
outerLayer.scaleUp(scale);
var insets = new D3D.Path();
for (var offset = wallThickness; offset <= shellThickness; offset += wallThickness) {
var inset = outerLayer.offset(-offset);
insets.join(inset);
}
var fillArea = (inset || outerLayer).offset(-wallThickness/2);
var downFill = (layer - skinCount >= 0) ? slices[layer - skinCount] : new D3D.Path();
var upFill = (layer + skinCount < slices.length) ? slices[layer + skinCount] : new D3D.Path();
var highFillArea = fillArea.difference(downFill.intersect(upFill).scaleUp(scale));
var lowFillArea = fillArea.difference(highFillArea);
var fill = new D3D.Path([], false);
fill.join(lowFillTemplate.intersect(lowFillArea));
if (highFillArea.path.length > 0) {
var highFillTemplate = this.getFillTemplate(dimensionsZ, wallThickness, (layer % 2 === 0), (layer % 2 === 1));
var highFillStrokes = highFillArea;
fill.join(highFillTemplate.intersect(highFillStrokes));
}
data.push({
outerLayer: outerLayer.scaleDown(scale),
insets: insets.scaleDown(scale),
fill: fill.scaleDown(scale)
});
}
return data;
}; };
D3D.Slicer.prototype.getFillTemplate = function (dimension, size, even, uneven) { D3D.Slicer.prototype.getFillTemplate = function (dimension, size, even, uneven) {
"use strict"; "use strict";
var paths = new ClipperLib.Paths(); var paths = [];
if (even) { if (even) {
for (var length = 0; length <= dimension; length += size) { for (var length = 0; length <= dimension; length += size) {
@ -225,125 +279,8 @@ D3D.Slicer.prototype.getFillTemplate = function (dimension, size, even, uneven)
} }
} }
return paths; //return paths;
}; return new D3D.Path(paths, false);
D3D.Slicer.prototype.slicesToData = function (slices, printer) {
"use strict";
//scale because of clipper crap
var scale = 100;
var layerHeight = printer.config["printer.layerHeight"] * scale;
var dimensionsZ = printer.config["printer.dimensions.z"] * scale;
var wallThickness = printer.config["printer.wallThickness"] * scale;
var shellThickness = printer.config["printer.shellThickness"] * scale;
var fillSize = printer.config["printer.fillSize"] * scale;
var brimOffset = printer.config["printer.brimOffset"] * scale;
var data = [];
var lowFillTemplate = this.getFillTemplate(dimensionsZ, fillSize, true, true);
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
//var outerLayer = ClipperLib.JS.Clean(slice, 1.0);
var outerLayer = slice.clone();
ClipperLib.JS.ScaleUpPaths(outerLayer, scale);
var innerLayer = [];
for (var i = wallThickness; i < shellThickness; i += wallThickness) {
var inset = this.getInset(outerLayer, i);
innerLayer = innerLayer.concat(inset);
}
//moet fillArea wel kleiner?
//var fillArea = this.getInset((inset || outerLayer), wallThickness);
var fillArea = (inset || outerLayer);
var fillAbove = false;
//for (var i = 1; i < shellThickness/layerHeight; i ++) {
for (var i = 1; i < shellThickness/layerHeight; i ++) {
var newLayer = ClipperLib.JS.Clone(slices[layer + i]);
ClipperLib.JS.ScaleUpPaths(newLayer, scale);
if (newLayer.length === 0 || (fillAbove && fillAbove.length === 0)) {
fillAbove = [];
break;
}
else if (fillAbove === false) {
fillAbove = newLayer;
}
else {
var c = new ClipperLib.Clipper();
var solution = new ClipperLib.Paths();
c.AddPaths(newLayer, ClipperLib.PolyType.ptSubject, true);
c.AddPaths(fillAbove, ClipperLib.PolyType.ptClip, true);
c.Execute(ClipperLib.ClipType.ctIntersection, solution);
fillAbove = solution;
}
}
//kijkt alleen nog naar boven
//omliggende lagen hebben inhoud van lowFill;
//inset moet opgevult worden;
//verschill tussen lowFill en inset moet vol, rest is raster
var clipper = new ClipperLib.Clipper();
var highFillArea = new ClipperLib.Paths();
clipper.AddPaths(fillArea, ClipperLib.PolyType.ptSubject, true);
clipper.AddPaths(fillAbove, ClipperLib.PolyType.ptClip, true);
clipper.Execute(ClipperLib.ClipType.ctDifference, highFillArea);
var clipper = new ClipperLib.Clipper();
var lowFillArea = new ClipperLib.Paths();
clipper.AddPaths(fillArea, ClipperLib.PolyType.ptSubject, true);
clipper.AddPaths(highFillArea, ClipperLib.PolyType.ptClip, true);
clipper.Execute(ClipperLib.ClipType.ctDifference, lowFillArea);
var fill = [];
var clipper = new ClipperLib.Clipper();
var lowFillStrokes = new ClipperLib.Paths();
clipper.AddPaths(lowFillTemplate, ClipperLib.PolyType.ptSubject, false);
clipper.AddPaths(lowFillArea, ClipperLib.PolyType.ptClip, true);
clipper.Execute(ClipperLib.ClipType.ctIntersection, lowFillStrokes);
fill = fill.concat(lowFillStrokes);
//optimize
//make as big as bounding box of highFillArea
var highFillTemplate = this.getFillTemplate(dimensionsZ, wallThickness, (layer % 2 === 0), (layer % 2 === 1));
var clipper = new ClipperLib.Clipper();
var highFillStrokes = new ClipperLib.Paths();
clipper.AddPaths(highFillTemplate, ClipperLib.PolyType.ptSubject, false);
clipper.AddPaths(highFillArea, ClipperLib.PolyType.ptClip, true);
clipper.Execute(ClipperLib.ClipType.ctIntersection, highFillStrokes);
fill = fill.concat(highFillStrokes);
//create brim
/*if (layer === 0) {
var brim = this.getInset(outerLayer, -brimOffset);
outerLayer = brim.concat(outerLayer);
}*/
ClipperLib.JS.ScaleDownPaths(outerLayer, scale);
ClipperLib.JS.ScaleDownPaths(innerLayer, scale);
ClipperLib.JS.ScaleDownPaths(fill, scale);
data.push({
outerLayer: outerLayer,
innerLayer: innerLayer,
fill: fill
});
}
return data;
}; };
D3D.Slicer.prototype.dataToGcode = function (data, printer) { D3D.Slicer.prototype.dataToGcode = function (data, printer) {
"use strict"; "use strict";
@ -365,8 +302,8 @@ D3D.Slicer.prototype.dataToGcode = function (data, printer) {
function sliceToGcode (slice) { function sliceToGcode (slice) {
var gcode = []; var gcode = [];
for (var i = 0; i < slice.length; i ++) { for (var i = 0; i < slice.path.length; i ++) {
var shape = slice[i]; var shape = slice.path[i];
var previousPoint; var previousPoint;
@ -440,54 +377,20 @@ D3D.Slicer.prototype.dataToGcode = function (data, printer) {
var z = ((layer + 1) * layerHeight).toFixed(3); var z = ((layer + 1) * layerHeight).toFixed(3);
gcode = gcode.concat(sliceToGcode(slice.outerLayer)); gcode = gcode.concat(sliceToGcode(slice.outerLayer));
gcode = gcode.concat(sliceToGcode(slice.innerLayer)); gcode = gcode.concat(sliceToGcode(slice.insets));
gcode = gcode.concat(sliceToGcode(slice.fill)); gcode = gcode.concat(sliceToGcode(slice.fill));
} }
gcode = gcode.concat(printer.getEndCode()); gcode = gcode.concat(printer.getEndCode());
return gcode; return gcode;
}; };
//only for debug purposes
D3D.Slicer.prototype.drawPaths = function (printer, min, max) { D3D.Slicer.prototype.drawPaths = function (printer, min, max) {
"use strict"; "use strict";
var layerHeight = printer.config["printer.layerHeight"]; var layerHeight = printer.config["printer.layerHeight"];
var dimensionsZ = printer.config["printer.dimensions.z"]; var dimensionsZ = printer.config["printer.dimensions.z"];
function drawLines (paths, color) {
context.fillStyle = color;
context.strokeStyle = color;
context.beginPath();
for (var i = 0; i < paths.length; i ++) {
var path = paths[i];
context.moveTo((path[0].X * 2), (path[0].Y * 2));
for (var j = 0; j < path.length; j ++) {
var point = path[j];
context.lineTo((point.X * 2), (point.Y * 2));
}
//context.closePath();
}
context.stroke();
}
function drawVertexes (paths, color) {
context.fillStyle = color;
context.strokeStyle = color;
for (var i = 0; i < paths.length; i ++) {
var path = paths[i];
for (var j = 0; j < path.length; j ++) {
var point = path[j];
context.beginPath();
context.arc(point.X * 2, point.Y * 2, 1, 0, Math.PI*2, false);
context.stroke();
}
}
}
var slices = this.slice(dimensionsZ, layerHeight); var slices = this.slice(dimensionsZ, layerHeight);
var data = this.slicesToData(slices, printer); var data = this.slicesToData(slices, printer);
@ -500,11 +403,9 @@ D3D.Slicer.prototype.drawPaths = function (printer, min, max) {
for (var layer = min; layer < max; layer ++) { for (var layer = min; layer < max; layer ++) {
var slice = data[layer % data.length]; var slice = data[layer % data.length];
drawLines(slice.outerLayer, "red"); slice.insets.draw(context, "blue");
drawLines(slice.innerLayer, "green"); slice.outerLayer.draw(context, "green");
drawLines(slice.fill, "blue"); slice.fill.draw(context, "red");
drawVertexes(slice.outerLayer, "green");
} }
return canvas; return canvas;

View File

@ -89,58 +89,3 @@ Array.prototype.clone = function () {
return array; return array;
}; };
function applyMouseControls (renderer, camera, center, maxDistance) {
"use strict";
//TODO
//impliment touch controls
//windows mouse wheel fix
var distance = 20;
var rotX = 0;
var rotY = 0;
var moveCamera = false;
function updateCamera () {
camera.position.set(
Math.cos(rotY)*Math.sin(rotX)*distance,
Math.sin(rotY)*distance,
Math.cos(rotY)*Math.cos(rotX)*distance
).add(center);
camera.lookAt(center);
}
$(renderer.domElement).on("mousedown", function (e) {
moveCamera = true;
}).on("wheel", function (e) {
var event = e.originalEvent;
event.preventDefault();
distance = THREE.Math.clamp(distance - event.wheelDelta, 1, maxDistance);
updateCamera();
});
$(window).on("mouseup", function (e) {
moveCamera = false;
}).on("mousemove", function (e) {
var event = e.originalEvent;
if (moveCamera === true) {
rotX = (rotX - event.webkitMovementX/100) % (2*Math.PI);
rotY = THREE.Math.clamp(rotY + event.webkitMovementY/100, -Math.PI/2, Math.PI/2);
updateCamera();
}
});
updateCamera();
}
var requestAnimFrame = (function () {
"use strict";
return requestAnimationFrame || webkitRequestAnimationFrame || mozRequestAnimationFrame || function (callback) {
setTimeout(callback, 1000/60);
};
})();

Binary file not shown.