Merge branch 'develop'

This commit is contained in:
casperlamboo 2017-07-28 15:13:01 +02:00
commit 48f2cef965
13 changed files with 196 additions and 185 deletions

View File

@ -9,6 +9,7 @@ const settings = {
};
const geometry = new THREE.TorusGeometry(20, 10, 30, 30).clone();
geometry.mergeVertices();
const onProgress = ({ progress: { done, total, action } }) => {
const percentage = `${(done / total * 100).toFixed()}%`
@ -17,4 +18,4 @@ const onProgress = ({ progress: { done, total, action } }) => {
sliceGeometry(settings, geometry, null, false, onProgress).then(gcode => {
document.body.innerHTML = gcode.replace(/(?:\r\n|\r|\n)/g, '<br />');
});
});

View File

@ -2,49 +2,49 @@ dimensions:
x: 200
y: 200
z: 200
temperature: 210
bedTemperature: 70
# heatBedTemperature: 20
# heatTemperature: 20
# heatupEnabled: true
travelSpeed: 200.0
layerHeight: 0.15
heatedBed: false
nozzleDiameter: 0.4
filamentThickness: 2.85
temperature: 210
bedTemperature: 70
layerHeight: 0.15
thickness:
top: 1.2
bottom: 1.2
shell: 0.8
retraction:
amount: 3.0
enabled: true
amount: 3.0
speed: 50.0
minDistance: 0.0
travel:
speed: 200.0
support:
enabled: false
acceptanceMargin: 1.5
distanceY: 0.4
enabled: false
gridSize: 6.0
margin: 2.0
plateSize: 4.0
flowRate: 0.8
speed: 40.0
outerLine:
innerShell:
flowRate: 1.0
speed: 50.0
outerShell:
flowRate: 1.0
speed: 40.0
innerLine:
innerInfill:
flowRate: 1.0
speed: 50.0
fill:
flowRate: 1.0
speed: 50.0
speed: 80.0
gridSize: 5.0
outerInfill:
flowRate: 1.0
speed: 50.0
brim:
offset: 4.0
flowRate: 1.0
speed: 40.0
offset: 4.0
top:
thickness: 1.2
bottom:
firstLayer:
flowRate: 1.2
speed: 40.0
thickness: 0.4
shell:
thickness: 0.4

View File

@ -2,10 +2,11 @@ import { PRECISION } from '../constants.js'
export default function applyPrecision(shapes) {
for (let i = 0; i < shapes.length; i ++) {
const { closedShapes, openShapes } = shapes[i];
const { fillShapes, lineShapesOpen, lineShapesClosed } = shapes[i];
scaleUpShape(closedShapes);
scaleUpShape(openShapes);
scaleUpShape(fillShapes);
scaleUpShape(lineShapesOpen);
scaleUpShape(lineShapesClosed);
}
}

View File

