-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
oreilly_commons.xhtml
326 lines (281 loc) · 13.3 KB
/
oreilly_commons.xhtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title>SVG Arcs</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="http://rawgit.com/waldyrious/default.css/master/default.css" />
</head>
<body>
<h2>SVG Path: Elliptical Arc</h2>
<p>Lines are simple; two points on a path uniquely determine the line segment between them. Since an infinite number of curves can be drawn between two points, you must give additional information to draw a curved path between them. The simplest of the curves we will examine is the elliptical arc — that is, drawing a section of an ellipse that connects two points.
</p><p>Although arcs are visually the simplest curves, specifying a unique arc requires the <i>most</i> information. The first pieces of information you need to specify are the <i>x</i>- and <i>y</i>-radii of the ellipse on which the points lie. This narrows it down to two possible ellipses, as you can see in section (a) of <a href="http://commons.oreilly.com/wiki/index.php/SVG_Essentials/Paths#svgess-CHP-6-FIG-4" title="SVG Essentials/Paths">Figure 6-4</a>. The two points divide the two ellipses into four arcs. Two of them, (b) and (c), are arcs that measure less than 180 degrees. The other two, (d) and (e) are greater than 180 degrees. If you look at (b) and (c), you will notice that they are differentiated by their direction; (b) is drawn in the direction of increasing negative angle, and (c) in the direction of increasing positive angle. The same relationship holds true between (d) and (e).
</p><p>But wait — we still haven’t uniquely specified the potential arcs! There’s no law that says that the ellipse has to have its <i>x</i>-radius parallel to the <i>x</i>-axis. Part (f) of <a href="http://commons.oreilly.com/wiki/index.php/SVG_Essentials/Paths#svgess-CHP-6-FIG-4" title="SVG Essentials/Paths">Figure 6-4</a> shows the two points with their candidate ellipses rotated thirty degrees with respect to the <i>x</i>-axis.
</p>
<div>
<p><b>Figure 6-4. Variations of the elliptical arc command</b>
</p>
<p><a href="http://commons.oreilly.com/wiki/index.php/Image:SVG_Essentials_I_6_tt106.png" title="Variations of the elliptical arc command"><img alt="Variations of the elliptical arc command" src="oreilly_commons.png" border="0"/></a></p>
</div>
<p>(<a href="http://commons.oreilly.com/wiki/index.php/SVG_Essentials/Paths#svgess-CHP-6-FIG-4" title="SVG Essentials/Paths">Figure 6-4</a> is adapted from the one found in section 8.3.8 of the World Wide Web Consortium’s SVG specification.)</p>
<p>Thus, an arc command begins with the <tt>A</tt> abbreviation for absolute coordinates or <tt>a</tt> for relative coordinates, and is followed by seven parameters:</p>
<ul>
<li> The <i>x</i>- and <i>y</i>-radius of the ellipse on which the points lie.</li>
<li> The <tt><i>x-axis-rotation</i></tt> of the ellipse.</li>
<li> The <tt><i>large-arc-flag</i></tt>, which is zero if the arc’s measure is less than 180 degrees, or one if the arc’s measure is greater than or equal to 180 degrees.</li>
<li> The <tt><i>sweep-flag</i></tt>, which is zero if the arc is to be drawn in the negative angle direction, or one if the arc is to be drawn in the positive angle direction.</li>
<li> The ending <i>x</i>- and <i>y</i>- coordinates of the ending point. (The starting point is determined by the last point drawn or the last <i>moveto</i> command.)</li>
</ul>
<p>Here are the paths used to draw the elliptical arcs in sections (b) through (e) of <a href="http://commons.oreilly.com/wiki/index.php/SVG_Essentials/Paths#svgess-CHP-6-FIG-4" title="SVG Essentials/Paths">Figure 6-4</a>:
</p>
<pre><path d="M 125,75 A100,50 0 0,0 225,125"/> <!-- b -->
<path d="M 125,75 A100,50 0 0,1 225,125"/> <!-- c -->
<path d="M 125,75 A100,50 0 1,0 225,125"/> <!-- d -->
<path d="M 125,75 A100,50 0 1,1 225,125"/> <!-- e --></pre>
<h3>Technique: Converting from Other Arc Formats</h3>
<p>Some other vector graphics systems let you specify an arc by defining a center point for the ellipse, its <i>x</i>- and <i>y</i>-radius, the starting angle, and the extent of the angle’s arc. This is a straightforward method of specification, and is excellent for drawing arcs as single objects. This, paradoxically, is exactly why SVG instead chooses such a seemingly eccentric method to specify arcs. In SVG, an arc is not presumed to be living in lonely splendor; it is intended to be part of a connected path of lines and curves. (For example, a rounded rectangle is precisely that: a series of lines and elliptical arcs.) Thus, it makes sense to specify an arc by its endpoints.
</p><p>Sometimes, though, you do want an isolated semicircle (or, more accurately, semi-ellipse). Presume that you have an ellipse specified as:
</p>
<pre><ellipse cx="<i>cx</i>" cy="<i>cy</i>" rx="<i>rx</i>" ry="<i>ry</i>"/></pre>
<p>Here are the paths to draw the four possible semi-ellipses:
</p>
<pre><!-- northern hemisphere -->
<path d="M <i>cx</i>-<i>rx</i>
<i>cy</i> A <i>rx</i>
<i>ry</i> 0 1 1 <i>cx</i>+<i>rx</i>
<i>cy</i>"/>
<!-- southern hemipshere -->
<path d="M <i>cx</i>-<i>rx</i>
<i>cy</i> A <i>rx</i>
<i>ry</i> 0 1 0 <i>cx</i>+<i>rx</i>
<i>cy</i>"/>
<!-- eastern hemisphere -->
<path d="M <i>cx</i>
<i>cy</i>-<i>ry</i> A <i>rx</i>
<i>ry</i> 0 1 1 <i>cx</i>
<i>cy</i>+<i>ry</i>"/>
<!-- western hemisphere -->
<path d="M <i>cx</i>
<i>cy</i>-<i>ry</i> A <i>rx</i>
<i>ry</i> 0 1 0 <i>cx</i>
<i>cy</i>+<i>ry</i>"/></pre>
<p>For the more general case, when you wish to draw an arbitrary arc that has been specified in “center-and-angles” notation and wish to convert it to SVG’s “endpoint-and-sweep” format, use the following Perl script. It prompts you for center coordinates, radii, starting angle, and angle extent. The output is a <tt><path></tt> tag that you can insert into your SVG files.
</p>
<pre>#!/usr/bin/perl
use Math::Trig;
#
# Convert an elliptical arc based around a central point
# to an elliptical arc parameterized for SVG.
#
# Input is a list containing:
# center x coordinate
# center y coordinate
# x-radius of ellipse
# y-radius of ellipse
# beginning angle of arc in degrees
# arc extent in degrees
# x-axis rotation angle in degrees
#
# Output is a list containing:
#
# x-coordinate of beginning of arc
# y-coordinate of beginning of arc
# x-radius of ellipse
# y-radius of ellipse
# large-arc-flag as defined in SVG specification
# sweep-flag as defined in SVG specification
# x-coordinate of endpoint of arc
# y-coordinate of endpoint of arc
#
sub convert_to_svg
{
my ($cx, $cy, $rx, $ry, $theta1, $delta, $phi) = @_;
my $theta2 = $delta + $theta1;
$theta1 = deg2rad($theta1);
$theta2 = deg2rad($theta2);
my $phi_r = deg2rad($phi);
#
# Figure out the coordinates of the beginning and
# ending points
#
my $x0 = $cx + cos($phi_r) * $rx * cos($theta1) +
sin(-$phi_r) * $ry * sin($theta1);
my $y0 = $cy + sin($phi_r) * $rx * cos($theta1) +
cos($phi_r) * $ry * sin($theta1);
my $x1 = $cx + cos($phi_r) * $rx * cos($theta2) +
sin(-$phi_r) * $ry * sin($theta2);
my $y1 = $cy + sin($phi_r) * $rx * cos($theta2) +
cos($phi_r) * $ry * sin($theta2);
my $large_arc = ($delta > 180) ? 1 : 0;
my $sweep = ($delta > 0) ? 1 : 0;
return ($x0, $y0, $rx, $ry, $phi, $large_arc, $sweep, $x1, $y1);
}
#
# Request input
#
print "Enter center x,y coordinates > ";
$data = <>;
$data =~ s/,/ /g;
($cx, $cy) = split /\s+/, $data;
print "Enter x and y radii > ";
$data = <>;
$data =~ s/,/ /g;
($rx, $ry) = split/\s+/, $data;
print "Enter starting angle in degrees > ";
$theta = <>;
chomp $theta;
print "Enter angle extent in degrees > ";
$delta = <>;
chomp $delta;
print "Enter angle of rotation in degrees > ";
$phi = <>;
chomp $phi;
#
# Echo original data
#
print "(cx,cy)=($cx,$cy) rx=$rx ry=$ry ",
"start angle=$theta extent=$delta rotate=$phi\n";
($x0, $y0, $rx, $ry, $phi, $large_arc_flag, $sweep_flag, $x1, $y1) =
convert_to_svg( $cx, $cy, $rx, $ry, $theta, $delta, $phi);
#
# Produce a <path> element that fits the
# specifications
#
print "<path d=\"M $x0 $y0 ", # Moveto initial point
"A $rx $ry ", # Arc command and radii,
"$phi ", # angle of rotation,
"$large_arc_flag ", # the "large-arc" flag,
"$sweep_flag ", # the "sweep" flag,
"$x1 $y1\"/>\n"; # and the endpoint
</pre>
<p>If you wish to convert from the SVG format to a “center-and-angles” format, the mathematics is considerably more complex. You can see the formulas in detail in the SVG specification. This Perl script was adapted from the code in the Apache XML Batik project.
</p>
<pre>#!/usr/bin/perl
use Math::Trig;
#
# Convert an elliptical arc based around a central point
# to an elliptical arc parameterized for SVG.
#
# Input is a list containing:
#
# x-coordinate of beginning of arc
# y-coordinate of beginning of arc
# x-radius of ellipse
# y-radius of ellipse
# large-arc-flag as defined in SVG specification
# sweep-flag as defined in SVG specification
# x-coordinate of endpoint of arc
# y-coordinate of endpoint of arc
#
# Output is a list containing:
# center x coordinate
# center y coordinate
# x-radius of ellipse
# y-radius of ellipse
# beginning angle of arc in degrees
# arc extent in degrees
# x-axis rotation angle in degrees
#
sub convert_from_svg
{
my ($x0, $y0, $rx, $ry, $phi, $large_arc, $sweep, $x, $y) = @_;
# Compute 1/2 distance between current and final point
my $dx2 = ($x0 - $x) / 2.0;
my $dy2 = ($y0 - $y) / 2.0;
# Convert from degrees to radians
$phi %= 360;
my $phi_r = deg2rad($phi);
# Compute (x1, y1)
my $x1 = cos($phi_r) * $dx2 + sin($phi_r) * $dy2;
my $y1 = -sin($phi_r) * $dx2 + cos($phi_r) * $dy2;
# Make sure radii are large enough
$rx = abs($rx); $ry = abs($ry);
my $rx_sq = $rx * $rx;
my $ry_sq = $ry * $ry;
my $x1_sq = $x1 * $x1;
my $y1_sq = $y1 * $y1;
$radius_check = ($x1_sq / $rx_sq) + ($y1_sq / $ry_sq);
if ($radius_check > 1)
{
$rx *= sqrt($radius_check);
$ry *= sqrt($adius_check);
$rx_sq = $rx * $rx;
$ry_sq = $ry * $ry;
}
# Step 2: Compute (cx1, cy1)
my $sign = ($large_arc == $sweep) ? -1 : 1;
my $sq = (($rx_sq * $ry_sq) - ($rx_sq * $y1_sq) - ($ry_sq * $x1_sq)) /
(($rx_sq * $y1_sq) + ($ry_sq * $x1_sq));
$sq = ($sq < 0) ? 0 : $sq;
my $coef = $sign * sqrt($sq);
my $cx1 = $coef * (($rx * $y1) / $ry);
my $cy1 = $coef * -(($ry * $x1) / $rx);
# Step 3: Compute (cx, cy) from (cx1, cy1)
my $sx2 = ($x0 + $x) / 2.0;
my $sy2 = ($y0 + $y) / 2.0;
my $cx = $sx2 + (cos($phi_r) * $cx1 - sin($phi_r) * $cy1);
my $cy = $sy2 + (sin($phi_r) * $cx1 + cos($phi_r) * $cy1);
# Step 4: Compute angle start and angle extent
my $ux = ($x1 - $cx1) / $rx;
my $uy = ($y1 - $cy1) / $ry;
my $vx = (-$x1 - $cx1) / $rx;
my $vy = (-$y1 - $cy1) / $ry;
my $n = sqrt( ($ux * $ux) + ($uy * $uy) );
my $p = $ux; # 1 * ux + 0 * uy
$sign = ($uy < 0) ? -1 : 1;
my $theta = $sign * acos( $p / $n );
$theta = rad2deg($theta);
$n = sqrt(($ux * $ux + $uy * $uy) * ($vx * $vx + $vy * $vy));
$p = $ux * $vx + $uy * $vy;
$sign = (($ux * $vy - $uy * $vx) < 0) ? -1 : 1;
my $delta = $sign * acos( $p / $n );
$delta = rad2deg($delta);
if ($sweep == 0 && $delta > 0)
{
$delta -= 360;
}
elsif ($sweep == 1 && $delta < 0)
{
$delta += 360;
}
$delta %= 360;
$theta %= 360;
return ($cx, $cy, $rx, $ry, $theta, $delta, $phi);
}
#
# Request input
#
print "Enter starting x,y coordinates > ";
$data = <>;
$data =~ s/,/ /g;
($x0, $y0) = split /\s+/, $data;
print "Enter ending x,y coordinates > ";
$data = <>;
$data =~ s/,/ /g;
($x, $y) = split /\s+/, $data;
print "Enter x and y radii > ";
$data = <>;
$data =~ s/,/ /g;
($rx, $ry) = split/\s+/, $data;
print "Enter rotation angle in degrees ";
$phi = <>;
chomp $phi;
print "Large arc flag (0=no, 1=yes) > ";
$large_arc = <>;
chomp $large_arc;
print "Sweep flag (0=negative, 1=positive) > ";
$sweep = <>;
chomp $sweep;
print "From ($x0,$y0) to ($x,$y) rotate $phi",
" large arc=$large_arc sweep=$sweep\n";
($cx, $cy, $rx, $ry, $theta, $delta, $phi) =
convert_from_svg( $x0, $y0, $rx, $ry, $phi, $large_arc, $sweep,
$x, $y );
print "Ellipse center = ($cx, $cy)\n";
print "Start angle = $theta\n";
print "Angle extent = $delta\n";</pre>
<cite>
Content retrieved from
<a href="commons.oreilly.com/wiki/index.php/SVG_Essentials/Paths#Elliptical_Arc">O'Reilly Commons</a>.
</cite>
</body>
</html>