separate into actions into different files

This commit is contained in:
casperlamboo 2016-03-29 00:26:58 +02:00
parent 7ae57e0bda
commit 3db03cd873
11 changed files with 673 additions and 667 deletions

View File

@ -0,0 +1,50 @@
import THREE from 'three.js';
export default function calculateLayersIntersections(lines, settings) {
console.log('calculating layer intersections');
var layerHeight = settings.config["layerHeight"];
var height = settings.config["dimensionsZ"];
var numLayers = Math.floor(height / layerHeight);
var layerIntersectionIndexes = [];
var layerIntersectionPoints = [];
for (var layer = 0; layer < numLayers; layer ++) {
layerIntersectionIndexes[layer] = [];
layerIntersectionPoints[layer] = [];
}
for (var lineIndex = 0; lineIndex < lines.length; lineIndex ++) {
var line = lines[lineIndex].line;
var min = Math.ceil(Math.min(line.start.y, line.end.y) / layerHeight);
var max = Math.floor(Math.max(line.start.y, line.end.y) / layerHeight);
for (var layerIndex = min; layerIndex <= max; layerIndex ++) {
if (layerIndex >= 0 && layerIndex < numLayers) {
layerIntersectionIndexes[layerIndex].push(lineIndex);
var y = layerIndex * layerHeight;
if (line.start.y === line.end.y) {
var x = line.start.x;
var z = line.start.z;
}
else {
var alpha = (y - line.start.y) / (line.end.y - line.start.y);
var x = line.end.x * alpha + line.start.x * (1 - alpha);
var z = line.end.z * alpha + line.start.z * (1 - alpha);
}
layerIntersectionPoints[layerIndex][lineIndex] = new THREE.Vector2(z, x);
}
}
}
return {
layerIntersectionIndexes,
layerIntersectionPoints
};
}

View File

@ -0,0 +1,49 @@
import THREE from 'three.js';
export default function createLines(geometry, settings) {
console.log('constructing unique lines from geometry');
var lines = [];
var lineLookup = {};
var addLine = (a, b) => {
var index = lineLookup[b + '_' + a];
if (index === undefined) {
index = lines.length;
lineLookup[a + '_' + b] = index;
lines.push({
line: new THREE.Line3(geometry.vertices[a], geometry.vertices[b]),
connects: [],
normals: []
});
}
return index;
}
for (var i = 0; i < geometry.faces.length; i ++) {
var face = geometry.faces[i];
if (face.normal.y !== 1 && face.normal.y !== -1) {
var normal = new THREE.Vector2(face.normal.z, face.normal.x).normalize();
// check for only adding unique lines
// returns index of said line
var a = addLine(face.a, face.b);
var b = addLine(face.b, face.c);
var c = addLine(face.c, face.a);
// set connecting lines (based on face)
lines[a].connects.push(b, c);
lines[b].connects.push(c, a);
lines[c].connects.push(a, b);
lines[a].normals.push(normal);
lines[b].normals.push(normal);
lines[c].normals.push(normal);
}
}
return lines;
}

View File

@ -0,0 +1,82 @@
import getFillTemplate from './getFillTemplate.js';
import Paths from '../paths.js';
export default function generateInfills(slices, settings) {
console.log("generating infills");
// need to scale up everything because of clipper rounding errors
var scale = 100;
var layerHeight = settings.config["layerHeight"];
var fillGridSize = settings.config["fillGridSize"] * scale;
var bottomThickness = settings.config["bottomThickness"];
var topThickness = settings.config["topThickness"];
var nozzleDiameter = settings.config["nozzleDiameter"] * scale;
var infillOverlap = settings.config["infillOverlap"] * scale;
var bottomSkinCount = Math.ceil(bottomThickness/layerHeight);
var topSkinCount = Math.ceil(topThickness/layerHeight);
var nozzleRadius = nozzleDiameter / 2;
var hightemplateSize = Math.sqrt(2 * Math.pow(nozzleDiameter, 2));
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
if (layer - bottomSkinCount >= 0 && layer + topSkinCount < slices.length) {
var downSkin = slices[layer - bottomSkinCount].getOutline();
var upSkin = slices[layer + topSkinCount].getOutline();
var surroundingLayer = upSkin.intersect(downSkin);
}
else {
var surroundingLayer = false;
}
for (var i = 0; i < slice.parts.length; i ++) {
var part = slice.parts[i];
if (!part.intersect.closed) {
continue;
}
var outerLine = part.outerLine;
if (outerLine.length > 0) {
var inset = (part.innerLines.length > 0) ? part.innerLines[part.innerLines.length - 1] : outerLine;
var fillArea = inset.offset(-nozzleRadius);
var lowFillArea = false;
if (surroundingLayer) {
var highFillArea = fillArea.difference(surroundingLayer);
if (infillOverlap > 0) {
highFillArea = highFillArea.offset(infillOverlap);
}
highFillArea = highFillArea.intersect(fillArea);
var lowFillArea = fillArea.difference(highFillArea);
}
else {
var highFillArea = fillArea;
}
var fill = new Paths([], false);
if (lowFillArea && lowFillArea.length > 0) {
var bounds = lowFillArea.bounds();
var lowFillTemplate = getFillTemplate(bounds, fillGridSize, true, true);
part.fill.join(lowFillTemplate.intersect(lowFillArea));
}
if (highFillArea.length > 0) {
var bounds = highFillArea.bounds();
var even = (layer % 2 === 0);
var highFillTemplate = getFillTemplate(bounds, hightemplateSize, even, !even);
part.fill.join(highFillTemplate.intersect(highFillArea));
}
}
}
}
}

