| Customize Help

Creating and modifying graphics interactively



You can create and edit graphics interactively in a display. This can be useful to, for example, interactively define a graphic that covers a required region, and then use the graphic to define or modify a region of interest.

The following steps provide a basic methodology for enabling and controlling interactivity with graphics:

  1. Associate a 2D graphics list with your display using MdispControl() with M_ASSOCIATED_GRAPHIC_LIST_ID.

  2. If necessary, restrict interactive properties of graphics using MgraControl() or MgraControlList().

  3. Hook a function to the 2D graphics list event caused by a change in the state of interactivity of a graphic, using MgraHookFunction() with M_INTERACTIVE_GRAPHIC_STATE_MODIFIED. For example, if the state of interactivity changes from M_STATE_..._DRAGGED to M_STATE_..._HOVERED, a graphic has been modified interactively.

  4. Make graphics in the 2D graphics list interactive using MdispControl() with M_GRAPHIC_LIST_INTERACTIVE.

  5. If necessary, allow the user to add graphics interactively to the 2D graphics list using MgraInteractive() set to one of the possible graphics types.

With a graphic that has interactivity enabled, you can move a graphic's position, resize the graphic, or rotate the graphic. When a user clicks on a displayed graphic with interactivity enabled, the graphic is considered selected. By default, you must click on the contour of a graphic to select it. To select closed graphics (such as circles) by clicking anywhere on them, even if they are not filled, call MgraControlList() and enable M_EASY_SELECTION.

A selected graphic is typically surrounded by a selection box (represented with a dotted line) with handles around it that allows a user to resize or rotate the graphic. Certain graphics can also have handles specific to that graphic. For example, an arc has handles to increase its length and the polygon has handles to change the position of each vertex.

When you restrict any or all of the interactive properties of a graphic using MgraControl() or MgraControlList(), the handles specific to that property will not appear. For example, disabling interactive rotation will cause the rotate handle to not appear when the graphic is selected. You can select multiple graphics on the display by pressing the Ctrl key while clicking on the required graphics. To change this behavior, call MgraControlList() with M_MULTIPLE_SELECTION_KEY.

The 2D graphics list has default behaviors for interactively moving, resizing, or rotating its graphics. To modify these behaviors, call MgraControlList() with M_MODE_.... For example, if you click on a corner of a graphic's selection box and drag your mouse to resize the graphic, the opposite corner remains fixed. This happens because M_MODE_RESIZE is set to M_FIXED_CORNER by default. To modify this resizing behavior so that, as you drag a corner of the selection box with your mouse, all corners move symmetrically (and the center remains fixed), set M_MODE_RESIZE to M_FIXED_CENTER. The 2D graphics list also has default behaviors for interactively moving, resizing, or rotating its graphics with your mouse while pressing the Alt (M_MODE_..._ALT), Ctrl (M_MODE_..._CTRL), or Shift (M_MODE_..._SHIFT) key.

Instead of using your mouse to move, resize, or rotate interactively selected graphics, you can use keys on the keyboard. For example, you can specify that pressing the Arrow keys will move the selected graphic. To specify keyboard keys with which to manipulate (perform actions on) interactively selected graphics, call MgraControlList() with M_ACTION_KEY_....

For a complete list of settings that let you control interactivity in your application, refer to MgraControl() or MgraControlList().

Hooking a function to a 2D graphics list event

The MIL Graphics module allows you to hook a function to a 2D graphics list event using MgraHookFunction(). When developing applications that allow interactive user manipulations of graphics, it can be advantageous if a function is hooked to the event caused by a change in the state of interactivity in the 2D graphics list (M_INTERACTIVE_GRAPHIC_STATE_MODIFIED). The associated function will automatically be triggered whenever the state of interactivity is changed. The state of interactivity (M_INTERACTIVE_GRAPHIC_STATE) indicates the active interaction state occurring on the selected graphics in the 2D graphics list. If, for example, a graphic is displayed with interactive mode enabled, and the cursor is not hovered over the graphic or one of its handles, then the state of interactivity is M_STATE_IDLE. If the user then hovers over the graphic or one of its handles with the cursor, the state of interactivity changes to M_STATE_GRAPHIC_HOVERED or M_STATE_HANDLE_HOVERED, respectively. If the user then holds down the left mouse button when hovering over the graphic or one of its handles, the state of interactivity changes to M_STATE_GRAPHIC_DRAGGED or M_STATE_HANDLE_DRAGGED, respectively.

There are other events to which you can hook a function, such as M_GRAPHIC_LIST_MODIFIED, M_GRAPHIC_MODIFIED, and M_GRAPHIC_SELECTION_MODIFIED. These events will trigger due to both interactive and programmatic changes. For an example of using MgraHookFunction() in an application with an interactive graphic, see:

Creating graphics interactively

You can allow a user to interactively add a graphic to a 2D graphics list. The function MgraInteractive() changes the state of interactivity of the 2D graphics list to allow the user to define graphics in the display using the mouse. The graphic types that can be created interactively are: an arc (M_GRAPHIC_TYPE_ARC), a dot (M_GRAPHIC_TYPE_DOT), a line (M_GRAPHIC_TYPE_LINE), a polygon (M_GRAPHIC_TYPE_POLYGON), a polyline (M_GRAPHIC_TYPE_POLYLINE), and a rectangle (M_GRAPHIC_TYPE_RECT). By calling MgraInteractive() with one of these values, the state of interactivity changes to M_STATE_WAITING_FOR_CREATION. For more information about how to create these graphics interactively, see the Details about creating graphics interactively subsection of this section.

When in an M_STATE_WAITING_FOR_CREATION state, the user is expected to define a graphic interactively in the display. Note, however, that MgraInteractive() is asynchronous and only changes the state of interactivity; it will not wait for the user to define the graphic. You should have your application wait for the user to finish defining the graphic, before calling further functions.

When the user begins defining the graphic, the state of interactivity (M_INTERACTIVE_GRAPHIC_STATE) changes to M_STATE_BEING_CREATED. After a user has defined the graphic interactively, the state of interactivity returns to M_STATE_IDLE and you can continue your application from that moment.

If the program does not wait for the user to create the graphic after a call to MgraInteractive(), your application might not perform as you initially intended. Waiting for user interaction can be accomplished by hooking a function to M_INTERACTIVE_GRAPHIC_STATE_MODIFIED event and having the hook function signal the end of the thread wait event. The following example shows how to wait for the user to interactively add a rectangle to the 2D graphics list and then use this graphic to define the ROI of an image buffer. The hook-handler function is called each time the state of interactivity changes, and checks if the state has changed to M_STATE_BEING_CREATED. A data structure is used to store information inquired in the hook-handler function. Note that when using interactive graphics, the graphics can potentially be added in a different thread. In the following example, the label of the graphic is obtained using MgraGetHookInfo() with M_GRAPHIC_LABEL_VALUE rather than using MgraInquireList() with M_LAST_LABEL. This is due to the fact that MgraGetHookInfo() with M_GRAPHIC_LABEL_VALUE will return the label value of an interactively created graphic regardless of the thread in which the graphic was added.

/* Structure used to hold information for the hook function. */
struct CreateGraphicInfo
   {
   MIL_ID  CreationDoneEventThrId;
   MIL_INT CreatedGraphicLabel;
   };

/* Function used to interactively define graphic for use as an ROI */
static MIL_INT DefineRectROI(MIL_ID SysId, MIL_ID ImageId, 
                      MIL_ID GraphicsContextId, MIL_ID GraphicsListId)
   {
   /* Create a synchronization event to indicate when graphic creation is done. */
   CreateGraphicInfo HookInfo;
   MthrAlloc(SysId, M_EVENT, M_NOT_SIGNALED+M_AUTO_RESET, M_NULL, M_NULL,
             &HookInfo.CreationDoneEventThrId);

   /* Hook a function to detect when the graphic's creation has ended,
      and pass a MIL event to it, in order to notify the thread to resume. */
   MgraHookFunction(GraphicsListId, M_INTERACTIVE_GRAPHIC_STATE_MODIFIED,
                    &WaitUntilCreationEnds, &HookInfo);

   /* Set the graphics list's state to M_WAIT_FOR_CREATION. This allows the user 
      to add a graphic interactively to the list from the display. The graphic 
      is created by clicking in the display at the location you require one of 
      the rectangle's corners and dragging your cursor to the opposite corner. */
   MgraInteractive(GraphicsContextId, GraphicsListId, M_GRAPHIC_TYPE_RECT, 
                   M_DEFAULT, M_AXIS_ALIGNED_RECT);

   /* Wait for the graphic creation to end. The function hooked to the event will 
      set the event state to M_SIGNALED to indicate that graphic creation has ended. */
   MthrWait(HookInfo.CreationDoneEventThrId, M_EVENT_WAIT, M_NULL);

   /* Free allocated resources. */
   MthrFree(HookInfo.CreationDoneEventThrId);

   /* Get the label of the newly created rectangle. Note that MgraInquireList() with 
      M_LAST_LABEL might not work at this point, since the graphic might have been created
      in a separate thread. The call to retrieve the graphic's label is performed in the 
      hooked function, after the graphic is added to the list.*/
   MIL_INT RectangleLabel = HookInfo.CreatedGraphicLabel;

   /* Use the user-defined rectangle to define the region of interest for later processing.*/
   MbufSetRegion(ImageId, GraphicsListId, M_DEFAULT, M_RASTERIZE+M_FILL_REGION, M_DEFAULT);
   
   /* Return the label of the rectangle to the main function */
   return RectangleLabel;
   }

Where the hooked function is:

/* Hook function used to notify a thread that interactive graphic creation has ended. */
static MIL_INT MFTYPE WaitUntilCreationEnds(MIL_INT HookType, MIL_ID EventId, void *UserDataPtr)
   {
   /* Casts UserDataPtr to the data type of the hook information structure. */
   CreateGraphicInfo* HookInfoPtr = (CreateGraphicInfo*)UserDataPtr;

   /* Get the previous and current interactive states of the graphics list. */
   MIL_INT PreviousInteractiveState, CurrentInteractiveState;
   MgraGetHookInfo(EventId, M_INTERACTIVE_GRAPHIC_PREVIOUS_STATE, &PreviousInteractiveState);
   MgraGetHookInfo(EventId, M_INTERACTIVE_GRAPHIC_STATE, &CurrentInteractiveState);

   if (CurrentInteractiveState == M_STATE_BEING_CREATED)
      {
      /* If the current state is M_STATE_BEING_CREATED, the graphic being 
         created has been assigned a label value and position in the graphics list.
         Fetch its label and store it in the hook information structure. */
      MgraGetHookInfo(EventId, M_GRAPHIC_LABEL_VALUE, &HookInfoPtr->CreatedGraphicLabel);
      }
   else if (PreviousInteractiveState == M_STATE_BEING_CREATED)
      {
      /* If the previous state was M_STATE_BEING_CREATED, graphic creation is done. Set
         the event state to M_SIGNALED so the waiting thread can continue execution. */
      MthrControl(HookInfoPtr->CreationDoneEventThrId, M_EVENT_SET, M_SIGNALED);
      }

   return M_NULL;
   }

MgraInteractive() can be useful if your application requires toolbars for a graphical user interface. For example, you can use another API to create a toolbar, with one of its buttons calling MgraInteractive() with M_GRAPHIC_TYPE_RECT. After clicking on the toolbar button, the user can create a rectangle in the display to specify where to perform processing or analysis in the image.

Details about creating graphics interactively

The following subsections illustrate some details about how to interactively create graphics, by calling MgraInteractive() with M_GRAPHIC_TYPE_....

Rectangle

M_GRAPHIC_TYPE_RECT adds a rectangle to the 2D graphics list by defining it interactively in the display. Typically, M_GRAPHIC_TYPE_RECT is set to M_AXIS_ALIGNED_RECT, to define an axis-aligned rectangle. This requires you to click on a rectangle's corner in the display, and then drag the mouse to the opposite corner. An example of this behavior can be seen in the animation below.

M_GRAPHIC_TYPE_RECT can also be set to M_ORIENTED_RECT, to define an oriented rectangle. This requires three clicks in the display. By default, the first click defines a fixed corner of the rectangle, the second click defines its width (a line extending to the fixed corner) and orientation, and the third click defines its height on one side of the width line. An example of this behavior can be seen in the animation below.

Arc, ellipse, and circle

M_GRAPHIC_TYPE_ARC adds an arc to the 2D graphics list by defining it interactively in the display. You can also use M_GRAPHIC_TYPE_ARC to interactively define a circle or ellipse.

Typically, M_GRAPHIC_TYPE_ARC is to M_AXIS_ALIGNED_ELLIPSE, to define an axis-aligned ellipse (or circle). This requires you to click on one corner of the ellipse bounding box in the display, and then drag the mouse to the opposite corner. An example of this behavior can be seen in the animation below.

By setting M_GRAPHIC_TYPE_ARC to M_ARC_THREE_POINTS, you can define a circular arc. This requires three clicks in the display. The first and third clicks determine the arc's start angle and end angle (not necessarily in this order); the second click establishes the position of the arc. An example of this behavior can be seen in the animation below.

Instead of using the ellipse's bounding box, you can set M_GRAPHIC_TYPE_ARC to M_CIRCLE, to use the circumference of a circle. An example of this behavior can be seen in the animation below.

Polygon

M_GRAPHIC_TYPE_POLYGON adds a polygon to the 2D graphics list by defining it interactively in the display. This requires you to left-click each polygon vertex in the display, except for the last vertex, which you must define with a right-click. By default, creating a polygon while holding down the Alt key snaps the next polygon segment to 15° angles. An example of this behavior can be seen in the animation below.

Polyline

M_GRAPHIC_TYPE_POLYLINE adds a polyline to the 2D graphics list by defining it interactively in the display. This requires you to left-click each polyline vertex in the display, except for the last vertex, which you must define with a right-click. By default, creating a polyline while holding down the Alt key snaps the next polyline segment to 15° angles. An example of this behavior can be seen in the animation below.

Line

M_GRAPHIC_TYPE_LINE adds a line to the 2D graphics list by defining the line interactively in the display. This requires you to click on the line's start point in the display, and then drag the mouse to the line's end point. Holding Alt during line manipulations will restrict its orientation to specific angles; use M_ANGLE_SNAPPING_VALUE to specify the snapping angle values. An example of this behavior can be seen in the animation below.