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

Last change on this file since 1228 was 1228, checked in by gah, 15 years ago

Fixes for parallel makes

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