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:
Associate a graphics list with your display using MdispControl() with M_ASSOCIATED_GRAPHIC_LIST_ID.
If necessary, restrict interactive properties of graphics using MgraControl() or MgraControlList().
Hook a function to the 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.
Make graphics in the graphics list interactive using MdispControl() with M_GRAPHIC_LIST_INTERACTIVE.
If necessary, allow the user to add graphics interactively to the 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. 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.
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:
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.