diff --git a/octoprint_prettygcode/static/js/prettygcode.js b/octoprint_prettygcode/static/js/prettygcode.js index ed79b3e..993c0be 100644 --- a/octoprint_prettygcode/static/js/prettygcode.js +++ b/octoprint_prettygcode/static/js/prettygcode.js @@ -25,26 +25,44 @@ $(function () { var buffer=[]; var HeadState =function(){ this.position=new THREE.Vector3(0,0,0); - this.rate=50.0*60; - this.extrude=0; + this.rate=5.0*60; + this.extrude=false; + //this.lastExtrudedZ=0;//used to better calc layer number + this.layerLineNumber=0; this.clone=function(){ var newState=new HeadState(); newState.position.copy(this.position); newState.rate=this.rate; + newState.extrude=this.extrude; + //newState.lastExtrudedZ=this.lastExtrudedZ; + newState.layerLineNumber=this.layerLineNumber; return(newState); } }; var curState= new HeadState(); var curEnd= new HeadState(); - var parserCurState= new HeadState();; + var parserCurState= new HeadState(); + + var observedLayerCount=0; + var parserLayerLineNumber=0; + var parserLastExtrudedZ=0; + + var curLastExtrudedZ=0; + + parserCurState.extrude=true; + this.getCurPosition=function(){ - return(curState.position); + return({position:curState.position,layerZ:curLastExtrudedZ,lineNumber:curState.layerLineNumber}); } + // + //var currentFileOffset=0; + //add gcode command to the buffer this.addCommand= function(cmd) { + //currentFileOffset+=cmd.length; if(buffer.length>1000) { console.log("PrintHeadSimulator buffer overflow") @@ -68,16 +86,43 @@ $(function () { { parserCurState.rate=f; } + var e= parseFloat(cmd.split("E")[1]) + if(!Number.isNaN(e)) + { + parserCurState.extrude=true; + if( parserLastExtrudedZ!=parserCurState.position.z) + { + //new layer (probably) + //observedLayerCount++ + //console.log("New layer Z."+parserCurState.position.z+" File offset:"+currentFileOffset) + parserLayerLineNumber=0; + parserLastExtrudedZ=parserCurState.position.z; + } + else + parserLayerLineNumber++; + }else{ + parserCurState.extrude=false; + } + + parserCurState.layerLineNumber =parserLayerLineNumber; + buffer.push(parserCurState.clone()); + } } +//window.myMaxRate=120.0; +//window.fudge=7; + //Update the printhead position based on time elapsed. this.updatePosition=function(timeStep){ //Convert the gcode feed rate (in MM/per min?) to rate per second. var rate = curState.rate/60.0; + rate=rate/2;//todo. why still too fast? + //adapt rate to keep up with buffer. + //todo. Make dist based rather than just buffer size. if(buffer.length>10) { rate=rate*(buffer.length/5.0); @@ -88,7 +133,7 @@ $(function () { rate=rate*(1.0/(buffer.length*5.0)); //console.log(["Too fast ",rate,buffer.length]) } - +//rate=Math.min(rate,window.myMaxRate); //dist head needs to travel this frame var dist = rate*timeStep while(buffer.length>0 && dist >0)//while some place to go and some dist left. @@ -109,21 +154,34 @@ $(function () { //subract dist for next loop. dist=dist-distToEnd; + //draw segment + //todo. + + //update lastZ for display of layers. + if(curEnd.extrude && curEnd.position.z != curLastExtrudedZ ) + { + curLastExtrudedZ=curEnd.position.z; + } + //console.log([curState.position.z,curState.layerLineNumber]) + //start on next buffer command buffer.shift(); if(buffer.length>0) + { curEnd=buffer[0]; - //draw segment - //todo. + curState.layerLineNumber=curEnd.layerLineNumber; + } } } } } var printHeadSim=new PrintHeadSimulator(); + var curPrinterState=null; self.fromCurrentData= function (data) { //update current loaded model. updateJob(data.job); + curPrinterState=data.state; //parse logs position data for simulator if(data.logs.length){ @@ -132,7 +190,17 @@ $(function () { if(e.startsWith("Send:")) { //console.log(["GCmd:",e]); - printHeadSim.addCommand(e); + if(printHeadSim) + printHeadSim.addCommand(e); + + //Strip out the extra stuff in the terminal line. + //match second space to * character. I hate regexp. + if(terminalGcodeProxy){ + var reg=/(?<=\s\S*\s).[^*]*/g + var matches=e.match(reg); + if(matches.length>0) + terminalGcodeProxy.parse(matches[0]+'\n'); + } } }) } @@ -148,7 +216,8 @@ $(function () { //Scene globals var camera, cameraControls,cameraLight; var scene, renderer; - var gcodeProxy; + var gcodeProxy;//used to display loaded gcode. + var terminalGcodeProxy;//used to display gcode actualy sent to printer. var cubeCamera; var nozzleModel; var clock; @@ -159,6 +228,8 @@ $(function () { var gcodeHei = 580; var gui; + var forceNoSync=false;//used to override sync when user drags slider. Todo. Better way to handle this? + var currentLayerNumber=0; //settings that are saved between sessions @@ -311,6 +382,12 @@ $(function () { if(curJobName!="") gcodeProxy.loadGcode('/downloads/files/local/' + curJobName); + //terminal parser + terminalGcodeProxy = new GCodeParser(); + var terminalGcodeObject = terminalGcodeProxy.getObject(); + terminalGcodeObject.position.set(100, -0, 0); + scene.add(terminalGcodeObject); + //note this is an octoprint version of a bootstrap slider. not a jquery ui slider. $('.gwin').append($('
')); @@ -324,7 +401,13 @@ $(function () { value: 100, }).on("slide", function (event, ui) { currentLayerNumber = event.value; - });; + }).on("slideStart", function (event, ui) { + //console.log("slideStart"); + forceNoSync=true; + }).on("slideStop", function (event, ui) { + //console.log("slideStop"); + forceNoSync=false; + }); $("#myslider").attr("style", "height:90%;position:absolute;top:5%;right:20px") @@ -413,12 +496,12 @@ $(function () { var currentLayer = undefined; - var defaultColor = new THREE.Color('black'); + var defaultColor = new THREE.Color('white'); var curColor = defaultColor; //material for fatlines var curMaterial = new THREE.LineMaterial({ - linewidth: 6, // in pixels + linewidth: 3, // in pixels //color: new THREE.Color(curColorHex),// rainbow.getColor(layers.length % 64).getHex() vertexColors: THREE.VertexColors, }); @@ -454,6 +537,65 @@ $(function () { } } } + this.syncGcodeObjToLayer=function (layerNumber,lineNumber=Infinity) + { + //hack comp for mirror. + //todo. better handle of mirror object so this isnt needed. + if(pgSettings.showMirror && lineNumber!=Infinity) + lineNumber=lineNumber*2; + + gcodeGroup.traverse(function (child) { + if (child.name.startsWith("layer#")) { + if (child.userData.layerNumberchild.userData.numLines) +// alert("this shouldn't happen") + child.geometry.maxInstancedCount=child.userData.numLines; + }else if (child.userData.layerNumber==layerNumber) { + child.visible = true; +// if(child.geometry.maxInstancedCount>child.userData.numLines) +// alert("this shouldn't happen") + + child.geometry.maxInstancedCount=Math.min(lineNumber,child.userData.numLines); +// if(lineNumber>child.userData.numLines-50) +// console.log([layerNumber,lineNumber,child.userData.numLines]) + } + else { + child.visible = false; + } + } + }); + } + this.syncGcodeObjTo=function (layerZ,lineNumber=Infinity) + { + //hack comp for mirror. + //todo. better handle of mirror object so this isnt needed. + if(pgSettings.showMirror && lineNumber!=Infinity) + lineNumber=lineNumber*2; + + gcodeGroup.traverse(function (child) { + if (child.name.startsWith("layer#")) { + if (child.userData.layerZchild.userData.numLines) +// alert("this shouldn't happen") + child.geometry.maxInstancedCount=child.userData.numLines; + }else if (child.userData.layerZ==layerZ) { + child.visible = true; +// if(child.geometry.maxInstancedCount>child.userData.numLines) +// alert("this shouldn't happen") + child.geometry.maxInstancedCount=Math.min(lineNumber,child.userData.numLines); +// if(lineNumber>child.userData.numLines-20) + // console.log([layerZ,lineNumber,child.userData.numLines]) + } + else { + child.visible = false; + } + } + }); + } this.currentUrl=""; this.loadGcode=function(url) { @@ -498,6 +640,27 @@ $(function () { if (currentLayer !== undefined) { addObject(currentLayer, true); } + + //update scene bounds. + var bsize=new THREE.Vector3(); + sceneBounds.getSize(bsize); + + //update ui slider + if ($("#myslider-vertical").length) { + $("#myslider-vertical").slider("setMax", layers.length) + $("#myslider-vertical").slider("setValue", layers.length) + currentLayerNumber = layers.length; + } + + //gcodeProxy.syncGcodeObjTo(Infinity); + + //updateDimensions(bsize); + + //Move zoom camera to new bounds. + var dist = Math.max(Math.abs(bsize.x), Math.abs(bsize.y)) / 2; + dist=Math.max(20,dist);//min distance to model. + //console.log(dist) + cameraControls.dollyTo(dist * 2.0 ,true); } function addObject(layer, extruding) { @@ -509,7 +672,7 @@ $(function () { geo.setColors(layer.colors) var line = new THREE.Line2(geo, curMaterial); line.name = 'layer#' + layers.length; - line.userData={layerZ:layer.z,layerNumber:layers.length+1}; + line.userData={layerZ:layer.z,layerNumber:layers.length+1,numLines:layer.vertex.length/6};// 6 because 2 x triplets gcodeGroup.add(line); //line.renderOrder = 2; }else{//plain lines @@ -518,7 +681,7 @@ $(function () { geo.addAttribute( 'color', new THREE.BufferAttribute( new Float32Array(layer.colors), 3 ) ); var line = new THREE.LineSegments( geo, curLineBasicMaterial ); line.name = 'layer#' + layers.length; - line.userData={layerZ:layer.z,layerNumber:layers.length+1}; + line.userData={layerZ:layer.z,layerNumber:layers.length+1,numLines:layer.vertex.length/6}; gcodeGroup.add(line); } @@ -534,11 +697,6 @@ $(function () { layers.push(currentLayer); //console.log("layer #" + layers.length + " z:" + line.z); - if ($("#myslider-vertical").length) { - $("#myslider-vertical").slider("setMax", layers.length) - $("#myslider-vertical").slider("setValue", layers.length) - currentLayerNumber = layers.length; - } } function addSegment(p1, p2) { @@ -561,23 +719,37 @@ $(function () { if (true)//faux shading. Darken line color based on angle { - var deltaX = p2.x - p1.x; - var deltaY = p2.y - p1.y; - var rad = Math.atan2(deltaY, deltaX); + //var p1=new THREE.Vector3(10,10,0); + //var p2=new THREE.Vector3(15,15,0); - rad = Math.abs(rad) - var per = (rad) / (2.0 * 3.1415); - //console.log(rad + " " + per); + var per=1.0;//bright + if(true) + { + //var np2=new THREE.Vector3(p2.x,p2.y,p2.z); + var vec = new THREE.Vector3(p2.x-p1.x,p2.y-p1.y,p2.z-p1.z); + vec.normalize(); +// per= Math.max(vec.dot(new THREE.Vector3(1,0,0)),0.0) +// per= Math.abs(vec.dot(new THREE.Vector3(1,0,0)),0.0) + per = (vec.dot(new THREE.Vector3(1,0,0))/2)+0.5; + per=(per/5.0); + }else{ + var deltaX = p2.x - p1.x; + var deltaY = p2.y - p1.y; + var rad = Math.atan2(deltaY, deltaX); + rad = Math.abs(rad) + per = (rad) / (2.0 * 3.1415); + //console.log(rad + " " + per); + } var drawColor = new THREE.Color(curColor) var hsl = {} drawColor.getHSL(hsl); //darken every other line to make the layers easier to see. if((layers.length%2)==0) - hsl.l = per+0.2; - else hsl.l = per+0.25; + else + hsl.l = per+0.30; drawColor.setHSL(hsl.h,hsl.s,hsl.l); //console.log(drawColor.r + " " + drawColor.g + " " + drawColor.b ) @@ -715,24 +887,11 @@ $(function () { //console.warn( 'THREE.GCodeLoader: Command not supported:' + cmd ); } } - - //update scene bounds. - var bsize=new THREE.Vector3(); - sceneBounds.getSize(bsize); - - //todo. move this - //updateDimensions(bsize); - - //Move zoom camera to new bounds. - var dist = Math.max(Math.abs(bsize.x), Math.abs(bsize.y)) / 2; - dist=Math.max(20,dist);//min distance to model. - //console.log(dist) - cameraControls.dollyTo(dist * 2.0 ,true); - } }; + //todo move to new file or remove. function updateDimensions(bsize) { if(dimensionsGroup===undefined) @@ -928,8 +1087,7 @@ $(function () { //plane.quaternion.setFromEuler(new THREE.Euler(- Math.PI / 2, 0, 0)); scene.add( plane ); - //todo. make bed sized. - + //make bed sized grid. var grid = new THREE.GridHelper(bedVolume.width, bedVolume.width/10, 0x000000, 0x888888); //todo handle other than lowerleft if(bedVolume.origin=="lowerleft") @@ -956,28 +1114,35 @@ $(function () { if(printHeadSim) { printHeadSim.updatePosition(delta); - if(pgSettings.syncToProgress) + if(curPrinterState && curPrinterState.flags.printing && + pgSettings.syncToProgress && (!forceNoSync)) { - var curPos=printHeadSim.getCurPosition(); + var curState=printHeadSim.getCurPosition(); if(nozzleModel) - nozzleModel.position.copy(curPos); + nozzleModel.position.copy(curState.position); - //todo. figure out another way of syncing z? - if(syncSavedZ!=curPos.z){ - syncSavedZ=curPos.z; - scene.traverse(function (child) { - if (child.name.startsWith("layer#")) { - if (child.userData.layerZ <=curPos.z) { - currentLayerNumber=child.userData.layerNumber; - } - } - }); - } + if(gcodeProxy) + gcodeProxy.syncGcodeObjTo(curState.layerZ,curState.lineNumber-1/*-window.fudge*/);//todo. figure out why *2 is needed. + + + // //todo. figure out another way of syncing z? + // if(syncSavedZ!=curPos.z){ + // syncSavedZ=curPos.z; + // scene.traverse(function (child) { + // if (child.name.startsWith("layer#")) { + // if (child.userData.layerZ <=curPos.z) { + // currentLayerNumber=child.userData.layerNumber; + // } + // } + // }); + // } }else{ if(nozzleModel) nozzleModel.position.set(0,0,0);//todo. hide instead/also? + if(gcodeProxy) + gcodeProxy.syncGcodeObjToLayer(currentLayerNumber);//todo. figure out why *2 is needed. } } @@ -991,18 +1156,18 @@ $(function () { nozzleModel.visible=true; } - //set visible layers - scene.traverse(function (child) { - if (child.name.startsWith("layer#")) { - var num = child.name.split("#")[1] - if (num < currentLayerNumber) { - child.visible = true; - } - else { - child.visible = false; - } - } - }); + // //set visible layers + // scene.traverse(function (child) { + // if (child.name.startsWith("layer#")) { + // var num = child.name.split("#")[1] + // if (num < currentLayerNumber) { + // child.visible = true; + // } + // else { + // child.visible = false; + // } + // } + // }); const updated = cameraControls.update(delta);