ILNumerics scene graph implements a flexible eventing model, which allows the realization of arbitrary user interactions for custom scenes. The processing of events is organized similar to the W3C Specification for Handling Events in DOM Documents. Further similarities arise in terms of the WPF event handling scheme – adapted for 3 - dimensional scenes.
Learn more about:
The following mouse events are recognized within ILNumerics: MouseMove, MouseDown, MouseUp, MouseEnter, MouseLeave, MouseWheel, MouseClick, MouseDoubleClick.
All events for ILNumerics scenes originate from the panel / control in the hosting environment. Often this will be the ILPanel control, which hosts the scene for Windows.Forms. Events received by the control are delegated to the scene and handled by individual handler methods associated with nodes within the scene graph.
The processing starts at the scene graph root node and travels further down to the target event node. That phase sometimes is referred to as “Capture Phase” (W3C) or “Preview or Tunneling Events” (WPF). How the target event node is determined is described in the next paragraph. After the event has reached the target event node, it travels (bubbles) up again until it reaches the scene graph root node. Afterwards, the processing of the event is finished.
How the target event node is found
If the mouse cursor is over one or several visual shapes, the shape closest to the camera is considered the event target. Visual shapes are any scene graph nodes in ILNumerics, which derive from ILDrawable; specifically: ILPoints, ILLines, ILLineStrip, ILTriangles, ILTriangleStrip, ILTriangleFan, ILLabel.
If the mouse is currently not over an ILDrawable node but instead is over the background in an area of the panel/ control which is associated with the ScreenRect rectangular area of a camera node (ILCamera) or a class derived from ILCamera, the corresponding camera node is taken as event target. If a conflict is detected (i.e.: the mouse location is contained in more than one ScreenRect rectangular area), the node with the smaller ScreenRect area is taken. If both ScreenRect areas are equally sized, the ‘younger’ node (with the higher ID) is taken.
An exception to the rules above exists for MouseMove events in a drag situation. If the mouse button was pressed, held over the scene and moved, the node, which was under the mouse when the button was pressed down is considered as event target. Therefore, in a drag situation, the node, which received the MouseDown event will receive all consecutive MouseMove events, regardless whether the cursor is still over that node or not.
How events travel along the scene graph
Events in ILNumerics always go through both phases: capturing and bubbling. The processing for the capture phase starts with the determination of the event target node (see above). If no target node was found, the event processing is canceled. If a target event node was found, the event is populated to the root node of the scene and travels down from the root node to the target event node.
On every node, which the event passes, all existing handlers are called. Two types of event handling methods can exist on every node: instance event handlers and custom event handlers. Instance event handlers are class members which were implemented by the designer of the scene graph node class. They override default implementations from ILNode and realize default behavior for special scene graph nodes. Examples are ILCamera and ILPlotCube which both handle MouseDown, MouseUp, MouseMove and MouseWheel events for default rotation and zoom actions within the scene.
Custom event handlers are implemented by the designer of the scene (this is you). Such events are registered for handling by help of the .NET event delegates exposed by ILNode: MouseMove, MouseDown, MouseUp etc…. Examples are given in the next section.
Once an ILNumerics event reaches a node in either phase (capture or bubbling) all registered custom event handlers are processed first. No guarantee is given regarding the order of processing among multiple registered custom events. After the processing of all custom events has finished, the event gets populated to the instance event handlers of the node (if any). If all existing events for the node have been processed, the event travels further down to the event target (capture phase) or up to the scene graph root (bubbling phase).
Two mouse events exist which are an exception to the above rules: MouseEnter and MouseLeave events are captured down to the target event node but are not bubbled up to the root anymore.
Registering Custom Event Handlers
In order to give certain nodes the ability to react on user input, one can utilize .NET event delegates which are exposed on every node: MouseMove, MouseDown, MouseUp, MouseClick, MouseDoubleClick, MouseEnter, MouseLeave, and MouseWheel. Those events fire every time a corresponding event passes the node in either phase. It is important to keep in mind: Every event commonly passes every node from the scene graph root to the event target node exactly two times – once for every phase (capture and bubbling). Therefore, the .NET event delegates exposed by ILNode fire twice! The next section will show how to deal with that.
The example below demonstrates how to register custom event handlers to scene graph nodes. A sphere is added to the scene and custom handlers for MouseClick, MouseEnter and MouseLeave events are registered on the sphere. The example uses C# += syntax. For Visual Basic, one would utilize the common WithEvents/ Handles or AddHandler syntax.
While moving the mouse over the scene, every time it enters or leaves a shape of the sphere the forms title bar shows its name. Note that the event handler was registered on the sphere and not on the actual shapes, since a sphere is made of:
By registering the event handler on the parent group node it is able to handle all events targeting any child node within the sphere. The event passes the sphere group node on its way to the event target node (or back up to the root in the bubbling phase).
This is the reason why the message box is shown twice when the user clicks on the sphere only once: the handler for sphere.MouseClick is processed twice for every single click event.
Properties of Events
Events in ILNumerics carry a class of type ILMouseEventArgs. It provides a number of useful information to the implementer of custom event handlers. Furthermore, it can be used to control the processing of the event. ILMouseEventArgs derive from System.Windows.Forms. MouseEventArgs, hence they provide all properties of common mouse events. In addition to that, ILNumerics adds the following properties:
|Cancel (bool)||false||Utilized by the user to cancel further processing of the event.|
|-||Eases the determination of the state of control keys.|
|TimeMS (long)||-||The global scene time when the event was raised.|
|Target (ILNode)||-||The final target node for the event, mostly the node under the mouse cursor.|
|Refresh (bool)||false||Users set this flag to true in order to signal the driver to redraw the scene after processing the event.|
|DirectionUp (bool)||-||Gives the direction of event processing; false: capture (down), true: bubbling (up).|
|LocationF (PointF)||-||The relative location of the mouse within the current camera’s viewport. See: Viewports.|
DirectionUpDirectionUp enables implementers of custom event handlers to control in which phase the handler should be acting. The flag is readonly and controlled by ILNumerics. In order to restrict a handler’s action to the bubbling phase, one may include a simple condition in the handling code:
Note that when an event reaches the target event node for processing, the DirectionUp flag will be set to true. Once a handler sets the Cancel flag to true the event processing is canceled. The event will not get populated to subsequent nodes on the path to / from the event target node. However, the processing of the handlers from the current node is not guaranteed to finish immediately.
DirectionUp and Cancel provide the option to prioritize the handlers of some nodes over handlers of other nodes depending on their position within the scene graph. Handling an event in the capture phase and setting the Cancel flag to true will disallow subsequent nodes further down the tree from receiving and handling the event. In contrast to that, handling an event in the bubbling phase will allow child nodes to handle the event earlier and may even cancel it before it ever reaches your handler.
The Target property reflects the current event target node. For most handlers on group nodes and derived types the target node will – naturally – differ from the current node. This is because group nodes do not produce any visual output itself, hence cannot serve as ‘node under cursor’ and hence are not selected as event target node directly. The only exception is found in camera nodes which are taken as event target node if the mouse is over the background of the camera (see above).
If a handler causes changes to the scene which eventually require immediate redraw, the Refresh flag of the event arguments should be set to true. After the processing of all subsequent events is finished, the driver will then trigger an immediate redraw of the scene.
We will end this section with another example. In order to show the name of the node which is currently under the cursor in the forms title bar, we register a custom event handler to the scene root node and handle the MouseEnter event:
Since we want the handler to catch and handle the mouse events for every object in the scene, we need to register the handler at some parent node for all those objects. The default camera (just like the default Screen node) is a direct child of the scenes root node. We use it to access the root and its MouseEnter event. The Target property of the mouse event arguments a reflects the node under the cursor. We simply take it and write it to the forms title.
Scene manipulations from inside custom mouse event handlers
One may wants to trigger object changes from custom mouse event handlers. Care must be taken in order to address the correct object as target for such changes. The source object of the event (i.e. the object which fired the event) is given to the implementor as first argument of the handler. In order to access properties of the scene, one must always use this object and cast it to the apropriate type: