2.2.5. Picking or probing a mesh

The previous section explains how to graphically represent a mesh. This section explains how to interact with a mesh and to get data from this mesh by picking or probing.

Picking a mesh

Picking on a mesh means interacting with a graphic representation of the mesh in order to retrieve information about the mesh at the position where it is intersected by a pick ray. The pick ray can be specified in 3D but normally is computed by projecting a 2D screen location, for example the current cursor position, into 3D space.

Picking is performed by applying an SoRayPickAction SoRayPickAction SoRayPickAction to the Open Inventor scene graph. The application can explicitly create an instance of this action, apply it to the scene graph and call the getPickedPoint method. However this is not necessary when handling events with an SoEventCallback SoEventCallback SoEventCallback node, because Open Inventor automatically applies a ray pick action when you call getPickedPoint in the callback function. Picking can be triggered on mouse click events using the SoMouseButtonEvent SoMouseButtonEvent SoMouseButtonEvent class. Picking can also be triggered when the cursor moves using the SoLocation2Event SoLocation2Event SoLocation2Event class. For example picking could be done on a 3D volume mesh as the cursor is moved over a pickable MoMeshSkin MoMeshSkin MoMeshSkin .

Note: A mesh representation is pickable or not pickable depending on the current SoPickStyle SoPickStyle SoPickStyle node. When a mesh pick occurs the following information can be easily retrieved: Specific detail at the picked point on the mesh representation:

  • The picked cell: The identity of the cell with either 1, 2 or 3 integer index (depending on the mesh type)

  • The value at the picking point. This value is computed from the mapped scalarset used by the picked representation

See class MoFaceDetail MoFaceDetail MoFaceDetail /MoFaceDetailI MoFaceDetailI MoFaceDetailI /MoFaceDetailIj /MoFaceDetailIjk MoFaceDetailIjk MoFaceDetailIjk for a picked surface See class MoLineDetail MoLineDetail MoLineDetail /MoLineDetailI MoLineDetailI MoLineDetailI /MoLineDetailIj /MoLineDetailIjk MoLineDetailIjk MoLineDetailIjk for a picked line

Specific detail on the input mesh: See MoMeshDetail MoMeshDetail MoMeshDetail

  • The type of mesh picked: for instance an unstructured volume mesh.

  • The application mesh interface of the picked representation. Both mesh type and mesh interface could be useful when several meshes are visible.

  • The application scalar set used by the picked representation as color mapping.

Specific detail on the picked representation of the mesh: see MoMeshRepresentationDetail MoMeshRepresentationDetail , MoSurfaceMeshDetail MoSurfaceMeshDetail MoSurfaceMeshDetail and MoLineMeshDetail MoLineMeshDetail MoLineMeshDetail

  • The extracted mesh (geometry and topology) computed to represent the input mesh.

  • The extracted scalar dataset computed to color the extracted line or surface.

For instance, when an isosurface of a volume mesh is picked, it is possible to get:

  • The picked volume cell of the input volume mesh

  • The extracted scalar value at the picked point

  • The input volume mesh

  • The extracted surface mesh that represents the isosurface

  • The extracted scalar set used to color the isosurface (if an input scalar set has been mapped onto the isosurface)

The following sample code explains how to detect mouse motion in the viewer window and to perform mesh picking:

// Insert an SoEventCallback node in the scene graph to listen events
SoEventCallback  *eventCallback = new SoEventCallback ;
root->addChild(eventCallback);
// Define the function called each time the mouse is moved
eventCallback->addEventCallback(SoLocation2Event::getClassTypeId(), 
mouseMovedCallback, userdata);
…
// Function called each time the mouse is moved in the viewer window
void mouseMovedCallback(void *user_data, SoEventCallback *eventCB) 
{
  // Get the picked point (if any)
  // Picking is automatically performed using the event location
  const SoPickedPoint *pickedPoint = eventCB->getPickedPoint();
  if (pickedPoint) 
  {
    // Something has been picked : It could be an Open Inventor shape
    // like SoCone or a mesh representation. Get detail and check type.
    const SoDetail* detail = pickedPoint->getDetail();
    if (detail->isOfType(MoFaceDetailI::getClassTypeId()))
    {
      // A face of a mesh representation of a unstructured mesh 
      // has been picked. Get detail about the picked face.
      MoFaceDetailI* fdetail = (MoFaceDetailI*) detail;
      // Get the picked cell and the value at the picked point
      size_t cellId = fdetail->getCellIndex();
      double value = fdetail->getValue(pickedPoint->getPoint());
      
      // Get detail about the picked mesh 
      const MoMeshDetail* mdetail = fdetail->getMeshDetail();
      const MiMesh* pickedMesh = mdetail->getMesh();
      MeshType meshType = mdetail->getMeshType();
      std::string colorName = mdetail->getColorScalarSet()->getName();
      // Process the detail information retreived by picking,
      // for example...
      m_textEditor->edit(cellId,value,meshType,colorName);
      // Note: The pickedMesh can be cast to the application
      // real mesh class in order to get any application data.
    }
  }
  else 
    // nothing picked
    m_textEditor->clear();
  eventCB->setHandled();
}

