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

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