Chapter 6. Using the Pick Data Type

This chapter describes the cxPick data type and how to use it in the 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 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.

Understanding the Picking Function

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 2-D and 3-D modules.

You can obtain information about an image in the window of a module that displays images (2-D), or a geometry object in the window of a rendering module (3-D).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 X Windows, GL, or a toolkit on top of these. This is because none of the Explorer widgets supply pick information.

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 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 Viewing Menu and toggling the “eye” icon to turn off viewing mode. To use Pick mode in a DisplayImg window, they simply press the middle 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 (2-D) or from geometry (3-D). Or it could write pick data obtained from an image or a geometry object.

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 “Labeling Items in Scene Graphs” later in this chapter.

The Fortran example in “Reading Pick Data” also reads pick data. It lists the number of hits the user has made on an object, looks at the list, and builds a 1D lattice from the data in the list.

Modules that Write Pick Data

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

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.

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 event number for the particular action.

eventData  

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

eventModifiers 

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

position 

Gives the exact 2-D 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 (3-D 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 (3-D only)

projectionMatrix 


Specifies the 3-D projection; contains a perspective transformation, if present. Of the standard 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.

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.

Figure 6-1. Schematic Structure of the Pick Data Type


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. The fields are all bit fields in validInfo.

point 

Defines a point which gives the placement of an object. DisplayImg returns coordinates (x, y) in image space and Render in 3-D world space (x, y, z). 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 Inventor nodes SoMaterial, SoBaseColor, and SoPackedColor 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 Inventor.

localTransform 

Gives the transform matrix if a geometry object has been translated or rotated in the Render module that generated the pick event. 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.


Note: Not all fields are applicable to all objects. The bit masks for each field in validInfo are in the cxPickValid enumeration in /usr/explorer/include/cx/Pick.h


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.

Figure 6-2. Schematic Structure of the PickHit Data Type


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 Explorer and can be launched from the Module Librarian.

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.

Gathering Shape Information.

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

xpoints 

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 (3-D 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

The Data Type Declaration

The cxPick data type, though defined in the 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 /usr/explorer/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 /usr/explorer/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)


The Pick API Routines

You can use the API (Application Programming Interface) routines to manipulate the pick data type in 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


Code Examples

This section includes examples of user functions for modules that read pick data. The user function for a module that writes pick data is given in “Writing Pick Data.”

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 code is in the files /usr/explorer/src/MWGcode/Picking/C/ReadPick.c and in /usr/explorer/src/MWGcode/Picking/Fortran/ReadPick.f.

C Version:

#include<stdio.h>

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

readpick(cxPick*pick,cxLattice**lattice)
{
    int     i;        /*loop variable*/
    long    dims[1];  /*dimensions vector*/
    long    id;       /*temporary variable for pickid*/
    float   *p;       /*coordinate pointer*/

    /*don't output anything if the pick didn't hit anything*/
    if ( pick->numHits == 0 )
       return;

     /* create the output lattice */ 
    /*lattice = cxLatNew(1,dims ,1,cx_prim_float ,3,  
     cx_coord_curvilinear );

    /* set the pointer to the coordinates */
    p = (*lattice)->coord->c.cx_coord_curvilinear.values;

    /* loop over the hits */
    for ( i = 0; i < pick->numHits; i++ )
    {
    /* get the location and id */
    cxPickHitGet(pick,i,
        &id, /* pick id */
        HULL, /* valid info flags */
        p, /* coordinate */
        NULL, /* normal */
        NULL, /* color */
        NULL, /* label */
     NULL, /* local transform */
     NULL, /* global transform */
     NULL); /* object type */
   /* set the data value to the pick id */
   (*lattice)->data->d.cx_prim_float.values[i] = id;

   /* increment the coordinate pointer */
   p += 3;
   }
}

Fortran Version

   subroutine PickProc(pickPort, numHits, ndim, dims, nDataVar, 
*     primType, data, nCoordVar, coords, coordType)

   integer pickPort
   integer numHits	
   integer ndim, dims(1), nDataVar, nCoordVar, coordType, primType
   real data(1)
   real coords(3,1)

   integer cxPickHitGet

c info from cxPickHitGet
   real point(3)
   integer id, validInfo, objectType
   character*1 label(256)
   real localxform(4,4), globalxform(4,4)
   real color(4), normal(3)

   if(numHits.eq.0) return
c set up the output lattice parameters

  ndim = 1
  dims(1) = numHits
  nDataVar = 1
  primType = 3
  nCoordVar = 3
  coordType = 2
 

  do 100 i=1,numHits
      ier = cxPickHitGet(pickPort,i-1,id,validInfo,point,normal,
 *       color,label,localxform,globalxform,objectType)
      coords(1,i) = point(1)
      coords(2,i) = point(2)
      coords(3,i) = point(3)

      data(i) = id
100 continue

    return
    end
 

Writing Pick Data

Here is 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.

#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>

extern “C” long pick2()
{
    static int flag = 1;
    if ( flag )
    {
      flag = 0;
      cxGeoInit();
    }

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

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

   /* if this is a new lattice, create the lookup table */
   static cxLookup *lut = NULL;
   static cxPrimType ltype = cx_prim_byte;
   int lport = cxInputPortOpen(“Lattice”);
   if ( cxInputDataChanged(lport) )
   {
      if ( lut )
       cxLookupDestroy(lut);

      cxLattice *lattice = (cxLattice  *)cxInputDataGet(lport);
      lut = cxLookupCreate(lattice,cx_lookup_linear);
      ltype = lattice->data->primType;
   }
/* get the pick information */
   cxPick *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
           }
#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;
}