6 Using the Pick Data Type

6 Using the Pick Data Type

This chapter describes the cxPick data type and how to use it in the IRIS Explorer environment. It defines the data type and describes how you can use it to write modules that read or write pick data. This is a specialized data type and requires a fairly sophisticated module writer to understand it. In general, you should be able to use the standard IRIS Explorer modules that accept and manipulate the cxPick data type for most of your needs. They include Render, QueryLat, DisplayImg, and MoleculeBuilder.

The API (Application Programming Interface) routines are listed and there are examples of user function code for writing modules that manipulate pick data.

6.1 Understanding the PickingFunction

The data type described here, cxPick, can be used in two kinds of modules: those that read cxPick data objects on their input ports, and those that write cxPick data objects through their output ports.

The purpose of the cxPick data type is to enable the user to pick, or select, a particular location in a module display window and obtain information about it. cxPick is a specialized data type, used only in 2D and 3D modules.

You can obtain information about an image in the window of a module that displays images (2D), or a geometry object in the window of a rendering module (3D).You do this by using the mouse to select items out of a picture in a module display window. For example, you may want to query part of a picture of an isosurface. You could find the coordinates of a spot on the surface or the temperature at that point. The window in which the isosurface is displayed can be in the Render module, in DisplayImg, or in your own rendering module, all of which have Pick output ports.

You can send the information you have picked in the Render or DisplayImg window to a module with a Pick input port, such as PrintPick.

You can also create an object where one did not previously exist, or position an object in a particular location in a rendering module display window. As you click on the position in the window, the information goes to the upstream module, which creates an object, for example a line or an atom, and sends it to the selected position in the Render window.

There are two points to note about the cxPick data type:

  • It is considerably easier to write modules that read cxPick data sent downstream by modules such as DisplayImg or Render than it is to write modules that generate pick data.

  • If you write a pick module which outputs pick data, you need to write your own user interface using the X Window System, OpenGL, or a toolkit on top of these. This is because none of the IRIS Explorer widgets supply pick information.

6.2 Using the Pick Data Type

You can use cxPick in any module that you want to output information about geometry objects, and in any module that you want to receive this information. In the IRIS Explorer module suite, for example, the QueryLat, PrintPick and MoleculeBuilder modules all have cxPick on their input ports and can therefore read pick data. The Render module can write pick data to its Pick output port and hence feed information back to the QueryLat and MoleculeBuilder modules.

Module users can activate Pick mode in a Render window by selecting User Pick Mode from the View menu and clicking on the arrow icon to turn off viewing mode (to go back to viewing mode click on the hand icon). To use Pick mode in a DisplayImg window, simply press the middle or right mouse button.

The cxPick data type provides a vast amount of data, as you can see from its data type structure in Figure 6-1. It takes some thought to decide exactly what you want cxPick to do for you. There are two ways in which module writers commonly use cxPick; to build modules that read pick data, and to build modules that write pick data. Further, your module could read pick data from either an image (2D) or from geometry (3D). Or it could write pick data obtained from an image or a geometry object.

6.2.1 Modules that Read Pick Data

Modules that read pick data need only inspect the components of the cxPick data structure in which the module has an interest. These modules, such as QueryLat and PrintPick, receive the data from a module that writes pick data.

For example, QueryLat reads information about the position of the mouse cursor relative to a lattice in the Render window. It takes the information, generates a text label that contains the information, and sends the label back to the Render window for the user to see. The code is given in Section 6.5.2.

6.2.2 Modules that Write Pick Data

Modules that write pick data include the Render module and DisplayImg in IRIS Explorer. These modules tend to be more difficult to write because of the effort it takes to manage the user interface.

6.2.3 The cxPick Structure

The variables in the cxPick data structure let you specify all the information you want to put into or get from the module. You may not need all the variables; for example, if you write a module that displays images, you will not need the variables that pertain to the picking of geometric objects. When you create the cxPick type, you should null out all the unfilled variables.

6.2.3.1 Terminology

To make the function of each variable easier to understand, it helps to clarify some terms. A pick is what you do when you select a spot in the display window of a module by clicking the mouse on it. A hit is when you touch on part of an object or image, instead of empty space. A pick object is the object or image you touched on. A pick event is the act of picking the object, and it occurs when you press a mouse button or key, or drag the mouse across the window.

Once you have made a hit, you can gather a great deal of information about the object you hit. This information is laid out in the subtype, cxPickHit.

This is the data type definition:

root typedef struct
{
    int           eventType               "Event Type";
    long          eventData               "Event Data";
    long          eventModifiers          "Modifier Keys";
    int           position[2]             "Position Array";
    int           windowSize[2]           "Window Size Array";
    float         origin[3]               "Ray Origin";
    float         direction[3]            "Ray Direction";
    float         projectionMatrix[4, 4]  "Projection Matrix";
    int           numHits                 "Num Object Hits";
    cxPickHit     hit[numHits]            "Hits Array";
} cxPick;

The variables are:

event type

Indicates what the user action was, i.e., whether the user dragged the mouse, pressed or released a mouse button, or pressed a key. The action is specified by the X Window System event number for the particular action – e.g. left or middle button down – CX_PICK_BTNDOWN, left or middle button up – CX_PICK_BTNUP, and key down – CX_PICK_KEY.

eventData

Indicates which mouse button or key was pressed. The X Window System definitions are used for buttons and keys.

eventModifiers

Indicates whether a modifier key, such as <Shift> or <Alt>, was pressed. Specified by the X Window event set of flags.

position

Gives the exact 2D position in the window where the action took place. It is x,y from the top left corner, in integers.

windowSize

Gives the size of the module window (in pixels) in which the pick occurred.

origin

Indicates the position of the eye (3D only). It can be defined as a ray running from eye point to the pick point, and it has both position and direction.

direction

Specifies the direction of the ray from eye to object (3D only).

projectionMatrix

Specifies the 3D projection; contains a perspective transformation, if present. Of the standard IRIS Explorer modules, only Render generates this data.

numHits

Gives the number of object that were clicked on, or ‘hit’ by the user's action.

hit

Tells you what kind of object you hit, ordered from nearest to furthest from the viewer. The information is given by cxObjectDefine, which specifies whether the hit was on a point, face, or volume.

The codes that are generated by cxPick are the same for modules running on a Windows or UNIX system, so modules will behave in the same way on both systems. Actual codes are following the convention used by the X Window System.

If the user does not hit any object in the module window, or if the window is empty, the variable numhits is zero and hits will contain no data.

If the user actually hits something, then a cxPickhit data structure will be filled in with information about the object or objects.

Figure 6-1 depicts the pick data type structure.

Schematic Structure of the Pick Data Type

Figure 6-1 Schematic Structure of the Pick Data Type


6.2.4 Getting Data on the Hit Object

When the user presses a button in Pick mode and hits an object or image, the cxPickHit data structure can provide a lot of information about that object or image.

The data type definition is:

typedef struct
{
    long            id;

    long            validInfo;
    float           point[3];
    float           normal[3];
    float           color[3];
    string          label;
    float           localTransform[4, 4];
    float           globalTransform[4, 4];

    cxPickObject    objectType;
    switch (objectType) {
    case cx_pick_point:
        long            index;
    case cx_pick_line:
        long            index[2];
        float           point[2, 3];
        float           normal[2, 3];
    case cx_pick_face:
        long            nvertices;
        long            index[nvertices];
        float           point[nvertices, 3];
        float           normal[nvertices, 3];
    case cx_pick_cone:
        long            part;
    case cx_pick_cylinder:
        long            part;
    } o;
} cxPickHit;

These are the variables:

id

Gives an ID for the module that generated the picked image or object. This field allows the module that generated the picked geometry to identify it.

