| MIL 10 User Guide
| Customize Help

Creating and modifying graphics interactively



See also
Availability
Available in MIL-Lite

Available in MIL
Next

Graphics can be created or edited interactively in a display. This can be useful, for example, to have a user 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:

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. 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. For more information on controlling interactivity in your application, consult MgraControl() or MgraControlList() in the MIL Reference. You can select multiple graphics on the display, by pressing the Ctrl key while clicking on the required graphics.

Hooking a function to a graphics list event

The MIL Graphics module allows you to hook a function to a 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 graphics list (M_INTERACTIVE_GRAPHIC_STATE_MODIFIED). The state of interactivity (M_INTERACTIVE_GRAPHIC_STATE) is used to indicate the interaction occuring to the graphics in the 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 graphics list. The function MgraInteractive() changes the state of interactivity of the 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 specfiying MgraInteractive() with one of these values, the state of interactivity changes to M_STATE_WAITING_FOR_CREATION.

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 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_DEFAULT);

   /* 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.