source: branches/1.3/gui/scripts/nanovisviewer.tcl @ 4560

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

lighting fixes

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