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-2015 -- leonerd@leonerd.org.uk |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Tickit::Widget::SegmentDisplay; |
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
881
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
29
|
|
9
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
29
|
|
10
|
1
|
|
|
1
|
|
31
|
use 5.010; # // |
|
1
|
|
|
|
|
3
|
|
11
|
1
|
|
|
1
|
|
5
|
use base qw( Tickit::Widget ); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
892
|
|
12
|
|
|
|
|
|
|
use Tickit::Style; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
use utf8; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
our $VERSION = '0.04'; |
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 new |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
$segmentdisplay = Tickit::Widget::SegmentDisplay->new( %args ) |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Constructs a new C object. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Takes the following named arguments |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=over 8 |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=item value => STR |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
Sets an initial value. |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=item type => STR |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
The type of display. Supported types are: |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=over 4 |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item seven |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
A 7-segment bar display. The display can also be blanked with the value " ". |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=item seven_dp |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
A 7-segment bar display with decimal-point. To light the decimal point, append |
100
|
|
|
|
|
|
|
the value with ".". |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=item colon |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
A static C<:> |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=item symb |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
A unit, prefix symbol or other character. The following characters are |
109
|
|
|
|
|
|
|
recognised: |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
V A W Ω F H s |
112
|
|
|
|
|
|
|
G M k m µ n p |
113
|
|
|
|
|
|
|
+ - % |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
Each will be drawn in a style approximately to fit the general LED shape |
116
|
|
|
|
|
|
|
display, by drawing lines of erased cells. Note however that some more |
117
|
|
|
|
|
|
|
intricate shapes may not be very visible on smaller scales. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=back |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=item use_unicode => BOOL |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
If true, use Unicode block-drawing characters. If false, use only coloured |
124
|
|
|
|
|
|
|
erase cells using the background colour. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=item use_halfline => BOOL |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
If true, vertical resolution of rendered block characters is effectively |
129
|
|
|
|
|
|
|
doubled by using half-filled Unicode block-drawing characters. Setting this |
130
|
|
|
|
|
|
|
option implies C. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=item thickness => INT |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
Gives the number of columns wide and half-lines tall that LED bars will be |
135
|
|
|
|
|
|
|
drawn in. Note that unless C is set, this value ought to be an |
136
|
|
|
|
|
|
|
even number. Defaults to 2. |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=back |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=cut |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
my %types = ( |
143
|
|
|
|
|
|
|
seven => [qw( 7 )], |
144
|
|
|
|
|
|
|
seven_dp => [qw( 7. )], |
145
|
|
|
|
|
|
|
colon => [qw( : )], |
146
|
|
|
|
|
|
|
symb => [], |
147
|
|
|
|
|
|
|
); |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub new |
150
|
|
|
|
|
|
|
{ |
151
|
|
|
|
|
|
|
my $class = shift; |
152
|
|
|
|
|
|
|
my %args = @_; |
153
|
|
|
|
|
|
|
my $self = $class->SUPER::new( %args ); |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
my $type = $args{type} // "seven"; |
156
|
|
|
|
|
|
|
my $method; |
157
|
|
|
|
|
|
|
foreach my $typename ( keys %types ) { |
158
|
|
|
|
|
|
|
$type eq $typename and $method = $typename, last; |
159
|
|
|
|
|
|
|
$type eq $_ and $method = $typename, last for @{ $types{$typename} }; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
defined $method or croak "Unrecognised type name '$type'"; |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
$self->{reshape_method} = $self->can( "reshape_${method}" ); |
164
|
|
|
|
|
|
|
$self->{render_method} = $self->can( "render_${method}_to_rb" ); |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
my $use_halfline = $args{use_halfline}; |
167
|
|
|
|
|
|
|
$self->{use_halfline} = $use_halfline; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
my $use_unicode = $args{use_unicode}; |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
$self->{flush_method} = $self->can( |
172
|
|
|
|
|
|
|
$use_halfline ? "flush_halfline" : |
173
|
|
|
|
|
|
|
$use_unicode ? "flush_unicode" : |
174
|
|
|
|
|
|
|
"flush" ); |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
$self->{thickness} = $args{thickness} // 2; |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
$self->{value} = $args{value} // ""; |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
$self->on_style_changed_values( |
181
|
|
|
|
|
|
|
lit => [ undef, $self->get_style_values( "lit" ) ], |
182
|
|
|
|
|
|
|
unlit => [ undef, $self->get_style_values( "unlit" ) ], |
183
|
|
|
|
|
|
|
); |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
return $self; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# ADG + atleast 1 line each for FB and EC |
189
|
|
|
|
|
|
|
sub lines { 3 + 2 } |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# FE, BC + atleast 2 columns for AGD |
192
|
|
|
|
|
|
|
sub cols { 4 + 2 } |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head1 ACCESSORS |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=cut |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=head2 value |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
$value = $segmentdisplay->value |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
$segmentdisplay->set_value( $value ) |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
Return or set the character on display |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=cut |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub value |
209
|
|
|
|
|
|
|
{ |
210
|
|
|
|
|
|
|
my $self = shift; |
211
|
|
|
|
|
|
|
return $self->{value}; |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
sub set_value |
215
|
|
|
|
|
|
|
{ |
216
|
|
|
|
|
|
|
my $self = shift; |
217
|
|
|
|
|
|
|
( $self->{value} ) = @_; |
218
|
|
|
|
|
|
|
$self->redraw; |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub on_style_changed_values |
222
|
|
|
|
|
|
|
{ |
223
|
|
|
|
|
|
|
my $self = shift; |
224
|
|
|
|
|
|
|
my %values = @_; |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
$self->{lit_pen} = Tickit::Pen::Immutable->new( fg => $values{lit}[1] ) if $values{lit}; |
227
|
|
|
|
|
|
|
$self->{unlit_pen} = Tickit::Pen::Immutable->new( fg => $values{unlit}[1] ) if $values{unlit}; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub reshape |
231
|
|
|
|
|
|
|
{ |
232
|
|
|
|
|
|
|
my $self = shift; |
233
|
|
|
|
|
|
|
my $win = $self->window or return; |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
my $linescale = 1 + !!$self->{use_halfline}; |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
$self->{reshape_method}->( $self, $win->lines * $linescale, $win->cols, 0, 0 ); |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
use constant { |
241
|
|
|
|
|
|
|
LIT => 0x01, |
242
|
|
|
|
|
|
|
UNLIT => 0x02, |
243
|
|
|
|
|
|
|
}; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
sub render_to_rb |
246
|
|
|
|
|
|
|
{ |
247
|
|
|
|
|
|
|
my $self = shift; |
248
|
|
|
|
|
|
|
my ( $rb, $rect ) = @_; |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
my @buff; |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# TODO: sizing? |
253
|
|
|
|
|
|
|
$self->{render_method}->( $self, \@buff ); |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
$rb->eraserect( $rect ); |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
$self->{flush_method}->( $self, \@buff, $rb, $rect ); |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
sub flush |
261
|
|
|
|
|
|
|
{ |
262
|
|
|
|
|
|
|
my $self = shift; |
263
|
|
|
|
|
|
|
my ( $buff, $rb, $rect ) = @_; |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
my $lit_pen = Tickit::Pen::Immutable->new( bg => $self->{lit_pen}->getattr( "fg" ) ); |
266
|
|
|
|
|
|
|
my $unlit_pen = Tickit::Pen::Immutable->new( bg => $self->{unlit_pen}->getattr( "fg" ) ); |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
foreach my $line ( $rect->linerange ) { |
269
|
|
|
|
|
|
|
next unless defined( my $cells = $buff->[$line] ); |
270
|
|
|
|
|
|
|
foreach my $col ( $rect->left .. $rect->right - 1 ) { |
271
|
|
|
|
|
|
|
my $val = vec( $cells, $col, 2 ) or next; |
272
|
|
|
|
|
|
|
$rb->setpen( $val == LIT ? $lit_pen : $unlit_pen ); |
273
|
|
|
|
|
|
|
$rb->erase_at( $line, $col, 1 ); |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
use constant { |
279
|
|
|
|
|
|
|
U_FULL => 0x2588, |
280
|
|
|
|
|
|
|
U_UPPER => 0x2580, |
281
|
|
|
|
|
|
|
U_LOWER => 0x2584, |
282
|
|
|
|
|
|
|
}; |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
sub flush_unicode |
285
|
|
|
|
|
|
|
{ |
286
|
|
|
|
|
|
|
my $self = shift; |
287
|
|
|
|
|
|
|
my ( $buff, $rb, $rect ) = @_; |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
my $lit_pen = $self->{lit_pen}; |
290
|
|
|
|
|
|
|
my $unlit_pen = $self->{unlit_pen}; |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
foreach my $line ( $rect->linerange ) { |
293
|
|
|
|
|
|
|
next unless defined( my $cells = $buff->[$line] ); |
294
|
|
|
|
|
|
|
foreach my $col ( $rect->left .. $rect->right - 1 ) { |
295
|
|
|
|
|
|
|
my $val = vec( $cells, $col, 2 ) or next; |
296
|
|
|
|
|
|
|
$rb->setpen( $val == LIT ? $lit_pen : $unlit_pen ); |
297
|
|
|
|
|
|
|
$rb->char_at( $line, $col, U_FULL ); |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub flush_halfline |
303
|
|
|
|
|
|
|
{ |
304
|
|
|
|
|
|
|
my $self = shift; |
305
|
|
|
|
|
|
|
my ( $buff, $rb, $rect ) = @_; |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
my $lit_pen = $self->{lit_pen}; |
308
|
|
|
|
|
|
|
my $unlit_pen = $self->{unlit_pen}; |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
my $both_pen = Tickit::Pen::Immutable->new( |
311
|
|
|
|
|
|
|
fg => $lit_pen->getattr( 'fg' ), |
312
|
|
|
|
|
|
|
bg => $unlit_pen->getattr( 'fg' ), |
313
|
|
|
|
|
|
|
); |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
foreach my $phyline ( $rect->linerange ) { |
316
|
|
|
|
|
|
|
my $hicells = $buff->[$phyline*2]; |
317
|
|
|
|
|
|
|
my $locells = $buff->[$phyline*2 + 1]; |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
next unless defined $hicells or defined $locells; |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
$hicells //= ""; |
322
|
|
|
|
|
|
|
$locells //= ""; |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
foreach my $col ( $rect->left .. $rect->right - 1 ) { |
325
|
|
|
|
|
|
|
my $hival = vec( $hicells, $col, 2 ); |
326
|
|
|
|
|
|
|
my $loval = vec( $locells, $col, 2 ); |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
$hival or $loval or next; |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
if( $hival == $loval ) { |
331
|
|
|
|
|
|
|
$rb->setpen( ( $hival || $loval ) == LIT ? $lit_pen : $unlit_pen ); |
332
|
|
|
|
|
|
|
$rb->char_at( $phyline, $col, U_FULL ); |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
elsif( !$hival or !$loval ) { |
335
|
|
|
|
|
|
|
$rb->setpen( ( $hival || $loval ) == LIT ? $lit_pen : $unlit_pen ); |
336
|
|
|
|
|
|
|
$rb->char_at( $phyline, $col, $hival ? U_UPPER : U_LOWER ); |
337
|
|
|
|
|
|
|
} |
338
|
|
|
|
|
|
|
else { |
339
|
|
|
|
|
|
|
# Half lit, half unlit |
340
|
|
|
|
|
|
|
$rb->setpen( $both_pen ); |
341
|
|
|
|
|
|
|
$rb->char_at( $phyline, $col, $hival == LIT ? U_UPPER : U_LOWER ); |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
sub _fill |
348
|
|
|
|
|
|
|
{ |
349
|
|
|
|
|
|
|
my $self = shift; |
350
|
|
|
|
|
|
|
my ( $buff, $startline, $endline, $startcol, $endcol, $val ) = @_; |
351
|
|
|
|
|
|
|
$val //= LIT; |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
my $thickness = $self->{thickness}; |
354
|
|
|
|
|
|
|
my @colrange = ( $startcol .. $endcol + $thickness - 1 ); |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
$thickness /= 2 unless $self->{use_halfline}; |
357
|
|
|
|
|
|
|
my @linerange = ( $startline .. $endline + $thickness - 1 ); |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
foreach my $line ( @linerange ) { |
360
|
|
|
|
|
|
|
vec( $buff->[$line], $_, 2 ) = $val for @colrange; |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
sub _dot |
365
|
|
|
|
|
|
|
{ |
366
|
|
|
|
|
|
|
my $self = shift; |
367
|
|
|
|
|
|
|
my ( $buff, $line, $col, $val ) = @_; |
368
|
|
|
|
|
|
|
$self->_fill( $buff, $line, $line, $col, $col, $val ); |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
# 7-Segment |
372
|
|
|
|
|
|
|
my %segments = ( |
373
|
|
|
|
|
|
|
' ' => " ", |
374
|
|
|
|
|
|
|
0 => "ABCDEF ", |
375
|
|
|
|
|
|
|
1 => " BC ", |
376
|
|
|
|
|
|
|
2 => "AB DE G", |
377
|
|
|
|
|
|
|
3 => "ABCD G", |
378
|
|
|
|
|
|
|
4 => " BC FG", |
379
|
|
|
|
|
|
|
5 => "A CD FG", |
380
|
|
|
|
|
|
|
6 => "A CDEFG", |
381
|
|
|
|
|
|
|
7 => "ABC ", |
382
|
|
|
|
|
|
|
8 => "ABCDEFG", |
383
|
|
|
|
|
|
|
9 => "ABCD FG", |
384
|
|
|
|
|
|
|
); |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
sub _val_for_seg |
387
|
|
|
|
|
|
|
{ |
388
|
|
|
|
|
|
|
my $self = shift; |
389
|
|
|
|
|
|
|
my ( $segment ) = @_; |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
my $segments = $segments{$self->value} or return UNLIT; |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
my $lit = substr( $segments, ord($segment) - ord("A"), 1 ) ne " "; |
394
|
|
|
|
|
|
|
return $lit ? LIT : UNLIT; |
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
sub reshape_seven |
398
|
|
|
|
|
|
|
{ |
399
|
|
|
|
|
|
|
my $self = shift; |
400
|
|
|
|
|
|
|
my ( $lines, $cols, $top, $left ) = @_; |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
my $thickness = $self->{thickness}; |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
my $right = $left + $cols - $thickness; |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
$self->{FE_col} = $left; |
407
|
|
|
|
|
|
|
$self->{AGD_startcol} = $left + $thickness; |
408
|
|
|
|
|
|
|
$self->{AGD_endcol} = $right - $thickness; |
409
|
|
|
|
|
|
|
$self->{BC_col} = $right; |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
$thickness /= 2 unless $self->{use_halfline}; |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
my $bottom = $top + $lines - $thickness; |
414
|
|
|
|
|
|
|
my $mid = int( ( $top + $bottom ) / 2 ); |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
$self->{A_line} = $top; |
417
|
|
|
|
|
|
|
$self->{BF_startline} = $top + $thickness; |
418
|
|
|
|
|
|
|
$self->{BF_endline} = $mid - $thickness; |
419
|
|
|
|
|
|
|
$self->{G_line} = $mid; |
420
|
|
|
|
|
|
|
$self->{CE_startline} = $mid + $thickness; |
421
|
|
|
|
|
|
|
$self->{CE_endline} = $bottom - $thickness; |
422
|
|
|
|
|
|
|
$self->{D_line} = $bottom; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
sub render_seven_to_rb |
426
|
|
|
|
|
|
|
{ |
427
|
|
|
|
|
|
|
my $self = shift; |
428
|
|
|
|
|
|
|
my ( $buff ) = @_; |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
$self->_fill( $buff, ( $self->{A_line} ) x 2, $self->{AGD_startcol}, $self->{AGD_endcol}, $self->_val_for_seg( "A" ) ); |
431
|
|
|
|
|
|
|
$self->_fill( $buff, ( $self->{G_line} ) x 2, $self->{AGD_startcol}, $self->{AGD_endcol}, $self->_val_for_seg( "G" ) ); |
432
|
|
|
|
|
|
|
$self->_fill( $buff, ( $self->{D_line} ) x 2, $self->{AGD_startcol}, $self->{AGD_endcol}, $self->_val_for_seg( "D" ) ); |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
$self->_fill( $buff, $self->{BF_startline}, $self->{BF_endline}, ( $self->{FE_col} ) x 2, $self->_val_for_seg( "F" ) ); |
435
|
|
|
|
|
|
|
$self->_fill( $buff, $self->{BF_startline}, $self->{BF_endline}, ( $self->{BC_col} ) x 2, $self->_val_for_seg( "B" ) ); |
436
|
|
|
|
|
|
|
$self->_fill( $buff, $self->{CE_startline}, $self->{CE_endline}, ( $self->{FE_col} ) x 2, $self->_val_for_seg( "E" ) ); |
437
|
|
|
|
|
|
|
$self->_fill( $buff, $self->{CE_startline}, $self->{CE_endline}, ( $self->{BC_col} ) x 2, $self->_val_for_seg( "C" ) ); |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
# 7-Segment with DP |
441
|
|
|
|
|
|
|
sub reshape_seven_dp |
442
|
|
|
|
|
|
|
{ |
443
|
|
|
|
|
|
|
my $self = shift; |
444
|
|
|
|
|
|
|
my ( $lines, $cols, $top, $left ) = @_; |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
$self->reshape_seven( $lines, $cols - 2, $top, $left ); |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
$self->{DP_line} = $top + $lines - 1; |
449
|
|
|
|
|
|
|
$self->{DP_col} = $left + $cols - 2; |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
sub render_seven_dp_to_rb |
453
|
|
|
|
|
|
|
{ |
454
|
|
|
|
|
|
|
my $self = shift; |
455
|
|
|
|
|
|
|
my ( $buff ) = @_; |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
my $value = $self->{value}; |
458
|
|
|
|
|
|
|
my $dp; |
459
|
|
|
|
|
|
|
local $self->{value}; |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
if( $value =~ m/^(\d?)(\.?)/ ) { |
462
|
|
|
|
|
|
|
$self->{value} = $1; |
463
|
|
|
|
|
|
|
$dp = length $2; |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
else { |
466
|
|
|
|
|
|
|
$self->{value} = $value; |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
$self->render_seven_to_rb( $buff ); |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
$self->_dot( $buff, $self->{DP_line}, $self->{DP_col}, $dp ? LIT : UNLIT ); |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# Static double-dot colon |
475
|
|
|
|
|
|
|
sub reshape_colon |
476
|
|
|
|
|
|
|
{ |
477
|
|
|
|
|
|
|
my $self = shift; |
478
|
|
|
|
|
|
|
my ( $lines, $cols, $top, $left ) = @_; |
479
|
|
|
|
|
|
|
my $bottom = $top + $lines - 1; |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
$self->{colon_col} = 2 + int( ( $cols - 4 ) / 2 ); |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
my $ofs = int( ( $lines - 1 + 0.5 ) / 4 ); |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
$self->{A_line} = $top + $ofs; |
486
|
|
|
|
|
|
|
$self->{B_line} = $bottom - $ofs; |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
sub render_colon_to_rb |
490
|
|
|
|
|
|
|
{ |
491
|
|
|
|
|
|
|
my $self = shift; |
492
|
|
|
|
|
|
|
my ( $buff ) = @_; |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
my $col = $self->{colon_col}; |
495
|
|
|
|
|
|
|
$self->_dot( $buff, $self->{A_line}, $col ); |
496
|
|
|
|
|
|
|
$self->_dot( $buff, $self->{B_line}, $col ); |
497
|
|
|
|
|
|
|
} |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
# Symbol drawing |
500
|
|
|
|
|
|
|
# |
501
|
|
|
|
|
|
|
# Each symbol is drawn as a series of erase calls on the RB to draw 'lines'. |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
my %symbol_strokes = do { |
504
|
|
|
|
|
|
|
no warnings 'qw'; # Quiet the 'Possible attempt to separate words with commas' warning |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
# Letters likely to be used for units |
507
|
|
|
|
|
|
|
V => [ [qw( 0,0 50,100 100,0 )] ], |
508
|
|
|
|
|
|
|
A => [ [qw( 0,100 50,0 100,100 )], [qw( 20,70 80,70)] ], |
509
|
|
|
|
|
|
|
W => [ [qw( 0,0 25,100 50,50 75,100 100,0)] ], |
510
|
|
|
|
|
|
|
Ω => [ [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 ) ] ], |
511
|
|
|
|
|
|
|
F => [ [qw( 0,100 0,0 100,0 )], [qw( 0,50 80,50 )] ], |
512
|
|
|
|
|
|
|
H => [ [qw( 0,0 0,100 )], [qw( 0,50 100,50 )], [qw( 100,0 100,100 )] ], |
513
|
|
|
|
|
|
|
s => [ [qw( 100,50 75,40 25,40 0,50 0,60 25,70 75,70 100,80 100,90 75,100 25,100 0,90 )] ], |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
# Symbols likely to be used as SI prefixes |
516
|
|
|
|
|
|
|
G => [ [qw( 100,25 65,0 35,0 0,25 0,75 35,100 65,100 100,75 100,50 55,50 )] ], |
517
|
|
|
|
|
|
|
M => [ [qw( 0,100 0,0 50,50 100,0 100,100 )] ], |
518
|
|
|
|
|
|
|
k => [ [qw( 10,0 10,100 )], [qw( 90,40 10,70 90,100 )] ], |
519
|
|
|
|
|
|
|
m => [ [qw( 0,100 0,50 10,40 40,40 50,50 50,100 )], [qw( 50,50 60,40 90,40 100,50 100,100 )] ], |
520
|
|
|
|
|
|
|
µ => [ [qw( 0,100 0,40 )], [qw( 0,80 70,80 80,75 90,60 100,40 )] ], |
521
|
|
|
|
|
|
|
n => [ [qw( 0,100 0,40 )], [qw( 0,50 30,40 70,40 100,50 100,100 )] ], |
522
|
|
|
|
|
|
|
p => [ [qw( 0,100 0,40 )], [qw( 0,55 30,40 70,40 100,55 100,60 70,80 30,80 0,60 )] ], |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
# Mathematical symbols |
525
|
|
|
|
|
|
|
'+' => [ [qw( 10,50 90,50 )], [qw( 50,30 50,70 )] ], |
526
|
|
|
|
|
|
|
'-' => [ [qw( 10,50 90,50 )] ], |
527
|
|
|
|
|
|
|
'%' => [ [qw( 10,10 10,30 30,30 30,10 10,10 )], [qw( 20,100 80,00 )], [qw( 70,70 70,90 90,90 90,70 70,70 )] ], |
528
|
|
|
|
|
|
|
}; |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
sub reshape_symb |
531
|
|
|
|
|
|
|
{ |
532
|
|
|
|
|
|
|
my $self = shift; |
533
|
|
|
|
|
|
|
my ( $lines, $cols, $top, $left ) = @_; |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
$self->{mid_line} = int( ( $lines - 1 ) / 2 ); |
536
|
|
|
|
|
|
|
$self->{mid_col} = int( ( $cols - 2 ) / 2 ); |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
$self->{Y_to_line} = ( $lines - 1 ) / 100; |
539
|
|
|
|
|
|
|
$self->{X_to_col} = ( $cols - 2 ) / 100; |
540
|
|
|
|
|
|
|
} |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
sub _roundpos |
543
|
|
|
|
|
|
|
{ |
544
|
|
|
|
|
|
|
my $self = shift; |
545
|
|
|
|
|
|
|
my ( $l, $c ) = @_; |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
# Round away from the centre of the widget |
548
|
|
|
|
|
|
|
return |
549
|
|
|
|
|
|
|
int($l) + ( $l > int($l) && $l > $self->{mid_line} ), |
550
|
|
|
|
|
|
|
int($c) + ( $c > int($c) && $c > $self->{mid_col} ); |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
sub render_symb_to_rb |
554
|
|
|
|
|
|
|
{ |
555
|
|
|
|
|
|
|
my $self = shift; |
556
|
|
|
|
|
|
|
my ( $buff ) = @_; |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
my $strokes = $symbol_strokes{$self->value} or return; |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
my $Y_to_line = $self->{Y_to_line}; |
561
|
|
|
|
|
|
|
my $X_to_col = $self->{X_to_col}; |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
foreach my $stroke ( @$strokes ) { |
564
|
|
|
|
|
|
|
my ( $start, @points ) = @$stroke; |
565
|
|
|
|
|
|
|
$start =~ m/^(\d+),(\d+)$/; |
566
|
|
|
|
|
|
|
my ( $atL, $atC ) = $self->_roundpos( $2 * $Y_to_line, $1 * $X_to_col ); |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
foreach ( @points ) { |
569
|
|
|
|
|
|
|
m/^(\d+),(\d+)$/; |
570
|
|
|
|
|
|
|
my ( $toL, $toC ) = $self->_roundpos( $2 * $Y_to_line, $1 * $X_to_col ); |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
if( $toL == $atL ) { |
573
|
|
|
|
|
|
|
my ( $c, $limC ) = $toC > $atC ? ( $atC, $toC ) : ( $toC, $atC ); |
574
|
|
|
|
|
|
|
$self->_fill( $buff, $atL, $atL, $c, $limC ); |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
elsif( $toC == $atC ) { |
577
|
|
|
|
|
|
|
my ( $l, $limL ) = $toL > $atL ? ( $atL, $toL ) : ( $toL, $atL ); |
578
|
|
|
|
|
|
|
$self->_fill( $buff, $l, $limL, $atC, $atC ); |
579
|
|
|
|
|
|
|
} |
580
|
|
|
|
|
|
|
else { |
581
|
|
|
|
|
|
|
my ( $sL, $eL, $sC, $eC ) = $toL > $atL ? ( $atL, $toL, $atC, $toC ) |
582
|
|
|
|
|
|
|
: ( $toL, $atL, $toC, $atC ); |
583
|
|
|
|
|
|
|
# Maths is all easier if we use exclusive coords. |
584
|
|
|
|
|
|
|
$eL++; |
585
|
|
|
|
|
|
|
$eC > $sC ? $eC++ : $eC--; |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
my $dL = $eL - $sL; |
588
|
|
|
|
|
|
|
my $dC = $eC - $sC; |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
if( $dL >= abs $dC ) { |
591
|
|
|
|
|
|
|
my $c = $sC; |
592
|
|
|
|
|
|
|
my $err = 0; |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
for( my $l = $sL; $l != $eL; $l++ ) { |
595
|
|
|
|
|
|
|
$c++, $err -= $dL if $err > $dL; |
596
|
|
|
|
|
|
|
$c--, $err += $dL if -$err > $dL; |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
$self->_dot( $buff, $l, $c ); |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
$err += $dC; |
601
|
|
|
|
|
|
|
} |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
else { |
604
|
|
|
|
|
|
|
my $l = $sL; |
605
|
|
|
|
|
|
|
my $err = 0; |
606
|
|
|
|
|
|
|
my $adC = abs $dC; |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
for( my $c = $sC; $c != $eC; $c += ( $eC > $sC ) ? 1 : -1 ) { |
609
|
|
|
|
|
|
|
$l++, $err -= $adC if $err > $adC; |
610
|
|
|
|
|
|
|
$l--, $err += $adC if -$err > $adC; |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
$self->_dot( $buff, $l, $c ); |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
$err += $dL; |
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
} |
617
|
|
|
|
|
|
|
} |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
$atL = $toL; |
620
|
|
|
|
|
|
|
$atC = $toC; |
621
|
|
|
|
|
|
|
} |
622
|
|
|
|
|
|
|
} |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
=head1 AUTHOR |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
Paul Evans |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
=cut |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
0x55AA; |