| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #  You may distribute under the terms of either the GNU General Public License | 
| 2 |  |  |  |  |  |  | #  or the Artistic License (the same terms as Perl itself) | 
| 3 |  |  |  |  |  |  | # | 
| 4 |  |  |  |  |  |  | #  (C) Paul Evans, 2013-2014 -- leonerd@leonerd.org.uk | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | package Tickit::Widget::SegmentDisplay; | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 1 |  |  | 1 |  | 508 | use strict; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 32 |  | 
| 9 | 1 |  |  | 1 |  | 4 | use warnings; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 24 |  | 
| 10 | 1 |  |  | 1 |  | 22 | use 5.010; # // | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 26 |  | 
| 11 | 1 |  |  | 1 |  | 3 | use base qw( Tickit::Widget ); | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 290 |  | 
| 12 | 1 |  |  | 1 |  | 162 | use Tickit::Style; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | use utf8; | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | our $VERSION = '0.03'; | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | use Carp; | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | # The 7 segments are | 
| 21 |  |  |  |  |  |  | #  AAA | 
| 22 |  |  |  |  |  |  | # F   B | 
| 23 |  |  |  |  |  |  | # F   B | 
| 24 |  |  |  |  |  |  | #  GGG | 
| 25 |  |  |  |  |  |  | # E   C | 
| 26 |  |  |  |  |  |  | # E   C | 
| 27 |  |  |  |  |  |  | #  DDD | 
| 28 |  |  |  |  |  |  | # | 
| 29 |  |  |  |  |  |  | # B,C,E,F == 2cols wide | 
| 30 |  |  |  |  |  |  | # A,D,G   == 1line tall | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | =encoding UTF-8 | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | =head1 NAME | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | C - show a single character like a segmented display | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | This class provides a widget that immitates a segmented LED or LCD display. It | 
| 41 |  |  |  |  |  |  | shows a single character by lighting or shading fixed rectangular bars. | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | =head1 STYLE | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | The default style pen is used as the widget pen, though only the background | 
| 46 |  |  |  |  |  |  | colour will actually matter as the widget does not directly display text. | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | The following style keys are used: | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | =over 4 | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | =item lit => COLOUR | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | =item unlit => COLOUR | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | Colour descriptions (index or name) for the lit and unlight segments of the | 
| 57 |  |  |  |  |  |  | display. | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | =back | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | =cut | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | style_definition base => | 
| 64 |  |  |  |  |  |  | lit => "red", | 
| 65 |  |  |  |  |  |  | unlit => 16+36; | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | use constant WIDGET_PEN_FROM_STYLE => 1; | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | =head1 CONSTRUCTOR | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | =cut | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | =head2 $segmentdisplay = Tickit::Widget::SegmentDisplay->new( %args ) | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | Constructs a new C object. | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | Takes the following named arguments | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | =over 8 | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | =item value => STR | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | Sets an initial value. | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | =item type => STR | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | The type of display. Supported types are: | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | =over 4 | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | =item seven | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | A 7-segment bar display | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | =item seven_dp | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | A 7-segment bar display with decimal-point. To light the decimal point, append | 
| 98 |  |  |  |  |  |  | the value with ".". | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | =item colon | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | A static C<:> | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | =item symb | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | A unit or prefix symbol character. The following characters are recognised | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | V A W Ω | 
| 109 |  |  |  |  |  |  | M k m µ | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | Each will be drawn in a style approximately to fit the general LED shape | 
| 112 |  |  |  |  |  |  | display, by drawing lines of erased cells. Note however that some more | 
| 113 |  |  |  |  |  |  | intricate shapes may not be very visible on smaller scales. | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | =back | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | =back | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | =cut | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | my %types = ( | 
| 122 |  |  |  |  |  |  | seven    => [qw( 7 )], | 
| 123 |  |  |  |  |  |  | seven_dp => [qw( 7. )], | 
| 124 |  |  |  |  |  |  | colon    => [qw( : )], | 
| 125 |  |  |  |  |  |  | symb     => [], | 
| 126 |  |  |  |  |  |  | ); | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | sub new | 
| 129 |  |  |  |  |  |  | { | 
| 130 |  |  |  |  |  |  | my $class = shift; | 
| 131 |  |  |  |  |  |  | my %args = @_; | 
| 132 |  |  |  |  |  |  | my $self = $class->SUPER::new( %args ); | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | my $type = $args{type} // "seven"; | 
| 135 |  |  |  |  |  |  | my $method; | 
| 136 |  |  |  |  |  |  | foreach my $typename ( keys %types ) { | 
| 137 |  |  |  |  |  |  | $type eq $typename and $method = $typename, last; | 
| 138 |  |  |  |  |  |  | $type eq $_ and $method = $typename, last for @{ $types{$typename} }; | 
| 139 |  |  |  |  |  |  | } | 
| 140 |  |  |  |  |  |  | defined $method or croak "Unrecognised type name '$type'"; | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | $self->{reshape_method} = $self->can( "reshape_${method}" ); | 
| 143 |  |  |  |  |  |  | $self->{render_method}  = $self->can( "render_${method}_to_rb" ); | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | $self->{value} = $args{value} // ""; | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | $self->on_style_changed_values( | 
| 148 |  |  |  |  |  |  | lit   => [ undef, $self->get_style_values( "lit" ) ], | 
| 149 |  |  |  |  |  |  | unlit => [ undef, $self->get_style_values( "unlit" ) ], | 
| 150 |  |  |  |  |  |  | ); | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | return $self; | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | # ADG + atleast 1 line each for FB and EC | 
| 156 |  |  |  |  |  |  | sub lines { 3 + 2 } | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | # FE, BC + atleast 2 columns for AGD | 
| 159 |  |  |  |  |  |  | sub cols  { 4 + 2 } | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | =head1 ACCESSORS | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | =cut | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | =head2 $value = $segmentdisplay->value | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | =head2 $segmentdisplay->set_value( $value ) | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | Return or set the character on display | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | =cut | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | sub value | 
| 174 |  |  |  |  |  |  | { | 
| 175 |  |  |  |  |  |  | my $self = shift; | 
| 176 |  |  |  |  |  |  | return $self->{value}; | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | sub set_value | 
| 180 |  |  |  |  |  |  | { | 
| 181 |  |  |  |  |  |  | my $self = shift; | 
| 182 |  |  |  |  |  |  | ( $self->{value} ) = @_; | 
| 183 |  |  |  |  |  |  | $self->redraw; | 
| 184 |  |  |  |  |  |  | } | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | sub on_style_changed_values | 
| 187 |  |  |  |  |  |  | { | 
| 188 |  |  |  |  |  |  | my $self = shift; | 
| 189 |  |  |  |  |  |  | my %values = @_; | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | $self->{lit_pen}   = Tickit::Pen::Immutable->new( bg => $values{lit}[1]   ) if $values{lit}; | 
| 192 |  |  |  |  |  |  | $self->{unlit_pen} = Tickit::Pen::Immutable->new( bg => $values{unlit}[1] ) if $values{unlit}; | 
| 193 |  |  |  |  |  |  | } | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | sub reshape | 
| 196 |  |  |  |  |  |  | { | 
| 197 |  |  |  |  |  |  | my $self = shift; | 
| 198 |  |  |  |  |  |  | my $win = $self->window or return; | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | $self->{reshape_method}->( $self, $win->lines, $win->cols, 0, 0 ); | 
| 201 |  |  |  |  |  |  | } | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | sub render_to_rb | 
| 204 |  |  |  |  |  |  | { | 
| 205 |  |  |  |  |  |  | my $self = shift; | 
| 206 |  |  |  |  |  |  | my ( $rb, $rect ) = @_; | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | $rb->eraserect( $rect ); | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | $self->{render_method}->( $self, $rb, $rect ); | 
| 211 |  |  |  |  |  |  | } | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | # 7-Segment | 
| 214 |  |  |  |  |  |  | my %segments = ( | 
| 215 |  |  |  |  |  |  | 0 => "ABCDEF ", | 
| 216 |  |  |  |  |  |  | 1 => " BC    ", | 
| 217 |  |  |  |  |  |  | 2 => "AB DE G", | 
| 218 |  |  |  |  |  |  | 3 => "ABCD  G", | 
| 219 |  |  |  |  |  |  | 4 => " BC  FG", | 
| 220 |  |  |  |  |  |  | 5 => "A CD FG", | 
| 221 |  |  |  |  |  |  | 6 => "A CDEFG", | 
| 222 |  |  |  |  |  |  | 7 => "ABC    ", | 
| 223 |  |  |  |  |  |  | 8 => "ABCDEFG", | 
| 224 |  |  |  |  |  |  | 9 => "ABCD FG", | 
| 225 |  |  |  |  |  |  | ); | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | sub _pen_for_seg | 
| 228 |  |  |  |  |  |  | { | 
| 229 |  |  |  |  |  |  | my $self = shift; | 
| 230 |  |  |  |  |  |  | my ( $segment ) = @_; | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | my $segments = $segments{$self->value} or return $self->{unlit_pen}; | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | my $lit = substr( $segments, ord($segment) - ord("A"), 1 ) ne " "; | 
| 235 |  |  |  |  |  |  | return $lit ? $self->{lit_pen} : $self->{unlit_pen}; | 
| 236 |  |  |  |  |  |  | } | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | sub reshape_seven | 
| 239 |  |  |  |  |  |  | { | 
| 240 |  |  |  |  |  |  | my $self = shift; | 
| 241 |  |  |  |  |  |  | my ( $lines, $cols, $top, $left ) = @_; | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | $self->{AGD_col}   = $left + 2; | 
| 244 |  |  |  |  |  |  | $self->{AGD_width} = $cols - 4; | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | $self->{FE_col} = $left; | 
| 247 |  |  |  |  |  |  | $self->{BC_col} = $left + $cols - 2; | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | $self->{A_line} = $top; | 
| 250 |  |  |  |  |  |  | $self->{G_line} = $top + int( ( $lines - 1 + 0.5 ) / 2 ); | 
| 251 |  |  |  |  |  |  | $self->{D_line} = $top + $lines - 1; | 
| 252 |  |  |  |  |  |  | } | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | sub render_seven_to_rb | 
| 255 |  |  |  |  |  |  | { | 
| 256 |  |  |  |  |  |  | my $self = shift; | 
| 257 |  |  |  |  |  |  | my ( $rb ) = @_; | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | $rb->erase_at( $self->{A_line}, $self->{AGD_col}, $self->{AGD_width}, $self->_pen_for_seg( "A" ) ); | 
| 260 |  |  |  |  |  |  | $rb->erase_at( $self->{G_line}, $self->{AGD_col}, $self->{AGD_width}, $self->_pen_for_seg( "G" ) ); | 
| 261 |  |  |  |  |  |  | $rb->erase_at( $self->{D_line}, $self->{AGD_col}, $self->{AGD_width}, $self->_pen_for_seg( "D" ) ); | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | my ( $F_pen, $B_pen ) = ( $self->_pen_for_seg( "F" ), $self->_pen_for_seg( "B" ) ); | 
| 264 |  |  |  |  |  |  | foreach my $line ( $self->{A_line}+1 .. $self->{G_line}-1 ) { | 
| 265 |  |  |  |  |  |  | $rb->erase_at( $line, $self->{FE_col}, 2, $F_pen ); | 
| 266 |  |  |  |  |  |  | $rb->erase_at( $line, $self->{BC_col}, 2, $B_pen ); | 
| 267 |  |  |  |  |  |  | } | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | my ( $E_pen, $C_pen ) = ( $self->_pen_for_seg( "E" ), $self->_pen_for_seg( "C" ) ); | 
| 270 |  |  |  |  |  |  | foreach my $line ( $self->{G_line}+1 .. $self->{D_line}-1 ) { | 
| 271 |  |  |  |  |  |  | $rb->erase_at( $line, $self->{FE_col}, 2, $E_pen ); | 
| 272 |  |  |  |  |  |  | $rb->erase_at( $line, $self->{BC_col}, 2, $C_pen ); | 
| 273 |  |  |  |  |  |  | } | 
| 274 |  |  |  |  |  |  | } | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | # 7-Segment with DP | 
| 277 |  |  |  |  |  |  | sub reshape_seven_dp | 
| 278 |  |  |  |  |  |  | { | 
| 279 |  |  |  |  |  |  | my $self = shift; | 
| 280 |  |  |  |  |  |  | my ( $lines, $cols, $top, $left ) = @_; | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | $self->reshape_seven( $lines, $cols - 2, $top, $left ); | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | $self->{DP_line} = $top  + $lines - 1; | 
| 285 |  |  |  |  |  |  | $self->{DP_col}  = $left + $cols  - 2; | 
| 286 |  |  |  |  |  |  | } | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | sub render_seven_dp_to_rb | 
| 289 |  |  |  |  |  |  | { | 
| 290 |  |  |  |  |  |  | my $self = shift; | 
| 291 |  |  |  |  |  |  | my ( $rb ) = @_; | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | my $value = $self->{value}; | 
| 294 |  |  |  |  |  |  | my $dp; | 
| 295 |  |  |  |  |  |  | local $self->{value}; | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | if( $value =~ m/^(\d?)(\.?)/ ) { | 
| 298 |  |  |  |  |  |  | $self->{value} = $1; | 
| 299 |  |  |  |  |  |  | $dp = length $2; | 
| 300 |  |  |  |  |  |  | } | 
| 301 |  |  |  |  |  |  | else { | 
| 302 |  |  |  |  |  |  | $self->{value} = $value; | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | $self->render_seven_to_rb( $rb ); | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | my $dp_pen = $dp ? $self->{lit_pen} : $self->{unlit_pen}; | 
| 308 |  |  |  |  |  |  | $rb->erase_at( $self->{DP_line}, $self->{DP_col}, 2, $dp_pen ); | 
| 309 |  |  |  |  |  |  | } | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | # Static double-dot colon | 
| 312 |  |  |  |  |  |  | sub reshape_colon | 
| 313 |  |  |  |  |  |  | { | 
| 314 |  |  |  |  |  |  | my $self = shift; | 
| 315 |  |  |  |  |  |  | my ( $lines, $cols, $top, $left ) = @_; | 
| 316 |  |  |  |  |  |  | my $bottom = $top + $lines - 1; | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | $self->{colon_col} = 2 + int( ( $cols - 4 ) / 2 ); | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | my $ofs = int( ( $lines - 1 + 0.5 ) / 4 ); | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | $self->{A_line} = $top    + $ofs; | 
| 323 |  |  |  |  |  |  | $self->{B_line} = $bottom - $ofs; | 
| 324 |  |  |  |  |  |  | } | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | sub render_colon_to_rb | 
| 327 |  |  |  |  |  |  | { | 
| 328 |  |  |  |  |  |  | my $self = shift; | 
| 329 |  |  |  |  |  |  | my ( $rb ) = @_; | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | my $col = $self->{colon_col}; | 
| 332 |  |  |  |  |  |  | $rb->erase_at( $self->{A_line}, $col, 2, $self->{lit_pen} ); | 
| 333 |  |  |  |  |  |  | $rb->erase_at( $self->{B_line}, $col, 2, $self->{lit_pen} ); | 
| 334 |  |  |  |  |  |  | } | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | # Symbol drawing | 
| 337 |  |  |  |  |  |  | # | 
| 338 |  |  |  |  |  |  | # Each symbol is drawn as a series of erase calls on the RB to draw 'lines'. | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | my %symbol_strokes = do { | 
| 341 |  |  |  |  |  |  | no warnings 'qw'; # Quiet the 'Possible attempt to separate words with commas' warning | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | # Letters likely to be used for units | 
| 344 |  |  |  |  |  |  | V => [ [qw( 0,0 50,100 100,0 )] ], | 
| 345 |  |  |  |  |  |  | A => [ [qw( 0,100 50,0 100,100 )], [qw( 20,70 80,70)] ], | 
| 346 |  |  |  |  |  |  | W => [ [qw( 0,0 25,100 50,50 75,100 100,0)] ], | 
| 347 |  |  |  |  |  |  | Ω => [ [qw( 0,100 25,100 25,75 10,60 0,50 0,20 20,0 80,0 100,20 100,50 90,60 75,75 75,100 100,100 ) ] ], | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | # Symbols likely to be used as SI prefixes | 
| 350 |  |  |  |  |  |  | M => [ [qw( 0,100 0,0 50,50 100,0 100,100 )] ], | 
| 351 |  |  |  |  |  |  | k => [ [qw( 10,0 10,100 )], [qw( 90,40 10,70 90,100 )] ], | 
| 352 |  |  |  |  |  |  | m => [ [qw( 0,100 0,50 )], [qw( 10,40 40,40 )], [qw( 50,50 50,100 )], [qw( 60,40 90,40 )], [qw( 90,50 100,100 )] ], | 
| 353 |  |  |  |  |  |  | µ => [ [qw( 0,100 0,40 )], [qw( 0,80 70,80 80,75 90,60 100,40 )] ], | 
| 354 |  |  |  |  |  |  | }; | 
| 355 |  |  |  |  |  |  |  | 
| 356 |  |  |  |  |  |  | sub reshape_symb | 
| 357 |  |  |  |  |  |  | { | 
| 358 |  |  |  |  |  |  | my $self = shift; | 
| 359 |  |  |  |  |  |  | my ( $lines, $cols, $top, $left ) = @_; | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | $self->{mid_line} = int( ( $lines - 1 ) / 2 ); | 
| 362 |  |  |  |  |  |  | $self->{mid_col}  = int( ( $cols  - 2 ) / 2 ); | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | $self->{Y_to_line} = ( $lines - 1 ) / 100; | 
| 365 |  |  |  |  |  |  | $self->{X_to_col}  = ( $cols  - 2 ) / 100; | 
| 366 |  |  |  |  |  |  | } | 
| 367 |  |  |  |  |  |  |  | 
| 368 |  |  |  |  |  |  | sub _roundpos | 
| 369 |  |  |  |  |  |  | { | 
| 370 |  |  |  |  |  |  | my $self = shift; | 
| 371 |  |  |  |  |  |  | my ( $l, $c ) = @_; | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | # Round away from the centre of the widget | 
| 374 |  |  |  |  |  |  | return | 
| 375 |  |  |  |  |  |  | int($l) + ( $l > int($l) && $l > $self->{mid_line} ), | 
| 376 |  |  |  |  |  |  | int($c) + ( $c > int($c) && $c > $self->{mid_col}  ); | 
| 377 |  |  |  |  |  |  | } | 
| 378 |  |  |  |  |  |  |  | 
| 379 |  |  |  |  |  |  | sub render_symb_to_rb | 
| 380 |  |  |  |  |  |  | { | 
| 381 |  |  |  |  |  |  | my $self = shift; | 
| 382 |  |  |  |  |  |  | my ( $rb ) = @_; | 
| 383 |  |  |  |  |  |  |  | 
| 384 |  |  |  |  |  |  | my $strokes = $symbol_strokes{$self->value} or return; | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | $rb->setpen( $self->{lit_pen} ); | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | my $Y_to_line = $self->{Y_to_line}; | 
| 389 |  |  |  |  |  |  | my $X_to_col  = $self->{X_to_col}; | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | foreach my $stroke ( @$strokes ) { | 
| 392 |  |  |  |  |  |  | my ( $start, @points ) = @$stroke; | 
| 393 |  |  |  |  |  |  | $start =~ m/^(\d+),(\d+)$/; | 
| 394 |  |  |  |  |  |  | my ( $atL, $atC ) = $self->_roundpos( $2 * $Y_to_line, $1 * $X_to_col ); | 
| 395 |  |  |  |  |  |  |  | 
| 396 |  |  |  |  |  |  | foreach ( @points ) { | 
| 397 |  |  |  |  |  |  | m/^(\d+),(\d+)$/; | 
| 398 |  |  |  |  |  |  | my ( $toL, $toC ) = $self->_roundpos( $2 * $Y_to_line, $1 * $X_to_col ); | 
| 399 |  |  |  |  |  |  |  | 
| 400 |  |  |  |  |  |  | if( $toL == $atL ) { | 
| 401 |  |  |  |  |  |  | my ( $c, $limC ) = $toC > $atC ? ( $atC, $toC ) : ( $toC, $atC ); | 
| 402 |  |  |  |  |  |  | $rb->erase_at( $atL, $c, $limC - $c + 2 ); | 
| 403 |  |  |  |  |  |  | } | 
| 404 |  |  |  |  |  |  | elsif( $toC == $atC ) { | 
| 405 |  |  |  |  |  |  | my ( $l, $limL ) = $toL > $atL ? ( $atL, $toL ) : ( $toL, $atL ); | 
| 406 |  |  |  |  |  |  | $rb->erase_at( $_, $atC, 2 ) for $l .. $limL; | 
| 407 |  |  |  |  |  |  | } | 
| 408 |  |  |  |  |  |  | else { | 
| 409 |  |  |  |  |  |  | my ( $sL, $eL, $sC, $eC ) = $toL > $atL ? ( $atL, $toL, $atC, $toC ) | 
| 410 |  |  |  |  |  |  | : ( $toL, $atL, $toC, $atC ); | 
| 411 |  |  |  |  |  |  | # Maths is all easier if we use exclusive coords. | 
| 412 |  |  |  |  |  |  | $eL++; | 
| 413 |  |  |  |  |  |  | $eC > $sC ? $eC++ : $eC--; | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | my $dL = $eL - $sL; | 
| 416 |  |  |  |  |  |  | my $dC = $eC - $sC; | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | if( $dL >= abs $dC ) { | 
| 419 |  |  |  |  |  |  | my $c = $sC; | 
| 420 |  |  |  |  |  |  | my $err = 0; | 
| 421 |  |  |  |  |  |  |  | 
| 422 |  |  |  |  |  |  | for( my $l = $sL; $l != $eL; $l++ ) { | 
| 423 |  |  |  |  |  |  | $c++, $err -= $dL if  $err > $dL; | 
| 424 |  |  |  |  |  |  | $c--, $err += $dL if -$err > $dL; | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | $rb->erase_at( $l, $c, 2 ); | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | $err += $dC; | 
| 429 |  |  |  |  |  |  | } | 
| 430 |  |  |  |  |  |  | } | 
| 431 |  |  |  |  |  |  | else { | 
| 432 |  |  |  |  |  |  | my $l = $sL; | 
| 433 |  |  |  |  |  |  | my $err = 0; | 
| 434 |  |  |  |  |  |  | my $adC = abs $dC; | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | for( my $c = $sC; $c != $eC; $c += ( $eC > $sC ) ? 1 : -1 ) { | 
| 437 |  |  |  |  |  |  | $l++, $err -= $adC if  $err > $adC; | 
| 438 |  |  |  |  |  |  | $l--, $err += $adC if -$err > $adC; | 
| 439 |  |  |  |  |  |  |  | 
| 440 |  |  |  |  |  |  | $rb->erase_at( $l, $c, 2 ); | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | $err += $dL; | 
| 443 |  |  |  |  |  |  | } | 
| 444 |  |  |  |  |  |  | } | 
| 445 |  |  |  |  |  |  | } | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | $atL = $toL; | 
| 448 |  |  |  |  |  |  | $atC = $toC; | 
| 449 |  |  |  |  |  |  | } | 
| 450 |  |  |  |  |  |  | } | 
| 451 |  |  |  |  |  |  | } | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | =head1 AUTHOR | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | Paul Evans | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | =cut | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | 0x55AA; |