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

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

merge r4765 from trunk

File size: 87.8 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                        puts $f $data
921                        close $f
922                    }
923                }
924                set nbytes [string length $data]
925                if { $_reportClientInfo }  {
926                    set info {}
927                    lappend info "tool_id"       [$dataobj hints toolid]
928                    lappend info "tool_name"     [$dataobj hints toolname]
929                    lappend info "tool_title"    [$dataobj hints tooltitle]
930                    lappend info "tool_command"  [$dataobj hints toolcommand]
931                    lappend info "tool_revision" [$dataobj hints toolrevision]
932                    lappend info "dataset_label" [$dataobj hints label]
933                    lappend info "dataset_size"  $nbytes
934                    lappend info "dataset_tag"   $tag
935                    SendCmd "clientinfo [list $info]"
936                }
937                SendCmd "volume data follows $nbytes $tag"
938                append _outbuf $data
939                set _recvdDatasets($tag) 1
940                set _serverDatasets($tag) 0
941            }
942            SetObjectStyle $dataobj $cname
943        }
944    }
945
946    # Outline seems to need to be reset every update.
947    InitSettings -outlinevisible -cutplanesvisible -current
948
949    set _first [lindex [get] 0]
950    if { $_reset } {
951        #
952        # Reset the camera and other view parameters
953        #
954        set _settings(-qw)    $_view(-qw)
955        set _settings(-qx)    $_view(-qx)
956        set _settings(-qy)    $_view(-qy)
957        set _settings(-qz)    $_view(-qz)
958        set _settings(-xpan)  $_view(-xpan)
959        set _settings(-ypan)  $_view(-ypan)
960        set _settings(-zoom)  $_view(-zoom)
961
962        set q [ViewToQuaternion]
963        $_arcball quaternion $q
964        SendCmd "camera orient $q"
965        SendCmd "camera reset"
966        PanCamera
967        SendCmd "camera zoom $_view(-zoom)"
968       
969        #cutplane state 0 all
970        foreach axis {x y z} {
971            # Turn off cutplanes for all volumes
972            SendCmd "cutplane state 0 $axis"
973        }
974
975        InitSettings -light2side -light -volumeopacity \
976            -isosurfaceshading -gridvisible -axesvisible \
977
978        if {"" != $_first} {
979            set axis [$_first hints updir]
980            if { "" != $axis } {
981                SendCmd "up $axis"
982            }
983            set location [$_first hints camera]
984            if { $location != "" } {
985                array set _view $location
986            }
987        }
988    }
989
990    # nothing to send -- activate the proper ivol
991    SendCmd "volume state 0"
992    if {"" != $_first} {
993        set datasets [array names _serverDatasets $_first-*]
994        if { $datasets != "" } {
995            SendCmd "volume state 1 $datasets"
996        }
997        # If the first volume already exists on the server, then make sure
998        # we display the proper transfer function in the legend.
999        set cname [lindex [$_first components] 0]
1000        if { [info exists _serverDatasets($_first-$cname)] } {
1001            updateTransferFunctions
1002        }
1003    }
1004    # Actually write the commands to the server socket.  If it fails, we don't
1005    # care.  We're finished here.
1006    blt::busy hold $itk_component(hull)
1007    StopBufferingCommands
1008    blt::busy release $itk_component(hull)
1009    set _reset 0
1010}
1011
1012# ----------------------------------------------------------------------
1013# USAGE: CurrentDatasets ?-cutplanes?
1014#
1015# Returns a list of volume server IDs for the current volume being
1016# displayed.  This is normally a single ID, but it might be a list
1017# of IDs if the current data object has multiple components.
1018# ----------------------------------------------------------------------
1019itcl::body Rappture::NanovisViewer::CurrentDatasets {{what -all}} {
1020    set rlist ""
1021    if { $_first == "" } {
1022        return
1023    }
1024    foreach cname [$_first components] {
1025        set tag $_first-$cname
1026        if { [info exists _serverDatasets($tag)] && $_serverDatasets($tag) } {
1027            array set styles {
1028                -cutplanes 1
1029            }
1030            array set styles [lindex [$_first components -style $cname] 0]
1031            if { $what != "-cutplanes" || $styles(-cutplanes) } {
1032                lappend rlist $tag
1033            }
1034        }
1035    }
1036    return $rlist
1037}
1038
1039# ----------------------------------------------------------------------
1040# USAGE: Zoom in
1041# USAGE: Zoom out
1042# USAGE: Zoom reset
1043#
1044# Called automatically when the user clicks on one of the zoom
1045# controls for this widget.  Changes the zoom for the current view.
1046# ----------------------------------------------------------------------
1047itcl::body Rappture::NanovisViewer::Zoom {option} {
1048    switch -- $option {
1049        "in" {
1050            set _view(-zoom) [expr {$_view(-zoom)*1.25}]
1051            set _settings(-zoom) $_view(-zoom)
1052            SendCmd "camera zoom $_view(-zoom)"
1053        }
1054        "out" {
1055            set _view(-zoom) [expr {$_view(-zoom)*0.8}]
1056            set _settings(-zoom) $_view(-zoom)
1057            SendCmd "camera zoom $_view(-zoom)"
1058        }
1059        "reset" {
1060            array set _view {
1061                -qw      0.853553
1062                -qx      -0.353553
1063                -qy      0.353553
1064                -qz      0.146447
1065                -xpan    0
1066                -ypan    0
1067                -zoom    1.0
1068            }
1069            if { $_first != "" } {
1070                set location [$_first hints camera]
1071                if { $location != "" } {
1072                    array set _view $location
1073                }
1074            }
1075            set q [ViewToQuaternion]         
1076            $_arcball quaternion $q
1077            SendCmd "camera orient $q"
1078            SendCmd "camera reset"
1079            set _settings(-qw)    $_view(-qw)
1080            set _settings(-qx)    $_view(-qx)
1081            set _settings(-qy)    $_view(-qy)
1082            set _settings(-qz)    $_view(-qz)
1083            set _settings(-xpan)  $_view(-xpan)
1084            set _settings(-ypan)  $_view(-ypan)
1085            set _settings(-zoom)  $_view(-zoom)
1086        }
1087    }
1088}
1089
1090itcl::body Rappture::NanovisViewer::PanCamera {} {
1091    set x $_view(-xpan)
1092    set y $_view(-ypan)
1093    SendCmd "camera pan $x $y"
1094}
1095
1096
1097# ----------------------------------------------------------------------
1098# USAGE: Rotate click <x> <y>
1099# USAGE: Rotate drag <x> <y>
1100# USAGE: Rotate release <x> <y>
1101#
1102# Called automatically when the user clicks/drags/releases in the
1103# plot area.  Moves the plot according to the user's actions.
1104# ----------------------------------------------------------------------
1105itcl::body Rappture::NanovisViewer::Rotate {option x y} {
1106    switch -- $option {
1107        click {
1108            $itk_component(3dview) configure -cursor fleur
1109            set _click(x) $x
1110            set _click(y) $y
1111        }
1112        drag {
1113            if {[array size _click] == 0} {
1114                Rotate click $x $y
1115            } else {
1116                set w [winfo width $itk_component(3dview)]
1117                set h [winfo height $itk_component(3dview)]
1118                if {$w <= 0 || $h <= 0} {
1119                    return
1120                }
1121
1122                if {[catch {
1123                    # this fails sometimes for no apparent reason
1124                    set dx [expr {double($x-$_click(x))/$w}]
1125                    set dy [expr {double($y-$_click(y))/$h}]
1126                }]} {
1127                    return
1128                }
1129
1130                set q [$_arcball rotate $x $y $_click(x) $_click(y)]
1131                QuaternionToView $q
1132                set _settings(-qw) $_view(-qw)
1133                set _settings(-qx) $_view(-qx)
1134                set _settings(-qy) $_view(-qy)
1135                set _settings(-qz) $_view(-qz)
1136                SendCmd "camera orient $q"
1137
1138                set _click(x) $x
1139                set _click(y) $y
1140            }
1141        }
1142        release {
1143            Rotate drag $x $y
1144            $itk_component(3dview) configure -cursor ""
1145            catch {unset _click}
1146        }
1147        default {
1148            error "bad option \"$option\": should be click, drag, release"
1149        }
1150    }
1151}
1152
1153# ----------------------------------------------------------------------
1154# USAGE: $this Pan click x y
1155#        $this Pan drag x y
1156#        $this Pan release x y
1157#
1158# Called automatically when the user clicks on one of the zoom
1159# controls for this widget.  Changes the zoom for the current view.
1160# ----------------------------------------------------------------------
1161itcl::body Rappture::NanovisViewer::Pan {option x y} {
1162    # Experimental stuff
1163    set w [winfo width $itk_component(3dview)]
1164    set h [winfo height $itk_component(3dview)]
1165    if { $option == "set" } {
1166        set x [expr $x / double($w)]
1167        set y [expr $y / double($h)]
1168        set _view(-xpan) [expr $_view(-xpan) + $x]
1169        set _view(-ypan) [expr $_view(-ypan) + $y]
1170        PanCamera
1171        set _settings(-xpan) $_view(-xpan)
1172        set _settings(-ypan) $_view(-ypan)
1173        return
1174    }
1175    if { $option == "click" } {
1176        set _click(x) $x
1177        set _click(y) $y
1178        $itk_component(3dview) configure -cursor hand1
1179    }
1180    if { $option == "drag" || $option == "release" } {
1181        set dx [expr ($_click(x) - $x)/double($w)]
1182        set dy [expr ($_click(y) - $y)/double($h)]
1183        set _click(x) $x
1184        set _click(y) $y
1185        set _view(-xpan) [expr $_view(-xpan) - $dx]
1186        set _view(-ypan) [expr $_view(-ypan) - $dy]
1187        PanCamera
1188        set _settings(-xpan) $_view(-xpan)
1189        set _settings(-ypan) $_view(-ypan)
1190    }
1191    if { $option == "release" } {
1192        $itk_component(3dview) configure -cursor ""
1193    }
1194}
1195
1196# ----------------------------------------------------------------------
1197# USAGE: InitSettings <what> ?<value>?
1198#
1199# Used internally to update rendering settings whenever parameters
1200# change in the popup settings panel.  Sends the new settings off
1201# to the back end.
1202# ----------------------------------------------------------------------
1203itcl::body Rappture::NanovisViewer::InitSettings { args } {
1204    foreach arg $args {
1205        AdjustSetting $arg
1206    }
1207}
1208
1209# ----------------------------------------------------------------------
1210# USAGE: AdjustSetting <what> ?<value>?
1211#
1212# Used internally to update rendering settings whenever parameters
1213# change in the popup settings panel.  Sends the new settings off
1214# to the back end.
1215# ----------------------------------------------------------------------
1216itcl::body Rappture::NanovisViewer::AdjustSetting {what {value ""}} {
1217    if {![isconnected]} {
1218        return
1219    }
1220    switch -- $what {
1221        "-axesvisible" {
1222            SendCmd "axis visible $_settings($what)"
1223        }
1224        "-background" {
1225            set bgcolor [$itk_component(background) value]
1226            array set fgcolors {
1227                "black" "white"
1228                "white" "black"
1229                "grey"  "black"
1230            }
1231            configure -plotbackground $bgcolor \
1232                -plotforeground $fgcolors($bgcolor)
1233            DrawLegend $_current
1234        }
1235        "-colormap" {
1236            set color [$itk_component(colormap) value]
1237            set _settings($what) $color
1238            set _settings($_current${what}) $color
1239            ResetColormap $_current $color
1240        }
1241        "-current" {
1242            set cname [$itk_component(volcomponents) value]
1243            SwitchComponent $cname
1244        }
1245        "-cutplanesvisible" {
1246            set bool $_settings($what)
1247            # We only set cutplanes on the first dataset.
1248            set datasets [CurrentDatasets -cutplanes]
1249            set tag [lindex $datasets 0]
1250            if { $bool } {
1251                foreach axis { x y z } {
1252                    if { $_settings(-${axis}cutplanevisible) } {
1253                        SendCmd "cutplane state 1 $axis $tag"
1254                    }
1255                }
1256            } else {
1257                foreach axis { x y z } {
1258                    SendCmd "cutplane state 0 $axis $tag"
1259                }
1260            }
1261        }
1262        "-gridvisible" {
1263            SendCmd "grid visible $_settings($what)"
1264        }
1265        "-isosurfaceshading" {
1266            SendCmd "volume shading isosurface $_settings($what)"
1267        }
1268        "-legendvisible" {
1269            if { $_settings($what) } {
1270                blt::table $itk_component(plotarea) \
1271                    0,0 $itk_component(3dview) -fill both \
1272                    1,0 $itk_component(legend) -fill x
1273                blt::table configure $itk_component(plotarea) r1 -resize none
1274            } else {
1275                blt::table forget $itk_component(legend)
1276            }
1277        }
1278        "-light" {
1279            set _settings($_current${what}) $_settings($what)
1280            set val $_settings($what)
1281            set diffuse [expr {0.01*$val}]
1282            set ambient [expr {1.0-$diffuse}]
1283            set specularLevel 0.3
1284            set specularExp 90.0
1285            foreach tag [GetDatasetsWithComponent $_current] {
1286                SendCmd "volume shading ambient $ambient $tag"
1287                SendCmd "volume shading diffuse $diffuse $tag"
1288                SendCmd "volume shading specularLevel $specularLevel $tag"
1289                SendCmd "volume shading specularExp $specularExp $tag"
1290            }
1291        }
1292        "-light2side" {
1293            set _settings($_current${what}) $_settings($what)
1294            set val $_settings($what)
1295            foreach tag [GetDatasetsWithComponent $_current] {
1296                SendCmd "volume shading light2side $val $tag"
1297            }
1298        }
1299        "-outlinevisible" {
1300            SendCmd "volume outline state $_settings($what)"
1301        }
1302        "-thickness" {
1303            set val $_settings($what)
1304            set _settings($_current${what}) $val
1305            updateTransferFunctions
1306        }
1307        "-volume" {
1308            # This is the global volume visibility control.  It controls the
1309            # visibility of all the all volumes.  Whenever it's changed, you
1310            # have to synchronize each of the local controls (see below) with
1311            # this.
1312            set datasets [CurrentDatasets]
1313            set bool $_settings($what)
1314            SendCmd "volume data state $bool $datasets"
1315            foreach cname $_componentsList {
1316                set _settings($cname-volumevisible) $bool
1317            }
1318            set _settings(-volumevisible) $bool
1319        }
1320        "-volumeopacity" {
1321            set _settings($what) [expr $_widget($what) * 0.01]
1322            set _settings($_current${what}) $_settings($what)
1323            foreach tag [GetDatasetsWithComponent $_current] {
1324                SendCmd "volume shading opacity $_settings($what) $tag"
1325            }
1326        }
1327        "-volumevisible" {
1328            # This is the component specific control.  It changes the
1329            # visibility of only the current component.
1330            set _settings($_current${what}) $_settings($what)
1331            foreach tag [GetDatasetsWithComponent $_current] {
1332                SendCmd "volume data state $_settings($what) $tag"
1333            }
1334        }
1335        "-xcutplanevisible" - "-ycutplanevisible" - "-zcutplanevisible" {
1336            set axis [string range $what 1 1]
1337            set bool $_settings($what)
1338            # We only set cutplanes on the first dataset.
1339            set datasets [CurrentDatasets -cutplanes]
1340            set tag [lindex $datasets 0]
1341            if { $_settings(-cutplanesvisible) } {
1342                SendCmd "cutplane state $bool $axis $tag"
1343            }
1344            if { $bool } {
1345                $itk_component(${axis}CutScale) configure -state normal \
1346                    -troughcolor white
1347            } else {
1348                $itk_component(${axis}CutScale) configure -state disabled \
1349                    -troughcolor grey82
1350            }
1351        }
1352        default {
1353            error "don't know how to fix $what"
1354        }
1355    }
1356}
1357
1358# ----------------------------------------------------------------------
1359# USAGE: FixLegend
1360#
1361# Used internally to update the legend area whenever it changes size
1362# or when the field changes.  Asks the server to send a new legend
1363# for the current field.
1364# ----------------------------------------------------------------------
1365itcl::body Rappture::NanovisViewer::FixLegend {} {
1366    set _resizeLegendPending 0
1367    set lineht [font metrics $itk_option(-font) -linespace]
1368    set w [expr {$_width-20}]
1369    set h [expr {[winfo height $itk_component(legend)]-20-$lineht}]
1370    if {$w > 0 && $h > 0 && $_first != "" } {
1371        if { [info exists _cname2transferFunction($_current)] } {
1372            SendCmd "legend $_current $w $h"
1373        }
1374    }
1375}
1376
1377#
1378# NameTransferFunction --
1379#
1380#       Creates a transfer function name based on the <style> settings in the
1381#       library run.xml file. This placeholder will be used later to create
1382#       and send the actual transfer function once the data info has been sent
1383#       to us by the render server. [We won't know the volume limits until the
1384#       server parses the 3D data and sends back the limits via ReceiveData.]
1385#
1386itcl::body Rappture::NanovisViewer::NameTransferFunction { dataobj cname } {
1387    array set styles {
1388        -color BCGYR
1389        -levels 6
1390        -markers ""
1391    }
1392    set tag $dataobj-$cname
1393    array set styles [lindex [$dataobj components -style $cname] 0]
1394    if { ![info exists _cname2transferFunction($cname)] } {
1395        # Get the colormap right now, since it doesn't change with marker
1396        # changes.
1397        set cmap [ColorsToColormap $styles(-color)]
1398        set wmap [list 0.0 0.0 1.0 1.0]
1399        set _cname2transferFunction($cname) [list $cmap $wmap]
1400        SendCmd [list transfunc define $cname $cmap $wmap]
1401    }
1402    SendCmd "volume shading transfunc $cname $tag"
1403    if { ![info exists _transferFunctionEditors($cname)] } {
1404        set _transferFunctionEditors($cname) \
1405            [Rappture::TransferFunctionEditor ::\#auto $itk_component(legend) \
1406                 $cname \
1407                 -command [itcl::code $this updateTransferFunctions]]
1408    }
1409    set _dataset2style($tag) $cname
1410    lappend _style2datasets($cname) $tag
1411    return $cname
1412}
1413
1414#
1415# ComputeTransferFunction --
1416#
1417#       Computes and sends the transfer function to the render server.  It's
1418#       assumed that the volume data limits are known and that the global
1419#       transfer-functions slider values have been set up.  Both parts are
1420#       needed to compute the relative value (location) of the marker, and
1421#       the alpha map of the transfer function.
1422#
1423itcl::body Rappture::NanovisViewer::ComputeTransferFunction { cname } {
1424    foreach {cmap wmap} $_cname2transferFunction($cname) break
1425
1426    # We have to parse the style attributes for a volume using this
1427    # transfer-function *once*.  This sets up the initial isomarkers for the
1428    # transfer function.  The user may add/delete markers, so we have to
1429    # maintain a list of markers for each transfer-function.  We use the one
1430    # of the volumes (the first in the list) using the transfer-function as a
1431    # reference.
1432    if { ![info exists _parsedFunction($cname)] } {
1433        array set styles {
1434            -color BCGYR
1435            -levels 6
1436            -markers ""
1437        }
1438        # Accumulate the style from all the datasets using it.
1439        foreach tag [GetDatasetsWithComponent $cname] {
1440            foreach {dataobj cname} [split [lindex $tag 0] -] break
1441            array set styles [lindex [$dataobj components -style $cname] 0]
1442        }
1443        eval $_transferFunctionEditors($cname) limits $_limits($cname)
1444        # Have to defer creation of isomarkers until we have data limits
1445        if { [info exists styles(-markers)] &&
1446             [llength $styles(-markers)] > 0 } {
1447            ParseMarkersOption $cname $styles(-markers)
1448        } else {
1449            ParseLevelsOption $cname $styles(-levels)
1450        }
1451       
1452    }
1453    set wmap [ComputeAlphamap $cname]
1454    set _cname2transferFunction($cname) [list $cmap $wmap]
1455    SendCmd [list transfunc define $cname $cmap $wmap]
1456}
1457
1458itcl::body Rappture::NanovisViewer::AddNewMarker { x y } {
1459    if { ![info exists _transferFunctionEditors($_current)] } {
1460        continue
1461    }
1462    # Add a new marker to the current transfer function
1463    $_transferFunctionEditors($_current) newMarker $x $y normal
1464    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1465}
1466
1467itcl::body Rappture::NanovisViewer::RemoveMarker { x y } {
1468    if { ![info exists _transferFunctionEditors($_current)] } {
1469        continue
1470    }
1471    # Add a new marker to the current transfer function
1472    $_transferFunctionEditors($_current) deleteMarker $x $y
1473}
1474
1475# ----------------------------------------------------------------------
1476# CONFIGURATION OPTION: -plotbackground
1477# ----------------------------------------------------------------------
1478itcl::configbody Rappture::NanovisViewer::plotbackground {
1479    if { [isconnected] } {
1480        set color $itk_option(-plotbackground)
1481        set rgb [Color2RGB $color]
1482        SendCmd "screen bgcolor $rgb"
1483        $itk_component(legend) configure -background $color
1484    }
1485}
1486
1487# ----------------------------------------------------------------------
1488# CONFIGURATION OPTION: -plotforeground
1489# ----------------------------------------------------------------------
1490itcl::configbody Rappture::NanovisViewer::plotforeground {
1491    if { [isconnected] } {
1492        set color $itk_option(-plotforeground)
1493        set rgb [Color2RGB $color]
1494        SendCmd "volume outline color $rgb"
1495        SendCmd "grid axiscolor $rgb"
1496        SendCmd "grid linecolor $rgb"
1497        $itk_component(legend) itemconfigure labels -fill $color
1498        $itk_component(legend) itemconfigure limits -fill $color
1499    }
1500}
1501
1502# ----------------------------------------------------------------------
1503# CONFIGURATION OPTION: -plotoutline
1504# ----------------------------------------------------------------------
1505itcl::configbody Rappture::NanovisViewer::plotoutline {
1506    # Must check if we are connected because this routine is called from the
1507    # class body when the -plotoutline itk_option is defined.  At that point
1508    # the NanovisViewer class constructor hasn't been called, so we can't
1509    # start sending commands to visualization server.
1510    if { [isconnected] } {
1511        if {"" == $itk_option(-plotoutline)} {
1512            SendCmd "volume outline state off"
1513        } else {
1514            SendCmd "volume outline state on"
1515            SendCmd "volume outline color [Color2RGB $itk_option(-plotoutline)]"
1516        }
1517    }
1518}
1519
1520#
1521# The -levels option takes a single value that represents the number
1522# of evenly distributed markers based on the current data range. Each
1523# marker is a relative value from 0.0 to 1.0.
1524#
1525itcl::body Rappture::NanovisViewer::ParseLevelsOption { cname levels } {
1526    set c $itk_component(legend)
1527    set list {}
1528    regsub -all "," $levels " " levels
1529    if {[string is int $levels]} {
1530        for {set i 1} { $i <= $levels } {incr i} {
1531            lappend list [expr {double($i)/($levels+1)}]
1532        }
1533    } else {
1534        foreach x $levels {
1535            lappend list $x
1536        }
1537    }
1538    set _parsedFunction($cname) 1
1539    $_transferFunctionEditors($cname) addMarkers $list
1540    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1541}
1542
1543#
1544# The -markers option takes a list of zero or more values (the values
1545# may be separated either by spaces or commas) that have the following
1546# format:
1547#
1548#   N%  Percent of current total data range.  Converted to
1549#       to a relative value between 0.0 and 1.0.
1550#   N   Absolute value of marker.  If the marker is outside of
1551#       the current range, it will be displayed on the outer
1552#       edge of the legends, but it range it represents will
1553#       not be seen.
1554#
1555itcl::body Rappture::NanovisViewer::ParseMarkersOption { cname markers } {
1556    set c $itk_component(legend)
1557    set list {}
1558    foreach { min max } $_limits($cname) break
1559    regsub -all "," $markers " " markers
1560    foreach marker $markers {
1561        set n [scan $marker "%g%s" value suffix]
1562        if { $n == 2 && $suffix == "%" } {
1563            # $n% : Set relative value (0..1).
1564            lappend list [expr {$value * 0.01}]
1565        } else {
1566            # $n : absolute value, compute relative
1567            lappend list  [expr {(double($value)-$min)/($max-$min)]}
1568        }
1569    }
1570    set _parsedFunction($cname) 1
1571    $_transferFunctionEditors($cname) addMarkers $list
1572    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1573}
1574
1575# ----------------------------------------------------------------------
1576# USAGE: UndateTransferFuncs
1577# ----------------------------------------------------------------------
1578itcl::body Rappture::NanovisViewer::updateTransferFunctions {} {
1579    $_dispatcher event -idle !send_transfunc
1580}
1581
1582itcl::body Rappture::NanovisViewer::limits { cname } {
1583    set _limits(min) 0.0
1584    set _limits(max) 1.0
1585    if { ![info exists _style2datasets($cname)] } {
1586        return [array get _limits]
1587    }
1588    set min ""; set max ""
1589    foreach tag [GetDatasetsWithComponent $cname] {
1590        if { ![info exists _limits($tag)] } {
1591            continue
1592        }
1593        foreach {amin amax} $_limits($tag) break
1594        if { $min == "" || $min > $amin } {
1595            set min $amin
1596        }
1597        if { $max == "" || $max < $amax } {
1598            set max $amax
1599        }
1600    }
1601    if { $min != "" } {
1602        set _limits(min) $min
1603    }
1604    if { $max != "" } {
1605        set _limits(max) $max
1606    }
1607    return [list $_limits(min) $_limits(max)]
1608}
1609
1610
1611itcl::body Rappture::NanovisViewer::BuildViewTab {} {
1612    set fg [option get $itk_component(hull) font Font]
1613    #set bfg [option get $itk_component(hull) boldFont Font]
1614
1615    set inner [$itk_component(main) insert end \
1616        -title "View Settings" \
1617        -icon [Rappture::icon wrench]]
1618    $inner configure -borderwidth 4
1619
1620    set ::Rappture::NanovisViewer::_settings(-isosurfaceshading) 0
1621    checkbutton $inner.isosurface \
1622        -text "Isosurface shading" \
1623        -variable [itcl::scope _settings(-isosurfaceshading)] \
1624        -command [itcl::code $this AdjustSetting -isosurfaceshading] \
1625        -font "Arial 9"
1626
1627    checkbutton $inner.axes \
1628        -text "Axes" \
1629        -variable [itcl::scope _settings(-axesvisible)] \
1630        -command [itcl::code $this AdjustSetting -axesvisible] \
1631        -font "Arial 9"
1632
1633    checkbutton $inner.grid \
1634        -text "Grid" \
1635        -variable [itcl::scope _settings(-gridvisible)] \
1636        -command [itcl::code $this AdjustSetting -gridvisible] \
1637        -font "Arial 9"
1638
1639    checkbutton $inner.outline \
1640        -text "Outline" \
1641        -variable [itcl::scope _settings(-outlinevisible)] \
1642        -command [itcl::code $this AdjustSetting -outlinevisible] \
1643        -font "Arial 9"
1644
1645    checkbutton $inner.legend \
1646        -text "Legend" \
1647        -variable [itcl::scope _settings(-legendvisible)] \
1648        -command [itcl::code $this AdjustSetting -legendvisible] \
1649        -font "Arial 9"
1650
1651    checkbutton $inner.volume \
1652        -text "Volume" \
1653        -variable [itcl::scope _settings(-volume)] \
1654        -command [itcl::code $this AdjustSetting -volume] \
1655        -font "Arial 9"
1656
1657    label $inner.background_l -text "Background" -font "Arial 9"
1658    itk_component add background {
1659        Rappture::Combobox $inner.background -width 10 -editable no
1660    }
1661    $inner.background choices insert end \
1662        "black"              "black"            \
1663        "white"              "white"            \
1664        "grey"               "grey"             
1665
1666    $itk_component(background) value $_settings(-background)
1667    bind $inner.background <<Value>> \
1668        [itcl::code $this AdjustSetting -background]
1669
1670    blt::table $inner \
1671        0,0 $inner.axes  -cspan 2 -anchor w \
1672        1,0 $inner.grid  -cspan 2 -anchor w \
1673        2,0 $inner.outline  -cspan 2 -anchor w \
1674        3,0 $inner.volume  -cspan 2 -anchor w \
1675        4,0 $inner.legend  -cspan 2 -anchor w \
1676        5,0 $inner.background_l       -anchor e -pady 2 \
1677        5,1 $inner.background                   -fill x \
1678
1679    if 0 {
1680    bind $inner <Map> [itcl::code $this GetVolumeInfo $inner]
1681    }
1682    blt::table configure $inner r* -resize none
1683    blt::table configure $inner r6 -resize expand
1684}
1685
1686itcl::body Rappture::NanovisViewer::BuildVolumeTab {} {
1687    set inner [$itk_component(main) insert end \
1688        -title "Volume Settings" \
1689        -icon [Rappture::icon volume-on]]
1690    $inner configure -borderwidth 4
1691
1692    set fg [option get $itk_component(hull) font Font]
1693    #set bfg [option get $itk_component(hull) boldFont Font]
1694
1695    label $inner.shading -text "Shading:" -font $fg
1696
1697    checkbutton $inner.light2side -text "Two-sided lighting" -font $fg \
1698        -variable [itcl::scope _settings(-light2side)] \
1699        -command [itcl::code $this AdjustSetting -light2side]
1700
1701    checkbutton $inner.visibility -text "Visible" -font $fg \
1702        -variable [itcl::scope _settings(-volumevisible)] \
1703        -command [itcl::code $this AdjustSetting -volumevisible]
1704
1705    label $inner.dim -text "Glow" -font $fg
1706    ::scale $inner.light -from 0 -to 100 -orient horizontal \
1707        -variable [itcl::scope _settings(-light)] \
1708        -width 10 \
1709        -showvalue off -command [itcl::code $this AdjustSetting -light]
1710    label $inner.bright -text "Surface" -font $fg
1711
1712    # Opacity
1713    label $inner.fog -text "Clear" -font $fg
1714    ::scale $inner.transp -from 0 -to 100 -orient horizontal \
1715        -variable [itcl::scope _widget(-volumeopacity)] \
1716        -width 10 \
1717        -showvalue off -command [itcl::code $this AdjustSetting -volumeopacity]
1718    label $inner.plastic -text "Opaque" -font $fg
1719
1720    # Tooth thickness
1721    label $inner.thin -text "Thin" -font $fg
1722    ::scale $inner.thickness -from 0 -to 1000 -orient horizontal \
1723        -variable [itcl::scope _settings(-thickness)] \
1724        -width 10 \
1725        -showvalue off -command [itcl::code $this AdjustSetting -thickness]
1726    label $inner.thick -text "Thick" -font $fg
1727
1728    # Colormap
1729    label $inner.colormap_l -text "Colormap" -font $fg
1730    itk_component add colormap {
1731        Rappture::Combobox $inner.colormap -width 10 -editable no
1732    }
1733
1734    $inner.colormap choices insert end [GetColormapList -includeDefault -includeNone]
1735    bind $inner.colormap <<Value>> \
1736        [itcl::code $this AdjustSetting -colormap]
1737    $itk_component(colormap) value "default"
1738    set _settings(-colormap) "default"
1739
1740    # Component
1741    label $inner.volcomponents_l -text "Component" -font $fg
1742    itk_component add volcomponents {
1743        Rappture::Combobox $inner.volcomponents -editable no
1744    }
1745    bind $inner.volcomponents <<Value>> \
1746        [itcl::code $this AdjustSetting -current]
1747
1748    blt::table $inner \
1749        0,0 $inner.volcomponents_l -anchor e -cspan 2 \
1750        0,2 $inner.volcomponents             -cspan 3 -fill x \
1751        1,0 $inner.shading -cspan 4 -anchor w -pady {10 2} \
1752        2,0 $inner.light2side -cspan 4 -anchor w -pady 2 \
1753        3,0 $inner.visibility -cspan 4 -anchor w -pady 2 \
1754        4,0 $inner.dim -anchor e -pady 2 \
1755        4,1 $inner.light -cspan 2 -pady 2 -fill x \
1756        4,3 $inner.bright -anchor w -pady 2 \
1757        5,0 $inner.fog -anchor e -pady 2 \
1758        5,1 $inner.transp -cspan 2 -pady 2 -fill x \
1759        5,3 $inner.plastic -anchor w -pady 2 \
1760        6,0 $inner.thin -anchor e -pady 2 \
1761        6,1 $inner.thickness -cspan 2 -pady 2 -fill x\
1762        6,3 $inner.thick -anchor w -pady 2
1763
1764    blt::table configure $inner c0 c1 c3 r* -resize none
1765    blt::table configure $inner r7 -resize expand
1766}
1767
1768itcl::body Rappture::NanovisViewer::BuildCutplanesTab {} {
1769    set inner [$itk_component(main) insert end \
1770        -title "Cutplane Settings" \
1771        -icon [Rappture::icon cutbutton]]
1772    $inner configure -borderwidth 4
1773
1774    checkbutton $inner.visible \
1775        -text "Show Cutplanes" \
1776        -variable [itcl::scope _settings(-cutplanesvisible)] \
1777        -command [itcl::code $this AdjustSetting -cutplanesvisible] \
1778        -font "Arial 9"
1779
1780    # X-value slicer...
1781    itk_component add xCutButton {
1782        Rappture::PushButton $inner.xbutton \
1783            -onimage [Rappture::icon x-cutplane] \
1784            -offimage [Rappture::icon x-cutplane] \
1785            -command [itcl::code $this AdjustSetting -xcutplanevisible] \
1786            -variable [itcl::scope _settings(-xcutplanevisible)]
1787    }
1788    Rappture::Tooltip::for $itk_component(xCutButton) \
1789        "Toggle the X cut plane on/off"
1790    $itk_component(xCutButton) select
1791
1792    itk_component add xCutScale {
1793        ::scale $inner.xval -from 100 -to 0 \
1794            -width 10 -orient vertical -showvalue off \
1795            -borderwidth 1 -highlightthickness 0 \
1796            -command [itcl::code $this Slice move x] \
1797            -variable [itcl::scope _settings(-xcutplaneposition)]
1798    } {
1799        usual
1800        ignore -borderwidth -highlightthickness
1801    }
1802    # Set the default cutplane value before disabling the scale.
1803    $itk_component(xCutScale) set 50
1804    $itk_component(xCutScale) configure -state disabled
1805    Rappture::Tooltip::for $itk_component(xCutScale) \
1806        "@[itcl::code $this SlicerTip x]"
1807
1808    # Y-value slicer...
1809    itk_component add yCutButton {
1810        Rappture::PushButton $inner.ybutton \
1811            -onimage [Rappture::icon y-cutplane] \
1812            -offimage [Rappture::icon y-cutplane] \
1813            -command [itcl::code $this AdjustSetting -ycutplanevisible] \
1814            -variable [itcl::scope _settings(-ycutplanevisible)]
1815    }
1816    Rappture::Tooltip::for $itk_component(yCutButton) \
1817        "Toggle the Y cut plane on/off"
1818    $itk_component(yCutButton) select
1819
1820    itk_component add yCutScale {
1821        ::scale $inner.yval -from 100 -to 0 \
1822            -width 10 -orient vertical -showvalue off \
1823            -borderwidth 1 -highlightthickness 0 \
1824            -command [itcl::code $this Slice move y] \
1825            -variable [itcl::scope _settings(-ycutplaneposition)]
1826    } {
1827        usual
1828        ignore -borderwidth -highlightthickness
1829    }
1830    Rappture::Tooltip::for $itk_component(yCutScale) \
1831        "@[itcl::code $this SlicerTip y]"
1832    # Set the default cutplane value before disabling the scale.
1833    $itk_component(yCutScale) set 50
1834    $itk_component(yCutScale) configure -state disabled
1835
1836    # Z-value slicer...
1837    itk_component add zCutButton {
1838        Rappture::PushButton $inner.zbutton \
1839            -onimage [Rappture::icon z-cutplane] \
1840            -offimage [Rappture::icon z-cutplane] \
1841            -command [itcl::code $this AdjustSetting -zcutplanevisible] \
1842            -variable [itcl::scope _settings(-zcutplanevisible)]
1843    }
1844    Rappture::Tooltip::for $itk_component(zCutButton) \
1845        "Toggle the Z cut plane on/off"
1846    $itk_component(zCutButton) select
1847
1848    itk_component add zCutScale {
1849        ::scale $inner.zval -from 100 -to 0 \
1850            -width 10 -orient vertical -showvalue off \
1851            -borderwidth 1 -highlightthickness 0 \
1852            -command [itcl::code $this Slice move z] \
1853            -variable [itcl::scope _settings(-zcutplaneposition)]
1854    } {
1855        usual
1856        ignore -borderwidth -highlightthickness
1857    }
1858    $itk_component(zCutScale) set 50
1859    $itk_component(zCutScale) configure -state disabled
1860    Rappture::Tooltip::for $itk_component(zCutScale) \
1861        "@[itcl::code $this SlicerTip z]"
1862
1863    blt::table $inner \
1864        0,1 $inner.visible              -anchor w -pady 2 -cspan 4 \
1865        1,1 $itk_component(xCutScale) \
1866        1,2 $itk_component(yCutScale) \
1867        1,3 $itk_component(zCutScale) \
1868        2,1 $itk_component(xCutButton) \
1869        2,2 $itk_component(yCutButton) \
1870        2,3 $itk_component(zCutButton)
1871
1872    blt::table configure $inner r0 r1 r2 c* -resize none
1873    blt::table configure $inner r3 c4 -resize expand
1874    blt::table configure $inner c0 -width 2
1875    blt::table configure $inner c1 c2 c3 -padx 2
1876}
1877
1878itcl::body Rappture::NanovisViewer::BuildCameraTab {} {
1879    set inner [$itk_component(main) insert end \
1880        -title "Camera Settings" \
1881        -icon [Rappture::icon camera]]
1882    $inner configure -borderwidth 4
1883
1884    label $inner.view_l -text "view" -font "Arial 9"
1885    set f [frame $inner.view]
1886    foreach side { front back left right top bottom } {
1887        button $f.$side  -image [Rappture::icon view$side] \
1888            -command [itcl::code $this SetOrientation $side]
1889        Rappture::Tooltip::for $f.$side "Change the view to $side"
1890        pack $f.$side -side left
1891    }
1892
1893    blt::table $inner \
1894        0,0 $inner.view_l -anchor e -pady 2 \
1895        0,1 $inner.view -anchor w -pady 2
1896
1897    set row 1
1898    set labels { qw qx qy qz xpan ypan zoom }
1899    foreach tag $labels {
1900        label $inner.${tag}label -text $tag -font "Arial 9"
1901        entry $inner.${tag} -font "Arial 9"  -bg white \
1902            -textvariable [itcl::scope _settings(-$tag)]
1903        bind $inner.${tag} <Return> \
1904            [itcl::code $this camera set -${tag}]
1905        bind $inner.${tag} <KP_Enter> \
1906            [itcl::code $this camera set -${tag}]
1907        blt::table $inner \
1908            $row,0 $inner.${tag}label -anchor e -pady 2 \
1909            $row,1 $inner.${tag} -anchor w -pady 2
1910        blt::table configure $inner r$row -resize none
1911        incr row
1912    }
1913
1914    blt::table configure $inner c* r* -resize none
1915    blt::table configure $inner c2 -resize expand
1916    blt::table configure $inner r$row -resize expand
1917}
1918
1919# ----------------------------------------------------------------------
1920# USAGE: Slice move x|y|z <newval>
1921#
1922# Called automatically when the user drags the slider to move the
1923# cut plane that slices 3D data.  Gets the current value from the
1924# slider and moves the cut plane to the appropriate point in the
1925# data set.
1926# ----------------------------------------------------------------------
1927itcl::body Rappture::NanovisViewer::Slice {option args} {
1928    switch -- $option {
1929        move {
1930            if {[llength $args] != 2} {
1931                error "wrong # args: should be \"Slice move x|y|z newval\""
1932            }
1933            set axis [lindex $args 0]
1934            set newval [lindex $args 1]
1935
1936            set newpos [expr {0.01*$newval}]
1937            set datasets [CurrentDatasets -cutplanes]
1938            set tag [lindex $datasets 0]
1939            SendCmd "cutplane position $newpos $axis $tag"
1940        }
1941        default {
1942            error "bad option \"$option\": should be axis, move, or volume"
1943        }
1944    }
1945}
1946
1947# ----------------------------------------------------------------------
1948# USAGE: SlicerTip <axis>
1949#
1950# Used internally to generate a tooltip for the x/y/z slicer controls.
1951# Returns a message that includes the current slicer value.
1952# ----------------------------------------------------------------------
1953itcl::body Rappture::NanovisViewer::SlicerTip {axis} {
1954    set val [$itk_component(${axis}CutScale) get]
1955    return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
1956}
1957
1958
1959itcl::body Rappture::NanovisViewer::DoResize {} {
1960    $_arcball resize $_width $_height
1961    SendCmd "screen size $_width $_height"
1962    set _resizePending 0
1963}
1964
1965itcl::body Rappture::NanovisViewer::EventuallyResize { w h } {
1966    set _width $w
1967    set _height $h
1968    $_arcball resize $w $h
1969    if { !$_resizePending } {
1970        $_dispatcher event -idle !resize
1971        set _resizePending 1
1972    }
1973}
1974
1975itcl::body Rappture::NanovisViewer::EventuallyRedrawLegend {} {
1976    if { !$_resizeLegendPending } {
1977        $_dispatcher event -idle !legend
1978        set _resizeLegendPending 1
1979    }
1980}
1981
1982#  camera --
1983#
1984itcl::body Rappture::NanovisViewer::camera {option args} {
1985    switch -- $option {
1986        "show" {
1987            puts [array get _view]
1988        }
1989        "set" {
1990            set what [lindex $args 0]
1991            set x $_settings($what)
1992            set code [catch { string is double $x } result]
1993            if { $code != 0 || !$result } {
1994                set _settings($what) $_view($what)
1995                return
1996            }
1997            switch -- $what {
1998                "-xpan" - "-ypan" {
1999                    set _view($what) $_settings($what)
2000                    PanCamera
2001                }
2002                "-qx" - "-qy" - "-qz" - "-qw" {
2003                    set _view($what) $_settings($what)
2004                    set q [ViewToQuaternion]
2005                    $_arcball quaternion $q
2006                    SendCmd "camera orient $q"
2007                }
2008                "-zoom" {
2009                    set _view($what) $_settings($what)
2010                    SendCmd "camera zoom $_view($what)"
2011                }
2012            }
2013        }
2014    }
2015}
2016
2017itcl::body Rappture::NanovisViewer::GetVolumeInfo { w } {
2018    set flowobj ""
2019    foreach key [array names _obj2flow] {
2020        set flowobj $_obj2flow($key)
2021        break
2022    }
2023    if { $flowobj == "" } {
2024        return
2025    }
2026    if { [winfo exists $w.frame] } {
2027        destroy $w.frame
2028    }
2029    set inner [frame $w.frame]
2030    blt::table $w \
2031        5,0 $inner -fill both -cspan 2 -anchor nw
2032    array set hints [$dataobj hints]
2033
2034    label $inner.volumes -text "Volumes" -font "Arial 9 bold"
2035    blt::table $inner \
2036        1,0 $inner.volumes  -anchor w \
2037    blt::table configure $inner c0 c1 -resize none
2038    blt::table configure $inner c2 -resize expand
2039
2040    set row 3
2041    set volumes [get]
2042    if { [llength $volumes] > 0 } {
2043        blt::table $inner $row,0 $inner.volumes  -anchor w
2044        incr row
2045    }
2046    foreach vol $volumes {
2047        array unset info
2048        array set info $vol
2049        set name $info(name)
2050        if { ![info exists _settings(-volumevisible-$name)] } {
2051            set _settings(-volumevisible-$name) $info(hide)
2052        }
2053        checkbutton $inner.vol$row -text $info(label) \
2054            -variable [itcl::scope _settings(-volumevisible-$name)] \
2055            -onvalue 0 -offvalue 1 \
2056            -command [itcl::code $this ToggleVolume $key $name] \
2057            -font "Arial 9"
2058        Rappture::Tooltip::for $inner.vol$row $info(description)
2059        blt::table $inner $row,0 $inner.vol$row -anchor w
2060        if { !$_settings(-volume-$name) } {
2061            $inner.vol$row select
2062        }
2063        incr row
2064    }
2065    blt::table configure $inner r* -resize none
2066    blt::table configure $inner r$row -resize expand
2067    blt::table configure $inner c3 -resize expand
2068    event generate [winfo parent [winfo parent $w]] <Configure>
2069}
2070
2071itcl::body Rappture::NanovisViewer::ToggleVolume { tag name } {
2072    set bool $_settings(-volumevisible-$name)
2073    SendCmd "volume state $bool $name"
2074}
2075
2076itcl::body Rappture::NanovisViewer::SetOrientation { side } {
2077    array set positions {
2078        front "1 0 0 0"
2079        back  "0 0 1 0"
2080        left  "0.707107 0 -0.707107 0"
2081        right "0.707107 0 0.707107 0"
2082        top   "0.707107 -0.707107 0 0"
2083        bottom "0.707107 0.707107 0 0"
2084    }
2085    foreach name { -qw -qx -qy -qz } value $positions($side) {
2086        set _view($name) $value
2087    }
2088    set q [ViewToQuaternion]
2089    $_arcball quaternion $q
2090    SendCmd "camera orient $q"
2091    SendCmd "camera reset"
2092    set _view(-xpan) 0
2093    set _view(-ypan) 0
2094    set _view(-zoom) 1.0
2095    set _settings(-xpan) $_view(-xpan)
2096    set _settings(-ypan) $_view(-ypan)
2097    set _settings(-zoom) $_view(-zoom)
2098}
2099
2100
2101#
2102# InitComponentSettings --
2103#
2104#    Initializes the volume settings for a specific component. This should
2105#    match what's used as global settings above. This is called the first
2106#    time we try to switch to a given component in SwitchComponent below.
2107#
2108itcl::body Rappture::NanovisViewer::InitComponentSettings { cname } {
2109    foreach {key value} {
2110        -colormap          "default"
2111        -light             40
2112        -light2side        1
2113        -thickness         350
2114        -volumeopacity     1.0
2115        -volumevisible     1
2116    } {
2117        if { ![info exists _settings($cname${key})] } {
2118            # Don't override existing component settings
2119            set _settings($cname${key}) $value
2120        }
2121    }
2122}
2123
2124#
2125# SwitchComponent --
2126#
2127#    This is called when the current component is changed by the dropdown
2128#    menu in the volume tab.  It synchronizes the global volume settings
2129#    with the settings of the new current component.
2130#
2131itcl::body Rappture::NanovisViewer::SwitchComponent { cname } {
2132    if { ![info exists _settings($cname-light)] } {
2133        InitComponentSettings $cname
2134    }
2135    if { $_settings(-colormap) != $_settings($cname-colormap) } {
2136        set _settings(-colormap)         $_settings($cname-colormap)
2137        EventuallyRedrawLegend
2138    }
2139    # _settings variables change widgets, except for colormap
2140    set _settings(-light)            $_settings($cname-light)
2141    set _settings(-light2side)       $_settings($cname-light2side)
2142    set _settings(-volumeopacity)    $_settings($cname-volumeopacity)
2143    set _settings(-thickness)        $_settings($cname-thickness)
2144    set _settings(-volumevisible)    $_settings($cname-volumevisible)
2145    $itk_component(colormap) value   $_settings($cname-colormap)
2146
2147    set _widget(-volumeopacity) [expr $_settings(-volumeopacity) * 100.0]
2148
2149    set _current $cname;                # Reset the current component
2150}
2151
2152#
2153# BuildVolumeComponents --
2154#
2155#    This is called from the "scale" method which is called when a new
2156#    dataset is added or deleted.  It repopulates the dropdown menu of
2157#    volume component names.  It sets the current component to the first
2158#    component in the list (of components found).  Finally, if there is
2159#    only one component, don't display the label or the combobox in the
2160#    volume settings tab.
2161#
2162itcl::body Rappture::NanovisViewer::BuildVolumeComponents {} {
2163    $itk_component(volcomponents) choices delete 0 end
2164    foreach name $_componentsList {
2165        $itk_component(volcomponents) choices insert end $name $name
2166    }
2167    set _current [lindex $_componentsList 0]
2168    $itk_component(volcomponents) value $_current
2169    set parent [winfo parent $itk_component(volcomponents)]
2170    if { [llength $_componentsList] <= 1 } {
2171        # Unpack the components label and dropdown if there's only one
2172        # component.
2173        blt::table forget $parent.volcomponents_l $parent.volcomponents
2174    } else {
2175        # Pack the components label and dropdown into the table there's
2176        # more than one component to select.
2177        blt::table $parent \
2178            0,0 $parent.volcomponents_l -anchor e -cspan 2 \
2179            0,2 $parent.volcomponents -cspan 3 -fill x
2180    }
2181}
2182
2183#
2184# GetDatasetsWithComponents --
2185#
2186#    Returns a list of all the datasets (known by the combination of their
2187#    data object and component name) that match the given component name.
2188#    For example, this is used where we want to change the settings of
2189#    volumes that have the current component.
2190#
2191itcl::body Rappture::NanovisViewer::GetDatasetsWithComponent { cname } {
2192    if { ![info exists _volcomponents($cname)] } {
2193        return ""
2194    }
2195    set list ""
2196    foreach tag $_volcomponents($cname) {
2197        if { ![info exists _serverDatasets($tag)] } {
2198            continue
2199        }
2200        lappend list $tag
2201    }
2202    return $list
2203}
2204
2205#
2206# HideAllMarkers --
2207#
2208#    Hide all the markers in all the transfer functions.  Can't simply
2209#    delete and recreate markers from the <style> since the user may have
2210#    created, deleted, or moved markers.
2211#
2212itcl::body Rappture::NanovisViewer::HideAllMarkers {} {
2213    foreach cname [array names _transferFunctionEditors] {
2214        $_transferFunctionEditors($cname) hideMarkers
2215    }
2216}
2217
2218itcl::body Rappture::NanovisViewer::GetColormap { cname color } {
2219    if { $color == "default" } {
2220        return $_cname2defaultcolormap($cname)
2221    }
2222    return [ColorsToColormap $color]
2223}
2224
2225itcl::body Rappture::NanovisViewer::GetAlphamap { cname name } {
2226    if { $name == "default" } {
2227        return $_cname2defaultalphamap($cname)
2228    }
2229    return [NameToAlphamap $name]
2230}
2231
2232itcl::body Rappture::NanovisViewer::ResetColormap { cname color } {
2233    # Get the current transfer function
2234    if { ![info exists _cname2transferFunction($cname)] } {
2235        return
2236    }
2237    foreach { cmap wmap } $_cname2transferFunction($cname) break
2238    set cmap [GetColormap $cname $color]
2239    set _cname2transferFunction($cname) [list $cmap $wmap]
2240    SendCmd [list transfunc define $cname $cmap $wmap]
2241    EventuallyRedrawLegend
2242}
2243
2244itcl::body Rappture::NanovisViewer::ComputeAlphamap { cname } {
2245    if { ![info exists _transferFunctionEditors($cname)] } {
2246        return [list 0.0 0.0 1.0 1.0]
2247    }
2248    if { ![info exists _settings($cname-light)] } {
2249        InitComponentSettings $cname
2250    }
2251
2252    set isovalues [$_transferFunctionEditors($cname) values]
2253
2254    # Transfer function should be normalized with [0,1] range
2255    # The volume shading opacity setting is used to scale opacity
2256    # in the volume shader.
2257    set max 1.0
2258
2259    # Use the component-wise thickness setting from the slider
2260    # settings widget
2261    # Scale values between 0.00001 and 0.01000
2262    set delta [expr {double($_settings($cname-thickness)) * 0.0001}]
2263   
2264    set first [lindex $isovalues 0]
2265    set last [lindex $isovalues end]
2266    set wmap ""
2267    if { $first == "" || $first != 0.0 } {
2268        lappend wmap 0.0 0.0
2269    }
2270    foreach x $isovalues {
2271        set x1 [expr {$x-$delta-0.00001}]
2272        set x2 [expr {$x-$delta}]
2273        set x3 [expr {$x+$delta}]
2274        set x4 [expr {$x+$delta+0.00001}]
2275        if { $x1 < 0.0 } {
2276            set x1 0.0
2277        } elseif { $x1 > 1.0 } {
2278            set x1 1.0
2279        }
2280        if { $x2 < 0.0 } {
2281            set x2 0.0
2282        } elseif { $x2 > 1.0 } {
2283            set x2 1.0
2284        }
2285        if { $x3 < 0.0 } {
2286            set x3 0.0
2287        } elseif { $x3 > 1.0 } {
2288            set x3 1.0
2289        }
2290        if { $x4 < 0.0 } {
2291            set x4 0.0
2292        } elseif { $x4 > 1.0 } {
2293            set x4 1.0
2294        }
2295        # add spikes in the middle
2296        lappend wmap $x1 0.0
2297        lappend wmap $x2 $max
2298        lappend wmap $x3 $max
2299        lappend wmap $x4 0.0
2300    }
2301    if { $last == "" || $last != 1.0 } {
2302        lappend wmap 1.0 0.0
2303    }
2304    return $wmap
2305}
2306
2307itcl::body Rappture::NanovisViewer::NameToAlphamap { name } {
2308    switch -- $name {
2309        "ramp-up" {
2310            set wmap {
2311                0.0 0.0
2312                1.0 1.0
2313            }
2314        }
2315        "ramp-down" {
2316            set wmap {
2317                0.0 1.0
2318                1.0 0.0
2319            }
2320        }
2321        "vee" {
2322            set wmap {
2323                0.0 1.0
2324                0.5 0.0
2325                1.0 1.0
2326            }
2327        }
2328        "tent-1" {
2329            set wmap {
2330                0.0 0.0
2331                0.5 1.0
2332                1.0 0.0
2333            }
2334        }
2335        "tent-2" {
2336            set wmap {
2337                0.0 0.0
2338                0.25 1.0
2339                0.5 0.0
2340                0.75 1.0
2341                1.0 0.0
2342            }
2343        }
2344        "tent-3" {
2345            set wmap {
2346                0.0 0.0
2347                0.16666 1.0
2348                0.33333 0.0
2349                0.5     1.0
2350                0.66666 0.0
2351                0.83333 1.0
2352                1.0 0.0
2353            }
2354        }
2355        "tent-4" {
2356            set wmap {
2357                0.0     0.0
2358                0.125   1.0
2359                0.25    0.0
2360                0.375   1.0
2361                0.5     0.0       
2362                0.625   1.0
2363                0.75    0.0
2364                0.875   1.0
2365                1.0     0.0
2366            }
2367        }
2368        "sinusoid-1" {
2369            set wmap {
2370                0.0                     0.000 0.600 0.800
2371                0.14285714285714285     0.400 0.900 1.000
2372                0.2857142857142857      0.600 1.000 1.000
2373                0.42857142857142855     0.800 1.000 1.000
2374                0.5714285714285714      0.900 0.900 0.900
2375                0.7142857142857143      0.600 0.600 0.600
2376                0.8571428571428571      0.400 0.400 0.400
2377                1.0                     0.200 0.200 0.200
2378            }
2379        }
2380        "sinusoid-2" {
2381            set wmap {
2382                0.0                     0.900 1.000 1.000
2383                0.1111111111111111      0.800 0.983 1.000
2384                0.2222222222222222      0.700 0.950 1.000
2385                0.3333333333333333      0.600 0.900 1.000
2386                0.4444444444444444      0.500 0.833 1.000
2387                0.5555555555555556      0.400 0.750 1.000
2388                0.6666666666666666      0.300 0.650 1.000
2389                0.7777777777777778      0.200 0.533 1.000
2390                0.8888888888888888      0.100 0.400 1.000
2391                1.0                     0.000 0.250 1.000
2392            }
2393        }
2394        "sinusoid-6" {
2395            set wmap {
2396                0.0                             0.200   0.100   0.000
2397                0.09090909090909091             0.400   0.187   0.000
2398                0.18181818181818182             0.600   0.379   0.210
2399                0.2727272727272727              0.800   0.608   0.480
2400                0.36363636363636365             0.850   0.688   0.595
2401                0.45454545454545453             0.950   0.855   0.808
2402                0.5454545454545454              0.800   0.993   1.000
2403                0.6363636363636364              0.600   0.973   1.000
2404                0.7272727272727273              0.400   0.940   1.000
2405                0.8181818181818182              0.200   0.893   1.000
2406                0.9090909090909091              0.000   0.667   0.800
2407                1.0                             0.000   0.480   0.600
2408            }
2409        }
2410        "sinusoid-10" {
2411            set wmap {
2412                0.0                             0.000   0.480   0.600
2413                0.09090909090909091             0.000   0.667   0.800
2414                0.18181818181818182             0.200   0.893   1.000
2415                0.2727272727272727              0.400   0.940   1.000
2416                0.36363636363636365             0.600   0.973   1.000
2417                0.45454545454545453             0.800   0.993   1.000
2418                0.5454545454545454              0.950   0.855   0.808
2419                0.6363636363636364              0.850   0.688   0.595
2420                0.7272727272727273              0.800   0.608   0.480
2421                0.8181818181818182              0.600   0.379   0.210
2422                0.9090909090909091              0.400   0.187   0.000
2423                1.0                             0.200   0.100   0.000
2424            }
2425        }
2426        "step-2" {
2427            set wmap {
2428                0.0                             0.000   0.167   1.000
2429                0.09090909090909091             0.100   0.400   1.000
2430                0.18181818181818182             0.200   0.600   1.000
2431                0.2727272727272727              0.400   0.800   1.000
2432                0.36363636363636365             0.600   0.933   1.000
2433                0.45454545454545453             0.800   1.000   1.000
2434                0.5454545454545454              1.000   1.000   0.800
2435                0.6363636363636364              1.000   0.933   0.600
2436                0.7272727272727273              1.000   0.800   0.400
2437                0.8181818181818182              1.000   0.600   0.200
2438                0.9090909090909091              1.000   0.400   0.100
2439                1.0                             1.000   0.167   0.000
2440            }
2441        }
2442        "step-5" {
2443            set wmap {
2444                0.0                             1.000   0.167   0.000
2445                0.09090909090909091             1.000   0.400   0.100
2446                0.18181818181818182             1.000   0.600   0.200
2447                0.2727272727272727              1.000   0.800   0.400
2448                0.36363636363636365             1.000   0.933   0.600
2449                0.45454545454545453             1.000   1.000   0.800
2450                0.5454545454545454              0.800   1.000   1.000
2451                0.6363636363636364              0.600   0.933   1.000
2452                0.7272727272727273              0.400   0.800   1.000
2453                0.8181818181818182              0.200   0.600   1.000
2454                0.9090909090909091              0.100   0.400   1.000
2455                1.0                             0.000   0.167   1.000
2456            }
2457        }
2458        "step-12" {
2459            set wmap {
2460                "#EE82EE"
2461                "#4B0082"
2462                "blue"
2463                "#008000"
2464                "yellow"
2465                "#FFA500"
2466                "red"
2467            }
2468        }
2469        default {
2470        }
2471    }
2472    return ""
2473}
2474
2475itcl::body Rappture::NanovisViewer::SetObjectStyle { dataobj cname } {
2476    array set styles {
2477        -opacity  0.5
2478    }
2479    array set styles [lindex [$dataobj components -style $cname] 0]
2480    set _settings($cname-volumeopacity) $styles(-opacity)
2481    set tag $dataobj-$cname
2482    SendCmd "volume shading opacity $_settings($cname-volumeopacity) $tag"
2483    NameTransferFunction $dataobj $cname
2484}
Note: See TracBrowser for help on using the repository browser.