Made gcode viewer capable of handling progress information

GCode viewer now updates its visualization (during printing) based on the current progress percentage, not based on the current line (which will not be available any more as part of the progress tuple with gcode streaming)
This commit is contained in:
Gina Häußge 2013-06-09 14:00:15 +02:00
parent 41aa633f38
commit 003109d59e
5 changed files with 242 additions and 88 deletions

View file

@ -223,42 +223,31 @@
var assumeNonDC = false;
for(var i=0;i<gcode.length;i++){
// for(var len = gcode.length- 1, i=0;i!=len;i++){
x=undefined;
y=undefined;
z=undefined;
retract = 0;
var line = gcode[i].line;
var percentage = gcode[i].percentage;
extrude=false;
gcode[i] = gcode[i].split(/[\(;]/)[0];
line = line.split(/[\(;]/)[0];
// prevRetract=0;
// retract=0;
// if(gcode[i].match(/^(?:G0|G1)\s+/i)){
if(reg.test(gcode[i])){
var args = gcode[i].split(/\s/);
if(reg.test(line)){
var args = line.split(/\s/);
for(j=0;j<args.length;j++){
// console.log(args);
// if(!args[j])continue;
switch(argChar = args[j].charAt(0).toLowerCase()){
case 'x':
x=args[j].slice(1);
// if(x === prevX){
// x=undefined;
// }
break;
case 'y':
y=args[j].slice(1);
// if(y===prevY){
// y=undefined;
// }
break;
case 'z':
z=args[j].slice(1);
z = Number(z);
if(z == prevZ)continue;
// z = Number(z);
if(z == prevZ) continue;
if(z_heights.hasOwnProperty(z)){
layer = z_heights[z];
}else{
@ -267,9 +256,6 @@
}
sendLayer = layer;
sendLayerZ = z;
// if(parseFloat(prevZ) < )
// if(args[j].charAt(1) === "-")layer--;
// else layer++;
prevZ = z;
break;
case 'e':
@ -292,13 +278,11 @@
retract = -1;
}
else if(prev_extrude["abs"]==0){
// if(prevRetract <0 )prevRetract=retract;
retract = 0;
}else if(prev_extrude["abs"]>0&&prevRetract < 0){
prevRetract = 0;
retract = 1;
} else {
// prevRetract = retract;
retract = 0;
}
prev_extrude[argChar] = numSlice;
@ -317,24 +301,24 @@
prev_extrude["abs"] = Math.sqrt((prevX-x)*(prevX-x)+(prevY-y)*(prevY-y));
}
if(!model[layer])model[layer]=[];
if(typeof(x) !== 'undefined' || typeof(y) !== 'undefined' ||typeof(z) !== 'undefined'||retract!=0) model[layer][model[layer].length] = {x: Number(x), y: Number(y), z: Number(z), extrude: extrude, retract: Number(retract), noMove: false, extrusion: (extrude||retract)?Number(prev_extrude["abs"]):0, prevX: Number(prevX), prevY: Number(prevY), prevZ: Number(prevZ), speed: Number(lastF), gcodeLine: Number(i)};
if(typeof(x) !== 'undefined' || typeof(y) !== 'undefined' ||typeof(z) !== 'undefined'||retract!=0) model[layer][model[layer].length] = {x: Number(x), y: Number(y), z: Number(z), extrude: extrude, retract: Number(retract), noMove: false, extrusion: (extrude||retract)?Number(prev_extrude["abs"]):0, prevX: Number(prevX), prevY: Number(prevY), prevZ: Number(prevZ), speed: Number(lastF), gcodeLine: Number(i), percentage: percentage};
//{x: x, y: y, z: z, extrude: extrude, retract: retract, noMove: false, extrusion: (extrude||retract)?prev_extrude["abs"]:0, prevX: prevX, prevY: prevY, prevZ: prevZ, speed: lastF, gcodeLine: i};
if(typeof(x) !== 'undefined') prevX = x;
if(typeof(y) !== 'undefined') prevY = y;
} else if(gcode[i].match(/^(?:M82)/i)){
} else if(line.match(/^(?:M82)/i)){
extrudeRelative = false;
}else if(gcode[i].match(/^(?:G91)/i)){
}else if(line.match(/^(?:G91)/i)){
extrudeRelative=true;
}else if(gcode[i].match(/^(?:G90)/i)){
}else if(line.match(/^(?:G90)/i)){
extrudeRelative=false;
}else if(gcode[i].match(/^(?:M83)/i)){
}else if(line.match(/^(?:M83)/i)){
extrudeRelative=true;
}else if(gcode[i].match(/^(?:M101)/i)){
}else if(line.match(/^(?:M101)/i)){
dcExtrude=true;
}else if(gcode[i].match(/^(?:M103)/i)){
}else if(line.match(/^(?:M103)/i)){
dcExtrude=false;
}else if(gcode[i].match(/^(?:G92)/i)){
var args = gcode[i].split(/\s/);
}else if(line.match(/^(?:G92)/i)){
var args = line.split(/\s/);
for(j=0;j<args.length;j++){
switch(argChar = args[j].charAt(0).toLowerCase()){
case 'x':
@ -354,16 +338,15 @@
else {
prev_extrude[argChar] = numSlice;
}
// prevZ = z;
break;
default:
break;
}
}
if(!model[layer])model[layer]=[];
if(typeof(x) !== 'undefined' || typeof(y) !== 'undefined' ||typeof(z) !== 'undefined') model[layer][model[layer].length] = {x: parseFloat(x), y: parseFloat(y), z: parseFloat(z), extrude: extrude, retract: parseFloat(retract), noMove: true, extrusion: (extrude||retract)?parseFloat(prev_extrude["abs"]):0, prevX: parseFloat(prevX), prevY: parseFloat(prevY), prevZ: parseFloat(prevZ), speed: parseFloat(lastF),gcodeLine: parseFloat(i)};
}else if(gcode[i].match(/^(?:G28)/i)){
var args = gcode[i].split(/\s/);
if(typeof(x) !== 'undefined' || typeof(y) !== 'undefined' ||typeof(z) !== 'undefined') model[layer][model[layer].length] = {x: parseFloat(x), y: parseFloat(y), z: parseFloat(z), extrude: extrude, retract: parseFloat(retract), noMove: true, extrusion: (extrude||retract)?parseFloat(prev_extrude["abs"]):0, prevX: parseFloat(prevX), prevY: parseFloat(prevY), prevZ: parseFloat(prevZ), speed: parseFloat(lastF),gcodeLine: parseFloat(i), percentage: percentage};
}else if(line.match(/^(?:G28)/i)){
var args = line.split(/\s/);
for(j=0;j<args.length;j++){
switch(argChar = args[j].charAt(0).toLowerCase()){
case 'x':
@ -405,17 +388,11 @@
}
prevZ = z;
}
// x=0, y=0,z=0,prevZ=0, extrude=false;
// if(typeof(prevX) === 'undefined'){prevX=0;}
// if(typeof(prevY) === 'undefined'){prevY=0;}
if(!model[layer])model[layer]=[];
if(typeof(x) !== 'undefined' || typeof(y) !== 'undefined' ||typeof(z) !== 'undefined'||retract!=0) model[layer][model[layer].length] = {x: Number(x), y: Number(y), z: Number(z), extrude: extrude, retract: Number(retract), noMove: false, extrusion: (extrude||retract)?Number(prev_extrude["abs"]):0, prevX: Number(prevX), prevY: Number(prevY), prevZ: Number(prevZ), speed: Number(lastF), gcodeLine: Number(i)};
// if(typeof(x) !== 'undefined' || typeof(y) !== 'undefined' ||typeof(z) !== 'undefined') model[layer][model[layer].length] = {x: x, y: y, z: z, extrude: extrude, retract: retract, noMove:false, extrusion: (extrude||retract)?prev_extrude["abs"]:0, prevX: prevX, prevY: prevY, prevZ: prevZ, speed: lastF, gcodeLine: parseFloat(i)};
if(typeof(x) !== 'undefined' || typeof(y) !== 'undefined' ||typeof(z) !== 'undefined'||retract!=0) model[layer][model[layer].length] = {x: Number(x), y: Number(y), z: Number(z), extrude: extrude, retract: Number(retract), noMove: false, extrusion: (extrude||retract)?Number(prev_extrude["abs"]):0, prevX: Number(prevX), prevY: Number(prevY), prevZ: Number(prevZ), speed: Number(lastF), gcodeLine: Number(i), percentage: percentage};
}
if(typeof(sendLayer) !== "undefined"){
// sendLayerToParent(sendLayer, sendLayerZ, i/gcode.length*100);
// sendLayer = undefined;
if(i-lastSend > gcode.length*0.02 && sendMultiLayer.length != 0){
lastSend = i;
@ -429,13 +406,7 @@
sendLayerZ = undefined;
}
}
// sendMultiLayer[sendMultiLayer.length] = layer;
// sendMultiLayerZ[sendMultiLayerZ.length] = z;
sendMultiLayerToParent(sendMultiLayer, sendMultiLayerZ, i/gcode.length*100);
// if(gCodeOptions["sortLayers"])sortLayers();
// if(gCodeOptions["purgeEmptyLayers"])purgeLayers();
};

View file

@ -23,57 +23,73 @@ GCODE.gCodeReader = (function(){
purgeEmptyLayers: true,
analyzeModel: false
};
var linesCmdIndex = {};
var prepareGCode = function(){
var percentageTree = undefined;
var prepareGCode = function(totalSize){
if(!lines)return;
gcode = [];
var i, tmp;
var i, tmp, byteCount;
byteCount = 0;
for(i=0;i<lines.length;i++){
// if(lines[i].match(/^(G0|G1|G90|G91|G92|M82|M83|G28)/i))gcode.push(lines[i]);
byteCount += lines[i].length + 1; // line length + \n
tmp = lines[i].indexOf(";");
if(tmp > 1 || tmp === -1) {
gcode.push(lines[i]);
gcode.push({line: lines[i], percentage: byteCount * 100 / totalSize});
}
}
lines = [];
// console.log("GCode prepared");
};
var sortLayers = function(){
var sortedZ = [];
var tmpModel = [];
// var cnt = 0;
// console.log(z_heights);
for(var layer in z_heights){
sortedZ[z_heights[layer]] = layer;
// cnt++;
}
// console.log("cnt is " + cnt);
sortedZ.sort(function(a,b){
return a-b;
});
// console.log(sortedZ);
// console.log(model.length);
for(var i=0;i<sortedZ.length;i++){
// console.log("i is " + i +" and sortedZ[i] is " + sortedZ[i] + "and z_heights[] is " + z_heights[sortedZ[i]] );
if(typeof(z_heights[sortedZ[i]]) === 'undefined')continue;
tmpModel[i] = model[z_heights[sortedZ[i]]];
}
model = tmpModel;
// console.log(model.length);
delete tmpModel;
};
var prepareLinesIndex = function(){
linesCmdIndex = {};
percentageTree = undefined;
for (var l in model){
for (var i=0; i< model[l].length; i++){
linesCmdIndex[model[l][i].gcodeLine] = {layer: l, cmd: i};
for (var l in model) {
for (var i=0; i< model[l].length; i++) {
var percentage = model[l][i].percentage;
var value = {layer: l, cmd: i};
if (!percentageTree) {
percentageTree = new AVLTree({key: percentage, value: value}, "key");
} else {
percentageTree.add({key: percentage, value: value});
}
}
}
}
};
var searchInPercentageTree = function(key) {
if (percentageTree === undefined) {
return undefined;
}
var elements = percentageTree.findBest(key);
if (elements.length == 0) {
return undefined;
}
return elements[0];
};
var purgeLayers = function(){
var purge=true;
@ -102,13 +118,13 @@ GCODE.gCodeReader = (function(){
return {
loadFile: function(reader){
// console.log("loadFile");
model = [];
z_heights = [];
var totalSize = reader.target.result.length;
lines = reader.target.result.split(/\n/);
reader.target.result = null;
prepareGCode();
prepareGCode(totalSize);
worker.postMessage({
"cmd":"parseGCode",
@ -129,32 +145,21 @@ GCODE.gCodeReader = (function(){
}
},
passDataToRenderer: function(){
// console.log(model);
if(gCodeOptions["sortLayers"])sortLayers();
// console.log(model);
if(gCodeOptions["purgeEmptyLayers"])purgeLayers();
prepareLinesIndex();
// console.log(model);
GCODE.renderer.doRender(model, 0);
// GCODE.renderer3d.setModel(model);
},
processLayerFromWorker: function(msg){
// var cmds = msg.cmds;
// var layerNum = msg.layerNum;
// var zHeightObject = msg.zHeightObject;
// var isEmpty = msg.isEmpty;
// console.log(zHeightObject);
model[msg.layerNum] = msg.cmds;
z_heights[msg.zHeightObject.zValue] = msg.zHeightObject.layer;
// GCODE.renderer.doRender(model, msg.layerNum);
},
processMultiLayerFromWorker: function(msg){
for(var i=0;i<msg.layerNum.length;i++){
model[msg.layerNum[i]] = msg.model[msg.layerNum[i]];
z_heights[msg.zHeightObject.zValue[i]] = msg.layerNum[i];
}
// console.log(model);
},
processAnalyzeModelDone: function(msg){
min = msg.min;
@ -190,8 +195,13 @@ GCODE.gCodeReader = (function(){
var result = {first: model[layer][fromSegments].gcodeLine, last: model[layer][toSegments].gcodeLine};
return result;
},
getLinesCmdIndex: function(line){
return linesCmdIndex[line];
getCmdIndexForPercentage: function(percentage) {
var command = searchInPercentageTree(percentage);
if (command === undefined) {
return undefined
} else {
return command.value;
}
}
}
}());

View file

@ -0,0 +1,169 @@
// AVLTree ///////////////////////////////////////////////////////////////////
// self file is originally from the Concentré XML project (version 0.2.1)
// Licensed under GPL and LGPL
//
// Modified by Jeremy Stephens.
//
// Taken from: https://gist.github.com/viking/2424106, modified to not only use string literals when searching
// Pass in the attribute you want to use for comparing
function AVLTree(n, attr) {
var self = this;
self.attr = attr;
self.left = null;
self.right = null;
self.node = n;
self.depth = 1;
self.elements = [n];
self.balance = function() {
var ldepth = self.left == null ? 0 : self.left.depth;
var rdepth = self.right == null ? 0 : self.right.depth;
if (ldepth > rdepth + 1) {
// LR or LL rotation
var lldepth = self.left.left == null ? 0 : self.left.left.depth;
var lrdepth = self.left.right == null ? 0 : self.left.right.depth;
if (lldepth < lrdepth) {
// LR rotation consists of a RR rotation of the left child
self.left.rotateRR();
// plus a LL rotation of self node, which happens anyway
}
self.rotateLL();
} else if (ldepth + 1 < rdepth) {
// RR or RL rorarion
var rrdepth = self.right.right == null ? 0 : self.right.right.depth;
var rldepth = self.right.left == null ? 0 : self.right.left.depth;
if (rldepth > rrdepth) {
// RR rotation consists of a LL rotation of the right child
self.right.rotateLL();
// plus a RR rotation of self node, which happens anyway
}
self.rotateRR();
}
}
self.rotateLL = function() {
// the left side is too long => rotate from the left (_not_ leftwards)
var nodeBefore = self.node;
var elementsBefore = self.elements;
var rightBefore = self.right;
self.node = self.left.node;
self.elements = self.left.elements;
self.right = self.left;
self.left = self.left.left;
self.right.left = self.right.right;
self.right.right = rightBefore;
self.right.node = nodeBefore;
self.right.elements = elementsBefore;
self.right.updateInNewLocation();
self.updateInNewLocation();
}
self.rotateRR = function() {
// the right side is too long => rotate from the right (_not_ rightwards)
var nodeBefore = self.node;
var elementsBefore = self.elements;
var leftBefore = self.left;
self.node = self.right.node;
self.elements = self.right.elements;
self.left = self.right;
self.right = self.right.right;
self.left.right = self.left.left;
self.left.left = leftBefore;
self.left.node = nodeBefore;
self.left.elements = elementsBefore;
self.left.updateInNewLocation();
self.updateInNewLocation();
}
self.updateInNewLocation = function() {
self.getDepthFromChildren();
}
self.getDepthFromChildren = function() {
self.depth = self.node == null ? 0 : 1;
if (self.left != null) {
self.depth = self.left.depth + 1;
}
if (self.right != null && self.depth <= self.right.depth) {
self.depth = self.right.depth + 1;
}
}
self.compare = function(n1, n2) {
var v1 = n1[self.attr];
var v2 = n2[self.attr];
if (v1 == v2) {
return 0;
}
if (v1 < v2) {
return -1;
}
return 1;
}
self.add = function(n) {
var o = self.compare(n, self.node);
if (o == 0) {
self.elements.push(n);
return false;
}
var ret = false;
if (o == -1) {
if (self.left == null) {
self.left = new AVLTree(n, self.attr);
ret = true;
} else {
ret = self.left.add(n);
if (ret) {
self.balance();
}
}
} else if (o == 1) {
if (self.right == null) {
self.right = new AVLTree(n, self.attr);
ret = true;
} else {
ret = self.right.add(n);
if (ret) {
self.balance();
}
}
}
if (ret) {
self.getDepthFromChildren();
}
return ret;
}
self.findBest = function(value) {
if (value < self.node[self.attr]) {
if (self.left != null) {
return self.left.findBest(value);
}
} else if (value > self.node[self.attr]) {
if (self.right != null) {
return self.right.findBest(value);
}
}
return self.elements;
}
self.find = function(value) {
var elements = self.findBest(value);
for (var i = 0; i < elements.length; i++) {
if (elements[i][self.attr] == value) {
return elements;
}
}
return false;
}
}

View file

@ -1093,13 +1093,16 @@ function GcodeViewModel(loginStateViewModel) {
}
self._processData = function(data) {
if(!self.enabled)return;
if (!self.enabled) return;
if (!data.job.filename) return;
if(self.loadedFilename == data.job.filename) {
var cmdIndex = GCODE.gCodeReader.getLinesCmdIndex(data.progress.progress);
if(cmdIndex){
GCODE.renderer.render(cmdIndex.layer, 0, cmdIndex.cmd);
GCODE.ui.updateLayerInfo(cmdIndex.layer);
if(self.loadedFilename && self.loadedFilename == data.job.filename) {
if (data.state.flags && (data.state.flags.printing || data.state.flags.paused)) {
var cmdIndex = GCODE.gCodeReader.getCmdIndexForPercentage(data.progress.progress * 100);
if(cmdIndex){
GCODE.renderer.render(cmdIndex.layer, 0, cmdIndex.cmd);
GCODE.ui.updateLayerInfo(cmdIndex.layer);
}
}
self.errorCount = 0
} else if (data.job.filename) {

View file

@ -579,6 +579,7 @@
<script type="text/javascript" src="{{ url_for('static', filename='js/modernizr.custom.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/underscore.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/knockout.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/avltree.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap/bootstrap.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap/bootstrap-modalmanager.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap/bootstrap-modal.js') }}"></script>