source: branches/1.3/gui/scripts/deviceLayout1D.tcl @ 4790

Last change on this file since 4790 was 3330, checked in by gah, 11 years ago

merge (by hand) with Rappture1.2 branch

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