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

Last change on this file since 921 was 921, checked in by gah, 17 years ago

untabify; new settings controls for nanovisviewer

File size: 56.1 KB
Line 
1
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-2005  Purdue Research Foundation
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
19option add *NanovisViewer.width 4i widgetDefault
20option add *NanovisViewer*cursor crosshair widgetDefault
21option add *NanovisViewer.height 4i widgetDefault
22option add *NanovisViewer.foreground black widgetDefault
23option add *NanovisViewer.controlBackground gray widgetDefault
24option add *NanovisViewer.controlDarkBackground #999999 widgetDefault
25option add *NanovisViewer.plotBackground black widgetDefault
26option add *NanovisViewer.plotForeground white widgetDefault
27option add *NanovisViewer.plotOutline gray widgetDefault
28option add *NanovisViewer.font \
29    -*-helvetica-medium-r-normal-*-12-* widgetDefault
30
31# must use this name -- plugs into Rappture::resources::load
32proc NanovisViewer_init_resources {} {
33    Rappture::resources::register \
34        nanovis_server [list Rappture::VisViewer::SetServerList "nanovis"]
35}
36
37itcl::class Rappture::NanovisViewer {
38    inherit Rappture::VisViewer
39 
40    itk_option define -plotforeground plotForeground Foreground ""
41    itk_option define -plotbackground plotBackground Background ""
42    itk_option define -plotoutline plotOutline PlotOutline ""
43   
44    constructor { hostlist args } {
45        Rappture::VisViewer::constructor $hostlist
46    } {
47        # defined below
48    }
49    destructor {
50        # defined below
51    }
52    public method add {dataobj {settings ""}}
53    public method get {args}
54    public method delete {args}
55    public method scale {args}
56    public method get_limits { { option ""} } {
57        return [array get _limits]
58    }
59    public method download {option args}
60    public method parameters {title args} {
61        # do nothing
62    }
63    public method isconnected {}
64
65    protected method Connect {}
66    protected method Disconnect {}
67    protected method _send {string}
68    protected method _send_dataobjs {}
69    protected method _send_transfuncs {}
70
71    protected method _receive_image {option size}
72    protected method _receive_legend {ivol vmin vmax size}
73    protected method _receive_data {args}
74
75    protected method _rebuild {}
76    protected method _currentVolumeIds {{what -all}}
77    protected method _zoom {option}
78    protected method _move {option x y}
79    protected method _slice {option args}
80    protected method _slicertip {axis}
81    protected method _probe {option args}
82    protected method _marker {index option args}
83
84    protected method _state {comp}
85    protected method _fixSettings {what {value ""}}
86    protected method _fixLegend {}
87    protected method GenTransfuncData {dataobj comp}
88    public method UpdateTransferFunction {}
89    public method RemoveDuplicateIsoMarker { m x }
90    public method OverIsoMarker { m x }
91    private method AddIsoMarker { x y }
92    private method InitIsoMarkers {dataobj comp}
93    private method HideIsoMarkers {dataobj}
94    private method ShowIsoMarkers {dataobj}
95
96    private variable _outbuf       ;# buffer for outgoing commands
97
98    private variable _dlist ""     ;# list of data objects
99    private variable _all_data_objs
100    private variable _dims ""      ;# dimensionality of data objects
101    private variable _obj2style    ;# maps dataobj => style settings
102    private variable _obj2ovride   ;# maps dataobj => style override
103    private variable _obj2id       ;# maps dataobj => volume ID in server
104    private variable _id2obj       ;# maps dataobj => volume ID in server
105    private variable _sendobjs ""  ;# list of data objs to send to server
106    private variable _sendobjs2 ""  ;# list of data objs to send to server
107    private variable _receiveids   ;# list of data objs to send to server
108    private variable _opacity
109    private variable _thickness
110
111    private variable _click        ;# info used for _move operations
112    private variable _limits       ;# autoscale min/max for all axes
113    private variable _view         ;# view params for 3D view
114       
115    private variable _isomarkers    ;# array of isosurface level values 0..1
116    private common _settings
117}
118
119itk::usual NanovisViewer {
120    keep -background -foreground -cursor -font
121    keep -plotbackground -plotforeground
122}
123
124# ----------------------------------------------------------------------
125# CONSTRUCTOR
126# ----------------------------------------------------------------------
127itcl::body Rappture::NanovisViewer::constructor {hostlist args} {
128
129    # Draw legend event
130    $_dispatcher register !legend
131    $_dispatcher dispatch $this !legend "[itcl::code $this _fixLegend]; list"
132    # Send dataobjs event
133    $_dispatcher register !send_dataobjs
134    $_dispatcher dispatch $this !send_dataobjs \
135        "[itcl::code $this _send_dataobjs]; list"
136    # Send transfer functions event
137    $_dispatcher register !send_transfuncs
138    $_dispatcher dispatch $this !send_transfuncs \
139        "[itcl::code $this _send_transfuncs]; list"
140    # Rebuild event
141    $_dispatcher register !rebuild
142    $_dispatcher dispatch $this !rebuild "[itcl::code $this _rebuild]; list"
143
144    set _outbuf ""
145
146    #
147    # Populate parser with commands handle incoming requests
148    #
149    $_parser alias image [itcl::code $this _receive_image]
150    $_parser alias legend [itcl::code $this _receive_legend]
151    $_parser alias data [itcl::code $this _receive_data]
152
153    # Initialize the view to some default parameters.
154    array set _view {
155        theta   45
156        phi     45
157        psi     0
158        zoom    1.0
159        xfocus  0
160        yfocus  0
161        zfocus  0
162    }
163    set _obj2id(count) 0
164    set _id2obj(count) 0
165    set _limits(vmin) 0.0
166    set _limits(vmax) 1.0
167
168    itk_component add zoom {
169        frame $itk_component(controls).zoom
170    } {
171        usual
172        rename -background -controlbackground controlBackground Background
173    }
174    pack $itk_component(zoom) -side top
175
176    itk_component add reset {
177        button $itk_component(zoom).reset \
178            -borderwidth 1 -padx 1 -pady 1 \
179            -bitmap [Rappture::icon reset] \
180            -command [itcl::code $this _zoom reset]
181    } {
182        usual
183        ignore -borderwidth
184        rename -highlightbackground -controlbackground controlBackground Background
185    }
186    pack $itk_component(reset) -side left -padx {4 1} -pady 4
187    Rappture::Tooltip::for $itk_component(reset) "Reset the view to the default zoom level"
188
189    itk_component add zoomin {
190        button $itk_component(zoom).zin \
191            -borderwidth 1 -padx 1 -pady 1 \
192            -bitmap [Rappture::icon zoomin] \
193            -command [itcl::code $this _zoom in]
194    } {
195        usual
196        ignore -borderwidth
197        rename -highlightbackground -controlbackground controlBackground Background
198    }
199    pack $itk_component(zoomin) -side left -padx 1 -pady 4
200    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
201
202    itk_component add zoomout {
203        button $itk_component(zoom).zout \
204            -borderwidth 1 -padx 1 -pady 1 \
205            -bitmap [Rappture::icon zoomout] \
206            -command [itcl::code $this _zoom out]
207    } {
208        usual
209        ignore -borderwidth
210        rename -highlightbackground -controlbackground controlBackground Background
211    }
212    pack $itk_component(zoomout) -side left -padx {1 4} -pady 4
213    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
214
215    #
216    # Create slicer controls...
217    #
218    itk_component add slicers {
219        frame $itk_component(controls).slicers
220    } {
221        usual
222        rename -background -controlbackground controlBackground Background
223    }
224    pack $itk_component(slicers) -side bottom -padx 4 -pady 4
225    grid rowconfigure $itk_component(slicers) 1 -weight 1
226    #
227    # X-value slicer...
228    #
229    itk_component add xslice {
230        label $itk_component(slicers).xslice \
231            -borderwidth 1 -relief raised -padx 1 -pady 1 \
232            -bitmap [Rappture::icon x]
233    } {
234        usual
235        ignore -borderwidth
236        rename -highlightbackground -controlbackground controlBackground Background
237    }
238    bind $itk_component(xslice) <ButtonPress> \
239        [itcl::code $this _slice axis x toggle]
240    Rappture::Tooltip::for $itk_component(xslice) \
241        "Toggle the X cut plane on/off"
242    grid $itk_component(xslice) -row 1 -column 0 -sticky ew -padx 1
243
244    itk_component add xslicer {
245        ::scale $itk_component(slicers).xval -from 100 -to 0 \
246            -width 10 -orient vertical -showvalue off \
247            -borderwidth 1 -highlightthickness 0 \
248            -command [itcl::code $this _slice move x]
249    } {
250        usual
251        ignore -borderwidth
252        ignore -highlightthickness
253        rename -highlightbackground -controlbackground controlBackground Background
254        rename -troughcolor -controldarkbackground controlDarkBackground Background
255    }
256    $itk_component(xslicer) set 50
257    $itk_component(xslicer) configure -state disabled
258    grid $itk_component(xslicer) -row 2 -column 0 -padx 1
259    Rappture::Tooltip::for $itk_component(xslicer) \
260        "@[itcl::code $this _slicertip x]"
261
262    #
263    # Y-value slicer...
264    #
265    itk_component add yslice {
266        label $itk_component(slicers).yslice \
267            -borderwidth 1 -relief raised -padx 1 -pady 1 \
268            -bitmap [Rappture::icon y]
269    } {
270        usual
271        ignore -borderwidth
272        rename -highlightbackground -controlbackground controlBackground Background
273    }
274    bind $itk_component(yslice) <ButtonPress> \
275        [itcl::code $this _slice axis y toggle]
276    Rappture::Tooltip::for $itk_component(yslice) \
277        "Toggle the Y cut plane on/off"
278    grid $itk_component(yslice) -row 1 -column 1 -sticky ew -padx 1
279
280    itk_component add yslicer {
281        ::scale $itk_component(slicers).yval -from 100 -to 0 \
282            -width 10 -orient vertical -showvalue off \
283            -borderwidth 1 -highlightthickness 0 \
284            -command [itcl::code $this _slice move y]
285    } {
286        usual
287        ignore -borderwidth
288        ignore -highlightthickness
289        rename -highlightbackground -controlbackground controlBackground Background
290        rename -troughcolor -controldarkbackground controlDarkBackground Background
291    }
292    $itk_component(yslicer) set 50
293    $itk_component(yslicer) configure -state disabled
294    grid $itk_component(yslicer) -row 2 -column 1 -padx 1
295    Rappture::Tooltip::for $itk_component(yslicer) \
296        "@[itcl::code $this _slicertip y]"
297
298    #
299    # Z-value slicer...
300    #
301    itk_component add zslice {
302        label $itk_component(slicers).zslice \
303            -borderwidth 1 -relief raised -padx 1 -pady 1 \
304            -bitmap [Rappture::icon z]
305    } {
306        usual
307        ignore -borderwidth
308        rename -highlightbackground -controlbackground controlBackground Background
309    }
310    grid $itk_component(zslice) -row 1 -column 2 -sticky ew -padx 1
311    bind $itk_component(zslice) <ButtonPress> \
312        [itcl::code $this _slice axis z toggle]
313    Rappture::Tooltip::for $itk_component(zslice) \
314        "Toggle the Z cut plane on/off"
315
316    itk_component add zslicer {
317        ::scale $itk_component(slicers).zval -from 100 -to 0 \
318            -width 10 -orient vertical -showvalue off \
319            -borderwidth 1 -highlightthickness 0 \
320            -command [itcl::code $this _slice move z]
321    } {
322        usual
323        ignore -borderwidth
324        ignore -highlightthickness
325        rename -highlightbackground -controlbackground controlBackground Background
326        rename -troughcolor -controldarkbackground controlDarkBackground Background
327    }
328    $itk_component(zslicer) set 50
329    $itk_component(zslicer) configure -state disabled
330    grid $itk_component(zslicer) -row 2 -column 2 -padx 1
331    Rappture::Tooltip::for $itk_component(zslicer) \
332        "@[itcl::code $this _slicertip z]"
333
334    #
335    # Volume toggle...
336    #
337    itk_component add volume {
338        label $itk_component(slicers).volume \
339            -borderwidth 1 -relief sunken -padx 1 -pady 1 \
340            -text "Volume"
341    } {
342        usual
343        ignore -borderwidth
344        rename -highlightbackground -controlbackground controlBackground Background
345    }
346    bind $itk_component(volume) <ButtonPress> \
347        [itcl::code $this _slice volume toggle]
348    Rappture::Tooltip::for $itk_component(volume) \
349        "Toggle the volume cloud on/off"
350    grid $itk_component(volume) -row 0 -column 0 -columnspan 3 \
351        -sticky ew -padx 1 -pady 3
352
353    #
354    # Settings panel...
355    #
356    itk_component add settings {
357        button $itk_component(controls).settings -text "Settings..." \
358            -borderwidth 1 -relief flat -overrelief raised \
359            -padx 2 -pady 1 \
360            -command [list $itk_component(controls).panel activate $itk_component(controls).settings left]
361    } {
362        usual
363        ignore -borderwidth
364        rename -background -controlbackground controlBackground Background
365        rename -highlightbackground -controlbackground controlBackground Background
366    }
367    pack $itk_component(settings) -side top -pady 8
368
369    Rappture::Balloon $itk_component(controls).panel -title "Settings"
370    set inner [$itk_component(controls).panel component inner]
371    frame $inner.scales
372    pack $inner.scales -side top -fill x
373    grid columnconfigure $inner.scales 1 -weight 1
374    set fg [option get $itk_component(hull) font Font]
375
376    label $inner.scales.diml -text "Dim" -font $fg
377    grid $inner.scales.diml -row 0 -column 0 -sticky e
378    ::scale $inner.scales.light -from 0 -to 100 -orient horizontal \
379        -showvalue off -command [itcl::code $this _fixSettings light]
380    grid $inner.scales.light -row 0 -column 1 -sticky ew
381    label $inner.scales.brightl -text "Bright" -font $fg
382    grid $inner.scales.brightl -row 0 -column 2 -sticky w
383    $inner.scales.light set 40
384
385    label $inner.scales.fogl -text "Fog" -font $fg
386    grid $inner.scales.fogl -row 1 -column 0 -sticky e
387    ::scale $inner.scales.transp -from 0 -to 100 -orient horizontal \
388        -showvalue off -command [itcl::code $this _fixSettings transp]
389    grid $inner.scales.transp -row 1 -column 1 -sticky ew
390    label $inner.scales.plasticl -text "Plastic" -font $fg
391    grid $inner.scales.plasticl -row 1 -column 2 -sticky w
392    $inner.scales.transp set 50
393
394    label $inner.scales.zerol -text "Clear" -font $fg
395    grid $inner.scales.zerol -row 2 -column 0 -sticky e
396    ::scale $inner.scales.opacity -from 0 -to 100 -orient horizontal \
397        -showvalue off -command [itcl::code $this _fixSettings opacity]
398    grid $inner.scales.opacity -row 2 -column 1 -sticky ew
399    label $inner.scales.onel -text "Opaque" -font $fg
400    grid $inner.scales.onel -row 2 -column 2 -sticky w
401    $inner.scales.opacity set 100
402
403    label $inner.scales.thinl -text "Thin" -font $fg
404    grid $inner.scales.thinl -row 3 -column 0 -sticky e
405    ::scale $inner.scales.thickness -from 0 -to 1000 -orient horizontal \
406        -showvalue off -command [itcl::code $this _fixSettings thickness]
407    grid $inner.scales.thickness -row 3 -column 1 -sticky ew
408    label $inner.scales.thickl -text "Thick" -font $fg
409    grid $inner.scales.thickl -row 3 -column 2 -sticky w
410    $inner.scales.thickness set 500
411
412    set ::Rappture::NanovisViewer::_settings($this-isosurface) 1
413    ::checkbutton $inner.scales.isosurface \
414        -text "Isosurface shading" \
415        -variable ::Rappture::NanovisViewer::_settings($this-isosurface) \
416        -command [itcl::code $this _fixSettings isosurface]
417    grid $inner.scales.isosurface -row 4 -column 0 -columnspan 2 -sticky w
418
419    set ::Rappture::NanovisViewer::_settings($this-axes) 1
420    ::checkbutton $inner.scales.axes \
421        -text "Show Axes" \
422        -variable ::Rappture::NanovisViewer::_settings($this-axes) \
423        -command [itcl::code $this _fixSettings axes]
424    grid $inner.scales.axes -row 5 -column 0 -columnspan 2 -sticky w
425
426    set ::Rappture::NanovisViewer::_settings($this-grid) 0
427    ::checkbutton $inner.scales.grid \
428        -text "Show Grid" \
429        -variable ::Rappture::NanovisViewer::_settings($this-grid) \
430        -command [itcl::code $this _fixSettings grid]
431    grid $inner.scales.grid -row 6 -column 0 -columnspan 2 -sticky w
432
433    set ::Rappture::NanovisViewer::_settings($this-outline) 1
434    ::checkbutton $inner.scales.outline \
435        -text "Show Outline" \
436        -variable ::Rappture::NanovisViewer::_settings($this-outline) \
437        -command [itcl::code $this _fixSettings outline]
438    grid $inner.scales.outline -row 7 -column 0 -columnspan 2 -sticky w
439
440    # Legend
441
442    set _image(legend) [image create photo]
443    itk_component add legend {
444        canvas $itk_component(area).legend -height 50 -highlightthickness 0
445    } {
446        usual
447        ignore -highlightthickness
448        rename -background -plotbackground plotBackground Background
449    }
450    pack $itk_component(legend) -side bottom -fill x
451    bind $itk_component(legend) <Configure> \
452        [list $_dispatcher event -idle !legend]
453
454    # set up bindings for rotation
455    bind $itk_component(3dview) <ButtonPress> \
456        [itcl::code $this _move click %x %y]
457    bind $itk_component(3dview) <B1-Motion> \
458        [itcl::code $this _move drag %x %y]
459    bind $itk_component(3dview) <ButtonRelease> \
460        [itcl::code $this _move release %x %y]
461    bind $itk_component(3dview) <Configure> \
462        [itcl::code $this _send "screen %w %h"]
463
464    set _image(download) [image create photo]
465
466    eval itk_initialize $args
467
468    Connect
469}
470
471# ----------------------------------------------------------------------
472# DESTRUCTOR
473# ----------------------------------------------------------------------
474itcl::body Rappture::NanovisViewer::destructor {} {
475    set _sendobjs ""  ;# stop any send in progress
476    set _sendobjs2 ""  ;# stop any send in progress
477    $_dispatcher cancel !rebuild
478    $_dispatcher cancel !send_dataobjs
479    $_dispatcher cancel !send_transfuncs
480    image delete $_image(plot)
481    image delete $_image(legend)
482    image delete $_image(download)
483    array unset _settings $this-*
484}
485
486# ----------------------------------------------------------------------
487# USAGE: add <dataobj> ?<settings>?
488#
489# Clients use this to add a data object to the plot.  The optional
490# <settings> are used to configure the plot.  Allowed settings are
491# -color, -brightness, -width, -linestyle, and -raise.
492# ----------------------------------------------------------------------
493itcl::body Rappture::NanovisViewer::add {dataobj {settings ""}} {
494    array set params {
495        -color auto
496        -width 1
497        -linestyle solid
498        -brightness 0
499        -raise 0
500        -description ""
501        -param ""
502    }
503    foreach {opt val} $settings {
504        if {![info exists params($opt)]} {
505            error "bad setting \"$opt\": should be [join [lsort [array names params]] {, }]"
506        }
507        set params($opt) $val
508    }
509    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
510        # can't handle -autocolors yet
511        set params(-color) black
512    }
513
514    set pos [lsearch -exact $dataobj $_dlist]
515    if {$pos < 0} {
516        lappend _dlist $dataobj
517        set _all_data_objs($dataobj) 1
518        set _obj2ovride($dataobj-color) $params(-color)
519        set _obj2ovride($dataobj-width) $params(-width)
520        set _obj2ovride($dataobj-raise) $params(-raise)
521        $_dispatcher event -idle !rebuild
522    }
523}
524
525# ----------------------------------------------------------------------
526# USAGE: get ?-objects?
527# USAGE: get ?-image 3dview|legend?
528#
529# Clients use this to query the list of objects being plotted, in
530# order from bottom to top of this result.  The optional "-image"
531# flag can also request the internal images being shown.
532# ----------------------------------------------------------------------
533itcl::body Rappture::NanovisViewer::get {args} {
534    if {[llength $args] == 0} {
535        set args "-objects"
536    }
537
538    set op [lindex $args 0]
539    switch -- $op {
540      -objects {
541        # put the dataobj list in order according to -raise options
542        set dlist $_dlist
543        foreach obj $dlist {
544            if {[info exists _obj2ovride($obj-raise)] && $_obj2ovride($obj-raise)} {
545                set i [lsearch -exact $dlist $obj]
546                if {$i >= 0} {
547                    set dlist [lreplace $dlist $i $i]
548                    lappend dlist $obj
549                }
550            }
551        }
552        return $dlist
553      }
554      -image {
555        if {[llength $args] != 2} {
556            error "wrong # args: should be \"get -image 3dview|legend\""
557        }
558        switch -- [lindex $args end] {
559            3dview {
560                return $_image(plot)
561            }
562            legend {
563                return $_image(legend)
564            }
565            default {
566                error "bad image name \"[lindex $args end]\": should be 3dview or legend"
567            }
568        }
569      }
570      default {
571        error "bad option \"$op\": should be -objects or -image"
572      }
573    }
574}
575
576# ----------------------------------------------------------------------
577# USAGE: delete ?<dataobj1> <dataobj2> ...?
578#
579# Clients use this to delete a dataobj from the plot.  If no dataobjs
580# are specified, then all dataobjs are deleted.
581# ----------------------------------------------------------------------
582itcl::body Rappture::NanovisViewer::delete {args} {
583    if {[llength $args] == 0} {
584        set args $_dlist
585    }
586
587    # delete all specified dataobjs
588    set changed 0
589    foreach dataobj $args {
590        set pos [lsearch -exact $_dlist $dataobj]
591        if {$pos >= 0} {
592            set _dlist [lreplace $_dlist $pos $pos]
593            foreach key [array names _obj2ovride $dataobj-*] {
594                unset _obj2ovride($key)
595            }
596            set changed 1
597        }
598    }
599
600    # if anything changed, then rebuild the plot
601    if {$changed} {
602        $_dispatcher event -idle !rebuild
603    }
604}
605
606# ----------------------------------------------------------------------
607# USAGE: scale ?<data1> <data2> ...?
608#
609# Sets the default limits for the overall plot according to the
610# limits of the data for all of the given <data> objects.  This
611# accounts for all objects--even those not showing on the screen.
612# Because of this, the limits are appropriate for all objects as
613# the user scans through data in the ResultSet viewer.
614# ----------------------------------------------------------------------
615itcl::body Rappture::NanovisViewer::scale {args} {
616    foreach val {xmin xmax ymin ymax zmin zmax vmin vmax} {
617        set _limits($val) ""
618    }
619    foreach obj $args {
620        foreach axis {x y z v} {
621            foreach {min max} [$obj limits $axis] break
622            if {"" != $min && "" != $max} {
623                if {"" == $_limits(${axis}min)} {
624                    set _limits(${axis}min) $min
625                    set _limits(${axis}max) $max
626                } else {
627                    if {$min < $_limits(${axis}min)} {
628                        set _limits(${axis}min) $min
629                    }
630                    if {$max > $_limits(${axis}max)} {
631                        set _limits(${axis}max) $max
632                    }
633                }
634            }
635        }
636    }
637}
638
639# ----------------------------------------------------------------------
640# USAGE: download coming
641# USAGE: download controls <downloadCommand>
642# USAGE: download now
643#
644# Clients use this method to create a downloadable representation
645# of the plot.  Returns a list of the form {ext string}, where
646# "ext" is the file extension (indicating the type of data) and
647# "string" is the data itself.
648# ----------------------------------------------------------------------
649itcl::body Rappture::NanovisViewer::download {option args} {
650    switch $option {
651        coming {
652            if {[catch {blt::winop snap $itk_component(area) $_image(download)}]} {
653                $_image(download) configure -width 1 -height 1
654                $_image(download) put #000000
655            }
656        }
657        controls {
658            # no controls for this download yet
659            return ""
660        }
661        now {
662            # Doing an image base64 encode/decode has to be better than
663            # writing the image to a file and reading it back in.
664            set data [$_image(plot) data -format jpeg]
665            set data [Rappture::encoding::decode -as b64 $data]
666            return [list .jpg $data]
667        }
668        default {
669            error "bad option \"$option\": should be coming, controls, now"
670        }
671    }
672}
673
674# ----------------------------------------------------------------------
675# USAGE: Connect ?<host:port>,<host:port>...?
676#
677# Clients use this method to establish a connection to a new
678# server, or to reestablish a connection to the previous server.
679# Any existing connection is automatically closed.
680# ----------------------------------------------------------------------
681itcl::body Rappture::NanovisViewer::Connect {} {
682    set _hosts [GetServerList "nanovis"]
683    if { "" == $_hosts } {
684        return 0
685    }
686    set result [VisViewer::Connect $_hosts]
687    return $result
688}
689
690#
691# isconnected --
692#
693#       Indicates if we are currently connected to the visualization server.
694#
695itcl::body Rappture::NanovisViewer::isconnected {} {
696    return [VisViewer::IsConnected]
697}
698
699#
700# Disconnect --
701#
702#       Clients use this method to disconnect from the current rendering
703#       server.
704#
705itcl::body Rappture::NanovisViewer::Disconnect {} {
706    VisViewer::Disconnect
707
708    # disconnected -- no more data sitting on server
709    set _outbuf ""
710    catch {unset _obj2id}
711    array unset _id2obj
712    set _obj2id(count) 0
713    set _id2obj(count) 0
714    set _sendobjs ""
715    set _sendobjs2 ""
716}
717
718#
719# _send
720#
721#       Send commands off to the rendering server.  If we're currently
722#       sending data objects to the server, buffer the commands to be
723#       sent later.
724#
725itcl::body Rappture::NanovisViewer::_send {string} {
726    if {[llength $_sendobjs] > 0} {
727        append _outbuf $string "\n"
728    } else {
729        if {[SendBytes $string]} {
730            foreach line [split $string \n] {
731                SendEcho >>line $line
732            }
733        }
734    }
735}
736
737# ----------------------------------------------------------------------
738# USAGE: _send_dataobjs
739#
740# Used internally to send a series of volume objects off to the
741# server.  Sends each object, a little at a time, with updates in
742# between so the interface doesn't lock up.
743# ----------------------------------------------------------------------
744itcl::body Rappture::NanovisViewer::_send_dataobjs {} {
745    blt::busy hold $itk_component(hull); update idletasks
746
747    foreach dataobj $_sendobjs {
748        foreach comp [$dataobj components] {
749            # send the data as one huge base64-encoded mess -- yuck!
750            set data [$dataobj values $comp]
751
752            # tell the engine to expect some data
753            set nbytes [string length $data]
754            if { ![SendBytes "volume data follows $nbytes"] } {
755                return
756            }
757            if { ![SendBytes $data] } {
758                return
759            }
760            set volId $_obj2id(count)
761            incr _obj2id(count)
762
763            set _id2obj($volId) [list $dataobj $comp]
764            set _obj2id($dataobj-$comp) $volId
765            set _receiveids($volId) 1
766        }
767    }
768    set _sendobjs ""
769    blt::busy release $itk_component(hull)
770
771    # activate the proper volume
772    set first [lindex [get] 0]
773    if {"" != $first} {
774        set axis [$first hints updir]
775        if {"" != $axis} {
776            _send "up $axis"
777        }
778    }
779
780    # sync the state of slicers
781    set vols [_currentVolumeIds -cutplanes]
782    foreach axis {x y z} {
783        _send "cutplane state [_state ${axis}slice] $axis $vols"
784        set pos [expr {0.01*[$itk_component(${axis}slicer) get]}]
785        _send "cutplane position $pos $axis $vols"
786    }
787    _send "volume data state [_state volume] $vols"
788
789    # if there are any commands in the buffer, send them now that we're done
790    SendBytes $_outbuf
791    set _outbuf ""
792
793    $_dispatcher event -idle !legend
794}
795
796# ----------------------------------------------------------------------
797# USAGE: _send_transfuncs
798# ----------------------------------------------------------------------
799itcl::body Rappture::NanovisViewer::_send_transfuncs {} {
800    blt::busy hold $itk_component(hull); update idletasks
801    foreach dataobj $_sendobjs2 {
802        foreach comp [$dataobj components] {
803            #
804            # Determine the transfer function needed for this volume
805            # and make sure that it's defined on the server.
806            #
807            if { ![info exists _isomarkers($dataobj)] } {
808                InitIsoMarkers $dataobj $comp
809            } else {
810                HideIsoMarkers $dataobj
811            }
812            foreach {sname cmap wmap} [GenTransfuncData $dataobj $comp] break
813            set cmdstr [list transfunc define $sname $cmap $wmap]
814            if {![SendBytes $cmdstr] } {
815                return
816            }
817            set _obj2style($dataobj-$comp) $sname
818        }
819    }
820    set _sendobjs2 ""
821    blt::busy release $itk_component(hull)
822
823    # activate the proper volume
824    set first [lindex [get] 0]
825    foreach key [array names _obj2id *-*] {
826        set state [string match $first-* $key]
827        _send "volume state $state $_obj2id($key)"
828        if {[info exists _obj2style($key)]} {
829            _send "volume shading transfunc $_obj2style($key) $_obj2id($key)"
830        }
831    }
832    ShowIsoMarkers $first
833    # if there are any commands in the buffer, send them now that we're done
834    SendBytes $_outbuf
835    set _outbuf ""
836    $_dispatcher event -idle !legend
837}
838
839# ----------------------------------------------------------------------
840# USAGE: _receive_image -bytes <size>
841#
842# Invoked automatically whenever the "image" command comes in from
843# the rendering server.  Indicates that binary image data with the
844# specified <size> will follow.
845# ----------------------------------------------------------------------
846itcl::body Rappture::NanovisViewer::_receive_image {option size} {
847    if { [isconnected] } {
848        set bytes [ReceiveBytes $size]
849        $_image(plot) configure -data $bytes
850        ReceiveEcho <<line "<read $size bytes for [image width $_image(plot)]x[image height $_image(plot)] image>"
851    }
852}
853
854# ----------------------------------------------------------------------
855# USAGE: _receive_legend <volume> <vmin> <vmax> <size>
856#
857# Invoked automatically whenever the "legend" command comes in from
858# the rendering server.  Indicates that binary image data with the
859# specified <size> will follow.
860# ----------------------------------------------------------------------
861itcl::body Rappture::NanovisViewer::_receive_legend { ivol vmin vmax size } {
862    if { [isconnected] } {
863        set bytes [ReceiveBytes $size]
864        $_image(legend) configure -data $bytes
865        ReceiveEcho <<line "<read $size bytes for [image width $_image(legend)]x[image height $_image(legend)] legend>"
866
867        set c $itk_component(legend)
868        set w [winfo width $c]
869        set h [winfo height $c]
870        if {"" == [$c find withtag transfunc]} {
871            $c create image 10 10 -anchor nw \
872                 -image $_image(legend) -tags transfunc
873            $c create text 10 [expr {$h-8}] -anchor sw \
874                 -fill $itk_option(-plotforeground) -tags vmin
875            $c create text [expr {$w-10}] [expr {$h-8}] -anchor se \
876                 -fill $itk_option(-plotforeground) -tags vmax
877            $c lower transfunc
878            $c bind transfunc <ButtonRelease-1> \
879                [itcl::code $this AddIsoMarker %x %y]
880        }
881        $c itemconfigure vmin -text $vmin
882        $c coords vmin 10 [expr {$h-8}]
883
884        $c itemconfigure vmax -text $vmax
885        $c coords vmax [expr {$w-10}] [expr {$h-8}]
886        set first [lindex [get] 0]
887        ShowIsoMarkers $first
888    }
889}
890
891# ----------------------------------------------------------------------
892# USAGE: _receive_data <volume> <vmin> <vmax>
893#
894# Invoked automatically whenever the "legend" command comes in from
895# the rendering server.  Indicates that binary image data with the
896# specified <size> will follow.
897# ----------------------------------------------------------------------
898itcl::body Rappture::NanovisViewer::_receive_data { args } {
899    if { [isconnected] } {
900        array set info $args
901        set volume $info(id)
902        foreach { dataobj comp } $_id2obj($volume) break
903       
904        if { ![info exists _limits($dataobj-vmin)] } {
905            set _limits($dataobj-vmin) $info(min)
906            set _limits($dataobj-vmax) $info(max)
907        } else {
908            if { $_limits($dataobj-vmin) > $info(min) } {
909                set _limits($dataobj-vmin) $info(min)
910            }
911            if { $_limits($dataobj-vmax) > $info(max) } {
912                set _limits($dataobj-vmax) $info(max)
913            }
914        }
915        set _limits(vmin) $info(vmin)
916        set _limits(vmax) $info(vmax)
917        lappend _sendobjs2 $dataobj
918        unset _receiveids($info(id))
919        if { [array size _receiveids] == 0 } {
920            $_dispatcher event -idle !send_transfuncs
921        }
922    }
923}
924
925# ----------------------------------------------------------------------
926# USAGE: _rebuild
927#
928# Called automatically whenever something changes that affects the
929# data in the widget.  Clears any existing data and rebuilds the
930# widget to display new data.
931# ----------------------------------------------------------------------
932itcl::body Rappture::NanovisViewer::_rebuild {} {
933    # in the midst of sending data? then bail out
934    if {[llength $_sendobjs] > 0} {
935        return
936    }
937
938    #
939    # Find any new data that needs to be sent to the server.
940    # Queue this up on the _sendobjs list, and send it out
941    # a little at a time.  Do this first, before we rebuild
942    # the rest.
943    #
944    foreach dataobj [get] {
945        set comp [lindex [$dataobj components] 0]
946        if {![info exists _obj2id($dataobj-$comp)]} {
947            set i [lsearch -exact $_sendobjs $dataobj]
948            if {$i < 0} {
949                lappend _sendobjs $dataobj
950            }
951        }
952    }
953    if {[llength $_sendobjs] > 0} {
954        # send off new data objects
955        $_dispatcher event -idle !send_dataobjs
956    } else {
957        set w [winfo width $itk_component(3dview)]
958        set h [winfo height $itk_component(3dview)]
959        _send "screen $w $h"
960        # nothing to send -- activate the proper volume
961        set first [lindex [get] 0]
962        if {"" != $first} {
963            set axis [$first hints updir]
964            if {"" != $axis} {
965                _send "up $axis"
966            }
967        }
968        ShowIsoMarkers $first
969        UpdateTransferFunction
970
971        # sync the state of slicers
972        set vols [_currentVolumeIds -cutplanes]
973        foreach axis {x y z} {
974            _send "cutplane state [_state ${axis}slice] $axis $vols"
975            set pos [expr {0.01*[$itk_component(${axis}slicer) get]}]
976            _send "cutplane position $pos $axis $vols"
977        }
978        _send "volume data state [_state volume] $vols"
979        $_dispatcher event -idle !legend
980    }
981
982    #
983    # Reset the camera and other view parameters
984    #
985    _send "camera angle [Euler2XYZ $_view(theta) $_view(phi) $_view(psi)]"
986    _send "camera zoom $_view(zoom)"
987
988    _fixSettings light
989    _fixSettings transp
990    _fixSettings isosurface
991    _fixSettings opacity
992    _fixSettings thickness
993    _fixSettings grid
994    _fixSettings axes
995    _fixSettings outline
996
997    if {"" == $itk_option(-plotoutline)} {
998        _send "volume outline state off"
999    } else {
1000        _send "volume outline state on"
1001        set rgb [Color2RGB $itk_option(-plotoutline)]
1002        _send "volume outline color $rgb"
1003    }
1004    _send "volume axis label x \"\""
1005    _send "volume axis label y \"\""
1006    _send "volume axis label z \"\""
1007}
1008
1009# ----------------------------------------------------------------------
1010# USAGE: _currentVolumeIds ?-cutplanes?
1011#
1012# Returns a list of volume server IDs for the current volume being
1013# displayed.  This is normally a single ID, but it might be a list
1014# of IDs if the current data object has multiple components.
1015# ----------------------------------------------------------------------
1016itcl::body Rappture::NanovisViewer::_currentVolumeIds {{what -all}} {
1017    set rlist ""
1018
1019    set first [lindex [get] 0]
1020    foreach key [array names _obj2id *-*] {
1021        if {[string match $first-* $key]} {
1022            array set style {
1023                -cutplanes 1
1024            }
1025            foreach {dataobj comp} [split $key -] break
1026            array set style [lindex [$dataobj components -style $comp] 0]
1027
1028            if {$what != "-cutplanes" || $style(-cutplanes)} {
1029                lappend rlist $_obj2id($key)
1030            }
1031        }
1032    }
1033    return $rlist
1034}
1035
1036# ----------------------------------------------------------------------
1037# USAGE: _zoom in
1038# USAGE: _zoom out
1039# USAGE: _zoom reset
1040#
1041# Called automatically when the user clicks on one of the zoom
1042# controls for this widget.  Changes the zoom for the current view.
1043# ----------------------------------------------------------------------
1044itcl::body Rappture::NanovisViewer::_zoom {option} {
1045    switch -- $option {
1046        "in" {
1047            set _view(zoom) [expr {$_view(zoom)* 1.25}]
1048        }
1049        "out" {
1050            set _view(zoom) [expr {$_view(zoom)* 0.8}]
1051        }
1052        "reset" {
1053            array set _view {
1054                theta 45
1055                phi 45
1056                psi 0
1057                zoom 1.0
1058            }
1059            set xyz [Euler2XYZ $_view(theta) $_view(phi) $_view(psi)]
1060            _send "camera angle $xyz"
1061        }
1062    }
1063    _send "camera zoom $_view(zoom)"
1064}
1065
1066# ----------------------------------------------------------------------
1067# USAGE: _move click <x> <y>
1068# USAGE: _move drag <x> <y>
1069# USAGE: _move release <x> <y>
1070#
1071# Called automatically when the user clicks/drags/releases in the
1072# plot area.  Moves the plot according to the user's actions.
1073# ----------------------------------------------------------------------
1074itcl::body Rappture::NanovisViewer::_move {option x y} {
1075    switch -- $option {
1076        click {
1077            $itk_component(3dview) configure -cursor fleur
1078            set _click(x) $x
1079            set _click(y) $y
1080            set _click(theta) $_view(theta)
1081            set _click(phi) $_view(phi)
1082        }
1083        drag {
1084            if {[array size _click] == 0} {
1085                _move click $x $y
1086            } else {
1087                set w [winfo width $itk_component(3dview)]
1088                set h [winfo height $itk_component(3dview)]
1089                if {$w <= 0 || $h <= 0} {
1090                    return
1091                }
1092
1093                if {[catch {
1094                    # this fails sometimes for no apparent reason
1095                    set dx [expr {double($x-$_click(x))/$w}]
1096                    set dy [expr {double($y-$_click(y))/$h}]
1097                }]} {
1098                    return
1099                }
1100
1101                #
1102                # Rotate the camera in 3D
1103                #
1104                if {$_view(psi) > 90 || $_view(psi) < -90} {
1105                    # when psi is flipped around, theta moves backwards
1106                    set dy [expr {-$dy}]
1107                }
1108                set theta [expr {$_view(theta) - $dy*180}]
1109                while {$theta < 0} { set theta [expr {$theta+180}] }
1110                while {$theta > 180} { set theta [expr {$theta-180}] }
1111
1112                if {abs($theta) >= 30 && abs($theta) <= 160} {
1113                    set phi [expr {$_view(phi) - $dx*360}]
1114                    while {$phi < 0} { set phi [expr {$phi+360}] }
1115                    while {$phi > 360} { set phi [expr {$phi-360}] }
1116                    set psi $_view(psi)
1117                } else {
1118                    set phi $_view(phi)
1119                    set psi [expr {$_view(psi) - $dx*360}]
1120                    while {$psi < -180} { set psi [expr {$psi+360}] }
1121                    while {$psi > 180} { set psi [expr {$psi-360}] }
1122                }
1123
1124                array set _view [subst {
1125                    theta $theta
1126                    phi $phi
1127                    psi $psi
1128                }]
1129                set xyz [Euler2XYZ $theta $phi $psi]
1130                _send "camera angle $xyz"
1131                set _click(x) $x
1132                set _click(y) $y
1133            }
1134        }
1135        release {
1136            _move drag $x $y
1137            $itk_component(3dview) configure -cursor ""
1138            catch {unset _click}
1139        }
1140        default {
1141            error "bad option \"$option\": should be click, drag, release"
1142        }
1143    }
1144}
1145
1146# ----------------------------------------------------------------------
1147# USAGE: _slice axis x|y|z ?on|off|toggle?
1148# USAGE: _slice move x|y|z <newval>
1149# USAGE: _slice volume ?on|off|toggle?
1150#
1151# Called automatically when the user drags the slider to move the
1152# cut plane that slices 3D data.  Gets the current value from the
1153# slider and moves the cut plane to the appropriate point in the
1154# data set.
1155# ----------------------------------------------------------------------
1156itcl::body Rappture::NanovisViewer::_slice {option args} {
1157    switch -- $option {
1158        axis {
1159            if {[llength $args] < 1 || [llength $args] > 2} {
1160                error "wrong # args: should be \"_slice axis x|y|z ?on|off|toggle?\""
1161            }
1162            set axis [lindex $args 0]
1163            set op [lindex $args 1]
1164            if {$op == ""} { set op "on" }
1165
1166            set current [_state ${axis}slice]
1167            if {$op == "toggle"} {
1168                if {$current == "on"} { set op "off" } else { set op "on" }
1169            }
1170            if {$op} {
1171                $itk_component(${axis}slicer) configure -state normal
1172                _send "cutplane state 1 $axis [_currentVolumeIds -cutplanes]"
1173                $itk_component(${axis}slice) configure -relief sunken
1174            } else {
1175                $itk_component(${axis}slicer) configure -state disabled
1176                _send "cutplane state 0 $axis [_currentVolumeIds -cutplanes]"
1177                $itk_component(${axis}slice) configure -relief raised
1178            }
1179        }
1180        move {
1181            if {[llength $args] != 2} {
1182                error "wrong # args: should be \"_slice move x|y|z newval\""
1183            }
1184            set axis [lindex $args 0]
1185            set newval [lindex $args 1]
1186
1187            set newpos [expr {0.01*$newval}]
1188#            set newval [expr {0.01*($newval-50)
1189#                *($_limits(${axis}max)-$_limits(${axis}min))
1190#                  + 0.5*($_limits(${axis}max)+$_limits(${axis}min))}]
1191
1192            # show the current value in the readout
1193#puts "readout: $axis = $newval"
1194
1195            set ids [_currentVolumeIds -cutplanes]
1196            _send "cutplane position $newpos $axis $ids"
1197        }
1198        volume {
1199            if {[llength $args] > 1} {
1200                error "wrong # args: should be \"_slice volume ?on|off|toggle?\""
1201            }
1202            set op [lindex $args 0]
1203            if {$op == ""} { set op "on" }
1204
1205            set current [_state volume]
1206            if {$op == "toggle"} {
1207                if {$current == "on"} { set op "off" } else { set op "on" }
1208            }
1209
1210            if {$op} {
1211                _send "volume data state on [_currentVolumeIds]"
1212                $itk_component(volume) configure -relief sunken
1213            } else {
1214                _send "volume data state off [_currentVolumeIds]"
1215                $itk_component(volume) configure -relief raised
1216            }
1217        }
1218        default {
1219            error "bad option \"$option\": should be axis, move, or volume"
1220        }
1221    }
1222}
1223
1224# ----------------------------------------------------------------------
1225# USAGE: _slicertip <axis>
1226#
1227# Used internally to generate a tooltip for the x/y/z slicer controls.
1228# Returns a message that includes the current slicer value.
1229# ----------------------------------------------------------------------
1230itcl::body Rappture::NanovisViewer::_slicertip {axis} {
1231    set val [$itk_component(${axis}slicer) get]
1232#    set val [expr {0.01*($val-50)
1233#        *($_limits(${axis}max)-$_limits(${axis}min))
1234#          + 0.5*($_limits(${axis}max)+$_limits(${axis}min))}]
1235    return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
1236}
1237
1238# ----------------------------------------------------------------------
1239# USAGE: _state <component>
1240#
1241# Used internally to determine the state of a toggle button.
1242# The <component> is the itk component name of the button.
1243# Returns on/off for the state of the button.
1244# ----------------------------------------------------------------------
1245itcl::body Rappture::NanovisViewer::_state {comp} {
1246    if {[$itk_component($comp) cget -relief] == "sunken"} {
1247        return "on"
1248    }
1249    return "off"
1250}
1251
1252# ----------------------------------------------------------------------
1253# USAGE: _fixSettings <what> ?<value>?
1254#
1255# Used internally to update rendering settings whenever parameters
1256# change in the popup settings panel.  Sends the new settings off
1257# to the back end.
1258# ----------------------------------------------------------------------
1259itcl::body Rappture::NanovisViewer::_fixSettings {what {value ""}} {
1260    set inner [$itk_component(controls).panel component inner]
1261    switch -- $what {
1262        light {
1263            if {[isconnected]} {
1264                set val [$inner.scales.light get]
1265                set sval [expr {0.1*$val}]
1266                _send "volume shading diffuse $sval"
1267
1268                set sval [expr {sqrt($val+1.0)}]
1269                _send "volume shading specular $sval"
1270            }
1271        }
1272
1273        transp {
1274            if {[isconnected]} {
1275                set val [$inner.scales.transp get]
1276                set sval [expr {0.2*$val+1}]
1277                _send "volume shading opacity $sval"
1278            }
1279        }
1280        opacity {
1281            if {[isconnected]} {
1282                set dataobj [lindex [get] 0]
1283                if {$dataobj != 0} {
1284                    set val [$inner.scales.opacity get]
1285                    set sval [expr {0.01*double($val)}]
1286                    set _opacity($dataobj) $sval
1287                    UpdateTransferFunction
1288                }
1289            }
1290        }
1291        thickness {
1292            if {[isconnected]} {
1293                set dataobj [lindex [get] 0]
1294                if {$dataobj != 0} {
1295                    set val [$inner.scales.thickness get]
1296                    # Scale values between 0.00001 and 0.01000
1297                    set sval [expr {0.0001*double($val)}]
1298                    set _thickness($dataobj) $sval
1299                    UpdateTransferFunction
1300                }
1301            }
1302        }
1303        "outline" {
1304            if {[isconnected]} {
1305                _send "volume outline state $_settings($this-outline)"
1306            }
1307        }           
1308        "isosurface" {
1309            if {[isconnected]} {
1310                _send "volume shading isosurface $_settings($this-isosurface)"
1311            }
1312        }           
1313        "grid" {
1314            if { [isconnected] } {
1315                _send "grid visible $_settings($this-grid)"
1316            }
1317        }
1318        "axes" {
1319            if { [isconnected] } {
1320                _send "axis visible $_settings($this-axes)"
1321            }
1322        }
1323        default {
1324            error "don't know how to fix $what"
1325        }
1326    }
1327}
1328
1329# ----------------------------------------------------------------------
1330# USAGE: _fixLegend
1331#
1332# Used internally to update the legend area whenever it changes size
1333# or when the field changes.  Asks the server to send a new legend
1334# for the current field.
1335# ----------------------------------------------------------------------
1336itcl::body Rappture::NanovisViewer::_fixLegend {} {
1337    set lineht [font metrics $itk_option(-font) -linespace]
1338    set w [expr {[winfo width $itk_component(legend)]-20}]
1339    set h [expr {[winfo height $itk_component(legend)]-20-$lineht}]
1340    set ivol ""
1341
1342    set dataobj [lindex [get] 0]
1343    if {"" != $dataobj} {
1344        set comp [lindex [$dataobj components] 0]
1345        if {[info exists _obj2id($dataobj-$comp)]} {
1346            set ivol $_obj2id($dataobj-$comp)
1347        }
1348    }
1349    if {$w > 0 && $h > 0 && "" != $ivol} {
1350        _send "legend $ivol $w $h"
1351    } else {
1352        #$itk_component(legend) delete all
1353    }
1354}
1355
1356# ----------------------------------------------------------------------
1357# USAGE: GenTransfuncData <dataobj> <comp>
1358#
1359# Used internally to compute the colormap and alpha map used to define
1360# a transfer function for the specified component in a data object.
1361# Returns: name {v r g b ...} {v w ...}
1362# ----------------------------------------------------------------------
1363itcl::body Rappture::NanovisViewer::GenTransfuncData {dataobj comp} {
1364    array set style {
1365        -color rainbow
1366        -levels 6
1367        -opacity 1.0
1368    }
1369    array set style [lindex [$dataobj components -style $comp] 0]
1370    set sname "$style(-color):$style(-levels):$style(-opacity)"
1371
1372    if {$style(-color) == "rainbow"} {
1373        set style(-color) "white:yellow:green:cyan:blue:magenta"
1374    }
1375    set clist [split $style(-color) :]
1376    set cmap "0.0 [Color2RGB white] "
1377    for {set i 0} {$i < [llength $clist]} {incr i} {
1378        set x [expr {double($i+1)/([llength $clist]+1)}]
1379        set color [lindex $clist $i]
1380        append cmap "$x [Color2RGB $color] "
1381    }
1382    append cmap "1.0 [Color2RGB $color]"
1383
1384    set max $style(-opacity)
1385    if { [info exists _opacity($dataobj)] } {
1386        set max $_opacity($dataobj)
1387    }
1388    set key $dataobj
1389    set isovalues {}
1390    foreach m $_isomarkers($key) {
1391        lappend isovalues [$m GetRelativeValue]
1392    }
1393
1394    # Sort the isovalues
1395    set isovalues [lsort -real $isovalues]
1396
1397    set delta 0.01
1398    if { [info exists _thickness($dataobj)]} {
1399        set delta $_thickness($dataobj)
1400    }
1401    set first [lindex $isovalues 0]
1402    set last [lindex $isovalues end]
1403    set wmap ""
1404    if { $first == "" || $first != 0.0 } {
1405        lappend wmap 0.0 0.0
1406    }
1407    foreach x $isovalues {
1408        set x1 [expr {$x-$delta-0.00001}]
1409        set x2 [expr {$x-$delta}]
1410        set x3 [expr {$x+$delta}]
1411        set x4 [expr {$x+$delta+0.00001}]
1412        if { $x1 < 0.0 } {
1413            set x1 0.0
1414        }
1415        if { $x2 < 0.0 } {
1416            set x2 0.0
1417        }
1418        if { $x3 > 1.0 } {
1419            set x3 1.0
1420        }
1421        if { $x4 > 1.0 } {
1422            set x4 1.0
1423        }
1424        # add spikes in the middle
1425        lappend wmap $x1 0.0 
1426        lappend wmap $x2 $max
1427        lappend wmap $x3 $max 
1428        lappend wmap $x4 0.0
1429    }
1430    if { $last == "" || $last != 1.0 } {
1431        lappend wmap 1.0 0.0
1432    }
1433    return [list $sname $cmap $wmap]
1434}
1435
1436# ----------------------------------------------------------------------
1437# CONFIGURATION OPTION: -plotbackground
1438# ----------------------------------------------------------------------
1439itcl::configbody Rappture::NanovisViewer::plotbackground {
1440    if { [isconnected] } {
1441        foreach {r g b} [Color2RGB $itk_option(-plotbackground)] break
1442        #fix this!
1443        #_send "color background $r $g $b"
1444    }
1445}
1446
1447# ----------------------------------------------------------------------
1448# CONFIGURATION OPTION: -plotforeground
1449# ----------------------------------------------------------------------
1450itcl::configbody Rappture::NanovisViewer::plotforeground {
1451    if { [isconnected] } {
1452        foreach {r g b} [Color2RGB $itk_option(-plotforeground)] break
1453        #fix this!
1454        #_send "color background $r $g $b"
1455    }
1456}
1457
1458# ----------------------------------------------------------------------
1459# CONFIGURATION OPTION: -plotoutline
1460# ----------------------------------------------------------------------
1461itcl::configbody Rappture::NanovisViewer::plotoutline {
1462    # Must check if we are connected because this routine is called from the
1463    # class body when the -plotoutline itk_option is defined.  At that point
1464    # the NanovisViewer class constructor hasn't been called, so we can't
1465    # start sending commands to visualization server.
1466    if { [isconnected] } {
1467        if {"" == $itk_option(-plotoutline)} {
1468            _send "volume outline state off"
1469        } else {
1470            _send "volume outline state on"
1471            _send "volume outline color [Color2RGB $itk_option(-plotoutline)]"
1472        }
1473    }
1474}
1475
1476itcl::body Rappture::NanovisViewer::HideIsoMarkers {dataobj} {
1477    if { [info exists _isomarkers($dataobj)] } {
1478        foreach m $_isomarkers($dataobj) {
1479            $m Hide
1480        }
1481    }
1482}
1483
1484itcl::body Rappture::NanovisViewer::ShowIsoMarkers {dataobj} {
1485    foreach obj [array names _all_data_objs] {
1486        HideIsoMarkers $obj
1487    }
1488    if { ![info exists _isomarkers($dataobj)] } {
1489        return
1490    }
1491    foreach m $_isomarkers($dataobj) {
1492        $m Show
1493    }
1494}
1495
1496itcl::body Rappture::NanovisViewer::InitIsoMarkers {dataobj comp} {
1497    array set style {
1498        -levels 6x
1499    }
1500    array set style [lindex [$dataobj components -style $comp] 0]
1501    set levels $style(-levels)
1502    set c $itk_component(legend)
1503    regsub -all "," $levels " " levels
1504    foreach level $levels {
1505        set n [scan $level "%g%s" value suffix]
1506        if { $n == 2 && $suffix == "%" } {
1507            # ${n}% : Set relative value.
1508            set value [expr {$value * 0.01}]
1509            set m [IsoMarker \#auto $c $this]
1510            $m SetRelativeValue $value
1511            lappend _isomarkers($dataobj) $m
1512        } elseif { $n == 2 && $suffix == "x" } {
1513            # ${n}x : Set equal number of levels
1514            if { $value != round($value) } {
1515                error "\# of levels \"$value\" must be an interger"
1516            }
1517            set nLevels [expr round($value)]
1518            for {set i 1} {$i <= $nLevels} {incr i} {
1519                set x [expr {double($i)/($nLevels+1)}]
1520                set m [IsoMarker \#auto $c $this]
1521                $m SetRelativeValue $x
1522                lappend _isomarkers($dataobj) $m
1523            }
1524        } else {
1525            # ${n} : Set absolute value.
1526            set m [IsoMarker \#auto $c $this]
1527            $m SetAbsoluteValue $value
1528            lappend _isomarkers($dataobj) $m
1529        }
1530    }
1531}
1532
1533# ----------------------------------------------------------------------
1534# USAGE: _marker start <x> <y>
1535# USAGE: _marker update <x> <y>
1536# USAGE: _marker end <x> <y>
1537#
1538# Used internally to handle the various marker operations performed
1539# when the user clicks and drags on the legend area.  The marker changes the
1540# transfer function to highlight the area being selected in the
1541# legend.
1542# ----------------------------------------------------------------------
1543itcl::body Rappture::NanovisViewer::UpdateTransferFunction {} {
1544    set dataobj [lindex [get] 0]
1545    if {"" == $dataobj} {
1546        return
1547    }
1548    set comp [lindex [$dataobj components] 0]
1549    set key $dataobj-$comp
1550    if {![info exists _obj2style($key)]} {
1551        return
1552    }
1553    # Compute a transfer function for the current data set.
1554    foreach {sname cmap wmap} [GenTransfuncData $dataobj $comp] break
1555    _send [list transfunc define $_obj2style($key) $cmap $wmap]
1556    _send [list volume shading transfunc $_obj2style($key) $_obj2id($key)]
1557    _fixLegend
1558}
1559
1560itcl::body Rappture::NanovisViewer::AddIsoMarker { x y } {
1561    set dataobj [lindex [get] 0]
1562    if {$dataobj == ""} {
1563        return 0;                       # No data sets defined
1564    }
1565    set c $itk_component(legend)
1566    set m [IsoMarker \#auto $c $this]
1567    set w [winfo width $c]
1568    $m SetRelativeValue [expr {double($x-10)/($w-20)}]
1569    $m Show
1570    lappend _isomarkers($dataobj) $m
1571    UpdateTransferFunction
1572    return 1
1573}
1574
1575itcl::body Rappture::NanovisViewer::RemoveDuplicateIsoMarker { marker x } {
1576    set dataobj [lindex [get] 0]
1577    if {"" == $dataobj} {
1578        return 0
1579    }
1580    set bool 0
1581    if { [info exists _isomarkers($dataobj)] } {
1582        set list {}
1583        set marker [namespace tail $marker]
1584        foreach m $_isomarkers($dataobj) {
1585            set sx [$m GetScreenPosition]
1586            if { $m != $marker } {
1587                if { $x >= ($sx-3) && $x <= ($sx+3) } {
1588                    $marker SetRelativeValue [$m GetRelativeValue]
1589                    itcl::delete object $m
1590                    bell
1591                    set bool 1
1592                    continue
1593                }
1594            }
1595            lappend list $m
1596        }
1597        set _isomarkers($dataobj) $list
1598        UpdateTransferFunction
1599    }
1600    return $bool
1601}
1602
1603itcl::body Rappture::NanovisViewer::OverIsoMarker { marker x } {
1604    set dataobj [lindex [get] 0]
1605    if {"" == $dataobj} {
1606        return ""
1607    }
1608    if { [info exists _isomarkers($dataobj)] } {
1609        set marker [namespace tail $marker]
1610        foreach m $_isomarkers($dataobj) {
1611            set sx [$m GetScreenPosition]
1612            if { $m != $marker } {
1613                set bool [expr { $x >= ($sx-3) && $x <= ($sx+3) }]
1614                $m Activate $bool
1615            }
1616        }
1617    }
1618    return ""
1619}
Note: See TracBrowser for help on using the repository browser.