Chapter 7 : Interactive Drawables

Interaction with 3d content can be achieved using two methods:

  • Retrieving the 2d projection of the complete object and analyzing what the mouse should do with it. This method is suitable for global scene analysis and allows to deal with a rectangle selection. For example, selecting a rectangle region of a 3d scatter plot requires to know the complete projection in advance, and to decide which dots stand in or out of the mouse selection rectangle as soon as this rectangle dimension changes. We call objects supporting this method Selectable.

  • Rendering a very small portion of the scene around the mouse pointer, and analysing which objects stand in that tiny window. This method is efficient for simple mouse crossing. We call objects supporting this method Pickable.

Selectable objects

Selectable objects let you easily get 2d projections of any geometry. You can then work the way you want with that projection. You may for example project a complete scatter plot, or only anchors of a shape model such as surfaces.

selectableScatter SelectableScatterDemo

Such objects must implement:

public void project(GL gl, GLU glu, Camera cam)

The implementation will mainly consist in calling and caching the 2d projection. Let’s see an example with SelectableScatter:

public void project(GLs gl, GLU glu, Camera cam) {
  projection = cam.modelToScreen(gl, glu, getData());
}

The scatter must have an associated mouse controller such as ScatterMouseSelector in charge of:

  • calling a projection of all the instances of Selectable objects appearing in the scene graph
  • retrieving the projected scatter, and verify how it matches the current selection rectangle
  • changing highlighted status of each point to have them rendered differently.
  • drawing the current selection rectangle This is simply implemented as follow:
protected void processSelection(Scene scene, View view, int width, int height) {    
  view.project();
}

Coord3d[] projection = scatter.getProjection();
for (int i = 0; i < projection.length; i++)
if (matchRectangleSelection(in, out, projection[i], width, height)) scatter.setHighlighted(i, true);

protected void drawSelection(Graphics2D g2d, int width, int height) { ...
  drawRectangle(g2d, in, out); // a simple rectangle
}

In the same fashion, a ScatterSphere has an associated SphereMouseSelector, that provides a selection renderer able to draw the convex hull of the selected sphere.

Keep in mind you remain responsible of calling project() once required. Having an ever up to date projection available for example requires customizing the view to perform such projection as soon as the viewpoint changes or the canvas dimensions change.

Projections must be used with care and optimally scheduled when working with large data. SelectableView is an extension of View able to update all projections at each any call to render(…).

When using it, take care to the chart rendering model that may be conflicting (continuously/on demand, see section Chart Quality).

Picked objects

Picking requires to register every pickable polygon into a PickingSupport instance. Indeed, once the mouse is clicked, PickingSupport retrieves a collection of polygon IDs that can be found below the mouse pointer. PickingSupport defines a pixel area under which content is detected (default is a 10 x 10 area). To illustrate how to enable picking, we will discuss elements you may find in the demo PickableGraphDemo.

pickableGraph

First, we deal with a set of Pickable objects: our graph is made of PickablePoints. PickablePoint is simply an extension of Point that implements Pickable, meaning that it has a unique ID that OpenGL can use to indicate picked points.

Second, we use a dedicated MousePickingController and override method mousePressed() to apply picking with the following lines of code (simplified):

public void mousePressed(MouseEvent e) {
  pick(e);
}

public void pick(MouseEvent e) {
  picking.pickObjects(gl, glu, view, graph, new IntegerCoord2d(e.getX(),yflipped));
}

To get notified by a successful picking, we then register a listener:

mouse.getPickingSupport().addObjectPickedListener(new IObjectPickedListener() {
  public void objectPicked(List<? extends Object> vertices, PickingSupport picking) {
    for(Object vertex: vertices)
      graph.setVertexHighlighted((String)vertex, true);
    chart.render();
  }
});

Last we have to register our PickablePoints to the PickingSupport instance:

public void setGraphModel(IGraph<V,E> graph, PickingSupport picking){
  for(V v: graph.getVertices()){
    PickablePoint p = newPoint(v);
    picking.registerDrawableObject(p, v);
  }
}