{"id":1587,"date":"2017-02-06T23:31:36","date_gmt":"2017-02-07T04:31:36","guid":{"rendered":"https:\/\/rpchurchill.com\/?p=1587"},"modified":"2017-02-07T03:09:58","modified_gmt":"2017-02-07T08:09:58","slug":"a-simple-discrete-event-simulation-part-81","status":"publish","type":"post","link":"https:\/\/rpchurchill.com\/wordpress\/posts\/2017\/02\/06\/a-simple-discrete-event-simulation-part-81\/","title":{"rendered":"A Simple Discrete-Event Simulation: Part 81"},"content":{"rendered":"<p><iframe loading=\"lazy\" width=\"426px\" height=\"1015px\" src=\"https:\/\/www.rpchurchill.com\/demo\/des\/discrete-event-sim_20170206.html\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n<p>This week and possibly much of next I&#8217;m concentrating on updating the fixed content of my website, which means I&#8217;m only going to be doing small increments on this project.  I&#8217;ve decided that the next item to work on is panning a zooming because of something I want to demonstrate in the fixed content.  To that end I installed the simplest possible commands to pan the 2D display around within certain limits.  Mostly I wanted to verify that I&#8217;d added the base x- and y-origins for the viewable area of the screen were added to all of the drawing commands properly.<\/p>\n<p>Here&#8217;s the relevant code from the <code>DisplayElement<\/code> object.  It shows how the drawing locations now include the offsets I&#8217;ve defined (<code>globalBaseX<\/code> and <code>globalBaseY<\/code>):<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n      this.drawBasic = function() {\r\n        var borderColor;\r\n        if (this.parent.getExclusive()) {\r\n          if (this.parent.getOpenStatus()) {\r\n            borderColor = this.readyColor;\r\n          } else {\r\n            borderColor = this.waitingColor;\r\n          }\r\n        } else {\r\n          borderColor = this.neutralColor;\r\n        }\r\n        \/\/if (this.parent.getComponentType() != \"Path\") {\r\n        if (this.isJourney) {\r\n          globalCTX.strokeStyle = borderColor;\r\n          globalCTX.beginPath();\r\n          if (this.innode) {\r\n            globalCTX.moveTo(this.x1-5-globalBaseX+0.5,this.y1-5-globalBaseY+0.5);\r\n            globalCTX.lineTo(this.x1+4-globalBaseX+0.5,this.y1-5-globalBaseY+0.5);\r\n            globalCTX.lineTo(this.x1+4-globalBaseX+0.5,this.y1+4-globalBaseY+0.5);\r\n            globalCTX.lineTo(this.x1-5-globalBaseX+0.5,this.y1+4-globalBaseY+0.5);\r\n            globalCTX.lineTo(this.x1-5-globalBaseX+0.5,this.y1-5-globalBaseY+0.5);\r\n          } else { \/\/outnode\r\n            globalCTX.moveTo(this.x2-5-globalBaseX+0.5,this.y2-5-globalBaseY+0.5);\r\n            globalCTX.lineTo(this.x2+4-globalBaseX+0.5,this.y2-5-globalBaseY+0.5);\r\n            globalCTX.lineTo(this.x2+4-globalBaseX+0.5,this.y2+4-globalBaseY+0.5);\r\n            globalCTX.lineTo(this.x2-5-globalBaseX+0.5,this.y2+4-globalBaseY+0.5);\r\n            globalCTX.lineTo(this.x2-5-globalBaseX+0.5,this.y2-5-globalBaseY+0.5);\r\n          }\r\n          globalCTX.stroke();\r\n          \/\/do nothing\r\n        } else if (!this.isPath) {\r\n          var runningHeight = 15;\r\n          globalCTX.strokeStyle = borderColor;\r\n          globalCTX.beginPath();\r\n          globalCTX.moveTo(this.x1-globalBaseX+0.5,this.y1-globalBaseY+0.5);\r\n          globalCTX.lineTo(this.x2-globalBaseX+0.5,this.y1-globalBaseY+0.5);\r\n          globalCTX.lineTo(this.x2-globalBaseX+0.5,this.y2-globalBaseY+0.5);\r\n          globalCTX.lineTo(this.x1-globalBaseX+0.5,this.y2-globalBaseY+0.5);\r\n          globalCTX.lineTo(this.x1-globalBaseX+0.5,this.y1-globalBaseY+0.5);\r\n          globalCTX.stroke();\r\n          globalCTX.font = \"12px Arial\";\r\n          globalCTX.fillStyle = this.labelColor;\r\n          globalCTX.textAlign = \"center\";\r\n          globalCTX.fillText(this.label,this.x1+this.nodexi-globalBaseX,this.y1+runningHeight-globalBaseY);\r\n          runningHeight += 12;\r\n          if (this.traverse) {\r\n            globalCTX.textAlign = \"right\";\r\n            var waitChar = \"P:\";\r\n            if (this.parent.getComponentType() == \"Queue\") {\r\n              waitChar = \"T:\";\r\n            } else if (this.parent.getComponentType() == \"Process\") {\r\n              waitChar = \"P:\";\r\n            } \r\n            globalCTX.fillText(waitChar,x1-globalBaseX+15,this.y1-globalBaseY+runningHeight);\r\n            globalCTX.fillStyle = this.valueColor;\r\n            globalCTX.textAlign = \"left\";\r\n            globalCTX.fillText(this.traverseValue,x1-globalBaseX+17,this.y1-globalBaseY+runningHeight);\r\n            runningHeight += 12;\r\n          }\r\n          if (this.count) {\r\n            globalCTX.fillStyle = this.labelColor;\r\n            globalCTX.textAlign = \"right\";\r\n            globalCTX.fillText(\"Q:\",x1-globalBaseX+15,this.y1-globalBaseY+runningHeight);\r\n            globalCTX.fillStyle = this.valueColor;\r\n            globalCTX.textAlign = \"left\";\r\n            globalCTX.fillText(this.countValue,x1-globalBaseX+17,this.y1-globalBaseY+runningHeight);\r\n            runningHeight += 12;\r\n          }\r\n        } else {\r\n          globalCTX.strokeStyle = borderColor;\r\n          globalCTX.beginPath();\r\n          globalCTX.moveTo(this.x1-globalBaseX+0.5,this.y1-globalBaseY+0.5);\r\n          globalCTX.lineTo(this.x2-globalBaseX+0.5,this.y2-globalBaseY+0.5);\r\n          globalCTX.stroke();        \r\n        }\r\n      };\r\n      this.drawNodes = function() {\r\n        \/\/if (this.parent.getComponentType() != \"Path\") {\r\n        if (this.isJourney) {\r\n          if (this.innode) {\r\n            drawNode(this.x1-globalBaseX,this.y1-globalBaseY,3,this.nodeColor);\r\n          }\r\n          if (this.outnode) {\r\n            drawNode(this.x2-globalBaseX,this.y2-globalBaseY,3,this.nodeColor);\r\n          }        \r\n        } else if (!this.isPath) {\r\n          if (this.innode) {\r\n            drawNode(this.x1+this.nodexi-globalBaseX,this.y1+this.nodeyi-globalBaseY,3,this.nodeColor);\r\n          }\r\n          if (this.outnode) {\r\n            drawNode(this.x1+this.nodexo-globalBaseX,this.y1+this.nodeyo-globalBaseY,3,this.nodeColor);\r\n          }\r\n        } else {\r\n          if (this.parent.previousComponent.getComponentType() == \"Path\") {\r\n            drawNode(this.x1-globalBaseX,this.y1-globalBaseY,3,this.nodeColor);\r\n          }\r\n          if (this.parent.nextComponent.getComponentType() == \"Path\") {\r\n            drawNode(this.x2-globalBaseX,this.y2-globalBaseY,3,this.nodeColor);\r\n          }\r\n        }\r\n      };\r\n      this.drawEntities = function() {\r\n        var i;\r\n        var drawCount;\r\n        var drawColor;\r\n        var xx;\r\n        var yy;\r\n        var qCount;\r\n        \/\/if (this.parent.getComponentType() == \"Path\") {\r\n        if (this.isPath) {\r\n          drawCount = this.parent.entityQueue.length;\r\n          for (i=0; i<drawCount; i++) {\r\n            var location = this.parent.entityQueue[i].getLocation();\r\n            drawNode(location.x-globalBaseX,location.y-globalBaseY,5,this.readyColor);\r\n            drawNode(location.x-globalBaseX,location.y-globalBaseY,3,this.parent.entityQueue[i].getEntityColor())\r\n          }\r\n        } else if (this.parent.getComponentType() == \"Bag\") {\r\n          drawCount = this.parent.maxCapacity;  \/\/should be < this.entityLocs.length\r\n          drawColor = this.waitingColor;\r\n          for (i=0; i<drawCount; i++) {\r\n            if (this.parent.entityQueue[i] != null) {\r\n              xx = this.entityLocs[0][i].x-globalBaseX;\r\n              yy = this.entityLocs[0][i].y-globalBaseY;\r\n              drawNode(this.x1+xx,this.y1+yy,5,drawColor);\r\n              drawNode(this.x1+xx,this.y1+yy,3,this.parent.entityQueue[i].getEntityColor());\r\n              if (this.parent.subEntityQueue[i] != null) {\r\n                drawNode(this.x1+xx+3,this.y1+yy-3,5,drawColor);\r\n                drawNode(this.x1+xx+3,this.y1+yy-3,3,this.parent.subEntityQueue[i].getEntityColor());\r\n              }\r\n            }\r\n          }\r\n          drawCount = this.entityLocs[0].length - this.parent.maxCapacity;\r\n          if (this.parent.exitQueue.length < drawCount) {\r\n            drawCount = this.parent.exitQueue.length;\r\n          }\r\n          drawColor = this.readyColor;\r\n          for (i=0; i<drawCount; i++) {\r\n            \/\/locs for exit queue in order from maxCapacity\r\n            xx = this.entityLocs[0][this.parent.maxCapacity + i].x-globalBaseX;\r\n            yy = this.entityLocs[0][this.parent.maxCapacity + i].y-globalBaseY;\r\n            drawNode(this.x1+xx,this.y1+yy,5,drawColor);\r\n            \/\/exit queue items in reverse order\r\n            drawNode(this.x1+xx,this.y1+yy,3,this.parent.exitQueue[this.parent.exitQueue.length-1-i].getEntityColor());            \r\n          }\r\n        } else if (this.parent.getComponentType()== \"Combined\") {\r\n          \/\/var locs = this.entityLocs.length;\r\n          qCount = this.countValue - this.traverseValue;\r\n          \/\/drawCount = locs - this.parent.processCapacity;\r\n          drawCount = this.locsCount[0] - this.parent.processCapacity;\r\n          if (this.parent.entityQueue.length < drawCount) {\r\n            drawCount = this.parent.entityQueue.length;\r\n          }\r\n          for (i=0; i<drawCount; i++) {\r\n            if (i < qCount) {\r\n              drawColor = this.readyColor;\r\n            } else {\r\n              drawColor = this.waitingColor;\r\n            }\r\n            xx = this.entityLocs[0][i].x-globalBaseX;\r\n            yy = this.entityLocs[0][i].y-globalBaseY;\r\n            drawNode(this.x1+xx,this.y1+yy,5,drawColor);\r\n            drawNode(this.x1+xx,this.y1+yy,3,this.parent.entityQueue[i].getEntityColor());\r\n          }\r\n          var pCount = this.parent.countInProcessQueue - this.parent.countInProcess;\r\n          drawCount = this.parent.countInProcessQueue;\r\n          \/\/start from end of entityLocs\r\n          for (i=0; i<drawCount; i++) {\r\n            if (i < pCount) {\r\n              drawColor = this.readyColor;\r\n            } else {\r\n              drawColor = this.waitingColor;\r\n            }\r\n            xx = this.entityLocs[0][this.locsCount[0]-i-1].x-globalBaseX;\r\n            yy = this.entityLocs[0][this.locsCount[0]-i-1].y-globalBaseY;\r\n            drawNode(this.x1+xx,this.y1+yy,5,drawColor);\r\n            drawNode(this.x1+xx,this.y1+yy,3,this.parent.processQueue[drawCount-i-1].getEntityColor());\r\n          }\r\n        } else if (this.parent.getComponentType() != \"Arrivals\") {\r\n          if (this.parent.componentName == \"Q0\") {\r\n            dummy1 = 6;\r\n          }\r\n          qCount = this.countValue - this.traverseValue;\r\n          if (this.countValue > this.locsCount[0]) {\r\n            drawCount = this.locsCount[0];\r\n          } else {\r\n            drawCount = this.countValue;\r\n          }\r\n          for (i=0; i<drawCount; i++) {\r\n            if (i < qCount) {\r\n              drawColor = this.readyColor;\r\n            } else {\r\n              drawColor = this.waitingColor;\r\n            }\r\n            xx = this.entityLocs[0][i].x-globalBaseX;\r\n            yy = this.entityLocs[0][i].y-globalBaseY;\r\n            drawNode(this.x1+xx,this.y1+yy,5,drawColor);\r\n            drawNode(this.x1+xx,this.y1+yy,3,this.parent.entityQueue[i].getEntityColor());\r\n          }\r\n        }\r\n      };\r\n<\/pre>\n<p>Here's the code that does the basic pan.  It picks up keystrokes and changes the base x- and y-axis panning origins within limits.  When the code is running in its own window there are times when higher priority events consume the messages for the up and down arrow keys, namely when the entire page wants to scroll.  When I embed this in an iframe on the website it doesn't seem to have this problem.  However, when it's in the iframe (like you see it here) you need to click on the canvas to give it the focus.  The arrow keys move the display and the home key restore the origin point to its original value.  There's no provision for panning on a mobile device, either, but at least I know the offsets are in correctly.<\/p>\n<p>You may also notice that these calls update the graphics when the simulation is not running, but don't need to do so when the simulation is running, though the panning action will be much more slow and choppy.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    var xPanMin = -200;  \/\/globalBaseX\r\n    var xPanMax = 200;\r\n    var xPanInc = 2;\r\n    var yPanMin = -200;  \/\/globalBaseY\r\n    var yPanMax = 200;\r\n    var yPanInc = 2;\r\n    \r\n    function panUp() {\r\n      globalBaseY += yPanInc;\r\n      if (globalBaseY > yPanMax) {\r\n        globalBaseY = yPanMax;\r\n      }\r\n      if (!running) {\r\n        drawModel();\r\n      } \r\n    }\r\n\r\n    function panDown() {\r\n      globalBaseY -= yPanInc;\r\n      if (globalBaseY < yPanMin) {\r\n        globalBaseY = yPanMin;\r\n      }\r\n      if (!running) {\r\n        drawModel();\r\n      } \r\n    }\r\n\r\n    function panRight() {\r\n      globalBaseX += xPanInc;\r\n      if (globalBaseX > xPanMax) {\r\n        globalBaseX = xPanMax;\r\n      }\r\n      if (!running) {\r\n        drawModel();\r\n      } \r\n    }\r\n\r\n    function panLeft() {\r\n      globalBaseX -= xPanInc;\r\n      if (globalBaseX < xPanMin) {\r\n        globalBaseX = xPanMin;\r\n      }\r\n      if (!running) {\r\n        drawModel();\r\n      } \r\n    }\r\n    \r\n    function resetPan() {\r\n      globalBaseX = 0;\r\n      globalBaseY = 0;\r\n      if (!running) {\r\n        drawModel();\r\n      }\r\n    }\r\n\r\n    document.onkeydown = checkKey;\r\n\r\n    function checkKey(e) {\r\n      e = e || window.event;  \/\/up 38 down 40\r\n      if (e.keyCode == '37') { \/\/left arrow\r\n        panLeft();\r\n      } else if (e.keyCode == '39') { \/\/right arrow\r\n        panRight();\r\n      } else if (e.keyCode == '38') { \/\/up arrow\r\n        panUp();\r\n      } else if (e.keyCode == '40') { \/\/down arrow\r\n        panDown();\r\n      } else if (e.keyCode == '66') { \/\/b key\r\n      } else if (e.keyCode == '70') { \/\/f key\r\n      } else if (e.keyCode == '36') { \/\/home key\r\n        resetPan();\r\n      }\r\n    }<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>This week and possibly much of next I&#8217;m concentrating on updating the fixed content of my website, which means I&#8217;m only going to be doing small increments on this project. I&#8217;ve decided that the next item to work on is &hellip; <a href=\"https:\/\/rpchurchill.com\/wordpress\/posts\/2017\/02\/06\/a-simple-discrete-event-simulation-part-81\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[60],"tags":[121,147],"_links":{"self":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1587"}],"collection":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/comments?post=1587"}],"version-history":[{"count":5,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1587\/revisions"}],"predecessor-version":[{"id":1592,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1587\/revisions\/1592"}],"wp:attachment":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1587"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1587"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1587"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}