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:
- Keypress j / Keypress t: toggle between joystick (position
sensitive) and trackball (motion sensitive) styles. In joystick style,
motion occurs continuously as long as a mouse button is pressed. In
trackball style, motion occurs when the mouse button is pressed and the
mouse pointer moves. (Not available for
vtkInteractorStyleTrackBallCamera !)
- Keypress c / Keypress o: toggle between camera and object (actor)
modes. In camera mode, mouse events affect the camera position and focal
point. In object mode, mouse events affect the actor that is under the
mouse pointer.(Not available for vtkInteractorStyleTrackBallCamera !)
- Button 1: rotate the camera around its focal point (if camera
mode) or rotate the actor around its origin (if actor mode). The
rotation is in the direction defined from the center of the renderer's
viewport towards the mouse position. In joystick mode, the magnitude of
the rotation is determined by the distance the mouse is from the center
of the render window.
- Button 2: pan the camera (if camera mode) or translate the actor
(if object mode). In joystick mode, the direction of pan or translation
is from the center of the viewport towards the mouse position. In
trackball mode, the direction of motion is the direction the mouse
moves. (Note: with 2-button mice, pan is defined as <Shift>-Button
1.)
- Button 3: zoom the camera (if camera mode) or scale the actor (if
object mode). Zoom in/increase scale if the mouse position is in the top
half of the viewport; zoom out/decrease scale if the mouse position is
in the bottom half. In joystick mode, the amount of zoom is controlled
by the distance of the mouse pointer from the horizontal centerline of
the window.
- Keypress 3: toggle the render window into and out of stereo mode.
By default, red-blue stereo pairs are created. Some systems support
Crystal Eyes LCD stereo glasses; you have to invoke
SetStereoTypeToCrystalEyes() on the rendering window.
- Keypress e, q: exit the application.
- Keypress f: fly to the picked point
- Keypress p: perform a pick operation. The render window
interactor has an internal instance of vtkPropPicker that it uses to
pick. When the prop (or actor) is picked the interactor draws a bounding
frame around it. The picked prop can be accessed from code but this is
not easy and requires some type-casting:
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.
- Keypress r: reset the camera view along the current view
direction. Centers the actors and moves the camera so that all actors
are visible.
- Keypress s: modify the representation of all actors so that they
are surfaces.
- Keypress u, i: invoke the user-defined function.
- Keypress w: modify the representation of all actors so that they
are wireframe.
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.
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>