For example, if a module generates geometry and then gets back pick information, it can compare the pick ID with its own ID to see if they match. If not, it can ignore the information.

validInfo

A bit vector that indicates whether the pick information in the following six fields is valid. These fields are all bit fields in validInfo. Not all fields are applicable to all objects. The bit masks for each field in validInfo are in the cxPickValid enumeration in $EXPLORERHOME/include/cx/Pick.h.

point

Defines a point which gives the placement of an object. DisplayImg returns coordinates (xy) in image space and Render in 3D world space (xyz). The coordinates are floating point values.

normal

Gives the surface normal (if available) through the API routine cxGeoNormalAdd.

color

Gives the color, if it has one. The color of an object in Render is governed by an upstream module, which may not necessarily send any color value down. In this case, Render gives the object a default color, but the hue bears no relation to the data values at all. You can use the API routines cxGeoColorAdd or cxGeoABGRAdd to set a color.

You can also use the Open Inventor nodes SoMaterial, SoBaseColor, and SoPackedColor, or the IRIS Explorer Inventor nodes cxSoBaseColor and cxSoPackedColor, if you are working with geometry.

label

Names the object by its label name in the geometry scene graph. DisplayImg does not use the label field. You can use cxGeoLabelAdd or add the label in Open Inventor.

localTransform

Gives the transform matrix if a geometry object has been translated or rotated in the module that generated the pick event (usually the Render module). Upstream modules don't know about local transforms.

You can use the API routines cxGeoRotate, cxGeoTranslate, and cxGeoScale to perform the transform.

globalTransform

Gives the transform matrix if the object has been translated or rotated anywhere in the module chain. This includes transforms from upstream modules. It is a camera transform.

objectType

Tells you what kind of object sustained the hit. For some objects, including points, lines, faces, cones, and cylinders, you can collect information about indices, normals, vertices, and parts.

Figure 6-2 shows the structure of the cxPickHit data type.

Schematic Structure of the PickHit Data Type

Figure 6-2 Schematic Structure of the PickHit Data Type


6.2.4.1 Labeling Items in Scene Graphs

The QueryLat module is an example of how to label items in a scene graph. QueryLat responds to pick mouse button events by determining the lattice value at the first pick intersection and creating a label, which is text geometry, with that value. The value passes into Render through its annotation port and is displayed at the correct location in the Render window.

Figure 6-3 shows the ‘pick’ map, which contains QueryLat. The map is distributed with IRIS Explorer and can be launched from the Module Librarian.

The Pick Map

Figure 6-3 The Pick Map


QueryLat takes as input a lattice and the pick data from the Render module. The input port of QueryLat has connections from ReadLat and the Render pick output. Its output port is wired into the Render Annotations input port. When you hold down the mouse button (in User Pick mode) on a point in the Render window, the value of the lattice at that point is displayed. If you pick on an empty part of the window, the value is null and no annotation is plotted.

6.2.4.2 Gathering Shape Information.

You can collect more information about certain types of hit objects from the Render module:

points

you can determine the index of the point that was hit

lines

you can collect the indices of end-points and the normals at the end-points (if they exist)

polygons

you can determine the number of sides, and the indices, locations, and normals of the vertices (3D only)

cones and cylinders

Render outputs a part number, taken from Inventor, which indicates whether you picked the side, base, or top of the object. For cylinders, the part numbers are: side=1; top=2; bottom=4

For cones, the part numbers are: side=1; bottom=2

6.3 The Data Type Declaration

The cxPick data type, though defined in the IRIS Explorer typing language, can be considered as a C structure. Fortran users need to set pointers to the data type structures when they use them. The type declaration resides in the header file $EXPLORERHOME/include/cx/cxPick.h.

typedef enum {
    cx_pick_none,
    cx_pick_point,
    cx_pick_line,
    cx_pick_face,
    cx_pick_sphere,
    cx_pick_cone,
    cx_pick_cylinder,
    cx_pick_nurb,
    cx_pick_splat,
    cx_pick_other
} cxPickObject;