Probing a mesh

Probing a mesh means querying information from that mesh at any point in 3D space. Unlike mesh picking, probing a mesh does not require any graphic interaction. Probing gives mainly the following information for any position in 3D space.

Probing checks if the position is inside or outside a mesh and if it is inside:

  • Gives the cell id containing this position.

  • Computes a value at this position (by interpolating any scalar or vector dataset using cell shape functions)

Usage restriction for mesh probing

Mesh probing should be limited to a volume mesh and when the position to probe does not depend on any mesh representation. The algorithm used to probe is independent from the algorithm used to extract and draw a mesh representation. On one hand the probing algorithm is based on the mathematical definition of the shape function of the cell, on the other hand the graphical representation of a mesh depends mainly on triangle tessellation (OpenGL triangle tessellation or MeshViz tessellation for non-linear cells).

This is why some points on a graphic representation could be considered as outside the mesh when probing. The following picture illustrates this artifact on a non linear cell having one curved edge (or face in 3D), but the same artifact exists also on linear volume cells with non planar faces.

Artifacts on cells having curved edges

Figure 2.15. Artifacts on cells having curved edges



Thus if you intend to get the input mesh cell containing a pixel of a representation of the mesh, you should normally use mesh picking (see previous section), not probing.

Note also that MeshViz uses an internal octree data structure to get maximum performance when probing a volume mesh. This octree is initialized when the first probe is used. The memory size used by this octree is related to the number of cells in the mesh. Mesh picking does not create (or need) this octree, therefore mesh probing normally uses more CPU time and memory compared to mesh picking. The mesh probing octree can be customized by setting the environment variables MESHVIZ_OCTREE_CACHE_CELLBBOX and MESHVIZ_OCTREE_MAX_CELL_PER_TILE. See MiPointProbeUnstructured MiPointProbeUnstructured MiPointProbeUnstructured in the reference manual for details.

Non interactive probing usage

Depending on the mesh type, use one of:

For instance for an IJK mesh of hexahedrons, (e.g. MiVolumeMeshHexahedronIjk MiVolumeMeshHexahedronIjk )

// Instanciate the right probe class
MiPointProbeUnstructuredIjk* probe = 
    MiPointProbeUnstructuredIjk::getNewInstance(myMiVolumeMeshHexahedronIjk) ;

// Define the position of this probe
probe->setLocation(SbVec3f(x,y,z));

// If this position is inside the mesh
if (probe->isFound())
{
  // Get the ids of the cell containing this position
  size_t i,j,k;
  probe->getCellId(i,j,k);
  // Interpolate the value at this position from the specified dataset
  double interpolatedvalue = probe->getValue(myMiScalardSetIjk);
}
Interactive probing usage

Probing can be used without any user interaction, however MeshViz provides the node MoMeshPointProbe MoMeshPointProbe MoMeshPointProbe to facilitate the implementation of interactive probing. For example, by connecting an Open Inventor dragger to an MoMeshPointProbe, it's really easy to implement interactive probing.

The MoMeshPointProbe MoMeshPointProbe MoMeshPointProbe node creates a point probe and triggers a callback each time the position field changes. The scalar and vector values are computed from the scalar sets and vector sets identified by the scalarSetIds and vectorSetIds fields. The setProbeCallback() method specifies an application-defined callback function. This callback must be derived from MoMeshPointProbe::MoProbeCallback.

The point probe can be retrieved by calling the getPointProbe*() method corresponding to the type of mesh stored in the scene graph. For instance, when creating an unstructured mesh, the MiPointProbeUnstructured MiPointProbeUnstructured MiPointProbeUnstructured extractor can be retrieved by calling getPointProbeUnstructured(). This allows you to retrieve the result of the extract performed internally by the MoMeshPointProbe MoMeshPointProbe MoMeshPointProbe node to use it. See also the section called “Probing” for other details.

The following sample code shows how to implement an interactive probe using an Open Inventor dragger.

void DemoProbing::insertRepresentations(SoSeparator* sep)
{
  // Dragger used to move the position of the probe 
  SoJackDragger* dragger = new SoJackDragger;
  dragger->translation = this->getCenter();
  sep->addChild(dragger);

  MoMeshPointProbe* probe = new MoMeshPointProbe;
  // callback called each time position changes 
  probe->setProbeCallback(*this);
  // attach the position of the probe to the position of the dragger
  probe->position.connectFrom(&dragger->translation);
  sep->addChild(probe);
}

// callback called each time the field position of the probe changes
void DemoProbing::motionCallback(size_t cellId, const MeXScalardSetI& scalars, const 
MeXVec3dSetI& vectors)
{
  // scalars or vectors may contain several values when multiple dataset are set.
  // here we use only the first one.
  double scalar = scalars.getSize() > 0 ? scalars.get(0) : 0;
  MbVec3d vector = vectors.getSize() > 0 ? vectors.get(0) : MbVec3d(0);
  m_textEditor->edit(cellId,scalar,vector);
}