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

Last change on this file since 890 was 890, checked in by gah, 17 years ago
File size: 69.6 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: nanovisviewer - 3D volume rendering
3#
4#  This widget performs volume rendering on 3D scalar/vector datasets.
5#  It connects to the Nanovis server running on a rendering farm,
6#  transmits data, and displays the results.
7# ======================================================================
8#  AUTHOR:  Michael McLennan, Purdue University
9#  Copyright (c) 2004-2005  Purdue Research Foundation
10#
11#  See the file "license.terms" for information on usage and
12#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13# ======================================================================
14package require Itk
15package require BLT
16package require Img
17
18option add *NanovisViewer.width 4i widgetDefault
19option add *NanovisViewer*cursor crosshair widgetDefault
20option add *NanovisViewer.height 4i widgetDefault
21option add *NanovisViewer.foreground black widgetDefault
22option add *NanovisViewer.controlBackground gray widgetDefault
23option add *NanovisViewer.controlDarkBackground #999999 widgetDefault
24option add *NanovisViewer.plotBackground black widgetDefault
25option add *NanovisViewer.plotForeground white widgetDefault
26option add *NanovisViewer.plotOutline gray widgetDefault
27option add *NanovisViewer.font \
28    -*-helvetica-medium-r-normal-*-12-* widgetDefault
29
30itcl::class Rappture::NanovisViewer {
31    inherit itk::Widget
32
33    itk_option define -plotforeground plotForeground Foreground ""
34    itk_option define -plotbackground plotBackground Background ""
35    itk_option define -plotoutline plotOutline PlotOutline ""
36    itk_option define -sendcommand sendCommand SendCommand ""
37    itk_option define -receivecommand receiveCommand ReceiveCommand ""
38
39    constructor {hostlist args} { # defined below }
40    destructor { # defined below }
41
42    public method add {dataobj {settings ""}}
43    public method get {args}
44    public method delete {args}
45    public method scale {args}
46    public method get_limits { { option ""} } {
47        return [array get _limits]
48    }
49    public method download {option args}
50    public method parameters {title args} { # do nothing }
51    public method connect {{hostlist ""}}
52    public method disconnect {}
53    public method isconnected {}
54
55    protected method _send {args}
56    protected method _send_text {string}
57    protected method _send_dataobjs {}
58    protected method _send_transfuncs {}
59    protected method _send_echo {channel {data ""}}
60    protected method _receive {}
61    protected method _receive_image {option size}
62    protected method _receive_legend {ivol vmin vmax size}
63    protected method _receive_data {args}
64    protected method _receive_echo {channel {data ""}}
65
66    protected method _rebuild {}
67    protected method _currentVolumeIds {{what -all}}
68    protected method _zoom {option}
69    protected method _move {option x y}
70    protected method _slice {option args}
71    protected method _slicertip {axis}
72    protected method _probe {option args}
73    protected method _marker {index option args}
74
75    protected method _state {comp}
76    protected method _fixSettings {what {value ""}}
77    protected method _fixLegend {}
78    protected method _serverDown {}
79    protected method _genTransfuncData {dataobj comp}
80    public method update_transfer_function {}
81    public method remove_duplicate_isomarker { m x }
82    public method over_isomarker { m x }
83    public method _addIsoMarker { x y }
84    protected method _initIsoMarkers {dataobj comp}
85    protected method _hideIsoMarkers {dataobj}
86    protected method _showIsoMarkers {dataobj}
87    protected method _color2rgb {color}
88    protected method _euler2xyz {theta phi psi}
89
90    private variable _dispatcher "" ;# dispatcher for !events
91
92    private variable _nvhosts ""   ;# list of hosts for nanovis server
93    private variable _sid ""       ;# socket connection to nanovis server
94    private variable _parser ""    ;# interpreter for incoming commands
95    private variable _buffer       ;# buffer for incoming/outgoing commands
96    private variable _image        ;# image displayed in plotting area
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 _isosurface     ;# indicates to use isosurface shading
117}
118
119itk::usual NanovisViewer {
120    keep -background -foreground -cursor -font
121    keep -plotbackground -plotforeground
122}
123
124itcl::class Rappture::NanovisViewer::IsoMarker {
125    private variable _value     "";     # Absolute value of marker.
126    private variable _label     ""
127    private variable _tick      ""
128    private variable _canvas    ""
129    private variable _nvobj     ""
130    private common _normalIcon ""
131    private common _activeIcon ""
132    private variable _active_motion   0
133    private variable _active_press    0
134
135    constructor {c obj args} {
136        set _canvas $c
137        set _nvobj $obj
138       
139        if { $_normalIcon == "" } {
140            set normal_icon_data {
141R0lGODlhBwATAOcxAAAAAAEBAQICAgMDAwQEBAUFBQYGBgcHBwgICAkJCQoKCgsLCwwMDA0N
142DQ4ODg8PDxAQEBERERISEhMTExQUFBUVFRYWFhcXFxgYGBkZGRoaGhsbGxwcHB0dHR4eHh8f
143HyAgICEhISIiIiMjIyQkJCUlJSYmJicnJygoKCkpKSoqKisrKywsLC0tLS4uLi8vLzAwMDEx
144MTIyMjMzMzQ0NDU1NTY2Njc3Nzg4ODk5OTo6Ojs7Ozw8PD09PT4+Pj8/P0BAQEFBQUJCQkND
145Q0REREVFRUZGRkdHR0hISElJSUpKSktLS0xMTE1NTU5OTk9PT1BQUFFRUVJSUlNTU1RUVFVV
146VVZWVldXV1hYWFlZWVpaWltbW1xcXF1dXV5eXl9fX2BgYGFhYWJiYmNjY2RkZGVlZWZmZmdn
147Z2hoaGlpaWpqamtra2xsbG1tbW5ubm9vb3BwcHFxcXJycnNzc3R0dHV1dXZ2dnd3d3h4eHl5
148eXp6ent7e3x8fH19fX5+fn9/f4CAgIGBgYKCgoODg4SEhIWFhYaGhoeHh4iIiImJiYqKiouL
149i4yMjI2NjY6Ojo+Pj5CQkJGRkZKSkpOTk5SUlJWVlZaWlpeXl5iYmJmZmZqampubm5ycnJ2d
150nZ6enp+fn6CgoKGhoaKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq6ysrK2tra6urq+v
151r7CwsLGxsbKysrOzs7S0tLW1tba2tre3t7i4uLm5ubq6uru7u7y8vL29vb6+vr+/v8DAwMHB
152wcLCwsPDw8TExMXFxcbGxsfHx8jIyMnJycrKysvLy8zMzM3Nzc7Ozs/Pz9DQ0NHR0dLS0tPT
15309TU1NXV1dbW1tfX19jY2NnZ2dra2tvb29zc3N3d3d7e3t/f3+Dg4OHh4eLi4uPj4+Tk5OXl
1545ebm5ufn5+jo6Onp6erq6uvr6+zs7O3t7e7u7u/v7/Dw8PHx8fLy8vPz8/T09PX19fb29vf3
1559/j4+Pn5+fr6+vv7+/z8/P39/f7+/v///yH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAP8A
156LAAAAAAHABMAAAg2AP8JHEiwoMGDCBFmW0gw259sDR9GhDjQIUWBFidiXPhwYTZTpv6AXBjy
157j0iSJk9+BDnSo8uAADs=
158            }
159            set active_icon_data {
160R0lGODlhBwATAOcxAAAAAAEBAQICAgMDAwQEBAUFBQYGBgcHBwgICAkJCQoKCgsLCwwMDA0N
161DQ4ODg8PDxAQEBERERISEhMTExQUFBUVFRYWFhcXFxgYGBkZGRoaGhsbGxwcHB0dHR4eHh8f
162HyAgICEhISIiIiMjIyQkJCUlJSYmJicnJygoKCkpKSoqKisrKywsLC0tLS4uLi8vLzAwMDEx
163MTIyMjMzMzQ0NDU1NTY2Njc3Nzg4ODk5OTo6Ojs7Ozw8PD09PT4+Pj8/P0BAQEFBQUJCQkND
164Q0REREVFRUZGRkdHR0hISElJSUpKSktLS0xMTE1NTU5OTk9PT1BQUFFRUVJSUlNTU1RUVFVV
165VVZWVldXV1hYWFlZWVpaWltbW1xcXF1dXV5eXl9fX2BgYGFhYWJiYmNjY2RkZGVlZWZmZmdn
166Z2hoaGlpaWpqamtra2xsbG1tbW5ubm9vb3BwcHFxcXJycnNzc3R0dHV1dXZ2dnd3d3h4eHl5
167eXp6ent7e3x8fH19fX5+fn9/f4CAgIGBgYKCgoODg4SEhIWFhYaGhoeHh4iIiImJiYqKiouL
168i4yMjI2NjY6Ojo+Pj5CQkJGRkZKSkpOTk5SUlJWVlZaWlpeXl5iYmJmZmZqampubm5ycnJ2d
169nZ6enp+fn6CgoKGhoaKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq6ysrK2tra6urq+v
170r7CwsLGxsbKysrOzs7S0tLW1tba2tre3t7i4uLm5ubq6uru7u7y8vL29vb6+vr+/v8DAwMHB
171wcLCwsPDw8TExMXFxcbGxsfHx8jIyMnJycrKysvLy8zMzM3Nzc7Ozs/Pz9DQ0NHR0dLS0tPT
17209TU1NXV1dbW1tfX19jY2NnZ2dra2tvb29zc3N3d3d7e3t/f3+Dg4OHh4eLi4uPj4+Tk5OXl
1735ebm5ufn5+jo6Onp6erq6uvr6+zs7O3t7e7u7u/v7/Dw8PHx8fLy8vPz8/T09PX19fb29vf3
1749/j4+Pn5+fr6+vv7+/z8/P39/f7+/v///yH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAP8A
175LAAAAAAHABMAAAg2AP8JHEiwoMGDCBFmW0gwG4BsDR9GhDjQIUWBFidiXPhwYbY/fwCAXBgS
176gEiSJk9+BDnSo8uAADs=
177            }
178            set _normalIcon [image create photo -data $normal_icon_data]
179            set _activeIcon [image create photo -data $active_icon_data]
180        }
181        set w [winfo width $_canvas]
182        set h [winfo height $_canvas]
183        set _tick [$c create image 0 $h \
184                -image $_normalIcon -anchor s \
185                -tags "$this $obj" -state hidden]
186        set _label [$c create text 0 $h \
187                -anchor n -fill white -font "Helvetica 6" \
188                -tags "$this $obj" -state hidden]
189        $c bind $_tick <Enter> [itcl::code $this handle_event "enter"]
190        $c bind $_tick <Leave> [itcl::code $this handle_event "leave"]
191        $c bind $_tick <ButtonPress-1> \
192            [itcl::code $this handle_event "start" %x %y]
193        $c bind $_tick <B1-Motion> \
194            [itcl::code $this handle_event "update" %x %y]
195        $c bind $_tick <ButtonRelease-1> \
196            [itcl::code $this handle_event "end" %x %y]
197    }
198    destructor {
199        $_canvas delete $this
200    }
201
202    public method get_absolute_value {} {
203        return $_value
204    }
205    public method get_relative_value {} {
206        array set limits [$_nvobj get_limits]
207        if { $limits(vmax) == $limits(vmin) } {
208            set limits(vmin) 0.0
209            set limits(vmax) 1.0
210        }
211        return [expr {($_value-$limits(vmin))/($limits(vmax) - $limits(vmin))}]
212    }
213    public method activate { bool } {
214        if  { $bool || $_active_press || $_active_motion } {
215            $_canvas itemconfigure $_label -state normal
216            $_canvas itemconfigure $_tick -image $_activeIcon
217        } else {
218            $_canvas itemconfigure $_label -state hidden
219            $_canvas itemconfigure $_tick -image $_normalIcon
220        }
221    }
222    public method show {} {
223        set_absolute_value $_value
224        $_canvas itemconfigure $_tick -state normal
225        $_canvas raise $_tick
226    }
227    public method hide {} {
228        $_canvas itemconfigure $_tick -state hidden
229    }
230    public method get_screen_position { } {
231        set x [get_relative_value]
232        if { $x < 0.0 } {
233            set x 0.0
234        } elseif { $x > 1.0 } {
235            set x 1.0
236        }
237        set low 10
238        set w [winfo width $_canvas]
239        set high [expr {$w  - 10}]
240        set x [expr {round($x*($high - $low) + $low)}]
241        return $x
242    }
243    public method set_absolute_value { x } {
244        set _value $x
245        set y 31
246        $_canvas itemconfigure $_label -text [format %.4g $_value]
247        set x [get_screen_position]
248        $_canvas coords $_tick $x [expr {$y+3}]
249        $_canvas coords $_label $x [expr {$y+5}]
250    }
251    public method set_relative_value { x } {
252        array set limits [$_nvobj get_limits]
253        if { $limits(vmax) == $limits(vmin) } {
254            set limits(vmin) 0.0
255            set limits(vmax) 1.0
256        }
257        set r [expr $limits(vmax) - $limits(vmin)]
258        set_absolute_value [expr {($x * $r) + $limits(vmin)}]
259    }
260    public method handle_event { option args } {
261        switch -- $option {
262            enter {
263                set _active_motion 1
264                activate yes
265                $_canvas raise $_tick
266            }
267            leave {
268                set _active_motion 0
269                activate no
270            }
271            start {
272                $_canvas raise $_tick
273                set _active_press 1
274                activate yes
275            }
276            update {
277                set w [winfo width $_canvas]
278                set x [lindex $args 0]
279                set_relative_value [expr {double($x-10)/($w-20)}]
280                $_nvobj over_isomarker $this $x
281                $_nvobj update_transfer_function
282            }
283            end {
284                set x [lindex $args 0]
285                if { ![$_nvobj remove_duplicate_isomarker $this $x]} {
286                    eval handle_event update $args
287                }
288                set _active_press 0
289                activate no
290            }
291            default {
292                error "bad option \"$option\": should be start, update, end"
293            }
294        }
295    }
296}
297
298# ----------------------------------------------------------------------
299# CONSTRUCTOR
300# ----------------------------------------------------------------------
301itcl::body Rappture::NanovisViewer::constructor {hostlist args} {
302    Rappture::dispatcher _dispatcher
303    $_dispatcher register !legend
304    $_dispatcher dispatch $this !legend "[itcl::code $this _fixLegend]; list"
305    $_dispatcher register !serverDown
306    $_dispatcher dispatch $this !serverDown "[itcl::code $this _serverDown]; list"
307
308    set _buffer(in) ""
309    set _buffer(out) ""
310
311    #
312    # Create a parser to handle incoming requests
313    #
314    set _parser [interp create -safe]
315    foreach cmd [$_parser eval {info commands}] {
316        $_parser hide $cmd
317    }
318    $_parser alias image [itcl::code $this _receive_image]
319    $_parser alias legend [itcl::code $this _receive_legend]
320    $_parser alias data [itcl::code $this _receive_data]
321
322    #
323    # Set up the widgets in the main body
324    #
325    option add hull.width hull.height
326    pack propagate $itk_component(hull) no
327
328    set _view(theta) 45
329    set _view(phi) 45
330    set _view(psi) 0
331    set _view(zoom) 1
332    set _view(xfocus) 0
333    set _view(yfocus) 0
334    set _view(zfocus) 0
335    set _obj2id(count) 0
336    set _id2obj(count) 0
337    set _limits(vmin) 0.0
338    set _limits(vmax) 1.0
339
340    itk_component add controls {
341        frame $itk_interior.cntls
342    } {
343        usual
344        rename -background -controlbackground controlBackground Background
345    }
346    pack $itk_component(controls) -side right -fill y
347
348    itk_component add zoom {
349        frame $itk_component(controls).zoom
350    } {
351        usual
352        rename -background -controlbackground controlBackground Background
353    }
354    pack $itk_component(zoom) -side top
355
356    itk_component add reset {
357        button $itk_component(zoom).reset \
358            -borderwidth 1 -padx 1 -pady 1 \
359            -bitmap [Rappture::icon reset] \
360            -command [itcl::code $this _zoom reset]
361    } {
362        usual
363        ignore -borderwidth
364        rename -highlightbackground -controlbackground controlBackground Background
365    }
366    pack $itk_component(reset) -side left -padx {4 1} -pady 4
367    Rappture::Tooltip::for $itk_component(reset) "Reset the view to the default zoom level"
368
369    itk_component add zoomin {
370        button $itk_component(zoom).zin \
371            -borderwidth 1 -padx 1 -pady 1 \
372            -bitmap [Rappture::icon zoomin] \
373            -command [itcl::code $this _zoom in]
374    } {
375        usual
376        ignore -borderwidth
377        rename -highlightbackground -controlbackground controlBackground Background
378    }
379    pack $itk_component(zoomin) -side left -padx 1 -pady 4
380    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
381
382    itk_component add zoomout {
383        button $itk_component(zoom).zout \
384            -borderwidth 1 -padx 1 -pady 1 \
385            -bitmap [Rappture::icon zoomout] \
386            -command [itcl::code $this _zoom out]
387    } {
388        usual
389        ignore -borderwidth
390        rename -highlightbackground -controlbackground controlBackground Background
391    }
392    pack $itk_component(zoomout) -side left -padx {1 4} -pady 4
393    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
394
395    #
396    # Create slicer controls...
397    #
398    itk_component add slicers {
399        frame $itk_component(controls).slicers
400    } {
401        usual
402        rename -background -controlbackground controlBackground Background
403    }
404    pack $itk_component(slicers) -side bottom -padx 4 -pady 4
405    grid rowconfigure $itk_component(slicers) 1 -weight 1
406    #
407    # X-value slicer...
408    #
409    itk_component add xslice {
410        label $itk_component(slicers).xslice \
411            -borderwidth 1 -relief raised -padx 1 -pady 1 \
412            -bitmap [Rappture::icon x]
413    } {
414        usual
415        ignore -borderwidth
416        rename -highlightbackground -controlbackground controlBackground Background
417    }
418    bind $itk_component(xslice) <ButtonPress> \
419        [itcl::code $this _slice axis x toggle]
420    Rappture::Tooltip::for $itk_component(xslice) \
421        "Toggle the X cut plane on/off"
422    grid $itk_component(xslice) -row 1 -column 0 -sticky ew -padx 1
423
424    itk_component add xslicer {
425        ::scale $itk_component(slicers).xval -from 100 -to 0 \
426            -width 10 -orient vertical -showvalue off \
427            -borderwidth 1 -highlightthickness 0 \
428            -command [itcl::code $this _slice move x]
429    } {
430        usual
431        ignore -borderwidth
432        ignore -highlightthickness
433        rename -highlightbackground -controlbackground controlBackground Background
434        rename -troughcolor -controldarkbackground controlDarkBackground Background
435    }
436    $itk_component(xslicer) set 50
437    $itk_component(xslicer) configure -state disabled
438    grid $itk_component(xslicer) -row 2 -column 0 -padx 1
439    Rappture::Tooltip::for $itk_component(xslicer) \
440        "@[itcl::code $this _slicertip x]"
441
442    #
443    # Y-value slicer...
444    #
445    itk_component add yslice {
446        label $itk_component(slicers).yslice \
447            -borderwidth 1 -relief raised -padx 1 -pady 1 \
448            -bitmap [Rappture::icon y]
449    } {
450        usual
451        ignore -borderwidth
452        rename -highlightbackground -controlbackground controlBackground Background
453    }
454    bind $itk_component(yslice) <ButtonPress> \
455        [itcl::code $this _slice axis y toggle]
456    Rappture::Tooltip::for $itk_component(yslice) \
457        "Toggle the Y cut plane on/off"
458    grid $itk_component(yslice) -row 1 -column 1 -sticky ew -padx 1
459
460    itk_component add yslicer {
461        ::scale $itk_component(slicers).yval -from 100 -to 0 \
462            -width 10 -orient vertical -showvalue off \
463            -borderwidth 1 -highlightthickness 0 \
464            -command [itcl::code $this _slice move y]
465    } {
466        usual
467        ignore -borderwidth
468        ignore -highlightthickness
469        rename -highlightbackground -controlbackground controlBackground Background
470        rename -troughcolor -controldarkbackground controlDarkBackground Background
471    }
472    $itk_component(yslicer) set 50
473    $itk_component(yslicer) configure -state disabled
474    grid $itk_component(yslicer) -row 2 -column 1 -padx 1
475    Rappture::Tooltip::for $itk_component(yslicer) \
476        "@[itcl::code $this _slicertip y]"
477
478    #
479    # Z-value slicer...
480    #
481    itk_component add zslice {
482        label $itk_component(slicers).zslice \
483            -borderwidth 1 -relief raised -padx 1 -pady 1 \
484            -bitmap [Rappture::icon z]
485    } {
486        usual
487        ignore -borderwidth
488        rename -highlightbackground -controlbackground controlBackground Background
489    }
490    grid $itk_component(zslice) -row 1 -column 2 -sticky ew -padx 1
491    bind $itk_component(zslice) <ButtonPress> \
492        [itcl::code $this _slice axis z toggle]
493    Rappture::Tooltip::for $itk_component(zslice) \
494        "Toggle the Z cut plane on/off"
495
496    itk_component add zslicer {
497        ::scale $itk_component(slicers).zval -from 100 -to 0 \
498            -width 10 -orient vertical -showvalue off \
499            -borderwidth 1 -highlightthickness 0 \
500            -command [itcl::code $this _slice move z]
501    } {
502        usual
503        ignore -borderwidth
504        ignore -highlightthickness
505        rename -highlightbackground -controlbackground controlBackground Background
506        rename -troughcolor -controldarkbackground controlDarkBackground Background
507    }
508    $itk_component(zslicer) set 50
509    $itk_component(zslicer) configure -state disabled
510    grid $itk_component(zslicer) -row 2 -column 2 -padx 1
511    Rappture::Tooltip::for $itk_component(zslicer) \
512        "@[itcl::code $this _slicertip z]"
513
514    #
515    # Volume toggle...
516    #
517    itk_component add volume {
518        label $itk_component(slicers).volume \
519            -borderwidth 1 -relief sunken -padx 1 -pady 1 \
520            -text "Volume"
521    } {
522        usual
523        ignore -borderwidth
524        rename -highlightbackground -controlbackground controlBackground Background
525    }
526    bind $itk_component(volume) <ButtonPress> \
527        [itcl::code $this _slice volume toggle]
528    Rappture::Tooltip::for $itk_component(volume) \
529        "Toggle the volume cloud on/off"
530    grid $itk_component(volume) -row 0 -column 0 -columnspan 3 \
531        -sticky ew -padx 1 -pady 3
532
533    #
534    # Settings panel...
535    #
536    itk_component add settings {
537        button $itk_component(controls).settings -text "Settings..." \
538            -borderwidth 1 -relief flat -overrelief raised \
539            -padx 2 -pady 1 \
540            -command [list $itk_component(controls).panel activate $itk_component(controls).settings left]
541    } {
542        usual
543        ignore -borderwidth
544        rename -background -controlbackground controlBackground Background
545        rename -highlightbackground -controlbackground controlBackground Background
546    }
547    pack $itk_component(settings) -side top -pady 8
548
549    Rappture::Balloon $itk_component(controls).panel -title "Settings"
550    set inner [$itk_component(controls).panel component inner]
551    frame $inner.scales
552    pack $inner.scales -side top -fill x
553    grid columnconfigure $inner.scales 1 -weight 1
554    set fg [option get $itk_component(hull) font Font]
555
556    label $inner.scales.diml -text "Dim" -font $fg
557    grid $inner.scales.diml -row 0 -column 0 -sticky e
558    ::scale $inner.scales.light -from 0 -to 100 -orient horizontal \
559        -showvalue off -command [itcl::code $this _fixSettings light]
560    grid $inner.scales.light -row 0 -column 1 -sticky ew
561    label $inner.scales.brightl -text "Bright" -font $fg
562    grid $inner.scales.brightl -row 0 -column 2 -sticky w
563    $inner.scales.light set 40
564
565    label $inner.scales.fogl -text "Fog" -font $fg
566    grid $inner.scales.fogl -row 1 -column 0 -sticky e
567    ::scale $inner.scales.transp -from 0 -to 100 -orient horizontal \
568        -showvalue off -command [itcl::code $this _fixSettings transp]
569    grid $inner.scales.transp -row 1 -column 1 -sticky ew
570    label $inner.scales.plasticl -text "Plastic" -font $fg
571    grid $inner.scales.plasticl -row 1 -column 2 -sticky w
572    $inner.scales.transp set 50
573
574    label $inner.scales.zerol -text "Clear" -font $fg
575    grid $inner.scales.zerol -row 2 -column 0 -sticky e
576    ::scale $inner.scales.opacity -from 0 -to 100 -orient horizontal \
577        -showvalue off -command [itcl::code $this _fixSettings opacity]
578    grid $inner.scales.opacity -row 2 -column 1 -sticky ew
579    label $inner.scales.onel -text "Opaque" -font $fg
580    grid $inner.scales.onel -row 2 -column 2 -sticky w
581    $inner.scales.opacity set 100
582
583    label $inner.scales.thinl -text "Thin" -font $fg
584    grid $inner.scales.thinl -row 3 -column 0 -sticky e
585    ::scale $inner.scales.thickness -from 0 -to 1000 -orient horizontal \
586        -showvalue off -command [itcl::code $this _fixSettings thickness]
587    grid $inner.scales.thickness -row 3 -column 1 -sticky ew
588    label $inner.scales.thickl -text "Thick" -font $fg
589    grid $inner.scales.thickl -row 3 -column 2 -sticky w
590    $inner.scales.thickness set 500
591
592    set ::Rappture::NanovisViewer::_isosurface($this) 1
593    ::checkbutton $inner.scales.isosurface \
594        -text "Isosurface shading" \
595        -variable ::Rappture::NanovisViewer::_isosurface($this) \
596        -command [itcl::code $this _fixSettings isosurface]
597    grid $inner.scales.isosurface -row 4 -column 0 -columnspan 2 -sticky w
598
599
600    #
601    # RENDERING AREA
602    #
603    itk_component add area {
604        frame $itk_interior.area
605    }
606    pack $itk_component(area) -expand yes -fill both
607
608    set _image(legend) [image create photo]
609    itk_component add legend {
610        canvas $itk_component(area).legend -height 50 -highlightthickness 0
611    } {
612        usual
613        ignore -highlightthickness
614        rename -background -plotbackground plotBackground Background
615    }
616    pack $itk_component(legend) -side bottom -fill x
617    bind $itk_component(legend) <Configure> \
618        [list $_dispatcher event -idle !legend]
619
620    set _image(plot) [image create photo]
621    itk_component add 3dview {
622        label $itk_component(area).vol -image $_image(plot) \
623            -highlightthickness 0
624    } {
625        usual
626        ignore -highlightthickness
627        rename -background -plotbackground plotBackground Background
628    }
629    pack $itk_component(3dview) -expand yes -fill both
630
631    # set up bindings for rotation
632    bind $itk_component(3dview) <ButtonPress> \
633        [itcl::code $this _move click %x %y]
634    bind $itk_component(3dview) <B1-Motion> \
635        [itcl::code $this _move drag %x %y]
636    bind $itk_component(3dview) <ButtonRelease> \
637        [itcl::code $this _move release %x %y]
638    bind $itk_component(3dview) <Configure> \
639        [itcl::code $this _send screen %w %h]
640
641    set _image(download) [image create photo]
642
643    eval itk_initialize $args
644
645    connect $hostlist
646}
647
648# ----------------------------------------------------------------------
649# DESTRUCTOR
650# ----------------------------------------------------------------------
651itcl::body Rappture::NanovisViewer::destructor {} {
652    set _sendobjs ""  ;# stop any send in progress
653    set _sendobjs2 ""  ;# stop any send in progress
654    after cancel [itcl::code $this _send_dataobjs]
655    after cancel [itcl::code $this _rebuild]
656    image delete $_image(plot)
657    image delete $_image(legend)
658    image delete $_image(download)
659    interp delete $_parser
660}
661
662# ----------------------------------------------------------------------
663# USAGE: add <dataobj> ?<settings>?
664#
665# Clients use this to add a data object to the plot.  The optional
666# <settings> are used to configure the plot.  Allowed settings are
667# -color, -brightness, -width, -linestyle, and -raise.
668# ----------------------------------------------------------------------
669itcl::body Rappture::NanovisViewer::add {dataobj {settings ""}} {
670    array set params {
671        -color auto
672        -width 1
673        -linestyle solid
674        -brightness 0
675        -raise 0
676        -description ""
677        -param ""
678    }
679    foreach {opt val} $settings {
680        if {![info exists params($opt)]} {
681            error "bad setting \"$opt\": should be [join [lsort [array names params]] {, }]"
682        }
683        set params($opt) $val
684    }
685    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
686        # can't handle -autocolors yet
687        set params(-color) black
688    }
689
690    set pos [lsearch -exact $dataobj $_dlist]
691    if {$pos < 0} {
692        lappend _dlist $dataobj
693        set _all_data_objs($dataobj) 1
694        set _obj2ovride($dataobj-color) $params(-color)
695        set _obj2ovride($dataobj-width) $params(-width)
696        set _obj2ovride($dataobj-raise) $params(-raise)
697
698        after cancel [itcl::code $this _rebuild]
699        after idle [itcl::code $this _rebuild]
700    }
701}
702
703# ----------------------------------------------------------------------
704# USAGE: get ?-objects?
705# USAGE: get ?-image 3dview|legend?
706#
707# Clients use this to query the list of objects being plotted, in
708# order from bottom to top of this result.  The optional "-image"
709# flag can also request the internal images being shown.
710# ----------------------------------------------------------------------
711itcl::body Rappture::NanovisViewer::get {args} {
712    if {[llength $args] == 0} {
713        set args "-objects"
714    }
715
716    set op [lindex $args 0]
717    switch -- $op {
718      -objects {
719        # put the dataobj list in order according to -raise options
720        set dlist $_dlist
721        foreach obj $dlist {
722            if {[info exists _obj2ovride($obj-raise)] && $_obj2ovride($obj-raise)} {
723                set i [lsearch -exact $dlist $obj]
724                if {$i >= 0} {
725                    set dlist [lreplace $dlist $i $i]
726                    lappend dlist $obj
727                }
728            }
729        }
730        return $dlist
731      }
732      -image {
733        if {[llength $args] != 2} {
734            error "wrong # args: should be \"get -image 3dview|legend\""
735        }
736        switch -- [lindex $args end] {
737            3dview {
738                return $_image(plot)
739            }
740            legend {
741                return $_image(legend)
742            }
743            default {
744                error "bad image name \"[lindex $args end]\": should be 3dview or legend"
745            }
746        }
747      }
748      default {
749        error "bad option \"$op\": should be -objects or -image"
750      }
751    }
752}
753
754# ----------------------------------------------------------------------
755# USAGE: delete ?<dataobj1> <dataobj2> ...?
756#
757# Clients use this to delete a dataobj from the plot.  If no dataobjs
758# are specified, then all dataobjs are deleted.
759# ----------------------------------------------------------------------
760itcl::body Rappture::NanovisViewer::delete {args} {
761    if {[llength $args] == 0} {
762        set args $_dlist
763    }
764
765    # delete all specified dataobjs
766    set changed 0
767    foreach dataobj $args {
768        set pos [lsearch -exact $_dlist $dataobj]
769        if {$pos >= 0} {
770            set _dlist [lreplace $_dlist $pos $pos]
771            foreach key [array names _obj2ovride $dataobj-*] {
772                unset _obj2ovride($key)
773            }
774            set changed 1
775        }
776    }
777
778    # if anything changed, then rebuild the plot
779    if {$changed} {
780        after cancel [itcl::code $this _rebuild]
781        after idle [itcl::code $this _rebuild]
782    }
783}
784
785# ----------------------------------------------------------------------
786# USAGE: scale ?<data1> <data2> ...?
787#
788# Sets the default limits for the overall plot according to the
789# limits of the data for all of the given <data> objects.  This
790# accounts for all objects--even those not showing on the screen.
791# Because of this, the limits are appropriate for all objects as
792# the user scans through data in the ResultSet viewer.
793# ----------------------------------------------------------------------
794itcl::body Rappture::NanovisViewer::scale {args} {
795    foreach val {xmin xmax ymin ymax zmin zmax vmin vmax} {
796        set _limits($val) ""
797    }
798    foreach obj $args {
799        foreach axis {x y z v} {
800            foreach {min max} [$obj limits $axis] break
801            if {"" != $min && "" != $max} {
802                if {"" == $_limits(${axis}min)} {
803                    set _limits(${axis}min) $min
804                    set _limits(${axis}max) $max
805                } else {
806                    if {$min < $_limits(${axis}min)} {
807                        set _limits(${axis}min) $min
808                    }
809                    if {$max > $_limits(${axis}max)} {
810                        set _limits(${axis}max) $max
811                    }
812                }
813            }
814        }
815    }
816}
817
818# ----------------------------------------------------------------------
819# USAGE: download coming
820# USAGE: download controls <downloadCommand>
821# USAGE: download now
822#
823# Clients use this method to create a downloadable representation
824# of the plot.  Returns a list of the form {ext string}, where
825# "ext" is the file extension (indicating the type of data) and
826# "string" is the data itself.
827# ----------------------------------------------------------------------
828itcl::body Rappture::NanovisViewer::download {option args} {
829    switch $option {
830        coming {
831            if {[catch {blt::winop snap $itk_component(area) $_image(download)}]} {
832                $_image(download) configure -width 1 -height 1
833                $_image(download) put #000000
834            }
835        }
836        controls {
837            # no controls for this download yet
838            return ""
839        }
840        now {
841            #
842            # Hack alert!  Need data in binary format,
843            # so we'll save to a file and read it back.
844            #
845            set tmpfile /tmp/image[pid].jpg
846            $_image(download) write $tmpfile -format jpeg
847            set fid [open $tmpfile r]
848            fconfigure $fid -encoding binary -translation binary
849            set bytes [read $fid]
850            close $fid
851            file delete -force $tmpfile
852
853            return [list .jpg $bytes]
854        }
855        default {
856            error "bad option \"$option\": should be coming, controls, now"
857        }
858    }
859}
860
861# ----------------------------------------------------------------------
862# USAGE: connect ?<host:port>,<host:port>...?
863#
864# Clients use this method to establish a connection to a new
865# server, or to reestablish a connection to the previous server.
866# Any existing connection is automatically closed.
867# ----------------------------------------------------------------------
868itcl::body Rappture::NanovisViewer::connect {{hostlist ""}} {
869    disconnect
870
871    if {"" != $hostlist} { set _nvhosts $hostlist }
872
873    if {"" == $_nvhosts} {
874        return 0
875    }
876
877    blt::busy hold $itk_component(hull); update idletasks
878
879    # HACK ALERT! punt on this for now
880    set memorySize 10000
881
882    #
883    # Connect to the nanovis server.  Send the server some estimate
884    # of the size of our job.  If it's too busy, that server may
885    # forward us to another.
886    #
887    set try [split $_nvhosts ,]
888    foreach {hostname port} [split [lindex $try 0] :] break
889    set try [lrange $try 1 end]
890
891    while {1} {
892        _send_echo <<line "connecting to $hostname:$port..."
893        if {[catch {socket $hostname $port} sid]} {
894            if {[llength $try] == 0} {
895                return 0
896            }
897            foreach {hostname port} [split [lindex $try 0] :] break
898            set try [lrange $try 1 end]
899            continue
900        }
901        fconfigure $sid -translation binary -encoding binary
902
903        # send memory requirement to the load balancer
904        puts -nonewline $sid [binary format I $memorySize]
905        flush $sid
906
907        # read back a reconnection order
908        set data [read $sid 4]
909        if {[binary scan $data cccc b1 b2 b3 b4] != 4} {
910            error "couldn't read redirection request"
911        }
912        set addr [format "%u.%u.%u.%u" \
913            [expr {$b1 & 0xff}] \
914            [expr {$b2 & 0xff}] \
915            [expr {$b3 & 0xff}] \
916            [expr {$b4 & 0xff}]]
917        _receive_echo <<line $addr
918
919        if {[string equal $addr "0.0.0.0"]} {
920            fconfigure $sid -buffering line
921            fileevent $sid readable [itcl::code $this _receive]
922            set _sid $sid
923            blt::busy release $itk_component(hull)
924            return 1
925        }
926        set hostname $addr
927    }
928    blt::busy release $itk_component(hull)
929
930    return 0
931}
932
933# ----------------------------------------------------------------------
934# USAGE: disconnect
935#
936# Clients use this method to disconnect from the current rendering
937# server.
938# ----------------------------------------------------------------------
939itcl::body Rappture::NanovisViewer::disconnect {} {
940    if {"" != $_sid} {
941        catch {close $_sid}
942        set _sid ""
943    }
944
945    set _buffer(in) ""
946    set _buffer(out) ""
947
948    # disconnected -- no more data sitting on server
949    catch {unset _obj2id}
950    array unset _id2obj
951    set _obj2id(count) 0
952    set _id2obj(count) 0
953    set _sendobjs ""
954    set _sendobjs2 ""
955}
956
957# ----------------------------------------------------------------------
958# USAGE: isconnected
959#
960# Clients use this method to see if we are currently connected to
961# a server.
962# ----------------------------------------------------------------------
963itcl::body Rappture::NanovisViewer::isconnected {} {
964    return [expr {"" != $_sid}]
965}
966
967# ----------------------------------------------------------------------
968# USAGE: _send <arg> <arg> ...
969#
970# Used internally to send commands off to the rendering server.
971# This is a more convenient form of _send_text, which actually
972# does the sending.
973# ----------------------------------------------------------------------
974itcl::body Rappture::NanovisViewer::_send {args} {
975    _send_text $args
976}
977
978# ----------------------------------------------------------------------
979# USAGE: _send_text <string>
980#
981# Used internally to send commands off to the rendering server.
982# ----------------------------------------------------------------------
983itcl::body Rappture::NanovisViewer::_send_text {string} {
984    if {"" == $_sid} {
985        $_dispatcher cancel !serverDown
986        set x [expr {[winfo rootx $itk_component(area)]+10}]
987        set y [expr {[winfo rooty $itk_component(area)]+10}]
988        Rappture::Tooltip::cue @$x,$y "Connecting..."
989
990        if {[catch {connect} ok] == 0 && $ok} {
991            set w [winfo width $itk_component(3dview)]
992            set h [winfo height $itk_component(3dview)]
993
994            if {[catch {puts $_sid "screen $w $h"}]} {
995                disconnect
996                _receive_echo closed
997                $_dispatcher event -after 750 !serverDown
998            } else {
999                _send_echo >>line "screen $w $h"
1000
1001                set _view(theta) 45
1002                set _view(phi) 45
1003                set _view(psi) 0
1004                set _view(zoom) 1.0
1005                after idle [itcl::code $this _rebuild]
1006                Rappture::Tooltip::cue hide
1007            }
1008            return
1009        }
1010        Rappture::Tooltip::cue @$x,$y "Can't connect to visualization server.  This may be a network problem.  Wait a few moments and try resetting the view."
1011        return
1012    }
1013    if {"" != $_sid} {
1014        # if we're transmitting objects, then buffer this command
1015        if {[llength $_sendobjs] > 0} {
1016            append _buffer(out) $string "\n"
1017        } else {
1018            if {[catch {puts $_sid $string}]} {
1019                disconnect
1020                _receive_echo closed
1021                $_dispatcher event -after 750 !serverDown
1022            } else {
1023                foreach line [split $string \n] {
1024                    _send_echo >>line $line
1025                }
1026            }
1027        }
1028    }
1029}
1030
1031# ----------------------------------------------------------------------
1032# USAGE: _send_dataobjs
1033#
1034# Used internally to send a series of volume objects off to the
1035# server.  Sends each object, a little at a time, with updates in
1036# between so the interface doesn't lock up.
1037# ----------------------------------------------------------------------
1038itcl::body Rappture::NanovisViewer::_send_dataobjs {} {
1039    blt::busy hold $itk_component(hull); update idletasks
1040
1041    foreach dataobj $_sendobjs {
1042        foreach comp [$dataobj components] {
1043            # send the data as one huge base64-encoded mess -- yuck!
1044            set data [$dataobj values $comp]
1045
1046            # tell the engine to expect some data
1047            set cmdstr "volume data follows [string length $data]"
1048            _send_echo >>line $cmdstr
1049            if {[catch {puts $_sid $cmdstr} err]} {
1050                disconnect
1051                $_dispatcher event -after 750 !serverDown
1052                return
1053            }
1054
1055            while {[string length $data] > 0} {
1056                update
1057
1058                set chunk [string range $data 0 8095]
1059                set data [string range $data 8096 end]
1060
1061                _send_echo >>line $chunk
1062                if {[catch {puts -nonewline $_sid $chunk} err]} {
1063                    disconnect
1064                    $_dispatcher event -after 750 !serverDown
1065                    return
1066                }
1067                catch {flush $_sid}
1068            }
1069            _send_echo >>line ""
1070            puts $_sid ""
1071
1072            set volId $_obj2id(count)
1073            incr _obj2id(count)
1074
1075            set _id2obj($volId) [list $dataobj $comp]
1076            set _obj2id($dataobj-$comp) $volId
1077            set _receiveids($volId) 1
1078        }
1079    }
1080    set _sendobjs ""
1081    blt::busy release $itk_component(hull)
1082
1083    # activate the proper volume
1084    set first [lindex [get] 0]
1085    if {"" != $first} {
1086        set axis [$first hints updir]
1087        if {"" != $axis} {
1088            _send up $axis
1089        }
1090    }
1091
1092    # sync the state of slicers
1093    set vols [_currentVolumeIds -cutplanes]
1094    foreach axis {x y z} {
1095        eval _send cutplane state [_state ${axis}slice] $axis $vols
1096        set pos [expr {0.01*[$itk_component(${axis}slicer) get]}]
1097        eval _send cutplane position $pos $axis $vols
1098    }
1099    eval _send volume data state [_state volume] $vols
1100
1101    # if there are any commands in the buffer, send them now that we're done
1102    _send_echo >>line $_buffer(out)
1103    if {[catch {puts $_sid $_buffer(out)} err]} {
1104        disconnect
1105        $_dispatcher event -after 750 !serverDown
1106    }
1107    set _buffer(out) ""
1108
1109#    $_dispatcher event -idle !legend
1110}
1111
1112# ----------------------------------------------------------------------
1113# USAGE: _send_transfuncs
1114# ----------------------------------------------------------------------
1115itcl::body Rappture::NanovisViewer::_send_transfuncs {} {
1116    blt::busy hold $itk_component(hull); update idletasks
1117    foreach dataobj $_sendobjs2 {
1118        foreach comp [$dataobj components] {
1119            #
1120            # Determine the transfer function needed for this volume
1121            # and make sure that it's defined on the server.
1122            #
1123            if { ![info exists _isomarkers($dataobj)] } {
1124                _initIsoMarkers $dataobj $comp
1125            } else {
1126                _hideIsoMarkers $dataobj
1127            }
1128            foreach {sname cmap wmap} [_genTransfuncData $dataobj $comp] break
1129            set cmdstr [list transfunc define $sname $cmap $wmap]
1130            _send_echo >>line $cmdstr
1131            if {[catch {puts $_sid $cmdstr} err]} {
1132                disconnect
1133                $_dispatcher event -after 750 !serverDown
1134                return
1135            }
1136            set _obj2style($dataobj-$comp) $sname
1137        }
1138    }
1139    set _sendobjs2 ""
1140    blt::busy release $itk_component(hull)
1141
1142    # activate the proper volume
1143    set first [lindex [get] 0]
1144    foreach key [array names _obj2id *-*] {
1145        set state [string match $first-* $key]
1146        _send volume state $state $_obj2id($key)
1147        if {[info exists _obj2style($key)]} {
1148            _send volume shading transfunc $_obj2style($key) $_obj2id($key)
1149        }
1150    }
1151    _showIsoMarkers $first
1152    # if there are any commands in the buffer, send them now that we're done
1153    _send_echo >>line $_buffer(out)
1154    if {[catch {puts $_sid $_buffer(out)} err]} {
1155        disconnect
1156        $_dispatcher event -after 750 !serverDown
1157    }
1158    set _buffer(out) ""
1159    $_dispatcher event -idle !legend
1160}
1161
1162# ----------------------------------------------------------------------
1163# USAGE: _send_echo <channel> ?<data>?
1164#
1165# Used internally to echo sent data to clients interested in
1166# this widget.  If the -sendcommand option is set, then it is
1167# invoked in the global scope with the <channel> and <data> values
1168# as arguments.  Otherwise, this does nothing.
1169# ----------------------------------------------------------------------
1170itcl::body Rappture::NanovisViewer::_send_echo {channel {data ""}} {
1171    if {[string length $itk_option(-sendcommand)] > 0} {
1172        uplevel #0 $itk_option(-sendcommand) [list $channel $data]
1173    }
1174}
1175
1176# ----------------------------------------------------------------------
1177# USAGE: _receive
1178#
1179# Invoked automatically whenever a command is received from the
1180# rendering server.  Reads the incoming command and executes it in
1181# a safe interpreter to handle the action.
1182# ----------------------------------------------------------------------
1183itcl::body Rappture::NanovisViewer::_receive {} {
1184    if {"" != $_sid} {
1185        if {[gets $_sid line] < 0} {
1186            disconnect
1187            _receive_echo closed
1188            $_dispatcher event -after 750 !serverDown
1189        } elseif {[string equal [string range $line 0 2] "nv>"]} {
1190            _receive_echo <<line $line
1191            append _buffer(in) [string range $line 3 end]
1192            if {[info complete $_buffer(in)]} {
1193                set request $_buffer(in)
1194                set _buffer(in) ""
1195                $_parser eval $request
1196            }
1197        } else {
1198            # this shows errors coming back from the engine
1199            _receive_echo <<error $line
1200            puts stderr $line
1201        }
1202    }
1203}
1204
1205# ----------------------------------------------------------------------
1206# USAGE: _receive_image -bytes <size>
1207#
1208# Invoked automatically whenever the "image" command comes in from
1209# the rendering server.  Indicates that binary image data with the
1210# specified <size> will follow.
1211# ----------------------------------------------------------------------
1212itcl::body Rappture::NanovisViewer::_receive_image {option size} {
1213    if {"" != $_sid} {
1214        set bytes [read $_sid $size]
1215        $_image(plot) configure -data $bytes
1216        _receive_echo <<line "<read $size bytes for [image width $_image(plot)]x[image height $_image(plot)] image>"
1217    }
1218}
1219
1220# ----------------------------------------------------------------------
1221# USAGE: _receive_legend <volume> <vmin> <vmax> <size>
1222#
1223# Invoked automatically whenever the "legend" command comes in from
1224# the rendering server.  Indicates that binary image data with the
1225# specified <size> will follow.
1226# ----------------------------------------------------------------------
1227itcl::body Rappture::NanovisViewer::_receive_legend {ivol vmin vmax size} {
1228    if {"" != $_sid} {
1229        set bytes [read $_sid $size]
1230        $_image(legend) configure -data $bytes
1231        _receive_echo <<line "<read $size bytes for [image width $_image(legend)]x[image height $_image(legend)] legend>"
1232
1233        set c $itk_component(legend)
1234        set w [winfo width $c]
1235        set h [winfo height $c]
1236        if {"" == [$c find withtag transfunc]} {
1237            $c create image 10 10 -anchor nw \
1238                 -image $_image(legend) -tags transfunc
1239            $c create text 10 [expr {$h-8}] -anchor sw \
1240                 -fill $itk_option(-plotforeground) -tags vmin
1241            $c create text [expr {$w-10}] [expr {$h-8}] -anchor se \
1242                 -fill $itk_option(-plotforeground) -tags vmax
1243            $c lower transfunc
1244            $c bind transfunc <ButtonRelease-1> \
1245                [itcl::code $this _addIsoMarker %x %y]
1246        }
1247        $c itemconfigure vmin -text $vmin
1248        $c coords vmin 10 [expr {$h-8}]
1249
1250        $c itemconfigure vmax -text $vmax
1251        $c coords vmax [expr {$w-10}] [expr {$h-8}]
1252        set first [lindex [get] 0]
1253        _showIsoMarkers $first
1254    }
1255}
1256
1257# ----------------------------------------------------------------------
1258# USAGE: _receive_data <volume> <vmin> <vmax>
1259#
1260# Invoked automatically whenever the "legend" command comes in from
1261# the rendering server.  Indicates that binary image data with the
1262# specified <size> will follow.
1263# ----------------------------------------------------------------------
1264itcl::body Rappture::NanovisViewer::_receive_data { args } {
1265    if {"" != $_sid} {
1266        array set info $args
1267        set volume $info(id)
1268        foreach { dataobj comp } $_id2obj($volume) break
1269        if { ![info exists _limits($dataobj-vmin] } {
1270            set _limits($dataobj-vmin) $info(min)
1271            set _limits($dataobj-vmax) $info(max)
1272        } else {
1273            if { $_limits($dataobj-vmin) > $info(min) } {
1274                set _limits($dataobj-vmin) $info(min)
1275            }
1276            if { $_limits($dataobj-vmax) > $info(max) } {
1277                set _limits($dataobj-vmax) $info(max)
1278            }
1279        }           
1280        set _limits(vmin) $info(vmin)
1281        set _limits(vmax) $info(vmax)
1282        lappend _sendobjs2 $dataobj
1283        unset _receiveids($info(id))
1284        if { [array size _receiveids] == 0 } {
1285            after idle [itcl::code $this _send_transfuncs]
1286        }
1287    }
1288}
1289
1290# ----------------------------------------------------------------------
1291# USAGE: _receive_echo <channel> ?<data>?
1292#
1293# Used internally to echo received data to clients interested in
1294# this widget.  If the -receivecommand option is set, then it is
1295# invoked in the global scope with the <channel> and <data> values
1296# as arguments.  Otherwise, this does nothing.
1297# ----------------------------------------------------------------------
1298itcl::body Rappture::NanovisViewer::_receive_echo {channel {data ""}} {
1299    if {[string length $itk_option(-receivecommand)] > 0} {
1300        uplevel #0 $itk_option(-receivecommand) [list $channel $data]
1301    }
1302}
1303
1304# ----------------------------------------------------------------------
1305# USAGE: _rebuild
1306#
1307# Called automatically whenever something changes that affects the
1308# data in the widget.  Clears any existing data and rebuilds the
1309# widget to display new data.
1310# ----------------------------------------------------------------------
1311itcl::body Rappture::NanovisViewer::_rebuild {} {
1312    # in the midst of sending data? then bail out
1313    if {[llength $_sendobjs] > 0} {
1314        return
1315    }
1316
1317    #
1318    # Find any new data that needs to be sent to the server.
1319    # Queue this up on the _sendobjs list, and send it out
1320    # a little at a time.  Do this first, before we rebuild
1321    # the rest.
1322    #
1323    foreach dataobj [get] {
1324        set comp [lindex [$dataobj components] 0]
1325        if {![info exists _obj2id($dataobj-$comp)]} {
1326            set i [lsearch -exact $_sendobjs $dataobj]
1327            if {$i < 0} {
1328                lappend _sendobjs $dataobj
1329            }
1330        }
1331    }
1332    if {[llength $_sendobjs] > 0} {
1333        # send off new data objects
1334        after idle [itcl::code $this _send_dataobjs]
1335    } else {
1336        # nothing to send -- activate the proper volume
1337        set first [lindex [get] 0]
1338        if {"" != $first} {
1339            set axis [$first hints updir]
1340            if {"" != $axis} {
1341                _send up $axis
1342            }
1343        }
1344
1345        _showIsoMarkers $first
1346        update_transfer_function
1347
1348        # sync the state of slicers
1349        set vols [_currentVolumeIds -cutplanes]
1350        foreach axis {x y z} {
1351            eval _send cutplane state [_state ${axis}slice] $axis $vols
1352            set pos [expr {0.01*[$itk_component(${axis}slicer) get]}]
1353            eval _send cutplane position $pos $axis $vols
1354        }
1355        eval _send volume data state [_state volume] $vols
1356        $_dispatcher event -idle !legend
1357    }
1358
1359    #
1360    # Reset the camera and other view parameters
1361    #
1362    eval _send camera angle [_euler2xyz $_view(theta) $_view(phi) $_view(psi)]
1363    _send camera zoom $_view(zoom)
1364
1365    _fixSettings light
1366    _fixSettings transp
1367    _fixSettings isosurface
1368    _fixSettings opacity
1369    _fixSettings thickness
1370
1371    if {"" == $itk_option(-plotoutline)} {
1372        _send volume outline state off
1373    } else {
1374        _send volume outline state on
1375        _send volume outline color [_color2rgb $itk_option(-plotoutline)]
1376    }
1377    _send volume axis label x ""
1378    _send volume axis label y ""
1379    _send volume axis label z ""
1380}
1381
1382# ----------------------------------------------------------------------
1383# USAGE: _currentVolumeIds ?-cutplanes?
1384#
1385# Returns a list of volume server IDs for the current volume being
1386# displayed.  This is normally a single ID, but it might be a list
1387# of IDs if the current data object has multiple components.
1388# ----------------------------------------------------------------------
1389itcl::body Rappture::NanovisViewer::_currentVolumeIds {{what -all}} {
1390    set rlist ""
1391
1392    set first [lindex [get] 0]
1393    foreach key [array names _obj2id *-*] {
1394        if {[string match $first-* $key]} {
1395            array set style {
1396                -cutplanes 1
1397            }
1398            foreach {dataobj comp} [split $key -] break
1399            array set style [lindex [$dataobj components -style $comp] 0]
1400
1401            if {$what != "-cutplanes" || $style(-cutplanes)} {
1402                lappend rlist $_obj2id($key)
1403            }
1404        }
1405    }
1406    return $rlist
1407}
1408
1409# ----------------------------------------------------------------------
1410# USAGE: _zoom in
1411# USAGE: _zoom out
1412# USAGE: _zoom reset
1413#
1414# Called automatically when the user clicks on one of the zoom
1415# controls for this widget.  Changes the zoom for the current view.
1416# ----------------------------------------------------------------------
1417itcl::body Rappture::NanovisViewer::_zoom {option} {
1418    switch -- $option {
1419        in {
1420            set _view(zoom) [expr {$_view(zoom)*1.25}]
1421            _send camera zoom $_view(zoom)
1422        }
1423        out {
1424            set _view(zoom) [expr {$_view(zoom)*0.8}]
1425            _send camera zoom $_view(zoom)
1426        }
1427        reset {
1428            set _view(theta) 45
1429            set _view(phi) 45
1430            set _view(psi) 0
1431            set _view(zoom) 1.0
1432            eval _send camera angle [_euler2xyz $_view(theta) $_view(phi) $_view(psi)]
1433            _send camera zoom $_view(zoom)
1434        }
1435    }
1436}
1437
1438# ----------------------------------------------------------------------
1439# USAGE: _move click <x> <y>
1440# USAGE: _move drag <x> <y>
1441# USAGE: _move release <x> <y>
1442#
1443# Called automatically when the user clicks/drags/releases in the
1444# plot area.  Moves the plot according to the user's actions.
1445# ----------------------------------------------------------------------
1446itcl::body Rappture::NanovisViewer::_move {option x y} {
1447    switch -- $option {
1448        click {
1449            $itk_component(3dview) configure -cursor fleur
1450            set _click(x) $x
1451            set _click(y) $y
1452            set _click(theta) $_view(theta)
1453            set _click(phi) $_view(phi)
1454        }
1455        drag {
1456            if {[array size _click] == 0} {
1457                _move click $x $y
1458            } else {
1459                set w [winfo width $itk_component(3dview)]
1460                set h [winfo height $itk_component(3dview)]
1461                if {$w <= 0 || $h <= 0} {
1462                    return
1463                }
1464
1465                if {[catch {
1466                    # this fails sometimes for no apparent reason
1467                    set dx [expr {double($x-$_click(x))/$w}]
1468                    set dy [expr {double($y-$_click(y))/$h}]
1469                }]} {
1470                    return
1471                }
1472
1473                #
1474                # Rotate the camera in 3D
1475                #
1476                if {$_view(psi) > 90 || $_view(psi) < -90} {
1477                    # when psi is flipped around, theta moves backwards
1478                    set dy [expr {-$dy}]
1479                }
1480                set theta [expr {$_view(theta) - $dy*180}]
1481                while {$theta < 0} { set theta [expr {$theta+180}] }
1482                while {$theta > 180} { set theta [expr {$theta-180}] }
1483
1484                if {abs($theta) >= 30 && abs($theta) <= 160} {
1485                    set phi [expr {$_view(phi) - $dx*360}]
1486                    while {$phi < 0} { set phi [expr {$phi+360}] }
1487                    while {$phi > 360} { set phi [expr {$phi-360}] }
1488                    set psi $_view(psi)
1489                } else {
1490                    set phi $_view(phi)
1491                    set psi [expr {$_view(psi) - $dx*360}]
1492                    while {$psi < -180} { set psi [expr {$psi+360}] }
1493                    while {$psi > 180} { set psi [expr {$psi-360}] }
1494                }
1495
1496                set _view(theta) $theta
1497                set _view(phi) $phi
1498                set _view(psi) $psi
1499                eval _send camera angle [_euler2xyz $_view(theta) $_view(phi) $_view(psi)]
1500
1501                set _click(x) $x
1502                set _click(y) $y
1503            }
1504        }
1505        release {
1506            _move drag $x $y
1507            $itk_component(3dview) configure -cursor ""
1508            catch {unset _click}
1509        }
1510        default {
1511            error "bad option \"$option\": should be click, drag, release"
1512        }
1513    }
1514}
1515
1516# ----------------------------------------------------------------------
1517# USAGE: _slice axis x|y|z ?on|off|toggle?
1518# USAGE: _slice move x|y|z <newval>
1519# USAGE: _slice volume ?on|off|toggle?
1520#
1521# Called automatically when the user drags the slider to move the
1522# cut plane that slices 3D data.  Gets the current value from the
1523# slider and moves the cut plane to the appropriate point in the
1524# data set.
1525# ----------------------------------------------------------------------
1526itcl::body Rappture::NanovisViewer::_slice {option args} {
1527    switch -- $option {
1528        axis {
1529            if {[llength $args] < 1 || [llength $args] > 2} {
1530                error "wrong # args: should be \"_slice axis x|y|z ?on|off|toggle?\""
1531            }
1532            set axis [lindex $args 0]
1533            set op [lindex $args 1]
1534            if {$op == ""} { set op "on" }
1535
1536            set current [_state ${axis}slice]
1537            if {$op == "toggle"} {
1538                if {$current == "on"} { set op "off" } else { set op "on" }
1539            }
1540
1541            if {$op} {
1542                $itk_component(${axis}slicer) configure -state normal
1543                eval _send cutplane state on $axis [_currentVolumeIds -cutplanes]
1544                $itk_component(${axis}slice) configure -relief sunken
1545            } else {
1546                $itk_component(${axis}slicer) configure -state disabled
1547                eval _send cutplane state off $axis [_currentVolumeIds -cutplanes]
1548                $itk_component(${axis}slice) configure -relief raised
1549            }
1550        }
1551        move {
1552            if {[llength $args] != 2} {
1553                error "wrong # args: should be \"_slice move x|y|z newval\""
1554            }
1555            set axis [lindex $args 0]
1556            set newval [lindex $args 1]
1557
1558            set newpos [expr {0.01*$newval}]
1559#            set newval [expr {0.01*($newval-50)
1560#                *($_limits(${axis}max)-$_limits(${axis}min))
1561#                  + 0.5*($_limits(${axis}max)+$_limits(${axis}min))}]
1562
1563            # show the current value in the readout
1564#puts "readout: $axis = $newval"
1565
1566            eval _send cutplane position $newpos $axis [_currentVolumeIds -cutplanes]
1567        }
1568        volume {
1569            if {[llength $args] > 1} {
1570                error "wrong # args: should be \"_slice volume ?on|off|toggle?\""
1571            }
1572            set op [lindex $args 0]
1573            if {$op == ""} { set op "on" }
1574
1575            set current [_state volume]
1576            if {$op == "toggle"} {
1577                if {$current == "on"} { set op "off" } else { set op "on" }
1578            }
1579
1580            if {$op} {
1581                eval _send volume data state on [_currentVolumeIds]
1582                $itk_component(volume) configure -relief sunken
1583            } else {
1584                eval _send volume data state off [_currentVolumeIds]
1585                $itk_component(volume) configure -relief raised
1586            }
1587        }
1588        default {
1589            error "bad option \"$option\": should be axis, move, or volume"
1590        }
1591    }
1592}
1593
1594# ----------------------------------------------------------------------
1595# USAGE: _slicertip <axis>
1596#
1597# Used internally to generate a tooltip for the x/y/z slicer controls.
1598# Returns a message that includes the current slicer value.
1599# ----------------------------------------------------------------------
1600itcl::body Rappture::NanovisViewer::_slicertip {axis} {
1601    set val [$itk_component(${axis}slicer) get]
1602#    set val [expr {0.01*($val-50)
1603#        *($_limits(${axis}max)-$_limits(${axis}min))
1604#          + 0.5*($_limits(${axis}max)+$_limits(${axis}min))}]
1605    return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
1606}
1607
1608# ----------------------------------------------------------------------
1609# USAGE: _state <component>
1610#
1611# Used internally to determine the state of a toggle button.
1612# The <component> is the itk component name of the button.
1613# Returns on/off for the state of the button.
1614# ----------------------------------------------------------------------
1615itcl::body Rappture::NanovisViewer::_state {comp} {
1616    if {[$itk_component($comp) cget -relief] == "sunken"} {
1617        return "on"
1618    }
1619    return "off"
1620}
1621
1622# ----------------------------------------------------------------------
1623# USAGE: _fixSettings <what> ?<value>?
1624#
1625# Used internally to update rendering settings whenever parameters
1626# change in the popup settings panel.  Sends the new settings off
1627# to the back end.
1628# ----------------------------------------------------------------------
1629itcl::body Rappture::NanovisViewer::_fixSettings {what {value ""}} {
1630    set inner [$itk_component(controls).panel component inner]
1631    switch -- $what {
1632        light {
1633            if {[isconnected]} {
1634                set val [$inner.scales.light get]
1635                set sval [expr {0.1*$val}]
1636                _send volume shading diffuse $sval
1637
1638                set sval [expr {sqrt($val+1.0)}]
1639                _send volume shading specular $sval
1640            }
1641        }
1642        transp {
1643            if {[isconnected]} {
1644                set val [$inner.scales.transp get]
1645                set sval [expr {0.2*$val+1}]
1646                _send volume shading opacity $sval
1647            }
1648        }
1649        opacity {
1650            if {[isconnected]} {
1651                set dataobj [lindex [get] 0]
1652                if {$dataobj != 0} {
1653                    set val [$inner.scales.opacity get]
1654                    set sval [expr {0.01*double($val)}]
1655                    set _opacity($dataobj) $sval
1656                    update_transfer_function
1657                }
1658            }
1659        }
1660        thickness {
1661            if {[isconnected]} {
1662                set dataobj [lindex [get] 0]
1663                if {$dataobj != 0} {
1664                    set val [$inner.scales.thickness get]
1665                    # Scale values between 0.00001 and 0.01000
1666                    set sval [expr {0.00001*double($val)}]
1667                    puts stderr "thickness($dataobj) = $sval"
1668                    set _thickness($dataobj) $sval
1669                    update_transfer_function
1670                }
1671            }
1672        }
1673        isosurface {
1674            if {[isconnected]} {
1675                set val $Rappture::NanovisViewer::_isosurface($this)
1676                set dataobj [lindex [get] 0]
1677                _send "volume" "shading" "isosurface" $val
1678            }
1679        }           
1680        default {
1681            error "don't know how to fix $what"
1682        }
1683    }
1684}
1685
1686# ----------------------------------------------------------------------
1687# USAGE: _fixLegend
1688#
1689# Used internally to update the legend area whenever it changes size
1690# or when the field changes.  Asks the server to send a new legend
1691# for the current field.
1692# ----------------------------------------------------------------------
1693itcl::body Rappture::NanovisViewer::_fixLegend {} {
1694    set lineht [font metrics $itk_option(-font) -linespace]
1695    set w [expr {[winfo width $itk_component(legend)]-20}]
1696    set h [expr {[winfo height $itk_component(legend)]-20-$lineht}]
1697    set ivol ""
1698
1699    set dataobj [lindex [get] 0]
1700    if {"" != $dataobj} {
1701        set comp [lindex [$dataobj components] 0]
1702        if {[info exists _obj2id($dataobj-$comp)]} {
1703            set ivol $_obj2id($dataobj-$comp)
1704        }
1705    }
1706    if {$w > 0 && $h > 0 && "" != $ivol} {
1707        _send legend $ivol $w $h
1708    } else {
1709        #$itk_component(legend) delete all
1710    }
1711}
1712
1713# ----------------------------------------------------------------------
1714# USAGE: _serverDown
1715#
1716# Used internally to let the user know when the connection to the
1717# visualization server has been lost.  Puts up a tip encouraging the
1718# user to press any control to reconnect.
1719# ----------------------------------------------------------------------
1720itcl::body Rappture::NanovisViewer::_serverDown {} {
1721    set x [expr {[winfo rootx $itk_component(area)]+10}]
1722    set y [expr {[winfo rooty $itk_component(area)]+10}]
1723    Rappture::Tooltip::cue @$x,$y "Lost connection to visualization server.  This happens sometimes when there are too many users and the system runs out of memory.\n\nTo reconnect, reset the view or press any other control.  Your picture should come right back up."
1724}
1725
1726# ----------------------------------------------------------------------
1727# USAGE: _genTransfuncData <dataobj> <comp>
1728#
1729# Used internally to compute the colormap and alpha map used to define
1730# a transfer function for the specified component in a data object.
1731# Returns: name {v r g b ...} {v w ...}
1732# ----------------------------------------------------------------------
1733itcl::body Rappture::NanovisViewer::_genTransfuncData {dataobj comp} {
1734    array set style {
1735        -color rainbow
1736        -levels 6
1737        -opacity 1.0
1738    }
1739    array set style [lindex [$dataobj components -style $comp] 0]
1740    set sname "$style(-color):$style(-levels):$style(-opacity)"
1741
1742    if {$style(-color) == "rainbow"} {
1743        set style(-color) "white:yellow:green:cyan:blue:magenta"
1744    }
1745    set clist [split $style(-color) :]
1746    set cmap "0.0 [_color2rgb white] "
1747    for {set i 0} {$i < [llength $clist]} {incr i} {
1748        set x [expr {double($i+1)/([llength $clist]+1)}]
1749        set color [lindex $clist $i]
1750        append cmap "$x [_color2rgb $color] "
1751    }
1752    append cmap "1.0 [_color2rgb $color]"
1753
1754    set max $style(-opacity)
1755    if { [info exists _opacity($dataobj)] } {
1756        set max $_opacity($dataobj)
1757    }
1758    set key $dataobj
1759    set isovalues {}
1760    foreach m $_isomarkers($key) {
1761        lappend isovalues [$m get_relative_value]
1762    }
1763
1764    # Sort the isovalues
1765    set isovalues [lsort -real $isovalues]
1766
1767    set delta 0.01
1768    if { [info exists _thickness($dataobj)]} {
1769        set delta $_thickness($dataobj)
1770    }
1771    puts stderr "delta=$delta thickness($dataobj)=$_thickness($dataobj)"
1772    set first [lindex $isovalues 0]
1773    set last [lindex $isovalues end]
1774    set wmap ""
1775    if { $first == "" || $first != 0.0 } {
1776        lappend wmap 0.0 0.0
1777    }
1778    foreach x $isovalues {
1779        set x1 [expr {$x-$delta-0.00001}]
1780        set x2 [expr {$x-$delta}]
1781        set x3 [expr {$x+$delta}]
1782        set x4 [expr {$x+$delta+0.00001}]
1783        if { $x1 < 0.0 } {
1784            set x1 0.0
1785        }
1786        if { $x2 < 0.0 } {
1787            set x2 0.0
1788        }
1789        if { $x3 > 1.0 } {
1790            set x3 1.0
1791        }
1792        if { $x4 > 1.0 } {
1793            set x4 1.0
1794        }
1795        # add spikes in the middle
1796        lappend wmap $x1 0.0 
1797        lappend wmap $x2 $max
1798        lappend wmap $x3 $max 
1799        lappend wmap $x4 0.0
1800    }
1801    if { $last == "" || $last != 1.0 } {
1802        lappend wmap 1.0 0.0
1803    }
1804    return [list $sname $cmap $wmap]
1805}
1806
1807# ----------------------------------------------------------------------
1808# USAGE: _color2rgb <color>
1809#
1810# Used internally to convert a color name to a set of {r g b} values
1811# needed for the engine.  Each r/g/b component is scaled in the
1812# range 0-1.
1813# ----------------------------------------------------------------------
1814itcl::body Rappture::NanovisViewer::_color2rgb {color} {
1815    foreach {r g b} [winfo rgb $itk_component(hull) $color] break
1816    set r [expr {$r/65535.0}]
1817    set g [expr {$g/65535.0}]
1818    set b [expr {$b/65535.0}]
1819    return [list $r $g $b]
1820}
1821
1822# ----------------------------------------------------------------------
1823# USAGE: _euler2xyz <theta> <phi> <psi>
1824#
1825# Used internally to convert euler angles for the camera placement
1826# the to angles of rotation about the x/y/z axes, used by the engine.
1827# Returns a list:  {xangle, yangle, zangle}.
1828# ----------------------------------------------------------------------
1829itcl::body Rappture::NanovisViewer::_euler2xyz {theta phi psi} {
1830    set xangle [expr {$theta-90.0}]
1831    set yangle [expr {180-$phi}]
1832    set zangle $psi
1833    return [list $xangle $yangle $zangle]
1834}
1835
1836# ----------------------------------------------------------------------
1837# CONFIGURATION OPTION: -plotbackground
1838# ----------------------------------------------------------------------
1839itcl::configbody Rappture::NanovisViewer::plotbackground {
1840    foreach {r g b} [_color2rgb $itk_option(-plotbackground)] break
1841    #fix this!
1842    #_send color background $r $g $b
1843}
1844
1845# ----------------------------------------------------------------------
1846# CONFIGURATION OPTION: -plotforeground
1847# ----------------------------------------------------------------------
1848itcl::configbody Rappture::NanovisViewer::plotforeground {
1849    foreach {r g b} [_color2rgb $itk_option(-plotforeground)] break
1850    #fix this!
1851    #_send color background $r $g $b
1852}
1853
1854# ----------------------------------------------------------------------
1855# CONFIGURATION OPTION: -plotoutline
1856# ----------------------------------------------------------------------
1857itcl::configbody Rappture::NanovisViewer::plotoutline {
1858    if {[isconnected]} {
1859        if {"" == $itk_option(-plotoutline)} {
1860            _send volume outline state off
1861        } else {
1862            _send volume outline state on
1863            _send volume outline color [_color2rgb $itk_option(-plotoutline)]
1864        }
1865    }
1866}
1867
1868itcl::body Rappture::NanovisViewer::_hideIsoMarkers {dataobj} {
1869    if { [info exists _isomarkers($dataobj)] } {
1870        foreach m $_isomarkers($dataobj) {
1871            $m hide
1872        }
1873    }
1874}
1875
1876itcl::body Rappture::NanovisViewer::_showIsoMarkers {dataobj} {
1877    foreach obj [array names _all_data_objs] {
1878        _hideIsoMarkers $obj
1879    }
1880    if { ![info exists _isomarkers($dataobj)] } {
1881        return
1882    }
1883    foreach m $_isomarkers($dataobj) {
1884        $m show
1885    }
1886}
1887
1888itcl::body Rappture::NanovisViewer::_initIsoMarkers {dataobj comp} {
1889    array set style {
1890        -levels 6x
1891    }
1892    array set style [lindex [$dataobj components -style $comp] 0]
1893    set levels $style(-levels)
1894    set c $itk_component(legend)
1895    regsub -all "," $levels " " levels
1896    foreach level $levels {
1897        set n [scan $level "%g%s" value suffix]
1898        if { $n == 2 && $suffix == "%" } {
1899            # ${n}% : Set relative value.
1900            set value [expr {$value * 0.01}]
1901            set m [IsoMarker \#auto $c $this]
1902            $m set_relative_value $value
1903            lappend _isomarkers($dataobj) $m
1904        } elseif { $n == 2 && $suffix == "x" } {
1905            # ${n}x : Set equal number of levels
1906            if { $value != round($value) } {
1907                error "\# of levels \"$value\" must be an interger"
1908            }
1909            set nLevels [expr round($value)]
1910            for {set i 1} {$i <= $nLevels} {incr i} {
1911                set x [expr {double($i)/($nLevels+1)}]
1912                set m [IsoMarker \#auto $c $this]
1913                $m set_relative_value $x
1914                lappend _isomarkers($dataobj) $m
1915            }
1916        } else {
1917            # ${n} : Set absolute value.
1918            set m [IsoMarker \#auto $c $this]
1919            $m set_absolute_value $value
1920            lappend _isomarkers($dataobj) $m
1921        }
1922    }
1923}
1924
1925# ----------------------------------------------------------------------
1926# USAGE: _marker start <x> <y>
1927# USAGE: _marker update <x> <y>
1928# USAGE: _marker end <x> <y>
1929#
1930# Used internally to handle the various marker operations performed
1931# when the user clicks and drags on the legend area.  The marker changes the
1932# transfer function to highlight the area being selected in the
1933# legend.
1934# ----------------------------------------------------------------------
1935itcl::body Rappture::NanovisViewer::update_transfer_function {} {
1936    set dataobj [lindex [get] 0]
1937    if {"" == $dataobj} {
1938        return
1939    }
1940    set comp [lindex [$dataobj components] 0]
1941    set key $dataobj-$comp
1942    if {![info exists _obj2style($key)]} {
1943        return
1944    }
1945    # Compute a transfer function for the current data set.
1946    foreach {sname cmap wmap} [_genTransfuncData $dataobj $comp] break
1947    _send transfunc define $_obj2style($key) $cmap $wmap
1948    _send volume shading transfunc $_obj2style($key) $_obj2id($key)
1949    _fixLegend
1950}
1951
1952itcl::body Rappture::NanovisViewer::_addIsoMarker { x y } {
1953    set dataobj [lindex [get] 0]
1954    if {$dataobj == ""} {
1955        return 0;                       # No data sets defined
1956    }
1957    set c $itk_component(legend)
1958    set m [IsoMarker \#auto $c $this]
1959    set w [winfo width $c]
1960    $m set_relative_value [expr {double($x-10)/($w-20)}]
1961    $m show
1962    lappend _isomarkers($dataobj) $m
1963    update_transfer_function
1964    return 1
1965}
1966
1967itcl::body Rappture::NanovisViewer::remove_duplicate_isomarker { marker x } {
1968    set dataobj [lindex [get] 0]
1969    if {"" == $dataobj} {
1970        return 0
1971    }
1972    set bool 0
1973    if { [info exists _isomarkers($dataobj)] } {
1974        set list {}
1975        set marker [namespace tail $marker]
1976        foreach m $_isomarkers($dataobj) {
1977            set sx [$m get_screen_position]
1978            if { $m != $marker } {
1979                if { $x >= ($sx-3) && $x <= ($sx+3) } {
1980                    $marker set_relative_value [$m get_relative_value]
1981                    itcl::delete object $m
1982                    bell
1983                    set bool 1
1984                    continue
1985                }
1986            }
1987            lappend list $m
1988        }
1989        set _isomarkers($dataobj) $list
1990        update_transfer_function
1991    }
1992    return $bool
1993}
1994
1995itcl::body Rappture::NanovisViewer::over_isomarker { marker x } {
1996    set dataobj [lindex [get] 0]
1997    if {"" == $dataobj} {
1998        return ""
1999    }
2000    if { [info exists _isomarkers($dataobj)] } {
2001        set marker [namespace tail $marker]
2002        foreach m $_isomarkers($dataobj) {
2003            set sx [$m get_screen_position]
2004            if { $m != $marker } {
2005                set bool [expr { $x >= ($sx-3) && $x <= ($sx+3) }]
2006                $m activate $bool
2007            }
2008        }
2009    }
2010    return ""
2011}
Note: See TracBrowser for help on using the repository browser.