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

Last change on this file since 3370 was 3370, checked in by ldelgass, 12 years ago

Fix volume shading sliders in nanovisviewer (broken in rev. 3366)

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