{"id":1598,"date":"2017-02-07T23:37:59","date_gmt":"2017-02-08T04:37:59","guid":{"rendered":"https:\/\/rpchurchill.com\/?p=1598"},"modified":"2017-02-08T03:53:46","modified_gmt":"2017-02-08T08:53:46","slug":"a-simple-discrete-event-simulation-part-82-2","status":"publish","type":"post","link":"https:\/\/rpchurchill.com\/wordpress\/posts\/2017\/02\/07\/a-simple-discrete-event-simulation-part-82-2\/","title":{"rendered":"A Simple Discrete-Event Simulation: Part 82"},"content":{"rendered":"<p><iframe loading=\"lazy\" width=\"426px\" height=\"1015px\" src=\"https:\/\/www.rpchurchill.com\/demo\/des\/discrete-event-sim_20170207.html\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n<p>Today I added the ability to use the mouse to drag the 2D display around within its canvas.  I had to update the various pan functions to allow specification of an arbitrary distance increment, though there are a number of ways I could have done that.  I may change things up going forward.  In the meantime, the code sets up events for mousedown, mousemove, and mouseup events and, when the <code>dragFlag<\/code> is set to true, goes ahead and moves the 2D display by the requested increments in both the x and y directions.<\/p>\n<p>I was a little bit stunned when it worked perfectly the first time I tried it, and it also works smoothly when the simulation is running full blast.  Fun!<\/p>\n<p>Here&#8217;s the new code for handling the mouse events:<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/mouse drag events\r\n    var dragFlag = false;\r\n    var startDragX;\r\n    var startDragY;\r\n    var dragBaseX;\r\n    var dragBaseY;\r\n    var dragLastX;\r\n    var dragLastY;\r\n    \r\n    \/\/start drag\r\n    canvas.addEventListener(\"mousedown\", function(event) {\r\n      startDrag(event);\r\n    });\r\n    \r\n    function startDrag(e) {\r\n      startDragX = globalBaseX;\r\n      startDragY = globalBaseY;\r\n      dragBaseX = e.clientX;\r\n      dragBaseY = e.clientY;\r\n      dragLastX = dragBaseX;\r\n      dragLastY = dragBaseY;\r\n      dragFlag = true;\r\n    }\r\n\r\n    \/\/end drag\r\n    canvas.addEventListener(\"mouseup\", function(event) {\r\n      endDrag(event);\r\n    });\r\n    \r\n    function endDrag(e) {\r\n      dragFlag = false;\r\n    }\r\n    \r\n    \/\/move while dragging\r\n    canvas.addEventListener(\"mousemove\", function(event) {\r\n      doDrag(event);\r\n    });\r\n    \r\n    function doDrag(e) {\r\n      if (dragFlag) {\r\n        var currentX = e.clientX;\r\n        var currentY = e.clientY;\r\n        var incrementX = currentX - dragLastX;\r\n        if (incrementX > 0) {\r\n          panLeft(incrementX)\r\n        } else if (incrementX < 0) {\r\n          panRight(-incrementX)\r\n        }\r\n        var incrementY = currentY - dragLastY;\r\n        if (incrementY > 0) {\r\n          panUp(incrementY);\r\n        } else if (incrementY < 0) {\r\n          panDown(-incrementY);\r\n        }\r\n        dragLastX = currentX;\r\n        dragLastY = currentY;\r\n        \/\/eat the event\r\n        if (e.stopPropagation) {\r\n          e.stopPropagation()\r\n        } else {\r\n          e.cancelBubble = true;\r\n        }\r\n      }    \r\n    }\r\n    \r\n    \/\/mouse leaves canvas\r\n    canvas.addEventListener(\"mouseleave\", function(event) {\r\n      leaveDrag(event);\r\n    });\r\n    \r\n    function leaveDrag(e) {\r\n      if (dragFlag) {\r\n        globalBaseX = startDragX;\r\n        globalBaseY = startDragY;\r\n        if (!running) {\r\n          drawModel();\r\n        }\r\n        dragFlag = false;\r\n      }\r\n    }\r\n<\/pre>\n<p>And here's the updated code for the pan functions.  I left the limits unchanged for now.<\/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 panDown(increment) {\r\n      if (typeof increment === \"unknown\") {increment = yPanInc;}\r\n      globalBaseY += increment;\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 panUp(increment) {\r\n      if (typeof increment === \"unknown\") {increment = yPanInc;}\r\n      globalBaseY -= increment;\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(increment) {\r\n      if (typeof increment === \"unknown\") {increment = xPanInc;}\r\n      globalBaseX += increment;\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(increment) {\r\n      if (typeof increment === \"unknown\") {increment = xPanInc;}\r\n      globalBaseX -= increment;\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      \/\/eat the event\r\n      if (e.stopPropagation) {\r\n        e.stopPropagation()\r\n      } else {\r\n        e.cancelBubble = true;\r\n      }\r\n    }\r\n<\/pre>\n<p>I found it necessary to handle the mouseleave event because it was possible to mouse out of the canvas, release the mouse, return to the canvas, and still be dragging the 2D display.  In order to get out of that the user would have to do a new mousedown and mouseup to properly clear the state.  Perhaps it would be better to only snap back to the drag origin when the user releases the mouse when outside the canvas, when a drag sequence is in progress.<\/p>\n<p>The drag mouse event code to eat that event, or prevent it from bubbling up through further elements, does run when expected, but I can't be sure it's doing anything.  It runs when using the keystrokes as well, but the entire window will still scroll up or down when the 2D display reaches its upper or lower limits.  It's bad enough when running this page standalone but when its embedded on a WordPress page with ten other posts it wants to scroll the whole window down rather than ever scrolling the 2D display back up.  This means either that the event that scrolls the larger window up and down is being processed at a lower level that the canvas and is being allowed to bubble up to the canvas, or that it isn't actually being stopped from propagating.<\/p>\n<p>I need to get a much, much better feel for how the whole event mechanism works in JavaScript, HTML, and the DOM.  It's amazing how far you can get without going into depth in some really important areas.  The explanations I've read seem simple enough, and of course I've been using event-driven frameworks forever, but my work didn't usually require me to dig into the details.  I suspect this is going to keep me hopping.<\/p>\n<p>In the longer run there's a <em>lot<\/em> more to add here.  For example, the canvas should only allow the user to drag when the entire image is not in view, and even then perhaps in certain modes, which themselves must be controllable.  Anyway, one piece at a time, especially when I'm busy with other things.<\/p>\n<p>Last but not least I ended up learning a lot more about how WebGL runs -- or fails to run -- on different browsers at different times.  I'll relate that saga tomorrow.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today I added the ability to use the mouse to drag the 2D display around within its canvas. I had to update the various pan functions to allow specification of an arbitrary distance increment, though there are a number of &hellip; <a href=\"https:\/\/rpchurchill.com\/wordpress\/posts\/2017\/02\/07\/a-simple-discrete-event-simulation-part-82-2\/\">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":[5],"tags":[121,49,103,148,147],"_links":{"self":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1598"}],"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=1598"}],"version-history":[{"count":3,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1598\/revisions"}],"predecessor-version":[{"id":1600,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1598\/revisions\/1600"}],"wp:attachment":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1598"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1598"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1598"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}