mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-11-19 12:27:56 +01:00
First commit
This commit is contained in:
parent
2b925cd17a
commit
921708331d
BIN
src/.DS_Store
vendored
Normal file
BIN
src/.DS_Store
vendored
Normal file
Binary file not shown.
150
src/doodle.html
Normal file
150
src/doodle.html
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Doedel Drie Dee</title>
|
||||||
|
<!--<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>-->
|
||||||
|
<script src="library/jquery.js"></script>
|
||||||
|
<script src="library/cal.js"></script>
|
||||||
|
<script src="library/three.js"></script>
|
||||||
|
|
||||||
|
<script src="script/utils.js"></script>
|
||||||
|
<script src="script/box.js"></script>
|
||||||
|
<script src="script/printer.js"></script>
|
||||||
|
<script src="script/slicer.js"></script>
|
||||||
|
|
||||||
|
<script src="gcode/testgcode.js"></script>
|
||||||
|
<script src="gcode/easterbunny.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var localIp = location.hash.substring(1);
|
||||||
|
var doodleBox, drawing;
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
doodleBox = new D3D.Box(localIp);
|
||||||
|
|
||||||
|
CAL.Scene.setCanvas(document.getElementById("canvas"));
|
||||||
|
|
||||||
|
var shape = new CAL.Shape({
|
||||||
|
shapeColor: false,
|
||||||
|
closePath: false,
|
||||||
|
points: [
|
||||||
|
new CAL.Vector(20, 180),
|
||||||
|
new CAL.Vector(180, 100),
|
||||||
|
new CAL.Vector(20, 80),
|
||||||
|
new CAL.Vector(180, 60)
|
||||||
|
]
|
||||||
|
});
|
||||||
|
CAL.Scene.add(shape);
|
||||||
|
|
||||||
|
drawing = new Drawing(shape);
|
||||||
|
CAL.Scene.add(drawing);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function Drawing (shape) {
|
||||||
|
this.shape = shape;
|
||||||
|
|
||||||
|
this.active = true;
|
||||||
|
this.mouse = false;
|
||||||
|
}
|
||||||
|
Drawing.prototype.mouseDown = function (mouse) {
|
||||||
|
this.mouse = mouse;
|
||||||
|
};
|
||||||
|
Drawing.prototype.mouseUp = function (mouse) {
|
||||||
|
this.mouse = false;
|
||||||
|
};
|
||||||
|
Drawing.prototype.generateGcode = function () {
|
||||||
|
var gcode = doodleBox.printer.getStartCode();
|
||||||
|
|
||||||
|
var extruder = 0.0;
|
||||||
|
var points = this.shape.points;
|
||||||
|
|
||||||
|
var normalSpeed = doodleBox.printer["printer.speed"] * 60;
|
||||||
|
var bottomSpeed = doodleBox.printer["printer.bottomLayerSpeed"] * 60;
|
||||||
|
var firstLayerSlow = doodleBox.printer["printer.firstLayerSlow"] * 60;
|
||||||
|
var bottomFlowRate = doodleBox.printer["printer.bottomFlowRate"];
|
||||||
|
var travelSpeed = doodleBox.printer["printer.travelSpeed"] * 60;
|
||||||
|
var filamentThickness = doodleBox.printer["printer.filamentThickness"];
|
||||||
|
var wallThickness = doodleBox.printer["printer.wallThickness"];
|
||||||
|
var screenToMillimeterScale = doodleBox.printer["printer.screenToMillimeterScale"];
|
||||||
|
var layerHeight = doodleBox.printer["printer.layerHeight"];
|
||||||
|
var useSubLayers = doodleBox.printer["printer.useSubLayers"];
|
||||||
|
var enableTraveling = doodleBox.printer["printer.enableTraveling"];
|
||||||
|
var retractionEnabled = doodleBox.printer["printer.retraction.enabled"];
|
||||||
|
var retractionspeed = doodleBox.printer["printer.retraction.speed"] * 60;
|
||||||
|
var retractionminDistance = doodleBox.printer["printer.retraction.minDistance"];
|
||||||
|
var retractionamount = doodleBox.printer["printer.retraction.amount"];
|
||||||
|
|
||||||
|
var speed = bottomSpeed.toFixed(3);
|
||||||
|
|
||||||
|
for (var layer = 0; layer < 10; layer ++) {
|
||||||
|
var lastPoint;
|
||||||
|
|
||||||
|
//turn on fan on layer 2
|
||||||
|
if (layer === 2) {
|
||||||
|
gcode.push("M106");
|
||||||
|
speed = normalSpeed.toFixed(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < points.length; i ++) {
|
||||||
|
var point = points[i];
|
||||||
|
|
||||||
|
var x = point.x.toFixed(3);
|
||||||
|
var y = point.y.toFixed(3);
|
||||||
|
var z = ((layer + 1) * layerHeight).toFixed(3);
|
||||||
|
|
||||||
|
if (i === 0) {
|
||||||
|
if (layer >= 2 && retractionEnabled) {
|
||||||
|
gcode.push("G0 E" + (extruder - retractionamount).toFixed(3) + " F" + (retractionspeed * 60).toFixed(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
gcode.push("G0 X" + x + " Y" + y + " Z" + z + " F" + travelSpeed);
|
||||||
|
|
||||||
|
if (layer >= 2 && retractionEnabled) {
|
||||||
|
gcode.push("G0 E" + extruder.toFixed(3) + " F" + (retractionspeed * 60).toFixed(3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var distance = point.subtract(lastPoint).length();
|
||||||
|
|
||||||
|
var flowRate = (layer < 2) ? bottomFlowRate : 1;
|
||||||
|
|
||||||
|
var filamentSurfaceArea = Math.pow((filamentThickness/2), 2) * Math.PI;
|
||||||
|
|
||||||
|
//extruder code from gcodeGenerating.js
|
||||||
|
extruder += distance * wallThickness * layerHeight / filamentSurfaceArea * flowRate;
|
||||||
|
|
||||||
|
gcode.push("G1 X" + x + " Y" + y + " Z" + z + " F" + speed + " E" + extruder.toFixed(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPoint = point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gcode = gcode.concat(doodleBox.printer.getEndCode());
|
||||||
|
return gcode;
|
||||||
|
};
|
||||||
|
Drawing.prototype.step = function (dt, group) {
|
||||||
|
if (this.mouse) {
|
||||||
|
this.shape.addPoint(new CAL.Vector(this.mouse.x, this.mouse.y));
|
||||||
|
|
||||||
|
group.drawCanvas = true;
|
||||||
|
group.clearCanvas = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(function loop () {
|
||||||
|
requestAnimFrame(loop);
|
||||||
|
CAL.Scene.cycle();
|
||||||
|
})();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<canvas id="canvas" width="200" height="200"></canvas>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
src/favicon.ico
Normal file
BIN
src/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
src/favicon.png
Normal file
BIN
src/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 814 B |
BIN
src/favicon_alt.ico
Normal file
BIN
src/favicon_alt.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 97 KiB |
172520
src/gcode/easterbunny.js
Normal file
172520
src/gcode/easterbunny.js
Normal file
File diff suppressed because it is too large
Load Diff
78309
src/gcode/testgcode.js
Normal file
78309
src/gcode/testgcode.js
Normal file
File diff suppressed because it is too large
Load Diff
66
src/index.html
Normal file
66
src/index.html
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Doedel Drie Dee</title>
|
||||||
|
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||||
|
<script src="script/utils.js"></script>
|
||||||
|
<script src="script/box.js"></script>
|
||||||
|
<script src="script/printer.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var api = "http://connect.doodle3d.com/api/";
|
||||||
|
var printers = [];
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var listDoodle = $("#printers-doodle");
|
||||||
|
var listSliceTest = $("#printers-slicetest");
|
||||||
|
|
||||||
|
listDoodle.append("<li><a href='doodle.html#192.168.5.1'>Wired Printer</a></li>");
|
||||||
|
listSliceTest.append("<li><a href='doodle.html#192.168.5.1'>Wired Printer</a></li>");
|
||||||
|
|
||||||
|
/*printers.push({
|
||||||
|
name: "wired box",
|
||||||
|
d3dbox: new D3D.Box("192.168.5.1")
|
||||||
|
});*/
|
||||||
|
|
||||||
|
getAPI(api + "list.php", function (boxes) {
|
||||||
|
for (var i = 0; i < boxes.length; i ++) {
|
||||||
|
var box = boxes[i];
|
||||||
|
|
||||||
|
/*printers.push({
|
||||||
|
name: box.wifiboxid,
|
||||||
|
d3dbox: new D3D.Box(box.localip)
|
||||||
|
});*/
|
||||||
|
|
||||||
|
listDoodle.append("<li><a href='doodle.html#" + box.localip + "'>" + box.wifiboxid + "</a></li>");
|
||||||
|
listSliceTest.append("<li><a href='slice_test.html#" + box.localip + "'>" + box.wifiboxid + "</a></li>");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p>Doodle</p>
|
||||||
|
<ul id="printers-doodle"></ul>
|
||||||
|
|
||||||
|
<p>Slice Test</p>
|
||||||
|
<ul id="printers-slicetest"></ul>
|
||||||
|
<!--<table>
|
||||||
|
<tr>
|
||||||
|
<th>localip</th>
|
||||||
|
<th>wifiboxid</th>
|
||||||
|
<th>date</th>
|
||||||
|
</tr>
|
||||||
|
<tr style="display: none">
|
||||||
|
<td>localip</td>
|
||||||
|
<td>wifiboxid</td>
|
||||||
|
<td>date</td>
|
||||||
|
</tr>
|
||||||
|
</table>-->
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
src/library/.DS_Store
vendored
Normal file
BIN
src/library/.DS_Store
vendored
Normal file
Binary file not shown.
2329
src/library/cal.js
Normal file
2329
src/library/cal.js
Normal file
File diff suppressed because it is too large
Load Diff
595
src/library/csg.js
Executable file
595
src/library/csg.js
Executable file
@ -0,0 +1,595 @@
|
|||||||
|
// Constructive Solid Geometry (CSG) is a modeling technique that uses Boolean
|
||||||
|
// operations like union and intersection to combine 3D solids. This library
|
||||||
|
// implements CSG operations on meshes elegantly and concisely using BSP trees,
|
||||||
|
// and is meant to serve as an easily understandable implementation of the
|
||||||
|
// algorithm. All edge cases involving overlapping coplanar polygons in both
|
||||||
|
// solids are correctly handled.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// var cube = CSG.cube();
|
||||||
|
// var sphere = CSG.sphere({ radius: 1.3 });
|
||||||
|
// var polygons = cube.subtract(sphere).toPolygons();
|
||||||
|
//
|
||||||
|
// ## Implementation Details
|
||||||
|
//
|
||||||
|
// All CSG operations are implemented in terms of two functions, `clipTo()` and
|
||||||
|
// `invert()`, which remove parts of a BSP tree inside another BSP tree and swap
|
||||||
|
// solid and empty space, respectively. To find the union of `a` and `b`, we
|
||||||
|
// want to remove everything in `a` inside `b` and everything in `b` inside `a`,
|
||||||
|
// then combine polygons from `a` and `b` into one solid:
|
||||||
|
//
|
||||||
|
// a.clipTo(b);
|
||||||
|
// b.clipTo(a);
|
||||||
|
// a.build(b.allPolygons());
|
||||||
|
//
|
||||||
|
// The only tricky part is handling overlapping coplanar polygons in both trees.
|
||||||
|
// The code above keeps both copies, but we need to keep them in one tree and
|
||||||
|
// remove them in the other tree. To remove them from `b` we can clip the
|
||||||
|
// inverse of `b` against `a`. The code for union now looks like this:
|
||||||
|
//
|
||||||
|
// a.clipTo(b);
|
||||||
|
// b.clipTo(a);
|
||||||
|
// b.invert();
|
||||||
|
// b.clipTo(a);
|
||||||
|
// b.invert();
|
||||||
|
// a.build(b.allPolygons());
|
||||||
|
//
|
||||||
|
// Subtraction and intersection naturally follow from set operations. If
|
||||||
|
// union is `A | B`, subtraction is `A - B = ~(~A | B)` and intersection is
|
||||||
|
// `A & B = ~(~A | ~B)` where `~` is the complement operator.
|
||||||
|
//
|
||||||
|
// ## License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011 Evan Wallace (http://madebyevan.com/), under the MIT license.
|
||||||
|
|
||||||
|
// # class CSG
|
||||||
|
|
||||||
|
// Holds a binary space partition tree representing a 3D solid. Two solids can
|
||||||
|
// be combined using the `union()`, `subtract()`, and `intersect()` methods.
|
||||||
|
|
||||||
|
CSG = function() {
|
||||||
|
this.polygons = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Construct a CSG solid from a list of `CSG.Polygon` instances.
|
||||||
|
CSG.fromPolygons = function(polygons) {
|
||||||
|
var csg = new CSG();
|
||||||
|
csg.polygons = polygons;
|
||||||
|
return csg;
|
||||||
|
};
|
||||||
|
|
||||||
|
CSG.prototype = {
|
||||||
|
clone: function() {
|
||||||
|
var csg = new CSG();
|
||||||
|
csg.polygons = this.polygons.map(function(p) { return p.clone(); });
|
||||||
|
return csg;
|
||||||
|
},
|
||||||
|
|
||||||
|
toPolygons: function() {
|
||||||
|
return this.polygons;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Return a new CSG solid representing space in either this solid or in the
|
||||||
|
// solid `csg`. Neither this solid nor the solid `csg` are modified.
|
||||||
|
//
|
||||||
|
// A.union(B)
|
||||||
|
//
|
||||||
|
// +-------+ +-------+
|
||||||
|
// | | | |
|
||||||
|
// | A | | |
|
||||||
|
// | +--+----+ = | +----+
|
||||||
|
// +----+--+ | +----+ |
|
||||||
|
// | B | | |
|
||||||
|
// | | | |
|
||||||
|
// +-------+ +-------+
|
||||||
|
//
|
||||||
|
union: function(csg) {
|
||||||
|
var a = new CSG.Node(this.clone().polygons);
|
||||||
|
var b = new CSG.Node(csg.clone().polygons);
|
||||||
|
a.clipTo(b);
|
||||||
|
b.clipTo(a);
|
||||||
|
b.invert();
|
||||||
|
b.clipTo(a);
|
||||||
|
b.invert();
|
||||||
|
a.build(b.allPolygons());
|
||||||
|
return CSG.fromPolygons(a.allPolygons());
|
||||||
|
},
|
||||||
|
|
||||||
|
// Return a new CSG solid representing space in this solid but not in the
|
||||||
|
// solid `csg`. Neither this solid nor the solid `csg` are modified.
|
||||||
|
//
|
||||||
|
// A.subtract(B)
|
||||||
|
//
|
||||||
|
// +-------+ +-------+
|
||||||
|
// | | | |
|
||||||
|
// | A | | |
|
||||||
|
// | +--+----+ = | +--+
|
||||||
|
// +----+--+ | +----+
|
||||||
|
// | B |
|
||||||
|
// | |
|
||||||
|
// +-------+
|
||||||
|
//
|
||||||
|
subtract: function(csg) {
|
||||||
|
var a = new CSG.Node(this.clone().polygons);
|
||||||
|
var b = new CSG.Node(csg.clone().polygons);
|
||||||
|
a.invert();
|
||||||
|
a.clipTo(b);
|
||||||
|
b.clipTo(a);
|
||||||
|
b.invert();
|
||||||
|
b.clipTo(a);
|
||||||
|
b.invert();
|
||||||
|
a.build(b.allPolygons());
|
||||||
|
a.invert();
|
||||||
|
return CSG.fromPolygons(a.allPolygons());
|
||||||
|
},
|
||||||
|
|
||||||
|
// Return a new CSG solid representing space both this solid and in the
|
||||||
|
// solid `csg`. Neither this solid nor the solid `csg` are modified.
|
||||||
|
//
|
||||||
|
// A.intersect(B)
|
||||||
|
//
|
||||||
|
// +-------+
|
||||||
|
// | |
|
||||||
|
// | A |
|
||||||
|
// | +--+----+ = +--+
|
||||||
|
// +----+--+ | +--+
|
||||||
|
// | B |
|
||||||
|
// | |
|
||||||
|
// +-------+
|
||||||
|
//
|
||||||
|
intersect: function(csg) {
|
||||||
|
var a = new CSG.Node(this.clone().polygons);
|
||||||
|
var b = new CSG.Node(csg.clone().polygons);
|
||||||
|
a.invert();
|
||||||
|
b.clipTo(a);
|
||||||
|
b.invert();
|
||||||
|
a.clipTo(b);
|
||||||
|
b.clipTo(a);
|
||||||
|
a.build(b.allPolygons());
|
||||||
|
a.invert();
|
||||||
|
return CSG.fromPolygons(a.allPolygons());
|
||||||
|
},
|
||||||
|
|
||||||
|
// Return a new CSG solid with solid and empty space switched. This solid is
|
||||||
|
// not modified.
|
||||||
|
inverse: function() {
|
||||||
|
var csg = this.clone();
|
||||||
|
csg.polygons.map(function(p) { p.flip(); });
|
||||||
|
return csg;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Construct an axis-aligned solid cuboid. Optional parameters are `center` and
|
||||||
|
// `radius`, which default to `[0, 0, 0]` and `[1, 1, 1]`. The radius can be
|
||||||
|
// specified using a single number or a list of three numbers, one for each axis.
|
||||||
|
//
|
||||||
|
// Example code:
|
||||||
|
//
|
||||||
|
// var cube = CSG.cube({
|
||||||
|
// center: [0, 0, 0],
|
||||||
|
// radius: 1
|
||||||
|
// });
|
||||||
|
CSG.cube = function(options) {
|
||||||
|
options = options || {};
|
||||||
|
var c = new CSG.Vector(options.center || [0, 0, 0]);
|
||||||
|
var r = !options.radius ? [1, 1, 1] : options.radius.length ?
|
||||||
|
options.radius : [options.radius, options.radius, options.radius];
|
||||||
|
return CSG.fromPolygons([
|
||||||
|
[[0, 4, 6, 2], [-1, 0, 0]],
|
||||||
|
[[1, 3, 7, 5], [+1, 0, 0]],
|
||||||
|
[[0, 1, 5, 4], [0, -1, 0]],
|
||||||
|
[[2, 6, 7, 3], [0, +1, 0]],
|
||||||
|
[[0, 2, 3, 1], [0, 0, -1]],
|
||||||
|
[[4, 5, 7, 6], [0, 0, +1]]
|
||||||
|
].map(function(info) {
|
||||||
|
return new CSG.Polygon(info[0].map(function(i) {
|
||||||
|
var pos = new CSG.Vector(
|
||||||
|
c.x + r[0] * (2 * !!(i & 1) - 1),
|
||||||
|
c.y + r[1] * (2 * !!(i & 2) - 1),
|
||||||
|
c.z + r[2] * (2 * !!(i & 4) - 1)
|
||||||
|
);
|
||||||
|
return new CSG.Vertex(pos, new CSG.Vector(info[1]));
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Construct a solid sphere. Optional parameters are `center`, `radius`,
|
||||||
|
// `slices`, and `stacks`, which default to `[0, 0, 0]`, `1`, `16`, and `8`.
|
||||||
|
// The `slices` and `stacks` parameters control the tessellation along the
|
||||||
|
// longitude and latitude directions.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// var sphere = CSG.sphere({
|
||||||
|
// center: [0, 0, 0],
|
||||||
|
// radius: 1,
|
||||||
|
// slices: 16,
|
||||||
|
// stacks: 8
|
||||||
|
// });
|
||||||
|
CSG.sphere = function(options) {
|
||||||
|
options = options || {};
|
||||||
|
var c = new CSG.Vector(options.center || [0, 0, 0]);
|
||||||
|
var r = options.radius || 1;
|
||||||
|
var slices = options.slices || 16;
|
||||||
|
var stacks = options.stacks || 8;
|
||||||
|
var polygons = [], vertices;
|
||||||
|
function vertex(theta, phi) {
|
||||||
|
theta *= Math.PI * 2;
|
||||||
|
phi *= Math.PI;
|
||||||
|
var dir = new CSG.Vector(
|
||||||
|
Math.cos(theta) * Math.sin(phi),
|
||||||
|
Math.cos(phi),
|
||||||
|
Math.sin(theta) * Math.sin(phi)
|
||||||
|
);
|
||||||
|
vertices.push(new CSG.Vertex(c.plus(dir.times(r)), dir));
|
||||||
|
}
|
||||||
|
for (var i = 0; i < slices; i++) {
|
||||||
|
for (var j = 0; j < stacks; j++) {
|
||||||
|
vertices = [];
|
||||||
|
vertex(i / slices, j / stacks);
|
||||||
|
if (j > 0) vertex((i + 1) / slices, j / stacks);
|
||||||
|
if (j < stacks - 1) vertex((i + 1) / slices, (j + 1) / stacks);
|
||||||
|
vertex(i / slices, (j + 1) / stacks);
|
||||||
|
polygons.push(new CSG.Polygon(vertices));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CSG.fromPolygons(polygons);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Construct a solid cylinder. Optional parameters are `start`, `end`,
|
||||||
|
// `radius`, and `slices`, which default to `[0, -1, 0]`, `[0, 1, 0]`, `1`, and
|
||||||
|
// `16`. The `slices` parameter controls the tessellation.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// var cylinder = CSG.cylinder({
|
||||||
|
// start: [0, -1, 0],
|
||||||
|
// end: [0, 1, 0],
|
||||||
|
// radius: 1,
|
||||||
|
// slices: 16
|
||||||
|
// });
|
||||||
|
CSG.cylinder = function(options) {
|
||||||
|
options = options || {};
|
||||||
|
var s = new CSG.Vector(options.start || [0, -1, 0]);
|
||||||
|
var e = new CSG.Vector(options.end || [0, 1, 0]);
|
||||||
|
var ray = e.minus(s);
|
||||||
|
var r = options.radius || 1;
|
||||||
|
var slices = options.slices || 16;
|
||||||
|
var axisZ = ray.unit(), isY = (Math.abs(axisZ.y) > 0.5);
|
||||||
|
var axisX = new CSG.Vector(isY, !isY, 0).cross(axisZ).unit();
|
||||||
|
var axisY = axisX.cross(axisZ).unit();
|
||||||
|
var start = new CSG.Vertex(s, axisZ.negated());
|
||||||
|
var end = new CSG.Vertex(e, axisZ.unit());
|
||||||
|
var polygons = [];
|
||||||
|
function point(stack, slice, normalBlend) {
|
||||||
|
var angle = slice * Math.PI * 2;
|
||||||
|
var out = axisX.times(Math.cos(angle)).plus(axisY.times(Math.sin(angle)));
|
||||||
|
var pos = s.plus(ray.times(stack)).plus(out.times(r));
|
||||||
|
var normal = out.times(1 - Math.abs(normalBlend)).plus(axisZ.times(normalBlend));
|
||||||
|
return new CSG.Vertex(pos, normal);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < slices; i++) {
|
||||||
|
var t0 = i / slices, t1 = (i + 1) / slices;
|
||||||
|
polygons.push(new CSG.Polygon([start, point(0, t0, -1), point(0, t1, -1)]));
|
||||||
|
polygons.push(new CSG.Polygon([point(0, t1, 0), point(0, t0, 0), point(1, t0, 0), point(1, t1, 0)]));
|
||||||
|
polygons.push(new CSG.Polygon([end, point(1, t1, 1), point(1, t0, 1)]));
|
||||||
|
}
|
||||||
|
return CSG.fromPolygons(polygons);
|
||||||
|
};
|
||||||
|
|
||||||
|
// # class Vector
|
||||||
|
|
||||||
|
// Represents a 3D vector.
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
//
|
||||||
|
// new CSG.Vector(1, 2, 3);
|
||||||
|
// new CSG.Vector([1, 2, 3]);
|
||||||
|
// new CSG.Vector({ x: 1, y: 2, z: 3 });
|
||||||
|
|
||||||
|
CSG.Vector = function(x, y, z) {
|
||||||
|
if (arguments.length == 3) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
} else if ('x' in x) {
|
||||||
|
this.x = x.x;
|
||||||
|
this.y = x.y;
|
||||||
|
this.z = x.z;
|
||||||
|
} else {
|
||||||
|
this.x = x[0];
|
||||||
|
this.y = x[1];
|
||||||
|
this.z = x[2];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CSG.Vector.prototype = {
|
||||||
|
clone: function() {
|
||||||
|
return new CSG.Vector(this.x, this.y, this.z);
|
||||||
|
},
|
||||||
|
|
||||||
|
negated: function() {
|
||||||
|
return new CSG.Vector(-this.x, -this.y, -this.z);
|
||||||
|
},
|
||||||
|
|
||||||
|
plus: function(a) {
|
||||||
|
return new CSG.Vector(this.x + a.x, this.y + a.y, this.z + a.z);
|
||||||
|
},
|
||||||
|
|
||||||
|
minus: function(a) {
|
||||||
|
return new CSG.Vector(this.x - a.x, this.y - a.y, this.z - a.z);
|
||||||
|
},
|
||||||
|
|
||||||
|
times: function(a) {
|
||||||
|
return new CSG.Vector(this.x * a, this.y * a, this.z * a);
|
||||||
|
},
|
||||||
|
|
||||||
|
dividedBy: function(a) {
|
||||||
|
return new CSG.Vector(this.x / a, this.y / a, this.z / a);
|
||||||
|
},
|
||||||
|
|
||||||
|
dot: function(a) {
|
||||||
|
return this.x * a.x + this.y * a.y + this.z * a.z;
|
||||||
|
},
|
||||||
|
|
||||||
|
lerp: function(a, t) {
|
||||||
|
return this.plus(a.minus(this).times(t));
|
||||||
|
},
|
||||||
|
|
||||||
|
length: function() {
|
||||||
|
return Math.sqrt(this.dot(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
unit: function() {
|
||||||
|
return this.dividedBy(this.length());
|
||||||
|
},
|
||||||
|
|
||||||
|
cross: function(a) {
|
||||||
|
return new CSG.Vector(
|
||||||
|
this.y * a.z - this.z * a.y,
|
||||||
|
this.z * a.x - this.x * a.z,
|
||||||
|
this.x * a.y - this.y * a.x
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// # class Vertex
|
||||||
|
|
||||||
|
// Represents a vertex of a polygon. Use your own vertex class instead of this
|
||||||
|
// one to provide additional features like texture coordinates and vertex
|
||||||
|
// colors. Custom vertex classes need to provide a `pos` property and `clone()`,
|
||||||
|
// `flip()`, and `interpolate()` methods that behave analogous to the ones
|
||||||
|
// defined by `CSG.Vertex`. This class provides `normal` so convenience
|
||||||
|
// functions like `CSG.sphere()` can return a smooth vertex normal, but `normal`
|
||||||
|
// is not used anywhere else.
|
||||||
|
|
||||||
|
CSG.Vertex = function(pos, normal) {
|
||||||
|
this.pos = new CSG.Vector(pos);
|
||||||
|
this.normal = new CSG.Vector(normal);
|
||||||
|
};
|
||||||
|
|
||||||
|
CSG.Vertex.prototype = {
|
||||||
|
clone: function() {
|
||||||
|
return new CSG.Vertex(this.pos.clone(), this.normal.clone());
|
||||||
|
},
|
||||||
|
|
||||||
|
// Invert all orientation-specific data (e.g. vertex normal). Called when the
|
||||||
|
// orientation of a polygon is flipped.
|
||||||
|
flip: function() {
|
||||||
|
this.normal = this.normal.negated();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Create a new vertex between this vertex and `other` by linearly
|
||||||
|
// interpolating all properties using a parameter of `t`. Subclasses should
|
||||||
|
// override this to interpolate additional properties.
|
||||||
|
interpolate: function(other, t) {
|
||||||
|
return new CSG.Vertex(
|
||||||
|
this.pos.lerp(other.pos, t),
|
||||||
|
this.normal.lerp(other.normal, t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// # class Plane
|
||||||
|
|
||||||
|
// Represents a plane in 3D space.
|
||||||
|
|
||||||
|
CSG.Plane = function(normal, w) {
|
||||||
|
this.normal = normal;
|
||||||
|
this.w = w;
|
||||||
|
};
|
||||||
|
|
||||||
|
// `CSG.Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a
|
||||||
|
// point is on the plane.
|
||||||
|
CSG.Plane.EPSILON = 1e-5;
|
||||||
|
|
||||||
|
CSG.Plane.fromPoints = function(a, b, c) {
|
||||||
|
var n = b.minus(a).cross(c.minus(a)).unit();
|
||||||
|
return new CSG.Plane(n, n.dot(a));
|
||||||
|
};
|
||||||
|
|
||||||
|
CSG.Plane.prototype = {
|
||||||
|
clone: function() {
|
||||||
|
return new CSG.Plane(this.normal.clone(), this.w);
|
||||||
|
},
|
||||||
|
|
||||||
|
flip: function() {
|
||||||
|
this.normal = this.normal.negated();
|
||||||
|
this.w = -this.w;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Split `polygon` by this plane if needed, then put the polygon or polygon
|
||||||
|
// fragments in the appropriate lists. Coplanar polygons go into either
|
||||||
|
// `coplanarFront` or `coplanarBack` depending on their orientation with
|
||||||
|
// respect to this plane. Polygons in front or in back of this plane go into
|
||||||
|
// either `front` or `back`.
|
||||||
|
splitPolygon: function(polygon, coplanarFront, coplanarBack, front, back) {
|
||||||
|
var COPLANAR = 0;
|
||||||
|
var FRONT = 1;
|
||||||
|
var BACK = 2;
|
||||||
|
var SPANNING = 3;
|
||||||
|
|
||||||
|
// Classify each point as well as the entire polygon into one of the above
|
||||||
|
// four classes.
|
||||||
|
var polygonType = 0;
|
||||||
|
var types = [];
|
||||||
|
for (var i = 0; i < polygon.vertices.length; i++) {
|
||||||
|
var t = this.normal.dot(polygon.vertices[i].pos) - this.w;
|
||||||
|
var type = (t < -CSG.Plane.EPSILON) ? BACK : (t > CSG.Plane.EPSILON) ? FRONT : COPLANAR;
|
||||||
|
polygonType |= type;
|
||||||
|
types.push(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put the polygon in the correct list, splitting it when necessary.
|
||||||
|
switch (polygonType) {
|
||||||
|
case COPLANAR:
|
||||||
|
(this.normal.dot(polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).push(polygon);
|
||||||
|
break;
|
||||||
|
case FRONT:
|
||||||
|
front.push(polygon);
|
||||||
|
break;
|
||||||
|
case BACK:
|
||||||
|
back.push(polygon);
|
||||||
|
break;
|
||||||
|
case SPANNING:
|
||||||
|
var f = [], b = [];
|
||||||
|
for (var i = 0; i < polygon.vertices.length; i++) {
|
||||||
|
var j = (i + 1) % polygon.vertices.length;
|
||||||
|
var ti = types[i], tj = types[j];
|
||||||
|
var vi = polygon.vertices[i], vj = polygon.vertices[j];
|
||||||
|
if (ti != BACK) f.push(vi);
|
||||||
|
if (ti != FRONT) b.push(ti != BACK ? vi.clone() : vi);
|
||||||
|
if ((ti | tj) == SPANNING) {
|
||||||
|
var t = (this.w - this.normal.dot(vi.pos)) / this.normal.dot(vj.pos.minus(vi.pos));
|
||||||
|
var v = vi.interpolate(vj, t);
|
||||||
|
f.push(v);
|
||||||
|
b.push(v.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (f.length >= 3) front.push(new CSG.Polygon(f, polygon.shared));
|
||||||
|
if (b.length >= 3) back.push(new CSG.Polygon(b, polygon.shared));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// # class Polygon
|
||||||
|
|
||||||
|
// Represents a convex polygon. The vertices used to initialize a polygon must
|
||||||
|
// be coplanar and form a convex loop. They do not have to be `CSG.Vertex`
|
||||||
|
// instances but they must behave similarly (duck typing can be used for
|
||||||
|
// customization).
|
||||||
|
//
|
||||||
|
// Each convex polygon has a `shared` property, which is shared between all
|
||||||
|
// polygons that are clones of each other or were split from the same polygon.
|
||||||
|
// This can be used to define per-polygon properties (such as surface color).
|
||||||
|
|
||||||
|
CSG.Polygon = function(vertices, shared) {
|
||||||
|
this.vertices = vertices;
|
||||||
|
this.shared = shared;
|
||||||
|
this.plane = CSG.Plane.fromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos);
|
||||||
|
};
|
||||||
|
|
||||||
|
CSG.Polygon.prototype = {
|
||||||
|
clone: function() {
|
||||||
|
var vertices = this.vertices.map(function(v) { return v.clone(); });
|
||||||
|
return new CSG.Polygon(vertices, this.shared);
|
||||||
|
},
|
||||||
|
|
||||||
|
flip: function() {
|
||||||
|
this.vertices.reverse().map(function(v) { v.flip(); });
|
||||||
|
this.plane.flip();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// # class Node
|
||||||
|
|
||||||
|
// Holds a node in a BSP tree. A BSP tree is built from a collection of polygons
|
||||||
|
// by picking a polygon to split along. That polygon (and all other coplanar
|
||||||
|
// polygons) are added directly to that node and the other polygons are added to
|
||||||
|
// the front and/or back subtrees. This is not a leafy BSP tree since there is
|
||||||
|
// no distinction between internal and leaf nodes.
|
||||||
|
|
||||||
|
CSG.Node = function(polygons) {
|
||||||
|
this.plane = null;
|
||||||
|
this.front = null;
|
||||||
|
this.back = null;
|
||||||
|
this.polygons = [];
|
||||||
|
if (polygons) this.build(polygons);
|
||||||
|
};
|
||||||
|
|
||||||
|
CSG.Node.prototype = {
|
||||||
|
clone: function() {
|
||||||
|
var node = new CSG.Node();
|
||||||
|
node.plane = this.plane && this.plane.clone();
|
||||||
|
node.front = this.front && this.front.clone();
|
||||||
|
node.back = this.back && this.back.clone();
|
||||||
|
node.polygons = this.polygons.map(function(p) { return p.clone(); });
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Convert solid space to empty space and empty space to solid space.
|
||||||
|
invert: function() {
|
||||||
|
for (var i = 0; i < this.polygons.length; i++) {
|
||||||
|
this.polygons[i].flip();
|
||||||
|
}
|
||||||
|
this.plane.flip();
|
||||||
|
if (this.front) this.front.invert();
|
||||||
|
if (this.back) this.back.invert();
|
||||||
|
var temp = this.front;
|
||||||
|
this.front = this.back;
|
||||||
|
this.back = temp;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Recursively remove all polygons in `polygons` that are inside this BSP
|
||||||
|
// tree.
|
||||||
|
clipPolygons: function(polygons) {
|
||||||
|
if (!this.plane) return polygons.slice();
|
||||||
|
var front = [], back = [];
|
||||||
|
for (var i = 0; i < polygons.length; i++) {
|
||||||
|
this.plane.splitPolygon(polygons[i], front, back, front, back);
|
||||||
|
}
|
||||||
|
if (this.front) front = this.front.clipPolygons(front);
|
||||||
|
if (this.back) back = this.back.clipPolygons(back);
|
||||||
|
else back = [];
|
||||||
|
return front.concat(back);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Remove all polygons in this BSP tree that are inside the other BSP tree
|
||||||
|
// `bsp`.
|
||||||
|
clipTo: function(bsp) {
|
||||||
|
this.polygons = bsp.clipPolygons(this.polygons);
|
||||||
|
if (this.front) this.front.clipTo(bsp);
|
||||||
|
if (this.back) this.back.clipTo(bsp);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Return a list of all polygons in this BSP tree.
|
||||||
|
allPolygons: function() {
|
||||||
|
var polygons = this.polygons.slice();
|
||||||
|
if (this.front) polygons = polygons.concat(this.front.allPolygons());
|
||||||
|
if (this.back) polygons = polygons.concat(this.back.allPolygons());
|
||||||
|
return polygons;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Build a BSP tree out of `polygons`. When called on an existing tree, the
|
||||||
|
// new polygons are filtered down to the bottom of the tree and become new
|
||||||
|
// nodes there. Each set of polygons is partitioned using the first polygon
|
||||||
|
// (no heuristic is used to pick a good split).
|
||||||
|
build: function(polygons) {
|
||||||
|
if (!polygons.length) return;
|
||||||
|
if (!this.plane) this.plane = polygons[0].plane.clone();
|
||||||
|
var front = [], back = [];
|
||||||
|
for (var i = 0; i < polygons.length; i++) {
|
||||||
|
this.plane.splitPolygon(polygons[i], this.polygons, this.polygons, front, back);
|
||||||
|
}
|
||||||
|
if (front.length) {
|
||||||
|
if (!this.front) this.front = new CSG.Node();
|
||||||
|
this.front.build(front);
|
||||||
|
}
|
||||||
|
if (back.length) {
|
||||||
|
if (!this.back) this.back = new CSG.Node();
|
||||||
|
this.back.build(back);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
4
src/library/jquery.js
vendored
Normal file
4
src/library/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
34545
src/library/three.js
Normal file
34545
src/library/three.js
Normal file
File diff suppressed because one or more lines are too long
BIN
src/script/.DS_Store
vendored
Normal file
BIN
src/script/.DS_Store
vendored
Normal file
Binary file not shown.
145
src/script/box.js
Normal file
145
src/script/box.js
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
/******************************************************
|
||||||
|
*
|
||||||
|
* Box
|
||||||
|
* Representation of de Doodle3DBox
|
||||||
|
* Handles all communication with the doodle box
|
||||||
|
*
|
||||||
|
******************************************************/
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
//Als meerdere clients met box zouden verbinden zal de api te veel requests krijgen waardoor hij crasht
|
||||||
|
//implimentatie van het veranderen van onder andere naam, netwerkverbinding etc
|
||||||
|
|
||||||
|
D3D.Box = function (localIp) {
|
||||||
|
"use strict";
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.batchSize = 512;
|
||||||
|
this.maxBufferedLines = 4096;
|
||||||
|
|
||||||
|
this.localIp = localIp;
|
||||||
|
this.api = "http://" + localIp + "/d3dapi/";
|
||||||
|
|
||||||
|
this.printBatches = [];
|
||||||
|
this.currentBatch = 0;
|
||||||
|
|
||||||
|
this.loaded = false;
|
||||||
|
this.onload;
|
||||||
|
|
||||||
|
getAPI(self.api + "config/all", function (data) {
|
||||||
|
//self.config = data;
|
||||||
|
|
||||||
|
self.printer = new D3D.Printer(data);
|
||||||
|
self.update();
|
||||||
|
|
||||||
|
self.loaded = true;
|
||||||
|
if (self.onload !== undefined) {
|
||||||
|
self.onload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
D3D.Box.prototype.update = function () {
|
||||||
|
"use strict";
|
||||||
|
//TODO
|
||||||
|
//Code is zo op gezet dat maar api call te gelijk is
|
||||||
|
//Bij error wordt gelijk zelfde data opnieuw gestuurd
|
||||||
|
//Als DoodleBox ontkoppeld wordt komt er een error in de loop waardoor pagina breekt en ververst moet worden
|
||||||
|
|
||||||
|
//if (this.printBatches.length > 0 && (this.progress["buffered_lines"] + this.batchSize) <= this.maxBufferedLines) {
|
||||||
|
if (this.printBatches.length > 0 ) {
|
||||||
|
this.printBatch();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.updateState();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
D3D.Box.prototype.updateState = function () {
|
||||||
|
"use strict";
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
//que api calls so they don't overload the d3d box
|
||||||
|
getAPI(this.api + "printer/state", function (data) {
|
||||||
|
self.state = data.state;
|
||||||
|
|
||||||
|
if (data.state !== "connecting" && data.state !== "disconnected") {
|
||||||
|
|
||||||
|
getAPI(self.api + "printer/temperature", function (data) {
|
||||||
|
self.temperature = data;
|
||||||
|
|
||||||
|
getAPI(self.api + "printer/progress", function (data) {
|
||||||
|
self.progress = data;
|
||||||
|
|
||||||
|
//finish updating state
|
||||||
|
self.update();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
D3D.Box.prototype.print = function (gcode) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
this.currentBatch = 0;
|
||||||
|
|
||||||
|
//clone gcode to remove array links
|
||||||
|
gcode = gcode.clone();
|
||||||
|
|
||||||
|
//gcode split in batches
|
||||||
|
while (gcode.length > 0) {
|
||||||
|
var gcodeBatch = gcode.splice(0, Math.min(this.batchSize, gcode.length));
|
||||||
|
this.printBatches.push(gcodeBatch);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
D3D.Box.prototype.printBatch = function () {
|
||||||
|
"use strict";
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var gcode = this.printBatches.shift();
|
||||||
|
|
||||||
|
sendAPI(this.api + "printer/print", {
|
||||||
|
"start": ((this.currentBatch === 0) ? "true" : "false"),
|
||||||
|
"first": ((this.currentBatch === 0) ? "true" : "false"),
|
||||||
|
"gcode": gcode.join("\n")
|
||||||
|
}, function (data) {
|
||||||
|
|
||||||
|
console.log("batch sent: " + self.currentBatch, data);
|
||||||
|
if (self.printBatches.length > 0) {
|
||||||
|
//sent new batch
|
||||||
|
self.currentBatch ++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//finish printing
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateState();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
D3D.Box.prototype.stop = function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
this.printBatches = [];
|
||||||
|
this.currentBatch = 0;
|
||||||
|
|
||||||
|
var finishMove = [
|
||||||
|
"M107 ;fan off",
|
||||||
|
"G91 ;relative positioning",
|
||||||
|
"G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure",
|
||||||
|
"G1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more",
|
||||||
|
"G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way",
|
||||||
|
"M84 ;disable axes / steppers",
|
||||||
|
"G90 ;absolute positioning",
|
||||||
|
"M104 S180",
|
||||||
|
";M140 S70",
|
||||||
|
"M117 Done ;display message (20 characters to clear whole screen)"
|
||||||
|
];
|
||||||
|
|
||||||
|
sendAPI(this.api + "printer/stop", {
|
||||||
|
"gcode": finishMove.join("\n")
|
||||||
|
//"gcode": {}
|
||||||
|
}, function (data) {
|
||||||
|
console.log(data);
|
||||||
|
});
|
||||||
|
};
|
58
src/script/printer.js
Normal file
58
src/script/printer.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/******************************************************
|
||||||
|
*
|
||||||
|
* Printer
|
||||||
|
* Representation of the connected printer
|
||||||
|
*
|
||||||
|
******************************************************/
|
||||||
|
|
||||||
|
D3D.Printer = function (config) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
for (var i in config) {
|
||||||
|
this[i] = config[i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
D3D.Printer.prototype.getStartCode = function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var gcode = this["printer.startcode"];
|
||||||
|
gcode = this.subsituteVariables(gcode);
|
||||||
|
|
||||||
|
return gcode.split("\n");
|
||||||
|
};
|
||||||
|
D3D.Printer.prototype.getEndCode = function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var gcode = this["printer.endcode"];
|
||||||
|
gcode = this.subsituteVariables(gcode);
|
||||||
|
|
||||||
|
return gcode.split("\n");
|
||||||
|
};
|
||||||
|
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"];
|
||||||
|
|
||||||
|
switch (printerType) {
|
||||||
|
case "makerbot_replicator2": printerType = "r2"; break;
|
||||||
|
case "makerbot_replicator2x": printerType = "r2x"; break;
|
||||||
|
case "makerbot_thingomatic": printerType = "t6"; break;
|
||||||
|
case "makerbot_generic": printerType = "r2"; break;
|
||||||
|
case "_3Dison_plus": printerType = "r2"; break;
|
||||||
|
}
|
||||||
|
var heatedBedReplacement = heatedbed ? "" : ";";
|
||||||
|
|
||||||
|
gcode = gcode.replace(/{printingTemp}/gi, temperature);
|
||||||
|
gcode = gcode.replace(/{printingBedTemp}/gi, bedTemperature);
|
||||||
|
gcode = gcode.replace(/{preheatTemp}/gi, preheatTemperature);
|
||||||
|
gcode = gcode.replace(/{preheatBedTemp}/gi, preheatBedTemperature);
|
||||||
|
gcode = gcode.replace(/{printerType}/gi, printerType);
|
||||||
|
gcode = gcode.replace(/{if heatedBed}/gi, heatedBedReplacement);
|
||||||
|
|
||||||
|
return gcode;
|
||||||
|
};
|
228
src/script/slicer.js
Normal file
228
src/script/slicer.js
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
/******************************************************
|
||||||
|
*
|
||||||
|
* Slicer
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
******************************************************/
|
||||||
|
|
||||||
|
D3D.Slicer = function (geometry) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
this.geometry = geometry;
|
||||||
|
this.geometry.mergeVertices();
|
||||||
|
|
||||||
|
this.lines = [];
|
||||||
|
this.lineLookup = {};
|
||||||
|
};
|
||||||
|
D3D.Slicer.prototype.addLine = function (a, b) {
|
||||||
|
"use stict";
|
||||||
|
|
||||||
|
//think lookup can only be b_a, a_b is only possible when face is flipped
|
||||||
|
var index = this.lineLookup[a + "_" + b] || this.lineLookup[b + "_" + a];
|
||||||
|
|
||||||
|
//if (!index) {
|
||||||
|
if (index === undefined) {
|
||||||
|
index = this.lines.length;
|
||||||
|
this.lineLookup[a + "_" + b] = index;
|
||||||
|
|
||||||
|
this.lines.push({
|
||||||
|
line: new THREE.Line3(this.geometry.vertices[a], this.geometry.vertices[b]),
|
||||||
|
connects: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
};
|
||||||
|
D3D.Slicer.prototype.createLines = function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
this.lines = [];
|
||||||
|
this.lineLookup = {};
|
||||||
|
|
||||||
|
for (var i = 0; i < this.geometry.faces.length; i ++) {
|
||||||
|
var face = this.geometry.faces[i];
|
||||||
|
|
||||||
|
//check for only adding unique lines
|
||||||
|
//returns index of said line
|
||||||
|
var a = this.addLine(face.a, face.b);
|
||||||
|
var b = this.addLine(face.b, face.c);
|
||||||
|
var c = this.addLine(face.c, face.a);
|
||||||
|
|
||||||
|
//set connecting lines (based on face)
|
||||||
|
this.lines[a].connects.push(b, c);
|
||||||
|
this.lines[b].connects.push(a, c);
|
||||||
|
this.lines[c].connects.push(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
//sort lines on min height
|
||||||
|
//this.lines.sort(function (a, b) {
|
||||||
|
// return Math.min() - Math.min();
|
||||||
|
//});
|
||||||
|
};
|
||||||
|
D3D.Slicer.prototype.slice = function (height, step) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
this.createLines();
|
||||||
|
|
||||||
|
var slices = [];
|
||||||
|
|
||||||
|
var plane = new THREE.Plane();
|
||||||
|
|
||||||
|
for (var z = 0; z < height; z += step) {
|
||||||
|
plane.set(new THREE.Vector3(0, -1, 0), z);
|
||||||
|
|
||||||
|
var slice = [];
|
||||||
|
slices.push(slice);
|
||||||
|
|
||||||
|
var intersections = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < this.lines.length; i ++) {
|
||||||
|
var line = this.lines[i].line;
|
||||||
|
|
||||||
|
var intersection = plane.intersectLine(line);
|
||||||
|
|
||||||
|
if (intersection !== undefined) {
|
||||||
|
//remove +100 when implimenting good structure for geometry is complete
|
||||||
|
var point = new THREE.Vector2(intersection.x + 100, intersection.z + 100);
|
||||||
|
|
||||||
|
intersections.push(point);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
intersections.push(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var done = [];
|
||||||
|
for (var i = 0; i < intersections.length; i ++) {
|
||||||
|
|
||||||
|
if (done.indexOf(i) === -1 && intersections[i]) {
|
||||||
|
var index = i;
|
||||||
|
|
||||||
|
var shape = [];
|
||||||
|
|
||||||
|
while (index !== -1) {
|
||||||
|
var intersection = intersections[index];
|
||||||
|
shape.push(intersection);
|
||||||
|
|
||||||
|
done.push(index);
|
||||||
|
|
||||||
|
var connects = this.lines[index].connects;
|
||||||
|
for (var j = 0; j < connects.length; j ++) {
|
||||||
|
index = connects[j];
|
||||||
|
|
||||||
|
if (done.indexOf(index) === -1 && intersections[index]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
index = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//think this check is not nescesary, always higher as 0
|
||||||
|
if (shape.length > 0) {
|
||||||
|
slice.push(shape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return slices;
|
||||||
|
};
|
||||||
|
D3D.Slicer.prototype.getGcode = function (printer) {
|
||||||
|
"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 screenToMillimeterScale = doodleBox.printer["printer.screenToMillimeterScale"];
|
||||||
|
var layerHeight = doodleBox.printer["printer.layerHeight"];
|
||||||
|
var useSubLayers = doodleBox.printer["printer.useSubLayers"];
|
||||||
|
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 gcode = doodleBox.printer.getStartCode();
|
||||||
|
|
||||||
|
var extruder = 0.0;
|
||||||
|
var speed = (bottomSpeed*60).toFixed(3);
|
||||||
|
var flowRate = bottomFlowRate;
|
||||||
|
var filamentSurfaceArea = Math.pow((filamentThickness/2), 2) * Math.PI;
|
||||||
|
|
||||||
|
var slices = this.slice(200, layerHeight);
|
||||||
|
|
||||||
|
for (var layer = 0; layer < slices.length; layer ++) {
|
||||||
|
var slice = slices[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);
|
||||||
|
|
||||||
|
for (var i = 0; i < slice.length; i ++) {
|
||||||
|
var shape = slice[i];
|
||||||
|
|
||||||
|
var previousPoint;
|
||||||
|
|
||||||
|
for (var j = 0; j <= shape.length; j ++) {
|
||||||
|
//Finish shape by going to first point
|
||||||
|
var point = shape[(j % shape.length)];
|
||||||
|
|
||||||
|
if (j === 0) {
|
||||||
|
//TODO
|
||||||
|
//add retraction
|
||||||
|
if (extruder > retractionamount && retractionEnabled) {
|
||||||
|
gcode.push([
|
||||||
|
"G0",
|
||||||
|
"E" + (extruder - retractionamount).toFixed(3),
|
||||||
|
"F" + (retractionspeed * 60).toFixed(3)
|
||||||
|
].join(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
gcode.push([
|
||||||
|
"G0",
|
||||||
|
"X" + point.x.toFixed(3) + " Y" + point.y.toFixed(3) + " Z" + z,
|
||||||
|
"F" + (travelSpeed*60)
|
||||||
|
].join(" "));
|
||||||
|
|
||||||
|
if (extruder > retractionamount && retractionEnabled) {
|
||||||
|
gcode.push([
|
||||||
|
"G0",
|
||||||
|
"E" + extruder.toFixed(3),
|
||||||
|
"F" + (retractionspeed * 60).toFixed(3)
|
||||||
|
].join(" "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var lineLength = new THREE.Vector2().copy(point).sub(previousPoint).length();
|
||||||
|
extruder += lineLength * wallThickness * layerHeight / filamentSurfaceArea * flowRate;
|
||||||
|
|
||||||
|
gcode.push([
|
||||||
|
"G1",
|
||||||
|
"X" + point.x.toFixed(3) + " Y" + point.y.toFixed(3) + " Z" + z,
|
||||||
|
"F" + speed,
|
||||||
|
"E" + extruder.toFixed(3)
|
||||||
|
].join(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
previousPoint = point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gcode = gcode.concat(doodleBox.printer.getEndCode());
|
||||||
|
return gcode;
|
||||||
|
};
|
136
src/script/utils.js
Normal file
136
src/script/utils.js
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/******************************************************
|
||||||
|
*
|
||||||
|
* Utils
|
||||||
|
* requires jQuery, Three.js
|
||||||
|
*
|
||||||
|
******************************************************/
|
||||||
|
|
||||||
|
var D3D = {
|
||||||
|
"version": "0.1",
|
||||||
|
"website": "http://www.doodle3d.com/",
|
||||||
|
"contact": "develop@doodle3d.com"
|
||||||
|
};
|
||||||
|
|
||||||
|
function sendAPI (url, data, callback) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
type: "POST",
|
||||||
|
data: data,
|
||||||
|
dataType: "json",
|
||||||
|
timeout: 10000,
|
||||||
|
success: function (response) {
|
||||||
|
if (response.status === "success") {
|
||||||
|
if (callback !== undefined) {
|
||||||
|
callback(response.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.warn(response.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).fail(function () {
|
||||||
|
|
||||||
|
console.warn("failed connecting to " + url);
|
||||||
|
sendAPI(url, data, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAPI (url, callback) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
dataType: "json",
|
||||||
|
timeout: 5000,
|
||||||
|
success: function (response) {
|
||||||
|
if (response.status === "success") {
|
||||||
|
if (callback !== undefined) {
|
||||||
|
callback(response.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.warn(response.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).fail(function () {
|
||||||
|
|
||||||
|
console.warn("failed connecting to " + url);
|
||||||
|
getAPI(url, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.clone = function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var array = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < this.length; i ++) {
|
||||||
|
array[i] = this[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function applyMouseControls (renderer, camera, maxDistance) {
|
||||||
|
"use strict";
|
||||||
|
//TODO
|
||||||
|
//impliment touch controls
|
||||||
|
//windows mouse wheel fix
|
||||||
|
|
||||||
|
var distance = 20;
|
||||||
|
var rotX = 0;
|
||||||
|
var rotY = 0;
|
||||||
|
var moveCamera = false;
|
||||||
|
function updateCamera () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
camera.position.x = Math.cos(rotY)*Math.sin(rotX)*distance;
|
||||||
|
camera.position.y = Math.sin(rotY)*distance;
|
||||||
|
camera.position.z = Math.cos(rotY)*Math.cos(rotX)*distance;
|
||||||
|
camera.lookAt(new THREE.Vector3(0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
$(renderer.domElement).on("mousedown", function (e) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
moveCamera = true;
|
||||||
|
}).on("wheel", function (e) {
|
||||||
|
"use strict";
|
||||||
|
var event = e.originalEvent;
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
distance = THREE.Math.clamp(distance - event.wheelDelta, 1, maxDistance);
|
||||||
|
|
||||||
|
updateCamera();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(window).on("mouseup", function (e) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
moveCamera = false;
|
||||||
|
}).on("mousemove", function (e) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
if (moveCamera === true) {
|
||||||
|
rotX = (rotX - event.webkitMovementX/100) % (2*Math.PI);
|
||||||
|
rotY = THREE.Math.clamp(rotY + event.webkitMovementY/100, -Math.PI/2, Math.PI/2);
|
||||||
|
|
||||||
|
updateCamera();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateCamera();
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestAnimFrame = (function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
return requestAnimationFrame || webkitRequestAnimationFrame || mozRequestAnimationFrame || function (callback) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
setTimeout(callback, 1000/60);
|
||||||
|
};
|
||||||
|
})();
|
89
src/slice_test.html
Normal file
89
src/slice_test.html
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Doedel Drie Dee || Slice Test</title>
|
||||||
|
<!--<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>-->
|
||||||
|
<script src="library/jquery.js"></script>
|
||||||
|
<script src="library/three.js"></script>
|
||||||
|
|
||||||
|
<script src="script/utils.js"></script>
|
||||||
|
<script src="script/box.js"></script>
|
||||||
|
<script src="script/printer.js"></script>
|
||||||
|
<script src="script/slicer.js"></script>
|
||||||
|
|
||||||
|
<script src="gcode/testgcode.js"></script>
|
||||||
|
<script src="gcode/easterbunny.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="3d-preview" height="480" width="720"></canvas>
|
||||||
|
<canvas id="canvas" width="200" height="200"></canvas>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var localIp = location.hash.substring(1);
|
||||||
|
var doodleBox = new D3D.Box(localIp);
|
||||||
|
|
||||||
|
doodleBox.onload = function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var gcode = slicer.getGcode(doodleBox.printer);
|
||||||
|
|
||||||
|
var print = $(document.createElement("button")).text("Print").on("click", function () {
|
||||||
|
doodleBox.print(gcode);
|
||||||
|
});
|
||||||
|
var stop = $(document.createElement("button")).text("Stop").on("click", function () {
|
||||||
|
doodleBox.stop();
|
||||||
|
});
|
||||||
|
$("body").append(print, stop);
|
||||||
|
};
|
||||||
|
|
||||||
|
var scene = new THREE.Scene();
|
||||||
|
|
||||||
|
var renderer = new THREE.WebGLRenderer({canvas: document.getElementById("3d-preview")});
|
||||||
|
renderer.setClearColor(0xffffff, 1);
|
||||||
|
|
||||||
|
var camera = new THREE.PerspectiveCamera(75, renderer.domElement.width/renderer.domElement.height, 1, 10000);
|
||||||
|
|
||||||
|
applyMouseControls(renderer, camera, 1000);
|
||||||
|
|
||||||
|
var material = new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true});
|
||||||
|
var geometry = new THREE.TorusGeometry(20, 10, 20, 20);
|
||||||
|
//var geometry = new THREE.BoxGeometry(10, 10, 10, 1, 1, 1);
|
||||||
|
//var geometry = new THREE.SphereGeometry(5, 32, 32);
|
||||||
|
var mesh = new THREE.Mesh(geometry, material);
|
||||||
|
scene.add(mesh);
|
||||||
|
|
||||||
|
var slicer = new D3D.Slicer(geometry);
|
||||||
|
|
||||||
|
/*var slices = slicer.slice(20, 0.2);
|
||||||
|
|
||||||
|
var canvas = document.getElementById("canvas");
|
||||||
|
var context = canvas.getContext("2d");
|
||||||
|
|
||||||
|
for (var layer = 0; layer < slices.length; layer ++) {
|
||||||
|
var slice = slices[layer];
|
||||||
|
|
||||||
|
for (var i = 0; i < slice.length; i ++) {
|
||||||
|
var shape = slice[i];
|
||||||
|
|
||||||
|
context.beginPath();
|
||||||
|
for (var j = 0; j < shape.length; j ++) {
|
||||||
|
var point = shape[(j % shape.length)];
|
||||||
|
|
||||||
|
context.lineTo((point.x-100) * 3 + 100, (point.y-100) * 3 + 100);
|
||||||
|
}
|
||||||
|
context.closePath();
|
||||||
|
context.strokeStyle = "rgb(" + Math.round(Math.random()*255) + ", " + Math.round(Math.random()*255) + ", " + Math.round(Math.random()*255) + ")";
|
||||||
|
context.stroke();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
(function animate () {
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user