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
(cherry picked from commit 030ffe6)
This commit is contained in:
parent
c552e9e9be
commit
7028bbfbfd
4 changed files with 110 additions and 46 deletions
|
|
@ -44,6 +44,7 @@ GCODE.renderer = (function(){
|
|||
zoomInOnModel: false,
|
||||
zoomInOnBed: false,
|
||||
centerViewport: false,
|
||||
invertAxes: {x: false, y: false},
|
||||
|
||||
bed: {x: 200, y: 200},
|
||||
container: undefined,
|
||||
|
|
@ -56,6 +57,7 @@ GCODE.renderer = (function(){
|
|||
var scaleX = 1, scaleY = 1;
|
||||
var speeds = [];
|
||||
var speedsByLayer = {};
|
||||
var currentInvertX = false, currentInvertY = false;
|
||||
|
||||
var reRender = function(){
|
||||
var p1 = ctx.transformedPoint(0,0);
|
||||
|
|
@ -290,8 +292,11 @@ GCODE.renderer = (function(){
|
|||
|
||||
var i;
|
||||
|
||||
isNotCurrentLayer = typeof isNotCurrentLayer !== 'undefined' ? isNotCurrentLayer : false;
|
||||
//~~ store current layer values
|
||||
|
||||
isNotCurrentLayer = isNotCurrentLayer !== undefined ? isNotCurrentLayer : false;
|
||||
if (!isNotCurrentLayer) {
|
||||
// not not current layer == current layer => store layer number and from/to progress
|
||||
layerNumStore = layerNum;
|
||||
progressStore = {from: fromProgress, to: toProgress};
|
||||
}
|
||||
|
|
@ -301,67 +306,74 @@ GCODE.renderer = (function(){
|
|||
var cmds = model[layerNum];
|
||||
var x, y;
|
||||
|
||||
if (fromProgress > 0) {
|
||||
prevX = cmds[fromProgress-1].x * zoomFactor;
|
||||
prevY = -cmds[fromProgress-1].y * zoomFactor;
|
||||
} else if (fromProgress === 0 && layerNum == 0) {
|
||||
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') {
|
||||
//~~ find our initial prevX/prevY tuple
|
||||
|
||||
if (cmds[0].prevX !== undefined && cmds[0].prevY !== undefined) {
|
||||
// command contains prevX/prevY values, use those
|
||||
prevX = cmds[0].prevX * zoomFactor;
|
||||
prevY = -cmds[0].prevY * zoomFactor;
|
||||
} else {
|
||||
if (model[layerNum-1]) {
|
||||
prevX = undefined;
|
||||
prevY = undefined;
|
||||
for (i = model[layerNum-1].length-1; i >= 0; i--) {
|
||||
if (prevX === undefined && model[layerNum-1][i].x !== undefined) prevX = model[layerNum-1][i].x * zoomFactor;
|
||||
if (prevY === undefined && model[layerNum-1][i].y !== undefined) prevY =- model[layerNum-1][i].y * zoomFactor;
|
||||
}
|
||||
if (prevX === undefined) prevX=0;
|
||||
if (prevY === undefined) prevY=0;
|
||||
} else {
|
||||
prevX = 0;
|
||||
prevY = 0;
|
||||
prevY = -1 * cmds[0].prevY * zoomFactor;
|
||||
} else if (fromProgress > 0) {
|
||||
// previous command in same layer exists, use x/y as prevX/prevY
|
||||
prevX = cmds[fromProgress - 1].x * zoomFactor;
|
||||
prevY = -cmds[fromProgress - 1].y * zoomFactor;
|
||||
} else if (model[layerNum - 1]) {
|
||||
// previous layer exists, use last x/y as prevX/prevY
|
||||
prevX = undefined;
|
||||
prevY = undefined;
|
||||
for (i = model[layerNum-1].length-1; i >= 0; i--) {
|
||||
if (prevX === undefined && model[layerNum - 1][i].x !== undefined) prevX = model[layerNum - 1][i].x * zoomFactor;
|
||||
if (prevY === undefined && model[layerNum - 1][i].y !== undefined) prevY =- model[layerNum - 1][i].y * zoomFactor;
|
||||
}
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
ctx.lineWidth = 1;
|
||||
|
||||
if (typeof(cmds[i]) === 'undefined') continue;
|
||||
|
||||
if (typeof(cmds[i].prevX) !== 'undefined' && typeof(cmds[i].prevY) !== 'undefined') {
|
||||
// override new (prevX, prevY)
|
||||
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)) {
|
||||
x = prevX / zoomFactor;
|
||||
} else {
|
||||
x = cmds[i].x;
|
||||
}
|
||||
|
||||
// new y
|
||||
if (typeof(cmds[i].y) === 'undefined' || isNaN(cmds[i].y)) {
|
||||
y = prevY / zoomFactor;
|
||||
} else {
|
||||
y = -cmds[i].y;
|
||||
}
|
||||
|
||||
// current tool
|
||||
var tool = cmds[i].tool;
|
||||
if (tool === undefined) tool = 0;
|
||||
|
||||
// line color based on tool
|
||||
var lineColor = renderOptions["colorLine"][tool];
|
||||
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 shade = tool * 0.15;
|
||||
|
||||
if (!cmds[i].extrude && !cmds[i].noMove) {
|
||||
// neither extrusion nor move
|
||||
if (cmds[i].retract == -1) {
|
||||
// retract => draw dot if configured to do so
|
||||
if (renderOptions["showRetracts"]) {
|
||||
ctx.strokeStyle = pusher.color(renderOptions["colorRetract"]).shade(shade).alpha(alpha).html();
|
||||
ctx.fillStyle = pusher.color(renderOptions["colorRetract"]).shade(shade).alpha(alpha).html();
|
||||
|
|
@ -371,7 +383,9 @@ GCODE.renderer = (function(){
|
|||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
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.beginPath();
|
||||
ctx.moveTo(prevX, prevY);
|
||||
|
|
@ -380,6 +394,7 @@ GCODE.renderer = (function(){
|
|||
}
|
||||
} else if(cmds[i].extrude) {
|
||||
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.lineWidth = renderOptions['extrusionWidth'];
|
||||
ctx.beginPath();
|
||||
|
|
@ -387,6 +402,7 @@ GCODE.renderer = (function(){
|
|||
ctx.lineTo(x*zoomFactor,y*zoomFactor);
|
||||
ctx.stroke();
|
||||
} else {
|
||||
// we were previously retracting, now we are restarting => draw dot if configured to do so
|
||||
if (renderOptions["showRetracts"]) {
|
||||
ctx.strokeStyle = pusher.color(renderOptions["colorRestart"]).shade(shade).alpha(alpha).html();
|
||||
ctx.fillStyle = pusher.color(renderOptions["colorRestart"]).shade(shade).alpha(alpha).html();
|
||||
|
|
@ -397,6 +413,8 @@ GCODE.renderer = (function(){
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set new (prevX, prevY)
|
||||
prevX = x * zoomFactor;
|
||||
prevY = y * zoomFactor;
|
||||
}
|
||||
|
|
@ -451,8 +469,8 @@ GCODE.renderer = (function(){
|
|||
var scaleF = mdlInfo.modelSize.x > mdlInfo.modelSize.y ? (canvas.width - 10) / mdlInfo.modelSize.x : (canvas.height - 10) / mdlInfo.modelSize.y;
|
||||
scaleF /= zoomFactor;
|
||||
if (transform.a && transform.d) {
|
||||
scaleX = scaleF / transform.a;
|
||||
scaleY = scaleF / transform.d;
|
||||
scaleX = scaleF / transform.a * (renderOptions["invertAxes"]["x"] ? -1 : 1);
|
||||
scaleY = scaleF / transform.d * (renderOptions["invertAxes"]["y"] ? -1 : 1);
|
||||
ctx.translate(pt.x,pt.y);
|
||||
ctx.scale(scaleX, scaleY);
|
||||
ctx.translate(-pt.x, -pt.y);
|
||||
|
|
@ -464,13 +482,37 @@ 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 *******
|
||||
return {
|
||||
init: function(){
|
||||
startCanvas();
|
||||
initialized = true;
|
||||
zoomFactor = Math.min((canvas.width - 10) / renderOptions["bed"]["x"], (canvas.height - 10) / renderOptions["bed"]["y"]);
|
||||
ctx.translate((canvas.width - renderOptions["bed"]["x"] * zoomFactor) / 2, renderOptions["bed"]["y"] * zoomFactor + (canvas.height - renderOptions["bed"]["y"] * zoomFactor) / 2);
|
||||
var bedWidth = renderOptions["bed"]["x"];
|
||||
var bedHeight = renderOptions["bed"]["y"];
|
||||
if(renderOptions["bed"]["circular"]) {
|
||||
bedWidth = bedHeight = renderOptions["bed"]["r"] * 2;
|
||||
}
|
||||
zoomFactor = Math.min((canvas.width - 10) / bedWidth, (canvas.height - 10) / bedHeight);
|
||||
|
||||
offsetModelX = 0;
|
||||
offsetModelY = 0;
|
||||
|
|
@ -487,7 +529,7 @@ GCODE.renderer = (function(){
|
|||
|
||||
dirty = true;
|
||||
renderOptions[opt] = options[opt];
|
||||
if ($.inArray(opt, ["moveModel", "centerViewport", "zoomInOnModel", "bed"]) > -1) {
|
||||
if ($.inArray(opt, ["moveModel", "centerViewport", "zoomInOnModel", "bed", "invertAxes"]) > -1) {
|
||||
mustRefresh = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -568,6 +610,7 @@ GCODE.renderer = (function(){
|
|||
}
|
||||
}
|
||||
|
||||
applyInversion();
|
||||
applyOffsets(mdlInfo);
|
||||
applyZoom(mdlInfo);
|
||||
|
||||
|
|
|
|||
|
|
@ -180,8 +180,12 @@ GCODE.ui = (function(){
|
|||
|
||||
updateOptions: function(options) {
|
||||
setOptions(options.ui);
|
||||
GCODE.gCodeReader.setOption(options.reader);
|
||||
GCODE.renderer.setOption(options.renderer);
|
||||
if (options.reader) {
|
||||
GCODE.gCodeReader.setOption(options.reader);
|
||||
}
|
||||
if (options.renderer) {
|
||||
GCODE.renderer.setOption(options.renderer);
|
||||
}
|
||||
},
|
||||
|
||||
changeSelectedLayer: function(newLayerNum) {
|
||||
|
|
|
|||
|
|
@ -97,21 +97,38 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) {
|
|||
// subscribe to relevant printer settings...
|
||||
self.settings.printer_extruderOffsets.subscribe(function() {
|
||||
if (!self.enabled) return;
|
||||
if (!self.settings.printer_extruderOffsets()) return;
|
||||
|
||||
var options = {
|
||||
GCODE.ui.updateOptions({
|
||||
reader: {
|
||||
toolOffsets: self.settings.printer_extruderOffsets()
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
self.settings.printer_bedDimensions.subscribe(function() {
|
||||
if (!self.enabled) return;
|
||||
|
||||
var bedDimensions = self.settings.printer_bedDimensions();
|
||||
if (bedDimensions && bedDimensions.hasOwnProperty("x") && bedDimensions.hasOwnProperty("y")) {
|
||||
options["renderer"] = {
|
||||
bed: self.settings.printer_bedDimensions()
|
||||
}
|
||||
}
|
||||
if (!bedDimensions || (!bedDimensions.hasOwnProperty("x") && !bedDimensions.hasOwnProperty("y") && !bedDimensions.hasOwnProperty("r"))) return;
|
||||
|
||||
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;
|
||||
|
|
@ -289,7 +306,7 @@ function GcodeViewModel(loginStateViewModel, settingsViewModel) {
|
|||
self.approveLargeFile = function() {
|
||||
self.waitForApproval(false);
|
||||
self.loadFile(self.selectedFile.name(), self.selectedFile.date());
|
||||
}
|
||||
};
|
||||
|
||||
self._onProgress = function(type, percentage) {
|
||||
self.ui_progress_type(type);
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
|
|||
if (callback) callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
self.fromResponse = function(response) {
|
||||
self.api_enabled(response.api.enabled);
|
||||
|
|
@ -238,7 +238,7 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
|
|||
self.system_actions(response.system.actions);
|
||||
|
||||
self.terminalFilters(response.terminalFilters);
|
||||
}
|
||||
};
|
||||
|
||||
self.saveData = function() {
|
||||
var data = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue