Rendering and User Interaction (Windows, WindowInteractors, Pickers and 3DWidgets)

The next two nodes (vtkRenderWindow and vtkRenderWindowInteractor) in the pipeline scheme are film on wich the show is recorded. Window is used to draw the scene and interactor controls user input and notifies the window to do redrawing when it is needed.
Here is some pecularities. The vtkRenderWindow (superclass too, it is likely to turn out being a vtkXOpenGLRenderWindow) is not persistent by itself. The picture you would draw on it is easily wiped out by overlapping windows (unless backing store is turned off on X-server). The purpose of vtkRenderWindowInteractor is to issue X-messages to do redrawing when redrawing is needed. The bad news is that all vtk interactors available to R are not truly interactive. vtkXRenderWindowInteractor would do the job during the call to it Start method (interrupted after the user hit "q" key in the render window). But R console stays blocked while you interact with VTK. I have hacked a drop-in replacement for this interactor (vtkXRenderWindowRInteractor). The windows and theirs interactors are created with vtk.figure() function and destroyed with vtk.close(...). When the window is created, a global variable (.vtk) is created pointing to this window. vtk.figure accepts one argument - the name (Window manager's one) of already opened window. The effect of this is just resetting the value of .vtk variable. Window's interactor is stored in its environment as .iren variable. Argument of vtk.close is the name of the window you want to close. Although you can use functions vtkRenderWindow() and/or vtkXOpenGLRenderWindow() directly without vtk.figure(...) wrapper function this is not recommended.

To complete the example we need the last link to connect pipeline:
.vtk$AddRenderer(ren)
and get a picture like this:
 


We can play a little with the mapper - make it display point data (mapper$SetScalarModeToUsePointData())



or to turn data visibility off (mapper$ScalarVisibilityOff())



Objects drawn in the window can be interactively moved and/or rotated. The way mouse movements and key-codes are interpretyed depends on the interactor style. By default .vtk$.iren interactor is provided with vtkInteractorStyleTrackBallCamera instance but it can be replaced by more functional vtkInteractorStyleSwitch or vtkInteractorStyleFlight, which is the style 3DShooters addicts more accustomed to. Here is complete list of key and mouse events vtkInteractorStyleSwitch supports:

      picker <- vtkPropPicker()$SafeDownCast(.vtk$.iren$GetPicker())
      actor <- vtkActor()$SafeDownCast(picker$GetPath()$GetFirstNode()$GetProp())
   
    If the class, whose SafeDownCast method is called, is not compatible with the class of its argument, then the obj field of return value is  C NULL value wrapped into R class. So care should be taken to check the return value. If the user wants to select not the whole actor but its individual cells or points, the interactor's default picker should be replaced with vtkCell/PointPicker (see inheritance graph below). Of course typecasting in this case has to be done differently because vtkCell/PointPicker are not compatible with vtkPropPicker.
Pickers
Keys "u" and "i" invokes events, the handler for which should be installed by the user. In general there are two types of VTK handlers. The older style is installed by class-specific routines. There is a RVTK demo AmoebaMinimizer with an example of such a handler used for minimization of user-provided function. vtkAmoebaMinimizer is an VTK class which do optimization with a sort of Nelder-Mead algorythm. The newer style handlers are so called observers and they can be installed for any VTK class for a predefined set of events (although not all VTk classes will in fact invoke these events). The handlers for observers are installed/removed by AddObserver/RemoveObserver methods inherited from vtkObject class. Being a multicast method AddObserver has two forms:
AddObserver(eventtype,R_function,priority,...)
AddObserver_v1(eventname,R_function,priority,...)
AddObserver methods add (not replace) new handler to the event. Each handler is identified by its tag - the return value of AddObserver functions and later handler can be removed by RemoveObserver function whose single parameter is observer's tag. RemoveObservers method removes all observers assigned to event (specified by its name or by its index).
For example we can install handler for UserEvent (fired by hitting "u" key) which cycle through different data representation (i.e. CellData, PointData or no at all) for picked actor. To do this we first create handler:

CycleData<-function(...){
    CycleDataCounter<<-CycleDataCounter+1
    picker <- vtkPropPicker()$SafeDownCast(.vtk$.iren$GetPicker())
    actor <- vtkActor()$SafeDownCast(picker$GetPath()$GetFirstNode()$GetProp())
    if (!is.nil(actor$obj)){
        vtkPolyDataMapper()$SafeDownCast(actor$GetMapper())->polymapper
        if(!is.nil(polymapper$obj)){
           switch(CycleDataCounter%3){
              {polymapper$SetScalarModeToUsePointData()},
              {polymapper$SetScalarModeToUseCellData()},
              {polymapper$ScalarVisibilityOff()}
            }      
        }
    }
}

and then install it :

CycleDataCounter<-0
.vtk$.iren$AddObserver_v1("UserEvent",CycleData)

Tu use it we first select the actor by selecting it with the pointer and hitting "p" key and later change its representation by hitting "u" key.

Interactor events (fired by "i" key) work differently. These type of events are not processed directly by the interpreter but dispatched to the so-called 3D widgets and later processed by theirs "InteractorEvents" suite. 3DWidgets enables VTK user select subspaces or regions in 3D space. There are quite a lot of them in VTK library (see picture below) and each of them are designed to perform its own type of selection.
3DWidgets
The simplest example is to use 3DPointWidget for selection of x,y,z coordinates in the window and displaying these coordinates  as a text in the VTK window. For this we have to use 3 handlers:

enable.interaction <- function(...){
  print("enable")
 
  z<-pw$GetPosition()
  xyz <<- vtkTextActor()
  xyz$SetPosition(c(30,30))
  xyz$SetInput(paste(z,collapse=","))
  invisible(ren$AddActor(xyz))
}

disable.interaction <- function(...){
  print("disable")
  ren$RemoveActor(xyz)
  invisible(xyz<<-NULL)
}


.interaction <- function(...){
  z<-pw$GetPosition()
  invisible(xyz$SetInput(paste(z,collapse=",")))
}


enable and disable.interaction are toggled after successive hits of "i" key and .interaction handler is called when user drags handles of 3DPointWidget. enable.interaction creates another example of "mapper-less" actor vtkTextActor and sets its text; disable.interaction deletes already created TextActor. The next, we have to create a 3DWidget and feed it into interactor:

vtkPointWidget()->pw
pw$AllOn()
pw$SetInput(model)
pw$PlaceWidget_v1()
pw$SetInteractor(.vtk$.iren)

SetInput method just sets the ranges where 3DPointWidget is allowed to change, the ranges are the same as the boundaries of input dataset. Another option is to do this directly with pw$SPlaceWidget(c(...)) with 6 min/max values. The last thing to do before the 3DWidget would work is to set its handlers:

pw$AddObserver_v1("EnableEvent",enable.interaction)
#pw$AddObserver_v1("StartInteractionEvent",start.interaction)
pw$AddObserver_v1("InteractionEvent",.interaction)
#pw$AddObserver_v1("EndInteractionEvent",end.interaction)
pw$AddObserver_v1("DisableEvent",disable.interaction)

Commented lines with "Start/EndInteractionEvents" are the handlers which is executed when the user grabs/releases widget's handles. In our simple example they are not needed.

 <Prev    Next>