View File

@ -0,0 +1,44 @@
export default function generateInnerLines(slices, settings) {
console.log("generating outer lines and inner lines");
// need to scale up everything because of clipper rounding errors
var scale = 100;
var layerHeight = settings.config["layerHeight"];
var nozzleDiameter = settings.config["nozzleDiameter"] * scale;
var shellThickness = settings.config["shellThickness"] * scale;
var nozzleRadius = nozzleDiameter / 2;
var shells = Math.round(shellThickness / nozzleDiameter);
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
for (var i = 0; i < slice.parts.length; i ++) {
var part = slice.parts[i];
if (!part.intersect.closed) {
continue;
}
// var outerLine = part.intersect.clone().scaleUp(scale).offset(-nozzleRadius);
var outerLine = part.intersect.scaleUp(scale).offset(-nozzleRadius);
if (outerLine.length > 0) {
part.outerLine = outerLine;
for (var shell = 1; shell < shells; shell += 1) {
var offset = shell * nozzleDiameter;
var innerLine = outerLine.offset(-offset);
if (innerLine.length > 0) {
part.innerLines.push(innerLine);
}
else {
break;
}
}
}
}
}
}

View File

@ -0,0 +1,71 @@
import getFillTemplate from './getFillTemplate.js';
import Paths from '../paths.js';
export default function generateSupport(slices, settings) {
console.log("generating support");
// need to scale up everything because of clipper rounding errors
var scale = 100;
var layerHeight = settings.config["layerHeight"];
var supportGridSize = settings.config["supportGridSize"] * scale;
var supportAcceptanceMargin = settings.config["supportAcceptanceMargin"] * scale;
var supportMargin = settings.config["supportMargin"] * scale;
var plateSize = settings.config["supportPlateSize"] * scale;
var supportDistanceY = settings.config["supportDistanceY"];
var supportDistanceLayers = Math.max(Math.ceil(supportDistanceY / layerHeight), 1);
var nozzleDiameter = settings.config["nozzleDiameter"] * scale;
var supportAreas = new Paths([], true);
for (var layer = slices.length - 1 - supportDistanceLayers; layer >= 0; layer --) {
var currentSlice = slices[layer];
if (supportAreas.length > 0) {
if (layer >= supportDistanceLayers) {
var sliceSkin = slices[layer - supportDistanceLayers].getOutline();
sliceSkin = sliceSkin;
var supportAreasSlimmed = supportAreas.difference(sliceSkin.offset(supportMargin));
if (supportAreasSlimmed.area() < 100.0) {
supportAreas = supportAreas.difference(sliceSkin);
}
else {
supportAreas = supportAreasSlimmed;
}
}
var supportTemplate = getFillTemplate(supportAreas.bounds(), supportGridSize, true, true);
var supportFill = supportTemplate.intersect(supportAreas);
if (supportFill.length === 0) {
currentSlice.support = supportAreas.clone();
}
else {
currentSlice.support = supportFill;
}
}
var supportSkin = slices[layer + supportDistanceLayers - 1].getOutline();
var slice = slices[layer + supportDistanceLayers];
for (var i = 0; i < slice.parts.length; i ++) {
var slicePart = slice.parts[i];
if (slicePart.intersect.closed) {
var outerLine = slicePart.outerLine;
}
else {
var outerLine = slicePart.intersect.offset(supportAcceptanceMargin);
}
var overlap = supportSkin.offset(supportAcceptanceMargin).intersect(outerLine);
var overhang = outerLine.difference(overlap);
if (overlap.length === 0 || overhang.length > 0) {
supportAreas = supportAreas.join(overhang);
}
}
}
}