typedef struct cxPickHit {
    long              id;
    long              validInfo;
    float            *point;
    float            *normal;
    float            *color;
    char             *label;
    float            *localTransform;
    float            *globalTransform;
    cxPickObject      objectType;
    union {
        struct {
            long              index;
        } cx_pick_point;
        struct {
            long             *index;
            float            *point;
            float            *normal;
        } cx_pick_line;
        struct {
            long              nvertices;
            long             *index;
            float            *point;
            float            *normal;
            } cx_pick_face;
        struct {
            long              part;
        } cx_pick_cone;
        struct {
            long              part;
        } cx_pick_cylinder;
    } o;
} cxPickHit;

typedef struct cxPick {
    cxDataCtlr       ctlr;
    int               eventType;
    long              eventData;
    long              eventModifiers;
    int              *position;
    int              *windowSize;
    float            *origin;
    float            *direction;
    float            *projectionMatrix;
    int               numHits;
    cxPickHit        *hit;
} cxPick;

The Fortran type enumerations reside in $EXPLORERHOME/include/cx/cxPick.inc.

INTEGER cx_pick_none
INTEGER cx_pick_point
INTEGER cx_pick_line
INTEGER cx_pick_face
INTEGER cx_pick_sphere
INTEGER cx_pick_cone
INTEGER cx_pick_cylinder
INTEGER cx_pick_nurb
INTEGER cx_pick_splat
INTEGER cx_pick_other
PARAMETER (cx_pick_none                    = 0)
PARAMETER (cx_pick_point                   = 1)
PARAMETER (cx_pick_line                    = 2)
PARAMETER (cx_pick_face                    = 3)
PARAMETER (cx_pick_sphere                  = 4)
PARAMETER (cx_pick_cone                    = 5)
PARAMETER (cx_pick_cylinder                = 6)
PARAMETER (cx_pick_nurb                    = 7)
PARAMETER (cx_pick_splat                   = 8)
PARAMETER (cx_pick_other                   = 9)

6.4 The Pick API Routines

You can use the API (Application Programming Interface) routines to manipulate the pick data type in IRIS Explorer. They are described in detail in the IRIS Explorer Reference Pages. They let you:

  • extract part of the data collected by the data structure

  • extract details about a specific object that has been hit

  • create a new instance of the cxPick data structure

Table 6-1 lists the subroutines and briefly describes the purpose of each one.

Table 6-1 Pick Subroutines

Subroutine Purpose
cxPickGet Gets information from a cxPick data structure
cx PickHitGet Gets information from a cxPickHit data structure
cxPickHitConeGet Gets detailed information about a hit on a cone
cxPickHitCylinderGet Gets detailed information about a hit on a cylinder
cxPickHitFaceGet Gets detailed information about a hit on a polygon
cxPickHitLineGet Gets detailed information about a hit on a line
cxPickHitPointGet Gets detailed information about a hit on a point
cxPickNew Creates a new cxPick data structure

6.5 Code Examples

This section includes examples of user functions for modules that read and write pick data.

6.5.1 Reading Pick Data

The following function lists the number of hits, looks at the list, and builds a 1D lattice from the data in the list. The C code and resources file may be found in directory $EXPLORERHOME/src/MWGcode/Picking/C. There is currently no equivalent Fortran module.

#include <cx/DataAccess.h>
#include <cx/cxLattice.h>
#include <cx/cxPick.h>
#include <cx/cxPick.api.h>

