There are two methods to select items within a chemical system rendering, where item refers to one of the following: atoms, bonds, atom labels, or bond labels. One method is to apply an SoRayPickAction to a scene graph and process the resulting picked point list. In this case, the information on the selected items is maintained in an instance of the ChemDetail class. The other method is to use the ChemSelection node provided by Molecular Inventor (MI).
The ChemSelection node provides selection mechanisms for users to interactively select items within a chemical system. These mechanisms include a drag rectangle, a lasso, and a selection sphere. MI also provides methods to programmatically select the items in a system.
MI does not do anything with the selected items except highlight them according to the field values in ChemDisplayParam. It is up to your MI application to process the selection list, for example, your MI application might enable the user to copy the selected portions of the chemical system to the X clipboard. To aid in processing the selections, ChemSelection maintains lists of callback functions that are invoked within your application at various stages of the selection process.
The items selected by ChemSelection are stored in instances of the ChemPath class. ChemSelection creates and maintains a list of selected ChemPaths in a selection list, which is an instance of the ChemPathList class.
This chapter contains the following sections:
“Using ChemDetail” describes the information stored in this class and how to use ChemDetail in an application.
“Understanding the ChemSelection Node” describes the ChemSelection node and how to use it within a scene graph.
“Understanding Selection Lists” describes the manner in which ChemSelection uses the ChemPath and ChemPathList classes to manage a selection list.
“Interactive Selection Mechanisms” describes the mechanisms provided by ChemSelection that allow users to interactively select items within a chemical system.
“Setting the Interactive Selection Parameters” describes the fields in ChemSelection which affect the manner in which the interactive selection mechanisms operate.
“Specifying Selections Programmatically” describes the methods in ChemSelection that you would use to programmatically select items within a chemical system.
“Using Callback Methods” describes the methods used to manage callback function lists within the ChemSelection node.
For a code example that shows how to copy selected items to the X clipboard, see “Using the Selection List.”
Chapter 9 in The Inventor Mentor discusses the various actions that can be applied to a scene graph. One of these is the SoRayPickAction. When you click on a scene, SoRayPickAction projects a ray from the camera position through the scene. The result of applying an SoRayPickAction is an SoPickedPoint or an SoPickedPointList, which is a list of SoPickedPoints, sorted closest to farthest, as shown in the following diagram.
An SoPickedPoint contains general information about the point that was picked, such as the coordinates and the surface normal at that point. It also provides a getDetail() method that returns more specific information regarding the picked object. In Molecular Inventor, when an SoRayPickAction is applied, the ChemDisplay node in a scene graph creates an instance of the ChemDetail class. It contains information about which atom, bond, atom label, or bond label was picked.
The getDetail() method returns a pointer to an instance of SoDetail. Therefore, you need to cast the pointer to an instance of ChemDetail in a fashion similar to the following:
const SoDetail *pickDetail = myPickedPoint->getDetail();
if (pickDetail != NULL && pickDetail->getTypeId() ==
ChemDetail::getClassTypeId()) {
int32_t atomIndex, bondIndex;
ChemDetail *chemDetail = (ChemDetail *)pickDetail;
chemDetail->getAtomBondIndex(atomIndex, bondIndex);
if (atomIndex) != -1) {
printf(“Atom %d picked\n”, atomIndex);
}
else if (bondIndex != -1) {
printf(“Bond %d picked\n”, bondIndex);
}
}
|
As shown in the example above, the get...() functions in ChemDetail return information regarding the items picked. All get...() functions return -1 if an item of a given type was not picked. The get...() methods are described in the following table:
Table 6-1. ChemDetail get...() methods
Method | Description |
|---|---|
Returns the atom picked or -1 if no atoms were picked. | |
Returns the bond picked or -1 if no bonds were picked. | |
Returns the atom label picked or -1 if no atom labels were picked. | |
Returns the bond label picked or -1 if no bond labels were picked. | |
Returns the atom or bond picked. Only one item can be picked so one of the return values will be -1. | |
Returns the atom label or bond label picked. Only one item can be picked so one of the return values will be -1. |
ChemSelection is very similar in concept to the SoSelection node within Open Inventor (described in Chapter 10 in The Inventor Mentor). ChemSelection is a grouping node derived from SoSeparator and therefore has methods that manage the addition and removal of child nodes. ChemSelection also provides methods an application can invoke to programmatically select items, and selection mechanisms that allow the user of an application to interactively select items from the display. While ChemSelection can have as its children both MI and Open Inventor nodes, its selection mechanisms, both programmatic and interactive, only operate on MI nodes.
To use the selection mechanisms provided by a ChemSelection node, you must make the ChemSelection node the parent of all the other nodes in the scene graph or the subgraph that contains the items you want to allow the user to select.
Typically, there is only one ChemSelection node within a scene graph and its position is near the top of the scene graph. There can be any number of MI and Open Inventor nodes as children of a ChemSelection node. However, it is unadvisable to have a ChemSelection node as a child of another ChemSelection node due to possible conflicts in performing the selections. The simplest MI scene graph which employs the ChemSelection node is:
There are two class constructors for ChemSelection:
ChemSelection() ChemSelection(int nChildren) |
The first constructor creates an instance of the ChemSelection node using default values. The second constructor allows you to specify the approximate number of child nodes added under the instance of the ChemSelection node. (See Figure 6-2.)
Since ChemSelection acts as a grouping node, it has several methods that are used to manage its children. These methods are described in the following table.
Table 6-2. Methods to Manage Child Nodes
Method | Description |
|---|---|
Adds a child as the last one in group. | |
Adds a child so that it becomes the one with the specified index. | |
Returns a pointer to index'th child node. | |
Finds the index of a specified child within the ChemSelection children. | |
Returns the number of children. | |
Removes the child with a specified index from the ChemSelection children. | |
Removes the first instance of a specified child from the ChemSelection children. | |
Removes all of the children of ChemSelection. | |
Replaces the child with a specified index with a new child. | |
Replaces the first instance of a specified child with the new child. |
Interactive selection mechanisms provided by the ChemSelection node allow users to select all or part of a chemical system for an application-defined purpose. Additionally, an application can programmatically select and deselect items, for example, an application might programmatically select atoms with ID's numbered 5 through 10. The ChemDisplay node can automatically highlight the selected items according to values set within a ChemDisplayParam node, as shown in the following figure.
Whether items are selected interactively or programmatically, ChemSelection places the selected items on a selection list. The selection list can be accessed by an application in order to take some appropriate action with the items on the list.
When one or more items are selected, ChemSelection stores information regarding the selected items in instances of the ChemPath class. A ChemPath is comprised of an SoPath, which is the list of nodes traversed in a selection process, and a set of indices, which store the atoms, bonds, atom labels and bond labels that were selected within the SoPath.
The SoPath portion of a ChemPath always begins with the ChemSelection node and always ends with the ChemDisplay node responsible for rendering the selected items. Therefore, the indices of the ChemPath specify the atoms, bonds, and labels that were selected within the particular ChemDisplay node.
To access the various portions of a ChemPath, you use the methods described in the following table:
Table 6-3. ChemPath's get*() Methods
Method | Description |
|---|---|
Returns a pointer to the SoPath portion of a ChemPath. | |
Returns the indices of the selected atoms. | |
Returns the indices of the selected bonds. | |
Returns the indices of the selected atom labels. | |
Returns the indices of the selected bond labels. |
Whenever any items are selected or deselected, a new instance of ChemPath is generated. This new ChemPath is compared with previously generated ChemPaths currently in use by ChemSelection. These ChemPaths are maintained by an instance of the ChemPathList class, which is also called a selection list.
Note that since the SoPath portion of a ChemPath ends with a ChemDisplay node, if there is only one ChemDisplay node in the scene graph, there need only be one ChemPath in the selection list. In this case, since the SoPath portions of the ChemPaths are identical, during the comparison process mentioned above, ChemPathList merges the set of indices in the new ChemPath with the indices of ChemPath in the selection list to produce a final ChemPath that reflects the items that are currently selected. At a maximum, there can only be as many ChemPaths in a selection list as there are ChemDisplay nodes in a scene graph, and only if something has been selected from every ChemDisplay node will the number of ChemDisplay nodes equal the number of ChemPaths.
An application has access to the selection list through ChemSelection's getList() method. While MI can automatically highlight selected items, an application is responsible for implementing functions that use the contents of the selection list, for example, an MI application might write out the selected items or copy them to the X clipboard. For an example, see “Using the Selection List.”
ChemSelection implements four interactive selection mechanisms:
clicking on items individually
a lasso
a drag rectangle
a selection sphere
Users can select individual items by clicking the left mouse button while the cursor is on an item.
A lasso is a selection mechanism that appears as a user-defined shape, according to the movement of the mouse. The user draws the lasso by pressing the left mouse button, dragging the mouse around the part of the display of interest, and then releasing the mouse button. Even though the beginning and ending points of the lasso may be far apart, MI considers the ends connected. The items within the lasso are selected according to the lasso policy. The lasso policy defines when an item is considered to be selected; there are three options:
Only its center must be inside the lasso.
The lasso must pass through the bounding box of the item.
The entire bounding box of an item must be contained in the lasso.
A drag rectangle is a lasso-type selection mechanism that appears as a rectangle when the user presses the left mouse button on anything other than an atom, drags the mouse over the part of the display of interest, and then releases the mouse button. The items within the rectangle are selected according to the lasso policy.
A selection sphere is a selection mechanism that appears as a sphere when a user presses the left mouse on an atom and drags the mouse. The radius of the sphere increases or decreases according to the mouse movement, much like the drag rectangle, except that the sphere is centered on an atom. In addition, as the sphere changes size, the value of its radius is displayed on the surface of the sphere. When the user releases the mouse button, the items within the sphere are selected according to the lasso policy.
Callback functions registered with ChemSelection can be invoked whenever a user performs a selection. The application can then access the current selection list and process the items on it. ChemDisplay can automatically highlight the selected items according to the values of the ChemDisplayParam fields. Alternatively, an application can provide customized highlighting.
ChemSelection maintains an internal scene graph in order to display the lasso and drag rectangle. To prevent the 3D scene graph from being rerendered each time the lasso or drag rectangle is updated, the lasso and the drag rectangle should be rendered in the overlay planes. It is up to the application to set the color map to be used in the overlay planes (and thus the color of the lasso and drag rectangle) and to connect ChemSelection's internal scene graph to the viewer being used.
An example of how to set the color map in the overlay planes and to connect the internal scene graph to the viewer follows. The ChemSelection->getSceneGraph() method accesses the scene graph used for the lasso and drag rectangle.
root->addChild(chemSelection); SbColor lassoColor(1, 1, 0); examinerViewer->setOverlayColorMap(1, 1, &lassoColor); examinerViewer->setSceneGraph(root); examinerViewer->setOverlaySceneGraph(chemSelection->getSceneGraph()); examinerViewer->show(); |
In this example, the color yellow (1, 1, 0) is placed in the color map and given the index value of 1.
The following sections describe how to set the ChemSelection fields that control various aspects of the interactive selection mechanisms.
The parts field in ChemSelection determines the types of items that are selected when one of the interactive selection mechanisms is used. It is possible, for example, to select only atoms and bonds or only atom labels by setting the value of parts appropriately. parts is a field of type SoSFBitmask which is a single-valued field containing a mask of bit flags. Therefore, use bit-wise “&” and “|” operators when testing and setting the value of parts. Valid parts values are defined by the ChemPart enum and described in the following table.
ChemPart Value | Description |
|---|---|
ATOMS | Selects atoms. |
BONDS | Selects bonds. |
ATOMLABELS | Selects atom labels. |
BONDLABELS | Selects bond labels. |
ALL | Selects items of all the above types. |
The following example shows how to set the value of parts so that only atoms and bonds are selected:
chemSelection->parts.setValue(ChemSelection::ATOMS | ChemSelection::BONDS); |
The atomLassoPolicy, bondLassoPolicy, atomLabelLassoPolicy, and bondLabelLassoPolicy fields determine what parts of an atom, bond, atom label, and bond label, respectively, must be inside a lasso, drag rectangle, or selection sphere to be selected.
![]() | Note: Although these fields are called lasso policies, they also apply to the selection sphere. |
The atomLassoPolicy, bondLassoPolicy, atomLabelLassoPolicy, and bondLabelLassoPolicy fields are equal to one of the values in the LassoPolicy enum shown in the following table.
LassoPolicy Value | Description |
|---|---|
CENTER | The lasso must contain the center of an item. |
THRU_BBOX | The lasso must pass through the bounding box of an item. |
ENTIRE_BBOX | The lasso must enclose all points of the bounding box of an item. |
A bounding box is box enclosing objects defined by a scene graph.
The togglePolicy field determines the manner in which selected items are added to a selection list. For example, at the completion of a selection action (releasing the mouse button), if the value of togglePolicy is ChemSelection::SINGLE, the current selection list is first cleared and the selected items are placed on the selection list.
The togglePolicy field is equal to one of the values in the TogglePolicy enum shown in the following table.
Table 6-6. TogglePolicy Values
TogglePolicy Value | Description |
|---|---|
SINGLE | The start of a new selection clears the previous selection list. If nothing is selected, the selection list remains empty. |
TOGGLE | The start of a new selection does not clear the previous selection list. If a previously selected item is again selected, it is deselected. If the item was not previously selected, it is added to the selection list. |
SHIFT | When the shift key is down, the selection policy is TOGGLE. When the shift key is up, the selection policy is SINGLE. If both the SHIFT and CTRL keys are down, new selections are merged with the current selection list such that if a newly selected item is already on the list, it remains on the list and any items not on the list are added. |
The lassoType field determines whether a lasso is used and, if so, which type of lasso to use. The lassoType field is equal to one of the values in the LassoType enum shown in the following table.
LassoType Value | Description |
|---|---|
NOLASSO | No lasso or drag rectangle is used. This value is the default. |
LASSO | The user can draw an arbitrary shape on the screen by pressing the left mouse button and dragging the cursor. Any items within the shape are selected. The first and last points of the shape are considered connected. |
DRAGGER | A rectangle is drawn on the screen as the user drags the cursor with the left mouse button down. Any items within that rectangle are selected when the mouse button is released. |
Figure 6-5 shows examples of LASSO and DRAGGER selection mechanisms.
The lassoLineWidth, lassoLinePattern, and lassoLineColorIndex fields determine the appearance of the lasso and drag rectangle. lassoLineWidth defines the width of the lines; the default is 1. lassoLinePattern defines the stipple pattern used when drawing the lines; the default is solid: 0xFFFF. lassoLineColorIndex specifies the color of the rectangle. The field is the index value in the color table used by overlay planes; the default is 1.
![]() | Note: The valid values for lassoLineWidth and lassoLinePattern are the same as the valid values for the lineWidth and linePattern fields in an SoDrawStyle node. |
The radiusSelect field is a Boolean value that enables or disables the use of the selection sphere. If the field is TRUE, when a user selects an atom with the left mouse button and continues to hold the button down while dragging, a sphere grows out from the selected atom. When the mouse button is released, all items within the sphere are selected.
The radiusSelectCutoff specifies the minimum allowed radius for the selection sphere; if the sphere is smaller than the minimum, no items are selected.
An application can enable the use of a lasso or drag rectangle as well as a selection sphere. Since the action required to invoke the selection sphere requires first selecting an atom, only one of these mechanisms is in use at any one time.
The radiusSelectSphereColor, radiusSelectSphereTransparency, radiusSelectSphereDrawStyle, and radiusSelectSphereComplexity fields specify the display parameters of a selection sphere. radiusSelectSphereColor specifies the color of the sphere in an RGB format; the default is white, 1 1 1.
radiusSelectSphereTransparency specifies the transparency of the sphere. Transparency values range between 0.0 and 1.0 where 0.0 is opaque and 1.0 is completely transparent; the default is 0.0.
radiusSelectSphereComplexity specifies how many triangles are used to draw the sphere; the more triangles, the smoother the surface, but the greater the rendering time. The default is 0.5.
The radiusSelectSphereDrawStyle field specifies the display style of the sphere. The field is equal to one of the values in the RadiusSelectSphereDrawStyle enum shown in the following table.
Table 6-8. RadiusSelectSphereDrawStyle Values
RadiusSelectSphereDrawStyle Value | Description |
|---|---|
FILLED | Draws a solid sphere. |
LINES | Draws the wireframe outline of a sphere. |
POINTS | Draws points at the sphere vertices. |
As a selection sphere is rendered, its radius is displayed on the surface of the sphere next to the atom clicked on. The radiusSelectLabelColor, radiusSelectLabelFontName, and radiusSelectLabelFontSize fields specify the display parameters for this label. radiusSelectLabelColor specifies the color of the label in an RGB format; the default is red, 1 0 0. radiusSelectLabelFontName specifies the font used in the label; the default is Utopia-Regular. radiusSelectLabelFontSize specifies the font size used in the label; the default is 10 point.
Figure 6-6 shows an example of the selection sphere mechanism.
The same methods that ChemSelection uses internally to work with the selection list when selections are made interactively are also available directly to an application. These methods allow the application to select, deselect, toggle, and merge specified ChemPaths.
void select(const ChemPath *path) void deselect(ChemPath *path) void deselect(int which) void deselectAll() void toggle(const ChemPath *path) void merge(const ChemPath *path) SbBool isSelected(const ChemPath *path) const SbBool isSelected(SoNode *node) const int getNumSelected() const const ChemPathList * getList() const ChemPath * getPath(int index) const ChemPath * operator [](int i) const |
To use many of these methods, the application must first create a ChemPath.
The following table describes these methods. Keep in mind that a ChemPath is comprised of an SoPath and a set of indices for atoms, bonds, and labels. Also recall, that if the SoPath portion of the passed ChemPath is the same as one which already exists in the selection list, ChemPathList does not generate a new entry. Instead, it updates the indices of the existing ChemPath with those of the passed ChemPath in accordance with the method invoked. Therefore use care when invoking methods that use an entry in a selection list rather than a ChemPath.
Table 6-9. Creating and Managing a Selection List
Method | Description |
|---|---|
Selects the passed ChemPath by adding it to the selection list. The current ChemSelection node must lie in the SoPath portion of the ChemPath otherwise the selection list remains unchanged. The ChemPath is copied and the SoPath portion truncated such that the current ChemSelection node is at the head of the path. This method ignores the current selection policy, that is, it always clears the selection list before adding the passed ChemPath. | |
Deselects the passed ChemPath by removing it from the selection list. | |
deselect(int which) | Removes an entry from the selection list. The argument specifies which entry in the list to be removed. |
deselectAll() | Removes all entries from the selection list. |
Toggles the selection status of the passed ChemPath. If the ChemPath is in the selection list, it is removed; if the ChemPath is not in the list, it is added. | |
Merges a ChemPath into the selection list. If the ChemPath is not in the selection list, it is selected; if the ChemPath is already in the selection list, it remains selected. | |
Returns TRUE if the passed ChemPath is selected; that is, if it is in the selection list. | |
isSelected(SoNode *node) const | Searches for the first instance of the passed node under the current ChemSelection node and returns whether that node exists in the SoPath portion of a currently selected ChemPath. |
Returns the number of entries in the selection list. This is not the number of items selected. It is essentially the number of unique SoPaths. | |
getList() const | Returns the selection list. |
getPath(int index) const operator [](int i) const | Returns the i'th entry in the selection list. |
The following code example shows the implementation of mySelect() that programmatically selects atoms with indices 10 through 19. This function is invoked through Edit > Select in the sample application, miApp. (For more information about miApp, see “Writing More Robust Molecular Inventor Applications.”).
void
mySelect()
{
// Set up a SoSearchAction to look for the chemDisplay node in
// the subgraph under chemSelection. The result is a SoPath
// which begins at chemSelection and ends at chemDisplay which is
// needed for a valid ChemPath.
SoSearchAction sa;
sa.setFind(SoSearchAction::NODE);
sa.setInterest(SoSearchAction::FIRST);
sa.setNode((SoNode *)chemDisplay);
sa.apply(chemSelection);
if (sa.getPath() == NULL) {
printf (“ChemDisplay not in path!!!\n”);
return;
}
// Create a new ChemPath
ChemPath *chemPath = new ChemPath;
chemPath->ref();
// Arbitrarily set the atom indices in the ChemPath to start at
// index 10 and include a total of 10 entries.
MFVec2i atomIndex;
atomIndex.set1Value(0, 10, 10);
if (!chemPath->setPath(sa.getPath(), &atomIndex, NULL, NULL, NULL)) {
printf (“Not a valid ChemPath!!!\n”);
chemPath->unref();
return;
}
// Deselect everything and then select chemPath
chemSelection->deselectAll();
chemSelection->select(chemPath);
chemPath->unref();
}
|
ChemSelection manages several lists of callback functions. Callback functions are functions within your application that you wish ChemSelection to invoke whenever a particular event occurs in the selection process. The types of callback-related events include the selection of a ChemPath, the deselection of the ChemPath, the start of a change to the selection list, the finish of a change to the selection list, the start of a lasso process, and the finish of a lasso process.
To add a callback function to a particular callback function list, use the appropriate add...Callback() method, such as addLassoStartCallback(). The functions on the callback list are invoked whenever a user presses down on the left mouse button to start using a lasso, drag rectangle, or selection sphere.
To remove a callback function from a particular callback function list, use the appropriate remove...Callback() function, such as removeLassoStartCallback().
Whenever callback-related events occur, ChemSelection looks at the callback function list associated with that event. If the list is not empty, every function on the list is invoked.
Of all the callback function lists, the one most commonly used is the one associated with the finish of all changes to the selection list. This list is managed with the addFinishCallback() and removeFinishCallback() methods. An example of adding a function to this list follows:
static void myFinishCB(void *, ChemSelection *cs); ... chemSelection = new ChemSelection; chemSelection->addFinishCallback(myFinishCB); |
All of the methods that manage the callback function lists have the same argument and return type:
void ...Callback(ChemSelectionClassCB *f, void *userData = NULL) |
For example,
void addChangeCallback(ChemSelectionClassCB *f, void *userData = NULL) |
The following table describes these methods.
Table 6-10. Callback Functions
Method | Description |
|---|---|
The change callbacks are invoked every time the selection list changes, whether it is the result of user interaction or a method call. If several ChemPaths are involved in the selection process, the callbacks on this list are invoked every time a ChemPath changes the selection list. | |
The selection callbacks are invoked every time a ChemPath is selected and added to the selection list, whether it is the result of user interaction or a method call. If several ChemPaths are involved in the selection process, the callbacks on this list are invoked every time a ChemPath changes the selection list. | |
The deselection callbacks are invoked every time a ChemPath is deselected and removed from the selection list, whether it is the result of user interaction or a method call. If several ChemPaths are involved in the deselection process, the callbacks on this list are invoked every time a ChemPath changes the selection list. | |
The start callbacks are invoked before the start of any changes to the selection list and thus are called just once during the selection process. These will be followed by invocations of any select and/or deselect callbacks, finally followed by any finish callbacks. A start callback can be used, for instance, to save the current selection list for later restoration, for example, undo/redo. The start callbacks are not called when the selection list is changed programmatically. | |
The finish callbacks are invoked at the end of any changes to the selection list and thus are called just once during the selection process. These are preceded by invocations of any start callbacks and invocations of any select and/or deselect callbacks. The finish callbacks are not called when the selection list is changed programmatically. | |
If using a lasso, drag rectangle, or selection sphere, the lassoStart callbacks are invoked on a left-mouse-button-down event. The lassoStart callbacks are not called when the selection list is changed programmatically. | |
If using a lasso, drag rectangle, or selection sphere, the lassoFinish callbacks are invoked on a left mouse button up event. They are invoked after the selection list has been modified by the current selection. The lassoFinish callbacks are not called when the selection list is changed programmatically. |