@ -5,19 +5,21 @@ import Shape from 'clipper-js';
export default function generateInfills(slices, settings) {
let {
layerHeight,
fill: { gridSize: fillGridSize },
bottom: { thickness: bottomThickness },
top: { thickness: topThickness },
innerInfill: { gridSize: infillGridSize },
thickness: {
top: topThickness,
bottom: bottomThickness
},
nozzleDiameter
} = settings;
fillGridSize /= PRECISION;
infillGridSize /= PRECISION;
nozzleDiameter /= PRECISION;
const bottomSkinCount = Math.ceil(bottomThickness/layerHeight);
const topSkinCount = Math.ceil(topThickness/layerHeight);
const nozzleRadius = nozzleDiameter / 2;
const hightemplateSize = Math.sqrt(2 * Math.pow(nozzleDiameter, 2));
const outerFillTemplateSize = Math.sqrt(2 * Math.pow(nozzleDiameter, 2));
for (let layer = 0; layer < slices.length; layer ++) {
const slice = slices[layer];
@ -32,39 +34,35 @@ export default function generateInfills(slices, settings) {
for (let i = 0; i < slice.parts.length; i ++) {
const part = slice.parts[i];
if (!part.shape.closed) {
continue;
if (!part.closed) continue;
const innerShell = part.shell[part.shell.length - 1];
if (innerShell.paths.length === 0) continue;
const fillArea = innerShell.offset(-nozzleRadius);
let innerFillArea;
let outerFillArea;
if (surroundingLayer) {
outerFillArea = fillArea.difference(surroundingLayer).intersect(fillArea);
innerFillArea = fillArea.difference(outerFillArea);
} else {
outerFillArea = fillArea;
}
const outerLine = part.outerLine;
if (innerFillArea && innerFillArea.paths.length > 0) {
const bounds = innerFillArea.shapeBounds();
const innerFillTemplate = getFillTemplate(bounds, infillGridSize, true, true);
if (outerLine.paths.length > 0) {
const inset = (part.innerLines.length > 0) ? part.innerLines[part.innerLines.length - 1] : outerLine;
part.innerFill.join(innerFillTemplate.intersect(innerFillArea));
}
const fillArea = inset.offset(-nozzleRadius);
let lowFillArea;
let highFillArea;
if (surroundingLayer) {
highFillArea = fillArea.difference(surroundingLayer).intersect(fillArea);
lowFillArea = fillArea.difference(highFillArea);
} else {
highFillArea = fillArea;
}
if (outerFillArea.paths.length > 0) {
const bounds = outerFillArea.shapeBounds();
const even = (layer % 2 === 0);
const outerFillTemplate = getFillTemplate(bounds, outerFillTemplateSize, even, !even);
if (lowFillArea && lowFillArea.paths.length > 0) {
const bounds = lowFillArea.shapeBounds();
const lowFillTemplate = getFillTemplate(bounds, fillGridSize, true, true);
part.fill.join(lowFillTemplate.intersect(lowFillArea));
}
if (highFillArea.paths.length > 0) {
const bounds = highFillArea.shapeBounds();
const even = (layer % 2 === 0);
const highFillTemplate = getFillTemplate(bounds, hightemplateSize, even, !even);
part.fill.join(highFillTemplate.intersect(highFillArea));
}
part.outerFill.join(outerFillTemplate.intersect(outerFillArea));
}
}
}

View File

@ -12,12 +12,14 @@ export default function generateInnerLines(slices, settings) {
let {
layerHeight,
nozzleDiameter,
shell: { thickness: shellThickness }
thickness: { shell: shellThickness }
} = settings;
nozzleDiameter /= PRECISION;
shellThickness /= PRECISION;
const nozzleRadius = nozzleDiameter / 2;
const shells = Math.round(shellThickness / nozzleDiameter);
const numShells = Math.round(shellThickness / nozzleDiameter);
for (let layer = 0; layer < slices.length; layer ++) {
const slice = slices[layer];
@ -25,21 +27,21 @@ export default function generateInnerLines(slices, settings) {
for (let i = 0; i < slice.parts.length; i ++) {
const part = slice.parts[i];
if (!part.shape.closed) continue;
if (!part.closed) continue;
const outerLine = part.shape.offset(-nozzleRadius, offsetOptions);
if (outerLine.paths.length > 0) {
part.outerLine.join(outerLine);
part.shell.push(outerLine);
// start with 1 because outerLine is the 1st (0) shell
for (let shell = 1; shell < shells; shell += 1) {
const offset = shell * nozzleDiameter;
for (let inset = 1; inset < numShells; inset += 1) {
const offset = inset * nozzleDiameter;
const innerLine = outerLine.offset(-offset, offsetOptions);
const shell = outerLine.offset(-offset, offsetOptions);
if (innerLine.paths.length > 0) {
part.innerLines.push(innerLine);
if (shell.paths.length > 0) {
part.shell.push(shell);
} else {
break;
}

View File

@ -5,7 +5,10 @@ export default function calculateOutlines(slices, settings) {
const slice = slices[layer];
slice.outline = slice.parts.reduce((shape, part) => {
if (part.outerLine) shape.join(part.outerLine);
if (part.closed) {
const [outerLine] = part.shell;
shape.join(outerLine);
}
return shape;
}, new Shape([], true));
}

View File

@ -10,16 +10,15 @@ const POSITION_Y = 'Y';
const POSITION_Z = 'Z';
export default class {
constructor(settings) {
constructor(nozzleToFilamentRatio) {
this._nozzleToFilamentRatio = nozzleToFilamentRatio;
this._gcode = '';
this._currentValues = {};
this._settings = settings;
this._nozzlePosition = new THREE.Vector2(0, 0);
this._extruder = 0.0;
this._isRetracted = false;
this._isFanOn = false;
this.bottom = true;
}
_addGCode(command) {
@ -62,14 +61,8 @@ export default class {
return this;
}
moveTo(x, y, layer) {
const {
layerHeight,
travelSpeed
} = this._settings;
const z = layer * layerHeight + 0.2;
const speed = travelSpeed * 60;
moveTo(x, y, z, { speed }) {
speed *= 60;
this._addGCode({
[MOVE]: 0,
@ -84,30 +77,13 @@ export default class {
return this;
}
lineTo(x, y, layer, type) {
lineTo(x, y, z, { speed, flowRate }) {
const newNozzlePosition = new THREE.Vector2(x, y);
const {
layerHeight,
nozzleDiameter,
filamentThickness,
travelSpeed
} = this._settings;
const profile = this._settings[(this.bottom ? 'bottom' : type)];
let {
speed,
flowRate
} = profile;
speed *= 60;
const z = layer * layerHeight + 0.2;
const lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
const filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI;
this._extruder += lineLength * ((nozzleDiameter * layerHeight) / filamentSurfaceArea) * flowRate;
this._extruder += this._nozzleToFilamentRatio * lineLength * flowRate;
this._addGCode({
[MOVE]: 1,
@ -123,21 +99,13 @@ export default class {
return this;
}
unRetract() {
const {
retraction: {
enabled: retractionEnabled,
minDistance: retractionMinDistance,
speed: retractionSpeed
}
} = this._settings;
if (this._isRetracted && retractionEnabled) {
unRetract({ enabled, speed, minDistance }) {
if (this._isRetracted && enabled) {
this._isRetracted = false;
const speed = retractionSpeed * 60;
speed *= 60;
if (this._extruder > retractionMinDistance) {
if (this._extruder > minDistance) {
this._addGCode({
[MOVE]: 0,
[EXTRUDER]: this._extruder.toFixed(3),
@ -149,25 +117,16 @@ export default class {
return this;
}
retract() {
const {
retraction: {
amount: retractionAmount,
enabled: retractionEnabled,
minDistance: retractionMinDistance,
speed: retractionSpeed
}
} = this._settings;
if (!this._isRetracted && retractionEnabled) {
retract({ enabled, speed, minDistance, amount }) {
if (!this._isRetracted && enabled) {
this._isRetracted = true;
const speed = retractionSpeed * 60;
speed *= 60;
if (this._extruder > retractionMinDistance) {
if (this._extruder > minDistance) {
this._addGCode({
[MOVE]: 0,
[EXTRUDER]: (this._extruder - retractionAmount).toFixed(3),
[EXTRUDER]: (this._extruder - amount).toFixed(3),
[SPEED]: speed.toFixed(3)
});
}

View File

@ -4,13 +4,13 @@ export default class {
constructor() {
this.parts = [];
}
add(shape) {
const part = { shape };
add(shape, closed) {
const part = { shape, closed };
if (shape.closed) {
part.innerLines = [];
part.outerLine = new Shape([], true);
part.fill = new Shape([], false);
if (closed) {
part.shell = [];
part.innerFill = new Shape([], false);
part.outerFill = new Shape([], false);
}
this.parts.push(part);

View File

@ -10,8 +10,9 @@ export default function intersectionsToShapes(layerIntersectionIndexes, layerInt
if (intersectionIndexes.length === 0) continue;
const closedShapes = [];
const openShapes = [];
const fillShapes = [];
const lineShapesOpen = [];
const lineShapesClosed = [];
for (let i = 0; i < intersectionIndexes.length; i ++) {
let index = intersectionIndexes[i];
@ -107,14 +108,17 @@ export default function intersectionsToShapes(layerIntersectionIndexes, layerInt
}
if (openGeometry) {
if (!openShape) shape.push(shape[0].clone());
openShapes.push(shape);
if (openShape) {
lineShapesOpen.push(shape);
} else {
lineShapesClosed.push(shape);
}
} else {
closedShapes.push(shape);
fillShapes.push(shape);
}
}
layers.push({ closedShapes, openShapes });
layers.push({ fillShapes, lineShapesOpen, lineShapesClosed });
}
return layers;

View File

@ -17,7 +17,7 @@ export default function optimizePaths(slices, settings) {
for (let i = 0; i < slice.parts.length; i ++) {
const part = slice.parts[i];
const shape = part.shape.closed ? part.outerLine : part.shape;
const shape = part.closed ? part.shell[0] : part.shape;
const bounds = shape.shapeBounds();
boundingBoxes.set(part, bounds);
@ -47,24 +47,24 @@ export default function optimizePaths(slices, settings) {
const [part] = slice.parts.splice(closestPart, 1);
parts.push(part);
if (part.shape.closed) {
if (part.outerLine.paths.length > 0) {
part.outerLine = optimizeShape(part.outerLine, start);
start.copy(part.outerLine.lastPoint(true));
if (part.closed) {
for (let i = 0; i < part.shell.length; i ++) {
const shell = part.shell[i];
if (shell.paths.length === 0) continue;
part.shell[i] = optimizeShape(shell, start);
start.copy(part.shell[i].lastPoint(true));
}
for (let i = 0; i < part.innerLines.length; i ++) {
const innerLine = part.innerLines[i];
if (innerLine.paths.length > 0) {
part.innerLines[i] = optimizeShape(innerLine, start);
start.copy(part.innerLines[i].lastPoint(true));
}
if (part.outerFill.paths.length > 0) {
part.outerFill = optimizeShape(part.outerFill, start);
start.copy(part.outerFill.lastPoint(true));
}
if (part.fill.paths.length > 0) {
part.fill = optimizeShape(part.fill, start);
start.copy(part.fill.lastPoint(true));
if (part.innerFill.paths.length > 0) {
part.innerFill = optimizeShape(part.innerFill, start);
start.copy(part.innerFill.lastPoint(true));
}
} else {
part.shape = optimizeShape(part.shape, start);

View File

@ -9,13 +9,13 @@ export default function removePrecision(slices) {
for (let i = 0; i < slice.parts.length; i ++) {
const part = slice.parts[i];
if (part.shape.closed) {
part.outerLine.scaleDown(inversePrecision);
for (let i = 0; i < part.innerLines.length; i ++) {
const innerLine = part.innerLines[i];
if (part.closed) {
for (let i = 0; i < part.shell.length; i ++) {
const innerLine = part.shell[i];
innerLine.scaleDown(inversePrecision);
}
part.fill.scaleDown(inversePrecision);
part.innerFill.scaleDown(inversePrecision);
part.outerFill.scaleDown(inversePrecision);
} else {
part.shape.scaleDown(inversePrecision);
}

View File

@ -9,15 +9,18 @@ export default function shapesToSlices(shapes, settings) {
const sliceLayers = [];
for (let layer = 0; layer < shapes.length; layer ++) {
let { closedShapes, openShapes } = shapes[layer];
let { fillShapes, lineShapesOpen, lineShapesClosed } = shapes[layer];
closedShapes = new Shape(closedShapes, true, true, true, true)
fillShapes = new Shape(fillShapes, true, true, true, true)
.fixOrientation()
.simplify('pftNonZero')
.clean(cleanDelta)
.seperateShapes();
openShapes = new Shape(openShapes, false, true, true, true);
lineShapesClosed = new Shape(lineShapesClosed, true, true, true, true)
.clean(cleanDelta);
lineShapesOpen = new Shape(lineShapesOpen, false, true, true, true);
// .clean(cleanDelta);
// TODO
// Cleaning is actually wanted here but there is a bug in the clean function
@ -25,17 +28,24 @@ export default function shapesToSlices(shapes, settings) {
const slice = new Slice();
for (let i = 0; i < closedShapes.length; i ++) {
const closedShape = closedShapes[i];
slice.add(closedShape);
for (let i = 0; i < fillShapes.length; i ++) {
const fillShape = fillShapes[i];
slice.add(fillShape, true);
// if (openShapes.path.length > 0) {
// openShapes = openShapes.difference(closedShape);
// if (lineShapesClosed.paths.length > 0) {
// lineShapesClosed = lineShapesClosed.difference(closedShape);
// }
// if (lineShapesOpen.paths.length > 0) {
// lineShapesOpen = lineShapesOpen.difference(closedShape);
// }
}
if (openShapes.paths.length > 0) {
slice.add(openShapes);
if (lineShapesClosed.paths.length > 0) {
slice.add(lineShapesClosed, false);
}
if (lineShapesOpen.paths.length > 0) {
slice.add(lineShapesOpen, false);
}
sliceLayers.push(slice);

View File

@ -1,47 +1,80 @@
import GCode from './helpers/GCode.js';
export default function slicesToGCode(slices, settings) {
const gcode = new GCode(settings);
const PROFILE_TYPES = ['support', 'innerShell', 'outerShell', 'innerInfill', 'outerInfill', 'brim'];
export default function slicesToGCode(slices, settings) {
const {
layerHeight,
filamentThickness,
nozzleDiameter,
travelSpeed,
retraction,
travel
} = settings;
const filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI;
const lineSurfaceArea = nozzleDiameter * layerHeight;
const nozzleToFilamentRatio = lineSurfaceArea / filamentSurfaceArea;
const gcode = new GCode(nozzleToFilamentRatio);
const defaultProfile = {
travelProfile: travel,
retractionProfile: retraction
};
let isFirstLayer = true;
for (let layer = 0; layer < slices.length; layer ++) {
const slice = slices[layer];
const z = layer * layerHeight + 0.2;
if (layer === 1) {
gcode.turnFanOn();
gcode.bottom = false;
isFirstLayer = false;
}
const profiles = PROFILE_TYPES.reduce((profiles, profileType) => {
profiles[profileType] = {
...defaultProfile,
lineProfile: isFirstLayer ? settings.firstLayer : settings[profileType]
};
return profiles;
}, {});
if (typeof slice.brim !== 'undefined') {
pathToGCode(gcode, slice.brim, true, true, layer, 'brim');
pathToGCode(gcode, slice.brim, true, true, z, profiles.brim);
}
for (let i = 0; i < slice.parts.length; i ++) {
const part = slice.parts[i];
if (part.shape.closed) {
pathToGCode(gcode, part.outerLine, false, true, layer, 'outerLine');
if (part.closed) {
for (let i = 0; i < part.shell.length; i ++) {
const shell = part.shell[i];
const isOuterShell = i === 0;
for (let i = 0; i < part.innerLines.length; i ++) {
const innerLine = part.innerLines[i];
pathToGCode(gcode, innerLine, false, false, layer, 'innerLine');
const unRetract = isOuterShell;
const profile = isOuterShell ? profiles.outerShell : profiles.innerShell;
pathToGCode(gcode, shell, false, unRetract, z, profile);
}
pathToGCode(gcode, part.fill, true, false, layer, 'fill');
pathToGCode(gcode, part.outerFill, false, false, z, profiles.outerInfill);
pathToGCode(gcode, part.innerFill, true, false, z, profiles.innerInfill);
} else {
const retract = !(slice.parts.length === 1 && typeof slice.support === 'undefined');
pathToGCode(gcode, part.shape, retract, retract, layer, 'outerLine');
pathToGCode(gcode, part.shape, retract, retract, z, profiles.outerShell);
}
}
if (typeof slice.support !== 'undefined') {
pathToGCode(gcode, slice.support, true, true, layer, 'support');
pathToGCode(gcode, slice.support, true, true, z, profiles.support);
}
}
return gcode.getGCode();
}
function pathToGCode(gcode, shape, retract, unRetract, layer, type) {
function pathToGCode(gcode, shape, retract, unRetract, z, { lineProfile, travelProfile, retractionProfile }) {
const { closed } = shape;
const paths = shape.mapToLower();
@ -55,18 +88,18 @@ function pathToGCode(gcode, shape, retract, unRetract, layer, type) {
if (i === 0) {
// TODO
// moveTo should impliment combing
gcode.moveTo(point.x, point.y, layer);
gcode.moveTo(point.x, point.y, z, travelProfile);
if (unRetract) {
gcode.unRetract();
gcode.unRetract(retractionProfile);
}
} else {
gcode.lineTo(point.x, point.y, layer, type);
gcode.lineTo(point.x, point.y, z, lineProfile);
}
}
}
if (retract) {
gcode.retract();
gcode.retract(retractionProfile);
}
}