source: branches/1.4/gui/scripts/nanovisviewer.tcl @ 5209

Last change on this file since 5209 was 5209, checked in by ldelgass, 10 years ago

merge r5208 from trunk

File size: 80.7 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2# ----------------------------------------------------------------------
3#  COMPONENT: nanovisviewer - 3D volume rendering
4#
5#  This widget performs volume rendering on 3D scalar/vector datasets.
6#  It connects to the Nanovis server running on a rendering farm,
7#  transmits data, and displays the results.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itk
16package require BLT
17package require Img
18
19#
20# FIXME:
21#       Need to Add DX readers this client to examine the data before
22#       it's sent to the server.  This will eliminate 90% of the insanity in
23#       computing the limits of all the volumes.  I can rip out all the
24#       "receive data" "send transfer function" event crap.
25#
26#       This means we can compute the transfer function (relative values) and
27#       draw the legend min/max values without waiting for the information to
28#       come from the server.  This will also prevent the flashing that occurs
29#       when a new volume is drawn (using the default transfer function) and
30#       then when the correct transfer function has been sent and linked to
31#       the volume.
32#
33option add *NanovisViewer.width 4i widgetDefault
34option add *NanovisViewer*cursor crosshair widgetDefault
35option add *NanovisViewer.height 4i widgetDefault
36option add *NanovisViewer.foreground black widgetDefault
37option add *NanovisViewer.controlBackground gray widgetDefault
38option add *NanovisViewer.controlDarkBackground #999999 widgetDefault
39option add *NanovisViewer.plotBackground black widgetDefault
40option add *NanovisViewer.plotForeground white widgetDefault
41option add *NanovisViewer.plotOutline gray widgetDefault
42option add *NanovisViewer.font \
43    -*-helvetica-medium-r-normal-*-12-* widgetDefault
44
45# must use this name -- plugs into Rappture::resources::load
46proc NanovisViewer_init_resources {} {
47    Rappture::resources::register \
48        nanovis_server Rappture::NanovisViewer::SetServerList
49}
50
51itcl::class Rappture::NanovisViewer {
52    inherit Rappture::VisViewer
53
54    itk_option define -plotforeground plotForeground Foreground ""
55    itk_option define -plotbackground plotBackground Background ""
56    itk_option define -plotoutline plotOutline PlotOutline ""
57
58    constructor { hostlist args } {
59        Rappture::VisViewer::constructor $hostlist
60    } {
61        # defined below
62    }
63    destructor {
64        # defined below
65    }
66    public proc SetServerList { namelist } {
67        Rappture::VisViewer::SetServerList "nanovis" $namelist
68    }
69    public method add {dataobj {settings ""}}
70    public method camera {option args}
71    public method delete {args}
72    public method disconnect {}
73    public method download {option args}
74    public method get {args}
75    public method isconnected {}
76    public method limits { tf }
77    public method parameters {title args} {
78        # do nothing
79    }
80    public method scale {args}
81    public method updateTransferFunctions {}
82
83    # The following methods are only used by this class.
84
85    private method AddNewMarker { x y }
86    private method AdjustSetting {what {value ""}}
87    private method BuildCameraTab {}
88    private method BuildCutplanesTab {}
89    private method BuildViewTab {}
90    private method BuildVolumeComponents {}
91    private method BuildVolumeTab {}
92    private method ComputeAlphamap { cname }
93    private method ComputeTransferFunction { cname }
94    private method Connect {}
95    private method CurrentDatasets {{what -all}}
96    private method Disconnect {}
97    private method DoResize {}
98    private method DrawLegend { cname }
99    private method EventuallyRedrawLegend { }
100    private method EventuallyResize { w h }
101    private method FixLegend {}
102    private method GetColormap { cname color }
103    private method GetDatasetsWithComponent { cname }
104    private method GetVolumeInfo { w }
105    private method HideAllMarkers {}
106    private method InitComponentSettings { cname }
107    private method InitSettings { args }
108    private method NameTransferFunction { dataobj comp }
109    private method Pan {option x y}
110    private method PanCamera {}
111    private method ParseLevelsOption { cname levels }
112    private method ParseMarkersOption { cname markers }
113    private method QuaternionToView { q } {
114        foreach { _view(-qw) _view(-qx) _view(-qy) _view(-qz) } $q break
115    }
116    private method Rebuild {}
117    private method ReceiveData { args }
118    private method ReceiveImage { args }
119    private method ReceiveLegend { tf vmin vmax size }
120    private method RemoveMarker { x y }
121    private method ResetColormap { cname color }
122    private method Rotate {option x y}
123    private method SendTransferFunctions {}
124    private method SetObjectStyle { dataobj cname }
125    private method SetOrientation { side }
126    private method Slice {option args}
127    private method SlicerTip {axis}
128    private method SwitchComponent { cname }
129    private method ToggleVolume { tag name }
130    private method ViewToQuaternion {} {
131        return [list $_view(-qw) $_view(-qx) $_view(-qy) $_view(-qz)]
132    }
133    private method Zoom {option}
134
135    private variable _arcball ""
136
137    private variable _dlist ""         ;# list of data objects
138    private variable _obj2ovride       ;# maps dataobj => style override
139    private variable _serverDatasets   ;# contains all the dataobj-component
140                                       ;# to volumes in the server
141    private variable _recvdDatasets;    # list of data objs to send to server
142    private variable _style2datasets;   # maps tf back to list of
143                                        # dataobj-components using the tf.
144
145    private variable _reset 1;          # Connection to server has been reset.
146    private variable _click;            # Info used for rotate operations.
147    private variable _limits;           # Autoscale min/max for all axes
148    private variable _view;             # View params for 3D view
149    private variable _parsedFunction
150    private variable _transferFunctionEditors
151    private variable _settings
152    private variable _widget
153
154    private variable _first "" ;        # This is the topmost volume.
155    private variable _current "";       # Currently selected component
156    private variable _volcomponents    ;# Array of components found
157    private variable _componentsList   ;# Array of components found
158    private variable _cname2transferFunction
159    private variable _cname2defaultcolormap
160
161    common _downloadPopup          ;# download options from popup
162    private common _hardcopy
163    private variable _width 0
164    private variable _height 0
165    private variable _resizePending 0
166    private variable _resizeLegendPending 0
167}
168
169itk::usual NanovisViewer {
170    keep -background -foreground -cursor -font
171    keep -plotbackground -plotforeground
172}
173
174# ----------------------------------------------------------------------
175# CONSTRUCTOR
176# ----------------------------------------------------------------------
177itcl::body Rappture::NanovisViewer::constructor {hostlist args} {
178    set _serverType "nanovis"
179
180    # Draw legend event
181    $_dispatcher register !legend
182    $_dispatcher dispatch $this !legend "[itcl::code $this FixLegend]; list"
183
184    # Send transfer functions event
185    $_dispatcher register !send_transfunc
186    $_dispatcher dispatch $this !send_transfunc \
187        "[itcl::code $this SendTransferFunctions]; list"
188
189    # Rebuild event
190    $_dispatcher register !rebuild
191    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
192
193    # Resize event
194    $_dispatcher register !resize
195    $_dispatcher dispatch $this !resize "[itcl::code $this DoResize]; list"
196
197    #
198    # Populate parser with commands handle incoming requests
199    #
200    $_parser alias image [itcl::code $this ReceiveImage]
201    $_parser alias legend [itcl::code $this ReceiveLegend]
202    $_parser alias data [itcl::code $this ReceiveData]
203
204    # Initialize the view to some default parameters.
205    array set _view {
206        -qw      0.853553
207        -qx      -0.353553
208        -qy      0.353553
209        -qz      0.146447
210        -xpan    0
211        -ypan    0
212        -zoom    1.0
213    }
214    set _arcball [blt::arcball create 100 100]
215    $_arcball quaternion [ViewToQuaternion]
216
217    set _limits(v) [list 0.0 1.0]
218    set _reset 1
219
220    array set _settings {
221        -axesvisible            1
222        -background             black
223        -colormap               "default"
224        -cutplanesvisible       0
225        -gridvisible            0
226        -isosurfaceshading      0
227        -legendvisible          1
228        -light                  40
229        -light2side             1
230        -outlinevisible         0
231        -qw                     0.853553
232        -qx                     -0.353553
233        -qy                     0.353553
234        -qz                     0.146447
235        -thickness              350
236        -volume                 1
237        -volumeopacity          0.5
238        -volumevisible          1
239        -xcutplaneposition      50
240        -xcutplanevisible       1
241        -xpan                   0
242        -ycutplaneposition      50
243        -ycutplanevisible       1
244        -ypan                   0
245        -zcutplaneposition      50
246        -zcutplanevisible       1
247        -zoom                   1.0
248    }
249    array set _widget {
250        -volumeopacity          50
251    }
252    itk_component add 3dview {
253        label $itk_component(plotarea).view -image $_image(plot) \
254            -highlightthickness 0 -borderwidth 0
255    } {
256        usual
257        ignore -highlightthickness -borderwidth  -background
258    }
259    bind $itk_component(3dview) <Control-F1> [itcl::code $this ToggleConsole]
260
261    set f [$itk_component(main) component controls]
262    itk_component add reset {
263        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
264            -highlightthickness 0 \
265            -image [Rappture::icon reset-view] \
266            -command [itcl::code $this Zoom reset]
267    } {
268        usual
269        ignore -highlightthickness
270    }
271    pack $itk_component(reset) -side top -padx 2 -pady 2
272    Rappture::Tooltip::for $itk_component(reset) \
273        "Reset the view to the default zoom level"
274
275    itk_component add zoomin {
276        button $f.zin -borderwidth 1 -padx 1 -pady 1 \
277            -highlightthickness 0 \
278            -image [Rappture::icon zoom-in] \
279            -command [itcl::code $this Zoom in]
280    } {
281        usual
282        ignore -highlightthickness
283    }
284    pack $itk_component(zoomin) -side top -padx 2 -pady 2
285    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
286
287    itk_component add zoomout {
288        button $f.zout -borderwidth 1 -padx 1 -pady 1 \
289            -highlightthickness 0 \
290            -image [Rappture::icon zoom-out] \
291            -command [itcl::code $this Zoom out]
292    } {
293        usual
294        ignore -highlightthickness
295    }
296    pack $itk_component(zoomout) -side top -padx 2 -pady 2
297    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
298
299    itk_component add volume {
300        Rappture::PushButton $f.volume \
301            -onimage [Rappture::icon volume-on] \
302            -offimage [Rappture::icon volume-off] \
303            -command [itcl::code $this AdjustSetting -volume] \
304            -variable [itcl::scope _settings(-volume)]
305    }
306    $itk_component(volume) select
307    Rappture::Tooltip::for $itk_component(volume) \
308        "Toggle the volume cloud on/off"
309    pack $itk_component(volume) -padx 2 -pady 2
310
311    itk_component add cutplane {
312        Rappture::PushButton $f.cutplane \
313            -onimage [Rappture::icon cutbutton] \
314            -offimage [Rappture::icon cutbutton] \
315            -variable [itcl::scope _settings(-cutplanesvisible)] \
316            -command [itcl::code $this AdjustSetting -cutplanesvisible]
317    }
318    Rappture::Tooltip::for $itk_component(cutplane) \
319        "Show/Hide cutplanes"
320    pack $itk_component(cutplane) -padx 2 -pady 2
321
322    if { [catch {
323        BuildViewTab
324        BuildVolumeTab
325        BuildCutplanesTab
326        BuildCameraTab
327    } errs] != 0 } {
328        global errorInfo
329        puts stderr "errs=$errs errorInfo=$errorInfo"
330    }
331
332    # Legend
333    set _image(legend) [image create photo]
334    itk_component add legend {
335        canvas $itk_component(plotarea).legend -height 50 -highlightthickness 0
336    } {
337        usual
338        ignore -highlightthickness
339        rename -background -plotbackground plotBackground Background
340    }
341    bind $itk_component(legend) <Configure> \
342        [itcl::code $this EventuallyRedrawLegend]
343    bind $itk_component(legend) <KeyPress-Delete> \
344        [itcl::code $this RemoveMarker %x %y]
345    bind $itk_component(legend) <Enter> \
346        [list focus $itk_component(legend)]
347
348    # Hack around the Tk panewindow.  The problem is that the requested
349    # size of the 3d view isn't set until an image is retrieved from
350    # the server.  So the panewindow uses the tiny size.
351    set w 10000
352    pack forget $itk_component(3dview)
353    blt::table $itk_component(plotarea) \
354        0,0 $itk_component(3dview) -fill both -reqwidth $w \
355        1,0 $itk_component(legend) -fill x
356    blt::table configure $itk_component(plotarea) r1 -resize none
357
358    # Bindings for rotation via mouse
359    bind $itk_component(3dview) <ButtonPress-1> \
360        [itcl::code $this Rotate click %x %y]
361    bind $itk_component(3dview) <B1-Motion> \
362        [itcl::code $this Rotate drag %x %y]
363    bind $itk_component(3dview) <ButtonRelease-1> \
364        [itcl::code $this Rotate release %x %y]
365    bind $itk_component(3dview) <Configure> \
366        [itcl::code $this EventuallyResize %w %h]
367
368    # Bindings for panning via mouse
369    bind $itk_component(3dview) <ButtonPress-2> \
370        [itcl::code $this Pan click %x %y]
371    bind $itk_component(3dview) <B2-Motion> \
372        [itcl::code $this Pan drag %x %y]
373    bind $itk_component(3dview) <ButtonRelease-2> \
374        [itcl::code $this Pan release %x %y]
375
376    # Bindings for panning via keyboard
377    bind $itk_component(3dview) <KeyPress-Left> \
378        [itcl::code $this Pan set -10 0]
379    bind $itk_component(3dview) <KeyPress-Right> \
380        [itcl::code $this Pan set 10 0]
381    bind $itk_component(3dview) <KeyPress-Up> \
382        [itcl::code $this Pan set 0 -10]
383    bind $itk_component(3dview) <KeyPress-Down> \
384        [itcl::code $this Pan set 0 10]
385    bind $itk_component(3dview) <Shift-KeyPress-Left> \
386        [itcl::code $this Pan set -2 0]
387    bind $itk_component(3dview) <Shift-KeyPress-Right> \
388        [itcl::code $this Pan set 2 0]
389    bind $itk_component(3dview) <Shift-KeyPress-Up> \
390        [itcl::code $this Pan set 0 -2]
391    bind $itk_component(3dview) <Shift-KeyPress-Down> \
392        [itcl::code $this Pan set 0 2]
393
394    # Bindings for zoom via keyboard
395    bind $itk_component(3dview) <KeyPress-Prior> \
396        [itcl::code $this Zoom out]
397    bind $itk_component(3dview) <KeyPress-Next> \
398        [itcl::code $this Zoom in]
399
400    bind $itk_component(3dview) <Enter> "focus $itk_component(3dview)"
401
402    if {[string equal "x11" [tk windowingsystem]]} {
403        # Bindings for zoom via mouse
404        bind $itk_component(3dview) <4> [itcl::code $this Zoom out]
405        bind $itk_component(3dview) <5> [itcl::code $this Zoom in]
406    }
407
408    set _image(download) [image create photo]
409
410    eval itk_initialize $args
411
412    EnableWaitDialog 900
413    Connect
414}
415
416# ----------------------------------------------------------------------
417# DESTRUCTOR
418# ----------------------------------------------------------------------
419itcl::body Rappture::NanovisViewer::destructor {} {
420    $_dispatcher cancel !rebuild
421    $_dispatcher cancel !send_transfunc
422    $_dispatcher cancel !resize
423    image delete $_image(plot)
424    image delete $_image(legend)
425    image delete $_image(download)
426    foreach cname [array names _transferFunctionEditors] {
427        itcl::delete object $_transferFunctionEditors($cname)
428    }
429    catch { blt::arcball destroy $_arcball }
430    array unset _settings
431}
432
433# ----------------------------------------------------------------------
434# USAGE: add <dataobj> ?<settings>?
435#
436# Clients use this to add a data object to the plot.  The optional
437# <settings> are used to configure the plot.  Allowed settings are
438# -color, -brightness, -width, -linestyle, and -raise.
439# ----------------------------------------------------------------------
440itcl::body Rappture::NanovisViewer::add {dataobj {settings ""}} {
441    if { ![$dataobj isvalid] } {
442        return;                         # Object doesn't contain valid data.
443    }
444    array set params {
445        -color auto
446        -width 1
447        -linestyle solid
448        -brightness 0
449        -raise 0
450        -description ""
451        -param ""
452    }
453    array set params $settings
454
455    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
456        # can't handle -autocolors yet
457        set params(-color) black
458    }
459    set pos [lsearch -exact $_dlist $dataobj]
460    if {$pos < 0} {
461        lappend _dlist $dataobj
462        set _obj2ovride($dataobj-color) $params(-color)
463        set _obj2ovride($dataobj-width) $params(-width)
464        set _obj2ovride($dataobj-raise) $params(-raise)
465        $_dispatcher event -idle !rebuild
466    }
467}
468
469# ----------------------------------------------------------------------
470# USAGE: get ?-objects?
471# USAGE: get ?-image 3dview|legend?
472#
473# Clients use this to query the list of objects being plotted, in
474# order from bottom to top of this result.  The optional "-image"
475# flag can also request the internal images being shown.
476# ----------------------------------------------------------------------
477itcl::body Rappture::NanovisViewer::get {args} {
478    if {[llength $args] == 0} {
479        set args "-objects"
480    }
481
482    set op [lindex $args 0]
483    switch -- $op {
484      -objects {
485        # put the dataobj list in order according to -raise options
486        set dlist $_dlist
487        foreach obj $dlist {
488            if {[info exists _obj2ovride($obj-raise)] && $_obj2ovride($obj-raise)} {
489                set i [lsearch -exact $dlist $obj]
490                if {$i >= 0} {
491                    set dlist [lreplace $dlist $i $i]
492                    lappend dlist $obj
493                }
494            }
495        }
496        return $dlist
497      }
498      -image {
499        if {[llength $args] != 2} {
500            error "wrong # args: should be \"get -image 3dview|legend\""
501        }
502        switch -- [lindex $args end] {
503            3dview {
504                return $_image(plot)
505            }
506            legend {
507                return $_image(legend)
508            }
509            default {
510                error "bad image name \"[lindex $args end]\": should be 3dview or legend"
511            }
512        }
513      }
514      default {
515        error "bad option \"$op\": should be -objects or -image"
516      }
517    }
518}
519
520# ----------------------------------------------------------------------
521# USAGE: delete ?<dataobj1> <dataobj2> ...?
522#
523#       Clients use this to delete a dataobj from the plot.  If no dataobjs
524#       are specified, then all dataobjs are deleted.  No data objects are
525#       deleted.  They are only removed from the display list.
526#
527# ----------------------------------------------------------------------
528itcl::body Rappture::NanovisViewer::delete {args} {
529    if {[llength $args] == 0} {
530        set args $_dlist
531    }
532    # Delete all specified dataobjs
533    set changed 0
534    foreach dataobj $args {
535        set pos [lsearch -exact $_dlist $dataobj]
536        if { $pos >= 0 } {
537            set _dlist [lreplace $_dlist $pos $pos]
538            array unset _obj2ovride $dataobj-*
539            set changed 1
540        }
541    }
542    # If anything changed, then rebuild the plot
543    if {$changed} {
544        $_dispatcher event -idle !rebuild
545    }
546}
547
548# ----------------------------------------------------------------------
549# USAGE: scale ?<data1> <data2> ...?
550#
551# Sets the default limits for the overall plot according to the
552# limits of the data for all of the given <data> objects.  This
553# accounts for all objects--even those not showing on the screen.
554# Because of this, the limits are appropriate for all objects as
555# the user scans through data in the ResultSet viewer.
556# ----------------------------------------------------------------------
557itcl::body Rappture::NanovisViewer::scale {args} {
558    array set style {
559        -color    BCGYR
560        -levels   6
561        -markers  ""
562    }
563    array unset _limits
564    array unset _volcomponents
565    foreach dataobj $args {
566        if { ![$dataobj isvalid] } {
567            continue;                     # Object doesn't contain valid data.
568        }
569        foreach cname [$dataobj components] {
570            if { ![info exists _volcomponents($cname)] } {
571                lappend _componentsList $cname
572                array set style [lindex [$dataobj components -style $cname] 0]
573                set cmap [ColorsToColormap $style(-color)]
574                set _cname2defaultcolormap($cname) $cmap
575                set _settings($cname-colormap) $style(-color)
576            }
577            lappend _volcomponents($cname) $dataobj-$cname
578            array unset limits
579            array set limits [$dataobj valueLimits $cname]
580            set _limits($cname) $limits(v)
581        }
582        foreach axis {x y z v} {
583            foreach { min max } [$dataobj limits $axis] break
584            if {"" != $min && "" != $max} {
585                if { ![info exists _limits($axis)] } {
586                    set _limits($axis) [list $min $max]
587                    continue
588                }
589                foreach {amin amax} $_limits($axis) break
590                if {$min < $amin} {
591                    set amin $min
592                }
593                if {$max > $amax} {
594                    set amax $max
595                }
596                set _limits($axis) [list $amin $amax]
597            }
598        }
599    }
600    BuildVolumeComponents
601}
602
603# ----------------------------------------------------------------------
604# USAGE: download coming
605# USAGE: download controls <downloadCommand>
606# USAGE: download now
607#
608# Clients use this method to create a downloadable representation
609# of the plot.  Returns a list of the form {ext string}, where
610# "ext" is the file extension (indicating the type of data) and
611# "string" is the data itself.
612# ----------------------------------------------------------------------
613itcl::body Rappture::NanovisViewer::download {option args} {
614    switch $option {
615        coming {
616            if {[catch {
617                blt::winop snap $itk_component(plotarea) $_image(download)
618            }]} {
619                $_image(download) configure -width 1 -height 1
620                $_image(download) put #000000
621            }
622        }
623        controls {
624            # no controls for this download yet
625            return ""
626        }
627        now {
628            # Get the image data (as base64) and decode it back to binary.
629            # This is better than writing to temporary files.  When we switch
630            # to the BLT picture image it won't be necessary to decode the
631            # image data.
632            if { [image width $_image(plot)] > 0 &&
633                 [image height $_image(plot)] > 0 } {
634                set bytes [$_image(plot) data -format "jpeg -quality 100"]
635                set bytes [Rappture::encoding::decode -as b64 $bytes]
636                return [list .jpg $bytes]
637            }
638            return ""
639        }
640        default {
641            error "bad option \"$option\": should be coming, controls, now"
642        }
643    }
644}
645
646# ----------------------------------------------------------------------
647# USAGE: Connect ?<host:port>,<host:port>...?
648#
649# Clients use this method to establish a connection to a new
650# server, or to reestablish a connection to the previous server.
651# Any existing connection is automatically closed.
652# ----------------------------------------------------------------------
653itcl::body Rappture::NanovisViewer::Connect {} {
654    set _hosts [GetServerList "nanovis"]
655    if { "" == $_hosts } {
656        return 0
657    }
658    set _reset 1
659    set result [VisViewer::Connect $_hosts]
660    if { $result } {
661        if { $_reportClientInfo }  {
662            # Tell the server the viewer, hub, user and session.
663            # Do this immediately on connect before buffering any commands
664            global env
665
666            set info {}
667            set user "???"
668            if { [info exists env(USER)] } {
669                set user $env(USER)
670            }
671            set session "???"
672            if { [info exists env(SESSION)] } {
673                set session $env(SESSION)
674            }
675            lappend info "version" "$Rappture::version"
676            lappend info "build" "$Rappture::build"
677            lappend info "svnurl" "$Rappture::svnurl"
678            lappend info "installdir" "$Rappture::installdir"
679            lappend info "hub" [exec hostname]
680            lappend info "client" "nanovisviewer"
681            lappend info "user" $user
682            lappend info "session" $session
683            SendCmd "clientinfo [list $info]"
684        }
685
686        set w [winfo width $itk_component(3dview)]
687        set h [winfo height $itk_component(3dview)]
688        EventuallyResize $w $h
689    }
690    return $result
691}
692
693#
694# isconnected --
695#
696#       Indicates if we are currently connected to the visualization server.
697#
698itcl::body Rappture::NanovisViewer::isconnected {} {
699    return [VisViewer::IsConnected]
700}
701
702#
703# disconnect --
704#
705itcl::body Rappture::NanovisViewer::disconnect {} {
706    Disconnect
707}
708
709#
710# Disconnect --
711#
712#       Clients use this method to disconnect from the current rendering
713#       server.
714#
715itcl::body Rappture::NanovisViewer::Disconnect {} {
716    VisViewer::Disconnect
717
718    # disconnected -- no more data sitting on server
719    array unset _serverDatasets
720}
721
722# ----------------------------------------------------------------------
723# USAGE: SendTransferFunctions
724# ----------------------------------------------------------------------
725itcl::body Rappture::NanovisViewer::SendTransferFunctions {} {
726    foreach cname [array names _volcomponents] {
727        ComputeTransferFunction $cname
728    }
729    FixLegend
730}
731
732# ----------------------------------------------------------------------
733# USAGE: ReceiveImage -bytes <size> -type <type> -token <token>
734#
735# Invoked automatically whenever the "image" command comes in from
736# the rendering server.  Indicates that binary image data with the
737# specified <size> will follow.
738# ----------------------------------------------------------------------
739itcl::body Rappture::NanovisViewer::ReceiveImage { args } {
740    array set info {
741        -token "???"
742        -bytes 0
743        -type image
744    }
745    array set info $args
746    set bytes [ReceiveBytes $info(-bytes)]
747    ReceiveEcho <<line "<read $info(-bytes) bytes"
748    if { $info(-type) == "image" } {
749        ReceiveEcho "for [image width $_image(plot)]x[image height $_image(plot)] image>"
750        $_image(plot) configure -data $bytes
751    } elseif { $info(-type) == "print" } {
752        set tag $this-print-$info(-token)
753        set _hardcopy($tag) $bytes
754    }
755}
756
757#
758# DrawLegend --
759#
760itcl::body Rappture::NanovisViewer::DrawLegend { cname } {
761    set c $itk_component(legend)
762    set w [winfo width $c]
763    set h [winfo height $c]
764    set lx 10
765    set ly [expr {$h - 1}]
766    if {"" == [$c find withtag colorbar]} {
767        $c create image 10 10 -anchor nw \
768            -image $_image(legend) -tags colorbar
769        $c create text $lx $ly -anchor sw \
770            -fill $itk_option(-plotforeground) -tags "limits text vmin"
771        $c create text [expr {$w-$lx}] $ly -anchor se \
772            -fill $itk_option(-plotforeground) -tags "limits text vmax"
773        $c create text [expr {$w/2}] $ly -anchor s \
774            -fill $itk_option(-plotforeground) -tags "limits text title"
775        $c lower colorbar
776        $c bind colorbar <ButtonRelease-1> [itcl::code $this AddNewMarker %x %y]
777    }
778
779    # Display the markers used by the current transfer function.
780    HideAllMarkers
781    $_transferFunctionEditors($cname) showMarkers $_limits($cname)
782
783    foreach {min max} $_limits($cname) break
784    $c itemconfigure vmin -text [format %g $min]
785    $c coords vmin $lx $ly
786
787    $c itemconfigure vmax -text [format %g $max]
788    $c coords vmax [expr {$w-$lx}] $ly
789
790    set title [$_first hints label]
791    set units [$_first hints units]
792    if { $units != "" } {
793        set title "$title ($units)"
794    }
795    $c itemconfigure title -text $title
796    $c coords title [expr {$w/2}] $ly
797
798    # The colormap may have changed. Resync the slicers with the colormap.
799    InitSettings -cutplanesvisible -xcutplanevisible -ycutplanevisible \
800        -zcutplanevisible
801}
802
803#
804#
805# ReceiveLegend --
806#
807#       The procedure is the response from the render server to each "legend"
808#       command.  The server sends back a "legend" command invoked our
809#       the slave interpreter.  The purpose is to collect data of the image
810#       representing the legend in the canvas.  In addition, the
811#       active transfer function is displayed.
812#
813#
814itcl::body Rappture::NanovisViewer::ReceiveLegend { cname vmin vmax size } {
815    if { ![isconnected] } {
816        return
817    }
818    set bytes [ReceiveBytes $size]
819    $_image(legend) configure -data $bytes
820    ReceiveEcho <<line "<read $size bytes for [image width $_image(legend)]x[image height $_image(legend)] legend>"
821
822    DrawLegend $_current
823}
824
825#
826# ReceiveData --
827#
828#       The procedure is the response from the render server to each "data
829#       follows" command.  The server sends back a "data" command invoked our
830#       the slave interpreter.  The purpose is to collect the min/max of the
831#       volume sent to the render server.  Since the client (nanovisviewer)
832#       doesn't parse 3D data formats, we rely on the server (nanovis) to
833#       tell us what the limits are.  Once we've received the limits to all
834#       the data we've sent (tracked by _recvdDatasets) we can then determine
835#       what the transfer functions are for these volumes.
836#
837#
838#       Note: There is a considerable tradeoff in having the server report
839#             back what the data limits are.  It means that much of the code
840#             having to do with transfer-functions has to wait for the data
841#             to come back, since the isomarkers are calculated based upon
842#             the data limits.  The client code is much messier because of
843#             this.  The alternative is to parse any of the 3D formats on the
844#             client side.
845#
846itcl::body Rappture::NanovisViewer::ReceiveData { args } {
847    if { ![isconnected] } {
848        return
849    }
850
851    # Arguments from server are name value pairs. Stuff them in an array.
852    array set info $args
853
854    set tag $info(tag)
855    set parts [split $tag -]
856
857    #
858    # Volumes don't exist until we're told about them.
859    #
860    set dataobj [lindex $parts 0]
861    set _serverDatasets($tag) 1
862    if { $_settings(-volumevisible) && $dataobj == $_first } {
863        SendCmd "volume state 1 $tag"
864    }
865    set _limits($tag) [list $info(min)  $info(max)]
866    set _limits(v)    [list $info(vmin) $info(vmax)]
867
868    unset _recvdDatasets($tag)
869    if { [array size _recvdDatasets] == 0 } {
870        updateTransferFunctions
871    }
872}
873
874# ----------------------------------------------------------------------
875# USAGE: Rebuild
876#
877# Called automatically whenever something changes that affects the
878# data in the widget.  Clears any existing data and rebuilds the
879# widget to display new data.
880# ----------------------------------------------------------------------
881itcl::body Rappture::NanovisViewer::Rebuild {} {
882    set w [winfo width $itk_component(3dview)]
883    set h [winfo height $itk_component(3dview)]
884    if { $w < 2 || $h < 2 } {
885        update
886        $_dispatcher event -idle !rebuild
887        return
888    }
889
890    # Turn on buffering of commands to the server.  We don't want to
891    # be preempted by a server disconnect/reconnect (which automatically
892    # generates a new call to Rebuild).
893    StartBufferingCommands
894
895    if { $_width != $w || $_height != $h || $_reset } {
896        set _width $w
897        set _height $h
898        $_arcball resize $w $h
899        DoResize
900    }
901
902    foreach dataobj [get] {
903        foreach cname [$dataobj components] {
904            set tag $dataobj-$cname
905            if { ![info exists _serverDatasets($tag)] } {
906                # Send the data as one huge base64-encoded mess -- yuck!
907                if { [$dataobj type] == "dx" } {
908                    if { ![$dataobj isvalid] } {
909                        puts stderr "??? $dataobj is invalid"
910                    }
911                    set data [$dataobj blob $cname]
912                } else {
913                    set data [$dataobj vtkdata $cname]
914                    if 0 {
915                        set f [open "/tmp/volume.vtk" "w"]
916                        fconfigure $f -translation binary -encoding binary
917                        puts -nonewline $f $data
918                        close $f
919                    }
920                }
921                set nbytes [string length $data]
922                if { $_reportClientInfo }  {
923                    set info {}
924                    lappend info "tool_id"       [$dataobj hints toolid]
925                    lappend info "tool_name"     [$dataobj hints toolname]
926                    lappend info "tool_title"    [$dataobj hints tooltitle]
927                    lappend info "tool_command"  [$dataobj hints toolcommand]
928                    lappend info "tool_revision" [$dataobj hints toolrevision]
929                    lappend info "dataset_label" [$dataobj hints label]
930                    lappend info "dataset_size"  $nbytes
931                    lappend info "dataset_tag"   $tag
932                    SendCmd "clientinfo [list $info]"
933                }
934                SendCmd "volume data follows $nbytes $tag"
935                SendData $data
936                set _recvdDatasets($tag) 1
937                set _serverDatasets($tag) 0
938            }
939            SetObjectStyle $dataobj $cname
940        }
941    }
942
943    # Outline seems to need to be reset every update.
944    InitSettings -outlinevisible -cutplanesvisible -current
945
946    set _first [lindex [get] 0]
947    if { $_reset } {
948        #
949        # Reset the camera and other view parameters
950        #
951        set _settings(-qw)    $_view(-qw)
952        set _settings(-qx)    $_view(-qx)
953        set _settings(-qy)    $_view(-qy)
954        set _settings(-qz)    $_view(-qz)
955        set _settings(-xpan)  $_view(-xpan)
956        set _settings(-ypan)  $_view(-ypan)
957        set _settings(-zoom)  $_view(-zoom)
958
959        set q [ViewToQuaternion]
960        $_arcball quaternion $q
961        SendCmd "camera orient $q"
962        SendCmd "camera reset"
963        PanCamera
964        SendCmd "camera zoom $_view(-zoom)"
965
966        # Turn off cutplanes for all volumes
967        foreach axis {x y z} {
968            SendCmd "cutplane state 0 $axis"
969        }
970
971        InitSettings -light2side -light -volumeopacity \
972            -isosurfaceshading -gridvisible -axesvisible \
973
974        if {"" != $_first} {
975            set axis [$_first hints updir]
976            if { "" != $axis } {
977                SendCmd "up $axis"
978            }
979            set location [$_first hints camera]
980            if { $location != "" } {
981                array set _view $location
982            }
983        }
984    }
985
986    # nothing to send -- activate the proper ivol
987    SendCmd "volume state 0"
988    if {"" != $_first} {
989        set datasets [array names _serverDatasets $_first-*]
990        if { $datasets != "" } {
991            SendCmd "volume state 1 $datasets"
992        }
993        # If the first volume already exists on the server, then make sure
994        # we display the proper transfer function in the legend.
995        set cname [lindex [$_first components] 0]
996        if { [info exists _serverDatasets($_first-$cname)] } {
997            updateTransferFunctions
998        }
999    }
1000    # Actually write the commands to the server socket.  If it fails, we don't
1001    # care.  We're finished here.
1002    blt::busy hold $itk_component(hull)
1003    StopBufferingCommands
1004    blt::busy release $itk_component(hull)
1005    set _reset 0
1006}
1007
1008# ----------------------------------------------------------------------
1009# USAGE: CurrentDatasets ?-cutplanes?
1010#
1011# Returns a list of volume server IDs for the current volume being
1012# displayed.  This is normally a single ID, but it might be a list
1013# of IDs if the current data object has multiple components.
1014# ----------------------------------------------------------------------
1015itcl::body Rappture::NanovisViewer::CurrentDatasets {{what -all}} {
1016    set rlist ""
1017    if { $_first == "" } {
1018        return
1019    }
1020    foreach cname [$_first components] {
1021        set tag $_first-$cname
1022        if { [info exists _serverDatasets($tag)] && $_serverDatasets($tag) } {
1023            array set style {
1024                -cutplanes 1
1025            }
1026            array set style [lindex [$_first components -style $cname] 0]
1027            if { $what != "-cutplanes" || $style(-cutplanes) } {
1028                lappend rlist $tag
1029            }
1030        }
1031    }
1032    return $rlist
1033}
1034
1035# ----------------------------------------------------------------------
1036# USAGE: Zoom in
1037# USAGE: Zoom out
1038# USAGE: Zoom reset
1039#
1040# Called automatically when the user clicks on one of the zoom
1041# controls for this widget.  Changes the zoom for the current view.
1042# ----------------------------------------------------------------------
1043itcl::body Rappture::NanovisViewer::Zoom {option} {
1044    switch -- $option {
1045        "in" {
1046            set _view(-zoom) [expr {$_view(-zoom)*1.25}]
1047            set _settings(-zoom) $_view(-zoom)
1048            SendCmd "camera zoom $_view(-zoom)"
1049        }
1050        "out" {
1051            set _view(-zoom) [expr {$_view(-zoom)*0.8}]
1052            set _settings(-zoom) $_view(-zoom)
1053            SendCmd "camera zoom $_view(-zoom)"
1054        }
1055        "reset" {
1056            array set _view {
1057                -qw      0.853553
1058                -qx      -0.353553
1059                -qy      0.353553
1060                -qz      0.146447
1061                -xpan    0
1062                -ypan    0
1063                -zoom    1.0
1064            }
1065            if { $_first != "" } {
1066                set location [$_first hints camera]
1067                if { $location != "" } {
1068                    array set _view $location
1069                }
1070            }
1071            set q [ViewToQuaternion]
1072            $_arcball quaternion $q
1073            SendCmd "camera orient $q"
1074            SendCmd "camera reset"
1075            set _settings(-qw)    $_view(-qw)
1076            set _settings(-qx)    $_view(-qx)
1077            set _settings(-qy)    $_view(-qy)
1078            set _settings(-qz)    $_view(-qz)
1079            set _settings(-xpan)  $_view(-xpan)
1080            set _settings(-ypan)  $_view(-ypan)
1081            set _settings(-zoom)  $_view(-zoom)
1082        }
1083    }
1084}
1085
1086itcl::body Rappture::NanovisViewer::PanCamera {} {
1087    set x $_view(-xpan)
1088    set y $_view(-ypan)
1089    SendCmd "camera pan $x $y"
1090}
1091
1092
1093# ----------------------------------------------------------------------
1094# USAGE: Rotate click <x> <y>
1095# USAGE: Rotate drag <x> <y>
1096# USAGE: Rotate release <x> <y>
1097#
1098# Called automatically when the user clicks/drags/releases in the
1099# plot area.  Moves the plot according to the user's actions.
1100# ----------------------------------------------------------------------
1101itcl::body Rappture::NanovisViewer::Rotate {option x y} {
1102    switch -- $option {
1103        click {
1104            $itk_component(3dview) configure -cursor fleur
1105            set _click(x) $x
1106            set _click(y) $y
1107        }
1108        drag {
1109            if {[array size _click] == 0} {
1110                Rotate click $x $y
1111            } else {
1112                set w [winfo width $itk_component(3dview)]
1113                set h [winfo height $itk_component(3dview)]
1114                if {$w <= 0 || $h <= 0} {
1115                    return
1116                }
1117
1118                if {[catch {
1119                    # this fails sometimes for no apparent reason
1120                    set dx [expr {double($x-$_click(x))/$w}]
1121                    set dy [expr {double($y-$_click(y))/$h}]
1122                }]} {
1123                    return
1124                }
1125
1126                set q [$_arcball rotate $x $y $_click(x) $_click(y)]
1127                QuaternionToView $q
1128                set _settings(-qw) $_view(-qw)
1129                set _settings(-qx) $_view(-qx)
1130                set _settings(-qy) $_view(-qy)
1131                set _settings(-qz) $_view(-qz)
1132                SendCmd "camera orient $q"
1133
1134                set _click(x) $x
1135                set _click(y) $y
1136            }
1137        }
1138        release {
1139            Rotate drag $x $y
1140            $itk_component(3dview) configure -cursor ""
1141            catch {unset _click}
1142        }
1143        default {
1144            error "bad option \"$option\": should be click, drag, release"
1145        }
1146    }
1147}
1148
1149# ----------------------------------------------------------------------
1150# USAGE: $this Pan click x y
1151#        $this Pan drag x y
1152#        $this Pan release x y
1153#
1154# Called automatically when the user clicks on one of the zoom
1155# controls for this widget.  Changes the zoom for the current view.
1156# ----------------------------------------------------------------------
1157itcl::body Rappture::NanovisViewer::Pan {option x y} {
1158    # Experimental stuff
1159    set w [winfo width $itk_component(3dview)]
1160    set h [winfo height $itk_component(3dview)]
1161    if { $option == "set" } {
1162        set x [expr $x / double($w)]
1163        set y [expr $y / double($h)]
1164        set _view(-xpan) [expr $_view(-xpan) + $x]
1165        set _view(-ypan) [expr $_view(-ypan) + $y]
1166        PanCamera
1167        set _settings(-xpan) $_view(-xpan)
1168        set _settings(-ypan) $_view(-ypan)
1169        return
1170    }
1171    if { $option == "click" } {
1172        set _click(x) $x
1173        set _click(y) $y
1174        $itk_component(3dview) configure -cursor hand1
1175    }
1176    if { $option == "drag" || $option == "release" } {
1177        set dx [expr ($_click(x) - $x)/double($w)]
1178        set dy [expr ($_click(y) - $y)/double($h)]
1179        set _click(x) $x
1180        set _click(y) $y
1181        set _view(-xpan) [expr $_view(-xpan) - $dx]
1182        set _view(-ypan) [expr $_view(-ypan) - $dy]
1183        PanCamera
1184        set _settings(-xpan) $_view(-xpan)
1185        set _settings(-ypan) $_view(-ypan)
1186    }
1187    if { $option == "release" } {
1188        $itk_component(3dview) configure -cursor ""
1189    }
1190}
1191
1192# ----------------------------------------------------------------------
1193# USAGE: InitSettings <what> ?<value>?
1194#
1195# Used internally to update rendering settings whenever parameters
1196# change in the popup settings panel.  Sends the new settings off
1197# to the back end.
1198# ----------------------------------------------------------------------
1199itcl::body Rappture::NanovisViewer::InitSettings { args } {
1200    foreach arg $args {
1201        AdjustSetting $arg
1202    }
1203}
1204
1205# ----------------------------------------------------------------------
1206# USAGE: AdjustSetting <what> ?<value>?
1207#
1208# Used internally to update rendering settings whenever parameters
1209# change in the popup settings panel.  Sends the new settings off
1210# to the back end.
1211# ----------------------------------------------------------------------
1212itcl::body Rappture::NanovisViewer::AdjustSetting {what {value ""}} {
1213    if {![isconnected]} {
1214        return
1215    }
1216    switch -- $what {
1217        "-axesvisible" {
1218            SendCmd "axis visible $_settings($what)"
1219        }
1220        "-background" {
1221            set bgcolor [$itk_component(background) value]
1222            array set fgcolors {
1223                "black" "white"
1224                "white" "black"
1225                "grey"  "black"
1226            }
1227            configure -plotbackground $bgcolor \
1228                -plotforeground $fgcolors($bgcolor)
1229            DrawLegend $_current
1230        }
1231        "-colormap" {
1232            set color [$itk_component(colormap) value]
1233            set _settings($what) $color
1234            set _settings($_current${what}) $color
1235            ResetColormap $_current $color
1236        }
1237        "-current" {
1238            set cname [$itk_component(volcomponents) value]
1239            SwitchComponent $cname
1240        }
1241        "-cutplanesvisible" {
1242            set bool $_settings($what)
1243            # We only set cutplanes on the first dataset.
1244            set datasets [CurrentDatasets -cutplanes]
1245            set tag [lindex $datasets 0]
1246            SendCmd "cutplane visible $bool $tag"
1247        }
1248        "-gridvisible" {
1249            SendCmd "grid visible $_settings($what)"
1250        }
1251        "-isosurfaceshading" {
1252            SendCmd "volume shading isosurface $_settings($what)"
1253        }
1254        "-legendvisible" {
1255            if { $_settings($what) } {
1256                blt::table $itk_component(plotarea) \
1257                    0,0 $itk_component(3dview) -fill both \
1258                    1,0 $itk_component(legend) -fill x
1259                blt::table configure $itk_component(plotarea) r1 -resize none
1260            } else {
1261                blt::table forget $itk_component(legend)
1262            }
1263        }
1264        "-light" {
1265            set _settings($_current${what}) $_settings($what)
1266            set val $_settings($what)
1267            set diffuse [expr {0.01*$val}]
1268            set ambient [expr {1.0-$diffuse}]
1269            set specularLevel 0.3
1270            set specularExp 90.0
1271            foreach tag [GetDatasetsWithComponent $_current] {
1272                SendCmd "volume shading ambient $ambient $tag"
1273                SendCmd "volume shading diffuse $diffuse $tag"
1274                SendCmd "volume shading specularLevel $specularLevel $tag"
1275                SendCmd "volume shading specularExp $specularExp $tag"
1276            }
1277        }
1278        "-light2side" {
1279            set _settings($_current${what}) $_settings($what)
1280            set val $_settings($what)
1281            foreach tag [GetDatasetsWithComponent $_current] {
1282                SendCmd "volume shading light2side $val $tag"
1283            }
1284        }
1285        "-outlinevisible" {
1286            SendCmd "volume outline state $_settings($what)"
1287        }
1288        "-thickness" {
1289            set val $_settings($what)
1290            set _settings($_current${what}) $val
1291            updateTransferFunctions
1292        }
1293        "-volume" {
1294            # This is the global volume visibility control.  It controls the
1295            # visibility of all the all volumes.  Whenever it's changed, you
1296            # have to synchronize each of the local controls (see below) with
1297            # this.
1298            set datasets [CurrentDatasets]
1299            set bool $_settings($what)
1300            SendCmd "volume data state $bool $datasets"
1301            foreach cname $_componentsList {
1302                set _settings($cname-volumevisible) $bool
1303            }
1304            set _settings(-volumevisible) $bool
1305        }
1306        "-volumeopacity" {
1307            set _settings($what) [expr $_widget($what) * 0.01]
1308            set _settings($_current${what}) $_settings($what)
1309            foreach tag [GetDatasetsWithComponent $_current] {
1310                SendCmd "volume shading opacity $_settings($what) $tag"
1311            }
1312        }
1313        "-volumevisible" {
1314            # This is the component specific control.  It changes the
1315            # visibility of only the current component.
1316            set _settings($_current${what}) $_settings($what)
1317            foreach tag [GetDatasetsWithComponent $_current] {
1318                SendCmd "volume data state $_settings($what) $tag"
1319            }
1320        }
1321        "-xcutplanevisible" - "-ycutplanevisible" - "-zcutplanevisible" {
1322            set axis [string range $what 1 1]
1323            set bool $_settings($what)
1324            # We only set cutplanes on the first dataset.
1325            set datasets [CurrentDatasets -cutplanes]
1326            set tag [lindex $datasets 0]
1327            SendCmd "cutplane state $bool $axis $tag"
1328            if { $bool } {
1329                $itk_component(${axis}CutScale) configure -state normal \
1330                    -troughcolor white
1331            } else {
1332                $itk_component(${axis}CutScale) configure -state disabled \
1333                    -troughcolor grey82
1334            }
1335        }
1336        default {
1337            error "don't know how to fix $what"
1338        }
1339    }
1340}
1341
1342# ----------------------------------------------------------------------
1343# USAGE: FixLegend
1344#
1345# Used internally to update the legend area whenever it changes size
1346# or when the field changes.  Asks the server to send a new legend
1347# for the current field.
1348# ----------------------------------------------------------------------
1349itcl::body Rappture::NanovisViewer::FixLegend {} {
1350    set _resizeLegendPending 0
1351    set lineht [font metrics $itk_option(-font) -linespace]
1352    set w [expr {$_width-20}]
1353    set h [expr {[winfo height $itk_component(legend)]-20-$lineht}]
1354    if {$w > 0 && $h > 0 && $_first != "" } {
1355        if { [info exists _cname2transferFunction($_current)] } {
1356            SendCmd "legend $_current $w $h"
1357        }
1358    }
1359}
1360
1361#
1362# NameTransferFunction --
1363#
1364#       Creates a transfer function name based on the <style> settings in the
1365#       library run.xml file. This placeholder will be used later to create
1366#       and send the actual transfer function once the data info has been sent
1367#       to us by the render server. [We won't know the volume limits until the
1368#       server parses the 3D data and sends back the limits via ReceiveData.]
1369#
1370itcl::body Rappture::NanovisViewer::NameTransferFunction { dataobj cname } {
1371    array set style {
1372        -color BCGYR
1373        -levels 6
1374        -markers ""
1375    }
1376    set tag $dataobj-$cname
1377    array set style [lindex [$dataobj components -style $cname] 0]
1378    if { ![info exists _cname2transferFunction($cname)] } {
1379        # Get the colormap right now, since it doesn't change with marker
1380        # changes.
1381        set cmap [ColorsToColormap $style(-color)]
1382        set amap [list 0.0 0.0 1.0 1.0]
1383        set _cname2transferFunction($cname) [list $cmap $amap]
1384        SendCmd [list transfunc define $cname $cmap $amap]
1385    }
1386    SendCmd "volume shading transfunc $cname $tag"
1387    if { ![info exists _transferFunctionEditors($cname)] } {
1388        set _transferFunctionEditors($cname) \
1389            [Rappture::TransferFunctionEditor ::\#auto $itk_component(legend) \
1390                 $cname \
1391                 -command [itcl::code $this updateTransferFunctions]]
1392    }
1393    lappend _style2datasets($cname) $tag
1394    return $cname
1395}
1396
1397#
1398# ComputeTransferFunction --
1399#
1400#       Computes and sends the transfer function to the render server.  It's
1401#       assumed that the volume data limits are known and that the global
1402#       transfer-functions slider values have been set up.  Both parts are
1403#       needed to compute the relative value (location) of the marker, and
1404#       the alpha map of the transfer function.
1405#
1406itcl::body Rappture::NanovisViewer::ComputeTransferFunction { cname } {
1407    foreach {cmap amap} $_cname2transferFunction($cname) break
1408
1409    # We have to parse the style attributes for a volume using this
1410    # transfer-function *once*.  This sets up the initial isomarkers for the
1411    # transfer function.  The user may add/delete markers, so we have to
1412    # maintain a list of markers for each transfer-function.  We use the one
1413    # of the volumes (the first in the list) using the transfer-function as a
1414    # reference.
1415    if { ![info exists _parsedFunction($cname)] } {
1416        array set style {
1417            -color BCGYR
1418            -levels 6
1419            -markers ""
1420        }
1421        # Accumulate the style from all the datasets using it.
1422        foreach tag [GetDatasetsWithComponent $cname] {
1423            foreach {dataobj cname} [split [lindex $tag 0] -] break
1424            array set style [lindex [$dataobj components -style $cname] 0]
1425        }
1426        eval $_transferFunctionEditors($cname) limits $_limits($cname)
1427        # Have to defer creation of isomarkers until we have data limits
1428        if { [info exists style(-markers)] &&
1429             [llength $style(-markers)] > 0 } {
1430            ParseMarkersOption $cname $style(-markers)
1431        } else {
1432            ParseLevelsOption $cname $style(-levels)
1433        }
1434
1435    }
1436    set amap [ComputeAlphamap $cname]
1437    set _cname2transferFunction($cname) [list $cmap $amap]
1438    SendCmd [list transfunc define $cname $cmap $amap]
1439}
1440
1441itcl::body Rappture::NanovisViewer::AddNewMarker { x y } {
1442    if { ![info exists _transferFunctionEditors($_current)] } {
1443        continue
1444    }
1445    # Add a new marker to the current transfer function
1446    $_transferFunctionEditors($_current) newMarker $x $y normal
1447    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1448}
1449
1450itcl::body Rappture::NanovisViewer::RemoveMarker { x y } {
1451    if { ![info exists _transferFunctionEditors($_current)] } {
1452        continue
1453    }
1454    # Add a new marker to the current transfer function
1455    $_transferFunctionEditors($_current) deleteMarker $x $y
1456}
1457
1458# ----------------------------------------------------------------------
1459# CONFIGURATION OPTION: -plotbackground
1460# ----------------------------------------------------------------------
1461itcl::configbody Rappture::NanovisViewer::plotbackground {
1462    if { [isconnected] } {
1463        set color $itk_option(-plotbackground)
1464        set rgb [Color2RGB $color]
1465        SendCmd "screen bgcolor $rgb"
1466        $itk_component(legend) configure -background $color
1467    }
1468}
1469
1470# ----------------------------------------------------------------------
1471# CONFIGURATION OPTION: -plotforeground
1472# ----------------------------------------------------------------------
1473itcl::configbody Rappture::NanovisViewer::plotforeground {
1474    if { [isconnected] } {
1475        set color $itk_option(-plotforeground)
1476        set rgb [Color2RGB $color]
1477        SendCmd "volume outline color $rgb"
1478        SendCmd "grid axiscolor $rgb"
1479        SendCmd "grid linecolor $rgb"
1480        $itk_component(legend) itemconfigure labels -fill $color
1481        $itk_component(legend) itemconfigure limits -fill $color
1482    }
1483}
1484
1485# ----------------------------------------------------------------------
1486# CONFIGURATION OPTION: -plotoutline
1487# ----------------------------------------------------------------------
1488itcl::configbody Rappture::NanovisViewer::plotoutline {
1489    # Must check if we are connected because this routine is called from the
1490    # class body when the -plotoutline itk_option is defined.  At that point
1491    # the NanovisViewer class constructor hasn't been called, so we can't
1492    # start sending commands to visualization server.
1493    if { [isconnected] } {
1494        if {"" == $itk_option(-plotoutline)} {
1495            SendCmd "volume outline state off"
1496        } else {
1497            SendCmd "volume outline state on"
1498            SendCmd "volume outline color [Color2RGB $itk_option(-plotoutline)]"
1499        }
1500    }
1501}
1502
1503#
1504# The -levels option takes a single value that represents the number
1505# of evenly distributed markers based on the current data range. Each
1506# marker is a relative value from 0.0 to 1.0.
1507#
1508itcl::body Rappture::NanovisViewer::ParseLevelsOption { cname levels } {
1509    set c $itk_component(legend)
1510    set list {}
1511    regsub -all "," $levels " " levels
1512    if {[string is int $levels]} {
1513        for {set i 1} { $i <= $levels } {incr i} {
1514            lappend list [expr {double($i)/($levels+1)}]
1515        }
1516    } else {
1517        foreach x $levels {
1518            lappend list $x
1519        }
1520    }
1521    set _parsedFunction($cname) 1
1522    $_transferFunctionEditors($cname) addMarkers $list
1523    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1524}
1525
1526#
1527# The -markers option takes a list of zero or more values (the values
1528# may be separated either by spaces or commas) that have the following
1529# format:
1530#
1531#   N%  Percent of current total data range.  Converted to
1532#       to a relative value between 0.0 and 1.0.
1533#   N   Absolute value of marker.  If the marker is outside of
1534#       the current range, it will be displayed on the outer
1535#       edge of the legends, but it range it represents will
1536#       not be seen.
1537#
1538itcl::body Rappture::NanovisViewer::ParseMarkersOption { cname markers } {
1539    set c $itk_component(legend)
1540    set list {}
1541    foreach { min max } $_limits($cname) break
1542    regsub -all "," $markers " " markers
1543    foreach marker $markers {
1544        set n [scan $marker "%g%s" value suffix]
1545        if { $n == 2 && $suffix == "%" } {
1546            # $n% : Set relative value (0..1).
1547            lappend list [expr {$value * 0.01}]
1548        } else {
1549            # $n : absolute value, compute relative
1550            lappend list  [expr {(double($value)-$min)/($max-$min)]}
1551        }
1552    }
1553    set _parsedFunction($cname) 1
1554    $_transferFunctionEditors($cname) addMarkers $list
1555    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1556}
1557
1558# ----------------------------------------------------------------------
1559# USAGE: UndateTransferFuncs
1560# ----------------------------------------------------------------------
1561itcl::body Rappture::NanovisViewer::updateTransferFunctions {} {
1562    $_dispatcher event -idle !send_transfunc
1563}
1564
1565itcl::body Rappture::NanovisViewer::limits { cname } {
1566    set _limits(min) 0.0
1567    set _limits(max) 1.0
1568    if { ![info exists _style2datasets($cname)] } {
1569        return [array get _limits]
1570    }
1571    set min ""; set max ""
1572    foreach tag [GetDatasetsWithComponent $cname] {
1573        if { ![info exists _limits($tag)] } {
1574            continue
1575        }
1576        foreach {amin amax} $_limits($tag) break
1577        if { $min == "" || $min > $amin } {
1578            set min $amin
1579        }
1580        if { $max == "" || $max < $amax } {
1581            set max $amax
1582        }
1583    }
1584    if { $min != "" } {
1585        set _limits(min) $min
1586    }
1587    if { $max != "" } {
1588        set _limits(max) $max
1589    }
1590    return [list $_limits(min) $_limits(max)]
1591}
1592
1593
1594itcl::body Rappture::NanovisViewer::BuildViewTab {} {
1595    set fg [option get $itk_component(hull) font Font]
1596    #set bfg [option get $itk_component(hull) boldFont Font]
1597
1598    set inner [$itk_component(main) insert end \
1599        -title "View Settings" \
1600        -icon [Rappture::icon wrench]]
1601    $inner configure -borderwidth 4
1602
1603    set ::Rappture::NanovisViewer::_settings(-isosurfaceshading) 0
1604    checkbutton $inner.isosurface \
1605        -text "Isosurface shading" \
1606        -variable [itcl::scope _settings(-isosurfaceshading)] \
1607        -command [itcl::code $this AdjustSetting -isosurfaceshading] \
1608        -font "Arial 9"
1609
1610    checkbutton $inner.axes \
1611        -text "Axes" \
1612        -variable [itcl::scope _settings(-axesvisible)] \
1613        -command [itcl::code $this AdjustSetting -axesvisible] \
1614        -font "Arial 9"
1615
1616    checkbutton $inner.grid \
1617        -text "Grid" \
1618        -variable [itcl::scope _settings(-gridvisible)] \
1619        -command [itcl::code $this AdjustSetting -gridvisible] \
1620        -font "Arial 9"
1621
1622    checkbutton $inner.outline \
1623        -text "Outline" \
1624        -variable [itcl::scope _settings(-outlinevisible)] \
1625        -command [itcl::code $this AdjustSetting -outlinevisible] \
1626        -font "Arial 9"
1627
1628    checkbutton $inner.legend \
1629        -text "Legend" \
1630        -variable [itcl::scope _settings(-legendvisible)] \
1631        -command [itcl::code $this AdjustSetting -legendvisible] \
1632        -font "Arial 9"
1633
1634    checkbutton $inner.volume \
1635        -text "Volume" \
1636        -variable [itcl::scope _settings(-volume)] \
1637        -command [itcl::code $this AdjustSetting -volume] \
1638        -font "Arial 9"
1639
1640    label $inner.background_l -text "Background" -font "Arial 9"
1641    itk_component add background {
1642        Rappture::Combobox $inner.background -width 10 -editable no
1643    }
1644    $inner.background choices insert end \
1645        "black"              "black"            \
1646        "white"              "white"            \
1647        "grey"               "grey"
1648
1649    $itk_component(background) value $_settings(-background)
1650    bind $inner.background <<Value>> \
1651        [itcl::code $this AdjustSetting -background]
1652
1653    blt::table $inner \
1654        0,0 $inner.axes  -cspan 2 -anchor w \
1655        1,0 $inner.grid  -cspan 2 -anchor w \
1656        2,0 $inner.outline  -cspan 2 -anchor w \
1657        3,0 $inner.volume  -cspan 2 -anchor w \
1658        4,0 $inner.legend  -cspan 2 -anchor w \
1659        5,0 $inner.background_l       -anchor e -pady 2 \
1660        5,1 $inner.background                   -fill x \
1661
1662    if 0 {
1663    bind $inner <Map> [itcl::code $this GetVolumeInfo $inner]
1664    }
1665    blt::table configure $inner r* -resize none
1666    blt::table configure $inner r6 -resize expand
1667}
1668
1669itcl::body Rappture::NanovisViewer::BuildVolumeTab {} {
1670    set inner [$itk_component(main) insert end \
1671        -title "Volume Settings" \
1672        -icon [Rappture::icon volume-on]]
1673    $inner configure -borderwidth 4
1674
1675    set fg [option get $itk_component(hull) font Font]
1676    #set bfg [option get $itk_component(hull) boldFont Font]
1677
1678    label $inner.shading -text "Shading:" -font $fg
1679
1680    checkbutton $inner.light2side -text "Two-sided lighting" -font $fg \
1681        -variable [itcl::scope _settings(-light2side)] \
1682        -command [itcl::code $this AdjustSetting -light2side]
1683
1684    checkbutton $inner.visibility -text "Visible" -font $fg \
1685        -variable [itcl::scope _settings(-volumevisible)] \
1686        -command [itcl::code $this AdjustSetting -volumevisible]
1687
1688    label $inner.dim -text "Glow" -font $fg
1689    ::scale $inner.light -from 0 -to 100 -orient horizontal \
1690        -variable [itcl::scope _settings(-light)] \
1691        -width 10 \
1692        -showvalue off -command [itcl::code $this AdjustSetting -light]
1693    label $inner.bright -text "Surface" -font $fg
1694
1695    # Opacity
1696    label $inner.fog -text "Clear" -font $fg
1697    ::scale $inner.transp -from 0 -to 100 -orient horizontal \
1698        -variable [itcl::scope _widget(-volumeopacity)] \
1699        -width 10 \
1700        -showvalue off -command [itcl::code $this AdjustSetting -volumeopacity]
1701    label $inner.plastic -text "Opaque" -font $fg
1702
1703    # Tooth thickness
1704    label $inner.thin -text "Thin" -font $fg
1705    ::scale $inner.thickness -from 0 -to 1000 -orient horizontal \
1706        -variable [itcl::scope _settings(-thickness)] \
1707        -width 10 \
1708        -showvalue off -command [itcl::code $this AdjustSetting -thickness]
1709    label $inner.thick -text "Thick" -font $fg
1710
1711    # Colormap
1712    label $inner.colormap_l -text "Colormap" -font $fg
1713    itk_component add colormap {
1714        Rappture::Combobox $inner.colormap -width 10 -editable no
1715    }
1716
1717    $inner.colormap choices insert end [GetColormapList -includeDefault -includeNone]
1718    bind $inner.colormap <<Value>> \
1719        [itcl::code $this AdjustSetting -colormap]
1720    $itk_component(colormap) value "default"
1721    set _settings(-colormap) "default"
1722
1723    # Component
1724    label $inner.volcomponents_l -text "Component" -font $fg
1725    itk_component add volcomponents {
1726        Rappture::Combobox $inner.volcomponents -editable no
1727    }
1728    bind $inner.volcomponents <<Value>> \
1729        [itcl::code $this AdjustSetting -current]
1730
1731    blt::table $inner \
1732        0,0 $inner.volcomponents_l -anchor e -cspan 2 \
1733        0,2 $inner.volcomponents             -cspan 3 -fill x \
1734        1,0 $inner.shading -cspan 4 -anchor w -pady {10 2} \
1735        2,0 $inner.light2side -cspan 4 -anchor w -pady 2 \
1736        3,0 $inner.visibility -cspan 4 -anchor w -pady 2 \
1737        4,0 $inner.dim -anchor e -pady 2 \
1738        4,1 $inner.light -cspan 2 -pady 2 -fill x \
1739        4,3 $inner.bright -anchor w -pady 2 \
1740        5,0 $inner.fog -anchor e -pady 2 \
1741        5,1 $inner.transp -cspan 2 -pady 2 -fill x \
1742        5,3 $inner.plastic -anchor w -pady 2 \
1743        6,0 $inner.thin -anchor e -pady 2 \
1744        6,1 $inner.thickness -cspan 2 -pady 2 -fill x\
1745        6,3 $inner.thick -anchor w -pady 2
1746
1747    blt::table configure $inner c0 c1 c3 r* -resize none
1748    blt::table configure $inner r7 -resize expand
1749}
1750
1751itcl::body Rappture::NanovisViewer::BuildCutplanesTab {} {
1752    set inner [$itk_component(main) insert end \
1753        -title "Cutplane Settings" \
1754        -icon [Rappture::icon cutbutton]]
1755    $inner configure -borderwidth 4
1756
1757    checkbutton $inner.visible \
1758        -text "Show Cutplanes" \
1759        -variable [itcl::scope _settings(-cutplanesvisible)] \
1760        -command [itcl::code $this AdjustSetting -cutplanesvisible] \
1761        -font "Arial 9"
1762
1763    # X-value slicer...
1764    itk_component add xCutButton {
1765        Rappture::PushButton $inner.xbutton \
1766            -onimage [Rappture::icon x-cutplane] \
1767            -offimage [Rappture::icon x-cutplane] \
1768            -command [itcl::code $this AdjustSetting -xcutplanevisible] \
1769            -variable [itcl::scope _settings(-xcutplanevisible)]
1770    }
1771    Rappture::Tooltip::for $itk_component(xCutButton) \
1772        "Toggle the X cut plane on/off"
1773    $itk_component(xCutButton) select
1774
1775    itk_component add xCutScale {
1776        ::scale $inner.xval -from 100 -to 0 \
1777            -width 10 -orient vertical -showvalue off \
1778            -borderwidth 1 -highlightthickness 0 \
1779            -command [itcl::code $this Slice move x] \
1780            -variable [itcl::scope _settings(-xcutplaneposition)]
1781    } {
1782        usual
1783        ignore -borderwidth -highlightthickness
1784    }
1785    # Set the default cutplane value before disabling the scale.
1786    $itk_component(xCutScale) set 50
1787    $itk_component(xCutScale) configure -state disabled
1788    Rappture::Tooltip::for $itk_component(xCutScale) \
1789        "@[itcl::code $this SlicerTip x]"
1790
1791    # Y-value slicer...
1792    itk_component add yCutButton {
1793        Rappture::PushButton $inner.ybutton \
1794            -onimage [Rappture::icon y-cutplane] \
1795            -offimage [Rappture::icon y-cutplane] \
1796            -command [itcl::code $this AdjustSetting -ycutplanevisible] \
1797            -variable [itcl::scope _settings(-ycutplanevisible)]
1798    }
1799    Rappture::Tooltip::for $itk_component(yCutButton) \
1800        "Toggle the Y cut plane on/off"
1801    $itk_component(yCutButton) select
1802
1803    itk_component add yCutScale {
1804        ::scale $inner.yval -from 100 -to 0 \
1805            -width 10 -orient vertical -showvalue off \
1806            -borderwidth 1 -highlightthickness 0 \
1807            -command [itcl::code $this Slice move y] \
1808            -variable [itcl::scope _settings(-ycutplaneposition)]
1809    } {
1810        usual
1811        ignore -borderwidth -highlightthickness
1812    }
1813    Rappture::Tooltip::for $itk_component(yCutScale) \
1814        "@[itcl::code $this SlicerTip y]"
1815    # Set the default cutplane value before disabling the scale.
1816    $itk_component(yCutScale) set 50
1817    $itk_component(yCutScale) configure -state disabled
1818
1819    # Z-value slicer...
1820    itk_component add zCutButton {
1821        Rappture::PushButton $inner.zbutton \
1822            -onimage [Rappture::icon z-cutplane] \
1823            -offimage [Rappture::icon z-cutplane] \
1824            -command [itcl::code $this AdjustSetting -zcutplanevisible] \
1825            -variable [itcl::scope _settings(-zcutplanevisible)]
1826    }
1827    Rappture::Tooltip::for $itk_component(zCutButton) \
1828        "Toggle the Z cut plane on/off"
1829    $itk_component(zCutButton) select
1830
1831    itk_component add zCutScale {
1832        ::scale $inner.zval -from 100 -to 0 \
1833            -width 10 -orient vertical -showvalue off \
1834            -borderwidth 1 -highlightthickness 0 \
1835            -command [itcl::code $this Slice move z] \
1836            -variable [itcl::scope _settings(-zcutplaneposition)]
1837    } {
1838        usual
1839        ignore -borderwidth -highlightthickness
1840    }
1841    $itk_component(zCutScale) set 50
1842    $itk_component(zCutScale) configure -state disabled
1843    Rappture::Tooltip::for $itk_component(zCutScale) \
1844        "@[itcl::code $this SlicerTip z]"
1845
1846    blt::table $inner \
1847        0,1 $inner.visible              -anchor w -pady 2 -cspan 4 \
1848        1,1 $itk_component(xCutScale) \
1849        1,2 $itk_component(yCutScale) \
1850        1,3 $itk_component(zCutScale) \
1851        2,1 $itk_component(xCutButton) \
1852        2,2 $itk_component(yCutButton) \
1853        2,3 $itk_component(zCutButton)
1854
1855    blt::table configure $inner r0 r1 r2 c* -resize none
1856    blt::table configure $inner r3 c4 -resize expand
1857    blt::table configure $inner c0 -width 2
1858    blt::table configure $inner c1 c2 c3 -padx 2
1859}
1860
1861itcl::body Rappture::NanovisViewer::BuildCameraTab {} {
1862    set inner [$itk_component(main) insert end \
1863        -title "Camera Settings" \
1864        -icon [Rappture::icon camera]]
1865    $inner configure -borderwidth 4
1866
1867    label $inner.view_l -text "view" -font "Arial 9"
1868    set f [frame $inner.view]
1869    foreach side { front back left right top bottom } {
1870        button $f.$side  -image [Rappture::icon view$side] \
1871            -command [itcl::code $this SetOrientation $side]
1872        Rappture::Tooltip::for $f.$side "Change the view to $side"
1873        pack $f.$side -side left
1874    }
1875
1876    blt::table $inner \
1877        0,0 $inner.view_l -anchor e -pady 2 \
1878        0,1 $inner.view -anchor w -pady 2
1879    blt::table configure $inner r0 -resize none
1880
1881    set row 1
1882    set labels { qw qx qy qz xpan ypan zoom }
1883    foreach tag $labels {
1884        label $inner.${tag}label -text $tag -font "Arial 9"
1885        entry $inner.${tag} -font "Arial 9"  -bg white \
1886            -textvariable [itcl::scope _settings(-$tag)]
1887        bind $inner.${tag} <Return> \
1888            [itcl::code $this camera set -${tag}]
1889        bind $inner.${tag} <KP_Enter> \
1890            [itcl::code $this camera set -${tag}]
1891        blt::table $inner \
1892            $row,0 $inner.${tag}label -anchor e -pady 2 \
1893            $row,1 $inner.${tag} -anchor w -pady 2
1894        blt::table configure $inner r$row -resize none
1895        incr row
1896    }
1897
1898    blt::table configure $inner c* -resize none
1899    blt::table configure $inner c2 -resize expand
1900    blt::table configure $inner r$row -resize expand
1901}
1902
1903# ----------------------------------------------------------------------
1904# USAGE: Slice move x|y|z <newval>
1905#
1906# Called automatically when the user drags the slider to move the
1907# cut plane that slices 3D data.  Gets the current value from the
1908# slider and moves the cut plane to the appropriate point in the
1909# data set.
1910# ----------------------------------------------------------------------
1911itcl::body Rappture::NanovisViewer::Slice {option args} {
1912    switch -- $option {
1913        move {
1914            if {[llength $args] != 2} {
1915                error "wrong # args: should be \"Slice move x|y|z newval\""
1916            }
1917            set axis [lindex $args 0]
1918            set newval [lindex $args 1]
1919
1920            set newpos [expr {0.01*$newval}]
1921            set datasets [CurrentDatasets -cutplanes]
1922            set tag [lindex $datasets 0]
1923            SendCmd "cutplane position $newpos $axis $tag"
1924        }
1925        default {
1926            error "bad option \"$option\": should be axis, move, or volume"
1927        }
1928    }
1929}
1930
1931# ----------------------------------------------------------------------
1932# USAGE: SlicerTip <axis>
1933#
1934# Used internally to generate a tooltip for the x/y/z slicer controls.
1935# Returns a message that includes the current slicer value.
1936# ----------------------------------------------------------------------
1937itcl::body Rappture::NanovisViewer::SlicerTip {axis} {
1938    set val [$itk_component(${axis}CutScale) get]
1939    return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
1940}
1941
1942
1943itcl::body Rappture::NanovisViewer::DoResize {} {
1944    $_arcball resize $_width $_height
1945    SendCmd "screen size $_width $_height"
1946    set _resizePending 0
1947}
1948
1949itcl::body Rappture::NanovisViewer::EventuallyResize { w h } {
1950    set _width $w
1951    set _height $h
1952    $_arcball resize $w $h
1953    if { !$_resizePending } {
1954        $_dispatcher event -idle !resize
1955        set _resizePending 1
1956    }
1957}
1958
1959itcl::body Rappture::NanovisViewer::EventuallyRedrawLegend {} {
1960    if { !$_resizeLegendPending } {
1961        $_dispatcher event -idle !legend
1962        set _resizeLegendPending 1
1963    }
1964}
1965
1966#  camera --
1967#
1968itcl::body Rappture::NanovisViewer::camera {option args} {
1969    switch -- $option {
1970        "show" {
1971            puts [array get _view]
1972        }
1973        "set" {
1974            set what [lindex $args 0]
1975            set x $_settings($what)
1976            set code [catch { string is double $x } result]
1977            if { $code != 0 || !$result } {
1978                set _settings($what) $_view($what)
1979                return
1980            }
1981            switch -- $what {
1982                "-xpan" - "-ypan" {
1983                    set _view($what) $_settings($what)
1984                    PanCamera
1985                }
1986                "-qx" - "-qy" - "-qz" - "-qw" {
1987                    set _view($what) $_settings($what)
1988                    set q [ViewToQuaternion]
1989                    $_arcball quaternion $q
1990                    SendCmd "camera orient $q"
1991                }
1992                "-zoom" {
1993                    set _view($what) $_settings($what)
1994                    SendCmd "camera zoom $_view($what)"
1995                }
1996            }
1997        }
1998    }
1999}
2000
2001itcl::body Rappture::NanovisViewer::GetVolumeInfo { w } {
2002    set flowobj ""
2003    foreach key [array names _obj2flow] {
2004        set flowobj $_obj2flow($key)
2005        break
2006    }
2007    if { $flowobj == "" } {
2008        return
2009    }
2010    if { [winfo exists $w.frame] } {
2011        destroy $w.frame
2012    }
2013    set inner [frame $w.frame]
2014    blt::table $w \
2015        5,0 $inner -fill both -cspan 2 -anchor nw
2016    array set hints [$dataobj hints]
2017
2018    label $inner.volumes -text "Volumes" -font "Arial 9 bold"
2019    blt::table $inner \
2020        1,0 $inner.volumes  -anchor w \
2021    blt::table configure $inner c0 c1 -resize none
2022    blt::table configure $inner c2 -resize expand
2023
2024    set row 3
2025    set volumes [get]
2026    if { [llength $volumes] > 0 } {
2027        blt::table $inner $row,0 $inner.volumes  -anchor w
2028        incr row
2029    }
2030    foreach vol $volumes {
2031        array unset info
2032        array set info $vol
2033        set name $info(name)
2034        if { ![info exists _settings(-volumevisible-$name)] } {
2035            set _settings(-volumevisible-$name) $info(hide)
2036        }
2037        checkbutton $inner.vol$row -text $info(label) \
2038            -variable [itcl::scope _settings(-volumevisible-$name)] \
2039            -onvalue 0 -offvalue 1 \
2040            -command [itcl::code $this ToggleVolume $key $name] \
2041            -font "Arial 9"
2042        Rappture::Tooltip::for $inner.vol$row $info(description)
2043        blt::table $inner $row,0 $inner.vol$row -anchor w
2044        if { !$_settings(-volume-$name) } {
2045            $inner.vol$row select
2046        }
2047        incr row
2048    }
2049    blt::table configure $inner r* -resize none
2050    blt::table configure $inner r$row -resize expand
2051    blt::table configure $inner c3 -resize expand
2052    event generate [winfo parent [winfo parent $w]] <Configure>
2053}
2054
2055itcl::body Rappture::NanovisViewer::ToggleVolume { tag name } {
2056    set bool $_settings(-volumevisible-$name)
2057    SendCmd "volume state $bool $name"
2058}
2059
2060itcl::body Rappture::NanovisViewer::SetOrientation { side } {
2061    array set positions {
2062        front "1 0 0 0"
2063        back  "0 0 1 0"
2064        left  "0.707107 0 -0.707107 0"
2065        right "0.707107 0 0.707107 0"
2066        top   "0.707107 -0.707107 0 0"
2067        bottom "0.707107 0.707107 0 0"
2068    }
2069    foreach name { -qw -qx -qy -qz } value $positions($side) {
2070        set _view($name) $value
2071    }
2072    set q [ViewToQuaternion]
2073    $_arcball quaternion $q
2074    SendCmd "camera orient $q"
2075    SendCmd "camera reset"
2076    set _view(-xpan) 0
2077    set _view(-ypan) 0
2078    set _view(-zoom) 1.0
2079    set _settings(-xpan) $_view(-xpan)
2080    set _settings(-ypan) $_view(-ypan)
2081    set _settings(-zoom) $_view(-zoom)
2082}
2083
2084
2085#
2086# InitComponentSettings --
2087#
2088#    Initializes the volume settings for a specific component. This should
2089#    match what's used as global settings above. This is called the first
2090#    time we try to switch to a given component in SwitchComponent below.
2091#
2092itcl::body Rappture::NanovisViewer::InitComponentSettings { cname } {
2093    foreach {key value} {
2094        -colormap          "default"
2095        -light             40
2096        -light2side        1
2097        -thickness         350
2098        -volumeopacity     1.0
2099        -volumevisible     1
2100    } {
2101        if { ![info exists _settings($cname${key})] } {
2102            # Don't override existing component settings
2103            set _settings($cname${key}) $value
2104        }
2105    }
2106}
2107
2108#
2109# SwitchComponent --
2110#
2111#    This is called when the current component is changed by the dropdown
2112#    menu in the volume tab.  It synchronizes the global volume settings
2113#    with the settings of the new current component.
2114#
2115itcl::body Rappture::NanovisViewer::SwitchComponent { cname } {
2116    if { ![info exists _settings($cname-light)] } {
2117        InitComponentSettings $cname
2118    }
2119    if { $_settings(-colormap) != $_settings($cname-colormap) } {
2120        set _settings(-colormap)         $_settings($cname-colormap)
2121        EventuallyRedrawLegend
2122    }
2123    # _settings variables change widgets, except for colormap
2124    set _settings(-light)            $_settings($cname-light)
2125    set _settings(-light2side)       $_settings($cname-light2side)
2126    set _settings(-volumeopacity)    $_settings($cname-volumeopacity)
2127    set _settings(-thickness)        $_settings($cname-thickness)
2128    set _settings(-volumevisible)    $_settings($cname-volumevisible)
2129    $itk_component(colormap) value   $_settings($cname-colormap)
2130
2131    set _widget(-volumeopacity) [expr $_settings(-volumeopacity) * 100.0]
2132
2133    set _current $cname;                # Reset the current component
2134}
2135
2136#
2137# BuildVolumeComponents --
2138#
2139#    This is called from the "scale" method which is called when a new
2140#    dataset is added or deleted.  It repopulates the dropdown menu of
2141#    volume component names.  It sets the current component to the first
2142#    component in the list (of components found).  Finally, if there is
2143#    only one component, don't display the label or the combobox in the
2144#    volume settings tab.
2145#
2146itcl::body Rappture::NanovisViewer::BuildVolumeComponents {} {
2147    $itk_component(volcomponents) choices delete 0 end
2148    foreach name $_componentsList {
2149        $itk_component(volcomponents) choices insert end $name $name
2150    }
2151    set _current [lindex $_componentsList 0]
2152    $itk_component(volcomponents) value $_current
2153    set parent [winfo parent $itk_component(volcomponents)]
2154    if { [llength $_componentsList] <= 1 } {
2155        # Unpack the components label and dropdown if there's only one
2156        # component.
2157        blt::table forget $parent.volcomponents_l $parent.volcomponents
2158    } else {
2159        # Pack the components label and dropdown into the table there's
2160        # more than one component to select.
2161        blt::table $parent \
2162            0,0 $parent.volcomponents_l -anchor e -cspan 2 \
2163            0,2 $parent.volcomponents -cspan 3 -fill x
2164    }
2165}
2166
2167#
2168# GetDatasetsWithComponents --
2169#
2170#    Returns a list of all the datasets (known by the combination of their
2171#    data object and component name) that match the given component name.
2172#    For example, this is used where we want to change the settings of
2173#    volumes that have the current component.
2174#
2175itcl::body Rappture::NanovisViewer::GetDatasetsWithComponent { cname } {
2176    if { ![info exists _volcomponents($cname)] } {
2177        return ""
2178    }
2179    set list ""
2180    foreach tag $_volcomponents($cname) {
2181        if { ![info exists _serverDatasets($tag)] } {
2182            continue
2183        }
2184        lappend list $tag
2185    }
2186    return $list
2187}
2188
2189#
2190# HideAllMarkers --
2191#
2192#    Hide all the markers in all the transfer functions.  Can't simply
2193#    delete and recreate markers from the <style> since the user may have
2194#    created, deleted, or moved markers.
2195#
2196itcl::body Rappture::NanovisViewer::HideAllMarkers {} {
2197    foreach cname [array names _transferFunctionEditors] {
2198        $_transferFunctionEditors($cname) hideMarkers
2199    }
2200}
2201
2202itcl::body Rappture::NanovisViewer::GetColormap { cname color } {
2203    if { $color == "default" } {
2204        return $_cname2defaultcolormap($cname)
2205    }
2206    return [ColorsToColormap $color]
2207}
2208
2209itcl::body Rappture::NanovisViewer::ResetColormap { cname color } {
2210    # Get the current transfer function
2211    if { ![info exists _cname2transferFunction($cname)] } {
2212        return
2213    }
2214    foreach { cmap amap } $_cname2transferFunction($cname) break
2215    set cmap [GetColormap $cname $color]
2216    set _cname2transferFunction($cname) [list $cmap $amap]
2217    SendCmd [list transfunc define $cname $cmap $amap]
2218    EventuallyRedrawLegend
2219}
2220
2221itcl::body Rappture::NanovisViewer::ComputeAlphamap { cname } {
2222    if { ![info exists _transferFunctionEditors($cname)] } {
2223        return [list 0.0 0.0 1.0 1.0]
2224    }
2225    if { ![info exists _settings($cname-light)] } {
2226        InitComponentSettings $cname
2227    }
2228
2229    set isovalues [$_transferFunctionEditors($cname) values]
2230
2231    # Transfer function should be normalized with [0,1] range
2232    # The volume shading opacity setting is used to scale opacity
2233    # in the volume shader.
2234    set max 1.0
2235
2236    # Use the component-wise thickness setting from the slider
2237    # settings widget
2238    # Scale values between 0.00001 and 0.01000
2239    set delta [expr {double($_settings($cname-thickness)) * 0.0001}]
2240
2241    set first [lindex $isovalues 0]
2242    set last [lindex $isovalues end]
2243    set amap ""
2244    if { $first == "" || $first != 0.0 } {
2245        lappend amap 0.0 0.0
2246    }
2247    foreach x $isovalues {
2248        set x1 [expr {$x-$delta-0.00001}]
2249        set x2 [expr {$x-$delta}]
2250        set x3 [expr {$x+$delta}]
2251        set x4 [expr {$x+$delta+0.00001}]
2252        if { $x1 < 0.0 } {
2253            set x1 0.0
2254        } elseif { $x1 > 1.0 } {
2255            set x1 1.0
2256        }
2257        if { $x2 < 0.0 } {
2258            set x2 0.0
2259        } elseif { $x2 > 1.0 } {
2260            set x2 1.0
2261        }
2262        if { $x3 < 0.0 } {
2263            set x3 0.0
2264        } elseif { $x3 > 1.0 } {
2265            set x3 1.0
2266        }
2267        if { $x4 < 0.0 } {
2268            set x4 0.0
2269        } elseif { $x4 > 1.0 } {
2270            set x4 1.0
2271        }
2272        # add spikes in the middle
2273        lappend amap $x1 0.0
2274        lappend amap $x2 $max
2275        lappend amap $x3 $max
2276        lappend amap $x4 0.0
2277    }
2278    if { $last == "" || $last != 1.0 } {
2279        lappend amap 1.0 0.0
2280    }
2281    return $amap
2282}
2283
2284itcl::body Rappture::NanovisViewer::SetObjectStyle { dataobj cname } {
2285    array set style {
2286        -opacity  0.5
2287    }
2288    array set style [lindex [$dataobj components -style $cname] 0]
2289    set _settings($cname-volumeopacity) $style(-opacity)
2290    set tag $dataobj-$cname
2291    SendCmd "volume shading opacity $_settings($cname-volumeopacity) $tag"
2292    NameTransferFunction $dataobj $cname
2293}
Note: See TracBrowser for help on using the repository browser.