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

Last change on this file since 965 was 965, checked in by gah, 16 years ago

add -markers style option to nanovisviewer. The reverts the -levels option back to its old format

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