A Simple Discrete-Event Simulation: Part 87

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’s DisplayGroup object. This process involves several steps.

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 globalBaseX and globalBaseY from the actual click location in the same manner that DisplayElement x- and y-locations are modified.

Here’s the updated event handling code, which also shows the new way that mouse click events are handled per yesterday’s discussion.

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:

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.

The intersection 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.

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.

The following steps are taken:

  1. The center points of the displayGroup and DisplayElement objects are determined (see the central purple line)
  2. The intersection of the center line and the outer frame of the DisplayElement is calculated (points ix and iy)
  3. The slope of the center line is determined
  4. A line perpendicular to the center line and passing through the center point of the DisplayGroup object is constructed
  5. 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 ax1,ay1 and ax2,ay2)
  6. The intersection of the two new lines with the border of the DisplayGroup are determined (points ix1,iy1 and ix2,iy2)
  7. A triangle from points points ix,iy, ix1,iy1, and ix2,iy2 is drawn and filled in black
  8. The two outer lines are drawn in the desired border color

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.

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.

Odds and ends:

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.

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.

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.

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.).

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.

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.

This entry was posted in Software and tagged , , . Bookmark the permalink.

Leave a Reply