diff --git a/src/clipper.js b/library/clipper.js similarity index 99% rename from src/clipper.js rename to library/clipper.js index 22aa676..5fb0652 100755 --- a/src/clipper.js +++ b/library/clipper.js @@ -3100,7 +3100,7 @@ if (this.m_ExecuteLocked) return false; if (this.m_HasOpenPaths) - ClipperLib.Error("Error: PolyTree struct is need for open path clipping."); + //ClipperLib.Error("Error: PolyTree struct is need for open path clipping."); this.m_ExecuteLocked = true; ClipperLib.Clear(solution); this.m_SubjFillType = subjFillType; diff --git a/slice_test.html b/slice_test.html index 4d31074..d159749 100644 --- a/slice_test.html +++ b/slice_test.html @@ -6,6 +6,7 @@ + @@ -27,22 +28,46 @@ canvas {border: 1px solid black;} var localIp = location.hash.substring(1); var doodleBox = new D3D.Box(localIp); +var gcode; doodleBox.onload = function () { - "use strict"; + doodleBox.printer.config["printer.bottomFlowRate"] = 1.0; + doodleBox.printer.config["printer.layerHeight"] = 0.3; + doodleBox.printer.config["printer.retraction.enabled"] = false; - /*var gcode = slicer.getGcode(doodleBox.printer); + gcode = slicer.getGcode(doodleBox.printer.config); - var print = $(document.createElement("a")).html("Print").on("click", function () { - doodleBox.print(gcode); - }); - var stop = $(document.createElement("a")).html("Stop").on("click", function () { - doodleBox.stop(); - }); - var download = $(document.createElement("a")).html("Download").attr({ - download: "test.gcode", - href: "data:text/plain," + encodeURIComponent(gcode.join("\n")) - }); - $("body").append(print, stop, download);*/ + var canvas = document.getElementById("canvas"); + var context = canvas.getContext("2d"); + + function drawPolygons (paths, color) { + context.fillStyle = color; + context.strokeStyle = color; + context.beginPath(); + + for (var i = 0; i < paths.length; i ++) { + var path = paths[i]; + + context.moveTo((path[0].X- 100) * 8.0 + 200, (path[0].Y- 100) * 8.0 + 200); + + for (var j = 0; j < path.length; j ++) { + var point = path[j]; + context.lineTo((point.X- 100) * 8.0 + 200, (point.Y- 100) * 8.0 + 200); + } + context.closePath(); + } + context.stroke(); + } + + //for (var layer = 0; layer < gcode.length; layer ++) { + var layer = 0; + var slice = gcode[layer]; + + console.log(gcode.length); + + drawPolygons(slice.outerLayer, "red"); + drawPolygons(slice.innerLayer, "green"); + drawPolygons(slice.fill, "blue"); + //}*/ }; var scene = new THREE.Scene(); @@ -54,35 +79,69 @@ var camera = new THREE.PerspectiveCamera(75, renderer.domElement.width/renderer. applyMouseControls(renderer, camera, 1000); +var geometry = (function () { + var circle = new THREE.Shape(); + circle.absarc(0, 0, 20, 0, Math.PI*2, false); + + var hole = new THREE.Path(); + hole.absarc(0, 0, 10, 0, Math.PI*2, true ); + + circle.holes.push(hole); + + var matrix = new THREE.Matrix4(); + matrix.makeRotationX(Math.PI*1.5); + + var geometry = new THREE.ExtrudeGeometry(circle, { + amount: 1, + bevelEnabled: false, + steps: 1 + }); + geometry.applyMatrix(matrix); + + return geometry; +})(); + var material = new THREE.MeshLambertMaterial({color: 0x000000, wireframe: true}); -var geometry = new THREE.TorusGeometry(20, 10, 10, 10); +//var geometry = new THREE.TorusGeometry(40, 20, 10, 10); //var geometry = new THREE.BoxGeometry(10, 10, 10, 1, 1, 1); -//var geometry = new THREE.SphereGeometry(40, 10, 10); +//var geometry = new THREE.SphereGeometry(10, 10, 10); var mesh = new THREE.Mesh(geometry, material); scene.add(mesh); +var slicer = new D3D.Slicer().setGeometry(geometry); + +var canvas = document.getElementById("canvas"); +var context = canvas.getContext("2d"); + +var slicer = new D3D.Slicer().setGeometry(geometry); +//slicer.draw(1, context); + +/* (function () { var slicer = new D3D.Slicer().setGeometry(geometry); var slices = slicer.slice(200, 0.2); + slices.shift(); //var slices = slicer.slice(1, 1); CAL.Scene.setCanvas(document.getElementById("canvas")); //error at layer 0; //maybe because of geomety - var layer = 4; + var layer = 0; var shapes = []; var slice = slices[layer]; + //downloadFile("slice.json", JSON.stringify(slice)); + for (var i = 0; i < slice.length; i ++) { var shape = new CAL.Shape({shapeColor: false}); shapes.push(shape); for (var j = 0; j < slice[i].length; j ++) { var point = slice[i][j]; - shape.addPoint(new CAL.Vector((point.x-100) * 5 + 200, (point.y-100) * 5 + 200)); + shape.addPoint(new CAL.Vector(point.X, point.Y)); } } @@ -100,6 +159,7 @@ scene.add(mesh); } } })(); +*/ (function animate () { requestAnimationFrame(animate); diff --git a/src/box.js b/src/box.js index 55cdddb..2706725 100644 --- a/src/box.js +++ b/src/box.js @@ -20,6 +20,8 @@ D3D.Box = function (localIp) { this.localIp = localIp; this.api = "http://" + localIp + "/d3dapi/"; + this.config = {}; + this.printBatches = []; this.currentBatch = 0; @@ -31,7 +33,7 @@ D3D.Box = function (localIp) { for (var i in data) { if (i.indexOf("doodle3d") === 0) { - self[i] = data[i]; + self.config[i] = data[i]; } } @@ -65,7 +67,7 @@ D3D.Box.prototype.updateState = function () { //que api calls so they don't overload the d3d box getAPI(this.api + "info/status", function (data) { - self.printer.data = data; + self.printer.status = data; self.update(); }); diff --git a/src/printer.js b/src/printer.js index 49eaf9b..882c240 100644 --- a/src/printer.js +++ b/src/printer.js @@ -8,18 +8,19 @@ D3D.Printer = function (config) { "use strict"; - this.data = {}; + this.status = {}; + this.config = {}; for (var i in config) { if (i.indexOf("printer") === 0) { - this[i] = config[i]; + this.config[i] = config[i]; } } }; D3D.Printer.prototype.getStartCode = function () { "use strict"; - var gcode = this["printer.startcode"]; + var gcode = this.config["printer.startcode"]; gcode = this.subsituteVariables(gcode); return gcode.split("\n"); @@ -27,7 +28,7 @@ D3D.Printer.prototype.getStartCode = function () { D3D.Printer.prototype.getEndCode = function () { "use strict"; - var gcode = this["printer.endcode"]; + var gcode = this.config["printer.endcode"]; gcode = this.subsituteVariables(gcode); return gcode.split("\n"); @@ -35,12 +36,12 @@ D3D.Printer.prototype.getEndCode = function () { D3D.Printer.prototype.subsituteVariables = function (gcode) { "use strict"; - var temperature = this["printer.temperature"]; - var bedTemperature = this["printer.bed.temperature"]; - var preheatTemperature = this["printer.heatup.temperature"]; - var preheatBedTemperature = this["printer.heatup.bed.temperature"]; - var printerType = this["printer.type"]; - var heatedbed = this["printer.heatedbed"]; + var temperature = this.config["printer.temperature"]; + var bedTemperature = this.config["printer.bed.temperature"]; + var preheatTemperature = this.config["printer.heatup.temperature"]; + var preheatBedTemperature = this.config["printer.heatup.bed.temperature"]; + var printerType = this.config["printer.type"]; + var heatedbed = this.config["printer.heatedbed"]; switch (printerType) { case "makerbot_replicator2": printerType = "r2"; break; diff --git a/src/slicer.js b/src/slicer.js index 3e1811d..3a473f1 100644 --- a/src/slicer.js +++ b/src/slicer.js @@ -5,8 +5,13 @@ * TODO (optimalisatie) * sorteer lijnen op laagste hoogte -> stop loop wanneer hij een lijn zonder intersectie heeft gevonden * verwijder lijnen die ooit interactie gehad hebben, maar nu niet meer +* helft van lijnen toevoegen omdat 4face altijd recht is, en 3face dus te veel data bevat +* +* omliggende lagen -> difference && sum omliggende lijnen +* voor laag 5 = 5 diff (3 && 4 && 6 && 7)) * ******************************************************/ +var use_deprecated = true; D3D.Slicer = function () { "use strict"; @@ -69,9 +74,9 @@ D3D.Slicer.prototype.createLines = function () { this.lines[b].connects.push(c, a); this.lines[c].connects.push(a, b); - this.lines[a].normals.push(normal, normal); - this.lines[b].normals.push(normal, normal); - this.lines[c].normals.push(normal, normal); + this.lines[a].normals.push(normal); + this.lines[b].normals.push(normal); + this.lines[c].normals.push(normal); } //sort lines on min height @@ -119,7 +124,7 @@ D3D.Slicer.prototype.slice = function (height, step) { while (index !== -1) { var intersection = intersections[index]; - shape.push(intersection); + shape.push({X: intersection.x, Y: intersection.y}); done.push(index); @@ -130,7 +135,7 @@ D3D.Slicer.prototype.slice = function (height, step) { if (intersections[index] && done.indexOf(index) === -1) { var normal = new THREE.Vector2().copy(intersection).sub(intersections[index]).normal().normalize(); - var faceNormal = faceNormals[j]; + var faceNormal = faceNormals[Math.floor(j/2)]; if (normal.dot(faceNormal) > 0) { break; @@ -152,6 +157,7 @@ D3D.Slicer.prototype.slice = function (height, step) { } } + //stop when ther are no intersects if (slice.length > 0) { slices.push(slice); } @@ -162,44 +168,128 @@ D3D.Slicer.prototype.slice = function (height, step) { return slices; }; -D3D.Slicer.prototype.getGcode = function (printer) { +D3D.Slicer.prototype.getInset = function (slice, offset) { "use strict"; - var normalSpeed = doodleBox.printer["printer.speed"]; - var bottomSpeed = doodleBox.printer["printer.bottomLayerSpeed"]; - var firstLayerSlow = doodleBox.printer["printer.firstLayerSlow"]; - var bottomFlowRate = doodleBox.printer["printer.bottomFlowRate"]; - var travelSpeed = doodleBox.printer["printer.travelSpeed"]; - var filamentThickness = doodleBox.printer["printer.filamentThickness"]; - var wallThickness = doodleBox.printer["printer.wallThickness"]; - var layerHeight = doodleBox.printer["printer.layerHeight"]; - var enableTraveling = doodleBox.printer["printer.enableTraveling"]; - var retractionEnabled = doodleBox.printer["printer.retraction.enabled"]; - var retractionSpeed = doodleBox.printer["printer.retraction.speed"]; - var retractionminDistance = doodleBox.printer["printer.retraction.minDistance"]; - var retractionAmount = doodleBox.printer["printer.retraction.amount"]; - var dimensionsZ = doodleBox.printer["printer.dimensions.z"]; + var solution = new ClipperLib.Paths(); + var co = new ClipperLib.ClipperOffset(1, 1); + co.AddPaths(slice, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon); + co.Execute(solution, -offset); - var gcode = doodleBox.printer.getStartCode(); + return solution; +}; +D3D.Slicer.prototype.getFillTemplate = function (dimension, size, even, uneven) { + var paths = new ClipperLib.Paths(); - var extruder = 0.0; - var speed = firstLayerSlow ? (bottomSpeed*60).toFixed(3) : (normalSpeed*60).toFixed(3); - var flowRate = bottomFlowRate; - var filamentSurfaceArea = Math.pow((filamentThickness/2), 2) * Math.PI; + if (even) { + for (var length = 0; length <= dimension; length += size) { + paths.push([{X: length, Y: 0}, {X: length, Y: dimension}]); + } + } + if (uneven) { + for (var length = 0; length <= dimension; length += size) { + paths.push([{X: 0, Y: length}, {X: dimension, Y: length}]); + } + } + + return paths; +}; +D3D.Slicer.prototype.slicesToData = function (slices, config) { + var data = []; - var slices = this.slice(dimensionsZ, layerHeight); + //scale because of clipper crap + var scale = 100; + + var layerHeight = config["printer.layerHeight"] * scale; + var dimensionsZ = config["printer.dimensions.z"] * scale; + //should come from config??? + //aan rick voorleggen + var nozzleSize = 0.4 * scale; + var shellThickness = 0.8 * scale; + var fillSize = 5 * scale; + + var lowFillTemplate = this.getFillTemplate(dimensionsZ, fillSize, true, true); + for (var layer = 0; layer < slices.length; layer ++) { var slice = slices[layer]; + var highFillTemplate = this.getFillTemplate(dimensionsZ, nozzleSize*2, (layer % 2 === 0), (layer % 2 === 1)); - //turn on fan on layer 2 - if (layer === 2) { - gcode.push("M106"); - speed = (normalSpeed*60).toFixed(3); - flowRate = 1; + //var outerLayer = ClipperLib.JS.Clean(slice, 1.0); + var outerLayer = slice.clone(); + ClipperLib.JS.ScaleUpPaths(outerLayer, scale); + + var innerLayer = []; + + for (var i = nozzleSize; i < shellThickness; i += nozzleSize) { + var inset = this.getInset(outerLayer, i); + + innerLayer = innerLayer.concat(inset); } - var z = ((layer + 1) * layerHeight).toFixed(3); + var fillArea = this.getInset((inset || outerLayer), nozzleSize); + + var highFill; + + var fillAbove; + //for (var i = 1; i < shellThickness/layerHeight; i ++) { + var newLayer = ClipperLib.JS.Clone(slices[layer + 1] || []); + ClipperLib.JS.ScaleUpPaths(newLayer, scale); + fillAbove = newLayer; + //} + //kijkt alleen nog naar boven + //omliggende lagen hebben inhoud van lowFill; + //inset moet opgevult worden; + //verschill tussen lowFill en inset moet vol, rest is raster + + var c = new ClipperLib.Clipper(); + var highFillArea = new ClipperLib.Paths(); + c.AddPaths(fillArea, ClipperLib.PolyType.ptSubject, true); + c.AddPaths(fillAbove, ClipperLib.PolyType.ptClip, true); + c.Execute(ClipperLib.ClipType.ctDifference, highFillArea); + + var c = new ClipperLib.Clipper(); + var lowFillArea = new ClipperLib.Paths(); + c.AddPaths(fillArea, ClipperLib.PolyType.ptSubject, true); + c.AddPaths(highFillArea, ClipperLib.PolyType.ptClip, true); + c.Execute(ClipperLib.ClipType.ctDifference, lowFillArea); + + var fill = []; + + var c = new ClipperLib.Clipper(); + var solution = new ClipperLib.Paths(); + c.AddPaths(lowFillTemplate, ClipperLib.PolyType.ptSubject, false); + c.AddPaths(lowFillArea, ClipperLib.PolyType.ptClip, true); + c.Execute(ClipperLib.ClipType.ctIntersection, solution); + + fill = fill.concat(solution); + + var c = new ClipperLib.Clipper(); + var solution = new ClipperLib.Paths(); + c.AddPaths(highFillTemplate, ClipperLib.PolyType.ptSubject, false); + c.AddPaths(highFillArea, ClipperLib.PolyType.ptClip, true); + c.Execute(ClipperLib.ClipType.ctIntersection, solution); + + fill = fill.concat(solution); + + ClipperLib.JS.ScaleDownPaths(outerLayer, scale); + ClipperLib.JS.ScaleDownPaths(innerLayer, scale); + ClipperLib.JS.ScaleDownPaths(fill, scale); + + data.push({ + outerLayer: outerLayer, + innerLayer: innerLayer, + fill: fill + }) + } + + return data; +}; +D3D.Slicer.prototype.getGcode = function (config) { + "use strict"; + + function sliceToGcode (slice) { + var gcode = []; for (var i = 0; i < slice.length; i ++) { var shape = slice[i]; @@ -213,7 +303,7 @@ D3D.Slicer.prototype.getGcode = function (printer) { if (j === 0) { //TODO //add retraction - if (extruder > retractionAmount && retractionEnabled) { + if (extruder > retractionMinDistance && retractionEnabled) { gcode.push([ "G0", "E" + (extruder - retractionAmount).toFixed(3), @@ -223,11 +313,11 @@ D3D.Slicer.prototype.getGcode = function (printer) { gcode.push([ "G0", - "X" + point.x.toFixed(3) + " Y" + point.y.toFixed(3) + " Z" + z, + "X" + point.X.toFixed(3) + " Y" + point.Y.toFixed(3) + " Z" + z, "F" + (travelSpeed*60) ].join(" ")); - if (extruder > retractionAmount && retractionEnabled) { + if (extruder > retractionMinDistance && retractionEnabled) { gcode.push([ "G0", "E" + extruder.toFixed(3), @@ -236,12 +326,15 @@ D3D.Slicer.prototype.getGcode = function (printer) { } } else { - var lineLength = new THREE.Vector2().copy(point).sub(previousPoint).length(); + var a = new THREE.Vector2().set(point.X, point.Y); + var b = new THREE.Vector2().set(previousPoint.X, previousPoint.Y); + var lineLength = a.distanceTo(b); + extruder += lineLength * wallThickness * layerHeight / filamentSurfaceArea * flowRate; gcode.push([ "G1", - "X" + point.x.toFixed(3) + " Y" + point.y.toFixed(3) + " Z" + z, + "X" + point.X.toFixed(3) + " Y" + point.Y.toFixed(3) + " Z" + z, "F" + speed, "E" + extruder.toFixed(3) ].join(" ")); @@ -250,6 +343,60 @@ D3D.Slicer.prototype.getGcode = function (printer) { previousPoint = point; } } + + return gcode; + } + + var normalSpeed = config["printer.speed"]; + var bottomSpeed = config["printer.bottomLayerSpeed"]; + var firstLayerSlow = config["printer.firstLayerSlow"]; + var bottomFlowRate = config["printer.bottomFlowRate"]; + var travelSpeed = config["printer.travelSpeed"]; + var filamentThickness = config["printer.filamentThickness"]; + var wallThickness = config["printer.wallThickness"]; + var layerHeight = config["printer.layerHeight"]; + var enableTraveling = config["printer.enableTraveling"]; + var retractionEnabled = config["printer.retraction.enabled"]; + var retractionSpeed = config["printer.retraction.speed"]; + var retractionMinDistance = config["printer.retraction.minDistance"]; + var retractionAmount = config["printer.retraction.amount"]; + var dimensionsZ = config["printer.dimensions.z"]; + + var gcode = doodleBox.printer.getStartCode(); + + var extruder = 0.0; + var speed = firstLayerSlow ? (bottomSpeed*60).toFixed(3) : (normalSpeed*60).toFixed(3); + var filamentSurfaceArea = Math.pow((filamentThickness/2), 2) * Math.PI; + var flowRate = bottomFlowRate; + + var slices = []; + + var slices = this.slice(dimensionsZ, layerHeight); + //still error in first layer, so remove first layer + //see https://github.com/Doodle3D/Doodle3D-Slicer/issues/1 + slices.shift(); + + //code for only printing the first layer + //var slices = [slices.shift()]; + + var data = this.slicesToData(slices, config); + //return data; + + for (var layer = 0; layer < data.length; layer ++) { + var slice = data[layer]; + + //turn on fan on layer 2 + if (layer === 2) { + gcode.push("M106"); + speed = (normalSpeed*60).toFixed(3); + flowRate = 1; + } + + var z = ((layer + 1) * layerHeight).toFixed(3); + + gcode = gcode.concat(sliceToGcode(slice.outerLayer)); + gcode = gcode.concat(sliceToGcode(slice.innerLayer)); + gcode = gcode.concat(sliceToGcode(slice.fill)); } gcode = gcode.concat(doodleBox.printer.getEndCode());