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

Last change on this file since 4725 was 4725, checked in by ldelgass, 9 years ago

Collapse redundant transp/opacity settings in flowvisviewer in place of single
opacity setting.

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