mix of svg and gcode files works
This commit is contained in:
parent
20927ad862
commit
b7ea00ebac
7 changed files with 679 additions and 97 deletions
|
|
@ -143,7 +143,7 @@ class SvgToGcodePlugin(octoprint.plugin.SlicerPlugin,
|
|||
|
||||
def get_assets(self):
|
||||
return {
|
||||
"js": [ "js/convert.js", "js/working_area.js", "js/lib/snap.svg-min.js"],
|
||||
"js": [ "js/convert.js", "js/working_area.js", "js/gcode_parser.js", "js/lib/snap.svg-min.js"],
|
||||
"less": ["less/svgtogcode.less"],
|
||||
"css": ["css/svgtogcode.css", "css/mrbeam.css"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,12 +48,13 @@ $(function(){
|
|||
self.laserSpeed(speed);
|
||||
|
||||
self.svg = self.workingArea.getCompositionSVG();
|
||||
self.gcodeFilesToAppend = self.workingArea.getPlacedGcodes();
|
||||
|
||||
// TODO: js svg conversion
|
||||
self.title(gettext("Converting"));
|
||||
var gcodeFile = self.create_gcode_filename(self.workingArea.placedDesigns());
|
||||
self.gcodeFilename(gcodeFile);
|
||||
$("#dialog_vector_graphics_conversion").modal("show");
|
||||
$("#dialog_vector_graphics_conversion").modal("show"); // calls self.convert afterwards
|
||||
};
|
||||
|
||||
self.create_gcode_filename = function(placedDesigns){
|
||||
|
|
@ -61,9 +62,8 @@ $(function(){
|
|||
var filemap = {};
|
||||
for(var idx in placedDesigns){
|
||||
var design = placedDesigns[idx];
|
||||
var start = design.url.lastIndexOf('/')+1;
|
||||
var end = design.url.lastIndexOf('.');
|
||||
var name = design.url.substring(start, end);
|
||||
var end = design.name.lastIndexOf('.');
|
||||
var name = design.name.substring(0, end);
|
||||
if(filemap[name] !== undefined) filemap[name] += 1;
|
||||
else filemap[name] = 1;
|
||||
}
|
||||
|
|
@ -83,7 +83,9 @@ $(function(){
|
|||
}
|
||||
return gcode_name + ".gco";
|
||||
} else {
|
||||
return "tmp"+Date.now()+".gco"; // TODO: user should not deal with gcode anymore. go and laser it.
|
||||
// return "tmp"+Date.now()+".gco";
|
||||
console.error("no designs placed.");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -194,6 +196,9 @@ $(function(){
|
|||
if(self.svg !== undefined){
|
||||
data.svg = self.svg;
|
||||
}
|
||||
if(self.gcodeFilesToAppend !== undefined){
|
||||
data.gcodeFilesToAppend = self.gcodeFilesToAppend;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: API_BASEURL + "files/convert",
|
||||
|
|
|
|||
532
src/octoprint/plugins/svgtogcode/static/js/gcode_parser.js
Normal file
532
src/octoprint/plugins/svgtogcode/static/js/gcode_parser.js
Normal file
|
|
@ -0,0 +1,532 @@
|
|||
$(function() {
|
||||
gcParser = function() {
|
||||
self = this;
|
||||
|
||||
self.toolOffsets = [{x: 0, y: 0}];
|
||||
|
||||
self.parse = function(gcode, blockDelimiter, blockCallback ) {
|
||||
var argChar, numSlice;
|
||||
|
||||
var x, y, z, pi, pj, pp = 0;
|
||||
var clockwise = false;
|
||||
var laser = 0;
|
||||
var prevX = 0, prevY = 0, prevZ = -1;
|
||||
var f, lastF = 4000;
|
||||
var extrude = false, extrudeRelative = false, retract = 0;
|
||||
var positionRelative = false;
|
||||
|
||||
var dcExtrude = false;
|
||||
var assumeNonDC = false;
|
||||
|
||||
var tool = 0;
|
||||
var prev_extrude = [{a: 0, b: 0, c: 0, e: 0, abs: 0}];
|
||||
var prev_retract = [0];
|
||||
var offset = self.toolOffsets[0];
|
||||
|
||||
var gcode_lines = gcode.split(/\n/);
|
||||
|
||||
var model = [];
|
||||
for (var i = 0; i < gcode_lines.length; i++) {
|
||||
x = undefined;
|
||||
y = undefined;
|
||||
z = undefined;
|
||||
pi = undefined;
|
||||
pj = undefined;
|
||||
pp = undefined;
|
||||
clockwise = false;
|
||||
retract = 0;
|
||||
|
||||
extrude = false;
|
||||
var line = gcode_lines[i].split(/[\(;]/)[0];
|
||||
|
||||
var addToModel = false;
|
||||
var convertAndAddToModel = false;
|
||||
var move = false;
|
||||
|
||||
|
||||
if (/^(?:G0|G00|G1|G01)\s+/i.test(line)) {
|
||||
var args = line.split(/\s+/);
|
||||
|
||||
for (var j = 0; j < args.length; j++) {
|
||||
switch (argChar = args[j].charAt(0).toLowerCase()) {
|
||||
case 'x':
|
||||
if (positionRelative) {
|
||||
x = prevX + Number(args[j].slice(1)) + offset.x;
|
||||
} else {
|
||||
x = Number(args[j].slice(1)) + offset.x;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'y':
|
||||
if (positionRelative) {
|
||||
y = prevY + Number(args[j].slice(1)) + offset.y;
|
||||
} else {
|
||||
y = Number(args[j].slice(1)) + offset.y;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
if (positionRelative) {
|
||||
z = prevZ + Number(args[j].slice(1));
|
||||
} else {
|
||||
z = Number(args[j].slice(1));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
assumeNonDC = true;
|
||||
numSlice = Number(args[j].slice(1));
|
||||
|
||||
if (!extrudeRelative) {
|
||||
// absolute extrusion positioning
|
||||
prev_extrude[tool]["abs"] = numSlice - prev_extrude[tool][argChar];
|
||||
prev_extrude[tool][argChar] = numSlice;
|
||||
} else {
|
||||
prev_extrude[tool]["abs"] = numSlice;
|
||||
prev_extrude[tool][argChar] += numSlice;
|
||||
}
|
||||
|
||||
extrude = prev_extrude[tool]["abs"] > 0;
|
||||
if (prev_extrude[tool]["abs"] < 0) {
|
||||
prev_retract[tool] = -1;
|
||||
retract = -1;
|
||||
} else if (prev_extrude[tool]["abs"] === 0) {
|
||||
retract = 0;
|
||||
} else if (prev_extrude[tool]["abs"] > 0 && prev_retract[tool] < 0) {
|
||||
prev_retract[tool] = 0;
|
||||
retract = 1;
|
||||
} else {
|
||||
retract = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
numSlice = parseFloat(args[j].slice(1));
|
||||
lastF = numSlice;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dcExtrude && !assumeNonDC) {
|
||||
extrude = true;
|
||||
prev_extrude[tool]["abs"] = Math.sqrt((prevX - x) * (prevX - x) + (prevY - y) * (prevY - y));
|
||||
}
|
||||
|
||||
if (typeof (x) !== 'undefined' || typeof (y) !== 'undefined' || typeof (z) !== 'undefined' || retract !== 0) {
|
||||
addToModel = true;
|
||||
move = true;
|
||||
}
|
||||
} else if (/^(?:G2|G02|G3|G03)\s+/i.test(line)) {
|
||||
var units = "G21"; // mm
|
||||
var args = line.split(/\s+/);
|
||||
var lastPos = {x: prevX, y: prevY, z: prevZ};
|
||||
|
||||
clockwise = /^(?:G2|G02)/i.test(args[0]);
|
||||
for (var j = 0; j < args.length; j++) {
|
||||
switch (argChar = args[j].charAt(0).toLowerCase()) {
|
||||
case 'x':
|
||||
if (positionRelative) {
|
||||
x = prevX + Number(args[j].slice(1)) + offset.x;
|
||||
} else {
|
||||
x = Number(args[j].slice(1)) + offset.x;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'y':
|
||||
if (positionRelative) {
|
||||
y = prevY + Number(args[j].slice(1)) + offset.y;
|
||||
} else {
|
||||
y = Number(args[j].slice(1)) + offset.y;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
if (positionRelative) {
|
||||
z = prevZ + Number(args[j].slice(1));
|
||||
} else {
|
||||
z = Number(args[j].slice(1));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
pi = Number(args[j].slice(1)) + offset.x;
|
||||
|
||||
break;
|
||||
|
||||
case 'j':
|
||||
pj = Number(args[j].slice(1)) + offset.y;
|
||||
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
pp = Number(args[j].slice(1));
|
||||
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
assumeNonDC = true;
|
||||
numSlice = Number(args[j].slice(1));
|
||||
|
||||
if (!extrudeRelative) {
|
||||
// absolute extrusion positioning
|
||||
prev_extrude[tool]["abs"] = numSlice - prev_extrude[tool][argChar];
|
||||
prev_extrude[tool][argChar] = numSlice;
|
||||
} else {
|
||||
prev_extrude[tool]["abs"] = numSlice;
|
||||
prev_extrude[tool][argChar] += numSlice;
|
||||
}
|
||||
|
||||
extrude = prev_extrude[tool]["abs"] > 0;
|
||||
if (prev_extrude[tool]["abs"] < 0) {
|
||||
prev_retract[tool] = -1;
|
||||
retract = -1;
|
||||
} else if (prev_extrude[tool]["abs"] === 0) {
|
||||
retract = 0;
|
||||
} else if (prev_extrude[tool]["abs"] > 0 && prev_retract[tool] < 0) {
|
||||
prev_retract[tool] = 0;
|
||||
retract = 1;
|
||||
} else {
|
||||
retract = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
numSlice = parseFloat(args[j].slice(1));
|
||||
lastF = numSlice;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dcExtrude && !assumeNonDC) {
|
||||
extrude = true;
|
||||
prev_extrude[tool]["abs"] = Math.sqrt((prevX - x) * (prevX - x) + (prevY - y) * (prevY - y));
|
||||
}
|
||||
|
||||
if (typeof (x) !== 'undefined' || typeof (y) !== 'undefined' || typeof (z) !== 'undefined'
|
||||
|| typeof (pi) !== 'undefined' || typeof (pj) !== 'undefined' || typeof (pp) !== 'undefined' || retract !== 0) {
|
||||
|
||||
convertAndAddToModel = true;
|
||||
move = true;
|
||||
}
|
||||
// } else if (/^(?:M82)/i.test(line)) {
|
||||
// extrudeRelative = false;
|
||||
} else if (/^(?:M3|M03)/i.test(line)) {
|
||||
var args = line.split(/\s+/);
|
||||
for (var j = 0; j < args.length; j++) {
|
||||
switch (argChar = args[j].charAt(0).toLowerCase()) {
|
||||
case 's':
|
||||
laser = Number(args[j].slice(1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (/^(?:M5|M05)/i.test(line)) {
|
||||
laser = 0;
|
||||
} else if (/^(?:G91)/i.test(line)) {
|
||||
positionRelative = true;
|
||||
extrudeRelative = true;
|
||||
} else if (/^(?:G90)/i.test(line)) {
|
||||
positionRelative = false;
|
||||
extrudeRelative = false;
|
||||
// } else if (/^(?:M83)/i.test(line)) {
|
||||
// extrudeRelative = true;
|
||||
// } else if (/^(?:M101)/i.test(line)) {
|
||||
// dcExtrude = true;
|
||||
// } else if (/^(?:M103)/i.test(line)) {
|
||||
// dcExtrude = false;
|
||||
} else if (/^(?:G92)/i.test(line)) {
|
||||
var args = line.split(/\s/);
|
||||
|
||||
for (var j = 0; j < args.length; j++) {
|
||||
if (!args[j])
|
||||
continue;
|
||||
|
||||
if (args.length === 1) {
|
||||
// G92 without coordinates => reset all axes to 0
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
prev_extrude[tool]["e"] = 0;
|
||||
prev_extrude[tool]["a"] = 0;
|
||||
prev_extrude[tool]["b"] = 0;
|
||||
prev_extrude[tool]["c"] = 0;
|
||||
} else {
|
||||
switch (argChar = args[j].charAt(0).toLowerCase()) {
|
||||
case 'x':
|
||||
x = Number(args[j].slice(1)) + offset.x;
|
||||
break;
|
||||
|
||||
case 'y':
|
||||
y = Number(args[j].slice(1)) + offset.y;
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
z = Number(args[j].slice(1));
|
||||
prevZ = z;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
numSlice = Number(args[j].slice(1));
|
||||
if (!extrudeRelative)
|
||||
prev_extrude[tool][argChar] = 0;
|
||||
else {
|
||||
prev_extrude[tool][argChar] = numSlice;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof (x) !== 'undefined' || typeof (y) !== 'undefined' || typeof (z) !== 'undefined') {
|
||||
addToModel = true;
|
||||
move = false;
|
||||
}
|
||||
|
||||
} else if (/^(?:G28|$H)/i.test(line)) {
|
||||
var args = line.split(/\s/);
|
||||
|
||||
if (args.length === 1) {
|
||||
// G28 with no arguments => home all axis
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
} else {
|
||||
for (j = 0; j < args.length; j++) {
|
||||
switch (argChar = args[j].charAt(0).toLowerCase()) {
|
||||
case 'x':
|
||||
x = 0;
|
||||
break;
|
||||
case 'y':
|
||||
y = 0;
|
||||
break;
|
||||
case 'z':
|
||||
z = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof (x) !== 'undefined' || typeof (y) !== 'undefined' || typeof (z) !== 'undefined' || retract !== 0) {
|
||||
addToModel = true;
|
||||
move = true;
|
||||
}
|
||||
} else if (/^(?:T\d+)/i.test(line)) {
|
||||
tool = Number(line.split(/\s/)[0].slice(1));
|
||||
if (!prev_extrude[tool])
|
||||
prev_extrude[tool] = {a: 0, b: 0, c: 0, e: 0, abs: 0};
|
||||
if (!prev_retract[tool])
|
||||
prev_retract[tool] = 0;
|
||||
|
||||
offset = self.toolOffsets[tool] || {x: 0, y: 0};
|
||||
}
|
||||
|
||||
// ensure z is set.
|
||||
if (typeof (z) === 'undefined') {
|
||||
if (typeof (prevZ) !== 'undefined') {
|
||||
z = prevZ;
|
||||
} else {
|
||||
z = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (addToModel) {
|
||||
model.push({
|
||||
x: x,
|
||||
y: y,
|
||||
z: z,
|
||||
extrude: extrude,
|
||||
laser: laser,
|
||||
retract: retract,
|
||||
noMove: !move,
|
||||
extrusion: (extrude || retract) && prev_extrude[tool]["abs"] ? prev_extrude[tool]["abs"] : 0,
|
||||
prevX: prevX,
|
||||
prevY: prevY,
|
||||
prevZ: prevZ,
|
||||
speed: lastF,
|
||||
gcodeLine: i,
|
||||
percentage: i / gcode_lines.length,
|
||||
tool: tool
|
||||
});
|
||||
}
|
||||
if (convertAndAddToModel) {
|
||||
var parts = self._convertG2G3(clockwise, x, y, z, pi, pj, pp, lastPos, units);
|
||||
var lastPart = parts[0];
|
||||
for (var l = 1; l < parts.length; l++) {
|
||||
var part = parts[l];
|
||||
|
||||
model.push({
|
||||
x: part[0],
|
||||
y: part[1],
|
||||
z: part[2],
|
||||
extrude: extrude,
|
||||
laser: laser,
|
||||
retract: retract,
|
||||
noMove: !move,
|
||||
extrusion: (extrude || retract) && prev_extrude[tool]["abs"] ? prev_extrude[tool]["abs"] : 0,
|
||||
prevX: lastPart[0],
|
||||
prevY: lastPart[1],
|
||||
prevZ: lastPart[2],
|
||||
speed: lastF,
|
||||
gcodeLine: i,
|
||||
percentage: i / gcode_lines.length,
|
||||
tool: tool
|
||||
});
|
||||
|
||||
lastPart = part;
|
||||
}
|
||||
}
|
||||
|
||||
if (move) {
|
||||
if (typeof (x) !== 'undefined')
|
||||
prevX = x;
|
||||
if (typeof (y) !== 'undefined')
|
||||
prevY = y;
|
||||
}
|
||||
|
||||
if (typeof (blockCallback) === 'function' && typeof (blockDelimiter) !== 'undefined' && blockDelimiter.test(line)) {
|
||||
blockCallback(model);
|
||||
model = [];
|
||||
}
|
||||
}
|
||||
|
||||
prevZ = z;
|
||||
if (typeof (blockCallback) === 'function' && model.length > 0) {
|
||||
blockCallback(model);
|
||||
}
|
||||
};
|
||||
|
||||
self._convertG2G3 = function(clockwise, x, y, z, i, j, p, lastPos, units) {
|
||||
if (typeof (x) === 'undefined')
|
||||
x = lastPos.x;
|
||||
if (typeof (y) === 'undefined')
|
||||
y = lastPos.y;
|
||||
if (typeof (z) === 'undefined')
|
||||
z = lastPos.z;
|
||||
if (typeof (i) === 'undefined')
|
||||
i = 0.0;
|
||||
if (typeof (j) === 'undefined')
|
||||
j = 0.0;
|
||||
if (typeof (p) === 'undefined')
|
||||
p = 1.0;
|
||||
|
||||
var curveSection = 1.0; // mm
|
||||
if (units === "G20") { // inches
|
||||
curveSection = 1.0 / 25.4;
|
||||
}
|
||||
|
||||
// angle variables.
|
||||
var angleA;
|
||||
var angleB;
|
||||
var angle;
|
||||
|
||||
// delta variables.
|
||||
var aX;
|
||||
var aY;
|
||||
var bX;
|
||||
var bY;
|
||||
|
||||
|
||||
// center of rotation
|
||||
var cX = lastPos.x + i;
|
||||
var cY = lastPos.y + j;
|
||||
|
||||
aX = lastPos.x - cX;
|
||||
aY = lastPos.y - cY;
|
||||
bX = x - cX;
|
||||
bY = y - cY;
|
||||
|
||||
// Clockwise
|
||||
if (clockwise) {
|
||||
angleA = Math.atan2(bY, bX);
|
||||
angleB = Math.atan2(aY, aX);
|
||||
} else {
|
||||
angleA = Math.atan2(aY, aX);
|
||||
angleB = Math.atan2(bY, bX);
|
||||
}
|
||||
|
||||
// Make sure angleB is always greater than angleA
|
||||
// and if not add 2PI so that it is (this also takes
|
||||
// care of the special case of angleA == angleB,
|
||||
// ie we want a complete circle)
|
||||
if (angleB <= angleA) {
|
||||
angleB += 2 * Math.PI * p;
|
||||
}
|
||||
angle = angleB - angleA;
|
||||
|
||||
// calculate a couple useful things.
|
||||
var radius = Math.sqrt(aX * aX + aY * aY);
|
||||
var length = radius * angle;
|
||||
|
||||
// for doing the actual move.
|
||||
var steps; // TODO accuracy setting
|
||||
var s;
|
||||
|
||||
// Maximum of either 2.4 times the angle in radians
|
||||
// or the length of the curve divided by the curve section constant
|
||||
steps = Math.ceil(Math.max(angle * 2.4, length / curveSection));
|
||||
|
||||
|
||||
var fta;
|
||||
if (!clockwise) {
|
||||
fta = angleA + angle;
|
||||
} else {
|
||||
fta = angleA;
|
||||
}
|
||||
|
||||
// THis if arc is correct
|
||||
// TODO move this into the validator
|
||||
var r2 = Math.sqrt(bX * bX + bY * bY);
|
||||
var percentage;
|
||||
if (r2 > radius) {
|
||||
percentage = Math.abs(radius / r2) * 100.0;
|
||||
} else {
|
||||
percentage = Math.abs(r2 / radius) * 100.0;
|
||||
}
|
||||
|
||||
if (percentage < 99.7) {
|
||||
var sb = "";
|
||||
sb += "Radius to end of arc differs from radius to start:\n";
|
||||
sb += "r1=" + radius + "\n";
|
||||
sb += "r2=" + r2 + "\n";
|
||||
console.error("gcode_parser.js convertG2G3", sb);
|
||||
}
|
||||
|
||||
// this is the real line calculation.
|
||||
var parts = [];
|
||||
var arcStartZ = lastPos.z;
|
||||
for (s = 1; s <= steps; s++) {
|
||||
var step;
|
||||
if (!clockwise)
|
||||
step = s;
|
||||
else
|
||||
step = steps - s;
|
||||
|
||||
var ta = (angleA + angle * (step / steps));
|
||||
|
||||
parts.push([cX + radius * Math.cos(ta), cY + radius * Math.sin(ta), lastPos.z + (z - arcStartZ) * s / steps]);
|
||||
|
||||
}
|
||||
|
||||
return parts;
|
||||
};
|
||||
|
||||
};
|
||||
});
|
||||
|
|
@ -2,6 +2,8 @@ $(function(){
|
|||
|
||||
function WorkingAreaViewModel(params) {
|
||||
var self = this;
|
||||
|
||||
self.parser = new gcParser();
|
||||
|
||||
self.loginState = params[0];
|
||||
self.settings = params[1];
|
||||
|
|
@ -101,51 +103,6 @@ $(function(){
|
|||
self.placedDesigns([]);
|
||||
};
|
||||
|
||||
// initialize list helper
|
||||
self.listHelper = new ItemListHelper(
|
||||
"gcodeFiles",
|
||||
{
|
||||
"name": function(a, b) {
|
||||
// sorts ascending
|
||||
if (a["name"].toLocaleLowerCase() < b["name"].toLocaleLowerCase()) return -1;
|
||||
if (a["name"].toLocaleLowerCase() > b["name"].toLocaleLowerCase()) return 1;
|
||||
return 0;
|
||||
},
|
||||
"upload": function(a, b) {
|
||||
// sorts descending
|
||||
if (b["date"] === undefined || a["date"] > b["date"]) return -1;
|
||||
if (a["date"] < b["date"]) return 1;
|
||||
return 0;
|
||||
},
|
||||
"size": function(a, b) {
|
||||
// sorts descending
|
||||
if (b["bytes"] === undefined || a["bytes"] > b["bytes"]) return -1;
|
||||
if (a["bytes"] < b["bytes"]) return 1;
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
{
|
||||
"printed": function(file) {
|
||||
return !(file["prints"] && file["prints"]["success"] && file["prints"]["success"] > 0);
|
||||
},
|
||||
"sd": function(file) {
|
||||
return file["origin"] && file["origin"] == "sdcard";
|
||||
},
|
||||
"local": function(file) {
|
||||
return !(file["origin"] && file["origin"] == "sdcard");
|
||||
},
|
||||
"machinecode": function(file) {
|
||||
return file["type"] && file["type"] == "machinecode";
|
||||
},
|
||||
"model": function(file) {
|
||||
return file["type"] && file["type"] == "model";
|
||||
}
|
||||
},
|
||||
"name",
|
||||
[],
|
||||
[["sd", "local"], ["machinecode", "model"]],
|
||||
0
|
||||
);
|
||||
|
||||
self.trigger_resize = function(){
|
||||
self.availableHeight(document.documentElement.clientHeight - $('body>nav').outerHeight() - $('footer>*').outerHeight() - 39); // magic number
|
||||
|
|
@ -192,6 +149,62 @@ $(function(){
|
|||
return val * self.svgDPI()/25.4;
|
||||
};
|
||||
|
||||
self.placeGcode = function(file){
|
||||
console.log(file);
|
||||
var previewId = self.getEntryId(file);
|
||||
|
||||
if(snap.select('#'+previewId)){
|
||||
console.error("working_area placeGcode: file already placed.");
|
||||
return;
|
||||
} else {
|
||||
var g = snap.group();
|
||||
g.attr({id: previewId});
|
||||
snap.select('#placedGcodes').append(g);
|
||||
self.placedDesigns.push(file);
|
||||
}
|
||||
|
||||
self.loadGcode(file, function(gcode){
|
||||
self.parser.parse(gcode, /(m0?3)|(m0?5)/i, function(block){
|
||||
var points = [];
|
||||
var intensity = -1;
|
||||
for (var idx = 0; idx < block.length; idx++) {
|
||||
var item = block[idx];
|
||||
points.push( [ item.x, item.y ] );
|
||||
intensity = item.laser;
|
||||
}
|
||||
if(points.length > 0)
|
||||
self.draw_gcode(points, intensity, '#'+previewId);
|
||||
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
self.loadGcode = function(file, callback){
|
||||
var url = file.refs.download;
|
||||
var date = file.date;
|
||||
$.ajax({
|
||||
url: url,
|
||||
data: { "ctime": date },
|
||||
type: "GET",
|
||||
success: function(response, rstatus) {
|
||||
if(rstatus === 'success'){
|
||||
if(typeof(callback) === 'function'){
|
||||
callback(response);
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
console.error("working_area.js placeGcode: unable to load ", url);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
self.removeGcode = function(file){
|
||||
var previewId = self.getEntryId(file);
|
||||
snap.select('#' + previewId).remove();
|
||||
self.placedDesigns.remove(file);
|
||||
};
|
||||
|
||||
self.placeSVG = function(file) {
|
||||
var url = self._getSVGserveUrl(file);
|
||||
|
|
@ -207,14 +220,15 @@ $(function(){
|
|||
|
||||
var newSvg = f.select("g");
|
||||
newSvg.attr(namespaces);
|
||||
// var id = self.generateId(url);
|
||||
var id = self.getEntryId(file); // works better on multiple instances.
|
||||
newSvg.attr({id: id});
|
||||
var id = self.getEntryId(file);
|
||||
var previewId = self.generateUniqueId(id); // appends -# if multiple times the same design is placed.
|
||||
newSvg.attr({id: previewId});
|
||||
snap.select("#userContent").append(newSvg);
|
||||
|
||||
newSvg.drag();// TODO debug drag. should not be affected by scale matrix
|
||||
|
||||
file.id = id;
|
||||
file.id = previewId;
|
||||
file.previewId = previewId;
|
||||
file.url = url;
|
||||
|
||||
self.placedDesigns.push(file);
|
||||
|
|
@ -227,17 +241,20 @@ $(function(){
|
|||
};
|
||||
|
||||
self.removeSVG = function(file){
|
||||
var url = self._getSVGserveUrl(file);
|
||||
for (var idx = 0; idx < self.placedDesigns().length; idx++) {
|
||||
var svg = self.placedDesigns()[idx];
|
||||
if(svg.url === url){
|
||||
snap.select('#'+svg.id).remove();
|
||||
self.placedDesigns.remove(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log("removeSVG", file.previewId, self.placedDesigns.indexOf(file));
|
||||
snap.select('#'+file.previewId).remove();
|
||||
self.placedDesigns.remove(file);
|
||||
// TODO debug why remove always clears all items of this type.
|
||||
// self.placedDesigns.remove(function(item){
|
||||
// console.log("item", item.previewId );
|
||||
// //return false;
|
||||
// if(item.previewId === file.previewId){
|
||||
// console.log("match", item.previewId );
|
||||
// return true;
|
||||
// } else return false;
|
||||
// });
|
||||
};
|
||||
|
||||
|
||||
self._getSVGserveUrl = function(file){
|
||||
if (file && file["refs"] && file["refs"]["download"]) {
|
||||
var url = file.refs.download.replace("downloads", "serve");
|
||||
|
|
@ -247,8 +264,9 @@ $(function(){
|
|||
};
|
||||
|
||||
self.templateFor = function(data) {
|
||||
console.log("data", data);
|
||||
var extension = data.name.split('.').pop().toLowerCase();
|
||||
if (extension == "svg") {
|
||||
if (extension === "svg") {
|
||||
return "wa_template_" + data.type + "_svg";
|
||||
} else {
|
||||
return "wa_template_" + data.type;
|
||||
|
|
@ -310,14 +328,12 @@ $(function(){
|
|||
}
|
||||
};
|
||||
|
||||
self.generateId = function(url){
|
||||
var idBase = '_'+url.substring(url.lastIndexOf('/')+1).replace(/[^a-zA-Z0-9]/ig, '-'); // _ at first place if filename starts with a digit
|
||||
idBase = idBase.replace('')
|
||||
self.generateUniqueId = function(idBase){
|
||||
var suffix = 0;
|
||||
var id = idBase + "-" + suffix;
|
||||
while(snap.select('#'+id) !== null){
|
||||
suffix += 1;
|
||||
id = idBase + suffix;
|
||||
id = idBase + "-" + suffix;
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
|
@ -335,7 +351,16 @@ $(function(){
|
|||
return svg;
|
||||
};
|
||||
|
||||
self.draw_gcode = function(points, intensity){
|
||||
self.getPlacedGcodes = ko.computed(function() {
|
||||
var gcodeFiles = [];
|
||||
ko.utils.arrayForEach(self.placedDesigns(), function(design) {
|
||||
if(design.type === 'machinecode') gcodeFiles.push(design);
|
||||
});
|
||||
return gcodeFiles;
|
||||
}, self);
|
||||
|
||||
|
||||
self.draw_gcode = function(points, intensity, target){
|
||||
var stroke_color = intensity === 0 ? '#BBBBBB' : '#FF0000';
|
||||
var d = 'M'+points.join(' ');
|
||||
var p = snap.path(d).attr({
|
||||
|
|
@ -343,14 +368,17 @@ $(function(){
|
|||
stroke: stroke_color,
|
||||
strokeWidth: 1
|
||||
});
|
||||
snap.select('#gCodePreview').append(p);
|
||||
console.log("target", target);
|
||||
snap.select(target).append(p);
|
||||
};
|
||||
self.clear_gcode = function(){
|
||||
// console.log("gcodeprev clear");
|
||||
snap.select('#gCodePreview').clear();
|
||||
};
|
||||
|
||||
self.onStartup = function(){
|
||||
GCODE.workingArea = self; // Temporary hack to use the gcode parser from the gCodeViewer
|
||||
self.state.workingArea = self;
|
||||
self.files.workingArea = self;
|
||||
self.conversion.workingArea = self;
|
||||
$(window).resize(function(){
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@
|
|||
|
||||
|
||||
<div class="row-fluid print-control" style="display: none;" data-bind="visible: loginState.isUser">
|
||||
<button class="btn btn-danger span4" data-bind="click: conversion.show_conversion_dialog, enable: isOperational() && isReady() && !isPrinting() && loginState.isUser(), attr: {title: titlePrintButton}" id="job_print">
|
||||
<button class="btn btn-danger span4" data-bind="click: conversion.show_conversion_dialog, enable: isOperational() && isReady() && !isPrinting() && loginState.isUser() && !workingArea.working_area_empty(), attr: {title: titlePrintButton}" id="job_print">
|
||||
<i class="icon-white" data-bind="css: {'icon-fire': !isPaused(), 'icon-undo': isPaused(), 'wobble': isPrinting()}"></i> <span data-bind="text: (isPaused() ? '{{ _('Restart') }}' : '{{ _('Laser') }}')">{{ _('Laser') }}</span>
|
||||
</button>
|
||||
<button class="btn span4" id="job_pause" data-bind="click: pause, enable: isOperational() && (isPrinting() || isPaused()) && loginState.isUser(), css: {active: isPaused()}, attr: {title: titlePauseButton}"><i data-bind="css: {'icon-pause': !isPaused(), 'icon-play': isPaused()}"></i> <span data-bind="visible: !isPaused()">{{ _('Pause') }}</span><span data-bind="visible: isPaused()">{{ _('Resume') }}</span></button>
|
||||
|
|
@ -195,7 +195,7 @@
|
|||
|
||||
|
||||
<div class="accordion-group" id="working_area_files">
|
||||
<div class="">
|
||||
<div class="" data-bind="visible: !working_area_empty()">
|
||||
<div class="accordion-heading">
|
||||
<a class="accordion-toggle" data-toggle="collapse" href="#wa_filelist"><i class="icon-list"></i> {{ _('Files') }}</a>
|
||||
<a class="pull-right btn btn-small" data-bind="click: clear" style="margin-right: 1em; margin-top: 4px;" ><i class="icon-remove-sign"></i> {{ _('Clear') }}</a>
|
||||
|
|
@ -209,9 +209,9 @@
|
|||
|
||||
<script type="text/html" id="wa_template_machinecode">
|
||||
<div class="file_list_entry">
|
||||
<div class="title" data-bind="css: $root.getSuccessClass($data), style: { 'font-weight': $root.listHelper.isSelected($data) ? 'bold' : 'normal' }, text: name"></div>
|
||||
<div class="title muted" data-bind="text: name" style="color:#ADADAD;"></div>
|
||||
<div class="btn-group action-buttons">
|
||||
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableRemove($data)) { $root.removeFile($data); } else { return; } }"><i class="icon-remove" title="{{ _('Remove') }}"></i></div>
|
||||
<div class="btn btn-mini" data-bind="click: function(){ $root.removeGcode($data); }"><i class="icon-remove" title="{{ _('Remove') }}"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
|
@ -257,24 +257,21 @@
|
|||
x="396.81018"
|
||||
y="592.36218"
|
||||
id="add_designs_hint"
|
||||
><tspan
|
||||
id="tspan2987"
|
||||
x="368.571426"
|
||||
y="532.36218"
|
||||
style="text-anchor:middle;text-align:center">add designs via </tspan><tspan
|
||||
x="368.571426"
|
||||
y="592.36218"
|
||||
id="tspan2989"
|
||||
style="text-anchor:middle;text-align:center">the design library </tspan><tspan
|
||||
x="368.571426"
|
||||
y="652.36218"
|
||||
id="tspan2993"
|
||||
style="text-anchor:middle;text-align:center">or drag 'n' drop </tspan><tspan
|
||||
x="368.571426"
|
||||
y="712.36218"
|
||||
id="tspan2991"
|
||||
>
|
||||
<tspan
|
||||
id="tspan2987" x="368.571426" y="532.36218"
|
||||
style="text-anchor:middle;text-align:center">add designs via </tspan>
|
||||
<tspan
|
||||
x="368.571426" y="592.36218" id="tspan2989"
|
||||
style="text-anchor:middle;text-align:center">the design library </tspan>
|
||||
<tspan
|
||||
x="368.571426" y="652.36218" id="tspan2993"
|
||||
style="text-anchor:middle;text-align:center">or drag 'n' drop </tspan>
|
||||
<tspan
|
||||
x="368.571426" y="712.36218" id="tspan2991"
|
||||
style="text-anchor:middle;text-align:center" /></text>
|
||||
<g id="userContent"></g>
|
||||
<g id="placedGcodes" data-bind="attr: { transform: scaleMatrixMMtoDisplay() }"></g>
|
||||
<g id="gCodePreview" data-bind="attr: { transform: scaleMatrixMMtoDisplay() }"></g>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
@ -322,9 +319,8 @@
|
|||
</ul>
|
||||
<div class="">
|
||||
<div class="accordion-heading">
|
||||
<a class="accordion-toggle" data-toggle="collapse" href="#files"><i class="icon-list"></i> {{ _('Files') }}</a>
|
||||
<a class="accordion-toggle" ><i class="icon-list"></i> {{ _('Files') }}</a>
|
||||
<a class="accordion-toggle" data-toggle="collapse" href="#files_search" style="text-decoration: none;"><i class="icon-search dropdown-toggle"></i><span class="caret" style="margin: .5em;"></span></a>
|
||||
<a class="pull-right btn btn-small" data-bind="click: workingArea.clear" style="margin-right: 1em; margin-top: 4px;" ><i class="icon-remove-sign"></i> {{ _('Clear') }}</a>
|
||||
</div>
|
||||
|
||||
<div class="settings-trigger accordion-heading-button btn-group" style="display: none;">
|
||||
|
|
@ -386,7 +382,8 @@
|
|||
<div class="btn-group action-buttons">
|
||||
<a class="btn btn-mini" data-bind="attr: {href: $root.downloadLink($data), css: {disabled: !$root.downloadLink($data)}}"><i class="icon-download-alt" title="{{ _('Download') }}"></i></a>
|
||||
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableRemove($data)) { $root.removeFile($data); } else { return; } }, css: {disabled: !$root.enableRemove($data)}"><i class="icon-trash" title="{{ _('Remove') }}"></i></div>
|
||||
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableSelect($data)) { $root.startGcodeWithSafetyWarning($data); } else { return; } }, css: {disabled: !$root.enableSelect($data)}"><i class="icon-fire" title="{{ _('Load and Laser') }}"></i></div>
|
||||
<!--<div class="btn btn-mini" data-bind="click: function() { if ($root.enableSelect($data)) { $root.startGcodeWithSafetyWarning($data); } else { return; } }, css: {disabled: !$root.enableSelect($data)}"><i class="icon-fire" title="{{ _('Load and Laser') }}"></i></div>-->
|
||||
<div class="btn btn-mini" data-bind="click: function() { $root.workingArea.placeGcode($data); }"><i class="icon-ok" title="{{ _('Use') }}"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from octoprint.server.util.flask import restricted_access
|
|||
from octoprint.server.api import api
|
||||
from octoprint.events import Events
|
||||
import octoprint.filemanager
|
||||
import shutil
|
||||
|
||||
|
||||
#~~ GCODE file handling
|
||||
|
|
@ -421,6 +422,23 @@ def gcodeConvertCommand():
|
|||
svg = data['svg']
|
||||
del data['svg']
|
||||
|
||||
appendGcodeFiles = data['gcodeFilesToAppend']
|
||||
del data['gcodeFilesToAppend']
|
||||
|
||||
def appendCallback(location, path, sources):
|
||||
output_path = fileManager.get_absolute_path(location, path)
|
||||
with open(output_path,'ab') as wfd:
|
||||
for f in sources:
|
||||
path = fileManager.get_absolute_path(f['origin'], f['name'])
|
||||
print("files.py appendCallback", path)
|
||||
wfd.write( "\n; "+ f['name'] + "\n")
|
||||
|
||||
with open(path,'rb') as fd:
|
||||
shutil.copyfileobj(fd, wfd, 1024*1024*10)
|
||||
|
||||
wfd.write( "\n")
|
||||
|
||||
print("files.py join done", output_path)
|
||||
|
||||
if command == "convert":
|
||||
|
||||
|
|
@ -474,7 +492,9 @@ def gcodeConvertCommand():
|
|||
for key in override_keys:
|
||||
overrides[key[len("profile."):]] = data[key]
|
||||
|
||||
ok, result = fileManager.slice(slicer, target, filename, target, gcode_name, profile=profile, overrides=overrides)
|
||||
ok, result = fileManager.slice(slicer, target, filename, target, gcode_name, profile=profile, overrides=overrides,
|
||||
callback=appendCallback, callback_args=[target, gcode_name, appendGcodeFiles])
|
||||
|
||||
if ok:
|
||||
files = {}
|
||||
location = url_for(".readGcodeFile", target=target, filename=gcode_name, _external=True)
|
||||
|
|
|
|||
|
|
@ -405,7 +405,7 @@ var drawLayer = function(layerNum, fromProgress, toProgress, isNotCurrentLayer){
|
|||
|
||||
if(lastLaser !== cmds[i].laser){
|
||||
|
||||
GCODE.workingArea.draw_gcode(points, lastLaser);
|
||||
GCODE.workingArea.draw_gcode(points, lastLaser, '#gCodePreview');
|
||||
points = [[prevX,prevY]];
|
||||
lastLaser = cmds[i].laser;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue