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

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

Add method to VisViewer? to get list of pre-defined colormap names. There was
an issue with the name "blue" since it could also be used for a constant color,
but the named "blue" was actually a white-to-blue ramp, so it is now named
"white-to-blue". Also, some work on expanded color settings for glyph viewer.

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