GCODE viewer now interprets inverted axes for printer control and mirrors print bed accordingly.
Should enable people to set their axes origin so that the viewer matches what they see on their printer. Should close #431
This commit is contained in:
parent
da19ad7943
commit
030ffe6dce
4 changed files with 105 additions and 45 deletions
|
|
@ -44,6 +44,7 @@ GCODE.renderer = (function(){
|
||||||
zoomInOnModel: false,
|
zoomInOnModel: false,
|
||||||
zoomInOnBed: false,
|
zoomInOnBed: false,
|
||||||
centerViewport: false,
|
centerViewport: false,
|
||||||
|
invertAxes: {x: false, y: false},
|
||||||
|
|
||||||
bed: {x: 200, y: 200},
|
bed: {x: 200, y: 200},
|
||||||
container: undefined
|
container: undefined
|
||||||
|
|
@ -54,6 +55,7 @@ GCODE.renderer = (function(){
|
||||||
var scaleX = 1, scaleY = 1;
|
var scaleX = 1, scaleY = 1;
|
||||||
var speeds = [];
|
var speeds = [];
|
||||||
var speedsByLayer = {};
|
var speedsByLayer = {};
|
||||||
|
var currentInvertX = false, currentInvertY = false;
|
||||||
|
|
||||||
var reRender = function(){
|
var reRender = function(){
|
||||||
var p1 = ctx.transformedPoint(0,0);
|
var p1 = ctx.transformedPoint(0,0);
|
||||||
|
|
@ -280,8 +282,11 @@ GCODE.renderer = (function(){
|
||||||
|
|
||||||
var i;
|
var i;
|
||||||
|
|
||||||
isNotCurrentLayer = typeof isNotCurrentLayer !== 'undefined' ? isNotCurrentLayer : false;
|
//~~ store current layer values
|
||||||
|
|
||||||
|
isNotCurrentLayer = isNotCurrentLayer !== undefined ? isNotCurrentLayer : false;
|
||||||
if (!isNotCurrentLayer) {
|
if (!isNotCurrentLayer) {
|
||||||
|
// not not current layer == current layer => store layer number and from/to progress
|
||||||
layerNumStore = layerNum;
|
layerNumStore = layerNum;
|
||||||
progressStore = {from: fromProgress, to: toProgress};
|
progressStore = {from: fromProgress, to: toProgress};
|
||||||
}
|
}
|
||||||
|
|
@ -291,67 +296,74 @@ GCODE.renderer = (function(){
|
||||||
var cmds = model[layerNum];
|
var cmds = model[layerNum];
|
||||||
var x, y;
|
var x, y;
|
||||||
|
|
||||||
if (fromProgress > 0) {
|
//~~ find our initial prevX/prevY tuple
|
||||||
prevX = cmds[fromProgress-1].x * zoomFactor;
|
|
||||||
prevY = -cmds[fromProgress-1].y * zoomFactor;
|
if (cmds[0].prevX !== undefined && cmds[0].prevY !== undefined) {
|
||||||
} else if (fromProgress === 0 && layerNum == 0) {
|
// command contains prevX/prevY values, use those
|
||||||
if (model[0] && model[0].x !== undefined && model[0].y !== undefined) {
|
|
||||||
prevX = model[0].x * zoomFactor;
|
|
||||||
prevY = -model[0].y * zoomFactor;
|
|
||||||
} else {
|
|
||||||
prevX = 0;
|
|
||||||
prevY = 0;
|
|
||||||
}
|
|
||||||
} else if(typeof(cmds[0].prevX) !== 'undefined' && typeof(cmds[0].prevY) !== 'undefined') {
|
|
||||||
prevX = cmds[0].prevX * zoomFactor;
|
prevX = cmds[0].prevX * zoomFactor;
|
||||||
prevY = -cmds[0].prevY * zoomFactor;
|
prevY = -1 * cmds[0].prevY * zoomFactor;
|
||||||
} else {
|
} else if (fromProgress > 0) {
|
||||||
if (model[layerNum-1]) {
|
// previous command in same layer exists, use x/y as prevX/prevY
|
||||||
prevX = undefined;
|
prevX = cmds[fromProgress - 1].x * zoomFactor;
|
||||||
prevY = undefined;
|
prevY = -cmds[fromProgress - 1].y * zoomFactor;
|
||||||
for (i = model[layerNum-1].length-1; i >= 0; i--) {
|
} else if (model[layerNum - 1]) {
|
||||||
if (prevX === undefined && model[layerNum-1][i].x !== undefined) prevX = model[layerNum-1][i].x * zoomFactor;
|
// previous layer exists, use last x/y as prevX/prevY
|
||||||
if (prevY === undefined && model[layerNum-1][i].y !== undefined) prevY =- model[layerNum-1][i].y * zoomFactor;
|
prevX = undefined;
|
||||||
}
|
prevY = undefined;
|
||||||
if (prevX === undefined) prevX=0;
|
for (i = model[layerNum-1].length-1; i >= 0; i--) {
|
||||||
if (prevY === undefined) prevY=0;
|
if (prevX === undefined && model[layerNum - 1][i].x !== undefined) prevX = model[layerNum - 1][i].x * zoomFactor;
|
||||||
} else {
|
if (prevY === undefined && model[layerNum - 1][i].y !== undefined) prevY =- model[layerNum - 1][i].y * zoomFactor;
|
||||||
prevX = 0;
|
|
||||||
prevY = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we did not find prevX or prevY, set it to 0 (might be that we are on the first command of the first layer,
|
||||||
|
// or it's just a very weird model...)
|
||||||
|
if (prevX === undefined) prevX = 0;
|
||||||
|
if (prevY === undefined) prevY = 0;
|
||||||
|
|
||||||
|
//~~ render this layer's commands
|
||||||
|
|
||||||
for (i = fromProgress; i <= toProgress; i++) {
|
for (i = fromProgress; i <= toProgress; i++) {
|
||||||
ctx.lineWidth = 1;
|
ctx.lineWidth = 1;
|
||||||
|
|
||||||
if (typeof(cmds[i]) === 'undefined') continue;
|
if (typeof(cmds[i]) === 'undefined') continue;
|
||||||
|
|
||||||
if (typeof(cmds[i].prevX) !== 'undefined' && typeof(cmds[i].prevY) !== 'undefined') {
|
if (typeof(cmds[i].prevX) !== 'undefined' && typeof(cmds[i].prevY) !== 'undefined') {
|
||||||
|
// override new (prevX, prevY)
|
||||||
prevX = cmds[i].prevX * zoomFactor;
|
prevX = cmds[i].prevX * zoomFactor;
|
||||||
prevY = -cmds[i].prevY * zoomFactor;
|
prevY = -1 * cmds[i].prevY * zoomFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// new x
|
||||||
if (typeof(cmds[i].x) === 'undefined' || isNaN(cmds[i].x)) {
|
if (typeof(cmds[i].x) === 'undefined' || isNaN(cmds[i].x)) {
|
||||||
x = prevX / zoomFactor;
|
x = prevX / zoomFactor;
|
||||||
} else {
|
} else {
|
||||||
x = cmds[i].x;
|
x = cmds[i].x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// new y
|
||||||
if (typeof(cmds[i].y) === 'undefined' || isNaN(cmds[i].y)) {
|
if (typeof(cmds[i].y) === 'undefined' || isNaN(cmds[i].y)) {
|
||||||
y = prevY / zoomFactor;
|
y = prevY / zoomFactor;
|
||||||
} else {
|
} else {
|
||||||
y = -cmds[i].y;
|
y = -cmds[i].y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// current tool
|
||||||
var tool = cmds[i].tool;
|
var tool = cmds[i].tool;
|
||||||
if (tool === undefined) tool = 0;
|
if (tool === undefined) tool = 0;
|
||||||
|
|
||||||
|
// line color based on tool
|
||||||
var lineColor = renderOptions["colorLine"][tool];
|
var lineColor = renderOptions["colorLine"][tool];
|
||||||
if (lineColor === undefined) lineColor = renderOptions["colorLine"][0];
|
if (lineColor === undefined) lineColor = renderOptions["colorLine"][0];
|
||||||
|
|
||||||
|
// alpha value (100% if current layer is being rendered, 30% otherwise)
|
||||||
var alpha = (renderOptions['showNextLayer'] || renderOptions['showPreviousLayer']) && isNotCurrentLayer ? 0.3 : 1.0;
|
var alpha = (renderOptions['showNextLayer'] || renderOptions['showPreviousLayer']) && isNotCurrentLayer ? 0.3 : 1.0;
|
||||||
var shade = tool * 0.15;
|
var shade = tool * 0.15;
|
||||||
|
|
||||||
if (!cmds[i].extrude && !cmds[i].noMove) {
|
if (!cmds[i].extrude && !cmds[i].noMove) {
|
||||||
|
// neither extrusion nor move
|
||||||
if (cmds[i].retract == -1) {
|
if (cmds[i].retract == -1) {
|
||||||
|
// retract => draw dot if configured to do so
|
||||||
if (renderOptions["showRetracts"]) {
|
if (renderOptions["showRetracts"]) {
|
||||||
ctx.strokeStyle = pusher.color(renderOptions["colorRetract"]).shade(shade).alpha(alpha).html();
|
ctx.strokeStyle = pusher.color(renderOptions["colorRetract"]).shade(shade).alpha(alpha).html();
|
||||||
ctx.fillStyle = pusher.color(renderOptions["colorRetract"]).shade(shade).alpha(alpha).html();
|
ctx.fillStyle = pusher.color(renderOptions["colorRetract"]).shade(shade).alpha(alpha).html();
|
||||||
|
|
@ -361,7 +373,9 @@ GCODE.renderer = (function(){
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(renderOptions["showMoves"]){
|
if(renderOptions["showMoves"]){
|
||||||
|
// move => draw line from (prevX, prevY) to (x, y) in move color
|
||||||
ctx.strokeStyle = pusher.color(renderOptions["colorMove"]).shade(shade).alpha(alpha).html();
|
ctx.strokeStyle = pusher.color(renderOptions["colorMove"]).shade(shade).alpha(alpha).html();
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(prevX, prevY);
|
ctx.moveTo(prevX, prevY);
|
||||||
|
|
@ -370,6 +384,7 @@ GCODE.renderer = (function(){
|
||||||
}
|
}
|
||||||
} else if(cmds[i].extrude) {
|
} else if(cmds[i].extrude) {
|
||||||
if (cmds[i].retract == 0) {
|
if (cmds[i].retract == 0) {
|
||||||
|
// no retraction => real extrusion move, use tool color to draw line
|
||||||
ctx.strokeStyle = pusher.color(renderOptions["colorLine"][tool]).shade(shade).alpha(alpha).html();
|
ctx.strokeStyle = pusher.color(renderOptions["colorLine"][tool]).shade(shade).alpha(alpha).html();
|
||||||
ctx.lineWidth = renderOptions['extrusionWidth'];
|
ctx.lineWidth = renderOptions['extrusionWidth'];
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
|
|
@ -377,6 +392,7 @@ GCODE.renderer = (function(){
|
||||||
ctx.lineTo(x*zoomFactor,y*zoomFactor);
|
ctx.lineTo(x*zoomFactor,y*zoomFactor);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
} else {
|
} else {
|
||||||
|
// we were previously retracting, now we are restarting => draw dot if configured to do so
|
||||||
if (renderOptions["showRetracts"]) {
|
if (renderOptions["showRetracts"]) {
|
||||||
ctx.strokeStyle = pusher.color(renderOptions["colorRestart"]).shade(shade).alpha(alpha).html();
|
ctx.strokeStyle = pusher.color(renderOptions["colorRestart"]).shade(shade).alpha(alpha).html();
|
||||||
ctx.fillStyle = pusher.color(renderOptions["colorRestart"]).shade(shade).alpha(alpha).html();
|
ctx.fillStyle = pusher.color(renderOptions["colorRestart"]).shade(shade).alpha(alpha).html();
|
||||||
|
|
@ -387,6 +403,8 @@ GCODE.renderer = (function(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set new (prevX, prevY)
|
||||||
prevX = x * zoomFactor;
|
prevX = x * zoomFactor;
|
||||||
prevY = y * zoomFactor;
|
prevY = y * zoomFactor;
|
||||||
}
|
}
|
||||||
|
|
@ -443,8 +461,8 @@ GCODE.renderer = (function(){
|
||||||
var scaleF = mdlInfo.modelSize.x > mdlInfo.modelSize.y ? (canvas.width - 10) / mdlInfo.modelSize.x : (canvas.height - 10) / mdlInfo.modelSize.y;
|
var scaleF = mdlInfo.modelSize.x > mdlInfo.modelSize.y ? (canvas.width - 10) / mdlInfo.modelSize.x : (canvas.height - 10) / mdlInfo.modelSize.y;
|
||||||
scaleF /= zoomFactor;
|
scaleF /= zoomFactor;
|
||||||
if (transform.a && transform.d) {
|
if (transform.a && transform.d) {
|
||||||
scaleX = scaleF / transform.a;
|
scaleX = scaleF / transform.a * (renderOptions["invertAxes"]["x"] ? -1 : 1);
|
||||||
scaleY = scaleF / transform.d;
|
scaleY = scaleF / transform.d * (renderOptions["invertAxes"]["y"] ? -1 : 1);
|
||||||
ctx.translate(pt.x,pt.y);
|
ctx.translate(pt.x,pt.y);
|
||||||
ctx.scale(scaleX, scaleY);
|
ctx.scale(scaleX, scaleY);
|
||||||
ctx.translate(-pt.x, -pt.y);
|
ctx.translate(-pt.x, -pt.y);
|
||||||
|
|
@ -455,13 +473,33 @@ GCODE.renderer = (function(){
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var applyInversion = function() {
|
||||||
|
var width = canvas.width - 10;
|
||||||
|
var height = canvas.height - 10;
|
||||||
|
|
||||||
|
if (currentInvertX || currentInvertY) {
|
||||||
|
ctx.scale(currentInvertX ? -1 : 1, currentInvertY ? -1 : 1);
|
||||||
|
ctx.translate(currentInvertX ? -width : 0, currentInvertY ? height : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var invertX = renderOptions["invertAxes"]["x"];
|
||||||
|
var invertY = renderOptions["invertAxes"]["y"];
|
||||||
|
if (invertX || invertY) {
|
||||||
|
ctx.translate(invertX ? width : 0, invertY ? -height : 0);
|
||||||
|
ctx.scale(invertX ? -1 : 1, invertY ? -1 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentInvertX = invertX;
|
||||||
|
currentInvertY = invertY;
|
||||||
|
};
|
||||||
|
|
||||||
// ***** PUBLIC *******
|
// ***** PUBLIC *******
|
||||||
return {
|
return {
|
||||||
init: function(){
|
init: function(){
|
||||||
startCanvas();
|
startCanvas();
|
||||||
initialized = true;
|
initialized = true;
|
||||||
var bedWidth = renderOptions["bed"]["x"];
|
var bedWidth = renderOptions["bed"]["x"];
|
||||||
var bedHeight = renderOptions["bed"]["y"];;
|
var bedHeight = renderOptions["bed"]["y"];
|
||||||
if(renderOptions["bed"]["circular"]) {
|
if(renderOptions["bed"]["circular"]) {
|
||||||
bedWidth = bedHeight = renderOptions["bed"]["r"] * 2;
|
bedWidth = bedHeight = renderOptions["bed"]["r"] * 2;
|
||||||
}
|
}
|
||||||
|
|
@ -492,7 +530,7 @@ GCODE.renderer = (function(){
|
||||||
|
|
||||||
dirty = true;
|
dirty = true;
|
||||||
renderOptions[opt] = options[opt];
|
renderOptions[opt] = options[opt];
|
||||||
if ($.inArray(opt, ["moveModel", "centerViewport", "zoomInOnModel", "bed"])) {
|
if ($.inArray(opt, ["moveModel", "centerViewport", "zoomInOnModel", "bed", "invertAxes"])) {
|
||||||
mustRefresh = true;
|
mustRefresh = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -573,6 +611,7 @@ GCODE.renderer = (function(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyInversion();
|
||||||
applyOffsets(mdlInfo);
|
applyOffsets(mdlInfo);
|
||||||
applyZoom(mdlInfo);
|
applyZoom(mdlInfo);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -180,8 +180,12 @@ GCODE.ui = (function(){
|
||||||
|
|
||||||
updateOptions: function(options) {
|
updateOptions: function(options) {
|
||||||
setOptions(options.ui);
|
setOptions(options.ui);
|
||||||
GCODE.gCodeReader.setOption(options.reader);
|
if (options.reader) {
|
||||||
GCODE.renderer.setOption(options.renderer);
|
GCODE.gCodeReader.setOption(options.reader);
|
||||||
|
}
|
||||||
|
if (options.renderer) {
|
||||||
|
GCODE.renderer.setOption(options.renderer);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
changeSelectedLayer: function(newLayerNum) {
|
changeSelectedLayer: function(newLayerNum) {
|
||||||
|
|
|
||||||
|
|
@ -96,21 +96,38 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) {
|
||||||
// subscribe to relevant printer settings...
|
// subscribe to relevant printer settings...
|
||||||
self.settings.printer_extruderOffsets.subscribe(function() {
|
self.settings.printer_extruderOffsets.subscribe(function() {
|
||||||
if (!self.enabled) return;
|
if (!self.enabled) return;
|
||||||
|
if (!self.settings.printer_extruderOffsets()) return;
|
||||||
|
|
||||||
var options = {
|
GCODE.ui.updateOptions({
|
||||||
reader: {
|
reader: {
|
||||||
toolOffsets: self.settings.printer_extruderOffsets()
|
toolOffsets: self.settings.printer_extruderOffsets()
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
});
|
||||||
|
self.settings.printer_bedDimensions.subscribe(function() {
|
||||||
|
if (!self.enabled) return;
|
||||||
|
|
||||||
var bedDimensions = self.settings.printer_bedDimensions();
|
var bedDimensions = self.settings.printer_bedDimensions();
|
||||||
if (bedDimensions && bedDimensions.hasOwnProperty("x") && bedDimensions.hasOwnProperty("y")) {
|
if (!bedDimensions || (!bedDimensions.hasOwnProperty("x") && !bedDimensions.hasOwnProperty("y") && !bedDimensions.hasOwnProperty("r"))) return;
|
||||||
options["renderer"] = {
|
|
||||||
bed: self.settings.printer_bedDimensions()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GCODE.ui.updateOptions(options);
|
GCODE.ui.updateOptions({
|
||||||
|
renderer: {
|
||||||
|
bed: bedDimensions
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
self.settings.printer_invertAxes.subscribe(function() {
|
||||||
|
if (!self.enabled) return;
|
||||||
|
if (!self.settings.printer_invertAxes()) return;
|
||||||
|
|
||||||
|
GCODE.ui.updateOptions({
|
||||||
|
renderer: {
|
||||||
|
invertAxes: {
|
||||||
|
x: self.settings.printer_invertX(),
|
||||||
|
y: self.settings.printer_invertY()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
self.loadedFilename = undefined;
|
self.loadedFilename = undefined;
|
||||||
|
|
@ -288,7 +305,7 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) {
|
||||||
self.approveLargeFile = function() {
|
self.approveLargeFile = function() {
|
||||||
self.waitForApproval(false);
|
self.waitForApproval(false);
|
||||||
self.loadFile(self.selectedFile.name(), self.selectedFile.date());
|
self.loadFile(self.selectedFile.name(), self.selectedFile.date());
|
||||||
}
|
};
|
||||||
|
|
||||||
self._onProgress = function(type, percentage) {
|
self._onProgress = function(type, percentage) {
|
||||||
self.ui_progress_type(type);
|
self.ui_progress_type(type);
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
|
||||||
if (callback) callback();
|
if (callback) callback();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
self.fromResponse = function(response) {
|
self.fromResponse = function(response) {
|
||||||
self.api_enabled(response.api.enabled);
|
self.api_enabled(response.api.enabled);
|
||||||
|
|
@ -244,7 +244,7 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
|
||||||
self.system_actions(response.system.actions);
|
self.system_actions(response.system.actions);
|
||||||
|
|
||||||
self.terminalFilters(response.terminalFilters);
|
self.terminalFilters(response.terminalFilters);
|
||||||
}
|
};
|
||||||
|
|
||||||
self.saveData = function() {
|
self.saveData = function() {
|
||||||
var data = {
|
var data = {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue