line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Image::Base ; # Documented at the __END__ |
2
|
|
|
|
|
|
|
|
3
|
3
|
|
|
3
|
|
21700
|
use 5.004 ; # 5.004 for __PACKAGE__ special literal |
|
3
|
|
|
|
|
12
|
|
|
3
|
|
|
|
|
7345
|
|
4
|
3
|
|
|
3
|
|
31
|
use strict ; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
138
|
|
5
|
|
|
|
|
|
|
|
6
|
3
|
|
|
3
|
|
18
|
use vars qw( $VERSION ) ; |
|
3
|
|
|
|
|
10
|
|
|
3
|
|
|
|
|
199
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
$VERSION = '1.17' ; |
9
|
|
|
|
|
|
|
|
10
|
3
|
|
|
3
|
|
18
|
use Carp qw( croak ) ; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
7655
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# uncomment this to run the ### lines |
13
|
|
|
|
|
|
|
#use Smart::Comments '###'; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
# All the supplied methods are expected to be inherited by subclasses; some |
16
|
|
|
|
|
|
|
# will be adequate, some will need to be overridden and some *must* be |
17
|
|
|
|
|
|
|
# overridden. |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
### Private methods |
20
|
|
|
|
|
|
|
# |
21
|
|
|
|
|
|
|
# _get object |
22
|
|
|
|
|
|
|
# _set object |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
sub _get { # Object method |
25
|
0
|
|
|
0
|
|
0
|
my $self = shift ; |
26
|
|
|
|
|
|
|
# my $class = ref( $self ) || $self ; |
27
|
|
|
|
|
|
|
|
28
|
0
|
|
|
|
|
0
|
$self->{shift()} ; |
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub _set { # Object method |
33
|
0
|
|
|
0
|
|
0
|
my $self = shift ; |
34
|
|
|
|
|
|
|
# my $class = ref( $self ) || $self ; |
35
|
|
|
|
|
|
|
|
36
|
0
|
|
|
|
|
0
|
my $field = shift ; |
37
|
|
|
|
|
|
|
|
38
|
0
|
|
|
|
|
0
|
$self->{$field} = shift ; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
|
42
|
0
|
|
|
0
|
|
0
|
sub DESTROY { |
43
|
|
|
|
|
|
|
; # Save's time |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
### Public methods |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
|
50
|
0
|
|
|
0
|
1
|
0
|
sub new { croak __PACKAGE__ . "::new() must be overridden" } |
51
|
0
|
|
|
0
|
1
|
0
|
sub xy { croak __PACKAGE__ . "::xy() must be overridden" } |
52
|
0
|
|
|
0
|
1
|
0
|
sub load { croak __PACKAGE__ . "::load() must be overridden" } |
53
|
0
|
|
|
0
|
1
|
0
|
sub save { croak __PACKAGE__ . "::save() must be overridden" } |
54
|
0
|
|
|
0
|
1
|
0
|
sub set { croak __PACKAGE__ . "::set() must be overridden" } |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
sub get { # Object method |
58
|
0
|
|
|
0
|
1
|
0
|
my $self = shift ; |
59
|
|
|
|
|
|
|
# my $class = ref( $self ) || $self ; |
60
|
|
|
|
|
|
|
|
61
|
0
|
|
|
|
|
0
|
my @result ; |
62
|
|
|
|
|
|
|
|
63
|
0
|
|
|
|
|
0
|
push @result, $self->_get( shift() ) while @_ ; |
64
|
|
|
|
|
|
|
|
65
|
0
|
0
|
|
|
|
0
|
wantarray ? @result : shift @result ; |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub new_from_image { # Object method |
70
|
0
|
|
|
0
|
1
|
0
|
my $self = shift ; # Must be an image to copy |
71
|
0
|
|
0
|
|
|
0
|
my $class = ref( $self ) || $self ; |
72
|
0
|
|
|
|
|
0
|
my $newclass = shift ; # Class of target taken from class or object |
73
|
|
|
|
|
|
|
|
74
|
0
|
0
|
|
|
|
0
|
croak "new_from_image() cannot read $class" unless $self->can( 'xy' ) ; |
75
|
|
|
|
|
|
|
|
76
|
0
|
|
|
|
|
0
|
my( $width, $height ) = $self->get( -width, -height ) ; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# If $newclass was an object reference we inherit its characteristics |
79
|
|
|
|
|
|
|
# except for width/height and any arguments we've supplied. |
80
|
0
|
|
|
|
|
0
|
my $obj = $newclass->new( @_, -width => $width, -height => $height ) ; |
81
|
|
|
|
|
|
|
|
82
|
0
|
0
|
|
|
|
0
|
croak "new_from_image() cannot convert to " . ref $obj unless $obj->can( 'xy' ) ; |
83
|
|
|
|
|
|
|
|
84
|
0
|
|
|
|
|
0
|
for( my $x = 0 ; $x < $width ; $x++ ) { |
85
|
0
|
|
|
|
|
0
|
for( my $y = 0 ; $y < $height ; $y++ ) { |
86
|
0
|
|
|
|
|
0
|
$obj->xy( $x, $y, $self->xy( $x, $y ) ) ; |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
0
|
|
|
|
|
0
|
$obj ; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub line { # Object method |
95
|
141
|
|
|
141
|
1
|
3772
|
my( $self, $x0, $y0, $x1, $y1, $colour ) = @_ ; |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# basic Bressenham line drawing |
98
|
|
|
|
|
|
|
|
99
|
141
|
|
|
|
|
193
|
my $dy = abs ($y1 - $y0); |
100
|
141
|
|
|
|
|
186
|
my $dx = abs ($x1 - $x0); |
101
|
|
|
|
|
|
|
#### $dy |
102
|
|
|
|
|
|
|
#### $dx |
103
|
|
|
|
|
|
|
|
104
|
141
|
100
|
|
|
|
243
|
if ($dx >= $dy) { |
105
|
|
|
|
|
|
|
# shallow slope |
106
|
|
|
|
|
|
|
|
107
|
125
|
100
|
|
|
|
267
|
( $x0, $y0, $x1, $y1 ) = ( $x1, $y1, $x0, $y0 ) if $x0 > $x1 ; |
108
|
|
|
|
|
|
|
|
109
|
125
|
|
|
|
|
132
|
my $y = $y0 ; |
110
|
125
|
100
|
|
|
|
207
|
my $ystep = ($y1 > $y0 ? 1 : -1); |
111
|
125
|
|
|
|
|
216
|
my $rem = int($dx/2) - $dx; |
112
|
125
|
|
|
|
|
306
|
for( my $x = $x0 ; $x <= $x1 ; $x++ ) { |
113
|
|
|
|
|
|
|
#### $rem |
114
|
637
|
|
|
|
|
1420
|
$self->xy( $x, $y, $colour ) ; |
115
|
637
|
100
|
|
|
|
6871
|
if (($rem += $dy) >= 0) { |
116
|
61
|
|
|
|
|
71
|
$rem -= $dx; |
117
|
61
|
|
|
|
|
207
|
$y += $ystep; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
} else { |
121
|
|
|
|
|
|
|
# steep slope |
122
|
|
|
|
|
|
|
|
123
|
16
|
100
|
|
|
|
41
|
( $x0, $y0, $x1, $y1 ) = ( $x1, $y1, $x0, $y0 ) if $y0 > $y1 ; |
124
|
|
|
|
|
|
|
|
125
|
16
|
|
|
|
|
22
|
my $x = $x0 ; |
126
|
16
|
100
|
|
|
|
29
|
my $xstep = ($x1 > $x0 ? 1 : -1); |
127
|
16
|
|
|
|
|
31
|
my $rem = int($dy/2) - $dy; |
128
|
16
|
|
|
|
|
42
|
for( my $y = $y0 ; $y <= $y1 ; $y++ ) { |
129
|
|
|
|
|
|
|
#### $rem |
130
|
68
|
|
|
|
|
161
|
$self->xy( $x, $y, $colour ) ; |
131
|
68
|
100
|
|
|
|
771
|
if (($rem += $dx) >= 0) { |
132
|
2
|
|
|
|
|
5
|
$rem -= $dy; |
133
|
2
|
|
|
|
|
5
|
$x += $xstep; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# Midpoint ellipse algorithm from Computer Graphics Principles and Practice. |
141
|
|
|
|
|
|
|
# |
142
|
|
|
|
|
|
|
# The points of the ellipse are |
143
|
|
|
|
|
|
|
# (x/a)^2 + (y/b)^2 == 1 |
144
|
|
|
|
|
|
|
# or expand out to |
145
|
|
|
|
|
|
|
# x^2*b^2 + y^2*a^2 == a^2*b^2 |
146
|
|
|
|
|
|
|
# |
147
|
|
|
|
|
|
|
# The x,y coordinates are taken relative to the centre $ox,$oy, with radials |
148
|
|
|
|
|
|
|
# $a and $b half the width $x1-x0 and height $y1-$y0. If $x1-$x0 is odd, |
149
|
|
|
|
|
|
|
# then $ox and $a are not integers but have 0.5 parts. Starting from $x=0.5 |
150
|
|
|
|
|
|
|
# and keeping that 0.5 means the final xy() pixels drawn in |
151
|
|
|
|
|
|
|
# &$ellipse_point() are integers. Similarly for y. |
152
|
|
|
|
|
|
|
# |
153
|
|
|
|
|
|
|
# Only a few lucky pixels exactly satisfy the ellipse equation above. For |
154
|
|
|
|
|
|
|
# the rest there's an error amount expressed as |
155
|
|
|
|
|
|
|
# |
156
|
|
|
|
|
|
|
# E(x,y) = x^2*b^2 + y^2*a^2 - a^2*b^2 |
157
|
|
|
|
|
|
|
# |
158
|
|
|
|
|
|
|
# The first loop maintains a "discriminator" d1 in $d |
159
|
|
|
|
|
|
|
# |
160
|
|
|
|
|
|
|
# d1 = (x+1)^2*b^2 + (y-1/2)^2*a^2 - a^2*b^2 |
161
|
|
|
|
|
|
|
# |
162
|
|
|
|
|
|
|
# which is E(x+1,y-1/2), being the error amount for the next x+1 position, |
163
|
|
|
|
|
|
|
# taken at y-1/2 which is the midpoint between the possible next y or y-1 |
164
|
|
|
|
|
|
|
# pixels. When d1 > 0 it means that the y-1/2 position is outside the |
165
|
|
|
|
|
|
|
# ellipse and the y-1 pixel is taken to be the better approximation to the |
166
|
|
|
|
|
|
|
# ellipse than y. |
167
|
|
|
|
|
|
|
# |
168
|
|
|
|
|
|
|
# The first loop does the four octants near the Y axis, ie. the nearly |
169
|
|
|
|
|
|
|
# horizontal parts. The second loop does the four octants near the X axis, |
170
|
|
|
|
|
|
|
# ie. the nearly vertical parts. For the second loop the discriminator in |
171
|
|
|
|
|
|
|
# $d is instead at the next y-1 position and between x and x+1, |
172
|
|
|
|
|
|
|
# |
173
|
|
|
|
|
|
|
# d2 = E(x+1/2,y-1) = (x+1/2)^2*b^2 + (y-1)^2*a^2 - a^2*b^2 |
174
|
|
|
|
|
|
|
# |
175
|
|
|
|
|
|
|
# The difference between d1 and d2 for the changeover is as follows and is |
176
|
|
|
|
|
|
|
# used to step across to the new position rather than a full recalculation. |
177
|
|
|
|
|
|
|
# Not much difference in speed, but less code. |
178
|
|
|
|
|
|
|
# |
179
|
|
|
|
|
|
|
# E(x+1/2,y-1) - E(x+1,y-1/2) |
180
|
|
|
|
|
|
|
# = -b^2 * (x + 3/4) + a^2 * (3/4 - y) |
181
|
|
|
|
|
|
|
# |
182
|
|
|
|
|
|
|
# since (x+1/2)^2 - (x+1)^2 = -x - 3/4 |
183
|
|
|
|
|
|
|
# (y-1)^2 - (y-1/2)^2 = -y + 3/4 |
184
|
|
|
|
|
|
|
# |
185
|
|
|
|
|
|
|
# |
186
|
|
|
|
|
|
|
# Other Possibilities: |
187
|
|
|
|
|
|
|
# |
188
|
|
|
|
|
|
|
# The calculations could be made all-integer by counting $x and $y from 0 at |
189
|
|
|
|
|
|
|
# the bounding box edges and measuring inwards, rather than outwards from a |
190
|
|
|
|
|
|
|
# fractional centre. E(x,y) could have a factor of 2 or 4 put through as |
191
|
|
|
|
|
|
|
# necessary, the discriminating >0 or <0 staying the same. The d1 and d2 |
192
|
|
|
|
|
|
|
# steps are at most roughly 2*max(a*b^2,b*a^2), which for a circle means |
193
|
|
|
|
|
|
|
# 2*r^3. This fits a 32-bit signed integer for up to about 1000 pixels or |
194
|
|
|
|
|
|
|
# so, and then of course Perl switches to 53-bit floats automatically, which |
195
|
|
|
|
|
|
|
# is still an exact integer up to about 160,000 pixels radius. |
196
|
|
|
|
|
|
|
# |
197
|
|
|
|
|
|
|
# It'd be possible to draw runs of horizontal pixels with line() instead of |
198
|
|
|
|
|
|
|
# individual xy() calls. That might help subclasses doing a block-fill for |
199
|
|
|
|
|
|
|
# a horizontal line segment. Except only big or flat ellipses have more |
200
|
|
|
|
|
|
|
# than a few adjacent horizontal pixels. Perhaps just the initial topmost |
201
|
|
|
|
|
|
|
# horizontal, using a sqrt to calculate where it crosses from the top y=b |
202
|
|
|
|
|
|
|
# down to y=b-1. |
203
|
|
|
|
|
|
|
# |
204
|
|
|
|
|
|
|
# The end o the first loop could be pre-calculated (with a sqrt), if that |
205
|
|
|
|
|
|
|
# seemed better than watching $aa*($y-0.5) vs $bb*($x+1). The loop change |
206
|
|
|
|
|
|
|
# is where the tangent slope is steeper than -1. Drawing a little diagram |
207
|
|
|
|
|
|
|
# shows that an x+0,y+1 downward step like in the second loop is not needed |
208
|
|
|
|
|
|
|
# until that point. |
209
|
|
|
|
|
|
|
# |
210
|
|
|
|
|
|
|
# dx/dy = -x*b^2 / y*a^2 = -1 slope |
211
|
|
|
|
|
|
|
# y = x*b^2/a^2 |
212
|
|
|
|
|
|
|
# b^2*x^2 + a^2*(b^4/a^4)*x^2 = a^2*b^2 into the ellipse equation |
213
|
|
|
|
|
|
|
# x^2 * (1 + b^2/a^2) = a^2 |
214
|
|
|
|
|
|
|
# x = a * sqrt (a^2 / (a^2 + b^2)) |
215
|
|
|
|
|
|
|
# = a^2 / sqrt (a^2 + b^2) |
216
|
|
|
|
|
|
|
# |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
sub ellipse { # Object method |
219
|
11
|
|
|
11
|
1
|
2689
|
my $self = shift ; |
220
|
|
|
|
|
|
|
# my $class = ref( $self ) || $self ; |
221
|
|
|
|
|
|
|
|
222
|
11
|
|
|
|
|
21
|
my( $x0, $y0, $x1, $y1, $colour, $fill ) = @_ ; |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# per the docs, x0,y0 top left, x1,y1 bottom right |
225
|
|
|
|
|
|
|
# could relax that fairly easily, if desired ... |
226
|
|
|
|
|
|
|
### assert: $x0 <= $x1 |
227
|
|
|
|
|
|
|
### assert: $y0 <= $y1 |
228
|
|
|
|
|
|
|
|
229
|
11
|
|
|
|
|
14
|
my ($a, $b); |
230
|
11
|
100
|
66
|
|
|
74
|
if (($a = ( $x1 - $x0 ) / 2) <= .5 |
231
|
|
|
|
|
|
|
|| ($b = ( $y1 - $y0 ) / 2) <= .5) { |
232
|
|
|
|
|
|
|
# one or two pixels high or wide, treat as rectangle |
233
|
1
|
|
|
|
|
4
|
$self->rectangle ($x0, $y0, $x1, $y1, $colour ); |
234
|
1
|
|
|
|
|
3
|
return; |
235
|
|
|
|
|
|
|
} |
236
|
10
|
|
|
|
|
32
|
my $aa = $a ** 2 ; |
237
|
10
|
|
|
|
|
13
|
my $bb = $b ** 2 ; |
238
|
10
|
|
|
|
|
13
|
my $ox = ($x0 + $x1) / 2; |
239
|
10
|
|
|
|
|
13
|
my $oy = ($y0 + $y1) / 2; |
240
|
|
|
|
|
|
|
|
241
|
10
|
|
|
|
|
74
|
my $x = $a - int($a) ; # 0 or 0.5 |
242
|
10
|
|
|
|
|
14
|
my $y = $b ; |
243
|
|
|
|
|
|
|
### initial: "origin $ox,$oy start xy $x,$y" |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
my $ellipse_point = |
246
|
|
|
|
|
|
|
($fill |
247
|
|
|
|
|
|
|
? sub { |
248
|
|
|
|
|
|
|
### ellipse_point fill: "$x,$y" |
249
|
6
|
|
|
6
|
|
31
|
$self->line( $ox - $x, $oy + $y, |
250
|
|
|
|
|
|
|
$ox + $x, $oy + $y, $colour ) ; |
251
|
6
|
|
|
|
|
22
|
$self->line( $ox - $x, $oy - $y, |
252
|
|
|
|
|
|
|
$ox + $x, $oy - $y, $colour ) ; |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
: sub { |
255
|
|
|
|
|
|
|
### ellipse_point xys: "$x,$y" |
256
|
15
|
|
|
15
|
|
41
|
$self->xy( $ox + $x, $oy + $y, $colour ) ; |
257
|
15
|
|
|
|
|
160
|
$self->xy( $ox - $x, $oy - $y, $colour ) ; |
258
|
15
|
|
|
|
|
140
|
$self->xy( $ox + $x, $oy - $y, $colour ) ; |
259
|
15
|
|
|
|
|
138
|
$self->xy( $ox - $x, $oy + $y, $colour ) ; |
260
|
10
|
100
|
|
|
|
71
|
}); |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# Initially, |
263
|
|
|
|
|
|
|
# d1 = E(x+1,y-1/2) |
264
|
|
|
|
|
|
|
# = (x+1)^2*b^2 + (y-1/2)^2*a^2 - a^2*b^2 |
265
|
|
|
|
|
|
|
# which for x=0,y=b is |
266
|
|
|
|
|
|
|
# = b^2 - a^2*b + a^2/4 |
267
|
|
|
|
|
|
|
# or for x=0.5,y=b |
268
|
|
|
|
|
|
|
# = 9/4*b^2 - ... |
269
|
|
|
|
|
|
|
# |
270
|
10
|
100
|
|
|
|
36
|
my $d = ($x ? 2.25*$bb : $bb) - ( $aa * $b ) + ( $aa / 4 ) ; |
271
|
|
|
|
|
|
|
|
272
|
10
|
|
100
|
|
|
55
|
while( $y >= 1 |
273
|
|
|
|
|
|
|
&& ( $aa * ( $y - 0.5 ) ) > ( $bb * ( $x + 1 ) ) ) { |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
### assert: $d == ($x+1)**2 * $bb + ($y-.5)**2 * $aa - $aa * $bb |
276
|
22
|
100
|
|
|
|
41
|
if( $d < 0 ) { |
277
|
18
|
100
|
|
|
|
36
|
if (! $fill) { |
278
|
|
|
|
|
|
|
# unfilled draws each pixel, but filled waits until stepping |
279
|
|
|
|
|
|
|
# down "--$y" and then draws whole horizontal line |
280
|
9
|
|
|
|
|
13
|
&$ellipse_point(); |
281
|
|
|
|
|
|
|
} |
282
|
18
|
|
|
|
|
95
|
$d += ( $bb * ( ( 2 * $x ) + 3 ) ) ; |
283
|
18
|
|
|
|
|
85
|
++$x ; |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
else { |
286
|
4
|
|
|
|
|
8
|
&$ellipse_point(); |
287
|
4
|
|
|
|
|
23
|
$d += ( ( $bb * ( ( 2 * $x ) + 3 ) ) + |
288
|
|
|
|
|
|
|
( $aa * ( ( -2 * $y ) + 2 ) ) ) ; |
289
|
4
|
|
|
|
|
6
|
++$x ; |
290
|
4
|
|
|
|
|
11
|
--$y ; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# switch to d2 = E(x+1/2,y-1) by adding E(x+1/2,y-1) - E(x+1,y-1/2) |
295
|
10
|
|
|
|
|
23
|
$d += $aa*(.75-$y) - $bb*($x+.75); |
296
|
|
|
|
|
|
|
### assert: $d == $bb*($x+0.5)**2 + $aa*($y-1)**2 - $aa*$bb |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
### second loop at: "$x, $y" |
299
|
|
|
|
|
|
|
|
300
|
10
|
|
|
|
|
22
|
while( $y >= 1 ) { |
301
|
8
|
|
|
|
|
16
|
&$ellipse_point(); |
302
|
8
|
100
|
|
|
|
49
|
if( $d < 0 ) { |
303
|
6
|
|
|
|
|
14
|
$d += ( $bb * ( ( 2 * $x ) + 2 ) ) + |
304
|
|
|
|
|
|
|
( $aa * ( ( -2 * $y ) + 3 ) ) ; |
305
|
6
|
|
|
|
|
7
|
++$x ; |
306
|
6
|
|
|
|
|
15
|
--$y ; |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
else { |
309
|
2
|
|
|
|
|
3
|
$d += ( $aa * ( ( -2 * $y ) + 3 ) ) ; |
310
|
2
|
|
|
|
|
5
|
--$y ; |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
### assert: $d == $bb*($x+0.5)**2 + $aa*($y-1)**2 - $aa*$bb |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
# loop ends with y=0 or y=0.5 according as the height is odd or even, |
316
|
|
|
|
|
|
|
# leaving one or two middle rows to draw out to x0 and x1 edges |
317
|
|
|
|
|
|
|
### assert: $y == $b - int($b) |
318
|
|
|
|
|
|
|
|
319
|
10
|
100
|
|
|
|
21
|
if ($fill) { |
320
|
|
|
|
|
|
|
### middle fill: "y ".($oy-$y)." to ".($oy+$y) |
321
|
5
|
|
|
|
|
16
|
$self->rectangle( $x0, $oy - $y, |
322
|
|
|
|
|
|
|
$x1, $oy + $y, |
323
|
|
|
|
|
|
|
$colour, 1 ) ; |
324
|
|
|
|
|
|
|
} else { |
325
|
|
|
|
|
|
|
# middle tails from $x out to the left/right edges |
326
|
|
|
|
|
|
|
# $x can be several pixels less than $a if small height large width |
327
|
|
|
|
|
|
|
### tail: "y=$y, left $x0 to ".($ox-$x).", right ".($ox+$x)." to $x1" |
328
|
5
|
|
|
|
|
18
|
$self->rectangle( $x0, $oy - $y, # left |
329
|
|
|
|
|
|
|
$ox - $x, $oy + $y, |
330
|
|
|
|
|
|
|
$colour, 1 ) ; |
331
|
5
|
|
|
|
|
38
|
$self->rectangle( $ox + $x, $oy - $y, # right |
332
|
|
|
|
|
|
|
$x1, $oy + $y, |
333
|
|
|
|
|
|
|
$colour, 1 ) ; |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
sub rectangle { # Object method |
338
|
50
|
|
|
50
|
1
|
3253
|
my ($self, $x0, $y0, $x1, $y1, $colour, $fill) = @_; |
339
|
|
|
|
|
|
|
|
340
|
50
|
100
|
|
|
|
102
|
if ($x0 == $x1) { |
341
|
|
|
|
|
|
|
# vertical line only |
342
|
19
|
|
|
|
|
41
|
$self->line( $x0, $y0, $x1, $y1, $colour ) ; |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
} else { |
345
|
31
|
100
|
|
|
|
53
|
if ($fill) { |
346
|
23
|
|
|
|
|
84
|
for( my $y = $y0 ; $y <= $y1 ; $y++ ) { |
347
|
47
|
|
|
|
|
94
|
$self->line( $x0, $y, $x1, $y, $colour ) ; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
} else { # unfilled |
351
|
|
|
|
|
|
|
|
352
|
8
|
|
|
|
|
19
|
$self->line( $x0, $y0, |
353
|
|
|
|
|
|
|
$x1, $y0, $colour ) ; # top |
354
|
8
|
100
|
|
|
|
21
|
if (++$y0 <= $y1) { |
355
|
|
|
|
|
|
|
# height >= 2 |
356
|
7
|
100
|
|
|
|
16
|
if ($y0 < $y1) { |
357
|
|
|
|
|
|
|
# height >= 3, verticals |
358
|
5
|
|
|
|
|
15
|
$self->line( $x0, $y0, |
359
|
|
|
|
|
|
|
$x0, $y1-1, $colour ) ; # left |
360
|
5
|
|
|
|
|
14
|
$self->line( $x1, $y0, |
361
|
|
|
|
|
|
|
$x1, $y1-1, $colour ) ; # right |
362
|
|
|
|
|
|
|
} |
363
|
7
|
|
|
|
|
17
|
$self->line( $x1, $y1, |
364
|
|
|
|
|
|
|
$x0, $y1, $colour ) ; # bottom |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
} |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub diamond { |
371
|
17
|
|
|
17
|
1
|
3911
|
my ($self, $x1,$y1, $x2,$y2, $colour, $fill) = @_; |
372
|
|
|
|
|
|
|
### diamond(): "$x1,$y1, $x2,$y2, $colour fill=".($fill||0) |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
### assert: $x2 >= $x1 |
375
|
|
|
|
|
|
|
### assert: $y2 >= $y1 |
376
|
|
|
|
|
|
|
|
377
|
17
|
|
|
|
|
27
|
my $w = $x2 - $x1; |
378
|
17
|
|
|
|
|
23
|
my $h = $y2 - $y1; |
379
|
17
|
100
|
100
|
|
|
79
|
if ($w < 2 || $h < 2) { |
380
|
6
|
|
|
|
|
15
|
$self->rectangle ($x1,$y1, $x2,$y2, $colour, 1); |
381
|
6
|
|
|
|
|
17
|
return; |
382
|
|
|
|
|
|
|
} |
383
|
11
|
|
|
|
|
20
|
$w = int ($w / 2); |
384
|
11
|
|
|
|
|
15
|
$h = int ($h / 2); |
385
|
11
|
|
|
|
|
12
|
my $x = $w; # middle |
386
|
11
|
|
|
|
|
12
|
my $y = 0; # top |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
### $w |
389
|
|
|
|
|
|
|
### $h |
390
|
|
|
|
|
|
|
### x1+x: $x1+$w |
391
|
|
|
|
|
|
|
### x2-x: $x2-$w |
392
|
|
|
|
|
|
|
### y1+y: $y1+$h |
393
|
|
|
|
|
|
|
### y2-y: $y2-$h |
394
|
|
|
|
|
|
|
|
395
|
11
|
|
|
|
|
11
|
my $draw; |
396
|
11
|
100
|
|
|
|
17
|
if ($fill) { |
397
|
|
|
|
|
|
|
$draw = sub { |
398
|
|
|
|
|
|
|
### draw across: "$x,$y" |
399
|
12
|
|
|
12
|
|
48
|
$self->line ($x1+$x,$y1+$y, $x2-$x,$y1+$y, $colour); # upper |
400
|
12
|
|
|
|
|
39
|
$self->line ($x1+$x,$y2-$y, $x2-$x,$y2-$y, $colour); # lower |
401
|
8
|
|
|
|
|
39
|
}; |
402
|
|
|
|
|
|
|
} else { |
403
|
|
|
|
|
|
|
$draw = sub { |
404
|
|
|
|
|
|
|
### draw: "$x,$y" |
405
|
4
|
|
|
4
|
|
14
|
$self->xy ($x1+$x,$y1+$y, $colour); # upper left |
406
|
4
|
|
|
|
|
45
|
$self->xy ($x2-$x,$y1+$y, $colour); # upper right |
407
|
|
|
|
|
|
|
|
408
|
4
|
|
|
|
|
51
|
$self->xy ($x1+$x,$y2-$y, $colour); # lower left |
409
|
4
|
|
|
|
|
38
|
$self->xy ($x2-$x,$y2-$y, $colour); # lower right |
410
|
3
|
|
|
|
|
26
|
}; |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
|
413
|
11
|
100
|
|
|
|
23
|
if ($w > $h) { |
414
|
|
|
|
|
|
|
### shallow ... |
415
|
|
|
|
|
|
|
|
416
|
3
|
|
|
|
|
6
|
my $rem = int($w/2) - $w; |
417
|
|
|
|
|
|
|
### $rem |
418
|
|
|
|
|
|
|
|
419
|
3
|
|
|
|
|
8
|
while ($x > 0) { |
420
|
|
|
|
|
|
|
### at: "x=$x rem=$rem" |
421
|
|
|
|
|
|
|
|
422
|
8
|
100
|
|
|
|
17
|
if (($rem += $h) >= 0) { |
423
|
4
|
|
|
|
|
9
|
&$draw(); |
424
|
4
|
|
|
|
|
6
|
$y++; |
425
|
4
|
|
|
|
|
5
|
$rem -= $w; |
426
|
4
|
|
|
|
|
10
|
$x--; |
427
|
|
|
|
|
|
|
} else { |
428
|
4
|
50
|
|
|
|
9
|
if (! $fill) { &$draw() } |
|
0
|
|
|
|
|
0
|
|
429
|
4
|
|
|
|
|
13
|
$x--; |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
} else { |
434
|
|
|
|
|
|
|
### steep ... |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
# when $h is odd bias towards pointier at the narrower top/bottom ends |
437
|
8
|
|
|
|
|
14
|
my $rem = int(($h-1)/2) - $h; |
438
|
|
|
|
|
|
|
### $rem |
439
|
|
|
|
|
|
|
|
440
|
8
|
|
|
|
|
20
|
while ($y < $h) { |
441
|
|
|
|
|
|
|
### $rem |
442
|
12
|
|
|
|
|
16
|
&$draw(); |
443
|
|
|
|
|
|
|
|
444
|
12
|
100
|
|
|
|
59
|
if (($rem += $w) >= 0) { |
445
|
10
|
|
|
|
|
12
|
$rem -= $h; |
446
|
10
|
|
|
|
|
12
|
$x--; |
447
|
|
|
|
|
|
|
### x inc to: "x=$x rem $rem" |
448
|
|
|
|
|
|
|
} |
449
|
12
|
|
|
|
|
26
|
$y++; |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
} |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
### final: "$x,$y" |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
# middle row if $h odd, or middle two rows if $h even |
456
|
|
|
|
|
|
|
# done explicitly rather than with &$draw() so as not to draw the middle |
457
|
|
|
|
|
|
|
# row twice when $h odd |
458
|
11
|
100
|
|
|
|
21
|
if ($fill) { |
459
|
8
|
|
|
|
|
22
|
$self->rectangle ($x1,$y1+$h, $x2,$y2-$h, $colour, 1); |
460
|
|
|
|
|
|
|
} else { |
461
|
3
|
|
|
|
|
10
|
$self->rectangle ($x1,$y1+$h, $x1+$x,$y2-$h, $colour, 1); # left |
462
|
3
|
|
|
|
|
9
|
$self->rectangle ($x2-$x,$y1+$h, $x2,$y2-$h, $colour, 1); # right |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
|
466
|
1
|
|
|
1
|
1
|
305
|
sub add_colours { |
467
|
|
|
|
|
|
|
# my ($self, $colour, $colour, ...) = @_; |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
1 ; |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
__END__ |