OctoPrint/octoprint/static/gcodeviewer/js/renderer.js

418 lines
15 KiB
JavaScript

/**
* User: hudbrog (hudbrog@gmail.com)
* Date: 10/20/12
* Time: 1:36 PM
* To change this template use File | Settings | File Templates.
*/
GCODE.renderer = (function(){
// ***** PRIVATE ******
var canvas;
var ctx;
var zoomFactor= 2.8, zoomFactorDelta = 0.4;
var gridSizeX=200,gridSizeY=200,gridStep=10;
var ctxHeight, ctxWidth;
var prevX=0, prevY=0;
// var colorGrid="#bbbbbb", colorLine="#000000";
var sliderHor, sliderVer;
var layerNumStore, progressStore={from: 0, to: -1};
var lastX, lastY;
var dragStart,dragged;
var scaleFactor = 1.1;
var model;
var initialized=false;
var renderOptions = {
showMoves: true,
showRetracts: true,
colorGrid: "#bbbbbb",
extrusionWidth: 1,
// colorLine: ["#000000", "#aabb88", "#ffe7a0", "#6e7700", "#331a00", "#44ba97", "#08262f", "#db0e00", "#ff9977"],
colorLine: ["#000000", "#45c7ba", "#a9533a", "#ff44cc", "#dd1177", "#eeee22", "#ffbb55", "#ff5511", "#777788"],
colorMove: "#00ff00",
colorRetract: "#ff0000",
colorRestart: "#0000ff",
sizeRetractSpot: 2,
modelCenter: {x: 0, y: 0},
moveModel: true,
differentiateColors: true,
showNextLayer: false
};
var offsetModelX=0, offsetModelY=0;
var speeds = [];
var speedsByLayer = {};
var reRender = function(){
var p1 = ctx.transformedPoint(0,0);
var p2 = ctx.transformedPoint(canvas.width,canvas.height);
ctx.clearRect(p1.x,p1.y,p2.x-p1.x,p2.y-p1.y);
drawGrid();
if(renderOptions['showNextLayer'] && layerNumStore < model.length - 1) {
drawLayer(layerNumStore+1, 0, GCODE.renderer.getLayerNumSegments(layerNumStore+1), true);
}
drawLayer(layerNumStore, progressStore.from, progressStore.to);
};
function trackTransforms(ctx){
var svg = document.createElementNS("http://www.w3.org/2000/svg",'svg');
var xform = svg.createSVGMatrix();
ctx.getTransform = function(){ return xform; };
var savedTransforms = [];
var save = ctx.save;
ctx.save = function(){
savedTransforms.push(xform.translate(0,0));
return save.call(ctx);
};
var restore = ctx.restore;
ctx.restore = function(){
xform = savedTransforms.pop();
return restore.call(ctx);
};
var scale = ctx.scale;
ctx.scale = function(sx,sy){
xform = xform.scaleNonUniform(sx,sy);
return scale.call(ctx,sx,sy);
};
var rotate = ctx.rotate;
ctx.rotate = function(radians){
xform = xform.rotate(radians*180/Math.PI);
return rotate.call(ctx,radians);
};
var translate = ctx.translate;
ctx.translate = function(dx,dy){
xform = xform.translate(dx,dy);
return translate.call(ctx,dx,dy);
};
var transform = ctx.transform;
ctx.transform = function(a,b,c,d,e,f){
var m2 = svg.createSVGMatrix();
m2.a=a; m2.b=b; m2.c=c; m2.d=d; m2.e=e; m2.f=f;
xform = xform.multiply(m2);
return transform.call(ctx,a,b,c,d,e,f);
};
var setTransform = ctx.setTransform;
ctx.setTransform = function(a,b,c,d,e,f){
xform.a = a;
xform.b = b;
xform.c = c;
xform.d = d;
xform.e = e;
xform.f = f;
return setTransform.call(ctx,a,b,c,d,e,f);
};
var pt = svg.createSVGPoint();
ctx.transformedPoint = function(x,y){
pt.x=x; pt.y=y;
return pt.matrixTransform(xform.inverse());
}
}
var startCanvas = function() {
canvas = document.getElementById('canvas');
// Проверяем понимает ли браузер canvas
if (!canvas.getContext) {
throw "exception";
}
ctx = canvas.getContext('2d'); // Получаем 2D контекст
ctxHeight = canvas.height;
ctxWidth = canvas.width;
lastX = ctxWidth/2;
lastY = ctxHeight/2;
ctx.lineWidth = 2;
ctx.lineCap = 'round';
trackTransforms(ctx);
canvas.addEventListener('mousedown',function(evt){
document.body.style.mozUserSelect = document.body.style.webkitUserSelect = document.body.style.userSelect = 'none';
lastX = evt.offsetX || (evt.pageX - canvas.offsetLeft);
lastY = evt.offsetY || (evt.pageY - canvas.offsetTop);
dragStart = ctx.transformedPoint(lastX,lastY);
dragged = false;
},false);
canvas.addEventListener('mousemove',function(evt){
lastX = evt.offsetX || (evt.pageX - canvas.offsetLeft);
lastY = evt.offsetY || (evt.pageY - canvas.offsetTop);
dragged = true;
if (dragStart){
var pt = ctx.transformedPoint(lastX,lastY);
ctx.translate(pt.x-dragStart.x,pt.y-dragStart.y);
reRender();
}
},false);
canvas.addEventListener('mouseup',function(evt){
dragStart = null;
if (!dragged) zoom(evt.shiftKey ? -1 : 1 );
},false);
var zoom = function(clicks){
var pt = ctx.transformedPoint(lastX,lastY);
ctx.translate(pt.x,pt.y);
var factor = Math.pow(scaleFactor,clicks);
ctx.scale(factor,factor);
ctx.translate(-pt.x,-pt.y);
reRender();
};
var handleScroll = function(evt){
var delta;
if(evt.detail<0 || evt.wheelDelta>0)delta=zoomFactorDelta;
else delta=-1*zoomFactorDelta;
if (delta) zoom(delta);
return evt.preventDefault() && false;
};
canvas.addEventListener('DOMMouseScroll',handleScroll,false);
canvas.addEventListener('mousewheel',handleScroll,false);
};
var drawGrid = function() {
var i;
ctx.strokeStyle = renderOptions["colorGrid"];
ctx.lineWidth = 1;
var offsetX=0, offsetY=0;
if(renderOptions["moveModel"]){
offsetX = offsetModelX;
offsetY = offsetModelY;
}
ctx.beginPath();
for(i=0;i<=gridSizeX;i+=gridStep){
ctx.moveTo(i*zoomFactor-offsetX, 0-offsetY);
ctx.lineTo(i*zoomFactor-offsetX, -gridSizeY*zoomFactor-offsetY);
}
ctx.stroke();
ctx.beginPath();
for(i=0;i<=gridSizeY;i+=gridStep){
ctx.moveTo(0-offsetX, -i*zoomFactor-offsetY);
ctx.lineTo(gridSizeX*zoomFactor-offsetX, -i*zoomFactor-offsetY);
}
ctx.stroke();
};
var drawLayer = function(layerNum, fromProgress, toProgress, isNextLayer){
var i, speedIndex= 0, prevZ = 0;
isNextLayer = typeof isNextLayer !== 'undefined' ? isNextLayer : false;
if(!isNextLayer){
layerNumStore=layerNum;
progressStore = {from: fromProgress, to: toProgress};
}
if(!model||!model[layerNum])return;
var cmds = model[layerNum];
var x, y;
// if(toProgress === -1){
// toProgress=cmds.length;
// }
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'){
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;
}
}
prevZ = GCODE.renderer.getZ(layerNum);
// ctx.strokeStyle = renderOptions["colorLine"];
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'){
prevX = cmds[i].prevX*zoomFactor;
prevY = -cmds[i].prevY*zoomFactor;
}
// console.log(cmds[i]);
if(typeof(cmds[i].x)==='undefined'||isNaN(cmds[i].x))x=prevX/zoomFactor;
else x = cmds[i].x;
if(typeof(cmds[i].y) === 'undefined'||isNaN(cmds[i].y))y=prevY/zoomFactor;
else y = -cmds[i].y;
if(renderOptions["differentiateColors"]&&!renderOptions['showNextLayer']){
// if(speedsByLayer['extrude'][prevZ]){
speedIndex = speeds['extrude'].indexOf(cmds[i].speed);
// speedIndex = GCODE.ui.ArrayIndexOf(speedsByLayer['extrude'][prevZ], function(obj) {return obj.speed === cmds[i].speed;});
// } else {
// speedIndex = -1;
// }
if(speedIndex === -1){
speedIndex = 0;
}else if(speedIndex > renderOptions["colorLine"].length -1){
speedIndex = speedIndex % (renderOptions["colorLine"].length-1);
// console.log("Too much colors");
}
}else if(renderOptions['showNextLayer']&&isNextLayer){
speedIndex=3;
}else{
speedIndex=0;
}
if(!cmds[i].extrude&&!cmds[i].noMove){
// ctx.stroke();
if(cmds[i].retract == -1){
if(renderOptions["showRetracts"]){
ctx.strokeStyle = renderOptions["colorRetract"];
ctx.fillStyle = renderOptions["colorRetract"];
ctx.beginPath();
ctx.arc(prevX, prevY, renderOptions["sizeRetractSpot"], 0, Math.PI*2, true);
ctx.stroke();
ctx.fill();
}
}
if(renderOptions["showMoves"]){
ctx.strokeStyle = renderOptions["colorMove"];
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(x*zoomFactor,y*zoomFactor);
ctx.stroke();
}
// ctx.strokeStyle = renderOptions["colorLine"][0];
// ctx.beginPath();
// console.log("moveto: "+cmds[i].x+":"+cmds[i].y)
// ctx.moveTo(cmds[i].x*zoomFactor,cmds[i].y*zoomFactor);
}
else if(cmds[i].extrude){
if(cmds[i].retract==0){
ctx.strokeStyle = renderOptions["colorLine"][speedIndex];
ctx.lineWidth = renderOptions['extrusionWidth'];
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(x*zoomFactor,y*zoomFactor);
ctx.stroke();
}else {
if(renderOptions["showRetracts"]){
// ctx.stroke();
ctx.strokeStyle = renderOptions["colorRestart"];
ctx.fillStyle = renderOptions["colorRestart"];
ctx.beginPath();
ctx.arc(prevX, prevY, renderOptions["sizeRetractSpot"], 0, Math.PI*2, true);
ctx.stroke();
ctx.fill();
// ctx.strokeStyle = renderOptions["colorLine"][0];
// ctx.beginPath();
}
}
}
prevX = x*zoomFactor;
prevY = y*zoomFactor;
}
ctx.stroke();
};
// ***** PUBLIC *******
return {
init: function(){
startCanvas();
initialized = true;
ctx.translate(10,gridSizeY*zoomFactor+20);
},
setOption: function(options){
for(var opt in options){
if(options.hasOwnProperty(opt))renderOptions[opt] = options[opt];
};
if(initialized)reRender();
},
getOptions: function(){
return renderOptions;
},
debugGetModel: function(){
return model;
},
render: function(layerNum, fromProgress, toProgress){
if(!initialized)this.init();
if(!model){
drawGrid();
}else{
if(layerNum < model.length){
var p1 = ctx.transformedPoint(0,0);
var p2 = ctx.transformedPoint(canvas.width,canvas.height);
ctx.clearRect(p1.x,p1.y,p2.x-p1.x,p2.y-p1.y);
drawGrid();
// ctx.globalAlpha = 0.5;
if(renderOptions['showNextLayer'] && layerNum < model.length - 1) {
drawLayer(layerNum+1, 0, this.getLayerNumSegments(layerNum+1), true);
}
drawLayer(layerNum, fromProgress, toProgress);
}else{
console.log("Got request to render non-existent layer!!");
}
}
},
getModelNumLayers: function(){
return model?model.length:1;
},
getLayerNumSegments: function(layer){
if(model){
return model[layer]?model[layer].length:1;
}else{
return 1;
}
},
doRender: function(mdl, layerNum){
var mdlInfo;
model = mdl;
prevX=0;
prevY=0;
if(!initialized)this.init();
mdlInfo = GCODE.gCodeReader.getModelInfo();
speeds = mdlInfo.speeds;
speedsByLayer = mdlInfo.speedsByLayer;
// console.log(speeds);
// console.log(mdlInfo.min.x + ' ' + mdlInfo.modelSize.x);
offsetModelX = (gridSizeX/2-(mdlInfo.min.x+mdlInfo.modelSize.x/2))*zoomFactor;
offsetModelY = (mdlInfo.min.y+mdlInfo.modelSize.y/2)*zoomFactor-gridSizeY/2*zoomFactor;
if(ctx)ctx.translate(offsetModelX, offsetModelY);
this.render(layerNum, 0, model[layerNum].length);
},
getZ: function(layerNum){
if(!model&&!model[layerNum]){
return '-1';
}
var cmds = model[layerNum];
for(var i=0;i<cmds.length;i++){
if(cmds[i].prevZ!==undefined)return cmds[i].prevZ;
}
return '-1';
}
}
}());