diff --git a/src/sliceActions/helpers/comb.js b/src/sliceActions/helpers/comb.js new file mode 100644 index 0000000..315a44d --- /dev/null +++ b/src/sliceActions/helpers/comb.js @@ -0,0 +1,114 @@ +export default function comb(outline, start, end) { + const [path] = outline.mapToLower(); + const combPath = []; + + const { closestPoint: closestPointStart, lineIndex: lineIndexStart } = findClosestPointOnPath(path, start); + const { closestPoint: closestPointEnd, lineIndex: lineIndexEnd } = findClosestPointOnPath(path, end); + + combPath.push(start, closestPointStart); + + if (lineIndexEnd > lineIndexStart) { + if (lineIndexStart + path.length - lineIndexEnd < lineIndexEnd - lineIndexStart) { + for (let i = lineIndexStart + path.length; i > lineIndexEnd; i --) { + combPath.push(path[i % path.length]); + } + } else { + for (let i = lineIndexStart; i < lineIndexEnd; i ++) { + combPath.push(path[i + 1]); + } + } + } else { + if (lineIndexEnd + path.length - lineIndexStart < lineIndexStart - lineIndexEnd) { + for (let i = lineIndexStart; i < lineIndexEnd + path.length; i ++) { + combPath.push(path[(i + 1) % path.length]); + } + } else { + for (let i = lineIndexStart; i > lineIndexEnd; i --) { + combPath.push(path[i]); + } + } + } + + combPath.push(closestPointEnd, end); + + return combPath; +} + +function findClosestPointOnPath(path, point) { + let distance = Infinity; + let lineIndex; + let closestPoint; + + for (let i = 0; i < path.length; i ++) { + const pointA = path[i]; + const pointB = path[(i + 1) % path.length]; + + const tempClosestPoint = findClosestPointOnLine(pointA, pointB, point); + const tempDistance = distanceTo(tempClosestPoint, point); + + if (tempDistance < distance) { + distance = tempDistance; + lineIndex = i; + closestPoint = tempClosestPoint; + } + } + + return { closestPoint, lineIndex }; +} + +function findClosestPointOnLine(a, b, c) { + const b_ = subtract(b, a); + const c_ = subtract(c, a); + + const lambda = dot(normalize(b_), c_) / length(b_); + + if (lambda >= 1) { + return b; + } else if (lambda > 0) { + return add(a, scale(b_, lambda)); + } else { + return a; + } +} + +function subtract(a, b) { + return { + x: a.x - b.x, + y: a.y - b.y + }; +} + +function add(a, b) { + return { + x: a.x + b.x, + y: a.y + b.y + }; +} + +function scale(a, factor) { + return { + x: a.x * factor, + y: a.y * factor + } +} + +function dot(a, b) { + return a.x * b.x + a.y * b.y; +} + +function normalize(a) { + const l = length(a); + + return { + x: a.x / l, + y: a.y / l + }; +} + +function length(a) { + return Math.sqrt(a.x * a.x + a.y * a.y); +} + +function distanceTo(a, b) { + return length(subtract(a, b)); +} diff --git a/src/sliceActions/slicesToGCode.js b/src/sliceActions/slicesToGCode.js index fd38039..32beffc 100644 --- a/src/sliceActions/slicesToGCode.js +++ b/src/sliceActions/slicesToGCode.js @@ -1,4 +1,5 @@ import GCode from './helpers/GCode.js'; +import comb from './helpers/comb.js'; const PROFILE_TYPES = ['support', 'innerShell', 'outerShell', 'innerInfill', 'outerInfill', 'brim']; @@ -42,7 +43,7 @@ export default function slicesToGCode(slices, settings) { }, {}); if (typeof slice.brim !== 'undefined') { - pathToGCode(gcode, slice.brim, true, true, z, profiles.brim); + pathToGCode(null, false, gcode, slice.brim, true, true, z, profiles.brim); } for (let i = 0; i < slice.parts.length; i ++) { @@ -55,26 +56,26 @@ export default function slicesToGCode(slices, settings) { const unRetract = isOuterShell; const profile = isOuterShell ? profiles.outerShell : profiles.innerShell; - pathToGCode(gcode, shell, false, unRetract, z, profile); + pathToGCode(null, false, gcode, shell, false, unRetract, z, profile); } - pathToGCode(gcode, part.outerFill, false, false, z, profiles.outerInfill); - pathToGCode(gcode, part.innerFill, true, false, z, profiles.innerInfill); + pathToGCode(part.shell[0], true, gcode, part.outerFill, false, false, z, profiles.outerInfill); + pathToGCode(part.shell[0], true, 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, z, profiles.outerShell); + pathToGCode(null, false, gcode, part.shape, retract, retract, z, profiles.outerShell); } } if (typeof slice.support !== 'undefined') { - pathToGCode(gcode, slice.support, true, true, z, profiles.support); + pathToGCode(null, false, gcode, slice.support, true, true, z, profiles.support); } } return gcode.getGCode(); } -function pathToGCode(gcode, shape, retract, unRetract, z, { lineProfile, travelProfile, retractionProfile }) { +function pathToGCode(outline, combing, gcode, shape, retract, unRetract, z, { lineProfile, travelProfile, retractionProfile }) { const { closed } = shape; const paths = shape.mapToLower(); @@ -86,9 +87,15 @@ function pathToGCode(gcode, shape, retract, unRetract, z, { lineProfile, travelP const point = line[i % line.length]; if (i === 0) { - // TODO - // moveTo should impliment combing - gcode.moveTo(point.x, point.y, z, travelProfile); + if (combing && gcode._nozzlePosition.distanceTo(point) > 3) { + const combPath = comb(outline, gcode._nozzlePosition, point); + for (let i = 0; i < combPath.length; i ++) { + const combPoint = combPath[i]; + gcode.moveTo(combPoint.x, combPoint.y, z, travelProfile); + } + } else { + gcode.moveTo(point.x, point.y, z, travelProfile); + } if (unRetract) { gcode.unRetract(retractionProfile);