View File

@ -0,0 +1,31 @@
import Paths from '../paths.js';
export default function getFillTemplate(bounds, size, even, uneven) {
var paths = new Paths([], false);
var left = Math.floor(bounds.left / size) * size;
var right = Math.ceil(bounds.right / size) * size;
var top = Math.floor(bounds.top / size) * size;
var bottom = Math.ceil(bounds.bottom / size) * size;
var width = right - left;
if (even) {
for (var y = top; y <= bottom + width; y += size) {
paths.push([
{X: left, Y: y},
{X: right, Y: y - width}
]);
}
}
if (uneven) {
for (var y = top - width; y <= bottom; y += size) {
paths.push([
{X: left, Y: y},
{X: right, Y: y + width}
]);
}
}
return paths;
}

View File

@ -0,0 +1,127 @@
import THREE from 'three.js';
import Paths from '../paths.js';
export default function intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings) {
console.log("generating slices");
var shapes = [];
for (var layer = 1; layer < layerIntersectionIndexes.length; layer ++) {
var intersectionIndexes = layerIntersectionIndexes[layer];
var intersectionPoints = layerIntersectionPoints[layer];
if (intersectionIndexes.length === 0) {
continue;
}
var shapeParts = [];
for (var i = 0; i < intersectionIndexes.length; i ++) {
var index = intersectionIndexes[i];
if (intersectionPoints[index] === undefined) {
continue;
}
var firstPoints = [index];
var isFirstPoint = true;
var closed = false;
var shape = [];
while (index !== -1) {
var intersection = intersectionPoints[index];
// uppercase X and Y because clipper vector
shape.push({X: intersection.x, Y: intersection.y});
delete intersectionPoints[index];
var connects = lines[index].connects;
var faceNormals = lines[index].normals;
for (var j = 0; j < connects.length; j ++) {
var index = connects[j];
if (firstPoints.indexOf(index) !== -1 && shape.length > 2) {
closed = true;
index = -1;
break;
}
// Check if index has an intersection or is already used
if (intersectionPoints[index] !== undefined) {
var faceNormal = faceNormals[Math.floor(j / 2)];
var a = new THREE.Vector2(intersection.x, intersection.y);
var b = new THREE.Vector2(intersectionPoints[index].x, intersectionPoints[index].y);
// can't calculate normal between points if distance is smaller as 0.0001
if ((faceNormal.x === 0 && faceNormal.y === 0) || a.distanceTo(b) < 0.0001) {
if (isFirstPoint) {
firstPoints.push(index);
}
delete intersectionPoints[index];
connects = connects.concat(lines[index].connects);
faceNormals = faceNormals.concat(lines[index].normals);
index = -1;
}
else {
// make sure the path goes the right direction
// THREE.Vector2.normal is not yet implimented
// var normal = a.sub(b).normal().normalize();
var normal = a.sub(b);
normal.set(-normal.y, normal.x).normalize();
if (normal.dot(faceNormal) > 0) {
break;
}
else {
index = -1;
}
}
}
else {
index = -1;
}
}
isFirstPoint = false;
}
if (!closed) {
var index = firstPoints[0];
while (index !== -1) {
if (firstPoints.indexOf(index) === -1) {
var intersection = intersectionPoints[index];
shape.unshift({X: intersection.x, Y: intersection.y});
delete intersectionPoints[index];
}
var connects = lines[index].connects;
for (var i = 0; i < connects.length; i ++) {
var index = connects[i];
if (intersectionPoints[index] !== undefined) {
break;
}
else {
index = -1;
}
}
}
}
var part = new Paths([shape], closed).clean(0.01);
if (part.length > 0) {
shapeParts.push(part);
}
}
shapes.push(shapeParts);
}
return shapes;
}

View File

@ -0,0 +1,42 @@
import THREE from 'three.js';
export default function optimizePaths(slices, settings) {
console.log("opimize paths");
// need to scale up everything because of clipper rounding errors
var scale = 100;
var brimOffset = settings.config["brimOffset"] * scale;
var start = new THREE.Vector2(0, 0);
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
if (layer === 0) {
slice.brim = slice.getOutline().offset(brimOffset);
}
start = slice.optimizePaths(start);
for (var i = 0; i < slice.parts.length; i ++) {
var part = slice.parts[i];
if (part.intersect.closed) {
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);
}
}
if (slice.support !== undefined) {
slice.support.scaleDown(scale);
}
if (slice.brim !== undefined) {
slice.brim.scaleDown(scale);
}
}
}

