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

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

merge r5187 from 1.4 branch

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