Added slice abstraction to Slice class

This commit is contained in:
casperlamboo 2015-06-11 14:34:30 +02:00 committed by Simon Voordouw
parent ccd650c533
commit 75d4d4758a
11 changed files with 221 additions and 262 deletions

View File

@ -11,7 +11,8 @@
"printer.retraction.speed": 50,
"printer.retraction.minDistance": 5.0,
"printer.shellThickness": 0.4,
"printer.speed": 50.0,
"printer.speed": 50.0,
"printer.brimOffset": 5.0,
"printer.temperature": 210.0,
"printer.topThickness": 0.8,
"printer.travelSpeed": 200.0,

View File

@ -14,6 +14,7 @@
<script src="src/paths.js"></script>
<script src="src/gcode.js"></script>
<script src="src/slicer.js"></script>
<script src="src/slice.js"></script>
<style>
canvas {border: 1px solid black;}
@ -36,7 +37,7 @@ function init () {
var printer = new D3D.Printer().updateConfig(USER_SETTINGS).updateConfig(PRINTER_SETTINGS["ultimaker"]);
var loader = new THREE.STLLoader();
loader.load('models/support_test.stl', function (geometry) {
loader.load('models/pokemon/pikachu.stl', function (geometry) {
//var geometry = new THREE.BoxGeometry(10, 10, 10, 1, 1, 1);
//var geometry = new THREE.SphereGeometry(10, 20, 10);
//var geometry = new THREE.TorusGeometry(20, 10, 30, 30);
@ -159,6 +160,8 @@ function createScene () {
//loadSettings;
(function () {
'use strict';
var loadedItems = 0;
function loaded () {
loadedItems ++;
@ -167,13 +170,22 @@ function createScene () {
}
}
loadSettings("settings/user_settings.json", function (data) {
USER_SETTINGS = data;
loaded();
$.ajax({
url: 'settings/user_settings.json',
dataType: 'json',
success: function (response) {
USER_SETTINGS = response;
loaded();
}
});
loadSettings("settings/printer_settings.json", function (data) {
PRINTER_SETTINGS = data;
loaded();
$.ajax({
url: 'settings/printer_settings.json',
dataType: 'json',
success: function (response) {
PRINTER_SETTINGS = response;
loaded();
}
});
})();
</script>

View File

@ -133,10 +133,9 @@ D3D.Box.prototype.print = function (gcode) {
this.currentBatch = 0;
//clone gcode to remove array links
gcode = gcode.clone();
gcode = gcode.split("\n");
//gcode split in batches
//split gcode in batches
while (gcode.length > 0) {
var gcodeBatch = gcode.splice(0, Math.min(this.batchSize, gcode.length));
this.printBatches.push(gcodeBatch);
@ -158,6 +157,7 @@ D3D.Box.prototype.printBatch = function () {
}, function (error, data) {
if (error) {
scope.printBatches.unshift(gcode);
console.warn(error);
scope.init();

70
src/slice.js Normal file
View File

@ -0,0 +1,70 @@
/******************************************************
*
* Slice
*
******************************************************/
D3D.Slice = function () {
'use strict';
this.parts = [];
};
D3D.Slice.prototype.optimizePaths = function (start) {
'use strict';
if (this.brim !== undefined && this.brim.length > 0) {
this.brim = this.brim.optimizePath(start);
start = this.brim.lastPoint();
}
//instead of for loop pick the closest shape to start;
for (var i = 0; i < this.parts.length; i ++) {
var part = this.parts[i];
if (part.outerLine.length > 0) {
part.outerLine = part.outerLine.optimizePath(start);
start = part.outerLine.lastPoint();
}
for (var j = 0; j < part.innerLines.length; j ++) {
var innerLine = part.innerLines[j];
if (innerLine.length > 0) {
part.innerLines[j] = innerLine.optimizePath(start);
//start = part.innerLines[j].lastPoint();
}
}
if (part.fill.length > 0) {
part.fill = part.fill.optimizePath(start);
start = part.fill.lastPoint();
}
}
if (this.support !== undefined && this.support.length > 0) {
this.support = this.support.optimizePath(start);
//start = this.support.lastPoint();
}
return start;
};
D3D.Slice.prototype.getOutline = function () {
'use strict';
var outLines = new D3D.Paths([], true);
for (var i = 0; i < this.parts.length; i ++) {
outLines.join(this.parts[i].outerLine);
}
return outLines;
};
D3D.Slice.prototype.addIntersect = function (intersect) {
'use strict';
this.parts.push({
intersect: intersect,
innerLines: [],
outerLine: new D3D.Paths([], true),
fill: new D3D.Paths([], false)
});
};

View File

@ -120,6 +120,7 @@ D3D.Slicer.prototype.createLines = function () {
};
D3D.Slicer.prototype.slice = function (layerHeight, height) {
"use strict";
var numLayers = height / layerHeight;
var layersIntersections = [];
@ -169,7 +170,7 @@ D3D.Slicer.prototype.slice = function (layerHeight, height) {
}
var done = [];
var slice = [];
var sliceParts = [];
for (var i = 0; i < layerIntersections.length; i ++) {
var index = layerIntersections[i];
@ -242,39 +243,32 @@ D3D.Slicer.prototype.slice = function (layerHeight, height) {
//think this check is not nescesary, always higher as 0
if (shape.length > 0) {
slice.push(new D3D.Paths([shape], true));
sliceParts.push(new D3D.Paths([shape], true));
}
}
}
var slice = new D3D.Slice();
var layerParts = [];
for (var i = 0; i < slice.length; i ++) {
var layerPart1 = slice[i];
for (var i = 0; i < sliceParts.length; i ++) {
var slicePart1 = sliceParts[i];
var merge = false;
for (var j = 0; j < layerParts.length; j ++) {
var layerPart2 = layerParts[j];
for (var j = 0; j < slice.parts.length; j ++) {
var slicePart2 = slice.parts[j].intersect;
if (layerPart2.intersect(layerPart1).length > 0) {
layerPart2.join(layerPart1);
if (slicePart2.intersect(slicePart1).length > 0) {
slicePart2.join(slicePart1);
merge = true;
break;
}
}
if (!merge) {
layerParts.push(layerPart1);
slice.addIntersect(slicePart1);
}
}
//stop when ther are no intersects
if (layerParts.length > 0) {
slices.push(layerParts);
}
else {
break;
}
slices.push(slice);
this.progress.sliceLayer = layer;
this.updateProgress();
@ -299,6 +293,7 @@ D3D.Slicer.prototype.slicesToData = function (slices, printer) {
var supportMargin = printer.config["printer.support.margin"] * scale;
var plateSize = printer.config["printer.support.plateSize"] * scale;
var supportDistanceY = printer.config["printer.support.distanceY"];
var brimOffset = printer.config["printer.brimOffset"] * scale;
var supportDistanceLayers = Math.ceil(supportDistanceY / layerHeight);
var bottomSkinCount = Math.ceil(bottomThickness/layerHeight);
@ -312,81 +307,63 @@ D3D.Slicer.prototype.slicesToData = function (slices, printer) {
bottom: this.geometry.boundingBox.max.x * scale
}, fillSize, true, true);
var data = [];
//generate outerLayer and insets
console.log("generating outer lines and inner lines");
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
var layerData = [];
data.push(layerData);
for (var i = 0; i < slice.parts.length; i ++) {
var part = slice.parts[i];
for (var i = 0; i < slice.length; i ++) {
var part = slice[i];
var outerLine = part.intersect.clone().scaleUp(scale).offset(-nozzleRadius);
var outerLayer = part.clone().scaleUp(scale).offset(-nozzleRadius);
if (outerLine.length > 0) {
part.outerLine = outerLine;
var insets = new D3D.Paths([], true);
if (outerLayer.length > 0) {
for (var offset = nozzleDiameter; offset <= shellThickness; offset += nozzleDiameter) {
var inset = outerLayer.offset(-offset);
var innerLine = outerLine.offset(-offset);
insets.join(inset);
if (innerLine.length > 0) {
part.innerLines.push(innerLine);
}
else {
break;
}
}
layerData.push({
outerLayer: outerLayer,
insets: insets
});
}
}
}
//generate infills
for (var layer = 0; layer < data.length; layer ++) {
var slice = data[layer];
console.log("generating infills");
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
var downSkin = new D3D.Paths([], true);
if (layer - bottomSkinCount >= 0) {
var downLayer = data[layer - bottomSkinCount];
for (var i = 0; i < downLayer.length; i ++) {
downSkin.join(downLayer[i].outerLayer);
}
}
var upSkin = new D3D.Paths([], true);
if (layer + topSkinCount < data.length) {
var upLayer = data[layer + topSkinCount];
for (var i = 0; i < upLayer.length; i ++) {
upSkin.join(upLayer[i].outerLayer);
}
}
var surroundingLayer = upSkin.intersect(downSkin);
var sliceData = [];
var downSkin = (layer - bottomSkinCount >= 0) ? slices[layer - bottomSkinCount].getOutline() : new D3D.Paths([], true);
var upSkin = (layer + topSkinCount < slices.length) ? slices[layer + topSkinCount].getOutline() : new D3D.Paths([], true);
var surroundingLayer = (downSkin.length === 0 || upSkin.length === 0) ? new D3D.Paths([], true) : upSkin.intersect(downSkin);
for (var i = 0; i < slice.length; i ++) {
var part = slice[i];
var outerLayer = part.outerLayer;
var insets = part.insets;
for (var i = 0; i < slice.parts.length; i ++) {
var part = slice.parts[i];
var outerLine = part.outerLine;
if (outerLayer.length > 0) {
var fillArea = ((insets.length > 0) ? insets : outerLayer).offset(-nozzleRadius);
if (outerLine.length > 0) {
var inset = ((part.innerLines.length > 0) ? part.innerLines[part.innerLines.length - 1] : outerLine);
var fillArea = inset.offset(-nozzleRadius);
var highFillArea = fillArea.difference(surroundingLayer);
var lowFillArea = fillArea.difference(highFillArea);
var fill = new D3D.Paths([], false);
if (lowFillTemplate.length > 0) {
fill.join(lowFillTemplate.intersect(lowFillArea));
part.fill.join(lowFillTemplate.intersect(lowFillArea));
}
if (highFillArea.length > 0) {
var bounds = highFillArea.bounds();
var even = (layer % 2 === 0);
var highFillTemplate = this.getFillTemplate(bounds, nozzleDiameter, even, !even);
fill.join(highFillTemplate.intersect(highFillArea));
part.fill.join(highFillTemplate.intersect(highFillArea));
}
part.fill = fill;
}
}
@ -394,8 +371,9 @@ D3D.Slicer.prototype.slicesToData = function (slices, printer) {
this.updateProgress();
}
//generate support
if (useSupport) {
console.log("generating support");
var supportTemplate = this.getFillTemplate({
left: this.geometry.boundingBox.min.z * scale,
top: this.geometry.boundingBox.min.x * scale,
@ -405,90 +383,84 @@ D3D.Slicer.prototype.slicesToData = function (slices, printer) {
var supportAreas = new D3D.Paths([], true);
for (var layer = data.length - 1 - supportDistanceLayers; layer >= 0; layer --) {
for (var layer = slices.length - 1 - supportDistanceLayers; layer >= 0; layer --) {
if (supportAreas.length > 0) {
if (layer >= supportDistanceLayers) {
var sliceSkin = new D3D.Paths([], true);
var slice = data[layer - supportDistanceLayers];
for (var i = 0; i < slice.length; i ++) {
sliceSkin.join(slice[i].outerLayer);
}
var sliceSkin = slices[layer - supportDistanceLayers].getOutline();
sliceSkin = sliceSkin.offset(supportMargin);
supportAreas = supportAreas.difference(sliceSkin);
}
var currentSlice = data[layer];
var currentSlice = slices[layer];
if (layer === 0) {
supportAreas = supportAreas.offset(plateSize).difference(sliceSkin);
var template = this.getFillTemplate(supportAreas.bounds(), nozzleDiameter, true, false);
currentSlice[0].support = template.intersect(supportAreas);
currentSlice.support = template.intersect(supportAreas);
}
else {
currentSlice[0].support = supportTemplate.intersect(supportAreas).join(supportAreas.clone());
currentSlice.support = supportTemplate.intersect(supportAreas).join(supportAreas.clone());
}
}
var supportSlice = data[layer + supportDistanceLayers - 1];
var supportSkin = new D3D.Paths([], true);
for (var i = 0; i < supportSlice.length; i ++) {
//supportSkin = supportSkin.union(supportSlice[i].outerLayer);
supportSkin.join(supportSlice[i].outerLayer);
}
var supportSkin = slices[layer + supportDistanceLayers - 1].getOutline();
var slice = data[layer + supportDistanceLayers];
for (var i = 0; i < slice.length; i ++) {
var slicePart = slice[i];
var outerLayer = slicePart.outerLayer;
var slice = slices[layer + supportDistanceLayers];
for (var i = 0; i < slice.parts.length; i ++) {
var slicePart = slice.parts[i];
var outerLine = slicePart.outerLine;
var overlap = supportSkin.offset(supportAccaptanceMargin).intersect(outerLayer);
var overhang = outerLayer.difference(overlap);
var overlap = supportSkin.offset(supportAccaptanceMargin).intersect(outerLine);
var overhang = outerLine.difference(overlap);
if (overlap.length === 0 || overhang.length > 0) {
//var supportArea = outerLayer.difference(supportSkin.intersect(outerLayer));
//var supportArea = outerLine.difference(supportSkin.intersect(outerLine));
//supportAreas = supportAreas.union(supportArea);
//supportAreas = supportAreas.union(overhang);
supportAreas = supportAreas.union(overhang.offset(supportAccaptanceMargin).intersect(outerLayer));
supportAreas = supportAreas.union(overhang.offset(supportAccaptanceMargin).intersect(outerLine));
}
}
}
}
//finalize paths
console.log("opimize paths");
var start = new THREE.Vector2(0, 0);
var order = ["outerLayer", "insets", "fill", "support"];
for (var layer = 0; layer < data.length; layer ++) {
var slice = data[layer];
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
for (var i = 0; i < slice.length; i ++) {
var part = slice[i];
if (layer === 0) {
slice.brim = slice.getOutline().offset(brimOffset);
}
for (var j = 0; j < order.length; j ++) {
var property = order[j];
if (part[property] !== undefined && part[property].length > 0) {
part[property] = part[property].optimizePath(start);
start = part[property].lastPoint();
}
var start = slice.optimizePaths(start);
for (var i = 0; i < slice.parts.length; i ++) {
var part = slice.parts[i];
part.outerLine.scaleDown(scale);
for (var j = 0; j < part.innerLines.length; j ++) {
var innerLine = part.innerLines[j];
innerLine.scaleDown(scale);
}
part.fill.scaleDown(scale);
}
part.outerLayer.scaleDown(100);
part.insets.scaleDown(100);
part.fill.scaleDown(100);
if (part.support !== undefined) {
part.support.scaleDown(100);
}
if (slice.support !== undefined) {
slice.support.scaleDown(scale);
}
if (slice.brim !== undefined) {
slice.brim.scaleDown(scale);
}
}
return data;
return slices;
};
D3D.Slicer.prototype.getFillTemplate = function (bounds, size, even, uneven) {
@ -550,16 +522,23 @@ D3D.Slicer.prototype.dataToGCode = function (data, printer) {
gcode.bottom = false;
}
for (var i = 0; i < slice.length; i ++) {
var layerPart = slice[i];
if (slice.brim !== undefined) {
sliceToGCode(slice.brim, true, true);
}
sliceToGCode(layerPart.outerLayer, false, true);
sliceToGCode(layerPart.insets, false, false);
sliceToGCode(layerPart.fill, true, false);
for (var i = 0; i < slice.parts.length; i ++) {
var part = slice.parts[i];
if (layerPart.support !== undefined) {
sliceToGCode(layerPart.support, true, true);
sliceToGCode(part.outerLine, false, true);
for (var j = 0; j < part.innerLines.length; j ++) {
var innerLine = part.innerLines[j];
sliceToGCode(innerLine, false, false);
}
sliceToGCode(part.fill, true, false);
}
if (slice.support !== undefined) {
sliceToGCode(slice.support, true, true);
}
this.progress.gcodeLayer = layer;
@ -585,12 +564,12 @@ D3D.Slicer.prototype.getGCode = function (printer) {
console.log("Slicing: " + (end - start) + "ms");
start = new Date().getTime();
var data = this.slicesToData(slices, printer);
this.slicesToData(slices, printer);
end = new Date().getTime();
console.log("Data: " + (end - start) + "ms");
start = new Date().getTime();
var gcode = this.dataToGCode(data, printer);
var gcode = this.dataToGCode(slices, printer);
end = new Date().getTime();
console.log("Gcode: " + (end - start) + "ms");

View File

@ -19,7 +19,7 @@ D3D.SlicerWorker = function () {
if (scope.onfinish !== undefined) {
var reader = new FileReader();
reader.addEventListener("loadend", function() {
var gcode = reader.result.split("\n");
var gcode = reader.result;
scope.onfinish(gcode);
});
reader.readAsBinaryString(event.data['gcode']);

View File

@ -1,9 +0,0 @@
self.addEventListener("message", function (event) {
"use strict";
//console.log(event.data);
if (event.data === "close") {
self.close();
}
});

View File

@ -1,58 +1,57 @@
importScripts("../library/three.js");
importScripts("../library/clipper.js");
importScripts("../src/utils.js");
importScripts("../src/printer.js");
importScripts("../src/paths.js");
importScripts("../src/slicer.js");
importScripts("../src/gcode.js");
importScripts('../library/three.js');
importScripts('../library/clipper.js');
importScripts('../src/utils.js');
importScripts('../src/printer.js');
importScripts('../src/paths.js');
importScripts('../src/slicer.js');
importScripts('../src/gcode.js');
importScripts('../src/slice.js');
var printer = new D3D.Printer();
var slicer = new D3D.Slicer();
slicer.onProgress = function (progress) {
"use strict";
'use strict';
self.postMessage({
"cmd": "PROGRESS",
"progress": progress
'cmd': 'PROGRESS',
'progress': progress
});
};
self.addEventListener("message", function (event) {
"use strict";
self.addEventListener('message', function (event) {
'use strict';
switch (event.data["cmd"]) {
case "SET_MESH":
switch (event.data['cmd']) {
case 'SET_MESH':
//hack...
//because boundings loses functions when converting
event.data["geometry"].boundingBox = event.data["geometry"].boundingSphere = null;
//because boundings loses prototype functions when converting
event.data['geometry'].boundingBox = event.data['geometry'].boundingSphere = null;
var geometry = new THREE.Geometry().fromBufferGeometry(event.data["geometry"]);
var matrix = new THREE.Matrix4().fromArray(event.data["matrix"]);
var geometry = new THREE.Geometry().fromBufferGeometry(event.data['geometry']);
var matrix = new THREE.Matrix4().fromArray(event.data['matrix']);
slicer.setMesh(geometry, matrix);
break;
case "SET_SETTINGS":
printer.updateConfig(event.data["USER_SETTINGS"]);
printer.updateConfig(event.data["PRINTER_SETTINGS"]);
case 'SET_SETTINGS':
printer.updateConfig(event.data['USER_SETTINGS']);
printer.updateConfig(event.data['PRINTER_SETTINGS']);
break;
case "SLICE":
case 'SLICE':
var gcode = slicer.getGCode(printer);
var blob = new Blob([gcode], {type: 'text/plain'});
//need to send the buffer of blob sepperatly;
//not sure how to send that
self.postMessage({
"cmd": "GCODE",
"gcode": blob
'cmd': 'GCODE',
'gcode': blob
});
break;
case "CLOSE":
case 'CLOSE':
self.close();
break;
}
});

View File

@ -1,74 +0,0 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<script src="library/benchmark.js"></script>
<script src="library/three.js"></script>
<title>Doedel Drie Dee || Webworker Benchmark</title>
<style>
</style>
</head>
<body>
<script>
var worker = new Worker('webworker/benchmark.js');
var geometry = new THREE.BoxGeometry(10, 30, 10).clone();
//var geometry = new THREE.TorusKnotGeometry(10, 3, 200, 200).clone();
new Benchmark.Suite().add('Buffer Geometry', function () {
var bufferGeometry = new THREE.BufferGeometry().fromGeometry(geometry);
worker.postMessage(bufferGeometry);
//this happens in the worker
var workerGeometry = new THREE.Geometry().fromBufferGeometry(bufferGeometry);
workerGeometry.mergeVertices();
}).add('Buffer Geometry Transferrable Object', function () {
var bufferGeometry = new THREE.BufferGeometry().fromGeometry(geometry);
var buffers = [];
for (var i = 0; i < bufferGeometry.attributesKeys.length; i ++) {
var key = bufferGeometry.attributesKeys[i];
buffers.push(bufferGeometry.attributes[key].array.buffer);
}
worker.postMessage(bufferGeometry, buffers);
//this happens in the worker
//this is redundant...
//buffers are transferred to worker so the do not exist in this part of the script
//[TEST NOT VIABLE]
var workerGeometry = new THREE.Geometry().fromBufferGeometry(bufferGeometry);
workerGeometry.mergeVertices();
}).add('Geometry JSON', function () {
var json = geometry.toJSON().data;
worker.postMessage(json);
//worker.postMessage(geometry);
//this happens in the worker
var loader = new THREE.JSONLoader();
var workerGeometry = loader.parse(json).geometry;
workerGeometry.mergeVertices();
}).on('cycle', function (event) {
document.body.innerHTML += "<p>" + String(event.target) + "</p>";
}).on('complete', function () {
document.body.innerHTML += "<p>" + 'Fastest is "' + this.filter('fastest').pluck('name') + '"' + "</p>";
}).run({
'async': true
});
</script>
</body>
</html>

View File

@ -1,19 +0,0 @@
----------- RESULTATEN TORUS KNOT --------------
Buffer Geometry x 0.53 ops/sec ±134.70% (8 runs sampled)
Buffer Geometry Transferrable Object x 63.04 ops/sec ±2.19% (67 runs sampled)
Geometry JSON x 0.50 ops/sec ±6.55% (6 runs sampled)
Fastest is "Buffer Geometry Transferrable Object"
----------- RESULTATEN KUBUS --------------
Buffer Geometry x 5,360 ops/sec ±19.42% (85 runs sampled)
Buffer Geometry Transferrable Object x 1,535 ops/sec ±10.17% (35 runs sampled)
Geometry JSON x 3,831 ops/sec ±2.52% (82 runs sampled)
Fastest is "Buffer Geometry"

View File

@ -86,7 +86,7 @@ function init () {
downloadButton.style.display = 'initial';
downloadButton.onclick = function () {
downloadFile("gcode.gcode", gcode.join('\n'));
downloadFile("gcode.gcode", gcode);
};
};
@ -99,7 +99,7 @@ function init () {
var mesh = new THREE.Mesh(geometry, material);
mesh.rotation.x = -Math.PI/2;
mesh.scale.x = mesh.scale.y = mesh.scale.z = 1;
mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.5;
mesh.position.y = -0.1;
mesh.position.x = 60;
mesh.position.z = 60;