1package require vtk
2package require vtkinteraction
3
4# This example demonstrates how to use the vtkImagePlaneWidget
5# and a vtkSplineWidger to do profile probing of a 3D image
6# dataset.  The use of vtkAnnotatedCubeActor, vtkAxesActor and
7# vtkOrientationMarkerWidget is also demostrated.
8#
9# GUI controls are provided as follows:
10# a) x,y,z buttons set the widgets to orthonormal
11#    positioning, set the horizontal slider to move the
12#    widgets along their common plane normal, and set the
13#    camera to face the widgets
14# b) right clicking on x,y,z buttons pops up a menu to set
15#    the widget's reslice interpolation mode
16# c) when in axes aligned, orthogonal orientation, the slider
17#    will move the widget by slice index within the appropriate range
18#
19
20# Start by loading some data.
21#
22vtkVolume16Reader v16
23  v16 SetDataDimensions 64 64
24  v16 SetDataByteOrderToLittleEndian
25  v16 SetImageRange 1 93
26  v16 SetDataSpacing 3.2 3.2 1.5
27  v16 SetFilePrefix "$VTK_DATA_ROOT/Data/headsq/quarter"
28  v16 Update
29
30scan [[v16 GetExecutive] GetWholeExtent [v16 GetOutputInformation 0]] "%d %d %d %d %d %d" \
31        xMin xMax yMin yMax zMin zMax
32
33set spacing [[v16 GetOutput] GetSpacing]
34set sx [lindex $spacing 0]
35set sy [lindex $spacing 1]
36set sz [lindex $spacing 2]
37
38set origin [[v16 GetOutput] GetOrigin]
39set ox [lindex $origin 0]
40set oy [lindex $origin 1]
41set oz [lindex $origin 2]
42
43# Create an outline of the 3D image data bounds.
44#
45vtkOutlineFilter outline
46  outline SetInputConnection [ v16 GetOutputPort ]
47
48vtkPolyDataMapper outlineMapper
49  outlineMapper SetInputConnection [ outline GetOutputPort ]
50
51vtkActor outlineActor
52  outlineActor SetMapper outlineMapper
53
54# Set up two renderers in one render window: one for the
55# vtkImagePlaneWidget, vtkSplineWidget, and outline, and
56# one for the profile plot.
57#
58vtkRenderer ren1
59   ren1 SetBackground 0.4 0.4 0.5
60vtkRenderer ren2
61   ren2 SetBackground 0.8 0.8 0.8
62
63vtkRenderWindow renWin
64  renWin AddRenderer ren1
65  renWin AddRenderer ren2
66  renWin SetSize 800 400
67
68ren1 SetViewport 0 0 0.5 1
69ren2 SetViewport 0.5 0 1 1
70
71# Create a vtkImagePlaneWidget to slice through the data.
72#
73vtkImagePlaneWidget ipw
74  ipw DisplayTextOn
75  ipw TextureInterpolateOff
76  ipw UserControlledLookupTableOff
77  ipw SetInputConnection [ v16 GetOutputPort ]
78  ipw SetResliceInterpolateToNearestNeighbour
79  ipw KeyPressActivationOff
80  [ ipw GetPlaneProperty ] SetColor 1 0 0
81  set xmode [ ipw GetResliceInterpolate ]
82  set ymode [ ipw GetResliceInterpolate ]
83  set zmode [ ipw GetResliceInterpolate ]
84  ipw SetPlaneOrientationToXAxes
85  ipw SetSliceIndex 32
86  ipw AddObserver InteractionEvent UpdateIPW
87
88# Create a vtkSplineWidget to interactively probe the data.
89#
90vtkSplineWidget spline
91  spline SetInputConnection [ v16 GetOutputPort ]
92  spline PlaceWidget
93  spline SetPriority 1.0
94  spline KeyPressActivationOff
95  spline ProjectToPlaneOn
96  spline SetProjectionNormal 0
97  spline SetProjectionPosition 102.4
98  spline SetNumberOfHandles 5
99  spline SetResolution 500
100  spline AddObserver InteractionEvent UpdateSW
101
102# A vtkPolyData will be continuously updated from the spline
103# during interaction.
104#
105vtkPolyData poly
106  spline GetPolyData poly
107
108# The filter to probe the image data.
109#
110vtkProbeFilter probe
111  probe SetInputData poly
112  probe SetSourceConnection [ v16 GetOutputPort ]
113
114# The plot of the profile data.
115#
116vtkXYPlotActor profile
117  profile AddDataSetInputConnection [ probe GetOutputPort ]
118  [ profile GetPositionCoordinate ] SetValue 0.05 0.05 0
119  [ profile GetPosition2Coordinate ] SetValue 0.95 0.95 0
120  profile SetXValuesToNormalizedArcLength
121  profile SetNumberOfXLabels 6
122  profile SetTitle "Profile Data "
123  profile SetXTitle "s"
124  profile SetYTitle "I(s)"
125  profile SetXRange 0 1
126  set range [[v16 GetOutput] GetScalarRange]
127  profile SetYRange [lindex $range 0] [lindex $range 1]
128  [ profile GetProperty ] SetColor 0 0 0
129  [ profile GetProperty ] SetLineWidth  2
130  profile SetLabelFormat "%g"
131  [ profile GetTitleTextProperty ] SetColor 0.02 0.06 0.62
132  [ profile GetTitleTextProperty ] SetFontFamilyToArial
133  profile SetAxisTitleTextProperty [ profile GetTitleTextProperty ]
134  profile SetAxisLabelTextProperty [ profile GetTitleTextProperty ]
135  profile SetTitleTextProperty [ profile GetTitleTextProperty ]
136
137# Create a composite orientation marker using
138# vtkAnnotatedCubeActor and vtkAxesActor.
139#
140vtkAnnotatedCubeActor cube
141  cube SetXPlusFaceText  "R"
142  cube SetXMinusFaceText "L"
143  cube SetYPlusFaceText  "A"
144  cube SetYMinusFaceText "P"
145  cube SetZPlusFaceText  "I"
146  cube SetZMinusFaceText "S"
147  cube SetXFaceTextRotation 180
148  cube SetYFaceTextRotation 180
149  cube SetZFaceTextRotation -90
150  cube SetFaceTextScale 0.65
151  set property [ cube GetCubeProperty ]
152  $property SetColor 0.5 1 1
153  set property [ cube GetTextEdgesProperty ]
154  $property SetLineWidth 1
155  $property SetDiffuse 0
156  $property SetAmbient 1
157  $property SetColor 0.18 0.28 0.23
158  set property [ cube GetXPlusFaceProperty ]
159  $property SetColor 0 0 1
160  $property SetInterpolationToFlat
161  set property [ cube GetXMinusFaceProperty ]
162  $property SetColor 0 0 1
163  $property SetInterpolationToFlat
164  set property [ cube GetYPlusFaceProperty ]
165  $property SetColor 0 1 0
166  $property SetInterpolationToFlat
167  set property [ cube GetYMinusFaceProperty ]
168  $property SetColor 0 1 0
169  $property SetInterpolationToFlat
170  set property [ cube GetZPlusFaceProperty ]
171  $property SetColor 1 0 0
172  $property SetInterpolationToFlat
173  set property [ cube GetZMinusFaceProperty ]
174  $property SetColor 1 0 0
175  $property SetInterpolationToFlat
176
177vtkAxesActor axes
178  axes SetShaftTypeToCylinder
179  axes SetXAxisLabelText "x"
180  axes SetYAxisLabelText "y"
181  axes SetZAxisLabelText "z"
182  axes SetTotalLength 1.5 1.5 1.5
183  vtkTextProperty tprop
184  tprop ItalicOn
185  tprop ShadowOn
186  tprop SetFontFamilyToTimes
187  [ axes GetXAxisCaptionActor2D ] SetCaptionTextProperty tprop
188  vtkTextProperty tprop2
189  tprop2 ShallowCopy tprop
190  [ axes GetYAxisCaptionActor2D ] SetCaptionTextProperty tprop2
191  vtkTextProperty tprop3
192  tprop3 ShallowCopy tprop
193  [ axes GetZAxisCaptionActor2D ] SetCaptionTextProperty tprop3
194
195# Combine the two actors into one with vtkPropAssembly ...
196#
197  vtkPropAssembly assembly
198  assembly AddPart axes
199  assembly AddPart cube
200
201# Add the composite marker to the widget.  The widget
202# should be kept in non-interactive mode and the aspect
203# ratio of the render window taken into account explicitly,
204# since the widget currently does not take this into
205# account in a multi-renderer environment.
206#
207  vtkOrientationMarkerWidget marker
208  marker SetOutlineColor 0.93 0.57 0.13
209  marker SetOrientationMarker assembly
210  marker SetViewport 0.0 0.0 0.15 0.3
211# Add the actors.
212#
213  ren1 AddActor outlineActor
214  ren2 AddActor2D profile
215
216# Prevent the tk window from showing up then start the event loop.
217  wm withdraw .
218
219# Build a tcl GUI.
220#
221
222toplevel .top
223wm title .top "Probe With vtkSplineWidget Example"
224wm protocol .top WM_DELETE_WINDOW ::vtk::cb_exit
225
226set popm [menu .top.mm -tearoff 0]
227set interpmode 0
228$popm add radiobutton -label "nearest" -variable interpmode -value 0  \
229           -command SetInterpolation
230$popm add radiobutton -label "linear" -variable interpmode -value 1  \
231           -command SetInterpolation
232$popm add radiobutton -label "cubic" -variable interpmode -value 2  \
233           -command SetInterpolation
234
235set display_frame [frame .top.f1]
236
237set ctrl_buttons [frame .top.btns]
238
239pack $display_frame $ctrl_buttons \
240        -side top -anchor n \
241        -fill both -expand f
242
243set quit_button [button $ctrl_buttons.btn1  \
244        -text "Quit" \
245        -command  ::vtk::cb_exit]
246
247set x_button [button $ctrl_buttons.btn2  \
248        -text "x" \
249        -command AlignXaxis]
250
251set y_button [button $ctrl_buttons.btn3  \
252        -text "y" \
253        -command AlignYaxis]
254
255set z_button [button $ctrl_buttons.btn4  \
256        -text "z" \
257        -command AlignZaxis]
258
259set last_btn -1
260bind $x_button <Button-3> "set last_btn 0; ConfigMenu; $popm post %X %Y"
261bind $y_button <Button-3> "set last_btn 1; ConfigMenu; $popm post %X %Y"
262bind $z_button <Button-3> "set last_btn 2; ConfigMenu; $popm post %X %Y"
263
264# Share the popup menu among buttons, keeping
265# track of associated plane's interpolation mode
266#
267proc ConfigMenu { } {
268  global last_btn popm interpmode xmode ymode zmode
269  if { $last_btn == 0 } {
270    set interpmode $xmode
271   } elseif { $last_btn == 1 } {
272    set interpmode $ymode
273  } else {
274    set interpmode $zmode
275  }
276  $popm entryconfigure $last_btn -variable interpmode
277}
278
279pack $quit_button $x_button $y_button $z_button \
280        -side left \
281        -expand t -fill both
282
283# Create the render widget
284#
285set renderer_frame [frame $display_frame.rFm]
286
287pack $renderer_frame \
288        -padx 3 -pady 3 \
289        -side left -anchor n \
290        -fill both -expand f
291
292set render_widget [vtkTkRenderWidget $renderer_frame.r \
293        -width 800 \
294        -height 400 \
295        -rw renWin]
296
297pack $render_widget $display_frame  \
298        -side top -anchor n \
299        -fill both -expand f
300
301# Add a slice scale to browse the current slice stack
302#
303
304set slice_number [ipw GetSliceIndex]
305
306scale .top.slice \
307        -from $xMin \
308        -to $xMax \
309        -orient horizontal \
310        -command SetSlice \
311        -variable slice_number \
312        -label "Slice"
313
314pack .top.slice \
315        -fill x -expand f
316
317proc SetSlice {slice} {
318  ipw SetSliceIndex $slice
319  UpdateIPW
320  renWin Render
321}
322
323::vtk::bind_tk_render_widget $render_widget
324# Set the interactor for the widgets
325#
326set iact [[$render_widget GetRenderWindow] GetInteractor]
327vtkInteractorStyleTrackballCamera style
328$iact SetInteractorStyle style
329
330ipw SetInteractor $iact
331ipw On
332
333spline SetInteractor $iact
334spline SetPlaneSource [ ipw GetPolyDataAlgorithm ]
335spline SetProjectionNormal 3
336spline On
337
338# The marker must be linked to the interactor after the $render_widget is mapped
339
340bind $render_widget <Map> [string map "%IA %iact" {
341  marker SetInteractor $iact
342  marker SetEnabled 1
343  marker InteractiveOff
344}]
345
346# Create an initial interesting view
347#
348set cam1 [ren1 GetActiveCamera]
349$cam1 Elevation 110
350$cam1 SetViewUp 0 0 -1
351$cam1 Azimuth 45
352ren1 ResetCamera
353
354# Render it
355#
356$render_widget Render
357
358# Supporting procedures
359#
360
361# Align the camera so that it faces the desired widget
362#
363proc AlignCamera { } {
364  global ox oy oz sx sy sz xMax xMin yMax yMin zMax zMin slice_number
365  set cx [expr $ox + (0.5*($xMax - $xMin))*$sx]
366  set cy [expr $oy + (0.5*($yMax - $yMin))*$sy]
367  set cz [expr $oy + (0.5*($zMax - $zMin))*$sz]
368  set vx 0
369  set vy 0
370  set vz 0
371  set nx 0
372  set ny 0
373  set nz 0
374  set iaxis [ipw GetPlaneOrientation]
375  if { $iaxis == 0 } {
376    set vz -1
377    set nx [expr $ox + $xMax*$sx]
378    set cx [expr $ox + $slice_number*$sx]
379  }  elseif  { $iaxis == 1 } {
380    set vz -1
381    set ny [expr $oy + $yMax*$sy]
382    set cy [expr $oy + $slice_number*$sy]
383  } else {
384    set vy 1
385    set nz [expr $oz + $zMax*$sz]
386    set cz [expr $oz + $slice_number*$sz]
387  }
388  set px [expr $cx + $nx*2]
389  set py [expr $cy + $ny*2]
390  set pz [expr $cz + $nz*3]
391
392  set camera [ ren1 GetActiveCamera ]
393  $camera SetViewUp $vx $vy $vz
394  $camera SetFocalPoint $cx $cy $cz
395  $camera SetPosition $px $py $pz
396  $camera OrthogonalizeViewUp
397  ren1 ResetCamera
398  renWin Render
399}
400
401# Align the widget back into orthonormal position,
402# set the slider to reflect the widget's position,
403# call AlignCamera to set the camera facing the widget
404#
405proc AlignXaxis { } {
406  global xMax xMin slice_number interpmode xmode
407  set ori [ ipw GetPlaneOrientation ]
408  if { $ori != 0 } {
409    ipw SetPlaneOrientationToXAxes
410    set slice_number [expr ($xMax - $xMin)/2]
411    ipw SetSliceIndex $slice_number
412  } else {
413    set slice_number [ipw GetSliceIndex]
414  }
415  .top.slice config -from $xMin -to $xMax
416  .top.slice set $slice_number
417  UpdateSplinePosition
418  AlignCamera
419  set interpmode $xmode
420  SetInterpolation
421}
422
423proc AlignYaxis { } {
424  global yMin yMax slice_number interpmode ymode
425
426  set po [ ipw GetPlaneOrientation ]
427  set ori [ ipw GetPlaneOrientation ]
428  if { $ori != 1 } {
429    ipw SetPlaneOrientationToYAxes
430    set slice_number [expr ($yMax - $yMin)/2]
431    ipw SetSliceIndex $slice_number
432  } else {
433    set slice_number [ipw GetSliceIndex]
434  }
435  .top.slice config -from $yMin -to $yMax
436  .top.slice set $slice_number
437  UpdateSplinePosition
438  AlignCamera
439  set interpmode $ymode
440  SetInterpolation
441}
442
443proc AlignZaxis { } {
444  global zMin zMax slice_number interpmode zmode
445  set ori [ ipw GetPlaneOrientation ]
446  if { $ori != 2 } {
447    ipw SetPlaneOrientationToZAxes
448    set slice_number [expr ($zMax - $zMin)/2]
449    ipw SetSliceIndex $slice_number
450  } else {
451    set slice_number [ipw GetSliceIndex]
452  }
453  .top.slice config -from $zMin -to $zMax
454  .top.slice set $slice_number
455  UpdateSplinePosition
456  AlignCamera
457  set interpmode $zmode
458  SetInterpolation
459}
460
461proc UpdateSplinePosition { } {
462  set ori [ ipw GetPlaneOrientation ]
463  spline ProjectToPlaneOff
464  spline PlaceWidget
465  spline ProjectToPlaneOn
466  spline SetProjectionNormal $ori
467  spline SetProjectionNormal 3
468  UpdateIPW
469}
470
471# Set the vtkImagePlaneWidget's reslice interpolation mode
472# to the corresponding popup menu choice and store
473# it for subsequent orientation changes.
474#
475proc SetInterpolation { } {
476  global interpmode xmode ymode zmode
477  if { $interpmode == 0 } {
478    ipw TextureInterpolateOff
479  } else {
480    ipw TextureInterpolateOn
481  }
482  ipw SetResliceInterpolate $interpmode
483  set ori [ipw GetPlaneOrientation]
484  if { $ori == 0 } {
485    set xmode $interpmode
486  }  elseif  { $ori == 1 } {
487    set ymode $interpmode
488  } else {
489    set zmode $interpmode
490  }
491  renWin Render
492}
493
494# Procedure to update the placement of the vtkSplineWidget on the
495# plane defined by the vtkImagePlaneWidget.
496#
497proc UpdateIPW { } {
498  set ori [ ipw GetPlaneOrientation ]
499
500  if { $ori == 3 } {
501    spline SetProjectionPosition 0
502  } else {
503    set pos [ ipw GetSlicePosition ]
504    spline SetProjectionPosition $pos
505  }
506
507  UpdateSW
508}
509
510# Procedure to update the spline geometry fed into the probe filter.
511#
512proc UpdateSW { } {
513  spline GetPolyData poly
514}
515
516