| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#! perl |
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
1223
|
use v5.26; |
|
|
2
|
|
|
|
|
8
|
|
|
4
|
2
|
|
|
2
|
|
10
|
use strict; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
55
|
|
|
5
|
2
|
|
|
2
|
|
22
|
use warnings; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
199
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
package SVGPDF::Contrib::Bogen; |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 NAME |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
SVGPDF::Contrib::Bogen - Circular and elliptic curves |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
$context->bogen( $x1,$y1, $x2,$y2, $r, @opts); |
|
16
|
|
|
|
|
|
|
$context->bogen_ellip( $x1,$y1, $x2,$y2, $rx,$ry, @opts); |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
This package contains functions to draw circular and elliptic curves. |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
This code is developed by Phil Perry, based on old PDF::API2 code and |
|
23
|
|
|
|
|
|
|
friendly contributed to the SVGPDF project. |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=cut |
|
26
|
|
|
|
|
|
|
|
|
27
|
2
|
|
|
2
|
|
474
|
use Math::Trig; |
|
|
2
|
|
|
|
|
18286
|
|
|
|
2
|
|
|
|
|
9130
|
|
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=over |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=item $context->bogen_ellip($x1,$y1, $x2,$y2, $rx,$ry, @opts) |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
This is a variant of the original C call from PDF::Builder, which |
|
34
|
|
|
|
|
|
|
drew a segment (arc) of a circle, which was adapted here by Phil Perry to draw |
|
35
|
|
|
|
|
|
|
an elliptical arc. |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
(German for I, as in a segment (arc) of an ellipse), this is a |
|
38
|
|
|
|
|
|
|
segment of an ellipse defined by the intersection of two ellipses of given x |
|
39
|
|
|
|
|
|
|
and y radii, with the two intersection points as inputs. There are four |
|
40
|
|
|
|
|
|
|
possible resulting arcs, which can be selected with opts C and C. |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
This extends the path along an arc of an ellipse of the specified x and y radii |
|
43
|
|
|
|
|
|
|
between C<[$x1,$y1]> to C<[$x2,$y2]>. The current position is then set |
|
44
|
|
|
|
|
|
|
to the endpoint of the arc (C<[$x2,$y2]>). |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
Options (C<@opts>) |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=over |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=item 'move' => move_flag |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
Set C to a I value if this arc is the beginning of a new |
|
53
|
|
|
|
|
|
|
path instead of the continuation of an existing path. Note that the default |
|
54
|
|
|
|
|
|
|
(C => I) is |
|
55
|
|
|
|
|
|
|
I a straight line to I and then the arc, but a blending into the curve |
|
56
|
|
|
|
|
|
|
from the current point. It will often I pass through I! Set to |
|
57
|
|
|
|
|
|
|
I, there will be a jump (move) from the current point to I, to where |
|
58
|
|
|
|
|
|
|
the arc will start. |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=item 'large' => larger_arc_flag |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
Set C to a I value to draw the larger ("outer") arc between the |
|
63
|
|
|
|
|
|
|
two points, instead of the smaller one. Both arcs are |
|
64
|
|
|
|
|
|
|
drawn I from I to I. The default value of I draws |
|
65
|
|
|
|
|
|
|
the smaller arc. |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=item 'dir' => draw_direction |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
Set C to a I value to draw the mirror image of the specified arc |
|
70
|
|
|
|
|
|
|
(flip it over, so that its center point is on the other side of the line |
|
71
|
|
|
|
|
|
|
connecting the two points). Both arcs (small or large) are drawn |
|
72
|
|
|
|
|
|
|
I from I to I. The default (I) draws |
|
73
|
|
|
|
|
|
|
clockwise arcs. |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=item 'rotate' => axis_rotation |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
A non-zero value is the degrees to rotate the axes of the ellipse (in a |
|
78
|
|
|
|
|
|
|
counter-clockwise manner). For example, C<'rotate'=E45> will have the |
|
79
|
|
|
|
|
|
|
ellipse's +X axis pointing "northeast" and the +Y axis pointing "northwest". |
|
80
|
|
|
|
|
|
|
The default value is 0 (no rotation). |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=item 'full' => color_spec |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
If given (no default), draw the full ellipse (not just the arc) |
|
85
|
|
|
|
|
|
|
in this color, with a dot at its center. This may be useful |
|
86
|
|
|
|
|
|
|
for diagnostic and development purposes, to show the ellipse from which |
|
87
|
|
|
|
|
|
|
the arc is obtained. |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=back |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
B |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
If the given radii C<$rx> and C<$ry> are too small for the points |
|
94
|
|
|
|
|
|
|
I and I to fit on the specified ellipse, they will be proportionately |
|
95
|
|
|
|
|
|
|
scaled up untilthe points fit on the ellipse. |
|
96
|
|
|
|
|
|
|
This is a silent error, as due to rounding, given points (even if correct) |
|
97
|
|
|
|
|
|
|
may not exactly fit on the ellipse. Further note that the algorithm only |
|
98
|
|
|
|
|
|
|
enlarges the radii until a sweep of 180 degrees is obtained, so it is possible |
|
99
|
|
|
|
|
|
|
that the ellipse will be smaller than your intended one! |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=back |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=cut |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub bogen_ellip { |
|
106
|
11
|
|
|
11
|
1
|
69
|
my ($self, $x1,$y1, $x2,$y2, $rx,$ry, %opts) = @_; |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# set default values for options |
|
109
|
11
|
|
|
|
|
27
|
my $move = 0; # 0 = continue from present point, 1 = move to point 1 |
|
110
|
11
|
|
|
|
|
19
|
my $larc = 0; # 0 = choose smaller arc, 1 = choose larger |
|
111
|
11
|
|
|
|
|
24
|
my $dir = 0; # 0 = CW, 1 = CCW |
|
112
|
11
|
|
|
|
|
16
|
my $rotate = 0; # degrees rotated around center of ellipse (so rx isn't |
|
113
|
|
|
|
|
|
|
# due left-right) |
|
114
|
11
|
50
|
|
|
|
42
|
if (defined $opts{'move'}) { $move = $opts{'move'}; } |
|
|
11
|
|
|
|
|
23
|
|
|
115
|
11
|
50
|
|
|
|
35
|
if (defined $opts{'large'}) { $larc = $opts{'large'}; } |
|
|
11
|
|
|
|
|
26
|
|
|
116
|
11
|
50
|
|
|
|
29
|
if (defined $opts{'dir'}) { $dir = $opts{'dir'}; } |
|
|
11
|
|
|
|
|
23
|
|
|
117
|
11
|
50
|
|
|
|
27
|
if (defined $opts{'rotate'}) { $rotate = $opts{'rotate'}; } |
|
|
11
|
|
|
|
|
66
|
|
|
118
|
|
|
|
|
|
|
|
|
119
|
11
|
|
|
|
|
60
|
my ($alpha,$beta); |
|
120
|
11
|
|
|
|
|
0
|
my ($cosR, $sinR, $x1P,$y1P, $xcP,$ycP, $xc,$yc, $lambda, $d,$k); |
|
121
|
11
|
|
|
|
|
0
|
my ($xm,$ym, $xM,$yM, $ux,$uy,$ulen, $vx,$vy,$vlen, $dp_uv); |
|
122
|
11
|
|
|
|
|
0
|
my ($cosTheta1,$theta1, $cosDeltaTheta,$deltaTheta); |
|
123
|
11
|
|
|
|
|
20
|
my $PI = 3.141593; |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# P1 and P2 need to be distinct |
|
126
|
11
|
50
|
33
|
|
|
39
|
if ($x1 == $x2 && $y1 == $y2) { |
|
127
|
0
|
|
|
|
|
0
|
print STDERR "bogen_ellip requires two distinct points. Skipping.\n"; |
|
128
|
0
|
|
|
|
|
0
|
return $self; |
|
129
|
|
|
|
|
|
|
} |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# think of the SVG coordinates (where this algorithm comes from) as being |
|
132
|
|
|
|
|
|
|
# like PDF's (conventional geometry), except mirrored about the x axis |
|
133
|
|
|
|
|
|
|
# (horizontal line). y grows downwards, angles + = CW sweep, starting at |
|
134
|
|
|
|
|
|
|
# angle 0 degrees points due east (axis rotation applied). |
|
135
|
|
|
|
|
|
|
# just compute everything SVG's way, and when applied to PDF everything |
|
136
|
|
|
|
|
|
|
# will be right side up and turning the right way. |
|
137
|
|
|
|
|
|
|
# larc and dir need to be 0 or 1, not just false/true |
|
138
|
11
|
100
|
|
|
|
27
|
if ($larc) { $larc = 1; } else { $larc = 0; } |
|
|
2
|
|
|
|
|
5
|
|
|
|
9
|
|
|
|
|
19
|
|
|
139
|
11
|
100
|
|
|
|
27
|
if ($dir) { $dir = 1; } else { $dir = 0; } |
|
|
9
|
|
|
|
|
16
|
|
|
|
2
|
|
|
|
|
4
|
|
|
140
|
|
|
|
|
|
|
# fS (from dir) 1 if sweep is increasing angle (CCW in PDF, CW in SVG) |
|
141
|
|
|
|
|
|
|
# fA (larc) 1 is larger (> 180 degrees) arc |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# need to flip rotation direction, sweep direction to match |
|
144
|
|
|
|
|
|
|
# SVG algorithm. |
|
145
|
|
|
|
|
|
|
# $dir = !$dir if $larc != $dir; |
|
146
|
|
|
|
|
|
|
# $dir = !$dir; |
|
147
|
|
|
|
|
|
|
# $rotate = -$rotate; |
|
148
|
11
|
|
|
|
|
31
|
$rotate = $rotate/180*$PI; |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# make both radii positive r = |r| |
|
151
|
11
|
50
|
|
|
|
27
|
if ($rx < 0) { $rx = -$rx; } |
|
|
0
|
|
|
|
|
0
|
|
|
152
|
11
|
50
|
|
|
|
29
|
if ($ry < 0) { $ry = -$ry; } |
|
|
0
|
|
|
|
|
0
|
|
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# if either radius is 0, arc is a straight line from P1 to P2 |
|
155
|
11
|
50
|
33
|
|
|
60
|
if (!$rx || !$ry) { |
|
156
|
0
|
|
|
|
|
0
|
$self->poly($x1,$y1, $x2,$y2); # degenerate case |
|
157
|
0
|
|
|
|
|
0
|
return $self; |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# compute elliptical arc parameters per |
|
161
|
|
|
|
|
|
|
# https://gitlab.gnome.org/GNOME/librsvg/-/blob/main/rsvg/src/path_builder.rs, |
|
162
|
|
|
|
|
|
|
# based on https://www.w3.org/TR/SVG2/implnote.html#Introduction |
|
163
|
|
|
|
|
|
|
# (code is more from the W3 math than the GNOME code, which it's not |
|
164
|
|
|
|
|
|
|
# clear what the sign conventions are) |
|
165
|
|
|
|
|
|
|
# if the radii are too small, they will be corrected below. |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
# midpoint distance of line from P1 to P2 |
|
168
|
11
|
|
|
|
|
24
|
$xm = ($x1-$x2)/2.0; |
|
169
|
11
|
|
|
|
|
24
|
$ym = ($y1-$y2)/2.0; |
|
170
|
|
|
|
|
|
|
# actual midpoint of line from P1 to P2 |
|
171
|
11
|
|
|
|
|
26
|
$xM = ($x1+$x2)/2.0; |
|
172
|
11
|
|
|
|
|
89
|
$yM = ($y1+$y2)/2.0; |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# P1' |
|
175
|
11
|
|
|
|
|
24
|
$cosR = cos($rotate); |
|
176
|
11
|
|
|
|
|
18
|
$sinR = sin($rotate); |
|
177
|
|
|
|
|
|
|
|
|
178
|
11
|
|
|
|
|
25
|
$x1P = $cosR*$xm + $sinR*$ym; |
|
179
|
11
|
|
|
|
|
28
|
$y1P = -$sinR*$xm + $cosR*$ym; |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
# increase radii if necessary |
|
182
|
11
|
|
|
|
|
40
|
$lambda = ($x1P/$rx)**2 + ($y1P/$ry)**2; |
|
183
|
11
|
100
|
|
|
|
30
|
if ($lambda > 1.0) { |
|
184
|
|
|
|
|
|
|
# a radius cannot be too large, but if too small (lambda > 1), |
|
185
|
|
|
|
|
|
|
# preserve aspect ratio while increasing rx and ry |
|
186
|
6
|
|
|
|
|
15
|
$rx *= sqrt($lambda); |
|
187
|
6
|
|
|
|
|
9
|
$ry *= sqrt($lambda); |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
# C' (transformed center) |
|
191
|
11
|
|
|
|
|
38
|
$d = ($rx * $ry)**2 - ($rx * $y1P)**2 - ($ry * $x1P)**2; |
|
192
|
11
|
|
|
|
|
28
|
$d /= (($rx * $y1P)**2 + ($ry * $x1P)**2); |
|
193
|
|
|
|
|
|
|
# deal with rounding issues |
|
194
|
11
|
100
|
66
|
|
|
43
|
$d = 0 if $d < 0.0 && $d > -1.0e-10; |
|
195
|
11
|
50
|
|
|
|
34
|
if ($d < 0.0) { |
|
196
|
|
|
|
|
|
|
# failure, skip |
|
197
|
0
|
|
|
|
|
0
|
print STDERR "Unable to compute elliptical arc (1) d=$d. Skipping.\n"; |
|
198
|
0
|
|
|
|
|
0
|
return $self; |
|
199
|
|
|
|
|
|
|
} |
|
200
|
11
|
|
|
|
|
20
|
$d = sqrt($d); |
|
201
|
|
|
|
|
|
|
# negate if small arc CW or large arc CCW (per normal PDF coordinates) |
|
202
|
11
|
100
|
|
|
|
48
|
$d = -$d if $larc == $dir; |
|
203
|
11
|
|
|
|
|
23
|
$xcP = $d * $rx/$ry * $y1P; |
|
204
|
11
|
|
|
|
|
25
|
$ycP = $d * -$ry/$rx * $x1P; |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
# C (actual center) |
|
207
|
11
|
|
|
|
|
27
|
$xc = $cosR * $xcP - $sinR * $ycP + $xM; |
|
208
|
11
|
|
|
|
|
24
|
$yc = $sinR * $xcP + $cosR * $ycP + $yM; |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# theta1 (start angle 0, sweep to P1'). 0 is due East, CW + |
|
211
|
|
|
|
|
|
|
# first, get unit vector for C'->P1' |
|
212
|
11
|
|
|
|
|
20
|
$ux = ($x1P - $xcP)/$rx; # "unstretch" ellipse into circle |
|
213
|
11
|
|
|
|
|
19
|
$uy = ($y1P - $ycP)/$ry; |
|
214
|
11
|
|
|
|
|
24
|
$ulen = sqrt(($ux**2 + $uy**2)); |
|
215
|
11
|
50
|
|
|
|
29
|
if ($ulen == 0.0) { |
|
216
|
|
|
|
|
|
|
# failure, skip (shouldn't see 0 length C' to P1' vector) |
|
217
|
0
|
|
|
|
|
0
|
print STDERR "Unable to compute elliptical arc (2). Skipping.\n"; |
|
218
|
0
|
|
|
|
|
0
|
return $self; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
11
|
|
|
|
|
21
|
$cosTheta1 = $ux/$ulen; # unit vector x component = cos(theta1) |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
# as rx and ry have already been corrected, is this ever needed? |
|
223
|
|
|
|
|
|
|
# better safe than sorry, especially if just past +/-1 due to rounding... |
|
224
|
11
|
50
|
|
|
|
31
|
$cosTheta1 = -1 if $cosTheta1 < -1.0; |
|
225
|
11
|
50
|
|
|
|
25
|
$cosTheta1 = 1 if $cosTheta1 > 1.0; |
|
226
|
11
|
|
|
|
|
54
|
$theta1 = acos($cosTheta1); |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# negate (flip) if on other side (up, negative y territory) |
|
229
|
11
|
100
|
|
|
|
152
|
$theta1 = -$theta1 if $uy < 0.0; |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# delta theta sweep P1 to P2. vector v is C' to P2' |
|
232
|
11
|
|
|
|
|
22
|
$vx = (-$x1P - $xcP)/$rx; # again, squash ellipse to circle |
|
233
|
11
|
|
|
|
|
50
|
$vy = (-$y1P - $ycP)/$ry; |
|
234
|
11
|
|
|
|
|
27
|
$vlen = sqrt($vx**2 + $vy**2); |
|
235
|
11
|
50
|
|
|
|
32
|
if ($vlen == 0.0) { |
|
236
|
|
|
|
|
|
|
# failure, skip (P1 == P2? vector can't be 0 length) |
|
237
|
0
|
|
|
|
|
0
|
print STDERR "Unable to compute elliptical arc (3). Skipping.\n"; |
|
238
|
0
|
|
|
|
|
0
|
return $self; |
|
239
|
|
|
|
|
|
|
} |
|
240
|
11
|
|
|
|
|
20
|
$vx /= $vlen; $vy /= $vlen; |
|
|
11
|
|
|
|
|
16
|
|
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# acos( u dot v / 1*1 ) is sweep angle |
|
243
|
11
|
|
|
|
|
22
|
$k = $ux*$vx + $uy*$vy; |
|
244
|
|
|
|
|
|
|
# again, better safe than sorry... |
|
245
|
11
|
100
|
|
|
|
28
|
$k = -1 if $k < -1.0; |
|
246
|
11
|
50
|
|
|
|
25
|
$k = 1 if $k > 1.0; |
|
247
|
11
|
|
|
|
|
28
|
$deltaTheta = acos($k); |
|
248
|
11
|
100
|
|
|
|
108
|
$deltaTheta = -$deltaTheta if $ux*$vy-$uy*$vx < 0.0; |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# convert sweep angles to PDF coordinates in degrees |
|
251
|
11
|
|
|
|
|
26
|
$alpha = $theta1*180/$PI; |
|
252
|
11
|
|
|
|
|
23
|
$beta = $alpha + $deltaTheta*180/$PI; |
|
253
|
11
|
|
|
|
|
31
|
while ($beta >= 360.0) { $beta -= 360.0; } |
|
|
0
|
|
|
|
|
0
|
|
|
254
|
11
|
|
|
|
|
30
|
while ($beta < 0.0) { $beta += 360.0; } |
|
|
1
|
|
|
|
|
4
|
|
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# ------------------------------------------------------------------- |
|
257
|
|
|
|
|
|
|
# if 'full' color ellipse requested, draw it now for angle 0 sweep 180 |
|
258
|
|
|
|
|
|
|
# and angle 180 sweep 180 (for full ellipse) |
|
259
|
11
|
50
|
|
|
|
33
|
if (defined $opts{'full'}) { |
|
260
|
|
|
|
|
|
|
# save current location to return to |
|
261
|
0
|
|
|
|
|
0
|
my @saveloc = ($self->{' x'},$self->{' y'}); |
|
262
|
0
|
|
|
|
|
0
|
$self->save(); |
|
263
|
|
|
|
|
|
|
|
|
264
|
0
|
|
|
|
|
0
|
$self->stroke_color($opts{'full'}); |
|
265
|
|
|
|
|
|
|
# move to P1, draw 180 arcs |
|
266
|
0
|
|
|
|
|
0
|
$self->move($x1,$y1); |
|
267
|
0
|
|
|
|
|
0
|
_arc2points($self, $rx,$ry, $alpha,$alpha+180, $x1,$y1, 2*$xc-$x1,2*$yc-$y1, 0, $rotate); |
|
268
|
0
|
|
|
|
|
0
|
$self->move($x1,$y1); |
|
269
|
0
|
|
|
|
|
0
|
_arc2points($self, $rx,$ry, $alpha,$alpha+180, $x1,$y1, 2*$xc-$x1,2*$yc-$y1, 1, $rotate); |
|
270
|
0
|
|
|
|
|
0
|
$self->stroke(); |
|
271
|
|
|
|
|
|
|
|
|
272
|
0
|
|
|
|
|
0
|
$self->restore(); |
|
273
|
0
|
|
|
|
|
0
|
$self->move(@saveloc); |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
# ------------------------------------------------------------------- |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
# move to starting point (if specified), then output arc |
|
278
|
11
|
50
|
|
|
|
28
|
$self->move($x1,$y1) if $move; |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# PDF::Builder's arc() includes a 'dir' flag, but PDF::API doesn't. |
|
281
|
|
|
|
|
|
|
# so, need to calculate points (for Bezier curves). |
|
282
|
11
|
|
|
|
|
38
|
_arc2points($self, $rx,$ry, $alpha,$beta, $x1,$y1, $x2,$y2, $dir, $rotate); |
|
283
|
|
|
|
|
|
|
|
|
284
|
11
|
|
|
|
|
77
|
return $self; |
|
285
|
|
|
|
|
|
|
} |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# calculate the Bezier control points for an elliptical arc, given |
|
288
|
|
|
|
|
|
|
# self = graphics context |
|
289
|
|
|
|
|
|
|
# rx and ry = radii |
|
290
|
|
|
|
|
|
|
# alpha and beta = starting and ending sweeps (degrees) |
|
291
|
|
|
|
|
|
|
# x' and y' = P1' |
|
292
|
|
|
|
|
|
|
# x2 and y2 = last point (if needed) |
|
293
|
|
|
|
|
|
|
# dir = 1 CW, 0 CCW |
|
294
|
|
|
|
|
|
|
# rotate = axis rotation in radians |
|
295
|
|
|
|
|
|
|
# returns nothing. curve called to output the curve to PDF |
|
296
|
|
|
|
|
|
|
sub _arc2points { |
|
297
|
11
|
|
|
11
|
|
46
|
my ($self, $rx,$ry, $alpha,$beta, $x1,$y1, $x2,$y2, $dir, $rotate) = @_; |
|
298
|
11
|
|
|
|
|
27
|
my (@points, $x,$y, $p0_x,$p0_y, $p1_x,$p1_y, $p2_x,$p2_y, $p3_x,$p3_y); |
|
299
|
11
|
|
|
|
|
19
|
$dir = !$dir; |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# @points is relative to starting point of arc |
|
302
|
11
|
|
|
|
|
34
|
@points = _arctocurve($rx,$ry, $alpha,$beta, $dir,$rotate); |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# counterrotate all start/end/control points around P1 by -rotate degrees |
|
305
|
11
|
100
|
|
|
|
32
|
if ($rotate) { |
|
306
|
5
|
|
|
|
|
11
|
my $r = $rotate; # already in radians |
|
307
|
5
|
|
|
|
|
11
|
my $cosR = cos($r); |
|
308
|
5
|
|
|
|
|
12
|
my $sinR = sin($r); |
|
309
|
5
|
|
|
|
|
9
|
my ($x,$y, $xr,$yr); |
|
310
|
5
|
|
|
|
|
20
|
for (my $i=0; $i<@points; $i+=2) { |
|
311
|
180
|
|
|
|
|
312
|
$x = $points[$i]; $y = $points[$i+1]; |
|
|
180
|
|
|
|
|
291
|
|
|
312
|
180
|
|
|
|
|
380
|
$xr = $x1 + $cosR*($x-$x1) - $sinR*($y-$y1); |
|
313
|
180
|
|
|
|
|
410
|
$yr = $y1 + $sinR*($x-$x1) + $cosR*($y-$y1); |
|
314
|
180
|
|
|
|
|
281
|
$points[$i] = $xr; $points[$i+1] = $yr; |
|
|
180
|
|
|
|
|
455
|
|
|
315
|
|
|
|
|
|
|
} |
|
316
|
|
|
|
|
|
|
} |
|
317
|
|
|
|
|
|
|
|
|
318
|
11
|
|
|
|
|
28
|
$p0_x = shift @points; |
|
319
|
11
|
|
|
|
|
17
|
$p0_y = shift @points; |
|
320
|
11
|
|
|
|
|
20
|
$x = $x1 - $p0_x; |
|
321
|
11
|
|
|
|
|
34
|
$y = $y1 - $p0_y; |
|
322
|
|
|
|
|
|
|
|
|
323
|
11
|
|
|
|
|
32
|
while (scalar @points > 0) { |
|
324
|
93
|
|
|
|
|
150
|
$p1_x = $x + shift @points; |
|
325
|
93
|
|
|
|
|
170
|
$p1_y = $y + shift @points; |
|
326
|
93
|
|
|
|
|
133
|
$p2_x = $x + shift @points; |
|
327
|
93
|
|
|
|
|
136
|
$p2_y = $y + shift @points; |
|
328
|
|
|
|
|
|
|
# if we run out of data points, use the end point instead |
|
329
|
93
|
50
|
|
|
|
223
|
if (scalar @points == 0) { |
|
330
|
0
|
|
|
|
|
0
|
$p3_x = $x2; |
|
331
|
0
|
|
|
|
|
0
|
$p3_y = $y2; |
|
332
|
|
|
|
|
|
|
} else { |
|
333
|
93
|
|
|
|
|
160
|
$p3_x = $x + shift @points; |
|
334
|
93
|
|
|
|
|
159
|
$p3_y = $y + shift @points; |
|
335
|
|
|
|
|
|
|
} |
|
336
|
93
|
|
|
|
|
316
|
$self->curve($p1_x,$p1_y, $p2_x,$p2_y, $p3_x,$p3_y); |
|
337
|
93
|
|
|
|
|
156
|
shift @points; |
|
338
|
93
|
|
|
|
|
238
|
shift @points; |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
|
|
341
|
11
|
|
|
|
|
30
|
return $self; |
|
342
|
|
|
|
|
|
|
} |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
# input: x and y axis radii |
|
345
|
|
|
|
|
|
|
# sweep start and end angles (degrees) |
|
346
|
|
|
|
|
|
|
# sweep direction (0=CCW (default), or 1=CW) |
|
347
|
|
|
|
|
|
|
# axis rotation (radians, + = CCW, default = 0) |
|
348
|
|
|
|
|
|
|
# output: two endpoints and two control points for |
|
349
|
|
|
|
|
|
|
# the Bezier curve describing the arc |
|
350
|
|
|
|
|
|
|
# maximum 30 degrees of sweep: is broken up into smaller |
|
351
|
|
|
|
|
|
|
# arc segments if necessary |
|
352
|
|
|
|
|
|
|
# if crosses 0 degree angle in either sweep direction, split there at 0 |
|
353
|
|
|
|
|
|
|
# if alpha=beta (0 degree sweep) or either radius <= 0, fatal error |
|
354
|
|
|
|
|
|
|
sub _arctocurve { |
|
355
|
282
|
|
|
282
|
|
776
|
my ($rx,$ry, $alpha,$beta, $dir,$rot) = @_; |
|
356
|
|
|
|
|
|
|
|
|
357
|
282
|
100
|
|
|
|
545
|
if (!defined $rot) { $rot = 0; } # default is no rotation |
|
|
6
|
|
|
|
|
12
|
|
|
358
|
282
|
50
|
|
|
|
551
|
if (!defined $dir) { $dir = 0; } # default is CCW sweep |
|
|
0
|
|
|
|
|
0
|
|
|
359
|
|
|
|
|
|
|
# check for non-positive radius |
|
360
|
282
|
50
|
33
|
|
|
912
|
if ($rx <= 0 || $ry <= 0) { |
|
361
|
0
|
|
|
|
|
0
|
die "curve request with radius not > 0 ($rx, $ry)"; |
|
362
|
|
|
|
|
|
|
} |
|
363
|
|
|
|
|
|
|
# check for zero degrees of sweep |
|
364
|
282
|
50
|
|
|
|
557
|
if ($alpha == $beta) { |
|
365
|
0
|
|
|
|
|
0
|
die "curve request with zero degrees of sweep ($alpha to $beta)"; |
|
366
|
|
|
|
|
|
|
} |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
# constrain alpha and beta to 0..360 range so 0 crossing check works |
|
369
|
282
|
|
|
|
|
569
|
while ($alpha < 0.0) { $alpha += 360.0; } |
|
|
9
|
|
|
|
|
25
|
|
|
370
|
282
|
|
|
|
|
607
|
while ( $beta < 0.0) { $beta += 360.0; } |
|
|
3
|
|
|
|
|
9
|
|
|
371
|
282
|
|
|
|
|
577
|
while ($alpha > 360.0) { $alpha -= 360.0; } |
|
|
0
|
|
|
|
|
0
|
|
|
372
|
282
|
|
|
|
|
574
|
while ( $beta > 360.0) { $beta -= 360.0; } |
|
|
0
|
|
|
|
|
0
|
|
|
373
|
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
# Note that there is a problem with the original code, when the 0 degree |
|
375
|
|
|
|
|
|
|
# angle is crossed. It especially shows up in arc() and pie(). Therefore, |
|
376
|
|
|
|
|
|
|
# split the original sweep at 0 degrees, if it crosses that angle. |
|
377
|
282
|
100
|
100
|
|
|
741
|
if (!$dir && $alpha > $beta) { # CCW pass over 0 degrees |
|
378
|
7
|
50
|
33
|
|
|
41
|
if ($alpha == 360.0 && $beta == 0.0) { # oddball case |
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
379
|
0
|
|
|
|
|
0
|
return (_arctocurve($rx,$ry, 0.0,360.0, 0,$rot)); |
|
380
|
|
|
|
|
|
|
} elsif ($alpha == 360.0) { # alpha to 360 would be null |
|
381
|
0
|
|
|
|
|
0
|
return (_arctocurve($rx,$ry, 0.0,$beta, 0,$rot)); |
|
382
|
|
|
|
|
|
|
} elsif ($beta == 0.0) { # 0 to beta would be null |
|
383
|
1
|
|
|
|
|
5
|
return (_arctocurve($rx,$ry, $alpha,360.0, 0,$rot)); |
|
384
|
|
|
|
|
|
|
} else { |
|
385
|
|
|
|
|
|
|
return ( |
|
386
|
6
|
|
|
|
|
18
|
_arctocurve($rx,$ry, $alpha,360.0, 0,$rot), |
|
387
|
|
|
|
|
|
|
_arctocurve($rx,$ry, 0.0,$beta, 0,$rot) |
|
388
|
|
|
|
|
|
|
); |
|
389
|
|
|
|
|
|
|
} |
|
390
|
|
|
|
|
|
|
} |
|
391
|
275
|
100
|
100
|
|
|
711
|
if ($dir && $alpha < $beta) { # CW pass over 0 degrees |
|
392
|
2
|
50
|
33
|
|
|
15
|
if ($alpha == 0.0 && $beta == 360.0) { # oddball case |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
393
|
0
|
|
|
|
|
0
|
return (_arctocurve($rx,$ry, 360.0,0.0, 1,$rot)); |
|
394
|
|
|
|
|
|
|
} elsif ($alpha == 0.0) { # alpha to 0 would be null |
|
395
|
0
|
|
|
|
|
0
|
return (_arctocurve($rx,$ry, 360.0,$beta, 1,$rot)); |
|
396
|
|
|
|
|
|
|
} elsif ($beta == 360.0) { # 360 to beta would be null |
|
397
|
0
|
|
|
|
|
0
|
return (_arctocurve($rx,$ry, $alpha,0.0, 1,$rot)); |
|
398
|
|
|
|
|
|
|
} else { |
|
399
|
|
|
|
|
|
|
return ( |
|
400
|
2
|
|
|
|
|
6
|
_arctocurve($rx,$ry, $alpha,0.0, 1,$rot), |
|
401
|
|
|
|
|
|
|
_arctocurve($rx,$ry, 360.0,$beta, 1,$rot) |
|
402
|
|
|
|
|
|
|
); |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
} |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
# limit arc length to 30 degrees, for reasonable smoothness |
|
407
|
|
|
|
|
|
|
# none of the long arcs or short resulting arcs cross 0 degrees |
|
408
|
273
|
100
|
|
|
|
536
|
if (abs($beta-$alpha) > 30) { |
|
409
|
|
|
|
|
|
|
return ( |
|
410
|
124
|
|
|
|
|
361
|
_arctocurve($rx,$ry, $alpha,($beta+$alpha)/2, $dir,$rot), |
|
411
|
|
|
|
|
|
|
_arctocurve($rx,$ry, ($beta+$alpha)/2,$beta, $dir,$rot) |
|
412
|
|
|
|
|
|
|
); |
|
413
|
|
|
|
|
|
|
} else { |
|
414
|
|
|
|
|
|
|
# calculate cubic Bezier points (start, two control, end) |
|
415
|
149
|
|
|
|
|
239
|
my ($p0_x,$p0_y, $p1_x,$p1_y, $p2_x,$p2_y, $p3_x,$p3_y); |
|
416
|
|
|
|
|
|
|
# Note that we can't use deg2rad(), because closed arcs (circle() and |
|
417
|
|
|
|
|
|
|
# ellipse()) are 0-360 degrees, which deg2rad treats as 0-0 radians! |
|
418
|
149
|
|
|
|
|
230
|
my $aa = $alpha * 3.141593 / 180; |
|
419
|
149
|
|
|
|
|
239
|
my $bb = $beta * 3.141593 / 180; |
|
420
|
|
|
|
|
|
|
|
|
421
|
149
|
|
|
|
|
318
|
my $bcp = (4.0/3 * (1 - cos(($bb - $aa)/2)) / sin(($bb - $aa)/2)); |
|
422
|
149
|
|
|
|
|
231
|
my $sin_alpha = sin($aa); |
|
423
|
149
|
|
|
|
|
243
|
my $sin_beta = sin($bb); |
|
424
|
149
|
|
|
|
|
274
|
my $cos_alpha = cos($aa); |
|
425
|
149
|
|
|
|
|
240
|
my $cos_beta = cos($bb); |
|
426
|
|
|
|
|
|
|
|
|
427
|
149
|
|
|
|
|
226
|
$p0_x = $rx * $cos_alpha; |
|
428
|
149
|
|
|
|
|
204
|
$p0_y = $ry * $sin_alpha; |
|
429
|
149
|
|
|
|
|
240
|
$p1_x = $rx * ($cos_alpha - $bcp * $sin_alpha); |
|
430
|
149
|
|
|
|
|
237
|
$p1_y = $ry * ($sin_alpha + $bcp * $cos_alpha); |
|
431
|
149
|
|
|
|
|
228
|
$p2_x = $rx * ($cos_beta + $bcp * $sin_beta); |
|
432
|
149
|
|
|
|
|
231
|
$p2_y = $ry * ($sin_beta - $bcp * $cos_beta); |
|
433
|
149
|
|
|
|
|
213
|
$p3_x = $rx * $cos_beta; |
|
434
|
149
|
|
|
|
|
241
|
$p3_y = $ry * $sin_beta; |
|
435
|
|
|
|
|
|
|
|
|
436
|
149
|
|
|
|
|
674
|
return ($p0_x,$p0_y, $p1_x,$p1_y, $p2_x,$p2_y, $p3_x,$p3_y); |
|
437
|
|
|
|
|
|
|
} |
|
438
|
|
|
|
|
|
|
} |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
# Circular arc ('bogen'), by PDF::API2 and anhanced by PDF::Builder. |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
=over |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=item $content->bogen($x1,$y1, $x2,$y2, $radius, $move, $larger, $reverse) |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=item $content->bogen($x1,$y1, $x2,$y2, $radius, $move, $larger) |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
=item $content->bogen($x1,$y1, $x2,$y2, $radius, $move) |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=item $content->bogen($x1,$y1, $x2,$y2, $radius) |
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
(I is German for I, as in a segment (arc) of a circle. This is a |
|
453
|
|
|
|
|
|
|
segment of a circle defined by the intersection of two circles of a given |
|
454
|
|
|
|
|
|
|
radius, with the two intersection points as inputs. There are B possible |
|
455
|
|
|
|
|
|
|
resulting arcs, which can be selected with C<$larger> and C<$reverse>.) |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
This extends the path along an arc of a circle of the specified radius |
|
458
|
|
|
|
|
|
|
between C<[$x1,$y1]> to C<[$x2,$y2]>. The current position is then set |
|
459
|
|
|
|
|
|
|
to the endpoint of the arc (C<[$x2,$y2]>). |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
Set C<$move> to a I value if this arc is the beginning of a new |
|
462
|
|
|
|
|
|
|
path instead of the continuation of an existing path. Note that the default |
|
463
|
|
|
|
|
|
|
(C<$move> = I) is |
|
464
|
|
|
|
|
|
|
I a straight line to I and then the arc, but a blending into the curve |
|
465
|
|
|
|
|
|
|
from the current point. It will often I pass through I! |
|
466
|
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
Set C<$larger> to a I value to draw the larger ("outer") arc between the |
|
468
|
|
|
|
|
|
|
two points, instead of the smaller one. Both arcs are drawn I from |
|
469
|
|
|
|
|
|
|
I to I. The default value of I draws the smaller arc. |
|
470
|
|
|
|
|
|
|
Note that the "other" circle's larger arc is used (the center point is |
|
471
|
|
|
|
|
|
|
"flipped" across the line between I and I), rather than using the |
|
472
|
|
|
|
|
|
|
"remainder" of the smaller arc's circle (which would necessitate reversing the |
|
473
|
|
|
|
|
|
|
direction of travel along the arc -- see C<$reverse>). |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
Set C<$reverse> to a I value to draw the mirror image of the |
|
476
|
|
|
|
|
|
|
specified arc (flip it over, so that its center point is on the other |
|
477
|
|
|
|
|
|
|
side of the line connecting the two points). Both arcs are drawn |
|
478
|
|
|
|
|
|
|
I from I to I. The default (I) draws |
|
479
|
|
|
|
|
|
|
clockwise arcs. An arc is B drawn from I to I; the direction |
|
480
|
|
|
|
|
|
|
(clockwise or counter-clockwise) may be chosen. |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
The C<$radius> value cannot be smaller than B the distance from |
|
483
|
|
|
|
|
|
|
C<[$x1,$y1]> to C<[$x2,$y2]>. If it is too small, the radius will be set to |
|
484
|
|
|
|
|
|
|
half the distance between the points (resulting in an arc that is a |
|
485
|
|
|
|
|
|
|
semicircle). This is a silent error. |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
=back |
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=cut |
|
490
|
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
sub bogen { |
|
492
|
6
|
|
|
6
|
1
|
43
|
my ($self, $x1,$y1, $x2,$y2, $r, $move, $larc, $spf) = @_; |
|
493
|
|
|
|
|
|
|
|
|
494
|
6
|
|
|
|
|
27
|
my ($p0_x,$p0_y, $p1_x,$p1_y, $p2_x,$p2_y, $p3_x,$p3_y); |
|
495
|
6
|
|
|
|
|
0
|
my ($dx,$dy, $x,$y, $alpha,$beta, $alpha_rad, $d,$z, $dir, @points); |
|
496
|
|
|
|
|
|
|
|
|
497
|
6
|
50
|
33
|
|
|
21
|
if ($x1 == $x2 && $y1 == $y2) { |
|
498
|
0
|
|
|
|
|
0
|
die "bogen requires two distinct points"; |
|
499
|
|
|
|
|
|
|
} |
|
500
|
6
|
50
|
|
|
|
19
|
if ($r <= 0.0) { |
|
501
|
0
|
|
|
|
|
0
|
die "bogen requires a positive radius"; |
|
502
|
|
|
|
|
|
|
} |
|
503
|
6
|
50
|
|
|
|
16
|
$move = 0 if !defined $move; |
|
504
|
6
|
50
|
|
|
|
17
|
$larc = 0 if !defined $larc; |
|
505
|
6
|
50
|
|
|
|
16
|
$spf = 0 if !defined $spf; |
|
506
|
|
|
|
|
|
|
|
|
507
|
6
|
|
|
|
|
9
|
$dx = $x2 - $x1; |
|
508
|
6
|
|
|
|
|
10
|
$dy = $y2 - $y1; |
|
509
|
6
|
|
|
|
|
21
|
$z = sqrt($dx**2 + $dy**2); |
|
510
|
6
|
|
|
|
|
30
|
$alpha_rad = asin($dy/$z); # |dy/z| guaranteed <= 1.0 |
|
511
|
6
|
100
|
|
|
|
64
|
$alpha_rad = pi - $alpha_rad if $dx < 0; |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
# alpha is direction of vector P1 to P2 |
|
514
|
6
|
|
|
|
|
22
|
$alpha = rad2deg($alpha_rad); |
|
515
|
|
|
|
|
|
|
# use the complementary angle for flipped arc (arc center on other side) |
|
516
|
|
|
|
|
|
|
# effectively clockwise draw from P2 to P1 |
|
517
|
6
|
100
|
|
|
|
86
|
$alpha -= 180 if $spf; |
|
518
|
|
|
|
|
|
|
|
|
519
|
6
|
|
|
|
|
13
|
$d = 2*$r; |
|
520
|
|
|
|
|
|
|
# z/d must be no greater than 1.0 (arcsine arg) |
|
521
|
6
|
100
|
|
|
|
18
|
if ($z > $d) { |
|
522
|
1
|
|
|
|
|
3
|
$d = $z; # SILENT error and fixup |
|
523
|
1
|
|
|
|
|
3
|
$r = $d/2; |
|
524
|
|
|
|
|
|
|
} |
|
525
|
|
|
|
|
|
|
|
|
526
|
6
|
|
|
|
|
18
|
$beta = rad2deg(2*asin($z/$d)); |
|
527
|
|
|
|
|
|
|
# beta is the sweep P1 to P2: ~0 (r very large) to 180 degrees (min r) |
|
528
|
6
|
100
|
|
|
|
85
|
$beta = 360-$beta if $larc; # large arc is remainder of small arc |
|
529
|
|
|
|
|
|
|
# for large arc, beta could approach 360 degrees if r is very large |
|
530
|
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
# always draw CW (dir=1) |
|
532
|
|
|
|
|
|
|
# note that start and end could be well out of +/-360 degree range |
|
533
|
6
|
|
|
|
|
30
|
@points = _arctocurve($r,$r, 90+$alpha+$beta/2,90+$alpha-$beta/2, 1); |
|
534
|
|
|
|
|
|
|
|
|
535
|
6
|
100
|
|
|
|
47
|
if ($spf) { # flip order of points for reverse arc |
|
536
|
2
|
|
|
|
|
16
|
my @pts = @points; |
|
537
|
2
|
|
|
|
|
9
|
@points = (); |
|
538
|
2
|
|
|
|
|
7
|
while (@pts) { |
|
539
|
80
|
|
|
|
|
149
|
$y = pop @pts; |
|
540
|
80
|
|
|
|
|
124
|
$x = pop @pts; |
|
541
|
80
|
|
|
|
|
200
|
push(@points, $x,$y); |
|
542
|
|
|
|
|
|
|
} |
|
543
|
|
|
|
|
|
|
} |
|
544
|
|
|
|
|
|
|
|
|
545
|
6
|
|
|
|
|
11
|
$p0_x = shift @points; |
|
546
|
6
|
|
|
|
|
12
|
$p0_y = shift @points; |
|
547
|
6
|
|
|
|
|
16
|
$x = $x1 - $p0_x; |
|
548
|
6
|
|
|
|
|
11
|
$y = $y1 - $p0_y; |
|
549
|
|
|
|
|
|
|
|
|
550
|
6
|
50
|
|
|
|
15
|
$self->move($x1,$y1) if $move; |
|
551
|
|
|
|
|
|
|
|
|
552
|
6
|
|
|
|
|
16
|
while (scalar @points > 0) { |
|
553
|
56
|
|
|
|
|
109
|
$p1_x = $x + shift @points; |
|
554
|
56
|
|
|
|
|
99
|
$p1_y = $y + shift @points; |
|
555
|
56
|
|
|
|
|
98
|
$p2_x = $x + shift @points; |
|
556
|
56
|
|
|
|
|
89
|
$p2_y = $y + shift @points; |
|
557
|
|
|
|
|
|
|
# if we run out of data points, use the end point instead |
|
558
|
56
|
50
|
|
|
|
126
|
if (scalar @points == 0) { |
|
559
|
0
|
|
|
|
|
0
|
$p3_x = $x2; |
|
560
|
0
|
|
|
|
|
0
|
$p3_y = $y2; |
|
561
|
|
|
|
|
|
|
} else { |
|
562
|
56
|
|
|
|
|
111
|
$p3_x = $x + shift @points; |
|
563
|
56
|
|
|
|
|
103
|
$p3_y = $y + shift @points; |
|
564
|
|
|
|
|
|
|
} |
|
565
|
56
|
|
|
|
|
208
|
$self->curve($p1_x,$p1_y, $p2_x,$p2_y, $p3_x,$p3_y); |
|
566
|
56
|
|
|
|
|
125
|
shift @points; |
|
567
|
56
|
|
|
|
|
161
|
shift @points; |
|
568
|
|
|
|
|
|
|
} |
|
569
|
|
|
|
|
|
|
|
|
570
|
6
|
|
|
|
|
36
|
return $self; |
|
571
|
|
|
|
|
|
|
} |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
1; |