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

Last change on this file since 3934 was 3813, checked in by ldelgass, 11 years ago

Fix bug in 'add' method of viewer widgets: list search for existing dataobj
entry was wrong (list and pattern transposed), causing potential duplicate
entries in dataobj list.

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.