source: trunk/gui/scripts/contourresult.tcl @ 783

Last change on this file since 783 was 767, checked in by mmc, 17 years ago

Oops! Forgot to add the new -param option emitted by the ResultSet?,
so that the various viewers don't choke.

File size: 43.9 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: contourresult - contour plot in a ResultSet
3#
4#  This widget is a contour plot for 2D meshes with a scalar value.
5#  It is normally used in the ResultViewer to show results from the
6#  run of a Rappture tool.  Use the "add" and "delete" methods to
7#  control the dataobjs showing on the plot.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2005  Purdue Research Foundation
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itk
16package require vtk
17package require vtkinteraction
18package require BLT
19package require Img
20
21option add *ContourResult.width 4i widgetDefault
22option add *ContourResult.height 4i widgetDefault
23option add *ContourResult.foreground black widgetDefault
24option add *ContourResult.controlBackground gray widgetDefault
25option add *ContourResult.controlDarkBackground #999999 widgetDefault
26option add *ContourResult.plotBackground black widgetDefault
27option add *ContourResult.plotForeground white widgetDefault
28option add *ContourResult.font \
29    -*-helvetica-medium-r-normal-*-12-* widgetDefault
30
31itcl::class Rappture::ContourResult {
32    inherit itk::Widget
33
34    itk_option define -plotforeground plotForeground Foreground ""
35    itk_option define -plotbackground plotBackground Background ""
36
37    constructor {args} { # defined below }
38    destructor { # defined below }
39
40    public method add {dataobj {settings ""}}
41    public method get {}
42    public method delete {args}
43    public method scale {args}
44    public method parameters {title args} { # do nothing }
45    public method download {option args}
46
47    protected method _rebuild {}
48    protected method _clear {}
49    protected method _zoom {option}
50    protected method _move {option x y}
51    protected method _slice {option args}
52    protected method _3dView {theta phi}
53    protected method _fixLimits {}
54    protected method _slicertip {axis}
55    protected method _color2rgb {color}
56
57    private variable _dlist ""     ;# list of data objects
58    private variable _dims ""      ;# dimensionality of data objects
59    private variable _obj2color    ;# maps dataobj => plotting color
60    private variable _obj2width    ;# maps dataobj => line width
61    private variable _obj2raise    ;# maps dataobj => raise flag 0/1
62    private variable _obj2vtk      ;# maps dataobj => vtk objects
63    private variable _actors       ;# list of actors for each renderer
64    private variable _lights       ;# list of lights for each renderer
65    private variable _click        ;# info used for _move operations
66    private variable _slicer       ;# vtk transform used for 3D slice plane
67    private variable _limits       ;# autoscale min/max for all axes
68    private variable _view         ;# view params for 3D view
69    private variable _download ""  ;# snapshot for download
70}
71
72itk::usual ContourResult {
73    keep -background -foreground -cursor -font
74    keep -plotbackground -plotforeground
75}
76
77# ----------------------------------------------------------------------
78# CONSTRUCTOR
79# ----------------------------------------------------------------------
80itcl::body Rappture::ContourResult::constructor {args} {
81    option add hull.width hull.height
82    pack propagate $itk_component(hull) no
83
84    set _slicer(xplane) ""
85    set _slicer(yplane) ""
86    set _slicer(zplane) ""
87    set _slicer(xslice) ""
88    set _slicer(yslice) ""
89    set _slicer(zslice) ""
90    set _slicer(readout) ""
91    set _view(theta) 0
92    set _view(phi) 0
93
94    foreach val {xmin xmax ymin ymax zmin zmax vmin vmax} {
95        set _limits($val) ""
96    }
97
98    itk_component add controls {
99        frame $itk_interior.cntls
100    } {
101        usual
102        rename -background -controlbackground controlBackground Background
103    }
104    pack $itk_component(controls) -side right -fill y
105
106    itk_component add zoom {
107        frame $itk_component(controls).zoom
108    } {
109        usual
110        rename -background -controlbackground controlBackground Background
111    }
112    pack $itk_component(zoom) -side top
113
114    itk_component add reset {
115        button $itk_component(zoom).reset \
116            -borderwidth 1 -padx 1 -pady 1 \
117            -bitmap [Rappture::icon reset] \
118            -command [itcl::code $this _zoom reset]
119    } {
120        usual
121        ignore -borderwidth
122        rename -highlightbackground -controlbackground controlBackground Background
123    }
124    pack $itk_component(reset) -padx 4 -pady 4
125    Rappture::Tooltip::for $itk_component(reset) "Reset the view to the default zoom level"
126
127    itk_component add zoomin {
128        button $itk_component(zoom).zin \
129            -borderwidth 1 -padx 1 -pady 1 \
130            -bitmap [Rappture::icon zoomin] \
131            -command [itcl::code $this _zoom in]
132    } {
133        usual
134        ignore -borderwidth
135        rename -highlightbackground -controlbackground controlBackground Background
136    }
137    pack $itk_component(zoomin) -padx 4 -pady 4
138    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
139
140    itk_component add zoomout {
141        button $itk_component(zoom).zout \
142            -borderwidth 1 -padx 1 -pady 1 \
143            -bitmap [Rappture::icon zoomout] \
144            -command [itcl::code $this _zoom out]
145    } {
146        usual
147        ignore -borderwidth
148        rename -highlightbackground -controlbackground controlBackground Background
149    }
150    pack $itk_component(zoomout) -padx 4 -pady 4
151    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
152
153    #
154    # Create slicer controls...
155    #
156    itk_component add slicers {
157        frame $itk_component(controls).slicers
158    } {
159        usual
160        rename -background -controlbackground controlBackground Background
161    }
162    pack $itk_component(slicers) -side bottom -padx 4 -pady 4
163    grid rowconfigure $itk_component(slicers) 1 -weight 1
164
165    #
166    # X-value slicer...
167    #
168    itk_component add xslice {
169        label $itk_component(slicers).xslice \
170            -borderwidth 1 -relief raised -padx 1 -pady 1 \
171            -bitmap [Rappture::icon x]
172    } {
173        usual
174        ignore -borderwidth
175        rename -highlightbackground -controlbackground controlBackground Background
176    }
177    bind $itk_component(xslice) <ButtonPress> \
178        [itcl::code $this _slice axis x toggle]
179    Rappture::Tooltip::for $itk_component(xslice) \
180        "Toggle the X cut plane on/off"
181    grid $itk_component(xslice) -row 0 -column 0 -sticky ew -padx 1
182
183    itk_component add xslicer {
184        ::scale $itk_component(slicers).xval -from 100 -to 0 \
185            -width 10 -orient vertical -showvalue off -state disabled \
186            -borderwidth 1 -highlightthickness 0 \
187            -command [itcl::code $this _slice move x]
188    } {
189        usual
190        ignore -borderwidth
191        ignore -highlightthickness
192        rename -highlightbackground -controlbackground controlBackground Background
193        rename -troughcolor -controldarkbackground controlDarkBackground Background
194    }
195    grid $itk_component(xslicer) -row 1 -column 0 -padx 1
196    Rappture::Tooltip::for $itk_component(xslicer) \
197        "@[itcl::code $this _slicertip x]"
198
199    #
200    # Y-value slicer...
201    #
202    itk_component add yslice {
203        label $itk_component(slicers).yslice \
204            -borderwidth 1 -relief raised -padx 1 -pady 1 \
205            -bitmap [Rappture::icon y]
206    } {
207        usual
208        ignore -borderwidth
209        rename -highlightbackground -controlbackground controlBackground Background
210    }
211    bind $itk_component(yslice) <ButtonPress> \
212        [itcl::code $this _slice axis y toggle]
213    Rappture::Tooltip::for $itk_component(yslice) \
214        "Toggle the Y cut plane on/off"
215    grid $itk_component(yslice) -row 0 -column 1 -sticky ew -padx 1
216
217    itk_component add yslicer {
218        ::scale $itk_component(slicers).yval -from 100 -to 0 \
219            -width 10 -orient vertical -showvalue off -state disabled \
220            -borderwidth 1 -highlightthickness 0 \
221            -command [itcl::code $this _slice move y]
222    } {
223        usual
224        ignore -borderwidth
225        ignore -highlightthickness
226        rename -highlightbackground -controlbackground controlBackground Background
227        rename -troughcolor -controldarkbackground controlDarkBackground Background
228    }
229    grid $itk_component(yslicer) -row 1 -column 1 -padx 1
230    Rappture::Tooltip::for $itk_component(yslicer) \
231        "@[itcl::code $this _slicertip y]"
232
233    #
234    # Z-value slicer...
235    #
236    itk_component add zslice {
237        label $itk_component(slicers).zslice \
238            -borderwidth 1 -relief raised -padx 1 -pady 1 \
239            -bitmap [Rappture::icon z]
240    } {
241        usual
242        ignore -borderwidth
243        rename -highlightbackground -controlbackground controlBackground Background
244    }
245    grid $itk_component(zslice) -row 0 -column 2 -sticky ew -padx 1
246    bind $itk_component(zslice) <ButtonPress> \
247        [itcl::code $this _slice axis z toggle]
248    Rappture::Tooltip::for $itk_component(zslice) \
249        "Toggle the Z cut plane on/off"
250
251    itk_component add zslicer {
252        ::scale $itk_component(slicers).zval -from 100 -to 0 \
253            -width 10 -orient vertical -showvalue off -state disabled \
254            -borderwidth 1 -highlightthickness 0 \
255            -command [itcl::code $this _slice move z]
256    } {
257        usual
258        ignore -borderwidth
259        ignore -highlightthickness
260        rename -highlightbackground -controlbackground controlBackground Background
261        rename -troughcolor -controldarkbackground controlDarkBackground Background
262    }
263    grid $itk_component(zslicer) -row 1 -column 2 -padx 1
264    Rappture::Tooltip::for $itk_component(zslicer) \
265        "@[itcl::code $this _slicertip z]"
266
267    #
268    # RENDERING AREA
269    #
270    itk_component add area {
271        frame $itk_interior.area
272    }
273    pack $itk_component(area) -expand yes -fill both
274
275    vtkRenderer $this-ren
276    vtkRenderWindow $this-renWin
277    $this-renWin AddRenderer $this-ren
278    $this-renWin LineSmoothingOn
279    $this-renWin PolygonSmoothingOn
280    vtkRenderWindowInteractor $this-iren
281    $this-iren SetRenderWindow $this-renWin
282
283    itk_component add plot {
284        vtkTkRenderWidget $itk_component(area).plot -rw $this-renWin \
285            -width 1 -height 1
286    } {
287    }
288    pack $itk_component(plot) -expand yes -fill both
289
290
291    vtkRenderer $this-ren2
292    vtkRenderWindow $this-renWin2
293    $this-renWin2 AddRenderer $this-ren2
294    vtkRenderWindowInteractor $this-iren2
295    $this-iren2 SetRenderWindow $this-renWin2
296
297    itk_component add legend {
298        vtkTkRenderWidget $itk_component(area).legend -rw $this-renWin2 \
299            -width 1 -height 40
300    } {
301    }
302    pack $itk_component(legend) -side bottom -fill x
303
304    #
305    # Create a photo for download snapshots
306    #
307    set _download [image create photo]
308
309    eval itk_initialize $args
310}
311
312# ----------------------------------------------------------------------
313# DESTRUCTOR
314# ----------------------------------------------------------------------
315itcl::body Rappture::ContourResult::destructor {} {
316    _clear
317    after cancel [itcl::code $this _rebuild]
318
319    rename $this-renWin ""
320    rename $this-ren ""
321    rename $this-iren ""
322
323    rename $this-renWin2 ""
324    rename $this-ren2 ""
325    rename $this-iren2 ""
326
327    image delete $_download
328}
329
330# ----------------------------------------------------------------------
331# USAGE: add <dataobj> ?<settings>?
332#
333# Clients use this to add a data object to the plot.  The optional
334# <settings> are used to configure the plot.  Allowed settings are
335# -color, -brightness, -width, -linestyle, and -raise.
336# ----------------------------------------------------------------------
337itcl::body Rappture::ContourResult::add {dataobj {settings ""}} {
338    array set params {
339        -color auto
340        -width 1
341        -linestyle solid
342        -brightness 0
343        -raise 0
344        -description ""
345        -param ""
346    }
347    foreach {opt val} $settings {
348        if {![info exists params($opt)]} {
349            error "bad setting \"$opt\": should be [join [lsort [array names params]] {, }]"
350        }
351        set params($opt) $val
352    }
353    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
354        # can't handle -autocolors yet
355        set params(-color) black
356    }
357
358    set pos [lsearch -exact $dataobj $_dlist]
359    if {$pos < 0} {
360        lappend _dlist $dataobj
361        set _obj2color($dataobj) $params(-color)
362        set _obj2width($dataobj) $params(-width)
363        set _obj2raise($dataobj) $params(-raise)
364
365        after cancel [itcl::code $this _rebuild]
366        after idle [itcl::code $this _rebuild]
367    }
368}
369
370# ----------------------------------------------------------------------
371# USAGE: get
372#
373# Clients use this to query the list of objects being plotted, in
374# order from bottom to top of this result.
375# ----------------------------------------------------------------------
376itcl::body Rappture::ContourResult::get {} {
377    # put the dataobj list in order according to -raise options
378    set dlist $_dlist
379    foreach obj $dlist {
380        if {[info exists _obj2raise($obj)] && $_obj2raise($obj)} {
381            set i [lsearch -exact $dlist $obj]
382            if {$i >= 0} {
383                set dlist [lreplace $dlist $i $i]
384                lappend dlist $obj
385            }
386        }
387    }
388    return $dlist
389}
390
391# ----------------------------------------------------------------------
392# USAGE: delete ?<dataobj1> <dataobj2> ...?
393#
394# Clients use this to delete a dataobj from the plot.  If no dataobjs
395# are specified, then all dataobjs are deleted.
396# ----------------------------------------------------------------------
397itcl::body Rappture::ContourResult::delete {args} {
398    if {[llength $args] == 0} {
399        set args $_dlist
400    }
401
402    # delete all specified dataobjs
403    set changed 0
404    foreach dataobj $args {
405        set pos [lsearch -exact $_dlist $dataobj]
406        if {$pos >= 0} {
407            set _dlist [lreplace $_dlist $pos $pos]
408            catch {unset _obj2color($dataobj)}
409            catch {unset _obj2width($dataobj)}
410            catch {unset _obj2raise($dataobj)}
411            set changed 1
412        }
413    }
414
415    # if anything changed, then rebuild the plot
416    if {$changed} {
417        after cancel [itcl::code $this _rebuild]
418        after idle [itcl::code $this _rebuild]
419    }
420}
421
422# ----------------------------------------------------------------------
423# USAGE: scale ?<data1> <data2> ...?
424#
425# Sets the default limits for the overall plot according to the
426# limits of the data for all of the given <data> objects.  This
427# accounts for all objects--even those not showing on the screen.
428# Because of this, the limits are appropriate for all objects as
429# the user scans through data in the ResultSet viewer.
430# ----------------------------------------------------------------------
431itcl::body Rappture::ContourResult::scale {args} {
432    foreach val {xmin xmax ymin ymax zmin zmax vmin vmax} {
433        set _limits($val) ""
434    }
435    foreach obj $args {
436        foreach axis {x y z v} {
437            foreach {min max} [$obj limits $axis] break
438            if {"" != $min && "" != $max} {
439                if {"" == $_limits(${axis}min)} {
440                    set _limits(${axis}min) $min
441                    set _limits(${axis}max) $max
442                } else {
443                    if {$min < $_limits(${axis}min)} {
444                        set _limits(${axis}min) $min
445                    }
446                    if {$max > $_limits(${axis}max)} {
447                        set _limits(${axis}max) $max
448                    }
449                }
450            }
451        }
452    }
453    _fixLimits
454}
455
456# ----------------------------------------------------------------------
457# USAGE: download coming
458# USAGE: download controls <downloadCommand>
459# USAGE: download now
460#
461# Clients use this method to create a downloadable representation
462# of the plot.  Returns a list of the form {ext string}, where
463# "ext" is the file extension (indicating the type of data) and
464# "string" is the data itself.
465# ----------------------------------------------------------------------
466itcl::body Rappture::ContourResult::download {option args} {
467    switch $option {
468        coming {
469            if {[catch {blt::winop snap $itk_component(area) $_download}]} {
470                $_download configure -width 1 -height 1
471                $_download put #000000
472            }
473        }
474        controls {
475            # no controls for this download yet
476            return ""
477        }
478        now {
479            #
480            # Hack alert!  Need data in binary format,
481            # so we'll save to a file and read it back.
482            #
483            set tmpfile /tmp/image[pid].jpg
484            $_download write $tmpfile -format jpeg
485            set fid [open $tmpfile r]
486            fconfigure $fid -encoding binary -translation binary
487            set bytes [read $fid]
488            close $fid
489            file delete -force $tmpfile
490
491            return [list .jpg $bytes]
492        }
493        default {
494            error "bad option \"$option\": should be coming, controls, now"
495        }
496    }
497}
498
499# ----------------------------------------------------------------------
500# USAGE: _rebuild
501#
502# Called automatically whenever something changes that affects the
503# data in the widget.  Clears any existing data and rebuilds the
504# widget to display new data.
505# ----------------------------------------------------------------------
506itcl::body Rappture::ContourResult::_rebuild {} {
507    _clear
508    set id 0
509
510    # determine the dimensionality from the topmost (raised) object
511    set dlist [get]
512    set dataobj [lindex $dlist end]
513    if {$dataobj != ""} {
514        set _dims [lindex [lsort [$dataobj components -dimensions]] end]
515    } else {
516        set _dims "0D"
517    }
518
519    #
520    # LOOKUP TABLE FOR COLOR CONTOURS
521    #
522    # use vmin/vmax if possible, otherwise get from data
523    if {$_limits(vmin) == "" || $_limits(vmax) == ""} {
524        set v0 0
525        set v1 1
526        if {[info exists _obj2vtk($dataobj)]} {
527            set pd [lindex $_obj2vtk($dataobj) 0]
528            if {"" != $pd} {
529                foreach {v0 v1} [$pd GetScalarRange] break
530            }
531        }
532    } else {
533        set v0 $_limits(vmin)
534        set v1 $_limits(vmax)
535    }
536
537    set lu $this-lookup$id
538    vtkLookupTable $lu
539    $lu SetTableRange $v0 $v1
540    $lu SetHueRange 0.66667 0.0
541    $lu Build
542
543    lappend _obj2vtk($dataobj) $lu
544
545    if {$_dims == "3D"} {
546        #
547        # 3D LIGHTS (on both sides of all three axes)
548        #
549        set x0 $_limits(xmin)
550        set x1 $_limits(xmax)
551        set xm [expr {0.5*($x0+$x1)}]
552        set y0 $_limits(ymin)
553        set y1 $_limits(ymax)
554        set ym [expr {0.5*($y0+$y1)}]
555        set z0 $_limits(zmin)
556        set z1 $_limits(zmax)
557        set zm [expr {0.5*($z0+$z1)}]
558        set xr [expr {$x1-$x0}]
559        set yr [expr {$y1-$y0}]
560        set zr [expr {$z1-$z0}]
561
562        set lt $this-light$id
563        vtkLight $lt
564        $lt SetColor 1 1 1
565        $lt SetAttenuationValues 0 0 0
566        $lt SetFocalPoint $xm $ym $zm
567        $lt SetLightTypeToHeadlight
568        $this-ren AddLight $lt
569        lappend _lights($this-ren) $lt
570
571    } else {
572    }
573
574    # scan through all data objects and build the contours
575    set firstobj 1
576    foreach dataobj [get] {
577        foreach comp [$dataobj components] {
578            #
579            # Add color contours.
580            #
581            if {$firstobj} {
582                if {$_dims == "3D"} {
583                    pack $itk_component(slicers) -side bottom -padx 4 -pady 4
584                    pack $itk_component(reset) -side left
585                    pack $itk_component(zoomin) -side left
586                    pack $itk_component(zoomout) -side left
587
588                    #
589                    # 3D DATA SET
590                    #
591                    set mesh [$dataobj mesh $comp]
592                    if {"" == $mesh} {
593                        set x [expr {[winfo rootx $itk_component(area)]+10}]
594                        set y [expr {[winfo rooty $itk_component(area)]+10}]
595                        Rappture::Tooltip::cue @$x,$y "This data requires the visualization server, and that appears to be down.  Please try your simulation again later."
596                        return
597                    }
598                    switch -- [$mesh GetClassName] {
599                      vtkPoints {
600                        # handle cloud of 3D points
601                        set pd $this-polydata$id
602                        vtkPolyData $pd
603                        $pd SetPoints $mesh
604                        [$pd GetPointData] SetScalars [$dataobj values $comp]
605
606                        set tr $this-triangles$id
607                        vtkDelaunay3D $tr
608                        $tr SetInput $pd
609                        $tr SetTolerance 0.0000000000001
610                        set source [$tr GetOutput]
611
612                        set mp $this-mapper$id
613                        vtkPolyDataMapper $mp
614
615                        lappend _obj2vtk($dataobj) $pd $tr $mp
616                      }
617                      vtkUnstructuredGrid {
618                        # handle 3D grid with connectivity
619                        set gr $this-grdata$id
620                        vtkUnstructuredGrid $gr
621                        $gr ShallowCopy $mesh
622                        [$gr GetPointData] SetScalars [$dataobj values $comp]
623                        set source $gr
624
625                        lappend _obj2vtk($dataobj) $gr
626                      }
627                      vtkRectilinearGrid {
628                        # handle 3D grid with connectivity
629                        set gr $this-grdata$id
630                        vtkRectilinearGrid $gr
631                        $gr ShallowCopy $mesh
632                        [$gr GetPointData] SetScalars [$dataobj values $comp]
633                        set source $gr
634
635                        lappend _obj2vtk($dataobj) $gr
636                      }
637                      default {
638                        error "don't know how to handle [$mesh GetClassName] data"
639                      }
640                    }
641
642                    #
643                    # 3D ISOSURFACES
644                    #
645                    set iso $this-iso$id
646                    vtkContourFilter $iso
647                      $iso SetInput $source
648
649                    set mp $this-isomap$id
650                    vtkPolyDataMapper $mp
651                      $mp SetInput [$iso GetOutput]
652
653                    set ac $this-isoactor$id
654                    vtkActor $ac
655                      $ac SetMapper $mp
656                      [$ac GetProperty] SetOpacity 0.3
657                      [$ac GetProperty] SetDiffuse 0.5
658                      [$ac GetProperty] SetAmbient 0.7
659                      [$ac GetProperty] SetSpecular 10.0
660                      [$ac GetProperty] SetSpecularPower 200.0
661                    $this-ren AddActor $ac
662
663                    lappend _obj2vtk($dataobj) $iso $mp $ac
664                    lappend _actors($this-ren) $ac
665
666                    catch {unset style}
667                    array set style [lindex [$dataobj components -style $comp] 0]
668                    if {[info exists style(-color)]} {
669                        $mp ScalarVisibilityOff  ;# take color from actor
670                        eval [$ac GetProperty] SetColor [_color2rgb $style(-color)]
671                    }
672
673                    if {[info exists style(-opacity)]} {
674                        [$ac GetProperty] SetOpacity $style(-opacity)
675                    }
676
677                    set levels 5
678                    if {[info exists style(-levels)]} {
679                        set levels $style(-levels)
680                    }
681                    if {$levels == 1} {
682                        $iso SetValue 0 [expr {0.5*($v1-$v0)+$v0}]
683                    } else {
684                        $iso GenerateValues [expr {$levels+2}] $v0 $v1
685                    }
686
687                    #
688                    # 3D CUT PLANES
689                    #
690                    if {$id == 0} {
691                        foreach axis {x y z} norm {{1 0 0} {0 1 0} {0 0 1}} {
692                            set pl $this-${axis}cutplane$id
693                            vtkPlane $pl
694                            eval $pl SetNormal $norm
695                            set _slicer(${axis}plane) $pl
696
697                            set ct $this-${axis}cutter$id
698                            vtkCutter $ct
699                            $ct SetInput $source
700                            $ct SetCutFunction $pl
701
702                            set mp $this-${axis}cutmapper$id
703                            vtkPolyDataMapper $mp
704                            $mp SetInput [$ct GetOutput]
705                            $mp SetScalarRange $v0 $v1
706                            $mp SetLookupTable $lu
707
708                            lappend _obj2vtk($dataobj) $pl $ct $mp
709
710                            set ac $this-${axis}actor$id
711                            vtkActor $ac
712                            $ac VisibilityOff
713                            $ac SetMapper $mp
714                            $ac SetPosition 0 0 0
715                            [$ac GetProperty] SetColor 0 0 0
716                            set _slicer(${axis}slice) $ac
717
718                            $this-ren AddActor $ac
719                            lappend _actors($this-ren) $ac
720                            lappend _obj2vtk($dataobj) $ac
721                        }
722
723                        #
724                        # CUT PLANE READOUT
725                        #
726                        set tx $this-text$id
727                        vtkTextMapper $tx
728                        set tp [$tx GetTextProperty]
729                        eval $tp SetColor [_color2rgb $itk_option(-plotforeground)]
730                        $tp SetVerticalJustificationToTop
731                        set _slicer(readout) $tx
732
733                        set txa $this-texta$id
734                        vtkActor2D $txa
735                        $txa SetMapper $tx
736                        [$txa GetPositionCoordinate] \
737                            SetCoordinateSystemToNormalizedDisplay
738                        [$txa GetPositionCoordinate] SetValue 0.02 0.98
739
740                        $this-ren AddActor $txa
741                        lappend _actors($this-ren) $txa
742
743                        lappend _obj2vtk($dataobj) $tx $txa
744
745                        # turn off all slicers by default
746                        foreach axis {x y z} {
747                            $itk_component(${axis}slicer) configure -state normal
748                            $itk_component(${axis}slicer) set 50
749                            _slice move $axis 50
750                            _slice axis $axis off
751                        }
752                    }
753
754                } else {
755                    pack forget $itk_component(slicers)
756                    pack $itk_component(reset) -side top
757                    pack $itk_component(zoomin) -side top
758                    pack $itk_component(zoomout) -side top
759
760                    set pd $this-polydata$id
761                    vtkPolyData $pd
762                    $pd SetPoints [$dataobj mesh $comp]
763                    [$pd GetPointData] SetScalars [$dataobj values $comp]
764
765                    set tr $this-triangles$id
766                    vtkDelaunay2D $tr
767                    $tr SetInput $pd
768                    $tr SetTolerance 0.0000000000001
769                    set source [$tr GetOutput]
770
771                    set mp $this-mapper$id
772                    vtkPolyDataMapper $mp
773                    $mp SetInput $source
774                    $mp SetScalarRange $v0 $v1
775                    $mp SetLookupTable $lu
776
777                    set ac $this-actor$id
778                    vtkActor $ac
779                    $ac SetMapper $mp
780                    $ac SetPosition 0 0 0
781                    [$ac GetProperty] SetColor 0 0 0
782                    $this-ren AddActor $ac
783                    lappend _actors($this-ren) $ac
784
785                    lappend _obj2vtk($dataobj) $pd $tr $mp $ac
786                }
787            } else {
788                #
789                # Add color lines
790                #
791                set cf $this-clfilter$id
792                vtkContourFilter $cf
793                $cf SetInput $source
794                $cf GenerateValues 20 $v0 $v1
795
796                set mp $this-clmapper$id
797                vtkPolyDataMapper $mp
798                $mp SetInput [$cf GetOutput]
799                $mp SetScalarRange $v0 $v1
800                $mp SetLookupTable $lu
801
802                set ac $this-clactor$id
803                vtkActor $ac
804                $ac SetMapper $mp
805                [$ac GetProperty] SetColor 1 1 1
806                $ac SetPosition 0 0 0
807                $this-ren AddActor $ac
808                lappend _actors($this-ren) $ac
809
810                lappend _obj2vtk($dataobj) $cf $mp $ac
811            }
812
813            #
814            # Add an outline around the data
815            #
816            if {$id == 0} {
817                set olf $this-olfilter$id
818                vtkOutlineFilter $olf
819                $olf SetInput $source
820
821                set olm $this-olmapper$id
822                vtkPolyDataMapper $olm
823                $olm SetInput [$olf GetOutput]
824
825                set ola $this-olactor$id
826                vtkActor $ola
827                $ola SetMapper $olm
828                eval [$ola GetProperty] SetColor [_color2rgb $itk_option(-plotforeground)]
829                $this-ren AddActor $ola
830                lappend _actors($this-ren) $ola
831
832                lappend _obj2vtk($dataobj) $olf $olm $ola
833
834                if {$_dims == "3D"} {
835                    # pick a good scale factor for text
836                    if {$xr < $yr} {
837                        set tscale [expr {0.1*$xr}]
838                    } else {
839                        set tscale [expr {0.1*$yr}]
840                    }
841
842                    foreach {i axis px py pz rx ry rz} {
843                        0  x   $xm   0   0   90   0   0
844                        1  y     0 $ym   0   90 -90   0
845                        2  z   $x1   0 $zm   90   0 -45
846                    } {
847                        set length "[expr {[set ${axis}1]-[set ${axis}0]}]"
848
849                        set vtx $this-${axis}label$id
850                        vtkVectorText $vtx
851                        $vtx SetText "$axis"
852
853                        set vmp $this-${axis}lmap$id
854                        vtkPolyDataMapper $vmp
855                        $vmp SetInput [$vtx GetOutput]
856
857                        set vac $this-${axis}lact$id
858                        vtkActor $vac
859                        $vac SetMapper $vmp
860                        $vac SetPosition [expr $px] [expr $py] [expr $pz]
861                        $vac SetOrientation $rx $ry $rz
862                        $vac SetScale $tscale
863                        $this-ren AddActor $vac
864
865                        lappend _obj2vtk($dataobj) $vtx $vmp $vac
866                        lappend _actors($this-ren) $vac
867
868                        $vmp Update
869                        foreach {xx0 xx1 yy0 yy1 zz0 zz1} [$vac GetBounds] break
870                        switch -- $axis {
871                          x {
872                            set dx [expr {-0.5*($xx1-$xx0)}]
873                            set dy 0
874                            set dz [expr {1.3*($zz0-$zz1)}]
875                          }
876                          y {
877                            set dx 0
878                            set dy [expr {0.5*($yy1-$yy0)}]
879                            set dz [expr {$zz0-$zz1}]
880                          }
881                          z {
882                            set dx [expr {0.2*$tscale}]
883                            set dy $dx
884                            set dz [expr {-0.5*($zz1-$zz0)}]
885                          }
886                        }
887                        $vac AddPosition $dx $dy $dz
888                    }
889                }
890            }
891
892            #
893            # Add a legend with the scale.
894            #
895            if {$id == 0} {
896                set lg $this-legend$id
897                vtkScalarBarActor $lg
898                $lg SetLookupTable $lu
899                [$lg GetPositionCoordinate] SetCoordinateSystemToNormalizedViewport
900                [$lg GetPositionCoordinate] SetValue 0.1 0.1
901                $lg SetOrientationToHorizontal
902                $lg SetWidth 0.8
903                $lg SetHeight 1.0
904
905                set tp [$lg GetLabelTextProperty]
906                eval $tp SetColor [_color2rgb $itk_option(-plotforeground)]
907                $tp BoldOff
908                $tp ItalicOff
909                $tp ShadowOff
910                #eval $tp SetShadowColor [_color2rgb gray]
911
912                $this-ren2 AddActor2D $lg
913                lappend _actors($this-ren2) $lg
914                lappend _obj2vtk($dataobj) $lg
915            }
916
917            incr id
918        }
919        set firstobj 0
920    }
921    _fixLimits
922    _zoom reset
923
924    #
925    # HACK ALERT!  A single ResetCamera doesn't seem to work for
926    #   some contour data.  You have to do it multiple times to
927    #   get to the right zoom factor on data.  I hope 20 times is
928    #   enough.  I hate Vtk sometimes...
929    #
930    for {set i 0} {$i < 20} {incr i} {
931        $this-ren ResetCamera
932        [$this-ren GetActiveCamera] Zoom 1.5
933    }
934
935    # prevent interactions -- use our own
936    blt::busy hold $itk_component(area) -cursor left_ptr
937    bind $itk_component(area)_Busy <ButtonPress> \
938        [itcl::code $this _move click %x %y]
939    bind $itk_component(area)_Busy <B1-Motion> \
940        [itcl::code $this _move drag %x %y]
941    bind $itk_component(area)_Busy <ButtonRelease> \
942        [itcl::code $this _move release %x %y]
943}
944
945# ----------------------------------------------------------------------
946# USAGE: _clear
947#
948# Used internally to clear the drawing area and tear down all vtk
949# objects in the current scene.
950# ----------------------------------------------------------------------
951itcl::body Rappture::ContourResult::_clear {} {
952    # clear out any old constructs
953    foreach ren [array names _actors] {
954        foreach actor $_actors($ren) {
955            $ren RemoveActor $actor
956        }
957        set _actors($ren) ""
958    }
959    foreach ren [array names _lights] {
960        foreach light $_lights($ren) {
961            $ren RemoveLight $light
962            rename $light ""
963        }
964        set _lights($ren) ""
965    }
966    foreach dataobj [array names _obj2vtk] {
967        foreach cmd $_obj2vtk($dataobj) {
968            rename $cmd ""
969        }
970        set _obj2vtk($dataobj) ""
971    }
972    set _slicer(xplane) ""
973    set _slicer(yplane) ""
974    set _slicer(zplane) ""
975    set _slicer(xslice) ""
976    set _slicer(yslice) ""
977    set _slicer(zslice) ""
978    set _slicer(readout) ""
979}
980
981# ----------------------------------------------------------------------
982# USAGE: _zoom in
983# USAGE: _zoom out
984# USAGE: _zoom reset
985#
986# Called automatically when the user clicks on one of the zoom
987# controls for this widget.  Changes the zoom for the current view.
988# ----------------------------------------------------------------------
989itcl::body Rappture::ContourResult::_zoom {option} {
990    switch -- $option {
991        in {
992            [$this-ren GetActiveCamera] Zoom 1.25
993            $this-renWin Render
994        }
995        out {
996            [$this-ren GetActiveCamera] Zoom 0.8
997            $this-renWin Render
998        }
999        reset {
1000            if {$_dims == "3D"} {
1001                [$this-ren GetActiveCamera] SetViewAngle 30
1002                $this-ren ResetCamera
1003                _3dView 45 45
1004            } else {
1005                $this-ren ResetCamera
1006                [$this-ren GetActiveCamera] Zoom 1.5
1007            }
1008            $this-renWin Render
1009            $this-renWin2 Render
1010        }
1011    }
1012}
1013
1014# ----------------------------------------------------------------------
1015# USAGE: _move click <x> <y>
1016# USAGE: _move drag <x> <y>
1017# USAGE: _move release <x> <y>
1018#
1019# Called automatically when the user clicks/drags/releases in the
1020# plot area.  Moves the plot according to the user's actions.
1021# ----------------------------------------------------------------------
1022itcl::body Rappture::ContourResult::_move {option x y} {
1023    switch -- $option {
1024        click {
1025            blt::busy configure $itk_component(area) -cursor fleur
1026            set _click(x) $x
1027            set _click(y) $y
1028            set _click(theta) $_view(theta)
1029            set _click(phi) $_view(phi)
1030        }
1031        drag {
1032            if {[array size _click] == 0} {
1033                _move click $x $y
1034            } else {
1035                set w [winfo width $itk_component(plot)]
1036                set h [winfo height $itk_component(plot)]
1037                set scalex [expr {$_limits(xmax)-$_limits(xmin)}]
1038                set scaley [expr {$_limits(ymax)-$_limits(ymin)}]
1039                set dx [expr {double($x-$_click(x))/$w*$scalex}]
1040                set dy [expr {double($y-$_click(y))/$h*$scaley}]
1041
1042                if {$_dims == "2D"} {
1043                    #
1044                    # Shift the contour plot in 2D
1045                    #
1046                    foreach actor $_actors($this-ren) {
1047                        foreach {ax ay az} [$actor GetPosition] break
1048                        $actor SetPosition [expr {$ax+$dx}] [expr {$ay-$dy}] 0
1049                    }
1050                    $this-renWin Render
1051                } elseif {$_dims == "3D"} {
1052                    #
1053                    # Rotate the camera in 3D
1054                    #
1055                    set theta [expr {$_view(theta) - $dy*180}]
1056                    if {$theta < 2} { set theta 2 }
1057                    if {$theta > 178} { set theta 178 }
1058                    set phi [expr {$_view(phi) - $dx*360}]
1059
1060                    _3dView $theta $phi
1061                    $this-renWin Render
1062                }
1063                set _click(x) $x
1064                set _click(y) $y
1065            }
1066        }
1067        release {
1068            _move drag $x $y
1069            blt::busy configure $itk_component(area) -cursor left_ptr
1070            catch {unset _click}
1071        }
1072        default {
1073            error "bad option \"$option\": should be click, drag, release"
1074        }
1075    }
1076}
1077
1078# ----------------------------------------------------------------------
1079# USAGE: _slice axis x|y|z ?on|off|toggle?
1080# USAGE: _slice move x|y|z <newval>
1081#
1082# Called automatically when the user drags the slider to move the
1083# cut plane that slices 3D data.  Gets the current value from the
1084# slider and moves the cut plane to the appropriate point in the
1085# data set.
1086# ----------------------------------------------------------------------
1087itcl::body Rappture::ContourResult::_slice {option args} {
1088    if {$_slicer(xplane) == ""} {
1089        # no slicer? then bail out!
1090        return
1091    }
1092    switch -- $option {
1093        axis {
1094            if {[llength $args] < 1 || [llength $args] > 2} {
1095                error "wrong # args: should be \"_slice axis x|y|z ?on|off|toggle?\""
1096            }
1097            set axis [lindex $args 0]
1098            set op [lindex $args 1]
1099            if {$op == ""} { set op "on" }
1100
1101            if {[$itk_component(${axis}slice) cget -relief] == "raised"} {
1102                set current "off"
1103            } else {
1104                set current "on"
1105            }
1106
1107            if {$op == "toggle"} {
1108                if {$current == "on"} { set op "off" } else { set op "on" }
1109            }
1110
1111            if {$op} {
1112                $itk_component(${axis}slicer) configure -state normal
1113                $_slicer(${axis}slice) VisibilityOn
1114                $itk_component(${axis}slice) configure -relief sunken
1115            } else {
1116                $itk_component(${axis}slicer) configure -state disabled
1117                $_slicer(${axis}slice) VisibilityOff
1118                $itk_component(${axis}slice) configure -relief raised
1119            }
1120            $this-renWin Render
1121        }
1122        move {
1123            if {[llength $args] != 2} {
1124                error "wrong # args: should be \"_slice move x|y|z newval\""
1125            }
1126            set axis [lindex $args 0]
1127            set newval [lindex $args 1]
1128
1129            set xm [expr {0.5*($_limits(xmax)+$_limits(xmin))}]
1130            set ym [expr {0.5*($_limits(ymax)+$_limits(ymin))}]
1131            set zm [expr {0.5*($_limits(zmax)+$_limits(zmin))}]
1132
1133            set newval [expr {0.01*($newval-50)
1134                *($_limits(${axis}max)-$_limits(${axis}min))
1135                  + 0.5*($_limits(${axis}max)+$_limits(${axis}min))}]
1136
1137            # show the current value in the readout
1138            if {$_slicer(readout) != ""} {
1139                $_slicer(readout) SetInput "$axis = $newval"
1140            }
1141
1142            # keep a little inside the volume, or the slice will disappear!
1143            if {$newval == $_limits(${axis}min)} {
1144                set range [expr {$_limits(${axis}max)-$_limits(${axis}min)}]
1145                set newval [expr {$newval + 1e-6*$range}]
1146            }
1147
1148            # xfer new value to the proper dimension and move the cut plane
1149            set ${axis}m $newval
1150            $_slicer(${axis}plane) SetOrigin $xm $ym $zm
1151
1152            $this-renWin Render
1153        }
1154        default {
1155            error "bad option \"$option\": should be axis or move"
1156        }
1157    }
1158}
1159
1160# ----------------------------------------------------------------------
1161# USAGE: _3dView <theta> <phi>
1162#
1163# Used internally to change the position of the camera for 3D data
1164# sets.  Sets the camera according to the angles <theta> (angle from
1165# the z-axis) and <phi> (angle from the x-axis in the x-y plane).
1166# Both angles are in degrees.
1167# ----------------------------------------------------------------------
1168itcl::body Rappture::ContourResult::_3dView {theta phi} {
1169    set deg2rad 0.0174532927778
1170    set xn [expr {sin($theta*$deg2rad)*cos($phi*$deg2rad)}]
1171    set yn [expr {sin($theta*$deg2rad)*sin($phi*$deg2rad)}]
1172    set zn [expr {cos($theta*$deg2rad)}]
1173
1174    set xm [expr {0.5*($_limits(xmax)+$_limits(xmin))}]
1175    set ym [expr {0.5*($_limits(ymax)+$_limits(ymin))}]
1176    set zm [expr {0.5*($_limits(zmax)+$_limits(zmin))}]
1177
1178    set cam [$this-ren GetActiveCamera]
1179    set zoom [$cam GetViewAngle]
1180    $cam SetViewAngle 30
1181
1182    $cam SetFocalPoint $xm $ym $zm
1183    $cam SetPosition [expr {$xm-$xn}] [expr {$ym-$yn}] [expr {$zm+$zn}]
1184    $cam ComputeViewPlaneNormal
1185    $cam SetViewUp 0 0 1  ;# z-dir is up
1186    $cam OrthogonalizeViewUp
1187    $this-ren ResetCamera
1188    $cam SetViewAngle $zoom
1189
1190    set _view(theta) $theta
1191    set _view(phi) $phi
1192}
1193
1194# ----------------------------------------------------------------------
1195# USAGE: _fixLimits
1196#
1197# Used internally to apply automatic limits to the axes for the
1198# current plot.
1199# ----------------------------------------------------------------------
1200itcl::body Rappture::ContourResult::_fixLimits {} {
1201    $this-ren ResetCamera
1202    [$this-ren GetActiveCamera] Zoom 1.5
1203    $this-renWin Render
1204    $this-renWin2 Render
1205}
1206
1207# ----------------------------------------------------------------------
1208# USAGE: _slicertip <axis>
1209#
1210# Used internally to generate a tooltip for the x/y/z slicer controls.
1211# Returns a message that includes the current slicer value.
1212# ----------------------------------------------------------------------
1213itcl::body Rappture::ContourResult::_slicertip {axis} {
1214    set val [$itk_component(${axis}slicer) get]
1215    set val [expr {0.01*($val-50)
1216        *($_limits(${axis}max)-$_limits(${axis}min))
1217          + 0.5*($_limits(${axis}max)+$_limits(${axis}min))}]
1218    return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val"
1219}
1220
1221# ----------------------------------------------------------------------
1222# USAGE: _color2rgb <color>
1223#
1224# Used internally to convert a color name to a set of {r g b} values
1225# needed for vtk.  Each r/g/b component is scaled in the range 0-1.
1226# ----------------------------------------------------------------------
1227itcl::body Rappture::ContourResult::_color2rgb {color} {
1228    foreach {r g b} [winfo rgb $itk_component(hull) $color] break
1229    set r [expr {$r/65535.0}]
1230    set g [expr {$g/65535.0}]
1231    set b [expr {$b/65535.0}]
1232    return [list $r $g $b]
1233}
1234
1235# ----------------------------------------------------------------------
1236# CONFIGURATION OPTION: -plotbackground
1237# ----------------------------------------------------------------------
1238itcl::configbody Rappture::ContourResult::plotbackground {
1239    foreach {r g b} [_color2rgb $itk_option(-plotbackground)] break
1240    $this-ren SetBackground $r $g $b
1241    $this-renWin Render
1242    $this-ren2 SetBackground $r $g $b
1243    $this-renWin2 Render
1244}
1245
1246# ----------------------------------------------------------------------
1247# CONFIGURATION OPTION: -plotforeground
1248# ----------------------------------------------------------------------
1249itcl::configbody Rappture::ContourResult::plotforeground {
1250    after cancel [itcl::code $this _rebuild]
1251    after idle [itcl::code $this _rebuild]
1252}
Note: See TracBrowser for help on using the repository browser.