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

Last change on this file since 5092 was 5092, checked in by ldelgass, 6 years ago

Add update in Rebuild to allow initial canvas size to be set. We don't want to
do this in the constructor since that can cause an error when the add method is
called before the constructor returns.

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