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

View File

@ -2,49 +2,49 @@ dimensions:
x: 200 x: 200
y: 200 y: 200
z: 200 z: 200
temperature: 210
bedTemperature: 70
# heatBedTemperature: 20
# heatTemperature: 20
# heatupEnabled: true
travelSpeed: 200.0
layerHeight: 0.15
heatedBed: false heatedBed: false
nozzleDiameter: 0.4 nozzleDiameter: 0.4
filamentThickness: 2.85 filamentThickness: 2.85
temperature: 210
bedTemperature: 70
layerHeight: 0.15
thickness:
top: 1.2
bottom: 1.2
shell: 0.8
retraction: retraction:
amount: 3.0
enabled: true enabled: true
amount: 3.0
speed: 50.0 speed: 50.0
minDistance: 0.0 minDistance: 0.0
travel:
speed: 200.0
support: support:
enabled: false
acceptanceMargin: 1.5 acceptanceMargin: 1.5
distanceY: 0.4 distanceY: 0.4
enabled: false
gridSize: 6.0 gridSize: 6.0
margin: 2.0 margin: 2.0
plateSize: 4.0 plateSize: 4.0
flowRate: 0.8 flowRate: 0.8
speed: 40.0 speed: 40.0
outerLine: innerShell:
flowRate: 1.0
speed: 50.0
outerShell:
flowRate: 1.0 flowRate: 1.0
speed: 40.0 speed: 40.0
innerLine: innerInfill:
flowRate: 1.0 flowRate: 1.0
speed: 50.0 speed: 80.0
fill:
flowRate: 1.0
speed: 50.0
gridSize: 5.0 gridSize: 5.0
outerInfill:
flowRate: 1.0
speed: 50.0
brim: brim:
offset: 4.0
flowRate: 1.0 flowRate: 1.0
speed: 40.0 speed: 40.0
offset: 4.0 firstLayer:
top:
thickness: 1.2
bottom:
flowRate: 1.2 flowRate: 1.2
speed: 40.0 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) { export default function applyPrecision(shapes) {
for (let i = 0; i < shapes.length; i ++) { for (let i = 0; i < shapes.length; i ++) {
const { closedShapes, openShapes } = shapes[i]; const { fillShapes, lineShapesOpen, lineShapesClosed } = shapes[i];
scaleUpShape(closedShapes); scaleUpShape(fillShapes);
scaleUpShape(openShapes); scaleUpShape(lineShapesOpen);
scaleUpShape(lineShapesClosed);
} }
} }

View File

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

View File

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

View File

@ -5,7 +5,10 @@ export default function calculateOutlines(slices, settings) {
const slice = slices[layer]; const slice = slices[layer];
slice.outline = slice.parts.reduce((shape, part) => { 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; return shape;
}, new Shape([], true)); }, new Shape([], true));
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,47 +1,80 @@
import GCode from './helpers/GCode.js'; import GCode from './helpers/GCode.js';
export default function slicesToGCode(slices, settings) { const PROFILE_TYPES = ['support', 'innerShell', 'outerShell', 'innerInfill', 'outerInfill', 'brim'];
const gcode = new GCode(settings);
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 ++) { for (let layer = 0; layer < slices.length; layer ++) {
const slice = slices[layer]; const slice = slices[layer];
const z = layer * layerHeight + 0.2;
if (layer === 1) { if (layer === 1) {
gcode.turnFanOn(); 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') { 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 ++) { for (let i = 0; i < slice.parts.length; i ++) {
const part = slice.parts[i]; const part = slice.parts[i];
if (part.shape.closed) { if (part.closed) {
pathToGCode(gcode, part.outerLine, false, true, layer, 'outerLine'); 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 unRetract = isOuterShell;
const innerLine = part.innerLines[i]; const profile = isOuterShell ? profiles.outerShell : profiles.innerShell;
pathToGCode(gcode, innerLine, false, false, layer, 'innerLine'); 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 { } else {
const retract = !(slice.parts.length === 1 && typeof slice.support === 'undefined'); 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') { 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(); 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 { closed } = shape;
const paths = shape.mapToLower(); const paths = shape.mapToLower();
@ -55,18 +88,18 @@ function pathToGCode(gcode, shape, retract, unRetract, layer, type) {
if (i === 0) { if (i === 0) {
// TODO // TODO
// moveTo should impliment combing // moveTo should impliment combing
gcode.moveTo(point.x, point.y, layer); gcode.moveTo(point.x, point.y, z, travelProfile);
if (unRetract) { if (unRetract) {
gcode.unRetract(); gcode.unRetract(retractionProfile);
} }
} else { } else {
gcode.lineTo(point.x, point.y, layer, type); gcode.lineTo(point.x, point.y, z, lineProfile);
} }
} }
} }
if (retract) { if (retract) {
gcode.retract(); gcode.retract(retractionProfile);
} }
} }