View File

@ -0,0 +1,65 @@
import Slice from '../slice.js';
export default function shapesToSlices(shapes, settings) {
var slices = [];
for (var layer = 0; layer < shapes.length; layer ++) {
var shapeParts = shapes[layer];
var slice = new Slice();
var holes = [];
var outlines = [];
for (var i = 0; i < shapeParts.length; i ++) {
var shape = shapeParts[i];
if (!shape.closed) {
slice.add(shape);
}
else if (shape.isHole()) {
holes.push(shape);
}
else {
slice.add(shape);
outlines.push(shape);
}
}
outlines.sort((a, b) => {
return a.boundSize() - b.boundSize();
});
if (holes.length > outlines.length) {
[holes, outlines] = [outlines, holes];
}
else if (holes.length === outlines.length) {
holes.sort((a, b) => {
return a.boundSize() - b.boundSize();
});
if (holes[0].boundSize > outlines[0].boundSize()) {
[holes, outlines] = [outlines, holes];
}
}
for (var i = 0; i < holes.length; i ++) {
var hole = holes[i];
for (var j = 0; j < outlines.length; j ++) {
var outline = outlines[j];
if (outline.pointCollision(hole[0][0])) {
outline.join(hole);
break;
}
}
}
slice.removeSelfIntersect();
slices.push(slice);
}
return slices;
}

View File

@ -0,0 +1,73 @@
import GCode from '../gcode.js';
export default function slicesToGCode(slices, settings) {
var gcode = new GCode().setSettings(settings);
function pathToGCode (path, retract, unRetract, type) {
for (var i = 0; i < path.length; i ++) {
var shape = path[i];
var length = path.closed ? (shape.length + 1) : shape.length;
for (var j = 0; j < length; j ++) {
var point = shape[j % shape.length];
if (j === 0) {
// TODO
// moveTo should impliment combing
gcode.moveTo(point.X, point.Y, layer);
if (unRetract) {
gcode.unRetract();
}
}
else {
gcode.lineTo(point.X, point.Y, layer, type);
}
}
}
if (retract) {
gcode.retract();
}
}
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
if (layer === 1) {
gcode.turnFanOn();
gcode.bottom = false;
}
if (slice.brim !== undefined) {
pathToGCode(slice.brim, true, true, "brim");
}
for (var i = 0; i < slice.parts.length; i ++) {
var part = slice.parts[i];
if (part.intersect.closed) {
pathToGCode(part.outerLine, false, true, "outerLine");
for (var j = 0; j < part.innerLines.length; j ++) {
var innerLine = part.innerLines[j];
pathToGCode(innerLine, false, false, "innerLine");
}
pathToGCode(part.fill, true, false, "fill");
}
else {
var retract = !(slice.parts.length === 1 && slice.support === undefined);
pathToGCode(part.intersect, retract, retract, "outerLine");
}
}
if (slice.support !== undefined) {
pathToGCode(slice.support, true, true, "support");
}
}
return gcode.getGCode();
}

View File

