474 lines
20 KiB
JavaScript
474 lines
20 KiB
JavaScript
/**
|
|
* User: hudbrog (hudbrog@gmail.com)
|
|
* Date: 10/24/12
|
|
* Time: 12:18 PM
|
|
*/
|
|
|
|
var gcode;
|
|
var firstReport;
|
|
var z_heights = {};
|
|
var model = [];
|
|
var gCodeOptions = {
|
|
sortLayers: false,
|
|
purgeEmptyLayers: true,
|
|
analyzeModel: false
|
|
};
|
|
var max = {x: undefined, y: undefined, z: undefined};
|
|
var min = {x: undefined, y: undefined, z: undefined};
|
|
var modelSize = {x: undefined, y: undefined, z: undefined};
|
|
var filamentByLayer = {};
|
|
var totalFilament=0;
|
|
var printTime=0;
|
|
var printTimeByLayer = {};
|
|
var layerHeight=0;
|
|
var layerCnt = 0;
|
|
var speeds = {extrude: [], retract: [], move: []};
|
|
var speedsByLayer = {extrude: {}, retract: {}, move: {}};
|
|
|
|
|
|
|
|
var sendLayerToParent = function(layerNum, z, progress){
|
|
self.postMessage({
|
|
"cmd": "returnLayer",
|
|
"msg": {
|
|
cmds: model[layerNum],
|
|
layerNum: layerNum,
|
|
zHeightObject: {zValue: z, layer: z_heights[z]},
|
|
isEmpty: false,
|
|
progress: progress
|
|
}
|
|
});
|
|
};
|
|
|
|
var sendMultiLayerToParent = function(layerNum, z, progress){
|
|
var tmpModel = [];
|
|
var tmpZHeight = {};
|
|
|
|
for(var i=0;i<layerNum.length;i++){
|
|
tmpModel[layerNum[i]] = model[layerNum[i]];
|
|
tmpZHeight[layerNum[i]] = z_heights[z[i]];
|
|
}
|
|
|
|
self.postMessage({
|
|
"cmd": "returnMultiLayer",
|
|
"msg": {
|
|
model: tmpModel,
|
|
layerNum: layerNum,
|
|
zHeightObject: {zValue: z, layer: tmpZHeight},
|
|
isEmpty: false,
|
|
progress: progress
|
|
}
|
|
});
|
|
};
|
|
|
|
|
|
var sendSizeProgress = function(progress){
|
|
self.postMessage({
|
|
"cmd": "analyzeProgress",
|
|
"msg": {
|
|
progress: progress,
|
|
printTime: printTime
|
|
}
|
|
});
|
|
};
|
|
|
|
var sendAnalyzeDone = function(){
|
|
self.postMessage({
|
|
"cmd": "analyzeDone",
|
|
"msg": {
|
|
max: max,
|
|
min: min,
|
|
modelSize: modelSize,
|
|
totalFilament:totalFilament,
|
|
filamentByLayer: filamentByLayer,
|
|
printTime: printTime,
|
|
layerHeight: layerHeight,
|
|
layerCnt: layerCnt,
|
|
layerTotal: model.length,
|
|
speeds: speeds,
|
|
speedsByLayer: speedsByLayer,
|
|
printTimeByLayer: printTimeByLayer
|
|
}
|
|
});
|
|
};
|
|
|
|
var purgeLayers = function(){
|
|
var purge=true;
|
|
for(var i=0;i<model.length;i++){
|
|
purge=true;
|
|
if(!model[i])purge=true;
|
|
else {
|
|
for(var j=0;j<model[i].length;j++){
|
|
if(model[i][j].extrude)purge=false;
|
|
}
|
|
}
|
|
if(!purge){
|
|
layerCnt+=1;
|
|
}
|
|
}
|
|
// self.postMessage('LayerCnt: ' + layerCnt);
|
|
};
|
|
|
|
|
|
var analyzeModel = function(){
|
|
var i,j;
|
|
var x_ok=false, y_ok=false;
|
|
var cmds;
|
|
var tmp1= 0, tmp2=0;
|
|
var speedIndex=0;
|
|
var type;
|
|
var printTimeAdd=0;
|
|
// var moveTime=0;
|
|
|
|
for(i=0;i<model.length;i++){
|
|
cmds = model[i];
|
|
if(!cmds)continue;
|
|
for(j=0;j<cmds.length;j++){
|
|
x_ok=false;
|
|
y_ok=false;
|
|
if(typeof(cmds[j].x) !== 'undefined'&&typeof(cmds[j].prevX) !== 'undefined'&&typeof(cmds[j].extrude) !== 'undefined'&&cmds[j].extrude&&!isNaN(cmds[j].x))
|
|
{
|
|
max.x = parseFloat(max.x)>parseFloat(cmds[j].x)?parseFloat(max.x):parseFloat(cmds[j].x);
|
|
max.x = parseFloat(max.x)>parseFloat(cmds[j].prevX)?parseFloat(max.x):parseFloat(cmds[j].prevX);
|
|
min.x = parseFloat(min.x)<parseFloat(cmds[j].x)?parseFloat(min.x):parseFloat(cmds[j].x);
|
|
min.x = parseFloat(min.x)<parseFloat(cmds[j].prevX)?parseFloat(min.x):parseFloat(cmds[j].prevX);
|
|
x_ok=true;
|
|
}
|
|
|
|
if(typeof(cmds[j].y) !== 'undefined'&&typeof(cmds[j].prevY) !== 'undefined'&&typeof(cmds[j].extrude) !== 'undefined'&&cmds[j].extrude&&!isNaN(cmds[j].y)){
|
|
max.y = parseFloat(max.y)>parseFloat(cmds[j].y)?parseFloat(max.y):parseFloat(cmds[j].y);
|
|
max.y = parseFloat(max.y)>parseFloat(cmds[j].prevY)?parseFloat(max.y):parseFloat(cmds[j].prevY);
|
|
min.y = parseFloat(min.y)<parseFloat(cmds[j].y)?parseFloat(min.y):parseFloat(cmds[j].y);
|
|
min.y = parseFloat(min.y)<parseFloat(cmds[j].prevY)?parseFloat(min.y):parseFloat(cmds[j].prevY);
|
|
y_ok=true;
|
|
}
|
|
|
|
if(typeof(cmds[j].prevZ) !== 'undefined'&&typeof(cmds[j].extrude) !== 'undefined'&&cmds[j].extrude&&!isNaN(cmds[j].prevZ)){
|
|
max.z = parseFloat(max.z)>parseFloat(cmds[j].prevZ)?parseFloat(max.z):parseFloat(cmds[j].prevZ);
|
|
min.z = parseFloat(min.z)<parseFloat(cmds[j].prevZ)?parseFloat(min.z):parseFloat(cmds[j].prevZ);
|
|
}
|
|
|
|
if(typeof(cmds[j].extrude) !== 'undefined'||cmds[j].retract!=0){
|
|
totalFilament+=cmds[j].extrusion;
|
|
if(!filamentByLayer[cmds[j].prevZ])filamentByLayer[cmds[j].prevZ]=0;
|
|
filamentByLayer[cmds[j].prevZ]+=cmds[j].extrusion;
|
|
}
|
|
|
|
if(x_ok&&y_ok){
|
|
printTimeAdd = Math.sqrt(Math.pow(parseFloat(cmds[j].x)-parseFloat(cmds[j].prevX),2)+Math.pow(parseFloat(cmds[j].y)-parseFloat(cmds[j].prevY),2))/(cmds[j].speed/60);
|
|
}else if(cmds[j].retract===0&&cmds[j].extrusion!==0){
|
|
tmp1 = Math.sqrt(Math.pow(parseFloat(cmds[j].x)-parseFloat(cmds[j].prevX),2)+Math.pow(parseFloat(cmds[j].y)-parseFloat(cmds[j].prevY),2))/(cmds[j].speed/60);
|
|
tmp2 = Math.abs(parseFloat(cmds[j].extrusion)/(cmds[j].speed/60));
|
|
printTimeAdd = tmp1>=tmp2?tmp1:tmp2;
|
|
}else if(cmds[j].retract!==0){
|
|
printTimeAdd = Math.abs(parseFloat(cmds[j].extrusion)/(cmds[j].speed/60));
|
|
}
|
|
|
|
printTime += printTimeAdd;
|
|
if(typeof(printTimeByLayer[cmds[j].prevZ])==='undefined'){printTimeByLayer[cmds[j].prevZ]=0;}
|
|
printTimeByLayer[cmds[j].prevZ] += printTimeAdd;
|
|
|
|
if(cmds[j].extrude&&cmds[j].retract===0){
|
|
type = 'extrude';
|
|
}else if(cmds[j].retract!==0){
|
|
type = 'retract';
|
|
}else if(!cmds[j].extrude&&cmds[j].retract===0){
|
|
type = 'move';
|
|
// if(cmds[j].prevZ == '17.1'){
|
|
// self.postMessage({cmd: 'Got speed ' + cmds[j].speed + 'with line ' + cmds[j].gcodeLine});
|
|
// }
|
|
}else {
|
|
self.postMessage({cmd: 'unknown type of move'});
|
|
type = 'unknown';
|
|
}
|
|
speedIndex = speeds[type].indexOf(cmds[j].speed);
|
|
if (speedIndex === -1) {
|
|
speeds[type].push(cmds[j].speed);
|
|
speedIndex = speeds[type].indexOf(cmds[j].speed);
|
|
}
|
|
if(typeof(speedsByLayer[type][cmds[j].prevZ]) === 'undefined'){
|
|
speedsByLayer[type][cmds[j].prevZ] = [];
|
|
}
|
|
if(speedsByLayer[type][cmds[j].prevZ].indexOf(cmds[j].speed) === -1){
|
|
speedsByLayer[type][cmds[j].prevZ][speedIndex] = cmds[j].speed;
|
|
}
|
|
|
|
}
|
|
sendSizeProgress(i/model.length*100);
|
|
|
|
}
|
|
purgeLayers();
|
|
|
|
modelSize.x = Math.abs(max.x - min.x);
|
|
modelSize.y = Math.abs(max.y - min.y);
|
|
modelSize.z = Math.abs(max.z - min.z);
|
|
layerHeight = (max.z-min.z)/(layerCnt-1);
|
|
|
|
sendAnalyzeDone();
|
|
};
|
|
|
|
var doParse = function(){
|
|
var argChar, numSlice;
|
|
model=[];
|
|
var sendLayer = undefined;
|
|
var sendLayerZ = 0;
|
|
var sendMultiLayer = [];
|
|
var sendMultiLayerZ = [];
|
|
var lastSend = 0;
|
|
// console.time("parseGCode timer");
|
|
var reg = new RegExp(/^(?:G0|G1)\s/i);
|
|
var comment = new RegExp()
|
|
var j, layer= 0, extrude=false, prevRetract= 0, retract=0, x, y, z=0, f, prevZ=0, prevX, prevY,lastF=4000, prev_extrude = {a: undefined, b: undefined, c: undefined, e: undefined, abs: undefined}, extrudeRelative=false;
|
|
var dcExtrude=false;
|
|
var assumeNonDC = false;
|
|
|
|
for(var i=0;i<gcode.length;i++){
|
|
x=undefined;
|
|
y=undefined;
|
|
z=undefined;
|
|
retract = 0;
|
|
|
|
var line = gcode[i].line;
|
|
var percentage = gcode[i].percentage;
|
|
|
|
extrude=false;
|
|
line = line.split(/[\(;]/)[0];
|
|
|
|
if(reg.test(line)){
|
|
var args = line.split(/\s/);
|
|
for(j=0;j<args.length;j++){
|
|
switch(argChar = args[j].charAt(0).toLowerCase()){
|
|
case 'x':
|
|
x=args[j].slice(1);
|
|
break;
|
|
case 'y':
|
|
y=args[j].slice(1);
|
|
break;
|
|
case 'z':
|
|
z=args[j].slice(1);
|
|
z = Number(z);
|
|
if(z == prevZ) continue;
|
|
if(z_heights.hasOwnProperty(z)){
|
|
layer = z_heights[z];
|
|
}else{
|
|
layer = model.length;
|
|
z_heights[z] = layer;
|
|
}
|
|
sendLayer = layer;
|
|
sendLayerZ = z;
|
|
prevZ = z;
|
|
break;
|
|
case 'e':
|
|
case 'a':
|
|
case 'b':
|
|
case 'c':
|
|
assumeNonDC = true;
|
|
numSlice = parseFloat(args[j].slice(1)).toFixed(3);
|
|
|
|
if(!extrudeRelative){
|
|
// absolute extrusion positioning
|
|
prev_extrude["abs"] = parseFloat(numSlice)-parseFloat(prev_extrude[argChar]);
|
|
|
|
}else{
|
|
prev_extrude["abs"] = parseFloat(numSlice);
|
|
}
|
|
extrude = prev_extrude["abs"]>0;
|
|
if(prev_extrude["abs"]<0){
|
|
prevRetract = -1;
|
|
retract = -1;
|
|
}
|
|
else if(prev_extrude["abs"]==0){
|
|
retract = 0;
|
|
}else if(prev_extrude["abs"]>0&&prevRetract < 0){
|
|
prevRetract = 0;
|
|
retract = 1;
|
|
} else {
|
|
retract = 0;
|
|
}
|
|
prev_extrude[argChar] = numSlice;
|
|
|
|
break;
|
|
case 'f':
|
|
numSlice = args[j].slice(1);
|
|
lastF = numSlice;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if(dcExtrude&&!assumeNonDC){
|
|
extrude = true;
|
|
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), 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(line.match(/^(?:M82)/i)){
|
|
extrudeRelative = false;
|
|
}else if(line.match(/^(?:G91)/i)){
|
|
extrudeRelative=true;
|
|
}else if(line.match(/^(?:G90)/i)){
|
|
extrudeRelative=false;
|
|
}else if(line.match(/^(?:M83)/i)){
|
|
extrudeRelative=true;
|
|
}else if(line.match(/^(?:M101)/i)){
|
|
dcExtrude=true;
|
|
}else if(line.match(/^(?:M103)/i)){
|
|
dcExtrude=false;
|
|
}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':
|
|
x=args[j].slice(1);
|
|
break;
|
|
case 'y':
|
|
y=args[j].slice(1);
|
|
break;
|
|
case 'z':
|
|
z=args[j].slice(1);
|
|
prevZ = z;
|
|
break;
|
|
case 'e'||'a'||'b'||'c':
|
|
numSlice = args[j].slice(1);
|
|
if(!extrudeRelative)
|
|
prev_extrude[argChar] = 0;
|
|
else {
|
|
prev_extrude[argChar] = numSlice;
|
|
}
|
|
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), 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':
|
|
x=args[j].slice(1);
|
|
break;
|
|
case 'y':
|
|
y=args[j].slice(1);
|
|
break;
|
|
case 'z':
|
|
z=args[j].slice(1);
|
|
z = Number(z);
|
|
if(z === prevZ)continue;
|
|
sendLayer = layer;
|
|
sendLayerZ = z;//}
|
|
if(z_heights.hasOwnProperty(z)){
|
|
layer = z_heights[z];
|
|
}else{
|
|
layer = model.length;
|
|
z_heights[z] = layer;
|
|
}
|
|
prevZ = z;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
// G28 with no arguments
|
|
if(args.length == 1){
|
|
//need to init values to default here
|
|
}
|
|
// if it's the first layer and G28 was without
|
|
if(layer==0&&typeof(z) === 'undefined'){
|
|
z=0;
|
|
if(z_heights.hasOwnProperty(z)){
|
|
layer = z_heights[z];
|
|
}else{
|
|
layer = model.length;
|
|
z_heights[z] = layer;
|
|
}
|
|
prevZ = z;
|
|
}
|
|
|
|
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), percentage: percentage};
|
|
}
|
|
if(typeof(sendLayer) !== "undefined"){
|
|
|
|
if(i-lastSend > gcode.length*0.02 && sendMultiLayer.length != 0){
|
|
lastSend = i;
|
|
sendMultiLayerToParent(sendMultiLayer, sendMultiLayerZ, i/gcode.length*100);
|
|
sendMultiLayer = [];
|
|
sendMultiLayerZ = [];
|
|
}
|
|
sendMultiLayer[sendMultiLayer.length] = sendLayer;
|
|
sendMultiLayerZ[sendMultiLayerZ.length] = sendLayerZ;
|
|
sendLayer = undefined;
|
|
sendLayerZ = undefined;
|
|
}
|
|
}
|
|
sendMultiLayerToParent(sendMultiLayer, sendMultiLayerZ, i/gcode.length*100);
|
|
};
|
|
|
|
|
|
var parseGCode = function(message){
|
|
gcode = message.gcode;
|
|
firstReport = message.options.firstReport;
|
|
|
|
|
|
doParse();
|
|
gcode = [];
|
|
self.postMessage({
|
|
"cmd": "returnModel",
|
|
"msg": {
|
|
// model: model
|
|
}
|
|
});
|
|
|
|
};
|
|
|
|
var runAnalyze = function(message){
|
|
analyzeModel();
|
|
model = [];
|
|
z_heights = [];
|
|
gcode = undefined;
|
|
firstReport = undefined;
|
|
z_heights = {};
|
|
model = [];
|
|
max = {x: undefined, y: undefined, z: undefined};
|
|
min = {x: undefined, y: undefined, z: undefined};
|
|
modelSize = {x: undefined, y: undefined, z: undefined};
|
|
filamentByLayer = {};
|
|
totalFilament=0;
|
|
printTime=0;
|
|
printTimeByLayer = {};
|
|
layerHeight=0;
|
|
layerCnt = 0;
|
|
speeds = {extrude: [], retract: [], move: []};
|
|
speedsByLayer = {extrude: {}, retract: {}, move: {}};
|
|
};
|
|
var setOption = function(options){
|
|
for(var opt in options){
|
|
gCodeOptions[opt] = options[opt];
|
|
}
|
|
};
|
|
|
|
onmessage = function (e){
|
|
var data = e.data;
|
|
// for some reason firefox doesn't garbage collect when something inside closures is deleted, so we delete and recreate whole object eaech time
|
|
switch (data.cmd) {
|
|
case 'parseGCode':
|
|
parseGCode(data.msg);
|
|
break;
|
|
case 'setOption':
|
|
setOption(data.msg);
|
|
break;
|
|
case 'analyzeModel':
|
|
runAnalyze(data.msg);
|
|
break;
|
|
|
|
default:
|
|
self.postMessage('Unknown command: ' + data.msg);
|
|
}
|
|
|
|
};
|