source: branches/1.3/gui/scripts/contourresult.tcl @ 4406

Last change on this file since 4406 was 3844, checked in by ldelgass, 11 years ago

Sync with trunk. Branch now differs only from trunk by r3722 (branch is version
1.3, trunk is version 1.4)

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