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

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

Fixed the device viewer to resize itself properly based on its
contents. The device layout takes into account the size of any
icon for a material layer, so a picture of a device can be embedded
by creating a single layer with an icon.

Fixed the Gauge to resize its icon area properly when the -spectrum
is configured later on.

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