source: branches/1.3/gui/scripts/nanovisviewer.tcl @ 4772

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

merge r4771 from trunk

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