diff --git a/config.js b/config.js index 59039c6..d6eaae0 100644 --- a/config.js +++ b/config.js @@ -16,81 +16,77 @@ System.config({ }, map: { - "babel": "npm:babel-core@5.8.21", - "babel-runtime": "npm:babel-runtime@5.8.20", + "Doodle3D/clipper-js": "github:Doodle3D/clipper-js@0.0.2", + "babel": "npm:babel-core@5.8.38", + "babel-runtime": "npm:babel-runtime@5.8.38", "casperlamboo/EventDispatcher": "github:casperlamboo/EventDispatcher@master", "clipper-lib": "npm:clipper-lib@1.0.0", "core-js": "npm:core-js@0.9.18", "json": "github:systemjs/plugin-json@0.1.0", - "nodeca/js-yaml": "github:nodeca/js-yaml@3.3.1", + "nodeca/js-yaml": "github:nodeca/js-yaml@3.5.5", "read-yaml": "npm:read-yaml@1.0.0", "systemjs/plugin-json": "github:systemjs/plugin-json@0.1.0", "three.js": "github:mrdoob/three.js@r72", + "github:Doodle3D/clipper-js@0.0.2": { + "clipper-lib": "npm:clipper-lib@1.0.0" + }, "github:jspm/nodelibs-assert@0.1.0": { "assert": "npm:assert@1.3.0" }, "github:jspm/nodelibs-path@0.1.0": { "path-browserify": "npm:path-browserify@0.0.0" }, - "github:jspm/nodelibs-process@0.1.1": { - "process": "npm:process@0.10.1" + "github:jspm/nodelibs-process@0.1.2": { + "process": "npm:process@0.11.2" }, "github:jspm/nodelibs-util@0.1.0": { "util": "npm:util@0.10.3" }, - "npm:argparse@1.0.2": { - "assert": "github:jspm/nodelibs-assert@0.1.0", + "npm:argparse@1.0.7": { "fs": "github:jspm/nodelibs-fs@0.1.2", - "lodash": "npm:lodash@3.10.1", "path": "github:jspm/nodelibs-path@0.1.0", - "process": "github:jspm/nodelibs-process@0.1.1", + "process": "github:jspm/nodelibs-process@0.1.2", "sprintf-js": "npm:sprintf-js@1.0.3", "util": "github:jspm/nodelibs-util@0.1.0" }, "npm:assert@1.3.0": { "util": "npm:util@0.10.3" }, - "npm:babel-runtime@5.8.20": { - "process": "github:jspm/nodelibs-process@0.1.1" + "npm:babel-runtime@5.8.38": { + "process": "github:jspm/nodelibs-process@0.1.2" }, "npm:clipper-lib@1.0.0": { - "process": "github:jspm/nodelibs-process@0.1.1" + "process": "github:jspm/nodelibs-process@0.1.2" }, "npm:core-js@0.9.18": { "fs": "github:jspm/nodelibs-fs@0.1.2", - "process": "github:jspm/nodelibs-process@0.1.1", + "process": "github:jspm/nodelibs-process@0.1.2", "systemjs-json": "github:systemjs/plugin-json@0.1.0" }, - "npm:esprima@2.2.0": { - "fs": "github:jspm/nodelibs-fs@0.1.2", - "process": "github:jspm/nodelibs-process@0.1.1" - }, "npm:inherits@2.0.1": { "util": "github:jspm/nodelibs-util@0.1.0" }, - "npm:js-yaml@3.3.1": { - "argparse": "npm:argparse@1.0.2", - "esprima": "npm:esprima@2.2.0", + "npm:js-yaml@3.5.5": { + "argparse": "npm:argparse@1.0.7", + "esprima": "npm:esprima@2.7.2", "fs": "github:jspm/nodelibs-fs@0.1.2", - "path": "github:jspm/nodelibs-path@0.1.0", - "process": "github:jspm/nodelibs-process@0.1.1", - "systemjs-json": "github:systemjs/plugin-json@0.1.0", - "util": "github:jspm/nodelibs-util@0.1.0" - }, - "npm:lodash@3.10.1": { - "process": "github:jspm/nodelibs-process@0.1.1" + "process": "github:jspm/nodelibs-process@0.1.2", + "systemjs-json": "github:systemjs/plugin-json@0.1.0" }, "npm:path-browserify@0.0.0": { - "process": "github:jspm/nodelibs-process@0.1.1" + "process": "github:jspm/nodelibs-process@0.1.2" + }, + "npm:process@0.11.2": { + "assert": "github:jspm/nodelibs-assert@0.1.0" }, "npm:read-yaml@1.0.0": { "fs": "github:jspm/nodelibs-fs@0.1.2", - "js-yaml": "npm:js-yaml@3.3.1", - "xtend": "npm:xtend@4.0.0" + "js-yaml": "npm:js-yaml@3.5.5", + "xtend": "npm:xtend@4.0.1" }, "npm:util@0.10.3": { "inherits": "npm:inherits@2.0.1", - "process": "github:jspm/nodelibs-process@0.1.1" + "process": "github:jspm/nodelibs-process@0.1.2" } } }); diff --git a/package.json b/package.json index 233a5cb..4424f9d 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "lib": "src" }, "dependencies": { + "Doodle3D/clipper-js": "github:Doodle3D/clipper-js@^0.0.2", "casperlamboo/EventDispatcher": "github:casperlamboo/EventDispatcher@master", "clipper-lib": "npm:clipper-lib@^1.0.0", "nodeca/js-yaml": "github:nodeca/js-yaml@^3.3.1", diff --git a/src/slice.js b/src/slice.js index c3c13d3..73902bb 100644 --- a/src/slice.js +++ b/src/slice.js @@ -1,35 +1,10 @@ -import Paths from './paths.js'; +import Shape from 'Doodle3D/clipper-js'; export default class { constructor () { this.parts = []; } - removeSelfIntersect () { - for (var i = 0; i < this.parts.length; i ++) { - var part1 = this.parts[i].intersect; - - if (!part1.closed) { - continue; - } - - for (var j = i + 1; j < this.parts.length; j ++) { - var part2 = this.parts[j].intersect; - - if (!part2.closed) { - continue; - } - - if (part2.intersect(part1).length > 0) { - part1 = this.parts[i].intersect = part1.union(part2); - - this.parts.splice(j, 1); - j --; - } - } - } - } - optimizePaths (start) { if (this.brim !== undefined && this.brim.length > 0) { this.brim = this.brim.optimizePath(start); @@ -45,11 +20,11 @@ export default class { for (var i = 0; i < this.parts.length; i ++) { var part = this.parts[i]; - if (part.intersect.closed) { + if (part.shape.closed) { var bounds = part.outerLine.bounds(); } else { - var bounds = part.intersect.bounds(); + var bounds = part.shape.bounds(); } var top = bounds.top - start.y; @@ -68,7 +43,7 @@ export default class { var part = this.parts.splice(closestPart, 1)[0]; parts.push(part); - if (part.intersect.closed) { + if (part.shape.closed) { if (part.outerLine.length > 0) { part.outerLine = part.outerLine.optimizePath(start); start = part.outerLine.lastPoint(); @@ -88,8 +63,8 @@ export default class { } } else { - part.intersect.optimizePath(start); - start = part.intersect.lastPoint(); + part.shape.optimizePath(start); + start = part.shape.lastPoint(); } } @@ -105,12 +80,12 @@ export default class { } getOutline () { - var outLines = new Paths([], true); + var outLines = new Shape([], true); for (var i = 0; i < this.parts.length; i ++) { var part = this.parts[i]; - if (part.intersect.closed) { + if (part.shape.closed) { outLines.join(this.parts[i].outerLine); } } @@ -118,17 +93,15 @@ export default class { return outLines; } - add (intersect) { - var parts = { - intersect - }; + add (shape) { + const part = { shape }; - if (intersect.closed) { - parts.innerLines = []; - parts.outerLine = new Paths([], true); - parts.fill = new Paths([], false); + if (shape.closed) { + part.innerLines = []; + part.outerLine = new Shape([], true); + part.fill = new Shape([], false); } - this.parts.push(parts); + this.parts.push(part); } -} \ No newline at end of file +} diff --git a/src/sliceActions/generateInfills.js b/src/sliceActions/generateInfills.js index b3ae254..9c23d50 100644 --- a/src/sliceActions/generateInfills.js +++ b/src/sliceActions/generateInfills.js @@ -41,7 +41,7 @@ export default function generateInfills(slices, settings) { for (var i = 0; i < slice.parts.length; i ++) { var part = slice.parts[i]; - if (!part.intersect.closed) { + if (!part.shape.closed) { continue; } diff --git a/src/sliceActions/generateInnerLines.js b/src/sliceActions/generateInnerLines.js index 006b5a6..1033ecb 100644 --- a/src/sliceActions/generateInnerLines.js +++ b/src/sliceActions/generateInnerLines.js @@ -1,10 +1,16 @@ const scale = 100; +const offsetOptions = { + jointType: 'jtSquare', + endType: 'etClosedPolygon', + miterLimit: 2.0, + roundPrecision: 0.25 +}; export default function generateInnerLines(slices, settings) { console.log("generating outer lines and inner lines"); // need to scale up everything because of clipper rounding errors - let {layerHeight, nozzleDiameter, shellThickness} = settings.config; + let { layerHeight, nozzleDiameter, shellThickness } = settings.config; nozzleDiameter *= scale; shellThickness *= scale; var nozzleRadius = nozzleDiameter / 2; @@ -16,23 +22,21 @@ export default function generateInnerLines(slices, settings) { for (var i = 0; i < slice.parts.length; i ++) { var part = slice.parts[i]; - if (!part.intersect.closed) { - continue; - } + if (!part.shape.closed) continue; - // var outerLine = part.intersect.clone().scaleUp(scale).offset(-nozzleRadius); - var outerLine = part.intersect.scaleUp(scale).offset(-nozzleRadius); + // var outerLine = part.shape.clone().scaleUp(scale).offset(-nozzleRadius); + var outerLine = part.shape.scaleUp(scale).offset(-nozzleRadius, offsetOptions); if (outerLine.length > 0) { - part.outerLine = outerLine; + part.outerLine.join(outerLine); for (var shell = 1; shell < shells; shell += 1) { var offset = shell * nozzleDiameter; - var innerLine = outerLine.offset(-offset); + var innerLine = outerLine.offset(-offset, offsetOptions); if (innerLine.length > 0) { - part.innerLines.push(innerLine); + part.innerLines.paths.push(innerLine); } else { break; diff --git a/src/sliceActions/getFillTemplate.js b/src/sliceActions/getFillTemplate.js index 034e51f..ea36f39 100644 --- a/src/sliceActions/getFillTemplate.js +++ b/src/sliceActions/getFillTemplate.js @@ -1,7 +1,7 @@ -import Paths from '../paths.js'; +import Shape from 'Doodle3D/clipper-js'; export default function getFillTemplate(bounds, size, even, uneven) { - var paths = new Paths([], false); + var shape = new Shape([], false); var left = Math.floor(bounds.left / size) * size; var right = Math.ceil(bounds.right / size) * size; @@ -12,7 +12,7 @@ export default function getFillTemplate(bounds, size, even, uneven) { if (even) { for (var y = top; y <= bottom + width; y += size) { - paths.push([ + shape.paths.push([ {X: left, Y: y}, {X: right, Y: y - width} ]); @@ -20,12 +20,12 @@ export default function getFillTemplate(bounds, size, even, uneven) { } if (uneven) { for (var y = top - width; y <= bottom; y += size) { - paths.push([ + shape.paths.push([ {X: left, Y: y}, {X: right, Y: y + width} ]); } } - return paths; + return shape; } diff --git a/src/sliceActions/intersectionsToShapes.js b/src/sliceActions/intersectionsToShapes.js index fe4f2fa..a4742f3 100644 --- a/src/sliceActions/intersectionsToShapes.js +++ b/src/sliceActions/intersectionsToShapes.js @@ -1,10 +1,10 @@ import THREE from 'three.js'; -import Paths from '../paths.js'; +import Shape from 'Doodle3D/clipper-js'; export default function intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings) { console.log("generating slices"); - var shapes = []; + var layers = []; for (var layer = 1; layer < layerIntersectionIndexes.length; layer ++) { var intersectionIndexes = layerIntersectionIndexes[layer]; @@ -14,7 +14,8 @@ export default function intersectionsToShapes(layerIntersectionIndexes, layerInt continue; } - var shapeParts = []; + var closedShapes = []; + var openShapes = []; for (var i = 0; i < intersectionIndexes.length; i ++) { var index = intersectionIndexes[i]; @@ -31,7 +32,7 @@ export default function intersectionsToShapes(layerIntersectionIndexes, layerInt while (index !== -1) { var intersection = intersectionPoints[index]; // uppercase X and Y because clipper vector - shape.push({X: intersection.x, Y: intersection.y}); + shape.push(intersection); delete intersectionPoints[index]; @@ -94,7 +95,7 @@ export default function intersectionsToShapes(layerIntersectionIndexes, layerInt while (index !== -1) { if (firstPoints.indexOf(index) === -1) { var intersection = intersectionPoints[index]; - shape.unshift({X: intersection.x, Y: intersection.y}); + shape.unshift(intersection); delete intersectionPoints[index]; } @@ -114,14 +115,16 @@ export default function intersectionsToShapes(layerIntersectionIndexes, layerInt } } - var part = new Paths([shape], closed).clean(0.01); - if (part.length > 0) { - shapeParts.push(part); + if (closed) { + closedShapes.push(shape); + } + else { + openShapes.push(shape); } } - shapes.push(shapeParts); + layers.push({ closedShapes, openShapes }); } - return shapes; + return layers; } diff --git a/src/sliceActions/optimizePaths.js b/src/sliceActions/optimizePaths.js index 9ed9b3a..7181f1f 100644 --- a/src/sliceActions/optimizePaths.js +++ b/src/sliceActions/optimizePaths.js @@ -1,4 +1,10 @@ import THREE from 'three.js'; +const offsetOptions = { + jointType: 'jtSquare', + endType: 'etClosedPolygon', + miterLimit: 2.0, + roundPrecision: 0.25 +}; export default function optimizePaths(slices, settings) { console.log("opimize paths"); @@ -14,15 +20,15 @@ export default function optimizePaths(slices, settings) { var slice = slices[layer]; if (layer === 0) { - slice.brim = slice.getOutline().offset(brimOffset); + slice.brim = slice.getOutline().offset(brimOffset, offsetOptions); } - start = slice.optimizePaths(start); + // start = slice.optimizePaths(start); for (var i = 0; i < slice.parts.length; i ++) { var part = slice.parts[i]; - if (part.intersect.closed) { + if (part.shape.closed) { part.outerLine.scaleDown(scale); for (var j = 0; j < part.innerLines.length; j ++) { var innerLine = part.innerLines[j]; diff --git a/src/sliceActions/shapesToSlices.js b/src/sliceActions/shapesToSlices.js index d261ca1..36371f9 100644 --- a/src/sliceActions/shapesToSlices.js +++ b/src/sliceActions/shapesToSlices.js @@ -1,65 +1,37 @@ +import Shape from 'Doodle3D/clipper-js'; import Slice from '../slice.js'; export default function shapesToSlices(shapes, settings) { - var slices = []; + const sliceLayers = []; for (var layer = 0; layer < shapes.length; layer ++) { - var shapeParts = shapes[layer]; + var { closedShapes, openShapes } = shapes[layer]; + + closedShapes = new Shape(closedShapes, true, true) + .clean(0.01) + .fixOrientation() + .removeOverlap() + .seperateShapes(); + + openShapes = new Shape(openShapes, false, true) + .clean(0.01); var slice = new Slice(); - var holes = []; - var outlines = []; + for (var i = 0; i < closedShapes.length; i ++) { + var closedShape = closedShapes[i]; + slice.add(closedShape); - 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); - } + // if (openShapes.path.length > 0) { + // openShapes = openShapes.difference(closedShape); + // } + } + if (openShapes.paths.length > 0) { + slice.add(openShapes); } - 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); + sliceLayers.push(slice); } - return slices; + return sliceLayers; } diff --git a/src/sliceActions/slicesToGCode.js b/src/sliceActions/slicesToGCode.js index 6d9ab1a..5abe6f5 100644 --- a/src/sliceActions/slicesToGCode.js +++ b/src/sliceActions/slicesToGCode.js @@ -3,15 +3,14 @@ import GCode from '../gcode.js'; export default function slicesToGCode(slices, settings) { var gcode = new GCode().setSettings(settings); - function pathToGCode (path, retract, unRetract, type) { + function pathToGCode (shape, retract, unRetract, type) { + for (var i = 0; i < shape.paths.length; i ++) { + var line = shape.paths[i]; - for (var i = 0; i < path.length; i ++) { - var shape = path[i]; - - var length = path.closed ? (shape.length + 1) : shape.length; + var length = shape.closed ? (line.length + 1) : line.length; for (var j = 0; j < length; j ++) { - var point = shape[j % shape.length]; + var point = line[j % line.length]; if (j === 0) { // TODO @@ -48,7 +47,7 @@ export default function slicesToGCode(slices, settings) { for (var i = 0; i < slice.parts.length; i ++) { var part = slice.parts[i]; - if (part.intersect.closed) { + if (part.shape.closed) { pathToGCode(part.outerLine, false, true, "outerLine"); for (var j = 0; j < part.innerLines.length; j ++) { @@ -60,7 +59,7 @@ export default function slicesToGCode(slices, settings) { } else { var retract = !(slice.parts.length === 1 && slice.support === undefined); - pathToGCode(part.intersect, retract, retract, "outerLine"); + pathToGCode(part.shape, retract, retract, "outerLine"); } } diff --git a/src/slicer.js b/src/slicer.js index ceae634..536d4d0 100644 --- a/src/slicer.js +++ b/src/slicer.js @@ -67,15 +67,18 @@ export default class extends EventDispatcher { this.progress.createdLines = true; this._updateProgress(settings); - var {layerIntersectionIndexes, layerIntersectionPoints} = calculateLayersIntersections(lines, settings); + const { + layerIntersectionIndexes, + layerIntersectionPoints + } = calculateLayersIntersections(lines, settings); this.progress.calculatedLayerIntersections = true; this._updateProgress(settings); - var shapes = intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings); + const shapes = intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings); this.progress.sliced = true; this._updateProgress(settings); - var slices = shapesToSlices(shapes, settings); + const slices = shapesToSlices(shapes, settings); this.progress.generatedSlices = true; this._updateProgress(settings);