diff --git a/simpleExample/coming_test.js b/simpleExample/coming_test.js new file mode 100644 index 0000000..14b302a --- /dev/null +++ b/simpleExample/coming_test.js @@ -0,0 +1,80 @@ +import Shape from 'clipper-js'; +import comb from '../src/sliceActions/helpers/comb.js'; + +const canvas = document.createElement('canvas'); +document.body.appendChild(canvas); +canvas.width = 720; +canvas.height = 480; +const context = canvas.getContext('2d'); + +const outline = new Shape([[ + { x: 100, y: 100 }, + { x: 400, y: 100 }, + { x: 400, y: 150 }, + { x: 200, y: 150 }, + { x: 200, y: 200 }, + { x: 400, y: 200 }, + { x: 400, y: 250 }, + { x: 200, y: 250 }, + { x: 200, y: 300 }, + { x: 400, y: 300 }, + { x: 400, y: 400 }, + { x: 100, y: 400 } +], [ + { x: 130, y: 310 }, + { x: 130, y: 370 }, + { x: 360, y: 370 }, + { x: 360, y: 360 }, + { x: 150, y: 360 }, + { x: 150, y: 350 }, + { x: 360, y: 350 }, + { x: 360, y: 340 }, + { x: 150, y: 340 }, + { x: 150, y: 330 }, + { x: 360, y: 330 }, + { x: 360, y: 310 } +]], true, true, false); + +const start = { x: 380, y: 120 }; +const end = { x: 200, y: 380 }; + +let combPath = comb(outline, start, end); + +canvas.onmousemove = (event) => { + start.x = event.x; + start.y = event.y; + + combPath = comb(outline, start, end); + draw(); +}; + +draw(); + +function draw() { + context.clearRect(0, 0, canvas.width, canvas.height); + + context.strokeStyle = 'black'; + for (const path of outline.mapToLower()) { + context.beginPath(); + for (const point of path) { + context.lineTo(point.x, point.y); + } + context.closePath(); + context.stroke(); + } + + context.strokeStyle = 'red'; + context.beginPath(); + for (const point of combPath) { + context.lineTo(point.x, point.y); + } + context.stroke(); + + context.beginPath(); + context.arc(start.x, start.y, 3, 0, Math.PI * 2.0, false); + context.stroke(); + + context.beginPath(); + context.arc(end.x, end.y, 3, 0, Math.PI * 2.0, false); + context.stroke(); +} diff --git a/src/settings/default.yml b/src/settings/default.yml index 128a25c..40532b8 100644 --- a/src/settings/default.yml +++ b/src/settings/default.yml @@ -8,6 +8,7 @@ filamentThickness: 2.85 temperature: 210 bedTemperature: 70 layerHeight: 0.15 +combing: true thickness: top: 1.2 bottom: 1.2 diff --git a/src/sliceActions/helpers/comb.js b/src/sliceActions/helpers/comb.js index 315a44d..e8c6c3f 100644 --- a/src/sliceActions/helpers/comb.js +++ b/src/sliceActions/helpers/comb.js @@ -1,43 +1,79 @@ +import Shape from 'clipper-js'; + 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]); - } - } + if (distanceTo(start, end) < 5) { + return [start, end]; } - combPath.push(closestPointEnd, end); + let combPath = new Shape([[start, end]], false, true, false); - return combPath; + for (let i = 0; i < outline.paths.length; i ++) { + let outlinePart = new Shape([outline.paths[i]], true, false, false); + let snappedCombPaths = i === 0 ? combPath.intersect(outlinePart) : combPath.difference(outlinePart); + + if (snappedCombPaths.paths.length <= 1) continue; + + snappedCombPaths = snappedCombPaths.mapToLower(); + outlinePart = outlinePart.mapToLower()[0]; + + const distanceMap = new WeakMap(); + + for (let i = 0; i < snappedCombPaths.length; i ++) { + const snappedCombPath = snappedCombPaths[i]; + const distanceStart = distanceTo(start, snappedCombPath[0]); + const distanceEnd = distanceTo(start, snappedCombPath[snappedCombPath.length - 1]); + + if (distanceStart < distanceEnd) { + distanceMap.set(snappedCombPath, distanceStart); + } else { + snappedCombPath.reverse(); + distanceMap.set(snappedCombPath, distanceEnd); + } + } + + snappedCombPaths.sort((a, b) => distanceMap.get(a) - distanceMap.get(b)); + + const startPath = snappedCombPaths[0]; + const startPoint = startPath[startPath.length - 1]; + + const endPath = snappedCombPaths[snappedCombPaths.length - 1]; + const endPoint = endPath[0]; + + const lineIndexStart = findClosestLineOnPath(outlinePart, startPoint); + const lineIndexEnd = findClosestLineOnPath(outlinePart, endPoint); + + const path = []; + if (lineIndexEnd > lineIndexStart) { + if (lineIndexStart + outlinePart.length - lineIndexEnd < lineIndexEnd - lineIndexStart) { + for (let i = lineIndexStart + outlinePart.length; i > lineIndexEnd; i --) { + path.push(outlinePart[i % outlinePart.length]); + } + } else { + for (let i = lineIndexStart; i < lineIndexEnd; i ++) { + path.push(outlinePart[i + 1]); + } + } + } else { + if (lineIndexEnd + outlinePart.length - lineIndexStart < lineIndexStart - lineIndexEnd) { + for (let i = lineIndexStart; i < lineIndexEnd + outlinePart.length; i ++) { + path.push(outlinePart[(i + 1) % outlinePart.length]); + } + } else { + for (let i = lineIndexStart; i > lineIndexEnd; i --) { + path.push(outlinePart[i]); + } + } + } + + combPath = new Shape([[...startPath, ...path, ...endPath]], false, true, false); + } + + return combPath.mapToLower()[0]; } -function findClosestPointOnPath(path, point) { +function findClosestLineOnPath(path, point) { let distance = Infinity; let lineIndex; - let closestPoint; for (let i = 0; i < path.length; i ++) { const pointA = path[i]; @@ -49,11 +85,10 @@ function findClosestPointOnPath(path, point) { if (tempDistance < distance) { distance = tempDistance; lineIndex = i; - closestPoint = tempClosestPoint; } } - return { closestPoint, lineIndex }; + return lineIndex; } function findClosestPointOnLine(a, b, c) { diff --git a/src/sliceActions/slicesToGCode.js b/src/sliceActions/slicesToGCode.js index 32beffc..9d2be7d 100644 --- a/src/sliceActions/slicesToGCode.js +++ b/src/sliceActions/slicesToGCode.js @@ -10,7 +10,8 @@ export default function slicesToGCode(slices, settings) { nozzleDiameter, travelSpeed, retraction, - travel + travel, + combing } = settings; const filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI; @@ -48,6 +49,7 @@ export default function slicesToGCode(slices, settings) { for (let i = 0; i < slice.parts.length; i ++) { const part = slice.parts[i]; + const outline = part.shell[0]; if (part.closed) { for (let i = 0; i < part.shell.length; i ++) { @@ -56,11 +58,11 @@ export default function slicesToGCode(slices, settings) { const unRetract = isOuterShell; const profile = isOuterShell ? profiles.outerShell : profiles.innerShell; - pathToGCode(null, false, gcode, shell, false, unRetract, z, profile); + pathToGCode(outline, combing && true, gcode, shell, false, unRetract, z, profile); } - 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); + pathToGCode(outline, combing && true, gcode, part.outerFill, false, false, z, profiles.outerInfill); + pathToGCode(outline, combing && true, gcode, part.innerFill, true, false, z, profiles.innerInfill); } else { const retract = !(slice.parts.length === 1 && typeof slice.support === 'undefined'); pathToGCode(null, false, gcode, part.shape, retract, retract, z, profiles.outerShell); @@ -87,7 +89,7 @@ function pathToGCode(outline, combing, gcode, shape, retract, unRetract, z, { li const point = line[i % line.length]; if (i === 0) { - if (combing && gcode._nozzlePosition.distanceTo(point) > 3) { + if (combing) { const combPath = comb(outline, gcode._nozzlePosition, point); for (let i = 0; i < combPath.length; i ++) { const combPoint = combPath[i];