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

Last change on this file since 4750 was 4750, checked in by ldelgass, 7 years ago

merge r4749 from trunk

File size: 87.7 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2# ----------------------------------------------------------------------
3#  COMPONENT: nanovisviewer - 3D volume rendering
4#
5#  This widget performs volume rendering on 3D scalar/vector datasets.
6#  It connects to the Nanovis server running on a rendering farm,
7#  transmits data, and displays the results.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itk
16package require BLT
17package require Img
18
19#
20# FIXME:
21#       Need to Add DX readers this client to examine the data before
22#       it's sent to the server.  This will eliminate 90% of the insanity in
23#       computing the limits of all the volumes.  I can rip out all the
24#       "receive data" "send transfer function" event crap.
25#
26#       This means we can compute the transfer function (relative values) and
27#       draw the legend min/max values without waiting for the information to
28#       come from the server.  This will also prevent the flashing that occurs
29#       when a new volume is drawn (using the default transfer function) and
30#       then when the correct transfer function has been sent and linked to
31#       the volume. 
32#
33option add *NanovisViewer.width 4i widgetDefault
34option add *NanovisViewer*cursor crosshair widgetDefault
35option add *NanovisViewer.height 4i widgetDefault
36option add *NanovisViewer.foreground black widgetDefault
37option add *NanovisViewer.controlBackground gray widgetDefault
38option add *NanovisViewer.controlDarkBackground #999999 widgetDefault
39option add *NanovisViewer.plotBackground black widgetDefault
40option add *NanovisViewer.plotForeground white widgetDefault
41option add *NanovisViewer.plotOutline gray widgetDefault
42option add *NanovisViewer.font \
43    -*-helvetica-medium-r-normal-*-12-* widgetDefault
44
45# must use this name -- plugs into Rappture::resources::load
46proc NanovisViewer_init_resources {} {
47    Rappture::resources::register \
48        nanovis_server Rappture::NanovisViewer::SetServerList
49}
50
51itcl::class Rappture::NanovisViewer {
52    inherit Rappture::VisViewer
53
54    itk_option define -plotforeground plotForeground Foreground ""
55    itk_option define -plotbackground plotBackground Background ""
56    itk_option define -plotoutline plotOutline PlotOutline ""
57
58    constructor { hostlist args } {
59        Rappture::VisViewer::constructor $hostlist
60    } {
61        # defined below
62    }
63    destructor {
64        # defined below
65    }
66    public proc SetServerList { namelist } {
67        Rappture::VisViewer::SetServerList "nanovis" $namelist
68    }
69    public method add {dataobj {settings ""}}
70    public method camera {option args}
71    public method delete {args}
72    public method disconnect {}
73    public method download {option args}
74    public method get {args}
75    public method isconnected {}
76    public method 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 SetObjectStyle { dataobj cname }
124    private method SetOrientation { side }
125    private method Slice {option args}
126    private method SlicerTip {axis}
127    private method SwitchComponent { cname }
128    private method ToggleVolume { tag name }
129    private method Zoom {option}
130    private method ViewToQuaternion {} {
131        return [list $_view(-qw) $_view(-qx) $_view(-qy) $_view(-qz)]
132    }
133
134    private variable _arcball ""
135
136    private variable _dlist ""     ;# list of data objects
137    private variable _obj2ovride   ;# maps dataobj => style override
138    private variable _serverDatasets   ;# contains all the dataobj-component
139                                   ;# to volumes in the server
140    private variable _recvdDatasets;    # list of data objs to send to server
141    private variable _dataset2style;    # maps dataobj-component to transfunc
142    private variable _style2datasets;   # maps tf back to list of
143                                        # dataobj-components using the tf.
144
145    private variable _reset 1;          # Connection to server has been reset.
146    private variable _click;            # Info used for rotate operations.
147    private variable _limits;           # Autoscale min/max for all axes
148    private variable _view;             # View params for 3D view
149    private variable _parsedFunction
150    private variable _transferFunctionEditors
151    private variable _settings
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 "version" "$Rappture::version"
678            lappend info "build" "$Rappture::build"
679            lappend info "svnurl" "$Rappture::svnurl"
680            lappend info "installdir" "$Rappture::installdir"
681            lappend info "hub" [exec hostname]
682            lappend info "client" "nanovisviewer"
683            lappend info "user" $user
684            lappend info "session" $session
685            SendCmd "clientinfo [list $info]"
686        }
687
688        set w [winfo width $itk_component(3dview)]
689        set h [winfo height $itk_component(3dview)]
690        EventuallyResize $w $h
691    }
692    return $result
693}
694
695#
696# isconnected --
697#
698#       Indicates if we are currently connected to the visualization server.
699#
700itcl::body Rappture::NanovisViewer::isconnected {} {
701    return [VisViewer::IsConnected]
702}
703
704#
705# disconnect --
706#
707itcl::body Rappture::NanovisViewer::disconnect {} {
708    Disconnect
709}
710
711#
712# Disconnect --
713#
714#       Clients use this method to disconnect from the current rendering
715#       server.
716#
717itcl::body Rappture::NanovisViewer::Disconnect {} {
718    VisViewer::Disconnect
719
720    # disconnected -- no more data sitting on server
721    array unset _serverDatasets
722}
723
724# ----------------------------------------------------------------------
725# USAGE: SendTransferFunctions
726# ----------------------------------------------------------------------
727itcl::body Rappture::NanovisViewer::SendTransferFunctions {} {
728    foreach cname [array names _volcomponents] {
729        ComputeTransferFunction $cname
730    }
731    FixLegend
732}
733
734# ----------------------------------------------------------------------
735# USAGE: ReceiveImage -bytes <size> -type <type> -token <token>
736#
737# Invoked automatically whenever the "image" command comes in from
738# the rendering server.  Indicates that binary image data with the
739# specified <size> will follow.
740# ----------------------------------------------------------------------
741itcl::body Rappture::NanovisViewer::ReceiveImage { args } {
742    array set info {
743        -token "???"
744        -bytes 0
745        -type image
746    }
747    array set info $args
748    set bytes [ReceiveBytes $info(-bytes)]
749    ReceiveEcho <<line "<read $info(-bytes) bytes"
750    if { $info(-type) == "image" } {
751        ReceiveEcho "for [image width $_image(plot)]x[image height $_image(plot)] image>"       
752        $_image(plot) configure -data $bytes
753    } elseif { $info(-type) == "print" } {
754        set tag $this-print-$info(-token)
755        set _hardcopy($tag) $bytes
756    }
757}
758
759#
760# DrawLegend --
761#
762itcl::body Rappture::NanovisViewer::DrawLegend { cname } {
763    set c $itk_component(legend)
764    set w [winfo width $c]
765    set h [winfo height $c]
766    set lx 10
767    set ly [expr {$h - 1}]
768    if {"" == [$c find withtag colorbar]} {
769        $c create image 10 10 -anchor nw \
770            -image $_image(legend) -tags colorbar
771        $c create text $lx $ly -anchor sw \
772            -fill $itk_option(-plotforeground) -tags "limits text vmin"
773        $c create text [expr {$w-$lx}] $ly -anchor se \
774            -fill $itk_option(-plotforeground) -tags "limits text vmax"
775        $c create text [expr {$w/2}] $ly -anchor s \
776            -fill $itk_option(-plotforeground) -tags "limits text title"
777        $c lower colorbar
778        $c bind colorbar <ButtonRelease-1> [itcl::code $this AddNewMarker %x %y]
779    }
780
781    # Display the markers used by the current transfer function.
782    HideAllMarkers
783    $_transferFunctionEditors($cname) showMarkers $_limits($cname)
784
785    foreach {min max} $_limits($cname) break
786    $c itemconfigure vmin -text [format %g $min]
787    $c coords vmin $lx $ly
788
789    $c itemconfigure vmax -text [format %g $max]
790    $c coords vmax [expr {$w-$lx}] $ly
791
792    set title [$_first hints label]
793    set units [$_first hints units]
794    if { $units != "" } {
795        set title "$title ($units)"
796    }
797    $c itemconfigure title -text $title
798    $c coords title [expr {$w/2}] $ly
799
800    # The colormap may have changed. Resync the slicers with the colormap.
801    InitSettings -cutplanesvisible -xcutplanevisible -ycutplanevisible \
802        -zcutplanevisible
803}
804
805#
806#
807# ReceiveLegend --
808#
809#       The procedure is the response from the render server to each "legend"
810#       command.  The server sends back a "legend" command invoked our
811#       the slave interpreter.  The purpose is to collect data of the image
812#       representing the legend in the canvas.  In addition, the
813#       active transfer function is displayed.
814#
815#
816itcl::body Rappture::NanovisViewer::ReceiveLegend { cname vmin vmax size } {
817    if { ![isconnected] } {
818        return
819    }
820    set bytes [ReceiveBytes $size]
821    $_image(legend) configure -data $bytes
822    ReceiveEcho <<line "<read $size bytes for [image width $_image(legend)]x[image height $_image(legend)] legend>"
823
824    DrawLegend $_current
825}
826
827#
828# ReceiveData --
829#
830#       The procedure is the response from the render server to each "data
831#       follows" command.  The server sends back a "data" command invoked our
832#       the slave interpreter.  The purpose is to collect the min/max of the
833#       volume sent to the render server.  Since the client (nanovisviewer)
834#       doesn't parse 3D data formats, we rely on the server (nanovis) to
835#       tell us what the limits are.  Once we've received the limits to all
836#       the data we've sent (tracked by _recvdDatasets) we can then determine
837#       what the transfer functions are for these volumes.
838#
839#
840#       Note: There is a considerable tradeoff in having the server report
841#             back what the data limits are.  It means that much of the code
842#             having to do with transfer-functions has to wait for the data
843#             to come back, since the isomarkers are calculated based upon
844#             the data limits.  The client code is much messier because of
845#             this.  The alternative is to parse any of the 3D formats on the
846#             client side.
847#
848itcl::body Rappture::NanovisViewer::ReceiveData { args } {
849    if { ![isconnected] } {
850        return
851    }
852
853    # Arguments from server are name value pairs. Stuff them in an array.
854    array set info $args
855
856    set tag $info(tag)
857    set parts [split $tag -]
858
859    #
860    # Volumes don't exist until we're told about them.
861    #
862    set dataobj [lindex $parts 0]
863    set _serverDatasets($tag) 1
864    if { $_settings(-volumevisible) && $dataobj == $_first } {
865        SendCmd "volume state 1 $tag"
866    }
867    set _limits($tag) [list $info(min)  $info(max)]
868    set _limits(v)    [list $info(vmin) $info(vmax)]
869
870    unset _recvdDatasets($tag)
871    if { [array size _recvdDatasets] == 0 } {
872        updateTransferFunctions
873    }
874}
875
876# ----------------------------------------------------------------------
877# USAGE: Rebuild
878#
879# Called automatically whenever something changes that affects the
880# data in the widget.  Clears any existing data and rebuilds the
881# widget to display new data.
882# ----------------------------------------------------------------------
883itcl::body Rappture::NanovisViewer::Rebuild {} {
884    set w [winfo width $itk_component(3dview)]
885    set h [winfo height $itk_component(3dview)]
886    if { $w < 2 || $h < 2 } {
887        $_dispatcher event -idle !rebuild
888        return
889    }
890
891    # Turn on buffering of commands to the server.  We don't want to
892    # be preempted by a server disconnect/reconnect (which automatically
893    # generates a new call to Rebuild).   
894    StartBufferingCommands
895
896    if { $_width != $w || $_height != $h || $_reset } {
897        set _width $w
898        set _height $h
899        $_arcball resize $w $h
900        DoResize
901    }
902
903    foreach dataobj [get] {
904        foreach cname [$dataobj components] {
905            set tag $dataobj-$cname
906            if { ![info exists _serverDatasets($tag)] } {
907                # Send the data as one huge base64-encoded mess -- yuck!
908                if { [$dataobj type] == "dx" } {
909                    if { ![$dataobj isvalid] } {
910                        puts stderr "??? $dataobj is invalid"
911                    }
912                    set data [$dataobj blob $cname]
913                } else {
914                    set data [$dataobj vtkdata $cname]
915                    if 0 {
916                        set f [open "/tmp/volume.vtk" "w"]
917                        puts $f $data
918                        close $f
919                    }
920                }
921                set nbytes [string length $data]
922                if { $_reportClientInfo }  {
923                    set info {}
924                    lappend info "tool_id"       [$dataobj hints toolid]
925                    lappend info "tool_name"     [$dataobj hints toolname]
926                    lappend info "tool_title"    [$dataobj hints tooltitle]
927                    lappend info "tool_command"  [$dataobj hints toolcommand]
928                    lappend info "tool_revision" [$dataobj hints toolrevision]
929                    lappend info "dataset_label" [$dataobj hints label]
930                    lappend info "dataset_size"  $nbytes
931                    lappend info "dataset_tag"   $tag
932                    SendCmd "clientinfo [list $info]"
933                }
934                SendCmd "volume data follows $nbytes $tag"
935                append _outbuf $data
936                set _recvdDatasets($tag) 1
937                set _serverDatasets($tag) 0
938            }
939            SetObjectStyle $dataobj $cname
940        }
941    }
942
943    # Outline seems to need to be reset every update.
944    InitSettings -outlinevisible -cutplanesvisible -current
945
946    set _first [lindex [get] 0]
947    if { $_reset } {
948        #
949        # Reset the camera and other view parameters
950        #
951        set _settings(-qw)    $_view(-qw)
952        set _settings(-qx)    $_view(-qx)
953        set _settings(-qy)    $_view(-qy)
954        set _settings(-qz)    $_view(-qz)
955        set _settings(-xpan)  $_view(-xpan)
956        set _settings(-ypan)  $_view(-ypan)
957        set _settings(-zoom)  $_view(-zoom)
958
959        set q [ViewToQuaternion]
960        $_arcball quaternion $q
961        SendCmd "camera orient $q"
962        SendCmd "camera reset"
963        PanCamera
964        SendCmd "camera zoom $_view(-zoom)"
965       
966        #cutplane state 0 all
967        foreach axis {x y z} {
968            # Turn off cutplanes for all volumes
969            SendCmd "cutplane state 0 $axis"
970        }
971
972        InitSettings -light2side -light -volumeopacity \
973            -isosurfaceshading -gridvisible -axesvisible \
974
975        if {"" != $_first} {
976            set axis [$_first hints updir]
977            if { "" != $axis } {
978                SendCmd "up $axis"
979            }
980            set location [$_first hints camera]
981            if { $location != "" } {
982                array set _view $location
983            }
984        }
985    }
986
987    # nothing to send -- activate the proper ivol
988    SendCmd "volume state 0"
989    if {"" != $_first} {
990        set datasets [array names _serverDatasets $_first-*]
991        if { $datasets != "" } {
992            SendCmd "volume state 1 $datasets"
993        }
994        # If the first volume already exists on the server, then make sure
995        # we display the proper transfer function in the legend.
996        set cname [lindex [$_first components] 0]
997        if { [info exists _serverDatasets($_first-$cname)] } {
998            updateTransferFunctions
999        }
1000    }
1001    # Actually write the commands to the server socket.  If it fails, we don't
1002    # care.  We're finished here.
1003    blt::busy hold $itk_component(hull)
1004    StopBufferingCommands
1005    blt::busy release $itk_component(hull)
1006    set _reset 0
1007}
1008
1009# ----------------------------------------------------------------------
1010# USAGE: CurrentDatasets ?-cutplanes?
1011#
1012# Returns a list of volume server IDs for the current volume being
1013# displayed.  This is normally a single ID, but it might be a list
1014# of IDs if the current data object has multiple components.
1015# ----------------------------------------------------------------------
1016itcl::body Rappture::NanovisViewer::CurrentDatasets {{what -all}} {
1017    set rlist ""
1018    if { $_first == "" } {
1019        return
1020    }
1021    foreach cname [$_first components] {
1022        set tag $_first-$cname
1023        if { [info exists _serverDatasets($tag)] && $_serverDatasets($tag) } {
1024            array set styles {
1025                -cutplanes 1
1026            }
1027            array set styles [lindex [$_first components -style $cname] 0]
1028            if { $what != "-cutplanes" || $styles(-cutplanes) } {
1029                lappend rlist $tag
1030            }
1031        }
1032    }
1033    return $rlist
1034}
1035
1036# ----------------------------------------------------------------------
1037# USAGE: Zoom in
1038# USAGE: Zoom out
1039# USAGE: Zoom reset
1040#
1041# Called automatically when the user clicks on one of the zoom
1042# controls for this widget.  Changes the zoom for the current view.
1043# ----------------------------------------------------------------------
1044itcl::body Rappture::NanovisViewer::Zoom {option} {
1045    switch -- $option {
1046        "in" {
1047            set _view(-zoom) [expr {$_view(-zoom)*1.25}]
1048            set _settings(-zoom) $_view(-zoom)
1049            SendCmd "camera zoom $_view(-zoom)"
1050        }
1051        "out" {
1052            set _view(-zoom) [expr {$_view(-zoom)*0.8}]
1053            set _settings(-zoom) $_view(-zoom)
1054            SendCmd "camera zoom $_view(-zoom)"
1055        }
1056        "reset" {
1057            array set _view {
1058                -qw      0.853553
1059                -qx      -0.353553
1060                -qy      0.353553
1061                -qz      0.146447
1062                -xpan    0
1063                -ypan    0
1064                -zoom    1.0
1065            }
1066            if { $_first != "" } {
1067                set location [$_first hints camera]
1068                if { $location != "" } {
1069                    array set _view $location
1070                }
1071            }
1072            set q [ViewToQuaternion]         
1073            $_arcball quaternion $q
1074            SendCmd "camera orient $q"
1075            SendCmd "camera reset"
1076            set _settings(-qw)    $_view(-qw)
1077            set _settings(-qx)    $_view(-qx)
1078            set _settings(-qy)    $_view(-qy)
1079            set _settings(-qz)    $_view(-qz)
1080            set _settings(-xpan)  $_view(-xpan)
1081            set _settings(-ypan)  $_view(-ypan)
1082            set _settings(-zoom)  $_view(-zoom)
1083        }
1084    }
1085}
1086
1087itcl::body Rappture::NanovisViewer::PanCamera {} {
1088    set x $_view(-xpan)
1089    set y $_view(-ypan)
1090    SendCmd "camera pan $x $y"
1091}
1092
1093
1094# ----------------------------------------------------------------------
1095# USAGE: Rotate click <x> <y>
1096# USAGE: Rotate drag <x> <y>
1097# USAGE: Rotate release <x> <y>
1098#
1099# Called automatically when the user clicks/drags/releases in the
1100# plot area.  Moves the plot according to the user's actions.
1101# ----------------------------------------------------------------------
1102itcl::body Rappture::NanovisViewer::Rotate {option x y} {
1103    switch -- $option {
1104        click {
1105            $itk_component(3dview) configure -cursor fleur
1106            set _click(x) $x
1107            set _click(y) $y
1108        }
1109        drag {
1110            if {[array size _click] == 0} {
1111                Rotate click $x $y
1112            } else {
1113                set w [winfo width $itk_component(3dview)]
1114                set h [winfo height $itk_component(3dview)]
1115                if {$w <= 0 || $h <= 0} {
1116                    return
1117                }
1118
1119                if {[catch {
1120                    # this fails sometimes for no apparent reason
1121                    set dx [expr {double($x-$_click(x))/$w}]
1122                    set dy [expr {double($y-$_click(y))/$h}]
1123                }]} {
1124                    return
1125                }
1126
1127                set q [$_arcball rotate $x $y $_click(x) $_click(y)]
1128                foreach { _view(-qw) _view(-qx) _view(-qy) _view(-qz) } $q break
1129                set _settings(-qw) $_view(-qw)
1130                set _settings(-qx) $_view(-qx)
1131                set _settings(-qy) $_view(-qy)
1132                set _settings(-qz) $_view(-qz)
1133                SendCmd "camera orient $q"
1134
1135                set _click(x) $x
1136                set _click(y) $y
1137            }
1138        }
1139        release {
1140            Rotate drag $x $y
1141            $itk_component(3dview) configure -cursor ""
1142            catch {unset _click}
1143        }
1144        default {
1145            error "bad option \"$option\": should be click, drag, release"
1146        }
1147    }
1148}
1149
1150# ----------------------------------------------------------------------
1151# USAGE: $this Pan click x y
1152#        $this Pan drag x y
1153#        $this Pan release x y
1154#
1155# Called automatically when the user clicks on one of the zoom
1156# controls for this widget.  Changes the zoom for the current view.
1157# ----------------------------------------------------------------------
1158itcl::body Rappture::NanovisViewer::Pan {option x y} {
1159    # Experimental stuff
1160    set w [winfo width $itk_component(3dview)]
1161    set h [winfo height $itk_component(3dview)]
1162    if { $option == "set" } {
1163        set x [expr $x / double($w)]
1164        set y [expr $y / double($h)]
1165        set _view(-xpan) [expr $_view(-xpan) + $x]
1166        set _view(-ypan) [expr $_view(-ypan) + $y]
1167        PanCamera
1168        set _settings(-xpan) $_view(-xpan)
1169        set _settings(-ypan) $_view(-ypan)
1170        return
1171    }
1172    if { $option == "click" } {
1173        set _click(x) $x
1174        set _click(y) $y
1175        $itk_component(3dview) configure -cursor hand1
1176    }
1177    if { $option == "drag" || $option == "release" } {
1178        set dx [expr ($_click(x) - $x)/double($w)]
1179        set dy [expr ($_click(y) - $y)/double($h)]
1180        set _click(x) $x
1181        set _click(y) $y
1182        set _view(-xpan) [expr $_view(-xpan) - $dx]
1183        set _view(-ypan) [expr $_view(-ypan) - $dy]
1184        PanCamera
1185        set _settings(-xpan) $_view(-xpan)
1186        set _settings(-ypan) $_view(-ypan)
1187    }
1188    if { $option == "release" } {
1189        $itk_component(3dview) configure -cursor ""
1190    }
1191}
1192
1193# ----------------------------------------------------------------------
1194# USAGE: InitSettings <what> ?<value>?
1195#
1196# Used internally to update rendering settings whenever parameters
1197# change in the popup settings panel.  Sends the new settings off
1198# to the back end.
1199# ----------------------------------------------------------------------
1200itcl::body Rappture::NanovisViewer::InitSettings { args } {
1201    foreach arg $args {
1202        AdjustSetting $arg
1203    }
1204}
1205
1206# ----------------------------------------------------------------------
1207# USAGE: AdjustSetting <what> ?<value>?
1208#
1209# Used internally to update rendering settings whenever parameters
1210# change in the popup settings panel.  Sends the new settings off
1211# to the back end.
1212# ----------------------------------------------------------------------
1213itcl::body Rappture::NanovisViewer::AdjustSetting {what {value ""}} {
1214    if {![isconnected]} {
1215        return
1216    }
1217    switch -- $what {
1218        "-axesvisible" {
1219            SendCmd "axis visible $_settings($what)"
1220        }
1221        "-background" {
1222            set bgcolor [$itk_component(background) value]
1223            array set fgcolors {
1224                "black" "white"
1225                "white" "black"
1226                "grey"  "black"
1227            }
1228            configure -plotbackground $bgcolor \
1229                -plotforeground $fgcolors($bgcolor)
1230            DrawLegend $_current
1231        }
1232        "-colormap" {
1233            set color [$itk_component(colormap) value]
1234            set _settings($what) $color
1235            set _settings($_current${what}) $color
1236            ResetColormap $_current $color
1237        }
1238        "-current" {
1239            set cname [$itk_component(volcomponents) value]
1240            SwitchComponent $cname
1241        }
1242        "-cutplanesvisible" {
1243            set bool $_settings($what)
1244            # We only set cutplanes on the first dataset.
1245            set datasets [CurrentDatasets -cutplanes]
1246            set tag [lindex $datasets 0]
1247            if { $bool } {
1248                foreach axis { x y z } {
1249                    if { $_settings(-${axis}cutplanevisible) } {
1250                        SendCmd "cutplane state 1 $axis $tag"
1251                    }
1252                }
1253            } else {
1254                foreach axis { x y z } {
1255                    SendCmd "cutplane state 0 $axis $tag"
1256                }
1257            }
1258        }
1259        "-gridvisible" {
1260            SendCmd "grid visible $_settings($what)"
1261        }
1262        "-isosurfaceshading" {
1263            SendCmd "volume shading isosurface $_settings($what)"
1264        }
1265        "-legendvisible" {
1266            if { $_settings($what) } {
1267                blt::table $itk_component(plotarea) \
1268                    0,0 $itk_component(3dview) -fill both \
1269                    1,0 $itk_component(legend) -fill x
1270                blt::table configure $itk_component(plotarea) r1 -resize none
1271            } else {
1272                blt::table forget $itk_component(legend)
1273            }
1274        }
1275        "-light" {
1276            set _settings($_current${what}) $_settings($what)
1277            set val $_settings($what)
1278            set diffuse [expr {0.01*$val}]
1279            set ambient [expr {1.0-$diffuse}]
1280            set specularLevel 0.3
1281            set specularExp 90.0
1282            foreach tag [GetDatasetsWithComponent $_current] {
1283                SendCmd "volume shading ambient $ambient $tag"
1284                SendCmd "volume shading diffuse $diffuse $tag"
1285                SendCmd "volume shading specularLevel $specularLevel $tag"
1286                SendCmd "volume shading specularExp $specularExp $tag"
1287            }
1288        }
1289        "-light2side" {
1290            set _settings($_current${what}) $_settings($what)
1291            set val $_settings($what)
1292            foreach tag [GetDatasetsWithComponent $_current] {
1293                SendCmd "volume shading light2side $val $tag"
1294            }
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        "-volumeopacity" {
1318            set _settings($what) [expr $_widget($what) * 0.01]
1319            set _settings($_current${what}) $_settings($what)
1320            foreach tag [GetDatasetsWithComponent $_current] {
1321                SendCmd "volume shading opacity $_settings($what) $tag"
1322            }
1323        }
1324        "-volumevisible" {
1325            # This is the component specific control.  It changes the
1326            # visibility of only the current component.
1327            set _settings($_current${what}) $_settings($what)
1328            foreach tag [GetDatasetsWithComponent $_current] {
1329                SendCmd "volume data state $_settings($what) $tag"
1330            }
1331        }
1332        "-xcutplanevisible" - "-ycutplanevisible" - "-zcutplanevisible" {
1333            set axis [string range $what 1 1]
1334            set bool $_settings($what)
1335            # We only set cutplanes on the first dataset.
1336            set datasets [CurrentDatasets -cutplanes]
1337            set tag [lindex $datasets 0]
1338            if { $_settings(-cutplanesvisible) } {
1339                SendCmd "cutplane state $bool $axis $tag"
1340            }
1341            if { $bool } {
1342                $itk_component(${axis}CutScale) configure -state normal \
1343                    -troughcolor white
1344            } else {
1345                $itk_component(${axis}CutScale) configure -state disabled \
1346                    -troughcolor grey82
1347            }
1348        }
1349        default {
1350            error "don't know how to fix $what"
1351        }
1352    }
1353}
1354
1355# ----------------------------------------------------------------------
1356# USAGE: FixLegend
1357#
1358# Used internally to update the legend area whenever it changes size
1359# or when the field changes.  Asks the server to send a new legend
1360# for the current field.
1361# ----------------------------------------------------------------------
1362itcl::body Rappture::NanovisViewer::FixLegend {} {
1363    set _resizeLegendPending 0
1364    set lineht [font metrics $itk_option(-font) -linespace]
1365    set w [expr {$_width-20}]
1366    set h [expr {[winfo height $itk_component(legend)]-20-$lineht}]
1367    if {$w > 0 && $h > 0 && $_first != "" } {
1368        if { [info exists _cname2transferFunction($_current)] } {
1369            SendCmd "legend $_current $w $h"
1370        }
1371    }
1372}
1373
1374#
1375# NameTransferFunction --
1376#
1377#       Creates a transfer function name based on the <style> settings in the
1378#       library run.xml file. This placeholder will be used later to create
1379#       and send the actual transfer function once the data info has been sent
1380#       to us by the render server. [We won't know the volume limits until the
1381#       server parses the 3D data and sends back the limits via ReceiveData.]
1382#
1383itcl::body Rappture::NanovisViewer::NameTransferFunction { dataobj cname } {
1384    array set styles {
1385        -color BCGYR
1386        -levels 6
1387        -markers ""
1388    }
1389    set tag $dataobj-$cname
1390    array set styles [lindex [$dataobj components -style $cname] 0]
1391    if { ![info exists _cname2transferFunction($cname)] } {
1392        # Get the colormap right now, since it doesn't change with marker
1393        # changes.
1394        set cmap [ColorsToColormap $styles(-color)]
1395        set wmap [list 0.0 0.0 1.0 1.0]
1396        set _cname2transferFunction($cname) [list $cmap $wmap]
1397        SendCmd [list transfunc define $cname $cmap $wmap]
1398    }
1399    SendCmd "volume shading transfunc $cname $tag"
1400    if { ![info exists _transferFunctionEditors($cname)] } {
1401        set _transferFunctionEditors($cname) \
1402            [Rappture::TransferFunctionEditor ::\#auto $itk_component(legend) \
1403                 $cname \
1404                 -command [itcl::code $this updateTransferFunctions]]
1405    }
1406    set _dataset2style($tag) $cname
1407    lappend _style2datasets($cname) $tag
1408    return $cname
1409}
1410
1411#
1412# ComputeTransferFunction --
1413#
1414#       Computes and sends the transfer function to the render server.  It's
1415#       assumed that the volume data limits are known and that the global
1416#       transfer-functions slider values have been set up.  Both parts are
1417#       needed to compute the relative value (location) of the marker, and
1418#       the alpha map of the transfer function.
1419#
1420itcl::body Rappture::NanovisViewer::ComputeTransferFunction { cname } {
1421    foreach {cmap wmap} $_cname2transferFunction($cname) break
1422
1423    # We have to parse the style attributes for a volume using this
1424    # transfer-function *once*.  This sets up the initial isomarkers for the
1425    # transfer function.  The user may add/delete markers, so we have to
1426    # maintain a list of markers for each transfer-function.  We use the one
1427    # of the volumes (the first in the list) using the transfer-function as a
1428    # reference.
1429    if { ![info exists _parsedFunction($cname)] } {
1430        array set styles {
1431            -color BCGYR
1432            -levels 6
1433            -markers ""
1434        }
1435        # Accumulate the style from all the datasets using it.
1436        foreach tag [GetDatasetsWithComponent $cname] {
1437            foreach {dataobj cname} [split [lindex $tag 0] -] break
1438            array set styles [lindex [$dataobj components -style $cname] 0]
1439        }
1440        eval $_transferFunctionEditors($cname) limits $_limits($cname)
1441        # Have to defer creation of isomarkers until we have data limits
1442        if { [info exists styles(-markers)] &&
1443             [llength $styles(-markers)] > 0 } {
1444            ParseMarkersOption $cname $styles(-markers)
1445        } else {
1446            ParseLevelsOption $cname $styles(-levels)
1447        }
1448       
1449    }
1450    set wmap [ComputeAlphamap $cname]
1451    set _cname2transferFunction($cname) [list $cmap $wmap]
1452    SendCmd [list transfunc define $cname $cmap $wmap]
1453}
1454
1455itcl::body Rappture::NanovisViewer::AddNewMarker { x y } {
1456    if { ![info exists _transferFunctionEditors($_current)] } {
1457        continue
1458    }
1459    # Add a new marker to the current transfer function
1460    $_transferFunctionEditors($_current) newMarker $x $y normal
1461    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1462}
1463
1464itcl::body Rappture::NanovisViewer::RemoveMarker { x y } {
1465    if { ![info exists _transferFunctionEditors($_current)] } {
1466        continue
1467    }
1468    # Add a new marker to the current transfer function
1469    $_transferFunctionEditors($_current) deleteMarker $x $y
1470}
1471
1472# ----------------------------------------------------------------------
1473# CONFIGURATION OPTION: -plotbackground
1474# ----------------------------------------------------------------------
1475itcl::configbody Rappture::NanovisViewer::plotbackground {
1476    if { [isconnected] } {
1477        set color $itk_option(-plotbackground)
1478        set rgb [Color2RGB $color]
1479        SendCmd "screen bgcolor $rgb"
1480        $itk_component(legend) configure -background $color
1481    }
1482}
1483
1484# ----------------------------------------------------------------------
1485# CONFIGURATION OPTION: -plotforeground
1486# ----------------------------------------------------------------------
1487itcl::configbody Rappture::NanovisViewer::plotforeground {
1488    if { [isconnected] } {
1489        set color $itk_option(-plotforeground)
1490        set rgb [Color2RGB $color]
1491        SendCmd "volume outline color $rgb"
1492        SendCmd "grid axiscolor $rgb"
1493        SendCmd "grid linecolor $rgb"
1494        $itk_component(legend) itemconfigure labels -fill $color
1495        $itk_component(legend) itemconfigure limits -fill $color
1496    }
1497}
1498
1499# ----------------------------------------------------------------------
1500# CONFIGURATION OPTION: -plotoutline
1501# ----------------------------------------------------------------------
1502itcl::configbody Rappture::NanovisViewer::plotoutline {
1503    # Must check if we are connected because this routine is called from the
1504    # class body when the -plotoutline itk_option is defined.  At that point
1505    # the NanovisViewer class constructor hasn't been called, so we can't
1506    # start sending commands to visualization server.
1507    if { [isconnected] } {
1508        if {"" == $itk_option(-plotoutline)} {
1509            SendCmd "volume outline state off"
1510        } else {
1511            SendCmd "volume outline state on"
1512            SendCmd "volume outline color [Color2RGB $itk_option(-plotoutline)]"
1513        }
1514    }
1515}
1516
1517#
1518# The -levels option takes a single value that represents the number
1519# of evenly distributed markers based on the current data range. Each
1520# marker is a relative value from 0.0 to 1.0.
1521#
1522itcl::body Rappture::NanovisViewer::ParseLevelsOption { cname levels } {
1523    set c $itk_component(legend)
1524    set list {}
1525    regsub -all "," $levels " " levels
1526    if {[string is int $levels]} {
1527        for {set i 1} { $i <= $levels } {incr i} {
1528            lappend list [expr {double($i)/($levels+1)}]
1529        }
1530    } else {
1531        foreach x $levels {
1532            lappend list $x
1533        }
1534    }
1535    set _parsedFunction($cname) 1
1536    $_transferFunctionEditors($cname) addMarkers $list
1537    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1538}
1539
1540#
1541# The -markers option takes a list of zero or more values (the values
1542# may be separated either by spaces or commas) that have the following
1543# format:
1544#
1545#   N%  Percent of current total data range.  Converted to
1546#       to a relative value between 0.0 and 1.0.
1547#   N   Absolute value of marker.  If the marker is outside of
1548#       the current range, it will be displayed on the outer
1549#       edge of the legends, but it range it represents will
1550#       not be seen.
1551#
1552itcl::body Rappture::NanovisViewer::ParseMarkersOption { cname markers } {
1553    set c $itk_component(legend)
1554    set list {}
1555    foreach { min max } $_limits($cname) break
1556    regsub -all "," $markers " " markers
1557    foreach marker $markers {
1558        set n [scan $marker "%g%s" value suffix]
1559        if { $n == 2 && $suffix == "%" } {
1560            # $n% : Set relative value (0..1).
1561            lappend list [expr {$value * 0.01}]
1562        } else {
1563            # $n : absolute value, compute relative
1564            lappend list  [expr {(double($value)-$min)/($max-$min)]}
1565        }
1566    }
1567    set _parsedFunction($cname) 1
1568    $_transferFunctionEditors($cname) addMarkers $list
1569    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1570}
1571
1572# ----------------------------------------------------------------------
1573# USAGE: UndateTransferFuncs
1574# ----------------------------------------------------------------------
1575itcl::body Rappture::NanovisViewer::updateTransferFunctions {} {
1576    $_dispatcher event -idle !send_transfunc
1577}
1578
1579itcl::body Rappture::NanovisViewer::limits { cname } {
1580    set _limits(min) 0.0
1581    set _limits(max) 1.0
1582    if { ![info exists _style2datasets($cname)] } {
1583        return [array get _limits]
1584    }
1585    set min ""; set max ""
1586    foreach tag [GetDatasetsWithComponent $cname] {
1587        if { ![info exists _limits($tag)] } {
1588            continue
1589        }
1590        foreach {amin amax} $_limits($tag) break
1591        if { $min == "" || $min > $amin } {
1592            set min $amin
1593        }
1594        if { $max == "" || $max < $amax } {
1595            set max $amax
1596        }
1597    }
1598    if { $min != "" } {
1599        set _limits(min) $min
1600    }
1601    if { $max != "" } {
1602        set _limits(max) $max
1603    }
1604    return [list $_limits(min) $_limits(max)]
1605}
1606
1607
1608itcl::body Rappture::NanovisViewer::BuildViewTab {} {
1609    set fg [option get $itk_component(hull) font Font]
1610    #set bfg [option get $itk_component(hull) boldFont Font]
1611
1612    set inner [$itk_component(main) insert end \
1613        -title "View Settings" \
1614        -icon [Rappture::icon wrench]]
1615    $inner configure -borderwidth 4
1616
1617    set ::Rappture::NanovisViewer::_settings(-isosurfaceshading) 0
1618    checkbutton $inner.isosurface \
1619        -text "Isosurface shading" \
1620        -variable [itcl::scope _settings(-isosurfaceshading)] \
1621        -command [itcl::code $this AdjustSetting -isosurfaceshading] \
1622        -font "Arial 9"
1623
1624    checkbutton $inner.axes \
1625        -text "Axes" \
1626        -variable [itcl::scope _settings(-axesvisible)] \
1627        -command [itcl::code $this AdjustSetting -axesvisible] \
1628        -font "Arial 9"
1629
1630    checkbutton $inner.grid \
1631        -text "Grid" \
1632        -variable [itcl::scope _settings(-gridvisible)] \
1633        -command [itcl::code $this AdjustSetting -gridvisible] \
1634        -font "Arial 9"
1635
1636    checkbutton $inner.outline \
1637        -text "Outline" \
1638        -variable [itcl::scope _settings(-outlinevisible)] \
1639        -command [itcl::code $this AdjustSetting -outlinevisible] \
1640        -font "Arial 9"
1641
1642    checkbutton $inner.legend \
1643        -text "Legend" \
1644        -variable [itcl::scope _settings(-legendvisible)] \
1645        -command [itcl::code $this AdjustSetting -legendvisible] \
1646        -font "Arial 9"
1647
1648    checkbutton $inner.volume \
1649        -text "Volume" \
1650        -variable [itcl::scope _settings(-volume)] \
1651        -command [itcl::code $this AdjustSetting -volume] \
1652        -font "Arial 9"
1653
1654    label $inner.background_l -text "Background" -font "Arial 9"
1655    itk_component add background {
1656        Rappture::Combobox $inner.background -width 10 -editable no
1657    }
1658    $inner.background choices insert end \
1659        "black"              "black"            \
1660        "white"              "white"            \
1661        "grey"               "grey"             
1662
1663    $itk_component(background) value $_settings(-background)
1664    bind $inner.background <<Value>> \
1665        [itcl::code $this AdjustSetting -background]
1666
1667    blt::table $inner \
1668        0,0 $inner.axes  -cspan 2 -anchor w \
1669        1,0 $inner.grid  -cspan 2 -anchor w \
1670        2,0 $inner.outline  -cspan 2 -anchor w \
1671        3,0 $inner.volume  -cspan 2 -anchor w \
1672        4,0 $inner.legend  -cspan 2 -anchor w \
1673        5,0 $inner.background_l       -anchor e -pady 2 \
1674        5,1 $inner.background                   -fill x \
1675
1676    if 0 {
1677    bind $inner <Map> [itcl::code $this GetVolumeInfo $inner]
1678    }
1679    blt::table configure $inner r* -resize none
1680    blt::table configure $inner r6 -resize expand
1681}
1682
1683itcl::body Rappture::NanovisViewer::BuildVolumeTab {} {
1684    set inner [$itk_component(main) insert end \
1685        -title "Volume Settings" \
1686        -icon [Rappture::icon volume-on]]
1687    $inner configure -borderwidth 4
1688
1689    set fg [option get $itk_component(hull) font Font]
1690    #set bfg [option get $itk_component(hull) boldFont Font]
1691
1692    label $inner.shading -text "Shading:" -font $fg
1693
1694    checkbutton $inner.light2side -text "Two-sided lighting" -font $fg \
1695        -variable [itcl::scope _settings(-light2side)] \
1696        -command [itcl::code $this AdjustSetting -light2side]
1697
1698    checkbutton $inner.visibility -text "Visible" -font $fg \
1699        -variable [itcl::scope _settings(-volumevisible)] \
1700        -command [itcl::code $this AdjustSetting -volumevisible]
1701
1702    label $inner.dim -text "Glow" -font $fg
1703    ::scale $inner.light -from 0 -to 100 -orient horizontal \
1704        -variable [itcl::scope _settings(-light)] \
1705        -width 10 \
1706        -showvalue off -command [itcl::code $this AdjustSetting -light]
1707    label $inner.bright -text "Surface" -font $fg
1708
1709    # Opacity
1710    label $inner.fog -text "Clear" -font $fg
1711    ::scale $inner.transp -from 0 -to 100 -orient horizontal \
1712        -variable [itcl::scope _widget(-volumeopacity)] \
1713        -width 10 \
1714        -showvalue off -command [itcl::code $this AdjustSetting -volumeopacity]
1715    label $inner.plastic -text "Opaque" -font $fg
1716
1717    # Tooth thickness
1718    label $inner.thin -text "Thin" -font $fg
1719    ::scale $inner.thickness -from 0 -to 1000 -orient horizontal \
1720        -variable [itcl::scope _settings(-thickness)] \
1721        -width 10 \
1722        -showvalue off -command [itcl::code $this AdjustSetting -thickness]
1723    label $inner.thick -text "Thick" -font $fg
1724
1725    # Colormap
1726    label $inner.colormap_l -text "Colormap" -font $fg
1727    itk_component add colormap {
1728        Rappture::Combobox $inner.colormap -width 10 -editable no
1729    }
1730
1731    $inner.colormap choices insert end [GetColormapList -includeDefault -includeNone]
1732    bind $inner.colormap <<Value>> \
1733        [itcl::code $this AdjustSetting -colormap]
1734    $itk_component(colormap) value "default"
1735    set _settings(-colormap) "default"
1736
1737    # Component
1738    label $inner.volcomponents_l -text "Component" -font $fg
1739    itk_component add volcomponents {
1740        Rappture::Combobox $inner.volcomponents -editable no
1741    }
1742    bind $inner.volcomponents <<Value>> \
1743        [itcl::code $this AdjustSetting -current]
1744
1745    blt::table $inner \
1746        0,0 $inner.volcomponents_l -anchor e -cspan 2 \
1747        0,2 $inner.volcomponents             -cspan 3 -fill x \
1748        1,0 $inner.shading -cspan 4 -anchor w -pady {10 2} \
1749        2,0 $inner.light2side -cspan 4 -anchor w -pady 2 \
1750        3,0 $inner.visibility -cspan 4 -anchor w -pady 2 \
1751        4,0 $inner.dim -anchor e -pady 2 \
1752        4,1 $inner.light -cspan 2 -pady 2 -fill x \
1753        4,3 $inner.bright -anchor w -pady 2 \
1754        5,0 $inner.fog -anchor e -pady 2 \
1755        5,1 $inner.transp -cspan 2 -pady 2 -fill x \
1756        5,3 $inner.plastic -anchor w -pady 2 \
1757        6,0 $inner.thin -anchor e -pady 2 \
1758        6,1 $inner.thickness -cspan 2 -pady 2 -fill x\
1759        6,3 $inner.thick -anchor w -pady 2
1760
1761    blt::table configure $inner c0 c1 c3 r* -resize none
1762    blt::table configure $inner r7 -resize expand
1763}
1764
1765itcl::body Rappture::NanovisViewer::BuildCutplanesTab {} {
1766    set inner [$itk_component(main) insert end \
1767        -title "Cutplane Settings" \
1768        -icon [Rappture::icon cutbutton]]
1769    $inner configure -borderwidth 4
1770
1771    checkbutton $inner.visible \
1772        -text "Show Cutplanes" \
1773        -variable [itcl::scope _settings(-cutplanesvisible)] \
1774        -command [itcl::code $this AdjustSetting -cutplanesvisible] \
1775        -font "Arial 9"
1776
1777    # X-value slicer...
1778    itk_component add xCutButton {
1779        Rappture::PushButton $inner.xbutton \
1780            -onimage [Rappture::icon x-cutplane] \
1781            -offimage [Rappture::icon x-cutplane] \
1782            -command [itcl::code $this AdjustSetting -xcutplanevisible] \
1783            -variable [itcl::scope _settings(-xcutplanevisible)]
1784    }
1785    Rappture::Tooltip::for $itk_component(xCutButton) \
1786        "Toggle the X cut plane on/off"
1787    $itk_component(xCutButton) select
1788
1789    itk_component add xCutScale {
1790        ::scale $inner.xval -from 100 -to 0 \
1791            -width 10 -orient vertical -showvalue off \
1792            -borderwidth 1 -highlightthickness 0 \
1793            -command [itcl::code $this Slice move x] \
1794            -variable [itcl::scope _settings(-xcutplaneposition)]
1795    } {
1796        usual
1797        ignore -borderwidth -highlightthickness
1798    }
1799    # Set the default cutplane value before disabling the scale.
1800    $itk_component(xCutScale) set 50
1801    $itk_component(xCutScale) configure -state disabled
1802    Rappture::Tooltip::for $itk_component(xCutScale) \
1803        "@[itcl::code $this SlicerTip x]"
1804
1805    # Y-value slicer...
1806    itk_component add yCutButton {
1807        Rappture::PushButton $inner.ybutton \
1808            -onimage [Rappture::icon y-cutplane] \
1809            -offimage [Rappture::icon y-cutplane] \
1810            -command [itcl::code $this AdjustSetting -ycutplanevisible] \
1811            -variable [itcl::scope _settings(-ycutplanevisible)]
1812    }
1813    Rappture::Tooltip::for $itk_component(yCutButton) \
1814        "Toggle the Y cut plane on/off"
1815    $itk_component(yCutButton) select
1816
1817    itk_component add yCutScale {
1818        ::scale $inner.yval -from 100 -to 0 \
1819            -width 10 -orient vertical -showvalue off \
1820            -borderwidth 1 -highlightthickness 0 \
1821            -command [itcl::code $this Slice move y] \
1822            -variable [itcl::scope _settings(-ycutplaneposition)]
1823    } {
1824        usual
1825        ignore -borderwidth -highlightthickness
1826    }
1827    Rappture::Tooltip::for $itk_component(yCutScale) \
1828        "@[itcl::code $this SlicerTip y]"
1829    # Set the default cutplane value before disabling the scale.
1830    $itk_component(yCutScale) set 50
1831    $itk_component(yCutScale) configure -state disabled
1832
1833    # Z-value slicer...
1834    itk_component add zCutButton {
1835        Rappture::PushButton $inner.zbutton \
1836            -onimage [Rappture::icon z-cutplane] \
1837            -offimage [Rappture::icon z-cutplane] \
1838            -command [itcl::code $this AdjustSetting -zcutplanevisible] \
1839            -variable [itcl::scope _settings(-zcutplanevisible)]
1840    }
1841    Rappture::Tooltip::for $itk_component(zCutButton) \
1842        "Toggle the Z cut plane on/off"
1843    $itk_component(zCutButton) select
1844
1845    itk_component add zCutScale {
1846        ::scale $inner.zval -from 100 -to 0 \
1847            -width 10 -orient vertical -showvalue off \
1848            -borderwidth 1 -highlightthickness 0 \
1849            -command [itcl::code $this Slice move z] \
1850            -variable [itcl::scope _settings(-zcutplaneposition)]
1851    } {
1852        usual
1853        ignore -borderwidth -highlightthickness
1854    }
1855    $itk_component(zCutScale) set 50
1856    $itk_component(zCutScale) configure -state disabled
1857    Rappture::Tooltip::for $itk_component(zCutScale) \
1858        "@[itcl::code $this SlicerTip z]"
1859
1860    blt::table $inner \
1861        0,1 $inner.visible              -anchor w -pady 2 -cspan 4 \
1862        1,1 $itk_component(xCutScale) \
1863        1,2 $itk_component(yCutScale) \
1864        1,3 $itk_component(zCutScale) \
1865        2,1 $itk_component(xCutButton) \
1866        2,2 $itk_component(yCutButton) \
1867        2,3 $itk_component(zCutButton)
1868
1869    blt::table configure $inner r0 r1 r2 c* -resize none
1870    blt::table configure $inner r3 c4 -resize expand
1871    blt::table configure $inner c0 -width 2
1872    blt::table configure $inner c1 c2 c3 -padx 2
1873}
1874
1875itcl::body Rappture::NanovisViewer::BuildCameraTab {} {
1876    set inner [$itk_component(main) insert end \
1877        -title "Camera Settings" \
1878        -icon [Rappture::icon camera]]
1879    $inner configure -borderwidth 4
1880
1881    label $inner.view_l -text "view" -font "Arial 9"
1882    set f [frame $inner.view]
1883    foreach side { front back left right top bottom } {
1884        button $f.$side  -image [Rappture::icon view$side] \
1885            -command [itcl::code $this SetOrientation $side]
1886        Rappture::Tooltip::for $f.$side "Change the view to $side"
1887        pack $f.$side -side left
1888    }
1889
1890    blt::table $inner \
1891        0,0 $inner.view_l -anchor e -pady 2 \
1892        0,1 $inner.view -anchor w -pady 2
1893
1894    set row 1
1895    set labels { qw qx qy qz xpan ypan zoom }
1896    foreach tag $labels {
1897        label $inner.${tag}label -text $tag -font "Arial 9"
1898        entry $inner.${tag} -font "Arial 9"  -bg white \
1899            -textvariable [itcl::scope _settings(-$tag)]
1900        bind $inner.${tag} <Return> \
1901            [itcl::code $this camera set -${tag}]
1902        bind $inner.${tag} <KP_Enter> \
1903            [itcl::code $this camera set -${tag}]
1904        blt::table $inner \
1905            $row,0 $inner.${tag}label -anchor e -pady 2 \
1906            $row,1 $inner.${tag} -anchor w -pady 2
1907        blt::table configure $inner r$row -resize none
1908        incr row
1909    }
1910
1911    blt::table configure $inner c* r* -resize none
1912    blt::table configure $inner c2 -resize expand
1913    blt::table configure $inner r$row -resize expand
1914}
1915
1916# ----------------------------------------------------------------------
1917# USAGE: Slice move x|y|z <newval>
1918#
1919# Called automatically when the user drags the slider to move the
1920# cut plane that slices 3D data.  Gets the current value from the
1921# slider and moves the cut plane to the appropriate point in the
1922# data set.
1923# ----------------------------------------------------------------------
1924itcl::body Rappture::NanovisViewer::Slice {option args} {
1925    switch -- $option {
1926        move {
1927            if {[llength $args] != 2} {
1928                error "wrong # args: should be \"Slice move x|y|z newval\""
1929            }
1930            set axis [lindex $args 0]
1931            set newval [lindex $args 1]
1932
1933            set newpos [expr {0.01*$newval}]
1934            set datasets [CurrentDatasets -cutplanes]
1935            set tag [lindex $datasets 0]
1936            SendCmd "cutplane position $newpos $axis $tag"
1937        }
1938        default {
1939            error "bad option \"$option\": should be axis, move, or volume"
1940        }
1941    }
1942}
1943
1944# ----------------------------------------------------------------------
1945# USAGE: SlicerTip <axis>
1946#
1947# Used internally to generate a tooltip for the x/y/z slicer controls.
1948# Returns a message that includes the current slicer value.
1949# ----------------------------------------------------------------------
1950itcl::body Rappture::NanovisViewer::SlicerTip {axis} {
1951    set val [$itk_component(${axis}CutScale) get]
1952    return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
1953}
1954
1955
1956itcl::body Rappture::NanovisViewer::DoResize {} {
1957    $_arcball resize $_width $_height
1958    SendCmd "screen size $_width $_height"
1959    set _resizePending 0
1960}
1961
1962itcl::body Rappture::NanovisViewer::EventuallyResize { w h } {
1963    set _width $w
1964    set _height $h
1965    $_arcball resize $w $h
1966    if { !$_resizePending } {
1967        $_dispatcher event -idle !resize
1968        set _resizePending 1
1969    }
1970}
1971
1972itcl::body Rappture::NanovisViewer::EventuallyRedrawLegend {} {
1973    if { !$_resizeLegendPending } {
1974        $_dispatcher event -idle !legend
1975        set _resizeLegendPending 1
1976    }
1977}
1978
1979#  camera --
1980#
1981itcl::body Rappture::NanovisViewer::camera {option args} {
1982    switch -- $option {
1983        "show" {
1984            puts [array get _view]
1985        }
1986        "set" {
1987            set what [lindex $args 0]
1988            set x $_settings($what)
1989            set code [catch { string is double $x } result]
1990            if { $code != 0 || !$result } {
1991                set _settings($what) $_view($what)
1992                return
1993            }
1994            switch -- $what {
1995                "-xpan" - "-ypan" {
1996                    set _view($what) $_settings($what)
1997                    PanCamera
1998                }
1999                "-qx" - "-qy" - "-qz" - "-qw" {
2000                    set _view($what) $_settings($what)
2001                    set q [ViewToQuaternion]
2002                    $_arcball quaternion $q
2003                    SendCmd "camera orient $q"
2004                }
2005                "-zoom" {
2006                    set _view($what) $_settings($what)
2007                    SendCmd "camera zoom $_view($what)"
2008                }
2009            }
2010        }
2011    }
2012}
2013
2014itcl::body Rappture::NanovisViewer::GetVolumeInfo { w } {
2015    set flowobj ""
2016    foreach key [array names _obj2flow] {
2017        set flowobj $_obj2flow($key)
2018        break
2019    }
2020    if { $flowobj == "" } {
2021        return
2022    }
2023    if { [winfo exists $w.frame] } {
2024        destroy $w.frame
2025    }
2026    set inner [frame $w.frame]
2027    blt::table $w \
2028        5,0 $inner -fill both -cspan 2 -anchor nw
2029    array set hints [$dataobj hints]
2030
2031    label $inner.volumes -text "Volumes" -font "Arial 9 bold"
2032    blt::table $inner \
2033        1,0 $inner.volumes  -anchor w \
2034    blt::table configure $inner c0 c1 -resize none
2035    blt::table configure $inner c2 -resize expand
2036
2037    set row 3
2038    set volumes [get]
2039    if { [llength $volumes] > 0 } {
2040        blt::table $inner $row,0 $inner.volumes  -anchor w
2041        incr row
2042    }
2043    foreach vol $volumes {
2044        array unset info
2045        array set info $vol
2046        set name $info(name)
2047        if { ![info exists _settings(-volumevisible-$name)] } {
2048            set _settings(-volumevisible-$name) $info(hide)
2049        }
2050        checkbutton $inner.vol$row -text $info(label) \
2051            -variable [itcl::scope _settings(-volumevisible-$name)] \
2052            -onvalue 0 -offvalue 1 \
2053            -command [itcl::code $this ToggleVolume $key $name] \
2054            -font "Arial 9"
2055        Rappture::Tooltip::for $inner.vol$row $info(description)
2056        blt::table $inner $row,0 $inner.vol$row -anchor w
2057        if { !$_settings(-volume-$name) } {
2058            $inner.vol$row select
2059        }
2060        incr row
2061    }
2062    blt::table configure $inner r* -resize none
2063    blt::table configure $inner r$row -resize expand
2064    blt::table configure $inner c3 -resize expand
2065    event generate [winfo parent [winfo parent $w]] <Configure>
2066}
2067
2068itcl::body Rappture::NanovisViewer::ToggleVolume { tag name } {
2069    set bool $_settings(-volumevisible-$name)
2070    SendCmd "volume state $bool $name"
2071}
2072
2073itcl::body Rappture::NanovisViewer::SetOrientation { side } {
2074    array set positions {
2075        front "1 0 0 0"
2076        back  "0 0 1 0"
2077        left  "0.707107 0 -0.707107 0"
2078        right "0.707107 0 0.707107 0"
2079        top   "0.707107 -0.707107 0 0"
2080        bottom "0.707107 0.707107 0 0"
2081    }
2082    foreach name { -qw -qx -qy -qz } value $positions($side) {
2083        set _view($name) $value
2084    }
2085    set q [ViewToQuaternion]
2086    $_arcball quaternion $q
2087    SendCmd "camera orient $q"
2088    SendCmd "camera reset"
2089    set _view(-xpan) 0
2090    set _view(-ypan) 0
2091    set _view(-zoom) 1.0
2092    set _settings(-xpan) $_view(-xpan)
2093    set _settings(-ypan) $_view(-ypan)
2094    set _settings(-zoom) $_view(-zoom)
2095}
2096
2097
2098#
2099# InitComponentSettings --
2100#
2101#    Initializes the volume settings for a specific component. This should
2102#    match what's used as global settings above. This is called the first
2103#    time we try to switch to a given component in SwitchComponent below.
2104#
2105itcl::body Rappture::NanovisViewer::InitComponentSettings { cname } {
2106    foreach {key value} {
2107        -colormap          "default"
2108        -light             40
2109        -light2side        1
2110        -thickness         350
2111        -volumeopacity     1.0
2112        -volumevisible     1
2113    } {
2114        if { ![info exists _settings($cname${key})] } {
2115            # Don't override existing component settings
2116            set _settings($cname${key}) $value
2117        }
2118    }
2119}
2120
2121#
2122# SwitchComponent --
2123#
2124#    This is called when the current component is changed by the dropdown
2125#    menu in the volume tab.  It synchronizes the global volume settings
2126#    with the settings of the new current component.
2127#
2128itcl::body Rappture::NanovisViewer::SwitchComponent { cname } {
2129    if { ![info exists _settings($cname-light)] } {
2130        InitComponentSettings $cname
2131    }
2132    if { $_settings(-colormap) != $_settings($cname-colormap) } {
2133        set _settings(-colormap)         $_settings($cname-colormap)
2134        EventuallyRedrawLegend
2135    }
2136    # _settings variables change widgets, except for colormap
2137    set _settings(-light)            $_settings($cname-light)
2138    set _settings(-light2side)       $_settings($cname-light2side)
2139    set _settings(-volumeopacity)    $_settings($cname-volumeopacity)
2140    set _settings(-thickness)        $_settings($cname-thickness)
2141    set _settings(-volumevisible)    $_settings($cname-volumevisible)
2142    $itk_component(colormap) value   $_settings($cname-colormap)
2143
2144    set _widget(-volumeopacity) [expr $_settings(-volumeopacity) * 100.0]
2145
2146    set _current $cname;                # Reset the current component
2147}
2148
2149#
2150# BuildVolumeComponents --
2151#
2152#    This is called from the "scale" method which is called when a new
2153#    dataset is added or deleted.  It repopulates the dropdown menu of
2154#    volume component names.  It sets the current component to the first
2155#    component in the list (of components found).  Finally, if there is
2156#    only one component, don't display the label or the combobox in the
2157#    volume settings tab.
2158#
2159itcl::body Rappture::NanovisViewer::BuildVolumeComponents {} {
2160    $itk_component(volcomponents) choices delete 0 end
2161    foreach name $_componentsList {
2162        $itk_component(volcomponents) choices insert end $name $name
2163    }
2164    set _current [lindex $_componentsList 0]
2165    $itk_component(volcomponents) value $_current
2166    set parent [winfo parent $itk_component(volcomponents)]
2167    if { [llength $_componentsList] <= 1 } {
2168        # Unpack the components label and dropdown if there's only one
2169        # component.
2170        blt::table forget $parent.volcomponents_l $parent.volcomponents
2171    } else {
2172        # Pack the components label and dropdown into the table there's
2173        # more than one component to select.
2174        blt::table $parent \
2175            0,0 $parent.volcomponents_l -anchor e -cspan 2 \
2176            0,2 $parent.volcomponents -cspan 3 -fill x
2177    }
2178}
2179
2180#
2181# GetDatasetsWithComponents --
2182#
2183#    Returns a list of all the datasets (known by the combination of their
2184#    data object and component name) that match the given component name.
2185#    For example, this is used where we want to change the settings of
2186#    volumes that have the current component.
2187#
2188itcl::body Rappture::NanovisViewer::GetDatasetsWithComponent { cname } {
2189    if { ![info exists _volcomponents($cname)] } {
2190        return ""
2191    }
2192    set list ""
2193    foreach tag $_volcomponents($cname) {
2194        if { ![info exists _serverDatasets($tag)] } {
2195            continue
2196        }
2197        lappend list $tag
2198    }
2199    return $list
2200}
2201
2202#
2203# HideAllMarkers --
2204#
2205#    Hide all the markers in all the transfer functions.  Can't simply
2206#    delete and recreate markers from the <style> since the user may have
2207#    created, deleted, or moved markers.
2208#
2209itcl::body Rappture::NanovisViewer::HideAllMarkers {} {
2210    foreach cname [array names _transferFunctionEditors] {
2211        $_transferFunctionEditors($cname) hideMarkers
2212    }
2213}
2214
2215itcl::body Rappture::NanovisViewer::GetColormap { cname color } {
2216    if { $color == "default" } {
2217        return $_cname2defaultcolormap($cname)
2218    }
2219    return [ColorsToColormap $color]
2220}
2221
2222itcl::body Rappture::NanovisViewer::GetAlphamap { cname name } {
2223    if { $name == "default" } {
2224        return $_cname2defaultalphamap($cname)
2225    }
2226    return [NameToAlphamap $name]
2227}
2228
2229itcl::body Rappture::NanovisViewer::ResetColormap { cname color } {
2230    # Get the current transfer function
2231    if { ![info exists _cname2transferFunction($cname)] } {
2232        return
2233    }
2234    foreach { cmap wmap } $_cname2transferFunction($cname) break
2235    set cmap [GetColormap $cname $color]
2236    set _cname2transferFunction($cname) [list $cmap $wmap]
2237    SendCmd [list transfunc define $cname $cmap $wmap]
2238    EventuallyRedrawLegend
2239}
2240
2241itcl::body Rappture::NanovisViewer::ComputeAlphamap { cname } {
2242    if { ![info exists _transferFunctionEditors($cname)] } {
2243        return [list 0.0 0.0 1.0 1.0]
2244    }
2245    if { ![info exists _settings($cname-light)] } {
2246        InitComponentSettings $cname
2247    }
2248
2249    set isovalues [$_transferFunctionEditors($cname) values]
2250
2251    # Transfer function should be normalized with [0,1] range
2252    # The volume shading opacity setting is used to scale opacity
2253    # in the volume shader.
2254    set max 1.0
2255
2256    # Use the component-wise thickness setting from the slider
2257    # settings widget
2258    # Scale values between 0.00001 and 0.01000
2259    set delta [expr {double($_settings($cname-thickness)) * 0.0001}]
2260   
2261    set first [lindex $isovalues 0]
2262    set last [lindex $isovalues end]
2263    set wmap ""
2264    if { $first == "" || $first != 0.0 } {
2265        lappend wmap 0.0 0.0
2266    }
2267    foreach x $isovalues {
2268        set x1 [expr {$x-$delta-0.00001}]
2269        set x2 [expr {$x-$delta}]
2270        set x3 [expr {$x+$delta}]
2271        set x4 [expr {$x+$delta+0.00001}]
2272        if { $x1 < 0.0 } {
2273            set x1 0.0
2274        } elseif { $x1 > 1.0 } {
2275            set x1 1.0
2276        }
2277        if { $x2 < 0.0 } {
2278            set x2 0.0
2279        } elseif { $x2 > 1.0 } {
2280            set x2 1.0
2281        }
2282        if { $x3 < 0.0 } {
2283            set x3 0.0
2284        } elseif { $x3 > 1.0 } {
2285            set x3 1.0
2286        }
2287        if { $x4 < 0.0 } {
2288            set x4 0.0
2289        } elseif { $x4 > 1.0 } {
2290            set x4 1.0
2291        }
2292        # add spikes in the middle
2293        lappend wmap $x1 0.0
2294        lappend wmap $x2 $max
2295        lappend wmap $x3 $max
2296        lappend wmap $x4 0.0
2297    }
2298    if { $last == "" || $last != 1.0 } {
2299        lappend wmap 1.0 0.0
2300    }
2301    return $wmap
2302}
2303
2304itcl::body Rappture::NanovisViewer::NameToAlphamap { name } {
2305    switch -- $name {
2306        "ramp-up" {
2307            set wmap {
2308                0.0 0.0
2309                1.0 1.0
2310            }
2311        }
2312        "ramp-down" {
2313            set wmap {
2314                0.0 1.0
2315                1.0 0.0
2316            }
2317        }
2318        "vee" {
2319            set wmap {
2320                0.0 1.0
2321                0.5 0.0
2322                1.0 1.0
2323            }
2324        }
2325        "tent-1" {
2326            set wmap {
2327                0.0 0.0
2328                0.5 1.0
2329                1.0 0.0
2330            }
2331        }
2332        "tent-2" {
2333            set wmap {
2334                0.0 0.0
2335                0.25 1.0
2336                0.5 0.0
2337                0.75 1.0
2338                1.0 0.0
2339            }
2340        }
2341        "tent-3" {
2342            set wmap {
2343                0.0 0.0
2344                0.16666 1.0
2345                0.33333 0.0
2346                0.5     1.0
2347                0.66666 0.0
2348                0.83333 1.0
2349                1.0 0.0
2350            }
2351        }
2352        "tent-4" {
2353            set wmap {
2354                0.0     0.0
2355                0.125   1.0
2356                0.25    0.0
2357                0.375   1.0
2358                0.5     0.0       
2359                0.625   1.0
2360                0.75    0.0
2361                0.875   1.0
2362                1.0     0.0
2363            }
2364        }
2365        "sinusoid-1" {
2366            set wmap {
2367                0.0                     0.000 0.600 0.800
2368                0.14285714285714285     0.400 0.900 1.000
2369                0.2857142857142857      0.600 1.000 1.000
2370                0.42857142857142855     0.800 1.000 1.000
2371                0.5714285714285714      0.900 0.900 0.900
2372                0.7142857142857143      0.600 0.600 0.600
2373                0.8571428571428571      0.400 0.400 0.400
2374                1.0                     0.200 0.200 0.200
2375            }
2376        }
2377        "sinusoid-2" {
2378            set wmap {
2379                0.0                     0.900 1.000 1.000
2380                0.1111111111111111      0.800 0.983 1.000
2381                0.2222222222222222      0.700 0.950 1.000
2382                0.3333333333333333      0.600 0.900 1.000
2383                0.4444444444444444      0.500 0.833 1.000
2384                0.5555555555555556      0.400 0.750 1.000
2385                0.6666666666666666      0.300 0.650 1.000
2386                0.7777777777777778      0.200 0.533 1.000
2387                0.8888888888888888      0.100 0.400 1.000
2388                1.0                     0.000 0.250 1.000
2389            }
2390        }
2391        "sinusoid-6" {
2392            set wmap {
2393                0.0                             0.200   0.100   0.000
2394                0.09090909090909091             0.400   0.187   0.000
2395                0.18181818181818182             0.600   0.379   0.210
2396                0.2727272727272727              0.800   0.608   0.480
2397                0.36363636363636365             0.850   0.688   0.595
2398                0.45454545454545453             0.950   0.855   0.808
2399                0.5454545454545454              0.800   0.993   1.000
2400                0.6363636363636364              0.600   0.973   1.000
2401                0.7272727272727273              0.400   0.940   1.000
2402                0.8181818181818182              0.200   0.893   1.000
2403                0.9090909090909091              0.000   0.667   0.800
2404                1.0                             0.000   0.480   0.600
2405            }
2406        }
2407        "sinusoid-10" {
2408            set wmap {
2409                0.0                             0.000   0.480   0.600
2410                0.09090909090909091             0.000   0.667   0.800
2411                0.18181818181818182             0.200   0.893   1.000
2412                0.2727272727272727              0.400   0.940   1.000
2413                0.36363636363636365             0.600   0.973   1.000
2414                0.45454545454545453             0.800   0.993   1.000
2415                0.5454545454545454              0.950   0.855   0.808
2416                0.6363636363636364              0.850   0.688   0.595
2417                0.7272727272727273              0.800   0.608   0.480
2418                0.8181818181818182              0.600   0.379   0.210
2419                0.9090909090909091              0.400   0.187   0.000
2420                1.0                             0.200   0.100   0.000
2421            }
2422        }
2423        "step-2" {
2424            set wmap {
2425                0.0                             0.000   0.167   1.000
2426                0.09090909090909091             0.100   0.400   1.000
2427                0.18181818181818182             0.200   0.600   1.000
2428                0.2727272727272727              0.400   0.800   1.000
2429                0.36363636363636365             0.600   0.933   1.000
2430                0.45454545454545453             0.800   1.000   1.000
2431                0.5454545454545454              1.000   1.000   0.800
2432                0.6363636363636364              1.000   0.933   0.600
2433                0.7272727272727273              1.000   0.800   0.400
2434                0.8181818181818182              1.000   0.600   0.200
2435                0.9090909090909091              1.000   0.400   0.100
2436                1.0                             1.000   0.167   0.000
2437            }
2438        }
2439        "step-5" {
2440            set wmap {
2441                0.0                             1.000   0.167   0.000
2442                0.09090909090909091             1.000   0.400   0.100
2443                0.18181818181818182             1.000   0.600   0.200
2444                0.2727272727272727              1.000   0.800   0.400
2445                0.36363636363636365             1.000   0.933   0.600
2446                0.45454545454545453             1.000   1.000   0.800
2447                0.5454545454545454              0.800   1.000   1.000
2448                0.6363636363636364              0.600   0.933   1.000
2449                0.7272727272727273              0.400   0.800   1.000
2450                0.8181818181818182              0.200   0.600   1.000
2451                0.9090909090909091              0.100   0.400   1.000
2452                1.0                             0.000   0.167   1.000
2453            }
2454        }
2455        "step-12" {
2456            set wmap {
2457                "#EE82EE"
2458                "#4B0082"
2459                "blue"
2460                "#008000"
2461                "yellow"
2462                "#FFA500"
2463                "red"
2464            }
2465        }
2466        default {
2467        }
2468    }
2469    return ""
2470}
2471
2472itcl::body Rappture::NanovisViewer::SetObjectStyle { dataobj cname } {
2473    array set styles {
2474        -opacity  0.5
2475    }
2476    array set styles [lindex [$dataobj components -style $cname] 0]
2477    set _settings($cname-volumeopacity) $styles(-opacity)
2478    set tag $dataobj-$cname
2479    SendCmd "volume shading opacity $_settings($cname-volumeopacity) $tag"
2480    NameTransferFunction $dataobj $cname
2481}
Note: See TracBrowser for help on using the repository browser.