{"id":1716,"date":"2017-03-08T23:15:07","date_gmt":"2017-03-09T04:15:07","guid":{"rendered":"https:\/\/rpchurchill.com\/wordpress\/?p=1716"},"modified":"2017-09-15T07:33:26","modified_gmt":"2017-09-15T12:33:26","slug":"a-simple-discrete-event-simulation-part-87","status":"publish","type":"post","link":"https:\/\/rpchurchill.com\/wordpress\/posts\/2017\/03\/08\/a-simple-discrete-event-simulation-part-87\/","title":{"rendered":"A Simple Discrete-Event Simulation: Part 87"},"content":{"rendered":"<p>Today I connected the display of the DisplayGroup objects to the component selection event.  That is, if you click on a component it brings up that component&#8217;s DisplayGroup object.  This process involves several steps.<\/p>\n<p>First, the location on the canvas has to be sensed, and that has been discussed previously.  However, I neglected to test the selection functionality when the scene had been dragged to a new location.  That was corrected by subtracting the factors <code>globalBaseX<\/code> and <code>globalBaseY<\/code> from the actual click location in the same manner that DisplayElement x- and y-locations are modified.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.rpchurchill.com\/images\/articles\/20170308_DisplayGroup.png\"><\/p>\n<p>Here&#8217;s the updated event handling code, which also shows the new way that mouse click events are handled per yesterday&#8217;s discussion.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/mouse click events\r\n    var componentSelectionIndex;\r\n    \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    var totalDragDistance;\r\n\r\n    var mouseTimeStamp = 0;\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    }\r\n\r\n    \/\/end drag\r\n    canvas.addEventListener(\"mouseup\", function(event) {\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      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        var incrementY = currentY - dragLastY;\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        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    \r\n    \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/touch drag events, for now only supports first touch point\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 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;\r\n      dragBaseY = touches[0].pageY;\r\n      dragLastX = dragBaseX;\r\n      dragLastY = dragBaseY;\r\n      dragFlag = true;\r\n      totalDragDistance = 0;\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      \/\/also process end drag event\r\n      e.preventDefault();\r\n      dragFlag = false;\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;  \/\/change to clientX\r\n        var currentY = touches[0].pageY;  \/\/change to clientY\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 (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          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      e.preventDefault();\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>We have to go back to defining the location and properties of each component's DisplayGroup object, as in this example, which is the only one so far defined.  It's the last line here:<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    var routingTable = [[1.0],[1.0],[1.0]];\r\n    var entry1 = new EntryComponent(routingTable);\r\n    entry1.setRoutingMethod(3);  \/\/1 single connection, 2 distribution logic, 3 model logic\r\n    entry1.setComponentName(\"Entry1\");\r\n    \r\n    tempGraphic = new DisplayElement(entry1, 210, 5, 68, 20);\r\n    entry1.defineDataGroup(2.0, 20, -100, 69, globalNeutralColor, globalValueColor, globalLabelColor);\r\n<\/pre>\n<p>Drawing the DisplayGroup object is now more complicated, since I've chosen to include a pointer from the DisplayGroup's frame to the edge of the DisplayElement's frame.  The pointer is drawn along the line between the centers of the two frames.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    function DisplayGroup1(parent) {\r\n\r\n      ...\r\n\r\n      this.drawBasic = function() {\r\n        this.height = (this.valueCount * 12) + this.labelHeight + 3;\r\n        this.width = this.maxLabelWidth + this.valueWidth + 8;\r\n        globalCTX.strokeStyle = this.borderColor;\r\n        globalCTX.beginPath();\r\n        var x1 = this.xLocation-globalBaseX+0.5;             \/\/boundaries of dataGroup object\r\n        var y1 = this.yLocation-globalBaseY+0.5;\r\n        var x2 = this.xLocation+this.width-globalBaseX+0.5;\r\n        var y2 = this.yLocation+this.height-globalBaseY+0.5;\r\n        var cx = (x1 + x2) * 0.5;                            \/\/center of dataGroup object\r\n        var cy = (y1 + y2) * 0.5;\r\n        var px1 = this.parent.graphic.x1-globalBaseX;        \/\/boundaries of displayElement object\r\n        var py1 = this.parent.graphic.y1-globalBaseY;\r\n        var px2 = this.parent.graphic.x2-globalBaseX;\r\n        var py2 = this.parent.graphic.y2-globalBaseY;\r\n        var pcx = (px1 + px2) * 0.5;                         \/\/center of displayElement object\r\n        var pcy = (py1 + py2) * 0.5;\r\n\r\n        var drawPointer = true;\r\n        var test;\r\n        var ix;\r\n        var iy;\r\n        test = intersection(cx,cy,pcx,pcy,px1,py1,px2,py1);\r\n        if (test !== false) {\r\n          ix = test.x;\r\n          iy = test.y;\r\n        } else {\r\n          test = intersection(cx,cy,pcx,pcy,px2,py1,px2,py2);\r\n          if (test !== false) {\r\n            ix = test.x;\r\n            iy = test.y;\r\n          } else {\r\n            test = intersection(cx,cy,pcx,pcy,px2,py2,px1,py2);\r\n            if (test !== false) {\r\n              ix = test.x;\r\n              iy = test.y;\r\n            } else {\r\n              test = intersection(cx,cy,pcx,pcy,px1,py2,px1,py1);\r\n              if (test !== false) {\r\n                ix = test.x;\r\n                iy = test.y;\r\n              } else {\r\n                drawPointer = false;\r\n              }            \r\n            }\r\n          }\r\n        }\r\n\r\n        globalCTX.fillStyle = globalBackgroundColor;\r\n        globalCTX.fillRect(x1,y1,this.width,this.height);\r\n\r\n        globalCTX.moveTo(x1,y1);\r\n        globalCTX.lineTo(x2,y1);\r\n        globalCTX.lineTo(x2,y2);\r\n        globalCTX.lineTo(x1,y2);\r\n        globalCTX.lineTo(x1,y1);\r\n        globalCTX.stroke();\r\n\r\n        var alpha;\r\n        var ax1; var ay1;\r\n        var ax2; var ay2;\r\n        var baseWid = 12;\r\n        var temp;\r\n        if (pcx != cx) {\r\n          var m1 = (pcy - cy) \/ (pcx - cx);\r\n          alpha = Math.atan(m1) + Math.PI * 0.5;\r\n          temp = baseWid * Math.cos(alpha);\r\n          ax1 = cx - temp;\r\n          ax2 = cx + temp;\r\n          temp = baseWid * Math.sin(alpha);\r\n          ay1 = cy - temp;\r\n          ay2 = cy + temp;\r\n        } else {\r\n          ay1 = cy;\r\n          ay2 = cy;\r\n          if (pcy >= pcx) {\r\n            ax1 = cx - baseWid;\r\n            ax2 = cx + baseWid;\r\n          } else {\r\n            ax1 = cx + baseWid;\r\n            ax2 = cx - baseWid;            \r\n          }\r\n        }\r\n\/*        globalCTX.strokeStyle = \"#FF8800\";\r\n        globalCTX.beginPath();\r\n        globalCTX.moveTo(ax1,ay1-4);\r\n        globalCTX.lineTo(ax1,ay1+4);\r\n        globalCTX.moveTo(ax1-4,ay1);\r\n        globalCTX.lineTo(ax1+4,ay1);\r\n        globalCTX.stroke();\r\n        globalCTX.strokeStyle = \"#0088FF\";\r\n        globalCTX.beginPath();\r\n        globalCTX.moveTo(ax2,ay2-4);\r\n        globalCTX.lineTo(ax2,ay2+4);\r\n        globalCTX.moveTo(ax2-4,ay2);\r\n        globalCTX.lineTo(ax2+4,ay2);\r\n        globalCTX.stroke();\r\n*\/        \r\n        var ix1;\r\n        var iy1;\r\n        test = intersection(ax1,ay1,ix,iy,x1,y1,x2,y1);\r\n        if (test !== false) {\r\n          ix1 = test.x;\r\n          iy1 = test.y;\r\n        } else {\r\n          test = intersection(ax1,ay1,ix,iy,x2,y1,x2,y2);\r\n          if (test !== false) {\r\n            ix1 = test.x;\r\n            iy1 = test.y;\r\n          } else {\r\n            test = intersection(ax1,ay1,ix,iy,x2,y2,x1,y2);\r\n            if (test !== false) {\r\n              ix1 = test.x;\r\n              iy1 = test.y;\r\n            } else {\r\n              test = intersection(ax1,ay1,ix,iy,x1,y2,x1,y1);\r\n              if (test !== false) {\r\n                ix1 = test.x;\r\n                iy1 = test.y;\r\n              } else {\r\n                drawPointer = false;\r\n                \/\/alert(\"this should not happen 2\");\r\n              }            \r\n            }\r\n          }\r\n        }\r\n        var ix2;\r\n        var iy2;\r\n        test = intersection(ax2,ay2,ix,iy,x1,y1,x2,y1);\r\n        if (test !== false) {\r\n          ix2 = test.x;\r\n          iy2 = test.y;\r\n        } else {\r\n          test = intersection(ax2,ay2,ix,iy,x2,y1,x2,y2);\r\n          if (test !== false) {\r\n            ix2 = test.x;\r\n            iy2 = test.y;\r\n          } else {\r\n            test = intersection(ax2,ay2,ix,iy,x2,y2,x1,y2);\r\n            if (test !== false) {\r\n              ix2 = test.x;\r\n              iy2 = test.y;\r\n            } else {\r\n              test = intersection(ax2,ay2,ix,iy,x1,y2,x1,y1);\r\n              if (test !== false) {\r\n                ix2 = test.x;\r\n                iy2 = test.y;\r\n              } else {\r\n                drawPointer = false;\r\n                \/\/alert(\"this should not happen 3\");\r\n              }            \r\n            }\r\n          }\r\n        }\r\n\r\n\/*        globalCTX.strokeStyle = \"#FF00FF\";\r\n        globalCTX.beginPath();\r\n        globalCTX.moveTo(cx,cy);\r\n        globalCTX.lineTo(pcx,pcy);\r\n        globalCTX.stroke();\r\n        globalCTX.beginPath();\r\n        globalCTX.moveTo(ax1,ay1);\r\n        globalCTX.lineTo(ix,iy);\r\n        globalCTX.stroke();\r\n        globalCTX.beginPath();\r\n        globalCTX.moveTo(ax2,ay2);\r\n        globalCTX.lineTo(ix,iy);\r\n        globalCTX.stroke();\r\n*\/\r\n        if (drawPointer) {\r\n          globalCTX.strokeStyle = \"#000000\";\r\n          globalCTX.beginPath();\r\n          globalCTX.moveTo(ix1,iy1);\r\n          globalCTX.lineTo(ix2,iy2);\r\n          globalCTX.lineTo(ix,iy);\r\n          globalCTX.lineTo(ix1,iy1);\r\n          globalCTX.stroke();\r\n          globalCTX.fillStyle = \"#000000\";\r\n          globalCTX.fill();\r\n          globalCTX.strokeStyle = \"#00FFFF\";\r\n          globalCTX.beginPath();\r\n          globalCTX.moveTo(ix1,iy1);\r\n          globalCTX.lineTo(ix,iy);\r\n          globalCTX.stroke();\r\n          globalCTX.beginPath();\r\n          globalCTX.moveTo(ix2,iy2);\r\n          globalCTX.lineTo(ix,iy);\r\n          globalCTX.stroke();\r\n        }\r\n\r\n        \/\/draw the text last\r\n        globalCTX.font = \"12px Arial\";\r\n        globalCTX.fillStyle = this.labelColor;\r\n        if (this.label.length > 0) {\r\n          globalCTX.textAlign = \"center\";\r\n          globalCTX.fillText(this.label,this.xLocation+(this.width*0.5)-globalBaseX,this.yLocation-globalBaseY+12);\r\n        }\r\n        globalCTX.textAlign = \"right\";\r\n        for (var i=0; i<this.valueCount; i++) {\r\n          var l = this.valueList[i].label;\r\n          var x = this.xLocation+this.maxLabelWidth-globalBaseX+5;\r\n          var y = this.yLocation+(i*12)+this.labelHeight-globalBaseY+12;\r\n          globalCTX.fillText(l,x,y);\r\n        }\r\n        globalCTX.fillStyle = this.valueColor;\r\n        globalCTX.textAlign = \"left\";\r\n        for (i=0; i<this.valueCount; i++) {\r\n          this.valueList[i].drawValue(this.xLocation+this.maxLabelWidth-globalBaseX+8,this.yLocation+(i*12)+this.labelHeight-globalBaseY+12);\r\n        }\r\n      };\r\n    }  \/\/DisplayGroup\r\n<\/pre>\n<p>The <code>intersection<\/code> function determines whether two line segments intersect.  Note that I said \"segments\" here; any two lines that aren't parallel will ultimately intersect, the question is whether it will happen in the segment(s) of interest.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    function intersection(x1,y1,x2,y2,x3,y3,x4,y4) {\r\n      \/\/see if line12 crosses line34 between the endpoints of both lines\r\n      var m1; var b1;\r\n      var m2; var b2;\r\n      var ix; var iy;\r\n      if (x1 != x2) {  \/\/first line not vertical\r\n        m1 = (y2 - y1) \/ (x2 - x1);\r\n        b1 = y1 - m1 * x1;\r\n        if (x3 != x4) {  \/\/second line not vertical\r\n          m2 = (y4 - y3) \/ (x4 - x3);\r\n          b2 = y3 - m2 * x3;\r\n          \r\n          if (m1 != m2) {\r\n            ix = (b2 - b1) \/ (m1 - m2);\r\n            iy = m1 * ix + b1;\r\n          } else {\r\n            \/\/should not need to explicitly test for overlapping parallel lines for convex shapes\r\n            return false;\r\n          }\r\n        } else {         \/\/second line is vertical\r\n          ix = x3;\r\n          iy = m1 * ix + b1;\r\n        }\r\n      } else {         \/\/first line vertical\r\n        if (x3 != x4) {  \/\/second line is not vertical\r\n          m2 = (y4 - y3) \/ (x4 - x3);\r\n          b2 = y3 - m2 * x3;\r\n          \r\n          ix = x1;\r\n          iy = m2 * ix + b2;\r\n        } else {\r\n          \/\/if this happens then no intersection\r\n          return false;\r\n        }\r\n      }\r\n      if (!(((ix >= x1) && (ix <= x2)) || ((ix >= x2) && (ix <= x1)))) {\r\n        return false;\r\n      }\r\n      if (!(((iy >= y1) && (iy <= y2)) || ((iy >= y2) && (iy <= y1)))) {\r\n        return false;\r\n      }\r\n      if (!(((ix >= x3) && (ix <= x4)) || ((ix >= x4) && (ix <= x3)))) {\r\n        return false;\r\n      }\r\n      if (!(((iy >= y3) && (iy <= y4)) || ((iy >= y4) && (iy <= y3)))) {\r\n        return false;\r\n      }\r\n      return {x: ix, y: iy};\r\n    }\r\n<\/pre>\n<p>This image shows how the construction lines are used to define the pointer.  If there is no intersection across the frame of either or both elements then no pointer is drawn. <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.rpchurchill.com\/images\/articles\/20170308_construction_lines.png\"><\/p>\n<p>The following steps are taken:<\/p>\n<ol>\n<li>The center points of the displayGroup and DisplayElement objects are determined (see the central purple line)<\/li>\n<li>The intersection of the center line and the outer frame of the DisplayElement is calculated (points <code>ix<\/code> and <code>iy<\/code>)<\/li>\n<li>The slope of the center line is determined<\/li>\n<li>A line perpendicular to the center line and passing through the center point of the DisplayGroup object is constructed<\/li>\n<li>Points are defined twelve pixels or units from the center point in each direction along the perpendicular line (marked by the blue and orange crosses, points <code>ax1,ay1<\/code> and <code>ax2,ay2<\/code>)<\/li>\n<li>The intersection of the two new lines with the border of the DisplayGroup are determined (points <code>ix1,iy1<\/code> and <code>ix2,iy2<\/code>)<\/li>\n<li>A triangle from points points <code>ix,iy<\/code>, <code>ix1,iy1<\/code>, and <code>ix2,iy2<\/code> is drawn <em>and filled<\/em> in black<\/li>\n<li>The two outer lines are drawn in the desired border color<\/li>\n<\/ol>\n<p>This method handles pointers that cut across a corner as well as any single edge segment.  It also doesn't obscure any text if the text is drawn after everything else.  I was happy when I realized I could take this shortcut and not have to worry about corners as a special case.  The code for drawing the construction lines shown in the image is commented out above, but left in place so you can follow the action.<\/p>\n<p>The mathematical methods for finding the intersection points are correct but the digital implementation has the infuriating habit of not finding some intersections that actually happen.  This is simply because the calculations aren't accurate enough to always pass the hit tests.  Given that we're using 64-bit reals I find that hard to understand but it is what it is.  I've traced through a couple of examples and have seen it happen.  I have to figure out a way around this problem but have not done so yet.  In the meantime I've tested to ensure that the process works from all angles, including when the center line is perfectly vertical when the DisplayGroup object is both above and below the DisplayElement target.<\/p>\n<p>Odds and ends:<\/p>\n<p>For now the described process only works with DisplayGroup objects that are not rotated (the process of finding intersection with the DisplayElement frame is general and would handle a rotated target object) but I'm not sure DisplayGroup objects would ever have to be rotated.<\/p>\n<p>For now the described process only \"works\" when the target DisplayElement is a rectangular component.  It would have to be modified for components that are paths, though that process should be relatively straightforward, since the center point of a line segment would be easy to define.<\/p>\n<p>I had to add a parent value to the DisplayGroup object that points back to the simulation component it represents.  This was needed so the code in the DisplayGroup object could trace to its parent component object and then to its DisplayElement (graphic) object.<\/p>\n<p>If a component has been selected and highlighted then, for now, its DisplayGroup is drawn as well if it is defined.  In the future this is going to require a separate flag since the selection process is going to lead to a menu to allow the user to take different actions or drive other actions based on varying contexts (e.g., run mode, edit mode, etc.).  <\/p>\n<p>This method could (and should and will) be generalized to display other types of information, with the primary example being a real-time scrolling graph of one or more state values.<\/p>\n<p>The DisplayGroup objects are drawn after everything else in the 2D scene so they are always on top.  I will expand the user hit testing to scan for these objects first when I implement code to allow the user to move, modify, or hide them.<\/p>\n<pre class=\"toolbar-overlay:false wrap:false height-set:true lang:default decode:true \">\r\n    function drawModel() {\r\n      clearCanvas(globalBackgroundColor);\r\n      var i;\r\n      \/\/draw components\r\n      for (i = 0; i < numComponents; i++) {\r\n        if (setOfComponents[i].getComponentType() == \"Path\") {\r\n          setOfComponents[i].updateGraphic();\r\n          setOfComponents[i].graphic.drawBasic();\r\n          \/\/setOfComponents[i].drawData();\r\n        } else if (setOfComponents[i].getComponentType() != \"Arrivals\") {\r\n          setOfComponents[i].updateGraphic();\r\n          setOfComponents[i].graphic.drawBasic();\r\n        } else if (setOfComponents[i].getComponentType() == \"Arrivals\") {\r\n          \/\/setOfComponents[i].drawData();\r\n        }\r\n      }\r\n      \/\/draw nodes\r\n      for (i = 0; i < numComponents; i++) {\r\n        if (setOfComponents[i].getComponentType() == \"Path\") {\r\n          setOfComponents[i].graphic.drawNodes();\r\n          \/\/setOfComponents[i].drawNodes();\r\n        } else if (setOfComponents[i].getComponentType() != \"Arrivals\") {\r\n          setOfComponents[i].graphic.drawNodes();\r\n        }\r\n      }\r\n      \/\/draw entities\r\n      for (i = 0; i < numComponents; i++) {\r\n        if (setOfComponents[i].componentType == \"Path\") {\r\n          setOfComponents[i].graphic.drawEntities();\r\n          \/\/setOfComponents[i].drawEntities();\r\n        } else if (setOfComponents[i].componentType != \"Arrivals\") {\r\n          setOfComponents[i].graphic.drawEntities();\r\n        }\r\n      }\r\n      \/\/draw display groups, if active\r\n      for (i = 0; i < numComponents; i++) {\r\n        if (typeof setOfComponents[i].graphic !== \"undefined\") {\r\n          if (setOfComponents[i].graphic.highlighted) {\r\n            setOfComponents[i].drawData();\r\n          }\r\n        }\r\n      }\r\n    }  \/\/drawModel\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Today I connected the display of the DisplayGroup objects to the component selection event. That is, if you click on a component it brings up that component&#8217;s DisplayGroup object. This process involves several steps. First, the location on the canvas &hellip; <a href=\"https:\/\/rpchurchill.com\/wordpress\/posts\/2017\/03\/08\/a-simple-discrete-event-simulation-part-87\/\">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,163],"_links":{"self":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1716"}],"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=1716"}],"version-history":[{"count":3,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1716\/revisions"}],"predecessor-version":[{"id":1719,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/posts\/1716\/revisions\/1719"}],"wp:attachment":[{"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1716"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1716"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpchurchill.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1716"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}