Compare commits
11 commits
stable-1.2
...
js2gcode
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb5ccf0c7a | ||
|
|
8820fe7cc7 | ||
|
|
114a7b8acd | ||
|
|
9099bdb303 | ||
|
|
5a62fc35c1 | ||
|
|
f63de0e630 | ||
|
|
22f474a029 | ||
|
|
7b88cddd3e | ||
|
|
ea591a954e | ||
|
|
fdbbf71cc7 | ||
|
|
753cb7845d |
7 changed files with 294 additions and 133 deletions
|
|
@ -235,7 +235,7 @@ class SvgToGcodePlugin(octoprint.plugin.SlicerPlugin,
|
|||
|
||||
def get_assets(self):
|
||||
return dict(
|
||||
js=[ "js/convert.js", "js/working_area.js", "js/gcode_parser.js", "js/lib/snap.svg-min.js", "js/lib/photobooth_min.js", "js/matrix_oven.js", "js/render_fills.js", "js/drag_scale_rotate.js"],
|
||||
js=[ "js/convert.js", "js/working_area.js", "js/gcode_parser.js", "js/lib/snap.svg-min.js", "js/lib/photobooth_min.js", "js/matrix_oven.js", "js/render_fills.js", "js/drag_scale_rotate.js", "js/svg2gcode.js"],
|
||||
less=["less/svgtogcode.less"],
|
||||
css=["css/svgtogcode.css", "css/mrbeam.css"]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@ $(function(){
|
|||
}
|
||||
}, self);
|
||||
|
||||
|
||||
self.maxSpeed.subscribe(function(val){
|
||||
self._configureFeedrateSlider();
|
||||
});
|
||||
|
|
@ -279,6 +278,10 @@ $(function(){
|
|||
data.gcodeFilesToAppend = self.gcodeFilesToAppend;
|
||||
}
|
||||
|
||||
var snapelement = snap.select("#userContent");
|
||||
snapelement.bake(false, 5);
|
||||
data.gcodedata = snapelement.toGcode(self.laserSpeed(), self.laserIntensity(), self.pierceTime());
|
||||
|
||||
$.ajax({
|
||||
url: API_BASEURL + "files/convert",
|
||||
type: "POST",
|
||||
|
|
@ -286,7 +289,6 @@ $(function(){
|
|||
contentType: "application/json; charset=UTF-8",
|
||||
data: JSON.stringify(data)
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ Snap.plugin(function (Snap, Element, Paper, global) {
|
|||
handleFill: "red",
|
||||
handleStrokeDashPreset: [5,5],
|
||||
handleStrokeWidth: 2,
|
||||
handleLength: 18,
|
||||
handleLength: 10,
|
||||
handleRadius: 16,
|
||||
unscale: 1,
|
||||
handleStrokeDash: "5,5",
|
||||
|
|
@ -135,7 +135,7 @@ Snap.plugin(function (Snap, Element, Paper, global) {
|
|||
this.data("ty", 0);
|
||||
this.attr({class:'_freeTransformInProgress'});
|
||||
|
||||
ftOption.unscale = 1 / this.paper.select('#scaleGroup').transform().localMatrix.a;
|
||||
ftOption.unscale = 1 / this.paper.select('#scaleGroup').transform().globalMatrix.a;
|
||||
this.data('unscale', ftOption.unscale);
|
||||
ftOption.handleStrokeDash = ftOption.handleStrokeDashPreset.map(function(v){ return v*ftOption.unscale; }).join(',');
|
||||
return this;
|
||||
|
|
@ -208,10 +208,10 @@ Snap.plugin(function (Snap, Element, Paper, global) {
|
|||
// transformed bbox
|
||||
this.data("bbT", this.paper.rect( rectObjFromBB( this.getBBox(1) ) )
|
||||
.attr({ fill: "none", stroke: ftOption.handleFill, strokeWidth: ftOption.handleStrokeWidth, strokeDasharray: ftOption.handleStrokeDashPreset.join(',') })
|
||||
.transform( this.transform().global.toString() ) );
|
||||
.transform( this.transform().local.toString() ) );
|
||||
// outer bbox
|
||||
this.data("bb", this.paper.select('#userContent').rect( rectObjFromBB( this.getBBox() ) )
|
||||
.attr({ fill: "none", stroke: 'gray', strokeWidth: ftOption.handleStrokeWidth, strokeDasharray: ftOption.handleStrokeDash }) );
|
||||
.attr({ fill: "none", stroke: 'gray', strokeWidth: 0.6, strokeDasharray: [2,2] }) );
|
||||
return this;
|
||||
};
|
||||
|
||||
|
|
@ -298,7 +298,7 @@ Snap.plugin(function (Snap, Element, Paper, global) {
|
|||
if(!mainEl.data('block_rotation')){
|
||||
var angle = Snap.angle( mainBB.cx, mainBB.cy, handle.attr('cx'), handle.attr('cy') ) - 180;
|
||||
if(event.shiftKey){
|
||||
angle = Math.round(angle/30) * 30;
|
||||
angle = Math.round(angle/15) * 15;
|
||||
}
|
||||
mainEl.data("angle", angle );
|
||||
}
|
||||
|
|
|
|||
154
src/octoprint/plugins/svgtogcode/static/js/svg2gcode.js
Normal file
154
src/octoprint/plugins/svgtogcode/static/js/svg2gcode.js
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
Snap.plugin(function (Snap, Element, Paper, global) {
|
||||
/**
|
||||
* generates and returns the svg as gcode
|
||||
*
|
||||
* @param {integer} laserSpeed : value for laser speed
|
||||
* @param {integer} laserIntensity : value for laser intensity
|
||||
* @param {integer} pierceTime : value for pierce time
|
||||
* @returns {list}
|
||||
*/
|
||||
Element.prototype.toGcode = function (laserSpeed, laserIntensity, pierceTime) {
|
||||
var gCodeList = [];
|
||||
|
||||
gCodeList.push(";Generated with svg2gcode Version 0.1\n");
|
||||
gCodeList.push("G21\n");
|
||||
//gCodeList.push("G1F" + laserSpeed + "\n");
|
||||
|
||||
svgWidth = this.paper.attr('viewBox').width;
|
||||
svgHeight = this.paper.attr('viewBox').height;
|
||||
feedrateSet = 0;
|
||||
|
||||
var elem = this.selectAll("path");
|
||||
for (var i = 0; i < elem.length; i++) {
|
||||
gCodeList.push(elem[i].pathStringToGCode(laserIntensity, laserSpeed, pierceTime));
|
||||
}
|
||||
|
||||
// Always append a M5 command to be sure the Laser is OFF
|
||||
gCodeList.push("\nM5");
|
||||
|
||||
return gCodeList;
|
||||
}
|
||||
|
||||
Element.prototype.pathStringToGCode = function (laserIntensity, laserSpeed, pierceTime) {
|
||||
if (this.type !== "path") {
|
||||
return this;
|
||||
}
|
||||
var gcode = [];
|
||||
var startP = [0, 0];
|
||||
var lastP = [0, 0];
|
||||
var lastXstr = "";
|
||||
var lastYstr = "";
|
||||
var xStr = "";
|
||||
var yStr = "";
|
||||
var laser = 0;
|
||||
var arr = Snap.parsePathString(Snap.path.toAbsolute(this.realPath));
|
||||
gcode.push(";NEW PATH STRING")
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i][0] == "M") {
|
||||
xStr = Math.round(arr[i][1] * 100) / 100;
|
||||
yStr = Math.round((svgHeight - arr[i][2]) * 100) / 100;
|
||||
if (laser == 1) {
|
||||
gcode.push("M3S0");
|
||||
}
|
||||
if (xStr != lastXstr && yStr != lastYstr) {
|
||||
gcode.push("G0X" + xStr + "Y" + yStr);
|
||||
lastXstr = xStr;
|
||||
lastYstr = yStr;
|
||||
} else if (xStr != lastXstr) {
|
||||
gcode.push("G0X" + xStr);
|
||||
lastXstr = xStr;
|
||||
} else if (yStr != lastYstr) {
|
||||
gcode.push("G0Y" + yStr);
|
||||
lastYstr = yStr;
|
||||
}
|
||||
if (feedrateSet == 0) {
|
||||
gcode.push("G1F" + laserSpeed);
|
||||
feedrateSet = 1;
|
||||
}
|
||||
gcode.push("M3S" + laserIntensity);
|
||||
laser = 1
|
||||
if (pierceTime != 0) {
|
||||
gcode.push("G4P" + pierceTime);
|
||||
}
|
||||
startP = [arr[i][1], (svgHeight - arr[i][2])];
|
||||
lastP = startP;
|
||||
} else if (arr[i][0] == "L") {
|
||||
xStr = Math.round(arr[i][1] * 100) / 100;
|
||||
yStr = Math.round((svgHeight - arr[i][2]) * 100) / 100;
|
||||
if (xStr != lastXstr && yStr != lastYstr) {
|
||||
gcode.push("G1X" + xStr + "Y" + yStr);
|
||||
lastXstr = xStr;
|
||||
lastYstr = yStr;
|
||||
} else if (xStr != lastXstr) {
|
||||
gcode.push("G1X" + xStr);
|
||||
lastXstr = xStr;
|
||||
} else if (yStr != lastYstr) {
|
||||
gcode.push("G1Y" + yStr);
|
||||
lastYstr = yStr;
|
||||
}
|
||||
lastP = [arr[i][1], (svgHeight - arr[i][2])];
|
||||
} else if (arr[i][0] == "H") {
|
||||
xStr = Math.round(arr[i][1] * 100) / 100;
|
||||
gcode.push("G1X" + xStr);
|
||||
lastXstr = xStr;
|
||||
lastP = [arr[i][1], lastP[1]];
|
||||
} else if (arr[i][0] == "V") {
|
||||
yStr = Math.round((svgHeight - arr[i][1]) * 100) / 100;
|
||||
gcode.push("G1Y" + yStr);
|
||||
lastYstr = yStr;
|
||||
lastP = [lastP[0], (svgHeight - arr[i][1])];
|
||||
} else if (arr[i][0] == "C") {
|
||||
var x0 = lastP[0];
|
||||
var y0 = lastP[1];
|
||||
var x1 = arr[i][1];
|
||||
var y1 = (svgHeight - arr[i][2]);
|
||||
var x2 = arr[i][3];
|
||||
var y2 = (svgHeight - arr[i][4]);
|
||||
var x3 = arr[i][5];
|
||||
var y3 = (svgHeight - arr[i][6]);
|
||||
var tmp = Snap.path.getTotalLength("M" + lastP[0] + "," + lastP[1] + "C" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + x3 + "," + y3);
|
||||
var range = Math.round(tmp) * 10;
|
||||
for (var t = 1; t <= range; t++) {
|
||||
obj = Snap.path.findDotsAtSegment(x0, y0, x1, y1, x2, y2, x3, y3, t / range)
|
||||
xStr = Math.round(obj.x * 100) / 100;
|
||||
yStr = Math.round(obj.y * 100) / 100;
|
||||
if (xStr != lastXstr && yStr != lastYstr) {
|
||||
gcode.push("G1X" + xStr + "Y" + yStr);
|
||||
lastXstr = xStr;
|
||||
lastYstr = yStr;
|
||||
} else if (xStr != lastXstr) {
|
||||
gcode.push("G1X" + xStr);
|
||||
lastXstr = xStr;
|
||||
} else if (yStr != lastYstr) {
|
||||
gcode.push("G1Y" + yStr);
|
||||
lastYstr = yStr;
|
||||
}
|
||||
}
|
||||
lastP = [x3, y3];
|
||||
} else if (arr[i][0] == "Q") {
|
||||
// TODO implement Q path element
|
||||
gcode.push("NOT_IMPLEMENTED");
|
||||
} else if (arr[i][0] == "A") {
|
||||
// TODO implement A path element
|
||||
gcode.push("NOT_IMPLEMENTED");
|
||||
} else if (arr[i][0] == "Z") {
|
||||
xStr = Math.round(startP[0] * 100) / 100;
|
||||
yStr = Math.round(startP[1] * 100) / 100;
|
||||
if (xStr != lastXstr && yStr != lastYstr) {
|
||||
gcode.push("G1X" + xStr + "Y" + yStr);
|
||||
lastXstr = xStr;
|
||||
lastYstr = yStr;
|
||||
} else if (xStr != lastXstr) {
|
||||
gcode.push("G1X" + xStr);
|
||||
lastXstr = xStr;
|
||||
} else if (yStr != lastYstr) {
|
||||
gcode.push("G1Y" + yStr);
|
||||
lastYstr = yStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
gcode.push("M3S0\n")
|
||||
//console.log(gcode.join("\n").length)
|
||||
return gcode.join("\n");
|
||||
}
|
||||
});
|
||||
|
|
@ -64,7 +64,8 @@ $(function(){
|
|||
// matrix scales svg units to display_pixels
|
||||
self.scaleMatrix = ko.computed(function(){
|
||||
var m = new Snap.Matrix();
|
||||
var factor = 25.4/self.svgDPI() * 1/self.px2mm_factor();
|
||||
//var factor = 25.4/self.svgDPI() * 1/self.px2mm_factor();
|
||||
var factor = 1;
|
||||
if(!isNaN(factor)){
|
||||
m.scale(factor);
|
||||
return m;
|
||||
|
|
@ -238,8 +239,9 @@ $(function(){
|
|||
}
|
||||
|
||||
// scale matrix
|
||||
var scale = self.svgDPI()/25.4;
|
||||
var mat = self.getDocumentViewBoxMatrix(doc_width, doc_height, doc_viewbox);
|
||||
var scaleMatrixStr = new Snap.Matrix(mat[0][0],mat[0][1],mat[1][0],mat[1][1],mat[0][2],mat[1][2]).toTransformString();
|
||||
var scaleMatrixStr = new Snap.Matrix(mat[0][0]/scale,mat[0][1],mat[1][0],mat[1][1]/scale,mat[0][2],mat[1][2]).toTransformString();
|
||||
newSvgAttrs['transform'] = scaleMatrixStr;
|
||||
|
||||
var newSvg = snap.group(f.selectAll("svg>*"));
|
||||
|
|
@ -399,12 +401,12 @@ $(function(){
|
|||
var wpx = this.width;
|
||||
var hpx = this.height;
|
||||
|
||||
var dimPT = self.getUsefulDimensions(wpx, hpx);
|
||||
var wPT = dimPT[0];
|
||||
var hPT = dimPT[1];
|
||||
var dimMM = self.getUsefulDimensions(wpx, hpx);
|
||||
var wMM = dimMM[0];
|
||||
var hMM = dimMM[1];
|
||||
|
||||
var y = self.mm2svgUnits(self.workingAreaHeightMM()) - hPT;
|
||||
var newImg = snap.image(url, 0, y, wPT, hPT);
|
||||
var y = self.workingAreaHeightMM() - hMM;
|
||||
var newImg = snap.image(url, 0, y, wMM, hMM);
|
||||
var id = self.getEntryId(file);
|
||||
var previewId = self.generateUniqueId(id); // appends # if multiple times the same design is placed.
|
||||
newImg.attr({id: previewId, filter: 'url(#grayscale_filter)', 'data-serveurl': url});
|
||||
|
|
@ -436,9 +438,7 @@ $(function(){
|
|||
} else {
|
||||
destHeightMM = destWidthMM / aspectRatio;
|
||||
}
|
||||
var destWidthPT = self.mm2svgUnits(destWidthMM);
|
||||
var destHeightPT = self.mm2svgUnits(destHeightMM);
|
||||
return [destWidthPT, destHeightPT];
|
||||
return [destWidthMM, destHeightMM];
|
||||
};
|
||||
|
||||
self.getDocumentDimensionsInPt = function(doc_width, doc_height, doc_viewbox){
|
||||
|
|
@ -579,18 +579,17 @@ $(function(){
|
|||
self.draw_coord_grid = function(){
|
||||
var grid = snap.select('#coordGrid');
|
||||
if(grid.attr('fill') === 'none'){
|
||||
var w = self.mm2svgUnits(self.workingAreaWidthMM());
|
||||
var h = self.mm2svgUnits(self.workingAreaHeightMM());
|
||||
var w = self.workingAreaWidthMM();
|
||||
var h = self.workingAreaHeightMM();
|
||||
var max_lines = 20;
|
||||
|
||||
var linedistMM = Math.floor(Math.max(self.workingAreaWidthMM(), self.workingAreaHeightMM()) / (max_lines * 10))*10;
|
||||
var yPatternOffset = self.mm2svgUnits(self.workingAreaHeightMM() % linedistMM);
|
||||
var linedist = self.mm2svgUnits(linedistMM);
|
||||
var yPatternOffset = self.workingAreaHeightMM() % linedistMM;
|
||||
var linedist = linedistMM;
|
||||
|
||||
var marker = snap.circle(linedist/2, linedist/2, 1).attr({
|
||||
var marker = snap.circle(linedist/2, linedist/2, 0.3).attr({
|
||||
fill: "#000000",
|
||||
stroke: "none",
|
||||
strokeWidth: 1
|
||||
stroke: "none"
|
||||
});
|
||||
|
||||
// dot pattern
|
||||
|
|
|
|||
|
|
@ -462,8 +462,6 @@ def gcodeConvertCommand():
|
|||
# #r.headers["Location"] = location
|
||||
# #return r
|
||||
|
||||
|
||||
|
||||
if command == "convert":
|
||||
# TODO stripping non-ascii is a hack - svg contains lots of non-ascii in <text> tags. Fix this!
|
||||
import re
|
||||
|
|
@ -518,7 +516,7 @@ def gcodeConvertCommand():
|
|||
del data["profile"]
|
||||
else:
|
||||
profile = None
|
||||
##
|
||||
|
||||
if "printerProfile" in data.keys() and data["printerProfile"]:
|
||||
printerProfile = data["printerProfile"]
|
||||
del data["printerProfile"]
|
||||
|
|
@ -548,34 +546,41 @@ def gcodeConvertCommand():
|
|||
for key in override_keys:
|
||||
overrides[key[len("profile."):]] = data[key]
|
||||
|
||||
def slicing_done(target, gcode_name, select_after_slicing, print_after_slicing, append_these_files):
|
||||
# append additioal gcodes
|
||||
if data.has_key('gcodedata'):
|
||||
output_path = fileManager.path_on_disk(target, gcode_name)
|
||||
with open(output_path,'ab') as wfd:
|
||||
for f in append_these_files:
|
||||
path = fileManager.path_on_disk(f['origin'], f['name'])
|
||||
wfd.write( "\n; "+ f['name'] + "\n")
|
||||
with open(output_path,'wb') as wfd:
|
||||
for line in data['gcodedata']:
|
||||
wfd.write(line)
|
||||
eventManager.fire(Events.SLICING_DONE, {"stl": filename, "gcode": gcode_name, "gcode_location": target, "time": 1.0})
|
||||
else:
|
||||
def slicing_done(target, gcode_name, select_after_slicing, print_after_slicing, append_these_files):
|
||||
# append additioal gcodes
|
||||
output_path = fileManager.path_on_disk(target, gcode_name)
|
||||
with open(output_path,'ab') as wfd:
|
||||
for f in append_these_files:
|
||||
path = fileManager.path_on_disk(f['origin'], f['name'])
|
||||
wfd.write( "\n; "+ f['name'] + "\n")
|
||||
|
||||
with open(path,'rb') as fd:
|
||||
shutil.copyfileobj(fd, wfd, 1024*1024*10)
|
||||
with open(path,'rb') as fd:
|
||||
shutil.copyfileobj(fd, wfd, 1024*1024*10)
|
||||
|
||||
wfd.write( "\nM05\n") # ensure that the laser is off.
|
||||
wfd.write( "\nM05\n") # ensure that the laser is off.
|
||||
|
||||
if select_after_slicing or print_after_slicing:
|
||||
sd = False
|
||||
filenameToSelect = fileManager.path_on_disk(target, gcode_name)
|
||||
printer.select_file(filenameToSelect, sd, True)
|
||||
if select_after_slicing or print_after_slicing:
|
||||
sd = False
|
||||
filenameToSelect = fileManager.path_on_disk(target, gcode_name)
|
||||
printer.select_file(filenameToSelect, sd, True)
|
||||
|
||||
try:
|
||||
fileManager.slice(slicer, target, filename, target, gcode_name,
|
||||
profile=profile,
|
||||
printer_profile_id=printerProfile,
|
||||
position=position,
|
||||
overrides=overrides,
|
||||
callback=slicing_done,
|
||||
callback_args=[target, gcode_name, select_after_slicing, print_after_slicing, appendGcodeFiles])
|
||||
except octoprint.slicing.UnknownProfile:
|
||||
return make_response("Profile {profile} doesn't exist".format(**locals()), 400)
|
||||
try:
|
||||
fileManager.slice(slicer, target, filename, target, gcode_name,
|
||||
profile=profile,
|
||||
printer_profile_id=printerProfile,
|
||||
position=position,
|
||||
overrides=overrides,
|
||||
callback=slicing_done,
|
||||
callback_args=[target, gcode_name, select_after_slicing, print_after_slicing, appendGcodeFiles])
|
||||
except octoprint.slicing.UnknownProfile:
|
||||
return make_response("Profile {profile} doesn't exist".format(**locals()), 400)
|
||||
|
||||
files = {}
|
||||
location = url_for(".readGcodeFile", target=target, filename=gcode_name, _external=True)
|
||||
|
|
|
|||
|
|
@ -287,6 +287,9 @@
|
|||
backgroundPosition: crosshairX()+'px'+' '+crosshairY()+'px',
|
||||
width: workingAreaWidthPx()+'px',
|
||||
height: workingAreaHeightPx()+'px'
|
||||
},
|
||||
attr: {
|
||||
viewBox: '0 0 '+workingAreaWidthMM()+' '+workingAreaHeightMM()
|
||||
}
|
||||
">
|
||||
<filter id="grayscale_filter">
|
||||
|
|
@ -304,25 +307,23 @@
|
|||
<text
|
||||
xml:space="preserve"
|
||||
data-bind="visible: working_area_empty"
|
||||
style="font-size:64px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#DDDDDD;fill-opacity:1;stroke:none;font-family:DIN-BoldAlternate, Helvetica, Arial, Sans-serif;"
|
||||
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#DDDDDD;fill-opacity:1;stroke:none;font-family:DIN-BoldAlternate, Helvetica, Arial, Sans-serif;"
|
||||
x="396.81018"
|
||||
y="552.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>
|
||||
id="tspan2987" x="60" y="60"
|
||||
style="text-anchor:left;text-align:center">add designs via </tspan>
|
||||
<tspan
|
||||
x="500" y="592.36218" id="tspan2989"
|
||||
style="text-anchor:middle;text-align:center">the design library </tspan>
|
||||
x="70" y="75" id="tspan2989"
|
||||
style="text-anchor:left;text-align:center">the design library </tspan>
|
||||
<tspan
|
||||
x="568.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="placedGcodes" data-bind="visible: !state.isPrinting() && !state.isPaused(), attr: { transform: scaleMatrixMMtoDisplay() }"></g>
|
||||
<g id="gCodePreview" data-bind="visible: state.isPrinting() || state.isPaused(), attr: { transform: scaleMatrixMMtoDisplay() }"></g>
|
||||
x="80" y="90" id="tspan2993"
|
||||
style="text-anchor:left;text-align:center">or drag 'n' drop </tspan>
|
||||
</text>
|
||||
<g id="placedGcodes" data-bind="visible: !state.isPrinting() && !state.isPaused()"></g>
|
||||
<g id="gCodePreview" data-bind="visible: state.isPrinting() || state.isPaused()"></g>
|
||||
<rect data-bind="click: move_laser"
|
||||
id="coordGrid" x="0" y="0" width="0" height="0"
|
||||
stroke="none" fill="none"></rect>
|
||||
|
|
|
|||
Loading…
Reference in a new issue