void readpick(cxPick *pick,cxLattice **lattice)
{
  cxErrorCode ec;
  float *points, p1[3];
  int i, j;
  long *data, dims[1], index;

  /* Do not generate a lattice if the pick did not hit anything */
  dims[0] = cxPickNumObjectHitsGet(pick, &ec);
  if (dims[0] == 0)
    return;

  /* Create a 1D curvilinear output lattice
   * of type long with 3 coordinates and dims[0] nodes
   */
  *lattice = cxLatNew(1, dims, 1, cx_prim_long, 3,
              cx_coord_curvilinear);

  /* Get the pointer to the coordinate and data values */
  cxLatPtrGet(*lattice,NULL,(void **)&data, NULL, (void **)&points);

  /* Loop over the hits */
  for (i = 0; i < dims[0]; i++)
    {
      ec = cxPickHitGet(pick, i, &index, NULL, p1, NULL, NULL, NULL,
            NULL, NULL, NULL);

      /* Get the lattice coordinates */
      for(j = 0; j < 3; j++)
    points[i*3+j] = p1[j];

      /* Set the lattice data value to be the pick identifier */
      data[i] = cxPickHitIdGet(pick, i, &ec);
    }
}

6.5.2 Writing Pick Data

Here is a user function based on the user function for the QueryLat module. Seeing how the cxPick data type is used here may help you in writing your own pick module. The C code and resources file may be found in directory $EXPLORERHOME/src/MWGcode/Picking/C. There is currently no equivalent Fortran module.

#include <stdio.h>

#include <cx/PortAccess.h>
#include <cx/DataTypes.h>
#include <cx/DataAccess.h>
#include <cx/UserFuncs.h>
#include <cx/Geometry.h>
#include <cx/Lookup.h>

#include <cx/Pick.h>

long pick()
{
  cxGeometry *geo;
  cxLattice *lattice;
  cxPick *p;
  int lport;

  static cxLookup *lut = NULL;
  static cxPrimType ltype = cx_prim_byte;
  static int flag = 1;

  if ( flag )
    {
      flag = 0;
      cxGeoInit();
    }

  /* allocate new geometry object */
  geo = cxGeoNew();
  cxGeoBufferSelect(geo);

  /* delete everything */
  cxGeoRoot();
  cxGeoDelete();

  /* if this is a new lattice, create the lookup table */
  lport = cxInputPortOpen("Lattice");
  if ( cxInputDataChanged(lport) )
    {
      if (lut)
        cxLookupDestroy(lut);
      lattice = (cxLattice *) cxInputDataGet(lport);
      lut = cxLookupCreate(lattice, cx_lookup_linear);
      ltype = lattice->data->primType;
    }

  /* get the pick information */
  p = (cxPick *) cxInputDataGet(cxInputPortOpen("Pick"));

  if (p->eventType == CX_PICK_BTNDOWN ||
      p->eventType == CX_PICK_MOTION)
    {
      if ( p->numHits > 0 )
    {
      /* do a lookup */
      switch (ltype)
        {
#define CASE(ctype,type,format) \
        case ctype: { \
              type value; \
              if ( cxLookupInterp(lut,p->hit[0].point,&value) ) \
              cxGeoTextDefine("domain error", \
                      NULL); \
              else { \
                 char str[128]; \
                 sprintf(str,format,value); \
                 cxGeoTextDefine(str, \
                         NULL); \
                   } \
            } \
          break;
        CASE(cx_prim_byte,char,"%d")
        CASE(cx_prim_short,short,"%d")
        CASE(cx_prim_long,long,"%d")
        CASE(cx_prim_float,float,"%f")
        CASE(cx_prim_double,double,"%f")
#undef CASE
        }
#ifdef ASDF
      static float point[3] = { 0.0, 0.0, 0.0};
      static float radius = 1.0;
      cxGeoSpheresDefine(1,(float *)point,&radius);
#endif
      cxGeoTranslate(p->hit[0].point[0],
             p->hit[0].point[1],
             p->hit[0].point[2]);
    }
    }

  /* close the buffer and output */
  cxGeoBufferClose(geo);
  cxOutputDataSet(cxOutputPortOpen("Annotation"),(void *)geo);

  return 0;
}