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

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