{"id":1720,"date":"2017-03-09T23:44:24","date_gmt":"2017-03-10T04:44:24","guid":{"rendered":"https:\/\/rpchurchill.com\/wordpress\/?p=1720"},"modified":"2017-09-15T07:33:04","modified_gmt":"2017-09-15T12:33:04","slug":"a-simple-discrete-event-simulation-part-88","status":"publish","type":"post","link":"https:\/\/rpchurchill.com\/wordpress\/posts\/2017\/03\/09\/a-simple-discrete-event-simulation-part-88\/","title":{"rendered":"A Simple Discrete-Event Simulation: Part 88"},"content":{"rendered":"<p><iframe loading=\"lazy\" width=\"426px\" height=\"1015px\" src=\"https:\/\/www.rpchurchill.com\/demo\/des\/discrete-event-sim_20170309.html\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n<p><a href=\"https:\/\/www.rpchurchill.com\/demo\/des\/discrete-event-sim_20170309.html\">Direct link<\/a> for mobile devices.<\/p>\n<p>Today I updated the code to define DisplayGroups for all non-Path components, which means the internal data for each component will become visible when the relevant component is clicked.  Moreover, each visible DisplayGroup object can be dragged to a new location.  These operations work for both mouse and touch operations.  Try it out and see what you think.<\/p>\n<p>I&#8217;m still highly annoyed that the intersection test doesn&#8217;t always generate the proper result, which causes the pointer not to be displayed, but the effect is otherwise pretty smooth.<\/p>\n<p>I cleaned up some code that caused the frame colors for the DisplayGroups to not be consistent (mostly by commenting out some old stuff) and shortened up the definition function for DisplayGroups to use default colors that can be customized separately.<\/p>\n<p>Here&#8217;s the code for handling events, made consistent for mouse and touch ops.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    var selectedDataGroup;\r\n    \r\n    function selectDisplayGroup(x, y) {\r\n      var i = 0;\r\n      if (setOfComponents.length > 0) {\r\n        var done = false;\r\n        while (!done) {\r\n          if ((typeof setOfComponents[i].dataGroup !== \"undefined\") &&\r\n              (setOfComponents[i].dataGroup.isVisible()) &&\r\n              (setOfComponents[i].dataGroup.isClickInside(x, y))) {\r\n            done = true;\r\n          } else {\r\n            i++;\r\n          }\r\n          if (i >= setOfComponents.length) {\r\n            done = true;\r\n          }\r\n        }\r\n        if (i < setOfComponents.length) {\r\n          return i;\r\n        } else {\r\n          return -1;\r\n        }\r\n      } else {\r\n        return -1;\r\n      }\r\n    }\r\n    \r\n    function selectComponent(x, y) {\r\n      var i = 0;\r\n      if (setOfComponents.length > 0) {\r\n        var done = false;\r\n        while (!done) {\r\n          if ((typeof setOfComponents[i].graphic !== \"undefined\") &&\r\n              (setOfComponents[i].graphic.isClickInside(x, y))) {\r\n            done = true;\r\n          } else {\r\n            i++;\r\n          }\r\n          if (i >= setOfComponents.length) {\r\n            done = true;\r\n          }\r\n        }\r\n        if (i < setOfComponents.length) {\r\n          if (!setOfComponents[i].graphic.highlighted) {\r\n            setOfComponents[i].graphic.highlight();\r\n            if (typeof setOfComponents[i].dataGroup !== \"undefined\") {\r\n              setOfComponents[i].dataGroup.turnOn();\r\n            }\r\n            drawModel();\r\n          } else {\r\n            setOfComponents[i].graphic.unHighlight();\r\n            if (typeof setOfComponents[i].dataGroup !== \"undefined\") {\r\n              setOfComponents[i].dataGroup.turnOff();\r\n            }\r\n            drawModel();\r\n          }\r\n          return i;\r\n        } else {\r\n          return -1;\r\n        }\r\n      } else {\r\n        return -1;\r\n      }\r\n    }\r\n    \r\n    \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/mouse drag events\r\n    var componentSelectionIndex;\r\n    \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    var totalDragDistance;\r\n\r\n    var mouseTimeStamp = 0;\r\n\r\n    var displayGroupPointer = -1;\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      mouseTimeStamp = Date.now();\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      totalDragDistance = 0;\r\n      var temp = selectDisplayGroup(dragLastX+globalBaseX, dragLastY+globalBaseY);\r\n      if (temp > -1) {\r\n        displayGroupPointer = temp;\r\n      }\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      var endTime = Date.now();\r\n      if (((endTime - mouseTimeStamp) < 300) &#038;&#038; (totalDragDistance <= 3)) {  \/\/treat as a click event\r\n        componentSelectionIndex = selectComponent(dragLastX+globalBaseX, dragLastY+globalBaseY);\r\n      }\r\n      dragFlag = false;\r\n      displayGroupPointer = -1;\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        var incrementY = currentY - dragLastY;\r\n        totalDragDistance += Math.abs(incrementX) + Math.abs(incrementX);\r\n        if (totalDragDistance > 3) {\r\n          if (displayGroupPointer > -1) {\r\n            setOfComponents[displayGroupPointer].dataGroup.moveBy(incrementX, incrementY);\r\n            drawModel();\r\n          } else {\r\n            if (incrementX > 0) {\r\n              panLeft(incrementX)\r\n            } else if (incrementX < 0) {\r\n              panRight(-incrementX)\r\n            }\r\n            if (incrementY > 0) {\r\n              panUp(incrementY);\r\n            } else if (incrementY < 0) {\r\n              panDown(-incrementY);\r\n            }\r\n          }\r\n          dragLastX = currentX;\r\n          dragLastY = currentY;\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    \r\n    \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/touch drag events, for now only supports first touch point\r\n    var touchTimeStamp = 0;\r\n\r\n    \/\/start drag\r\n    canvas.addEventListener(\"touchstart\", function(event) {\r\n      startDragT(event);\r\n    });\r\n    \r\n    function startDragT(e) {\r\n      touchTimeStamp = Date.now();\r\n      startDragX = globalBaseX;\r\n      startDragY = globalBaseY;\r\n      e.preventDefault();\r\n      var touches = e.changedTouches;\r\n      dragBaseX = touches[0].pageX;  \/\/change to clientX\r\n      dragBaseY = touches[0].pageY;  \/\/change to clientY\r\n      dragLastX = dragBaseX;\r\n      dragLastY = dragBaseY;\r\n      dragFlag = true;\r\n      totalDragDistance = 0;\r\n      var temp = selectDisplayGroup(dragLastX+globalBaseX, dragLastY+globalBaseY);\r\n      if (temp > -1) {\r\n        displayGroupPointer = temp;\r\n      }\r\n    }\r\n\r\n    \/\/end drag\r\n    canvas.addEventListener(\"touchend\", function(event) {\r\n      endDragT(event);\r\n    });\r\n    \r\n    function endDragT(e) {\r\n      var endTime = Date.now();\r\n      if (((endTime - touchTimeStamp) < 300) &#038;&#038; (totalDragDistance <= 3)) {  \/\/treat as a click event\r\n        componentSelectionIndex = selectComponent(dragLastX+globalBaseX, dragLastY+globalBaseY);\r\n      }\r\n      dragFlag = false;\r\n      displayGroupPointer = -1;\r\n    }\r\n    \r\n    \/\/move while dragging\r\n    canvas.addEventListener(\"touchmove\", function(event) {\r\n      doDragT(event);\r\n    });\r\n    \r\n    function doDragT(e) {\r\n      if (dragFlag) {\r\n        \/\/e.preventDefault();\r\n        var touches = e.changedTouches;\r\n        var currentX = touches[0].pageX;\r\n        var currentY = touches[0].pageY;\r\n        var incrementX = currentX - dragLastX;\r\n        var incrementY = currentY - dragLastY;\r\n        \/\/accumulate drag distance\r\n        totalDragDistance += Math.abs(incrementX) + Math.abs(incrementX);\r\n        if (totalDragDistance > 3) {\r\n          if (displayGroupPointer > -1) {\r\n            setOfComponents[displayGroupPointer].dataGroup.moveBy(incrementX, incrementY);\r\n            drawModel();\r\n          } else {\r\n            if (incrementX > 0) {\r\n              panLeft(incrementX)\r\n            } else if (incrementX < 0) {\r\n              panRight(-incrementX)\r\n            }\r\n            if (incrementY > 0) {\r\n              panUp(incrementY);\r\n            } else if (incrementY < 0) {\r\n              panDown(-incrementY);\r\n            }\r\n          }\r\n          dragLastX = currentX;\r\n          dragLastY = currentY;\r\n        }\r\n      }    \r\n    }\r\n    \r\n    \/\/touch leaves appropriate area (control works beyond canvas)\r\n    canvas.addEventListener(\"touchcancel\", function(event) {\r\n      leaveDragT(event);\r\n    });\r\n    \r\n    function leaveDragT(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","protected":false},"excerpt":{"rendered":"<p>Direct link for mobile devices. Today I updated the code to define DisplayGroups for all non-Path components, which means the internal data for each component will become visible when the relevant component is clicked. Moreover, each visible DisplayGroup object can &hellip; <a href=\"https:\/\/rpchurchill.com\/wordpress\/posts\/2017\/03\/09\/a-simple-discrete-event-simulation-part-88\/\">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,164,49,148,163,161],"_links":{"self":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1720"}],"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=1720"}],"version-history":[{"count":4,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1720\/revisions"}],"predecessor-version":[{"id":1724,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1720\/revisions\/1724"}],"wp:attachment":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1720"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1720"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1720"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}