source: trunk/gui/scripts/nanovisviewer.tcl @ 4765

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

sync up camera settings across viewers

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