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

Last change on this file since 5246 was 5246, checked in by ldelgass, 5 years ago

typo in comment

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