@ -1,7 +1,13 @@
import THREE from 'three.js';
import Paths from './paths.js';
import Slice from './slice.js';
import GCode from './gcode.js';
import calculateLayersIntersections from './sliceActions/calculateLayersIntersections.js';
import createLines from './sliceActions/createLines.js';
import generateInfills from './sliceActions/generateInfills.js';
import generateInnerLines from './sliceActions/generateInnerLines.js';
import generateSupport from './sliceActions/generateSupport.js';
import intersectionsToShapes from './sliceActions/intersectionsToShapes.js';
import optimizePaths from './sliceActions/optimizePaths.js';
import shapesToSlices from './sliceActions/shapesToSlices.js';
import slicesToGCode from './sliceActions/slicesToGCode.js';
export default class {
constructor () {
@ -54,25 +60,43 @@ export default class {
var supportEnabled = settings.config['supportEnabled'];
// get unique lines from geometry;
var lines = this._createLines(settings);
var lines = createLines(this.geometry, settings);
this.progress.createdLines = true;
this._updateProgress(settings);
var {layerIntersectionIndexes, layerIntersectionPoints} = this._calculateLayersIntersections(lines, settings);
var {layerIntersectionIndexes, layerIntersectionPoints} = calculateLayersIntersections(lines, settings);
this.progress.calculatedLayerIntersections = true;
this._updateProgress(settings);
var shapes = this._intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings);
var shapes = intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings);
this.progress.sliced = true;
this._updateProgress(settings);
var slices = this._shapesToSlices(shapes, settings);
var slices = shapesToSlices(shapes, settings);
this.progress.generatedSlices = true;
this._updateProgress(settings);
generateInnerLines(slices, settings);
this.progress.generatedInnerLines = true;
this._updateProgress(settings);
generateInfills(slices, settings);
this.progress.generatedInfills = true;
this._updateProgress(settings);
this._generateInnerLines(slices, settings);
this._generateInfills(slices, settings);
if (supportEnabled) {
this._generateSupport(slices, settings);
generateSupport(slices, settings);
this.progress.generatedSupport = true;
this._updateProgress(settings);
}
this._optimizePaths(slices, settings);
var gcode = this._slicesToGCode(slices, settings);
optimizePaths(slices, settings);
this.progress.optimizedPaths = true;
this._updateProgress(settings);
var gcode = slicesToGCode(slices, settings);
this.progress.generatedGCode = true;
this._updateProgress(settings);
if (this.onfinish !== undefined) {
this.onfinish(gcode);
@ -81,658 +105,6 @@ export default class {
return gcode;
}
_createLines (settings) {
console.log('constructing unique lines from geometry');
var lines = [];
var lineLookup = {};
var addLine = (a, b) => {
var index = lineLookup[b + '_' + a];
if (index === undefined) {
index = lines.length;
lineLookup[a + '_' + b] = index;
lines.push({
line: new THREE.Line3(this.geometry.vertices[a], this.geometry.vertices[b]),
connects: [],
normals: []
});
}
return index;
}
for (var i = 0; i < this.geometry.faces.length; i ++) {
var face = this.geometry.faces[i];
if (face.normal.y !== 1 && face.normal.y !== -1) {
var normal = new THREE.Vector2(face.normal.z, face.normal.x).normalize();
// check for only adding unique lines
// returns index of said line
var a = addLine(face.a, face.b);
var b = addLine(face.b, face.c);
var c = addLine(face.c, face.a);
// set connecting lines (based on face)
lines[a].connects.push(b, c);
lines[b].connects.push(c, a);
lines[c].connects.push(a, b);
lines[a].normals.push(normal);
lines[b].normals.push(normal);
lines[c].normals.push(normal);
}
}
this.progress.createdLines = true;
this._updateProgress(settings);
return lines;
}
_calculateLayersIntersections (lines, settings) {
console.log('calculating layer intersections');
var layerHeight = settings.config["layerHeight"];
var height = settings.config["dimensionsZ"];
var numLayers = Math.floor(height / layerHeight);
var layerIntersectionIndexes = [];
var layerIntersectionPoints = [];
for (var layer = 0; layer < numLayers; layer ++) {
layerIntersectionIndexes[layer] = [];
layerIntersectionPoints[layer] = [];
}
for (var lineIndex = 0; lineIndex < lines.length; lineIndex ++) {
var line = lines[lineIndex].line;
var min = Math.ceil(Math.min(line.start.y, line.end.y) / layerHeight);
var max = Math.floor(Math.max(line.start.y, line.end.y) / layerHeight);
for (var layerIndex = min; layerIndex <= max; layerIndex ++) {
if (layerIndex >= 0 && layerIndex < numLayers) {
layerIntersectionIndexes[layerIndex].push(lineIndex);
var y = layerIndex * layerHeight;
if (line.start.y === line.end.y) {
var x = line.start.x;
var z = line.start.z;
}
else {
var alpha = (y - line.start.y) / (line.end.y - line.start.y);
var x = line.end.x * alpha + line.start.x * (1 - alpha);
var z = line.end.z * alpha + line.start.z * (1 - alpha);
}
layerIntersectionPoints[layerIndex][lineIndex] = new THREE.Vector2(z, x);
}
}
}
this.progress.calculatedLayerIntersections = true;
this._updateProgress(settings);
return {
layerIntersectionIndexes,
layerIntersectionPoints
};
}
_intersectionsToShapes (layerIntersectionIndexes, layerIntersectionPoints, lines, settings) {
console.log("generating slices");
var shapes = [];
for (var layer = 1; layer < layerIntersectionIndexes.length; layer ++) {
var intersectionIndexes = layerIntersectionIndexes[layer];
var intersectionPoints = layerIntersectionPoints[layer];
if (intersectionIndexes.length === 0) {
continue;
}
var shapeParts = [];
for (var i = 0; i < intersectionIndexes.length; i ++) {
var index = intersectionIndexes[i];
if (intersectionPoints[index] === undefined) {
continue;
}
var firstPoints = [index];
var isFirstPoint = true;
var closed = false;
var shape = [];
while (index !== -1) {
var intersection = intersectionPoints[index];
// uppercase X and Y because clipper vector
shape.push({X: intersection.x, Y: intersection.y});
delete intersectionPoints[index];
var connects = lines[index].connects;
var faceNormals = lines[index].normals;
for (var j = 0; j < connects.length; j ++) {
var index = connects[j];
if (firstPoints.indexOf(index) !== -1 && shape.length > 2) {
closed = true;
index = -1;
break;
}
// Check if index has an intersection or is already used
if (intersectionPoints[index] !== undefined) {
var faceNormal = faceNormals[Math.floor(j / 2)];
var a = new THREE.Vector2(intersection.x, intersection.y);
var b = new THREE.Vector2(intersectionPoints[index].x, intersectionPoints[index].y);
// can't calculate normal between points if distance is smaller as 0.0001
if ((faceNormal.x === 0 && faceNormal.y === 0) || a.distanceTo(b) < 0.0001) {
if (isFirstPoint) {
firstPoints.push(index);
}
delete intersectionPoints[index];
connects = connects.concat(lines[index].connects);
faceNormals = faceNormals.concat(lines[index].normals);
index = -1;
}
else {
// make sure the path goes the right direction
// THREE.Vector2.normal is not yet implimented
// var normal = a.sub(b).normal().normalize();
var normal = a.sub(b);
normal.set(-normal.y, normal.x).normalize();
if (normal.dot(faceNormal) > 0) {
break;
}
else {
index = -1;
}
}
}
else {
index = -1;
}
}
isFirstPoint = false;
}
if (!closed) {
var index = firstPoints[0];
while (index !== -1) {
if (firstPoints.indexOf(index) === -1) {
var intersection = intersectionPoints[index];
shape.unshift({X: intersection.x, Y: intersection.y});
delete intersectionPoints[index];
}
var connects = lines[index].connects;
for (var i = 0; i < connects.length; i ++) {
var index = connects[i];
if (intersectionPoints[index] !== undefined) {
break;
}
else {
index = -1;
}
}
}
}
var part = new Paths([shape], closed).clean(0.01);
if (part.length > 0) {
shapeParts.push(part);
}
}
shapes.push(shapeParts);
}
this.progress.sliced = true;
this._updateProgress(settings);
return shapes;
}
_shapesToSlices (shapes, settings) {
var slices = [];
for (var layer = 0; layer < shapes.length; layer ++) {
var shapeParts = shapes[layer];
var slice = new Slice();
var holes = [];
var outlines = [];
for (var i = 0; i < shapeParts.length; i ++) {
var shape = shapeParts[i];
if (!shape.closed) {
slice.add(shape);
}
else if (shape.isHole()) {
holes.push(shape);
}
else {
slice.add(shape);
outlines.push(shape);
}
}
outlines.sort((a, b) => {
return a.boundSize() - b.boundSize();
});
console.log('test');
if (holes.length > outlines.length) {
[holes, outlines] = [outlines, holes];
}
else if (holes.length === outlines.length) {
holes.sort((a, b) => {
return a.boundSize() - b.boundSize();
});
if (holes[0].boundSize > outlines[0].boundSize()) {
[holes, outlines] = [outlines, holes];
}
}
for (var i = 0; i < holes.length; i ++) {
var hole = holes[i];
for (var j = 0; j < outlines.length; j ++) {
var outline = outlines[j];
if (outline.pointCollision(hole[0][0])) {
outline.join(hole);
break;
}
}
}
slice.removeSelfIntersect();
slices.push(slice);
}
this.progress.generatedSlices = true;
this._updateProgress(settings);
return slices;
}
_generateInnerLines (slices, settings) {
console.log("generating outer lines and inner lines");
// need to scale up everything because of clipper rounding errors
var scale = 100;
var layerHeight = settings.config["layerHeight"];
var nozzleDiameter = settings.config["nozzleDiameter"] * scale;
var shellThickness = settings.config["shellThickness"] * scale;
var nozzleRadius = nozzleDiameter / 2;
var shells = Math.round(shellThickness / nozzleDiameter);
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
for (var i = 0; i < slice.parts.length; i ++) {
var part = slice.parts[i];
if (!part.intersect.closed) {
continue;
}
// var outerLine = part.intersect.clone().scaleUp(scale).offset(-nozzleRadius);
var outerLine = part.intersect.scaleUp(scale).offset(-nozzleRadius);
if (outerLine.length > 0) {
part.outerLine = outerLine;
for (var shell = 1; shell < shells; shell += 1) {
var offset = shell * nozzleDiameter;
var innerLine = outerLine.offset(-offset);
if (innerLine.length > 0) {
part.innerLines.push(innerLine);
}
else {
break;
}
}
}
}
}
this.progress.generatedInnerLines = true;
this._updateProgress(settings);
}
_generateInfills (slices, settings) {
console.log("generating infills");
// need to scale up everything because of clipper rounding errors
var scale = 100;
var layerHeight = settings.config["layerHeight"];
var fillGridSize = settings.config["fillGridSize"] * scale;
var bottomThickness = settings.config["bottomThickness"];
var topThickness = settings.config["topThickness"];
var nozzleDiameter = settings.config["nozzleDiameter"] * scale;
var infillOverlap = settings.config["infillOverlap"] * scale;
var bottomSkinCount = Math.ceil(bottomThickness/layerHeight);
var topSkinCount = Math.ceil(topThickness/layerHeight);
var nozzleRadius = nozzleDiameter / 2;
var hightemplateSize = Math.sqrt(2 * Math.pow(nozzleDiameter, 2));
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
if (layer - bottomSkinCount >= 0 && layer + topSkinCount < slices.length) {
var downSkin = slices[layer - bottomSkinCount].getOutline();
var upSkin = slices[layer + topSkinCount].getOutline();
var surroundingLayer = upSkin.intersect(downSkin);
}
else {
var surroundingLayer = false;
}
for (var i = 0; i < slice.parts.length; i ++) {
var part = slice.parts[i];
if (!part.intersect.closed) {
continue;
}
var outerLine = part.outerLine;
if (outerLine.length > 0) {
var inset = (part.innerLines.length > 0) ? part.innerLines[part.innerLines.length - 1] : outerLine;
var fillArea = inset.offset(-nozzleRadius);
var lowFillArea = false;
if (surroundingLayer) {
var highFillArea = fillArea.difference(surroundingLayer);
if (infillOverlap > 0) {
highFillArea = highFillArea.offset(infillOverlap);
}
highFillArea = highFillArea.intersect(fillArea);
var lowFillArea = fillArea.difference(highFillArea);
}
else {
var highFillArea = fillArea;
}
var fill = new Paths([], false);
if (lowFillArea && lowFillArea.length > 0) {
var bounds = lowFillArea.bounds();
var lowFillTemplate = this._getFillTemplate(bounds, fillGridSize, true, true);
part.fill.join(lowFillTemplate.intersect(lowFillArea));
}
if (highFillArea.length > 0) {
var bounds = highFillArea.bounds();
var even = (layer % 2 === 0);
var highFillTemplate = this._getFillTemplate(bounds, hightemplateSize, even, !even);
part.fill.join(highFillTemplate.intersect(highFillArea));
}
}
}
}
this.progress.generatedInfills = true;
this._updateProgress(settings);
}
_generateSupport (slices, settings) {
console.log("generating support");
// need to scale up everything because of clipper rounding errors
var scale = 100;
var layerHeight = settings.config["layerHeight"];
var supportGridSize = settings.config["supportGridSize"] * scale;
var supportAcceptanceMargin = settings.config["supportAcceptanceMargin"] * scale;
var supportMargin = settings.config["supportMargin"] * scale;
var plateSize = settings.config["supportPlateSize"] * scale;
var supportDistanceY = settings.config["supportDistanceY"];
var supportDistanceLayers = Math.max(Math.ceil(supportDistanceY / layerHeight), 1);
var nozzleDiameter = settings.config["nozzleDiameter"] * scale;
var supportAreas = new Paths([], true);
for (var layer = slices.length - 1 - supportDistanceLayers; layer >= 0; layer --) {
var currentSlice = slices[layer];
if (supportAreas.length > 0) {
if (layer >= supportDistanceLayers) {
var sliceSkin = slices[layer - supportDistanceLayers].getOutline();
sliceSkin = sliceSkin;
var supportAreasSlimmed = supportAreas.difference(sliceSkin.offset(supportMargin));
if (supportAreasSlimmed.area() < 100.0) {
supportAreas = supportAreas.difference(sliceSkin);
}
else {
supportAreas = supportAreasSlimmed;
}
}
var supportTemplate = this._getFillTemplate(supportAreas.bounds(), supportGridSize, true, true);
var supportFill = supportTemplate.intersect(supportAreas);
if (supportFill.length === 0) {
currentSlice.support = supportAreas.clone();
}
else {
currentSlice.support = supportFill;
}
}
var supportSkin = slices[layer + supportDistanceLayers - 1].getOutline();
var slice = slices[layer + supportDistanceLayers];
for (var i = 0; i < slice.parts.length; i ++) {
var slicePart = slice.parts[i];
if (slicePart.intersect.closed) {
var outerLine = slicePart.outerLine;
}
else {
var outerLine = slicePart.intersect.offset(supportAcceptanceMargin);
}
var overlap = supportSkin.offset(supportAcceptanceMargin).intersect(outerLine);
var overhang = outerLine.difference(overlap);
if (overlap.length === 0 || overhang.length > 0) {
supportAreas = supportAreas.join(overhang);
}
}
}
this.progress.generatedSupport = true;
this._updateProgress(settings);
}
_optimizePaths (slices, settings) {
console.log("opimize paths");
// need to scale up everything because of clipper rounding errors
var scale = 100;
var brimOffset = settings.config["brimOffset"] * scale;
var start = new THREE.Vector2(0, 0);
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
if (layer === 0) {
slice.brim = slice.getOutline().offset(brimOffset);
}
start = slice.optimizePaths(start);
for (var i = 0; i < slice.parts.length; i ++) {
var part = slice.parts[i];
if (part.intersect.closed) {
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);
}
}
if (slice.support !== undefined) {
slice.support.scaleDown(scale);
}
if (slice.brim !== undefined) {
slice.brim.scaleDown(scale);
}
}
this.progress.optimizedPaths = true;
this._updateProgress(settings);
}
_getFillTemplate (bounds, size, even, uneven) {
var paths = new Paths([], false);
var left = Math.floor(bounds.left / size) * size;
var right = Math.ceil(bounds.right / size) * size;
var top = Math.floor(bounds.top / size) * size;
var bottom = Math.ceil(bounds.bottom / size) * size;
var width = right - left;
if (even) {
for (var y = top; y <= bottom + width; y += size) {
paths.push([
{X: left, Y: y},
{X: right, Y: y - width}
]);
}
}
if (uneven) {
for (var y = top - width; y <= bottom; y += size) {
paths.push([
{X: left, Y: y},
{X: right, Y: y + width}
]);
}
}
return paths;
}
_slicesToGCode (slices, settings) {
var gcode = new GCode().setSettings(settings);
function pathToGCode (path, retract, unRetract, type) {
for (var i = 0; i < path.length; i ++) {
var shape = path[i];
var length = path.closed ? (shape.length + 1) : shape.length;
for (var j = 0; j < length; j ++) {
var point = shape[j % shape.length];
if (j === 0) {
// TODO
// moveTo should impliment combing
gcode.moveTo(point.X, point.Y, layer);
if (unRetract) {
gcode.unRetract();
}
}
else {
gcode.lineTo(point.X, point.Y, layer, type);
}
}
}
if (retract) {
gcode.retract();
}
}
for (var layer = 0; layer < slices.length; layer ++) {
var slice = slices[layer];
if (layer === 1) {
gcode.turnFanOn();
gcode.bottom = false;
}
if (slice.brim !== undefined) {
pathToGCode(slice.brim, true, true, "brim");
}
for (var i = 0; i < slice.parts.length; i ++) {
var part = slice.parts[i];
if (part.intersect.closed) {
pathToGCode(part.outerLine, false, true, "outerLine");
for (var j = 0; j < part.innerLines.length; j ++) {
var innerLine = part.innerLines[j];
pathToGCode(innerLine, false, false, "innerLine");
}
pathToGCode(part.fill, true, false, "fill");
}
else {
var retract = !(slice.parts.length === 1 && slice.support === undefined);
pathToGCode(part.intersect, retract, retract, "outerLine");
}
}
if (slice.support !== undefined) {
pathToGCode(slice.support, true, true, "support");
}
}
this.progress.generatedGCode = true;
this._updateProgress(settings);
return gcode.getGCode();
}
_updateProgress (settings) {
if (this.onprogress !== undefined) {
var supportEnabled = settings.config["supportEnabled"];