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

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

add comments from 1.4 branch to sync

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        $_dispatcher event -idle !rebuild
902        return
903    }
904
905    # Turn on buffering of commands to the server.  We don't want to
906    # be preempted by a server disconnect/reconnect (which automatically
907    # generates a new call to Rebuild).   
908    StartBufferingCommands
909
910    if { $_width != $w || $_height != $h || $_reset } {
911        set _width $w
912        set _height $h
913        $_arcball resize $w $h
914        DoResize
915    }
916
917    foreach dataobj [get] {
918        foreach cname [$dataobj components] {
919            set tag $dataobj-$cname
920            if { ![info exists _serverDatasets($tag)] } {
921                # Send the data as one huge base64-encoded mess -- yuck!
922                if { [$dataobj type] == "dx" } {
923                    if { ![$dataobj isvalid] } {
924                        puts stderr "??? $dataobj is invalid"
925                    }
926                    set data [$dataobj blob $cname]
927                } else {
928                    set data [$dataobj vtkdata $cname]
929                    if 0 {
930                        set f [open "/tmp/volume.vtk" "w"]
931                        fconfigure $f -translation binary -encoding binary
932                        puts -nonewline $f $data
933                        close $f
934                    }
935                }
936                set nbytes [string length $data]
937                if { $_reportClientInfo }  {
938                    set info {}
939                    lappend info "tool_id"       [$dataobj hints toolid]
940                    lappend info "tool_name"     [$dataobj hints toolname]
941                    lappend info "tool_title"    [$dataobj hints tooltitle]
942                    lappend info "tool_command"  [$dataobj hints toolcommand]
943                    lappend info "tool_revision" [$dataobj hints toolrevision]
944                    lappend info "dataset_label" [$dataobj hints label]
945                    lappend info "dataset_size"  $nbytes
946                    lappend info "dataset_tag"   $tag
947                    SendCmd "clientinfo [list $info]"
948                }
949                SendCmd "volume data follows $nbytes $tag"
950                append _outbuf $data
951                set _recvdDatasets($tag) 1
952                set _serverDatasets($tag) 0
953            }
954            NameTransferFunction $dataobj $cname
955        }
956    }
957    set _first [lindex [get] 0]
958    if { $_reset } {
959        #
960        # Reset the camera and other view parameters
961        #
962        set _settings(-qw)    $_view(-qw)
963        set _settings(-qx)    $_view(-qx)
964        set _settings(-qy)    $_view(-qy)
965        set _settings(-qz)    $_view(-qz)
966        set _settings(-xpan)  $_view(-xpan)
967        set _settings(-ypan)  $_view(-ypan)
968        set _settings(-zoom)  $_view(-zoom)
969
970        set q [ViewToQuaternion]
971        $_arcball quaternion $q
972        SendCmd "camera orient $q"
973        SendCmd "camera reset"
974        PanCamera
975        SendCmd "camera zoom $_view(-zoom)"
976       
977        #cutplane state 0 all
978        foreach axis {x y z} {
979            # Turn off cutplanes for all volumes
980            SendCmd "cutplane state 0 $axis"
981        }
982
983        InitSettings -light2side -ambient -diffuse -specularlevel \
984            -specularexponent -opacity -isosurfaceshading -gridvisible \
985            -axesvisible -xcutplanevisible -ycutplanevisible -zcutplanevisible \
986            -current
987
988        if {"" != $_first} {
989            set axis [$_first hints updir]
990            if { "" != $axis } {
991                SendCmd "up $axis"
992            }
993            set location [$_first hints camera]
994            if { $location != "" } {
995                array set _view $location
996            }
997        }
998    }
999    # Outline seems to need to be reset every update.
1000    InitSettings -outlinevisible -cutplanesvisible
1001    # nothing to send -- activate the proper ivol
1002    SendCmd "volume state 0"
1003    if {"" != $_first} {
1004        set datasets [array names _serverDatasets $_first-*]
1005        if { $datasets != "" } {
1006            SendCmd "volume state 1 $datasets"
1007        }
1008        # If the first volume already exists on the server, then make sure
1009        # we display the proper transfer function in the legend.
1010        set cname [lindex [$_first components] 0]
1011        if { [info exists _serverDatasets($_first-$cname)] } {
1012            updateTransferFunctions
1013        }
1014    }
1015    # Actually write the commands to the server socket.  If it fails, we don't
1016    # care.  We're finished here.
1017    blt::busy hold $itk_component(hull)
1018    StopBufferingCommands
1019    blt::busy release $itk_component(hull)
1020    set _reset 0
1021}
1022
1023# ----------------------------------------------------------------------
1024# USAGE: CurrentDatasets ?-cutplanes?
1025#
1026# Returns a list of volume server IDs for the current volume being
1027# displayed.  This is normally a single ID, but it might be a list
1028# of IDs if the current data object has multiple components.
1029# ----------------------------------------------------------------------
1030itcl::body Rappture::NanovisViewer::CurrentDatasets {{what -all}} {
1031    set rlist ""
1032    if { $_first == "" } {
1033        return
1034    }
1035    foreach cname [$_first components] {
1036        set tag $_first-$cname
1037        if { [info exists _serverDatasets($tag)] && $_serverDatasets($tag) } {
1038            array set styles {
1039                -cutplanes 1
1040            }
1041            array set styles [lindex [$_first components -style $cname] 0]
1042            if { $what != "-cutplanes" || $styles(-cutplanes) } {
1043                lappend rlist $tag
1044            }
1045        }
1046    }
1047    return $rlist
1048}
1049
1050# ----------------------------------------------------------------------
1051# USAGE: Zoom in
1052# USAGE: Zoom out
1053# USAGE: Zoom reset
1054#
1055# Called automatically when the user clicks on one of the zoom
1056# controls for this widget.  Changes the zoom for the current view.
1057# ----------------------------------------------------------------------
1058itcl::body Rappture::NanovisViewer::Zoom {option} {
1059    switch -- $option {
1060        "in" {
1061            set _view(-zoom) [expr {$_view(-zoom)*1.25}]
1062            set _settings(-zoom) $_view(-zoom)
1063            SendCmd "camera zoom $_view(-zoom)"
1064        }
1065        "out" {
1066            set _view(-zoom) [expr {$_view(-zoom)*0.8}]
1067            set _settings(-zoom) $_view(-zoom)
1068            SendCmd "camera zoom $_view(-zoom)"
1069        }
1070        "reset" {
1071            array set _view {
1072                -qw      0.853553
1073                -qx      -0.353553
1074                -qy      0.353553
1075                -qz      0.146447
1076                -xpan    0
1077                -ypan    0
1078                -zoom    1.0
1079            }
1080            if { $_first != "" } {
1081                set location [$_first hints camera]
1082                if { $location != "" } {
1083                    array set _view $location
1084                }
1085            }
1086            set q [ViewToQuaternion]         
1087            $_arcball quaternion $q
1088            SendCmd "camera orient $q"
1089            SendCmd "camera reset"
1090            set _settings(-qw)    $_view(-qw)
1091            set _settings(-qx)    $_view(-qx)
1092            set _settings(-qy)    $_view(-qy)
1093            set _settings(-qz)    $_view(-qz)
1094            set _settings(-xpan)  $_view(-xpan)
1095            set _settings(-ypan)  $_view(-ypan)
1096            set _settings(-zoom)  $_view(-zoom)
1097        }
1098    }
1099}
1100
1101itcl::body Rappture::NanovisViewer::PanCamera {} {
1102    set x $_view(-xpan)
1103    set y $_view(-ypan)
1104    SendCmd "camera pan $x $y"
1105}
1106
1107
1108# ----------------------------------------------------------------------
1109# USAGE: Rotate click <x> <y>
1110# USAGE: Rotate drag <x> <y>
1111# USAGE: Rotate release <x> <y>
1112#
1113# Called automatically when the user clicks/drags/releases in the
1114# plot area.  Moves the plot according to the user's actions.
1115# ----------------------------------------------------------------------
1116itcl::body Rappture::NanovisViewer::Rotate {option x y} {
1117    switch -- $option {
1118        click {
1119            $itk_component(3dview) configure -cursor fleur
1120            set _click(x) $x
1121            set _click(y) $y
1122        }
1123        drag {
1124            if {[array size _click] == 0} {
1125                Rotate click $x $y
1126            } else {
1127                set w [winfo width $itk_component(3dview)]
1128                set h [winfo height $itk_component(3dview)]
1129                if {$w <= 0 || $h <= 0} {
1130                    return
1131                }
1132
1133                if {[catch {
1134                    # this fails sometimes for no apparent reason
1135                    set dx [expr {double($x-$_click(x))/$w}]
1136                    set dy [expr {double($y-$_click(y))/$h}]
1137                }]} {
1138                    return
1139                }
1140
1141                set q [$_arcball rotate $x $y $_click(x) $_click(y)]
1142                QuaternionToView $q
1143                set _settings(-qw) $_view(-qw)
1144                set _settings(-qx) $_view(-qx)
1145                set _settings(-qy) $_view(-qy)
1146                set _settings(-qz) $_view(-qz)
1147                SendCmd "camera orient $q"
1148
1149                set _click(x) $x
1150                set _click(y) $y
1151            }
1152        }
1153        release {
1154            Rotate drag $x $y
1155            $itk_component(3dview) configure -cursor ""
1156            catch {unset _click}
1157        }
1158        default {
1159            error "bad option \"$option\": should be click, drag, release"
1160        }
1161    }
1162}
1163
1164# ----------------------------------------------------------------------
1165# USAGE: $this Pan click x y
1166#        $this Pan drag x y
1167#        $this Pan release x y
1168#
1169# Called automatically when the user clicks on one of the zoom
1170# controls for this widget.  Changes the zoom for the current view.
1171# ----------------------------------------------------------------------
1172itcl::body Rappture::NanovisViewer::Pan {option x y} {
1173    # Experimental stuff
1174    set w [winfo width $itk_component(3dview)]
1175    set h [winfo height $itk_component(3dview)]
1176    if { $option == "set" } {
1177        set x [expr $x / double($w)]
1178        set y [expr $y / double($h)]
1179        set _view(-xpan) [expr $_view(-xpan) + $x]
1180        set _view(-ypan) [expr $_view(-ypan) + $y]
1181        PanCamera
1182        set _settings(-xpan) $_view(-xpan)
1183        set _settings(-ypan) $_view(-ypan)
1184        return
1185    }
1186    if { $option == "click" } {
1187        set _click(x) $x
1188        set _click(y) $y
1189        $itk_component(3dview) configure -cursor hand1
1190    }
1191    if { $option == "drag" || $option == "release" } {
1192        set dx [expr ($_click(x) - $x)/double($w)]
1193        set dy [expr ($_click(y) - $y)/double($h)]
1194        set _click(x) $x
1195        set _click(y) $y
1196        set _view(-xpan) [expr $_view(-xpan) - $dx]
1197        set _view(-ypan) [expr $_view(-ypan) - $dy]
1198        PanCamera
1199        set _settings(-xpan) $_view(-xpan)
1200        set _settings(-ypan) $_view(-ypan)
1201    }
1202    if { $option == "release" } {
1203        $itk_component(3dview) configure -cursor ""
1204    }
1205}
1206
1207# ----------------------------------------------------------------------
1208# USAGE: InitSettings <what> ?<value>?
1209#
1210# Used internally to update rendering settings whenever parameters
1211# change in the popup settings panel.  Sends the new settings off
1212# to the back end.
1213# ----------------------------------------------------------------------
1214itcl::body Rappture::NanovisViewer::InitSettings { args } {
1215    foreach arg $args {
1216        AdjustSetting $arg
1217    }
1218}
1219
1220# ----------------------------------------------------------------------
1221# USAGE: AdjustSetting <what> ?<value>?
1222#
1223# Used internally to update rendering settings whenever parameters
1224# change in the popup settings panel.  Sends the new settings off
1225# to the back end.
1226# ----------------------------------------------------------------------
1227itcl::body Rappture::NanovisViewer::AdjustSetting {what {value ""}} {
1228    if {![isconnected]} {
1229        return
1230    }
1231    switch -- $what {
1232        "-ambient" {
1233            # Other parts of the code use the ambient setting to
1234            # tell if the component settings have been initialized
1235            if { ![info exists _settings($_current${what})] } {
1236                InitComponentSettings $_current
1237            }
1238            set _settings($_current${what}) $_settings($what)
1239            set val $_settings($what)
1240            set val [expr {0.01*$val}]
1241            foreach tag [GetDatasetsWithComponent $_current] {
1242                SendCmd "volume shading ambient $val $tag"
1243            }
1244        }
1245        "-axesvisible" {
1246            SendCmd "axis visible $_settings($what)"
1247        }
1248        "-background" {
1249            set bgcolor [$itk_component(background) value]
1250            array set fgcolors {
1251                "black" "white"
1252                "white" "black"
1253                "grey"  "black"
1254            }
1255            configure -plotbackground $bgcolor \
1256                -plotforeground $fgcolors($bgcolor)
1257            DrawLegend $_current
1258        }
1259        "-colormap" {
1260            set color [$itk_component(colormap) value]
1261            set _settings($what) $color
1262            set _settings($_current${what}) $color
1263            ResetColormap $_current $color
1264        }
1265        "-current" {
1266            set cname [$itk_component(volcomponents) value]
1267            SwitchComponent $cname
1268        }
1269        "-cutplanesvisible" {
1270            set bool $_settings($what)
1271            # We only set cutplanes on the first dataset.
1272            set datasets [CurrentDatasets -cutplanes]
1273            set tag [lindex $datasets 0]
1274            SendCmd "cutplane visible $bool $tag"
1275        }
1276        "-diffuse" {
1277            set _settings($_current${what}) $_settings($what)
1278            set val $_settings($what)
1279            set val [expr {0.01*$val}]
1280            foreach tag [GetDatasetsWithComponent $_current] {
1281                SendCmd "volume shading diffuse $val $tag"
1282            }
1283        }
1284        "-gridvisible" {
1285            SendCmd "grid visible $_settings($what)"
1286        }
1287        "-isosurfaceshading" {
1288            SendCmd "volume shading isosurface $_settings($what)"
1289        }
1290        "-legendvisible" {
1291            if { $_settings($what) } {
1292                blt::table $itk_component(plotarea) \
1293                    0,0 $itk_component(3dview) -fill both \
1294                    1,0 $itk_component(legend) -fill x
1295                blt::table configure $itk_component(plotarea) r1 -resize none
1296            } else {
1297                blt::table forget $itk_component(legend)
1298            }
1299        }
1300        "-light2side" {
1301            set _settings($_current${what}) $_settings($what)
1302            set val $_settings($what)
1303            foreach tag [GetDatasetsWithComponent $_current] {
1304                SendCmd "volume shading light2side $val $tag"
1305            }
1306        }
1307        "-opacity" {
1308            set _settings($_current${what}) $_settings($what)
1309            set val $_settings($what)
1310            set sval [expr { 0.01 * double($val) }]
1311            foreach tag [GetDatasetsWithComponent $_current] {
1312                SendCmd "volume shading opacity $sval $tag"
1313            }
1314        }
1315        "-outlinevisible" {
1316            SendCmd "volume outline state $_settings($what)"
1317        }
1318        "-outlinecolor" {
1319            set rgb [Color2RGB $_settings($what)]
1320            SendCmd "volume outline color $rgb"
1321        }
1322        "-specularlevel" {
1323            set _settings($_current${what}) $_settings($what)
1324            set val $_settings($what)
1325            set val [expr {0.01*$val}]
1326            foreach tag [GetDatasetsWithComponent $_current] {
1327                SendCmd "volume shading specularLevel $val $tag"
1328            }
1329        }
1330        "-specularexponent" {
1331            set _settings($_current${what}) $_settings($what)
1332            set val $_settings($what)
1333            foreach tag [GetDatasetsWithComponent $_current] {
1334                SendCmd "volume shading specularExp $val $tag"
1335            }
1336        }
1337        "-thickness" {
1338            set val $_settings($what)
1339            set _settings($_current${what}) $val
1340            updateTransferFunctions
1341        }
1342        "-volume" {
1343            # This is the global volume visibility control.  It controls the
1344            # visibility of all the all volumes.  Whenever it's changed, you
1345            # have to synchronize each of the local controls (see below) with
1346            # this.
1347            set datasets [CurrentDatasets]
1348            set bool $_settings($what)
1349            SendCmd "volume data state $bool $datasets"
1350            foreach cname $_componentsList {
1351                set _settings($cname-volumevisible) $bool
1352            }
1353            set _settings(-volumevisible) $bool
1354        }
1355        "-volumevisible" {
1356            # This is the component specific control.  It changes the
1357            # visibility of only the current component.
1358            set _settings($_current${what}) $_settings($what)
1359            foreach tag [GetDatasetsWithComponent $_current] {
1360                SendCmd "volume data state $_settings($what) $tag"
1361            }
1362        }
1363        "-xcutplanevisible" - "-ycutplanevisible" - "-zcutplanevisible" {
1364            set axis [string range $what 1 1]
1365            set bool $_settings($what)
1366            # We only set cutplanes on the first dataset.
1367            set datasets [CurrentDatasets -cutplanes]
1368            set tag [lindex $datasets 0]
1369            SendCmd "cutplane state $bool $axis $tag"
1370            if { $bool } {
1371                $itk_component(${axis}CutScale) configure -state normal \
1372                    -troughcolor white
1373            } else {
1374                $itk_component(${axis}CutScale) configure -state disabled \
1375                    -troughcolor grey82
1376            }
1377        }
1378        default {
1379            error "don't know how to fix $what"
1380        }
1381    }
1382}
1383
1384# ----------------------------------------------------------------------
1385# USAGE: FixLegend
1386#
1387# Used internally to update the legend area whenever it changes size
1388# or when the field changes.  Asks the server to send a new legend
1389# for the current field.
1390# ----------------------------------------------------------------------
1391itcl::body Rappture::NanovisViewer::FixLegend {} {
1392    set _resizeLegendPending 0
1393    set lineht [font metrics $itk_option(-font) -linespace]
1394    set w [expr {$_width-20}]
1395    set h [expr {[winfo height $itk_component(legend)]-20-$lineht}]
1396    if {$w > 0 && $h > 0 && $_first != "" } {
1397        if { [info exists _cname2transferFunction($_current)] } {
1398            SendCmd "legend $_current $w $h"
1399        }
1400    }
1401}
1402
1403#
1404# NameTransferFunction --
1405#
1406#       Creates a transfer function name based on the <style> settings in the
1407#       library run.xml file. This placeholder will be used later to create
1408#       and send the actual transfer function once the data info has been sent
1409#       to us by the render server. [We won't know the volume limits until the
1410#       server parses the 3D data and sends back the limits via ReceiveData.]
1411#
1412itcl::body Rappture::NanovisViewer::NameTransferFunction { dataobj cname } {
1413    array set styles {
1414        -color BCGYR
1415        -levels 6
1416        -markers ""
1417    }
1418    set tag $dataobj-$cname
1419    array set styles [lindex [$dataobj components -style $cname] 0]
1420    if { ![info exists _cname2transferFunction($cname)] } {
1421        # Get the colormap right now, since it doesn't change with marker
1422        # changes.
1423        set cmap [ColorsToColormap $styles(-color)]
1424        set wmap [list 0.0 0.0 1.0 1.0]
1425        set _cname2transferFunction($cname) [list $cmap $wmap]
1426        SendCmd [list transfunc define $cname $cmap $wmap]
1427    }
1428    SendCmd "volume shading transfunc $cname $tag"
1429    if { ![info exists _transferFunctionEditors($cname)] } {
1430        set _transferFunctionEditors($cname) \
1431            [Rappture::TransferFunctionEditor ::\#auto $itk_component(legend) \
1432                 $cname \
1433                 -command [itcl::code $this updateTransferFunctions]]
1434    }
1435    set _dataset2style($tag) $cname
1436    lappend _style2datasets($cname) $tag
1437    return $cname
1438}
1439
1440#
1441# ComputeTransferFunction --
1442#
1443#       Computes and sends the transfer function to the render server.  It's
1444#       assumed that the volume data limits are known and that the global
1445#       transfer-functions slider values have been set up.  Both parts are
1446#       needed to compute the relative value (location) of the marker, and
1447#       the alpha map of the transfer function.
1448#
1449itcl::body Rappture::NanovisViewer::ComputeTransferFunction { cname } {
1450    foreach {cmap wmap} $_cname2transferFunction($cname) break
1451
1452    # We have to parse the style attributes for a volume using this
1453    # transfer-function *once*.  This sets up the initial isomarkers for the
1454    # transfer function.  The user may add/delete markers, so we have to
1455    # maintain a list of markers for each transfer-function.  We use the one
1456    # of the volumes (the first in the list) using the transfer-function as a
1457    # reference.
1458    if { ![info exists _parsedFunction($cname)] } {
1459        array set styles {
1460            -color BCGYR
1461            -levels 6
1462            -markers ""
1463        }
1464        # Accumulate the style from all the datasets using it.
1465        foreach tag [GetDatasetsWithComponent $cname] {
1466            foreach {dataobj cname} [split [lindex $tag 0] -] break
1467            array set styles [lindex [$dataobj components -style $cname] 0]
1468        }
1469        eval $_transferFunctionEditors($cname) limits $_limits($cname)
1470        # Have to defer creation of isomarkers until we have data limits
1471        if { [info exists styles(-markers)] &&
1472             [llength $styles(-markers)] > 0 } {
1473            ParseMarkersOption $cname $styles(-markers)
1474        } else {
1475            ParseLevelsOption $cname $styles(-levels)
1476        }
1477       
1478    }
1479    set wmap [ComputeAlphamap $cname]
1480    set _cname2transferFunction($cname) [list $cmap $wmap]
1481    SendCmd [list transfunc define $cname $cmap $wmap]
1482}
1483
1484itcl::body Rappture::NanovisViewer::AddNewMarker { x y } {
1485    if { ![info exists _transferFunctionEditors($_current)] } {
1486        continue
1487    }
1488    # Add a new marker to the current transfer function
1489    $_transferFunctionEditors($_current) newMarker $x $y normal
1490    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1491}
1492
1493itcl::body Rappture::NanovisViewer::RemoveMarker { x y } {
1494    if { ![info exists _transferFunctionEditors($_current)] } {
1495        continue
1496    }
1497    # Add a new marker to the current transfer function
1498    $_transferFunctionEditors($_current) deleteMarker $x $y
1499}
1500
1501# ----------------------------------------------------------------------
1502# CONFIGURATION OPTION: -plotbackground
1503# ----------------------------------------------------------------------
1504itcl::configbody Rappture::NanovisViewer::plotbackground {
1505    if { [isconnected] } {
1506        set color $itk_option(-plotbackground)
1507        set rgb [Color2RGB $color]
1508        SendCmd "screen bgcolor $rgb"
1509        $itk_component(legend) configure -background $color
1510    }
1511}
1512
1513# ----------------------------------------------------------------------
1514# CONFIGURATION OPTION: -plotforeground
1515# ----------------------------------------------------------------------
1516itcl::configbody Rappture::NanovisViewer::plotforeground {
1517    if { [isconnected] } {
1518        set color $itk_option(-plotforeground)
1519        set rgb [Color2RGB $color]
1520        SendCmd "volume outline color $rgb"
1521        SendCmd "grid axiscolor $rgb"
1522        SendCmd "grid linecolor $rgb"
1523        $itk_component(legend) itemconfigure labels -fill $color
1524        $itk_component(legend) itemconfigure limits -fill $color
1525    }
1526}
1527
1528# ----------------------------------------------------------------------
1529# CONFIGURATION OPTION: -plotoutline
1530# ----------------------------------------------------------------------
1531itcl::configbody Rappture::NanovisViewer::plotoutline {
1532    # Must check if we are connected because this routine is called from the
1533    # class body when the -plotoutline itk_option is defined.  At that point
1534    # the NanovisViewer class constructor hasn't been called, so we can't
1535    # start sending commands to visualization server.
1536    if { [isconnected] } {
1537        if {"" == $itk_option(-plotoutline)} {
1538            SendCmd "volume outline state off"
1539        } else {
1540            SendCmd "volume outline state on"
1541            SendCmd "volume outline color [Color2RGB $itk_option(-plotoutline)]"
1542        }
1543    }
1544}
1545
1546#
1547# The -levels option takes a single value that represents the number
1548# of evenly distributed markers based on the current data range. Each
1549# marker is a relative value from 0.0 to 1.0.
1550#
1551itcl::body Rappture::NanovisViewer::ParseLevelsOption { cname levels } {
1552    set c $itk_component(legend)
1553    set list {}
1554    regsub -all "," $levels " " levels
1555    if {[string is int $levels]} {
1556        for {set i 1} { $i <= $levels } {incr i} {
1557            lappend list [expr {double($i)/($levels+1)}]
1558        }
1559    } else {
1560        foreach x $levels {
1561            lappend list $x
1562        }
1563    }
1564    set _parsedFunction($cname) 1
1565    $_transferFunctionEditors($cname) addMarkers $list
1566    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1567}
1568
1569#
1570# The -markers option takes a list of zero or more values (the values
1571# may be separated either by spaces or commas) that have the following
1572# format:
1573#
1574#   N%  Percent of current total data range.  Converted to
1575#       to a relative value between 0.0 and 1.0.
1576#   N   Absolute value of marker.  If the marker is outside of
1577#       the current range, it will be displayed on the outer
1578#       edge of the legends, but it range it represents will
1579#       not be seen.
1580#
1581itcl::body Rappture::NanovisViewer::ParseMarkersOption { cname markers } {
1582    set c $itk_component(legend)
1583    set list {}
1584    foreach { min max } $_limits($cname) break
1585    regsub -all "," $markers " " markers
1586    foreach marker $markers {
1587        set n [scan $marker "%g%s" value suffix]
1588        if { $n == 2 && $suffix == "%" } {
1589            # $n% : Set relative value (0..1).
1590            lappend list [expr {$value * 0.01}]
1591        } else {
1592            # $n : absolute value, compute relative
1593            lappend list  [expr {(double($value)-$min)/($max-$min)]}
1594        }
1595    }
1596    set _parsedFunction($cname) 1
1597    $_transferFunctionEditors($cname) addMarkers $list
1598    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1599}
1600
1601# ----------------------------------------------------------------------
1602# USAGE: UndateTransferFuncs
1603# ----------------------------------------------------------------------
1604itcl::body Rappture::NanovisViewer::updateTransferFunctions {} {
1605    $_dispatcher event -idle !send_transfunc
1606}
1607
1608itcl::body Rappture::NanovisViewer::limits { cname } {
1609    set _limits(min) 0.0
1610    set _limits(max) 1.0
1611    if { ![info exists _style2datasets($cname)] } {
1612        return [array get _limits]
1613    }
1614    set min ""; set max ""
1615    foreach tag [GetDatasetsWithComponent $cname] {
1616        if { ![info exists _limits($tag)] } {
1617            continue
1618        }
1619        foreach {amin amax} $_limits($tag) break
1620        if { $min == "" || $min > $amin } {
1621            set min $amin
1622        }
1623        if { $max == "" || $max < $amax } {
1624            set max $amax
1625        }
1626    }
1627    if { $min != "" } {
1628        set _limits(min) $min
1629    }
1630    if { $max != "" } {
1631        set _limits(max) $max
1632    }
1633    return [list $_limits(min) $_limits(max)]
1634}
1635
1636
1637itcl::body Rappture::NanovisViewer::BuildViewTab {} {
1638    set fg [option get $itk_component(hull) font Font]
1639    #set bfg [option get $itk_component(hull) boldFont Font]
1640
1641    set inner [$itk_component(main) insert end \
1642        -title "View Settings" \
1643        -icon [Rappture::icon wrench]]
1644    $inner configure -borderwidth 4
1645
1646    set ::Rappture::NanovisViewer::_settings(-isosurfaceshading) 0
1647    checkbutton $inner.isosurface \
1648        -text "Isosurface shading" \
1649        -variable [itcl::scope _settings(-isosurfaceshading)] \
1650        -command [itcl::code $this AdjustSetting -isosurfaceshading] \
1651        -font "Arial 9"
1652
1653    checkbutton $inner.axes \
1654        -text "Axes" \
1655        -variable [itcl::scope _settings(-axesvisible)] \
1656        -command [itcl::code $this AdjustSetting -axesvisible] \
1657        -font "Arial 9"
1658
1659    checkbutton $inner.grid \
1660        -text "Grid" \
1661        -variable [itcl::scope _settings(-gridvisible)] \
1662        -command [itcl::code $this AdjustSetting -gridvisible] \
1663        -font "Arial 9"
1664
1665    checkbutton $inner.outline \
1666        -text "Outline" \
1667        -variable [itcl::scope _settings(-outlinevisible)] \
1668        -command [itcl::code $this AdjustSetting -outlinevisible] \
1669        -font "Arial 9"
1670
1671    checkbutton $inner.legend \
1672        -text "Legend" \
1673        -variable [itcl::scope _settings(-legendvisible)] \
1674        -command [itcl::code $this AdjustSetting -legendvisible] \
1675        -font "Arial 9"
1676
1677    checkbutton $inner.volume \
1678        -text "Volume" \
1679        -variable [itcl::scope _settings(-volume)] \
1680        -command [itcl::code $this AdjustSetting -volume] \
1681        -font "Arial 9"
1682
1683    label $inner.background_l -text "Background" -font "Arial 9"
1684    itk_component add background {
1685        Rappture::Combobox $inner.background -width 10 -editable no
1686    }
1687    $inner.background choices insert end \
1688        "black"              "black"            \
1689        "white"              "white"            \
1690        "grey"               "grey"             
1691
1692    $itk_component(background) value $_settings(-background)
1693    bind $inner.background <<Value>> \
1694        [itcl::code $this AdjustSetting -background]
1695
1696    blt::table $inner \
1697        0,0 $inner.axes  -cspan 2 -anchor w \
1698        1,0 $inner.grid  -cspan 2 -anchor w \
1699        2,0 $inner.outline  -cspan 2 -anchor w \
1700        3,0 $inner.volume  -cspan 2 -anchor w \
1701        4,0 $inner.legend  -cspan 2 -anchor w \
1702        5,0 $inner.background_l       -anchor e -pady 2 \
1703        5,1 $inner.background                   -fill x \
1704
1705    if 0 {
1706    bind $inner <Map> [itcl::code $this GetVolumeInfo $inner]
1707    }
1708    blt::table configure $inner r* -resize none
1709    blt::table configure $inner r6 -resize expand
1710}
1711
1712itcl::body Rappture::NanovisViewer::BuildVolumeTab {} {
1713    set inner [$itk_component(main) insert end \
1714        -title "Volume Settings" \
1715        -icon [Rappture::icon volume-on]]
1716    $inner configure -borderwidth 4
1717
1718    set fg [option get $itk_component(hull) font Font]
1719    #set bfg [option get $itk_component(hull) boldFont Font]
1720
1721    label $inner.lighting_l \
1722        -text "Lighting / Material Properties" \
1723        -font "Arial 9 bold"
1724
1725    checkbutton $inner.light2side -text "Two-sided lighting" -font $fg \
1726        -variable [itcl::scope _settings(-light2side)] \
1727        -command [itcl::code $this AdjustSetting -light2side]
1728
1729    checkbutton $inner.visibility -text "Visible" -font $fg \
1730        -variable [itcl::scope _settings(-volumevisible)] \
1731        -command [itcl::code $this AdjustSetting -volumevisible]
1732
1733    label $inner.ambient_l -text "Ambient" -font $fg
1734    ::scale $inner.ambient -from 0 -to 100 -orient horizontal \
1735        -variable [itcl::scope _settings(-ambient)] \
1736        -showvalue off -command [itcl::code $this AdjustSetting -ambient] \
1737        -troughcolor grey92
1738
1739    label $inner.diffuse_l -text "Diffuse" -font $fg
1740    ::scale $inner.diffuse -from 0 -to 100 -orient horizontal \
1741        -variable [itcl::scope _settings(-diffuse)] \
1742        -showvalue off -command [itcl::code $this AdjustSetting -diffuse] \
1743        -troughcolor grey92
1744
1745    label $inner.specularLevel_l -text "Specular" -font $fg
1746    ::scale $inner.specularLevel -from 0 -to 100 -orient horizontal \
1747        -variable [itcl::scope _settings(-specularlevel)] \
1748        -showvalue off \
1749        -command [itcl::code $this AdjustSetting -specularlevel] \
1750        -troughcolor grey92
1751
1752    label $inner.specularExponent_l -text "Shininess" -font $fg
1753    ::scale $inner.specularExponent -from 10 -to 128 -orient horizontal \
1754        -variable [itcl::scope _settings(-specularexponent)] \
1755        -showvalue off \
1756        -command [itcl::code $this AdjustSetting -specularexponent] \
1757        -troughcolor grey92
1758
1759    # Opacity
1760    label $inner.opacity_l -text "Opacity" -font $fg
1761    ::scale $inner.opacity -from 0 -to 100 -orient horizontal \
1762        -variable [itcl::scope _settings(-opacity)] \
1763        -showvalue off -command [itcl::code $this AdjustSetting -opacity] \
1764        -troughcolor grey92
1765
1766    label $inner.transferfunction_l \
1767        -text "Transfer Function" -font "Arial 9 bold"
1768
1769    # Tooth thickness
1770    label $inner.thin -text "Thin" -font $fg
1771    ::scale $inner.thickness -from 0 -to 1000 -orient horizontal \
1772        -variable [itcl::scope _settings(-thickness)] \
1773        -showvalue off -command [itcl::code $this AdjustSetting -thickness] \
1774        -troughcolor grey92
1775
1776    label $inner.thick -text "Thick" -font $fg
1777
1778    # Colormap
1779    label $inner.colormap_l -text "Colormap" -font $fg
1780    itk_component add colormap {
1781        Rappture::Combobox $inner.colormap -width 10 -editable no
1782    }
1783
1784    $inner.colormap choices insert end [GetColormapList -includeDefault -includeNone]
1785    bind $inner.colormap <<Value>> \
1786        [itcl::code $this AdjustSetting -colormap]
1787    $itk_component(colormap) value "default"
1788    set _settings(-colormap) "default"
1789
1790    # Component
1791    label $inner.volcomponents_l -text "Component" -font $fg
1792    itk_component add volcomponents {
1793        Rappture::Combobox $inner.volcomponents -editable no
1794    }
1795    bind $inner.volcomponents <<Value>> \
1796        [itcl::code $this AdjustSetting -current]
1797
1798    blt::table $inner \
1799        0,0 $inner.volcomponents_l -anchor e -cspan 2 \
1800        0,2 $inner.volcomponents             -cspan 3 -fill x \
1801        1,1 $inner.lighting_l -anchor w -cspan 4 \
1802        2,1 $inner.ambient_l       -anchor e -pady 2 \
1803        2,2 $inner.ambient                   -cspan 3 -fill x \
1804        3,1 $inner.diffuse_l       -anchor e -pady 2 \
1805        3,2 $inner.diffuse                   -cspan 3 -fill x \
1806        4,1 $inner.specularLevel_l -anchor e -pady 2 \
1807        4,2 $inner.specularLevel             -cspan 3 -fill x \
1808        5,1 $inner.specularExponent_l -anchor e -pady 2 \
1809        5,2 $inner.specularExponent          -cspan 3 -fill x \
1810        6,1 $inner.light2side -cspan 3 -anchor w \
1811        7,1 $inner.visibility -cspan 3 -anchor w \
1812        8,1 $inner.transferfunction_l -anchor w              -cspan 4 \
1813        9,1 $inner.opacity_l -anchor e -pady 2 \
1814        9,2 $inner.opacity                    -cspan 3 -fill x \
1815        10,1 $inner.colormap_l       -anchor e \
1816        10,2 $inner.colormap                  -padx 2 -cspan 3 -fill x \
1817        11,1 $inner.thin             -anchor e \
1818        11,2 $inner.thickness                 -cspan 2 -fill x \
1819        11,4 $inner.thick -anchor w 
1820
1821    blt::table configure $inner c* r* -resize none
1822    blt::table configure $inner r* -pady { 2 0 }
1823    blt::table configure $inner c2 c3 r12 -resize expand
1824    blt::table configure $inner c0 -width .1i
1825}
1826
1827itcl::body Rappture::NanovisViewer::BuildCutplanesTab {} {
1828    set inner [$itk_component(main) insert end \
1829        -title "Cutplane Settings" \
1830        -icon [Rappture::icon cutbutton]]
1831    $inner configure -borderwidth 4
1832
1833    checkbutton $inner.visible \
1834        -text "Show Cutplanes" \
1835        -variable [itcl::scope _settings(-cutplanesvisible)] \
1836        -command [itcl::code $this AdjustSetting -cutplanesvisible] \
1837        -font "Arial 9"
1838
1839    # X-value slicer...
1840    itk_component add xCutButton {
1841        Rappture::PushButton $inner.xbutton \
1842            -onimage [Rappture::icon x-cutplane] \
1843            -offimage [Rappture::icon x-cutplane] \
1844            -command [itcl::code $this AdjustSetting -xcutplanevisible] \
1845            -variable [itcl::scope _settings(-xcutplanevisible)]
1846    }
1847    Rappture::Tooltip::for $itk_component(xCutButton) \
1848        "Toggle the X cut plane on/off"
1849    $itk_component(xCutButton) select
1850
1851    itk_component add xCutScale {
1852        ::scale $inner.xval -from 100 -to 0 \
1853            -width 10 -orient vertical -showvalue off \
1854            -borderwidth 1 -highlightthickness 0 \
1855            -command [itcl::code $this Slice move x] \
1856            -variable [itcl::scope _settings(-xcutplaneposition)]
1857    } {
1858        usual
1859        ignore -borderwidth -highlightthickness
1860    }
1861    # Set the default cutplane value before disabling the scale.
1862    $itk_component(xCutScale) set 50
1863    $itk_component(xCutScale) configure -state disabled
1864    Rappture::Tooltip::for $itk_component(xCutScale) \
1865        "@[itcl::code $this SlicerTip x]"
1866
1867    # Y-value slicer...
1868    itk_component add yCutButton {
1869        Rappture::PushButton $inner.ybutton \
1870            -onimage [Rappture::icon y-cutplane] \
1871            -offimage [Rappture::icon y-cutplane] \
1872            -command [itcl::code $this AdjustSetting -ycutplanevisible] \
1873            -variable [itcl::scope _settings(-ycutplanevisible)]
1874    }
1875    Rappture::Tooltip::for $itk_component(yCutButton) \
1876        "Toggle the Y cut plane on/off"
1877    $itk_component(yCutButton) select
1878
1879    itk_component add yCutScale {
1880        ::scale $inner.yval -from 100 -to 0 \
1881            -width 10 -orient vertical -showvalue off \
1882            -borderwidth 1 -highlightthickness 0 \
1883            -command [itcl::code $this Slice move y] \
1884            -variable [itcl::scope _settings(-ycutplaneposition)]
1885    } {
1886        usual
1887        ignore -borderwidth -highlightthickness
1888    }
1889    Rappture::Tooltip::for $itk_component(yCutScale) \
1890        "@[itcl::code $this SlicerTip y]"
1891    # Set the default cutplane value before disabling the scale.
1892    $itk_component(yCutScale) set 50
1893    $itk_component(yCutScale) configure -state disabled
1894
1895    # Z-value slicer...
1896    itk_component add zCutButton {
1897        Rappture::PushButton $inner.zbutton \
1898            -onimage [Rappture::icon z-cutplane] \
1899            -offimage [Rappture::icon z-cutplane] \
1900            -command [itcl::code $this AdjustSetting -zcutplanevisible] \
1901            -variable [itcl::scope _settings(-zcutplanevisible)]
1902    }
1903    Rappture::Tooltip::for $itk_component(zCutButton) \
1904        "Toggle the Z cut plane on/off"
1905    $itk_component(zCutButton) select
1906
1907    itk_component add zCutScale {
1908        ::scale $inner.zval -from 100 -to 0 \
1909            -width 10 -orient vertical -showvalue off \
1910            -borderwidth 1 -highlightthickness 0 \
1911            -command [itcl::code $this Slice move z] \
1912            -variable [itcl::scope _settings(-zcutplaneposition)]
1913    } {
1914        usual
1915        ignore -borderwidth -highlightthickness
1916    }
1917    $itk_component(zCutScale) set 50
1918    $itk_component(zCutScale) configure -state disabled
1919    Rappture::Tooltip::for $itk_component(zCutScale) \
1920        "@[itcl::code $this SlicerTip z]"
1921
1922    blt::table $inner \
1923        0,1 $inner.visible              -anchor w -pady 2 -cspan 4 \
1924        1,1 $itk_component(xCutScale) \
1925        1,2 $itk_component(yCutScale) \
1926        1,3 $itk_component(zCutScale) \
1927        2,1 $itk_component(xCutButton) \
1928        2,2 $itk_component(yCutButton) \
1929        2,3 $itk_component(zCutButton)
1930
1931    blt::table configure $inner r0 r1 r2 c* -resize none
1932    blt::table configure $inner r3 c4 -resize expand
1933    blt::table configure $inner c0 -width 2
1934    blt::table configure $inner c1 c2 c3 -padx 2
1935}
1936
1937itcl::body Rappture::NanovisViewer::BuildCameraTab {} {
1938    set inner [$itk_component(main) insert end \
1939        -title "Camera Settings" \
1940        -icon [Rappture::icon camera]]
1941    $inner configure -borderwidth 4
1942
1943    label $inner.view_l -text "view" -font "Arial 9"
1944    set f [frame $inner.view]
1945    foreach side { front back left right top bottom } {
1946        button $f.$side  -image [Rappture::icon view$side] \
1947            -command [itcl::code $this SetOrientation $side]
1948        Rappture::Tooltip::for $f.$side "Change the view to $side"
1949        pack $f.$side -side left
1950    }
1951
1952    blt::table $inner \
1953        0,0 $inner.view_l -anchor e -pady 2 \
1954        0,1 $inner.view -anchor w -pady 2
1955    blt::table configure $inner r0 -resize none
1956
1957    set row 1
1958    set labels { qw qx qy qz xpan ypan zoom }
1959    foreach tag $labels {
1960        label $inner.${tag}label -text $tag -font "Arial 9"
1961        entry $inner.${tag} -font "Arial 9"  -bg white \
1962            -textvariable [itcl::scope _settings(-$tag)]
1963        bind $inner.${tag} <Return> \
1964            [itcl::code $this camera set -${tag}]
1965        bind $inner.${tag} <KP_Enter> \
1966            [itcl::code $this camera set -${tag}]
1967        blt::table $inner \
1968            $row,0 $inner.${tag}label -anchor e -pady 2 \
1969            $row,1 $inner.${tag} -anchor w -pady 2
1970        blt::table configure $inner r$row -resize none
1971        incr row
1972    }
1973
1974    blt::table configure $inner c* -resize none
1975    blt::table configure $inner c2 -resize expand
1976    blt::table configure $inner r$row -resize expand
1977}
1978
1979# ----------------------------------------------------------------------
1980# USAGE: Slice move x|y|z <newval>
1981#
1982# Called automatically when the user drags the slider to move the
1983# cut plane that slices 3D data.  Gets the current value from the
1984# slider and moves the cut plane to the appropriate point in the
1985# data set.
1986# ----------------------------------------------------------------------
1987itcl::body Rappture::NanovisViewer::Slice {option args} {
1988    switch -- $option {
1989        move {
1990            if {[llength $args] != 2} {
1991                error "wrong # args: should be \"Slice move x|y|z newval\""
1992            }
1993            set axis [lindex $args 0]
1994            set newval [lindex $args 1]
1995
1996            set newpos [expr {0.01*$newval}]
1997            set datasets [CurrentDatasets -cutplanes]
1998            set tag [lindex $datasets 0]
1999            SendCmd "cutplane position $newpos $axis $tag"
2000        }
2001        default {
2002            error "bad option \"$option\": should be axis, move, or volume"
2003        }
2004    }
2005}
2006
2007# ----------------------------------------------------------------------
2008# USAGE: SlicerTip <axis>
2009#
2010# Used internally to generate a tooltip for the x/y/z slicer controls.
2011# Returns a message that includes the current slicer value.
2012# ----------------------------------------------------------------------
2013itcl::body Rappture::NanovisViewer::SlicerTip {axis} {
2014    set val [$itk_component(${axis}CutScale) get]
2015    return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
2016}
2017
2018
2019itcl::body Rappture::NanovisViewer::DoResize {} {
2020    $_arcball resize $_width $_height
2021    SendCmd "screen size $_width $_height"
2022    set _resizePending 0
2023}
2024
2025itcl::body Rappture::NanovisViewer::EventuallyResize { w h } {
2026    set _width $w
2027    set _height $h
2028    $_arcball resize $w $h
2029    if { !$_resizePending } {
2030        $_dispatcher event -idle !resize
2031        set _resizePending 1
2032    }
2033}
2034
2035itcl::body Rappture::NanovisViewer::EventuallyRedrawLegend {} {
2036    if { !$_resizeLegendPending } {
2037        $_dispatcher event -idle !legend
2038        set _resizeLegendPending 1
2039    }
2040}
2041
2042#  camera --
2043#
2044itcl::body Rappture::NanovisViewer::camera {option args} {
2045    switch -- $option {
2046        "show" {
2047            puts [array get _view]
2048        }
2049        "set" {
2050            set what [lindex $args 0]
2051            set x $_settings($what)
2052            set code [catch { string is double $x } result]
2053            if { $code != 0 || !$result } {
2054                set _settings($what) $_view($what)
2055                return
2056            }
2057            switch -- $what {
2058                "-xpan" - "-ypan" {
2059                    set _view($what) $_settings($what)
2060                    PanCamera
2061                }
2062                "-qx" - "-qy" - "-qz" - "-qw" {
2063                    set _view($what) $_settings($what)
2064                    set q [ViewToQuaternion]
2065                    $_arcball quaternion $q
2066                    SendCmd "camera orient $q"
2067                }
2068                "-zoom" {
2069                    set _view($what) $_settings($what)
2070                    SendCmd "camera zoom $_view($what)"
2071                }
2072            }
2073        }
2074    }
2075}
2076
2077itcl::body Rappture::NanovisViewer::GetVolumeInfo { w } {
2078    set flowobj ""
2079    foreach key [array names _obj2flow] {
2080        set flowobj $_obj2flow($key)
2081        break
2082    }
2083    if { $flowobj == "" } {
2084        return
2085    }
2086    if { [winfo exists $w.frame] } {
2087        destroy $w.frame
2088    }
2089    set inner [frame $w.frame]
2090    blt::table $w \
2091        5,0 $inner -fill both -cspan 2 -anchor nw
2092    array set hints [$dataobj hints]
2093
2094    label $inner.volumes -text "Volumes" -font "Arial 9 bold"
2095    blt::table $inner \
2096        1,0 $inner.volumes  -anchor w \
2097    blt::table configure $inner c0 c1 -resize none
2098    blt::table configure $inner c2 -resize expand
2099
2100    set row 3
2101    set volumes [get]
2102    if { [llength $volumes] > 0 } {
2103        blt::table $inner $row,0 $inner.volumes  -anchor w
2104        incr row
2105    }
2106    foreach vol $volumes {
2107        array unset info
2108        array set info $vol
2109        set name $info(name)
2110        if { ![info exists _settings(-volumevisible-$name)] } {
2111            set _settings(-volumevisible-$name) $info(hide)
2112        }
2113        checkbutton $inner.vol$row -text $info(label) \
2114            -variable [itcl::scope _settings(-volumevisible-$name)] \
2115            -onvalue 0 -offvalue 1 \
2116            -command [itcl::code $this ToggleVolume $key $name] \
2117            -font "Arial 9"
2118        Rappture::Tooltip::for $inner.vol$row $info(description)
2119        blt::table $inner $row,0 $inner.vol$row -anchor w
2120        if { !$_settings(-volume-$name) } {
2121            $inner.vol$row select
2122        }
2123        incr row
2124    }
2125    blt::table configure $inner r* -resize none
2126    blt::table configure $inner r$row -resize expand
2127    blt::table configure $inner c3 -resize expand
2128    event generate [winfo parent [winfo parent $w]] <Configure>
2129}
2130
2131itcl::body Rappture::NanovisViewer::ToggleVolume { tag name } {
2132    set bool $_settings(-volumevisible-$name)
2133    SendCmd "volume state $bool $name"
2134}
2135
2136itcl::body Rappture::NanovisViewer::SetOrientation { side } {
2137    array set positions {
2138        front "1 0 0 0"
2139        back  "0 0 1 0"
2140        left  "0.707107 0 -0.707107 0"
2141        right "0.707107 0 0.707107 0"
2142        top   "0.707107 -0.707107 0 0"
2143        bottom "0.707107 0.707107 0 0"
2144    }
2145    foreach name { -qw -qx -qy -qz } value $positions($side) {
2146        set _view($name) $value
2147    }
2148    set q [ViewToQuaternion]
2149    $_arcball quaternion $q
2150    SendCmd "camera orient $q"
2151    SendCmd "camera reset"
2152    set _view(-xpan) 0
2153    set _view(-ypan) 0
2154    set _view(-zoom) 1.0
2155    set _settings(-xpan) $_view(-xpan)
2156    set _settings(-ypan) $_view(-ypan)
2157    set _settings(-zoom) $_view(-zoom)
2158}
2159
2160
2161#
2162# InitComponentSettings --
2163#
2164#    Initializes the volume settings for a specific component. This should
2165#    match what's used as global settings above. This is called the first
2166#    time we try to switch to a given component in SwitchComponent below.
2167#
2168itcl::body Rappture::NanovisViewer::InitComponentSettings { cname } {
2169    # Expanding component name for key.
2170    array set _settings [subst {
2171        $cname-ambient           60
2172        $cname-colormap          default
2173        $cname-diffuse           40
2174        $cname-light2side        1
2175        $cname-opacity           50
2176        $cname-specularexponent  90
2177        $cname-specularlevel     30
2178        $cname-thickness         350
2179        $cname-volumevisible     1
2180    }]
2181}
2182
2183#
2184# SwitchComponent --
2185#
2186#    This is called when the current component is changed by the dropdown
2187#    menu in the volume tab.  It synchronizes the global volume settings
2188#    with the settings of the new current component.
2189#
2190itcl::body Rappture::NanovisViewer::SwitchComponent { cname } {
2191    if { ![info exists _settings($cname-ambient)] } {
2192        InitComponentSettings $cname
2193    }
2194    # _settings variables change widgets, except for colormap
2195    set _settings(-ambient)          $_settings($cname-ambient)
2196    set _settings(-colormap)         $_settings($cname-colormap)
2197    set _settings(-diffuse)          $_settings($cname-diffuse)
2198    set _settings(-light2side)       $_settings($cname-light2side)
2199    set _settings(-opacity)          $_settings($cname-opacity)
2200    set _settings(-specularexponent) $_settings($cname-specularexponent)
2201    set _settings(-specularlevel)    $_settings($cname-specularlevel)
2202    set _settings(-thickness)        $_settings($cname-thickness)
2203    set _settings(-volumevisible)    $_settings($cname-volumevisible)
2204    $itk_component(colormap) value   $_settings($cname-colormap)
2205    set _current $cname;                # Reset the current component
2206}
2207
2208#
2209# BuildVolumeComponents --
2210#
2211#    This is called from the "scale" method which is called when a new
2212#    dataset is added or deleted.  It repopulates the dropdown menu of
2213#    volume component names.  It sets the current component to the first
2214#    component in the list (of components found).  Finally, if there is
2215#    only one component, don't display the label or the combobox in the
2216#    volume settings tab.
2217#
2218itcl::body Rappture::NanovisViewer::BuildVolumeComponents {} {
2219    $itk_component(volcomponents) choices delete 0 end
2220    foreach name $_componentsList {
2221        $itk_component(volcomponents) choices insert end $name $name
2222    }
2223    set _current [lindex $_componentsList 0]
2224    $itk_component(volcomponents) value $_current
2225    set parent [winfo parent $itk_component(volcomponents)]
2226    if { [llength $_componentsList] <= 1 } {
2227        # Unpack the components label and dropdown if there's only one
2228        # component.
2229        blt::table forget $parent.volcomponents_l $parent.volcomponents
2230    } else {
2231        # Pack the components label and dropdown into the table there's
2232        # more than one component to select.
2233        blt::table $parent \
2234            0,0 $parent.volcomponents_l -anchor e -cspan 2 \
2235            0,2 $parent.volcomponents -cspan 3 -fill x
2236    }
2237}
2238
2239#
2240# GetDatasetsWithComponents --
2241#
2242#    Returns a list of all the datasets (known by the combination of their
2243#    data object and component name) that match the given component name.
2244#    For example, this is used where we want to change the settings of
2245#    volumes that have the current component.
2246#
2247itcl::body Rappture::NanovisViewer::GetDatasetsWithComponent { cname } {
2248    if { ![info exists _volcomponents($cname)] } {
2249        return ""
2250    }
2251    set list ""
2252    foreach tag $_volcomponents($cname) {
2253        if { ![info exists _serverDatasets($tag)] } {
2254            continue
2255        }
2256        lappend list $tag
2257    }
2258    return $list
2259}
2260
2261#
2262# HideAllMarkers --
2263#
2264#    Hide all the markers in all the transfer functions.  Can't simply
2265#    delete and recreate markers from the <style> since the user may have
2266#    created, deleted, or moved markers.
2267#
2268itcl::body Rappture::NanovisViewer::HideAllMarkers {} {
2269    foreach cname [array names _transferFunctionEditors] {
2270        $_transferFunctionEditors($cname) hideMarkers
2271    }
2272}
2273
2274itcl::body Rappture::NanovisViewer::GetColormap { cname color } {
2275    if { $color == "default" } {
2276        return $_cname2defaultcolormap($cname)
2277    }
2278    return [ColorsToColormap $color]
2279}
2280
2281itcl::body Rappture::NanovisViewer::GetAlphamap { cname name } {
2282    if { $name == "default" } {
2283        return $_cname2defaultalphamap($cname)
2284    }
2285    return [NameToAlphamap $name]
2286}
2287
2288itcl::body Rappture::NanovisViewer::ResetColormap { cname color } {
2289    # Get the current transfer function
2290    if { ![info exists _cname2transferFunction($cname)] } {
2291        return
2292    }
2293    foreach { cmap wmap } $_cname2transferFunction($cname) break
2294    set cmap [GetColormap $cname $color]
2295    set _cname2transferFunction($cname) [list $cmap $wmap]
2296    SendCmd [list transfunc define $cname $cmap $wmap]
2297    EventuallyRedrawLegend
2298}
2299
2300itcl::body Rappture::NanovisViewer::ComputeAlphamap { cname } {
2301    if { ![info exists _transferFunctionEditors($cname)] } {
2302        return [list 0.0 0.0 1.0 1.0]
2303    }
2304    if { ![info exists _settings($cname-ambient)] } {
2305        InitComponentSettings $cname
2306    }
2307
2308    set isovalues [$_transferFunctionEditors($cname) values]
2309
2310    # Transfer function should be normalized with [0,1] range
2311    # The volume shading opacity setting is used to scale opacity
2312    # in the volume shader.
2313    set max 1.0
2314
2315    # Use the component-wise thickness setting from the slider
2316    # settings widget
2317    # Scale values between 0.00001 and 0.01000
2318    set delta [expr {double($_settings($cname-thickness)) * 0.0001}]
2319   
2320    set first [lindex $isovalues 0]
2321    set last [lindex $isovalues end]
2322    set wmap ""
2323    if { $first == "" || $first != 0.0 } {
2324        lappend wmap 0.0 0.0
2325    }
2326    foreach x $isovalues {
2327        set x1 [expr {$x-$delta-0.00001}]
2328        set x2 [expr {$x-$delta}]
2329        set x3 [expr {$x+$delta}]
2330        set x4 [expr {$x+$delta+0.00001}]
2331        if { $x1 < 0.0 } {
2332            set x1 0.0
2333        } elseif { $x1 > 1.0 } {
2334            set x1 1.0
2335        }
2336        if { $x2 < 0.0 } {
2337            set x2 0.0
2338        } elseif { $x2 > 1.0 } {
2339            set x2 1.0
2340        }
2341        if { $x3 < 0.0 } {
2342            set x3 0.0
2343        } elseif { $x3 > 1.0 } {
2344            set x3 1.0
2345        }
2346        if { $x4 < 0.0 } {
2347            set x4 0.0
2348        } elseif { $x4 > 1.0 } {
2349            set x4 1.0
2350        }
2351        # add spikes in the middle
2352        lappend wmap $x1 0.0
2353        lappend wmap $x2 $max
2354        lappend wmap $x3 $max
2355        lappend wmap $x4 0.0
2356    }
2357    if { $last == "" || $last != 1.0 } {
2358        lappend wmap 1.0 0.0
2359    }
2360    return $wmap
2361}
2362
2363itcl::body Rappture::NanovisViewer::NameToAlphamap { name } {
2364    switch -- $name {
2365        "ramp-up" {
2366            set wmap {
2367                0.0 0.0
2368                1.0 1.0
2369            }
2370        }
2371        "ramp-down" {
2372            set wmap {
2373                0.0 1.0
2374                1.0 0.0
2375            }
2376        }
2377        "vee" {
2378            set wmap {
2379                0.0 1.0
2380                0.5 0.0
2381                1.0 1.0
2382            }
2383        }
2384        "tent-1" {
2385            set wmap {
2386                0.0 0.0
2387                0.5 1.0
2388                1.0 0.0
2389            }
2390        }
2391        "tent-2" {
2392            set wmap {
2393                0.0 0.0
2394                0.25 1.0
2395                0.5 0.0
2396                0.75 1.0
2397                1.0 0.0
2398            }
2399        }
2400        "tent-3" {
2401            set wmap {
2402                0.0 0.0
2403                0.16666 1.0
2404                0.33333 0.0
2405                0.5     1.0
2406                0.66666 0.0
2407                0.83333 1.0
2408                1.0 0.0
2409            }
2410        }
2411        "tent-4" {
2412            set wmap {
2413                0.0     0.0
2414                0.125   1.0
2415                0.25    0.0
2416                0.375   1.0
2417                0.5     0.0       
2418                0.625   1.0
2419                0.75    0.0
2420                0.875   1.0
2421                1.0     0.0
2422            }
2423        }
2424        "sinusoid-1" {
2425            set wmap {
2426                0.0                     0.000 0.600 0.800
2427                0.14285714285714285     0.400 0.900 1.000
2428                0.2857142857142857      0.600 1.000 1.000
2429                0.42857142857142855     0.800 1.000 1.000
2430                0.5714285714285714      0.900 0.900 0.900
2431                0.7142857142857143      0.600 0.600 0.600
2432                0.8571428571428571      0.400 0.400 0.400
2433                1.0                     0.200 0.200 0.200
2434            }
2435        }
2436        "sinusoid-2" {
2437            set wmap {
2438                0.0                     0.900 1.000 1.000
2439                0.1111111111111111      0.800 0.983 1.000
2440                0.2222222222222222      0.700 0.950 1.000
2441                0.3333333333333333      0.600 0.900 1.000
2442                0.4444444444444444      0.500 0.833 1.000
2443                0.5555555555555556      0.400 0.750 1.000
2444                0.6666666666666666      0.300 0.650 1.000
2445                0.7777777777777778      0.200 0.533 1.000
2446                0.8888888888888888      0.100 0.400 1.000
2447                1.0                     0.000 0.250 1.000
2448            }
2449        }
2450        "sinusoid-6" {
2451            set wmap {
2452                0.0                             0.200   0.100   0.000
2453                0.09090909090909091             0.400   0.187   0.000
2454                0.18181818181818182             0.600   0.379   0.210
2455                0.2727272727272727              0.800   0.608   0.480
2456                0.36363636363636365             0.850   0.688   0.595
2457                0.45454545454545453             0.950   0.855   0.808
2458                0.5454545454545454              0.800   0.993   1.000
2459                0.6363636363636364              0.600   0.973   1.000
2460                0.7272727272727273              0.400   0.940   1.000
2461                0.8181818181818182              0.200   0.893   1.000
2462                0.9090909090909091              0.000   0.667   0.800
2463                1.0                             0.000   0.480   0.600
2464            }
2465        }
2466        "sinusoid-10" {
2467            set wmap {
2468                0.0                             0.000   0.480   0.600
2469                0.09090909090909091             0.000   0.667   0.800
2470                0.18181818181818182             0.200   0.893   1.000
2471                0.2727272727272727              0.400   0.940   1.000
2472                0.36363636363636365             0.600   0.973   1.000
2473                0.45454545454545453             0.800   0.993   1.000
2474                0.5454545454545454              0.950   0.855   0.808
2475                0.6363636363636364              0.850   0.688   0.595
2476                0.7272727272727273              0.800   0.608   0.480
2477                0.8181818181818182              0.600   0.379   0.210
2478                0.9090909090909091              0.400   0.187   0.000
2479                1.0                             0.200   0.100   0.000
2480            }
2481        }
2482        "step-2" {
2483            set wmap {
2484                0.0                             0.000   0.167   1.000
2485                0.09090909090909091             0.100   0.400   1.000
2486                0.18181818181818182             0.200   0.600   1.000
2487                0.2727272727272727              0.400   0.800   1.000
2488                0.36363636363636365             0.600   0.933   1.000
2489                0.45454545454545453             0.800   1.000   1.000
2490                0.5454545454545454              1.000   1.000   0.800
2491                0.6363636363636364              1.000   0.933   0.600
2492                0.7272727272727273              1.000   0.800   0.400
2493                0.8181818181818182              1.000   0.600   0.200
2494                0.9090909090909091              1.000   0.400   0.100
2495                1.0                             1.000   0.167   0.000
2496            }
2497        }
2498        "step-5" {
2499            set wmap {
2500                0.0                             1.000   0.167   0.000
2501                0.09090909090909091             1.000   0.400   0.100
2502                0.18181818181818182             1.000   0.600   0.200
2503                0.2727272727272727              1.000   0.800   0.400
2504                0.36363636363636365             1.000   0.933   0.600
2505                0.45454545454545453             1.000   1.000   0.800
2506                0.5454545454545454              0.800   1.000   1.000
2507                0.6363636363636364              0.600   0.933   1.000
2508                0.7272727272727273              0.400   0.800   1.000
2509                0.8181818181818182              0.200   0.600   1.000
2510                0.9090909090909091              0.100   0.400   1.000
2511                1.0                             0.000   0.167   1.000
2512            }
2513        }
2514        "step-12" {
2515            set wmap {
2516                "#EE82EE"
2517                "#4B0082"
2518                "blue"
2519                "#008000"
2520                "yellow"
2521                "#FFA500"
2522                "red"
2523            }
2524        }
2525        default {
2526        }
2527    }
2528    return ""
2529}
Note: See TracBrowser for help on using the repository browser.