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

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

merge background settings from trunk

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