source: trunk/gui/scripts/deviceLayout1D.tcl @ 6

Last change on this file since 6 was 6, checked in by mmc, 19 years ago

Fixed the Tcl library to mirror the API developed for XML
libraries on the Python side. The Tcl Rappture::library
now has methods like "children", "element", "put", etc.
One difference: On the Tcl side, the default -flavor for
element/children is "component", since that works better
in Tcl code. In Python, the default is flavor=object.

Also fixed the Tcl install script to install not just
the tcl/scripts library, but also the ../gui and ../lib
directories.

File size: 20.5 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: deviceLayout1D - visualizer for 1D device geometries
3#
4#  This widget is a simple visualizer for the layout of 1D devices.
5#  It takes the Rappture XML representation for a 1D device and draws
6#  the series of slabs representing the various material layers in the
7#  device.  It can be configured to allow simple selection and editing
8#  of material layers.
9# ======================================================================
10#  AUTHOR:  Michael McLennan, Purdue University
11#  Copyright (c) 2004  Purdue Research Foundation, West Lafayette, IN
12# ======================================================================
13package require Itk
14package require BLT
15
16option add *DeviceLayout1D.width 4i widgetDefault
17option add *DeviceLayout1D.deviceSize 0.25i widgetDefault
18option add *DeviceLayout1D.deviceOutline black widgetDefault
19option add *DeviceLayout1D.annotate all widgetDefault
20option add *DeviceLayout1D.font \
21    -*-helvetica-medium-r-normal-*-*-120-* widgetDefault
22
23itcl::class Rappture::DeviceLayout1D {
24    inherit itk::Widget
25
26    itk_option define -font font Font ""
27    itk_option define -library library Library ""
28    itk_option define -device device Device ""
29    itk_option define -devicesize deviceSize DeviceSize 0
30    itk_option define -deviceoutline deviceOutline DeviceOutline ""
31    itk_option define -leftmargin leftMargin LeftMargin 0
32    itk_option define -rightmargin rightMargin RightMargin 0
33
34    constructor {args} { # defined below }
35    public method limits {}
36    public method extents {what}
37    public method controls {option args}
38
39    protected method _layout {}
40    protected method _redraw {}
41    protected method _drawLayer {index x0 x1}
42    protected method _drawMolecule {index x0 x1}
43    protected method _drawAnnotation {index x0 x1}
44    protected method _mater2color {mater}
45
46    private variable _dispatcher "" ;# dispatcher for !events
47    private variable _sizes         ;# maps size name => pixels
48
49    private variable _library ""    ;# LibraryObj for library information
50    private variable _device ""     ;# LibraryObj for device representation
51    private variable _slabs ""      ;# list of node names for slabs in device
52    private variable _z0 ""         ;# list parallel to _slabs with z0
53                                    ;#   coord for lhs of each slab
54    private variable _zthick ""     ;# list parallel to _slabs with thickness
55                                    ;#   for each slab
56    private variable _maters ""     ;# list parallel to _slabs with material
57                                    ;#   for each slab
58
59    private variable _controls      ;# maps control path => status on/off
60
61    private common _icons
62    set _icons(molecule) [image create photo -file \
63        [file join $Rappture::installdir scripts images molecule.gif]]
64}
65                                                                               
66itk::usual DeviceLayout1D {
67    keep -background -cursor
68    keep -library -device
69    keep -deviceoutline -devicesize
70    keep -selectbackground -selectforeground -selectborderwidth
71    keep -width
72}
73
74# ----------------------------------------------------------------------
75# CONSTRUCTOR
76# ----------------------------------------------------------------------
77itcl::body Rappture::DeviceLayout1D::constructor {args} {
78    Rappture::dispatcher _dispatcher
79    $_dispatcher register !layout
80    $_dispatcher dispatch $this !layout "[itcl::code $this _layout]; list"
81    $_dispatcher register !redraw
82    $_dispatcher dispatch $this !redraw "[itcl::code $this _redraw]; list"
83
84    itk_option add hull.width
85    pack propagate $itk_component(hull) no
86
87    itk_component add area {
88        canvas $itk_interior.area -borderwidth 0 -highlightthickness 0
89    } {
90        usual
91        ignore -borderwidth -relief
92        ignore -highlightthickness -highlightbackground -highlightcolor
93    }
94    pack $itk_component(area) -fill both
95    bind $itk_component(area) <Configure> \
96        [list $_dispatcher event -idle !redraw]
97
98    eval itk_initialize $args
99}
100
101# ----------------------------------------------------------------------
102# USAGE: limits
103#
104# Clients use this to query the limits of the x-axis for the
105# current device.  Returns the min/max limits indicating the
106# physical size of the device.
107# ----------------------------------------------------------------------
108itcl::body Rappture::DeviceLayout1D::limits {} {
109    if {[$_dispatcher ispending !layout]} {
110        $_dispatcher cancel !layout
111        _layout
112    }
113    set zmin [lindex $_z0 0]
114    set zmax [lindex $_z0 end]
115    return [list $zmin $zmax]
116}
117
118# ----------------------------------------------------------------------
119# USAGE: extents <what>
120#
121# Clients use this to query the size of various things within this
122# widget--similar to the "extents" method of the BLT graph.  Returns
123# the size of some item in pixels.
124# ----------------------------------------------------------------------
125itcl::body Rappture::DeviceLayout1D::extents {what} {
126    switch -- $what {
127        bar3D { return $_sizes(bar45) }
128        default {
129            error "bad option \"$what\": should be bar3D"
130        }
131    }
132}
133
134# ----------------------------------------------------------------------
135# USAGE: controls add <path>
136#
137# Clients use this to add hints about the controls that should be
138# added to the layout area.  Common paths are recipe.slab#.material
139# and recipe.slab#.thickness.
140# ----------------------------------------------------------------------
141itcl::body Rappture::DeviceLayout1D::controls {option args} {
142    switch -- $option {
143        add {
144            if {[llength $args] != 1} {
145                error "wrong # args: should be \"controls add path\""
146            }
147            set path [lindex $args 0]
148            set _controls($path) 1
149            $_dispatcher event -idle !layout
150        }
151        remove {
152            error "not yet implemented"
153        }
154    }
155}
156
157# ----------------------------------------------------------------------
158# USAGE: _layout
159#
160# Called automatically whenever something changes that affects the
161# layout of the widget.  Recalculates the layout and computes a good
162# overall value for the minimum height of the widget.  This may cause
163# the widget to change size, which in turn would trigger another
164# _redraw.
165# ----------------------------------------------------------------------
166itcl::body Rappture::DeviceLayout1D::_layout {} {
167    # first, recompute the overall height of this widget
168    set h [expr {$_sizes(bar)+$_sizes(bar45)+20}]
169
170    set fnt $itk_option(-font)
171    if {[regexp {\.material} [array names _controls]]} {
172        # one of the slabs has its material displayed
173        set extra [expr {1.2*[font metrics $fnt -linespace]}]
174        set h [expr {$h+$extra}]
175    }
176    if {[regexp {\.thickness} [array names _controls]]} {
177        # one of the slabs has its thickness displayed
178        set extra [expr {1.2*[font metrics $fnt -linespace]}]
179        set h [expr {$h+$extra}]
180    }
181
182    # see if any of the slabs has a label
183    if {$_device != ""} {
184        foreach nn [$_device children recipe] {
185            if {"" != [$_device get recipe.$nn.label]} {
186                set extra [expr {1.2*[font metrics $fnt -linespace]}]
187                set h [expr {$h+$extra}]
188                break
189            }
190        }
191    }
192
193    # a little extra height for the molecule image
194    if {"" != [$_device element recipe.molecule]} {
195        set h [expr {$h+15}]
196    }
197
198    set oldh [component hull cget -height]
199    if {$h != $oldh} {
200        component hull configure -height $h
201        $_dispatcher event -idle !redraw
202    }
203
204    # next, scan through the device and compute layer positions
205    set slabs ""
206    set z0 ""
207    set zthick ""
208    set maters ""
209
210    set z 0
211    if {$_device != ""} {
212        foreach nn [$_device children recipe] {
213            switch -glob -- $nn {
214                slab* - molecule* {
215                    set tval [$_device get recipe.$nn.thickness]
216                    set tval [Rappture::Units::convert $tval \
217                        -context um -to um -units off]
218                    lappend slabs recipe.$nn
219                    lappend z0 $z
220                    lappend zthick $tval
221                    lappend maters [$_device get recipe.$nn.material]
222
223                    set z [expr {$z+$tval}]
224                }
225                default {
226                    # element not recognized -- skip it
227                }
228            }
229        }
230    }
231    lappend z0 $z
232
233    # something change? then store new layout and redraw
234    if {![string equal $z0 $_z0]
235          || ![string equal $zthick $_zthick]
236          || ![string equal $maters $_maters]} {
237        set _slabs $slabs
238        set _z0 $z0
239        set _zthick $zthick
240        set _maters $maters
241
242        $_dispatcher event -idle !redraw
243    }
244}
245
246# ----------------------------------------------------------------------
247# USAGE: _redraw
248#
249# Called automatically whenever the device geometry changes, or when
250# the canvas changes size, to redraw all elements within it.
251# ----------------------------------------------------------------------
252itcl::body Rappture::DeviceLayout1D::_redraw {} {
253    set c $itk_component(area)
254
255    # clean up images and delete all other items
256    foreach item [$c find withtag image] {
257        image delete [$c itemcget $item -image]
258    }
259    $c delete all
260
261    set w [expr {[winfo width $c]-1 - $_sizes(lm) - $_sizes(rm)}]
262
263    set x0 $_sizes(lm)
264    set x1 [expr {$x0 + $w}]
265
266    set zmax [lindex $_z0 end]
267    set xx0 $x0
268    set xx1 $x1
269
270    set drewslab 0
271    for {set i 0} {$i < [llength $_slabs]} {incr i} {
272        set name [lindex $_slabs $i]
273        if {[regexp {slab[0-9]*$} $name]} {
274            set z0 [lindex $_z0 $i]
275            set zthick [lindex $_zthick $i]
276            set xx0 [expr {double($z0)/$zmax * ($x1-$x0) + $x0}]
277            set xx1 [expr {double($z0+$zthick)/$zmax * ($x1-$x0) + $x0}]
278            _drawLayer $i $xx0 $xx1
279            _drawAnnotation $i $xx0 $xx1
280            set drewslab 1
281        } else {
282            if {$drewslab} {
283                _drawLayer cap $xx0 $xx1  ;# draw the end cap
284                set drewslab 0
285            }
286            if {[regexp {molecule[0-9]*$} $name]} {
287                set z0 [lindex $_z0 $i]
288                set zthick [lindex $_zthick $i]
289                set xx0 [expr {double($z0)/$zmax * ($x1-$x0) + $x0}]
290                set xx1 [expr {double($z0+$zthick)/$zmax * ($x1-$x0) + $x0}]
291                _drawMolecule $i $xx0 $xx1
292                _drawAnnotation $i $xx0 $xx1
293            }
294        }
295    }
296    if {[llength $_slabs] > 0} {
297        _drawLayer cap $xx0 $xx1  ;# draw the end cap
298    }
299}
300
301# ----------------------------------------------------------------------
302# USAGE: _drawLayer <index> <x0> <x1>
303#
304# Used internally within _redraw to draw one material layer at the
305# <index> within the slab list into the active area.  The layer is
306# drawn between coordinates <x0> and <x1> on the canvas.
307# ----------------------------------------------------------------------
308itcl::body Rappture::DeviceLayout1D::_drawLayer {index x0 x1} {
309    set c $itk_component(area)
310    set h [expr {[winfo height $c]-1}]
311    # a little extra height for the molecule image
312    if {"" != [$_device element recipe.molecule]} {
313        set h [expr {$h-15}]
314    }
315
316    set y0 $h
317    set y0p [expr {$y0-$_sizes(bar45)}]
318    set y1p [expr {$y0-$_sizes(bar)}]
319    set y1 [expr {$y1p-$_sizes(bar45)}]
320
321    set x0p [expr {$x0+$_sizes(bar45)}]
322    set x1p [expr {$x1+$_sizes(bar45)}]
323
324    set lcolor $itk_option(-deviceoutline)
325
326    if {$index == "cap"} {
327        #
328        # Draw the outline around the end cap
329        #
330        $c create line $x1 $y0  $x1 $y1p  $x1p $y1 -fill $lcolor
331
332    } elseif {$index < [llength $_slabs]} {
333        set fcolor [_mater2color [lindex $_maters $index]]
334
335        #
336        # Draw one segment of the bar in the canvas:
337        #
338        #      ___________________  ____ y1
339        #     /                  /| ____ y0p
340        #    /__________________/ / ____ y1p
341        #    |__________________|/: ____ y0
342        #    : :                : :
343        #   x0 x0p             x1 x1p
344        #
345        $c create polygon \
346            $x0 $y0  $x1 $y0  $x1p $y0p  $x1p $y1  $x0p $y1  $x0 $y1p  $x0 $y0 \
347            -outline $lcolor -fill $fcolor
348        $c create line $x0 $y1p  $x1 $y1p -fill $lcolor
349    }
350}
351
352# ----------------------------------------------------------------------
353# USAGE: _drawMolecule <index> <x0> <x1>
354#
355# Used internally within _redraw to draw one molecule layer at the
356# <index> within the slab list into the active area.  The layer is
357# drawn between coordinates <x0> and <x1> on the canvas.
358# ----------------------------------------------------------------------
359itcl::body Rappture::DeviceLayout1D::_drawMolecule {index x0 x1} {
360    set c $itk_component(area)
361    set h [expr {[winfo height $c]-1}]
362    # a little extra height for the molecule image
363    if {"" != [$_device element recipe.molecule]} {
364        set h [expr {$h-15}]
365    }
366
367    set y0 $h
368    set y0p [expr {$y0-$_sizes(bar45)}]
369    set y1p [expr {$y0-$_sizes(bar)}]
370    set y1 [expr {$y1p-$_sizes(bar45)}]
371    set x0p [expr {$x0+$_sizes(bar45)}]
372
373    set x [expr {0.5*($x0+$x0p)}]
374    set y [expr {0.5*($y0+$y0p) + 0.5*($y1-$y0p)}]
375
376    set w [image width $_icons(molecule)]
377    set h [image height $_icons(molecule)]
378    set dx [expr {round($x1-$x0)}]
379    set dy [expr {round(double($x1-$x0)/$w*$h)}]
380    set imh [image create photo -width $dx -height $dy]
381    blt::winop resample $_icons(molecule) $imh
382
383    $c create image $x $y -anchor w -image $imh -tags image
384}
385
386# ----------------------------------------------------------------------
387# USAGE: _drawAnnotation <index> <x0> <x1>
388#
389# Used internally within _redraw to draw one material layer at the
390# <index> within the slab list into the active area.  The layer is
391# drawn between coordinates <x0> and <x1> on the canvas.
392# ----------------------------------------------------------------------
393itcl::body Rappture::DeviceLayout1D::_drawAnnotation {index x0 x1} {
394    set c $itk_component(area)
395    set h [expr {[winfo height $c]-1}]
396    # a little extra height for the molecule image
397    if {"" != [$_device element recipe.molecule]} {
398        set h [expr {$h-15}]
399    }
400
401    set y0 $h
402    set y1 [expr {$y0-$_sizes(bar)-$_sizes(bar45)}]
403
404    set x0p [expr {$x0+$_sizes(bar45)}]
405    set x1p [expr {$x1+$_sizes(bar45)}]
406    set xmid [expr {0.5*($x0p+$x1p)}]
407
408    set fnt $itk_option(-font)
409    set lh [font metrics $fnt -linespace]
410    set ymid [expr {$y1-2-0.5*$lh}]
411    set y [expr {$y1-2}]
412
413    #
414    # If there's a .thickness control for this slab, draw it here.
415    #
416    set elem [lindex $_slabs $index]
417    set path "device.$elem.thickness"
418    if {[info exists _controls($path)] && $_controls($path)} {
419        set zthick [lindex $_zthick $index]
420        set zthick [Rappture::Units::convert $zthick -context um -to um]
421
422        $c create line $x0p $y $x0p [expr {$y-$lh}]
423        $c create line $x1p $y $x1p [expr {$y-$lh}]
424
425        $c create line $x0p $ymid $x1p $ymid -arrow both
426        $c create text $xmid [expr {$ymid-2}] -anchor s -text $zthick
427        set y [expr {$y-2.0*$lh}]
428    }
429
430    #
431    # If there's a .material control for this slab, draw it here.
432    #
433    set elem [lindex $_slabs $index]
434    set path "device.$elem.material"
435    if {[info exists _controls($path)] && $_controls($path)} {
436        set mater [lindex $_maters $index]
437        set w [expr {12+[font measure $fnt $mater]}]
438        set x [expr {$x1p - 0.5*($x1p-$x0p-$w)}]
439        $c create rectangle [expr {$x-10}] [expr {$y-10}] \
440            $x $y -outline black -fill [_mater2color $mater]
441        $c create text [expr {$x-12}] [expr {$y-5}] -anchor e \
442            -text $mater
443        set y [expr {$y-1.2*$lh}]
444    }
445
446    #
447    # If there's a <label> for this layer, then draw it.
448    #
449    if {"" != $_device} {
450        set label [$_device get $elem.label]
451        if {"" != $label} {
452            set y [expr {$y-0.5*$lh}]
453            $c create text [expr {0.5*($x0p+$x1p)}] $y -anchor s \
454                -text $label
455        }
456    }
457}
458
459# ----------------------------------------------------------------------
460# USAGE: _mater2color <material>
461#
462# Used internally to convert a <material> name to the color that
463# should be used to represent it in the viewer.
464# ----------------------------------------------------------------------
465itcl::body Rappture::DeviceLayout1D::_mater2color {mater} {
466    if {$_library != ""} {
467        set color [$_library get materials.$mater.color]
468        if {$color != ""} {
469            return $color
470        }
471    }
472    return gray
473}
474
475# ----------------------------------------------------------------------
476# CONFIGURATION OPTION: -font
477#
478# Controls the font used to all text on the layout, including
479# annotations.
480# ----------------------------------------------------------------------
481itcl::configbody Rappture::DeviceLayout1D::font {
482    $_dispatcher event -idle !layout
483}
484
485# ----------------------------------------------------------------------
486# CONFIGURATION OPTION: -library
487#
488# Set to the Rappture::Library object representing the library with
489# material properties and other info.
490# ----------------------------------------------------------------------
491itcl::configbody Rappture::DeviceLayout1D::library {
492    if {$itk_option(-library) != ""} {
493        if {![Rappture::library isvalid $itk_option(-library)]} {
494            error "bad value \"$itk_option(-library)\": should be Rappture::Library"
495        }
496    }
497    set _library $itk_option(-library)
498    $_dispatcher event -idle !redraw
499}
500
501# ----------------------------------------------------------------------
502# CONFIGURATION OPTION: -device
503#
504# Set to the Rappture::Library object representing the device being
505# displayed in the viewer.  If set to "", the viewer is cleared to
506# display nothing.
507# ----------------------------------------------------------------------
508itcl::configbody Rappture::DeviceLayout1D::device {
509    if {$itk_option(-device) != ""} {
510        if {![Rappture::library isvalid $itk_option(-device)]} {
511            error "bad value \"$itk_option(-device)\": should be Rappture::Library"
512        }
513    }
514    set _device $itk_option(-device)
515    $_dispatcher event -idle !redraw
516}
517
518# ----------------------------------------------------------------------
519# CONFIGURATION OPTION: -devicesize
520#
521# Sets the height of the bar representing the device area.
522# ----------------------------------------------------------------------
523itcl::configbody Rappture::DeviceLayout1D::devicesize {
524    if {[catch {
525          winfo pixels $itk_component(hull) $itk_option(-devicesize)
526      } pixels]} {
527        error "bad screen distance \"$itk_option(-devicesize)\""
528    }
529    set _sizes(bar) $pixels
530    set _sizes(bar45) [expr {0.5*sqrt(2)*$pixels}]
531
532    set pixels [winfo pixels $itk_component(hull) $itk_option(-rightmargin)]
533    set _sizes(rm) [expr {($pixels > 0) ? $pixels : $_sizes(bar45)}]
534
535    $_dispatcher event -idle !layout
536}
537
538# ----------------------------------------------------------------------
539# CONFIGURATION OPTION: -deviceoutline
540#
541# Sets the outline color around the various slabs in the device.
542# ----------------------------------------------------------------------
543itcl::configbody Rappture::DeviceLayout1D::deviceoutline {
544    $_dispatcher event -idle !redraw
545}
546
547# ----------------------------------------------------------------------
548# CONFIGURATION OPTION: -leftmargin
549#
550# Sets the width of the left margin, a blank area along the left
551# side.  Setting the margin allows the device layout to line up
552# with a graph of internal fields.
553# ----------------------------------------------------------------------
554itcl::configbody Rappture::DeviceLayout1D::leftmargin {
555    if {[catch {
556          winfo pixels $itk_component(hull) $itk_option(-leftmargin)
557      } pixels]} {
558        error "bad screen distance \"$itk_option(-leftmargin)\""
559    }
560    set _sizes(lm) $pixels
561    $_dispatcher event -idle !layout
562    $_dispatcher event -idle !redraw
563}
564
565# ----------------------------------------------------------------------
566# CONFIGURATION OPTION: -rightmargin
567#
568# Sets the width of the right margin, a blank area along the right
569# side.  Setting the margin allows the device layout to line up
570# with a graph of internal fields.
571# ----------------------------------------------------------------------
572itcl::configbody Rappture::DeviceLayout1D::rightmargin {
573    if {[catch {
574          winfo pixels $itk_component(hull) $itk_option(-rightmargin)
575      } pixels]} {
576        error "bad screen distance \"$itk_option(-rightmargin)\""
577    }
578    set _sizes(rm) [expr {($pixels > 0) ? $pixels : $_sizes(bar45)}]
579    $_dispatcher event -idle !layout
580    $_dispatcher event -idle !redraw
581}
Note: See TracBrowser for help on using the repository browser.