.
1 /*
2 * BirdFont code from SVG Salamander
3 *
4 * Copyright (c) 2004, Mark McKay
5 * Copyright (c) 2014, Johan Mattsson
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or
9 * without modification, are permitted provided that the following
10 * conditions are met:
11 *
12 * - Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 * - Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials
18 * provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 * OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
34 * projects can be found at http://www.kitfox.com
35 *
36 * Created on January 26, 2004, 8:40 PM
37 * Adapded to BirdFont on Juli 2, 2014, 5:01 PM
38 */
39
40 using Math;
41
42 namespace BirdFont {
43
44 /** Convert an SVG arc instruction to a Beziér path. */
45 static void add_arc_points (BezierPoints[] bezier_points, ref int bi, double x0, double y0, double rx, double ry, double angle, bool largeArcFlag, bool sweepFlag, double x, double y) {
46
47 //
48 // Elliptical arc implementation based on the SVG specification notes
49 //
50
51 double dx2, dy2, cosAngle, sinAngle;
52 double x1, y1, Prx, Pry, Px1, Py1, radiiCheck;
53 double sign, sq, coef, cx1, cy1;
54 double sx2, sy2, cx, cy;
55 double ux, uy, vx, vy, p, n;
56 double angleStart, angleExtent;
57 double s, step, theta;
58
59 // Compute the half distance between the current and the final point
60 dx2 = (x0 - x) / 2.0;
61 dy2 = (y0 - y) / 2.0;
62
63 // Convert angle from degrees to radians
64 angle = 2 * PI * ((angle % 360.0) / 360.0);
65
66 cosAngle = cos (angle);
67 sinAngle = sin (angle);
68
69 //
70 // Step 1 : Compute (x1, y1)
71 //
72 x1 = cosAngle * dx2 + sinAngle * dy2;
73 y1 = -sinAngle * dx2 + cosAngle * dy2;
74
75 // Ensure radii are large enough
76 rx = fabs(rx);
77 ry = fabs(ry);
78 Prx = rx * rx;
79 Pry = ry * ry;
80 Px1 = x1 * x1;
81 Py1 = y1 * y1;
82
83
84 // Check that radii are large enough
85 radiiCheck = Px1 / Prx + Py1 / Pry;
86
87 if (radiiCheck > 1) {
88 rx = sqrt (radiiCheck) * rx;
89 ry = sqrt (radiiCheck) * ry;
90 Prx = rx * rx;
91 Pry = ry * ry;
92 }
93
94 //
95 // Step 2 : Compute (cx1, cy1)
96 //
97 sign = (largeArcFlag == sweepFlag) ? -1 : 1;
98 sq = ((Prx * Pry) - (Prx * Py1) - (Pry * Px1)) / ((Prx * Py1) + (Pry * Px1));
99 sq = (sq < 0) ? 0 : sq;
100 coef = (sign * Math.sqrt(sq));
101 cx1 = coef * ((rx * y1) / ry);
102 cy1 = coef * -((ry * x1) / rx);
103
104 //
105 // Step 3 : Compute (cx, cy) from (cx1, cy1)
106 //
107
108 sx2 = (x0 + x) / 2.0;
109 sy2 = (y0 + y) / 2.0;
110 cx = sx2 - (cosAngle * cx1 - sinAngle * cy1);
111 cy = sy2 - (sinAngle * cx1 + cosAngle * cy1);
112
113 //
114 // Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle)
115 //
116
117 ux = (x1 - cx1) / rx;
118 uy = (y1 - cy1) / ry;
119 vx = (-x1 - cx1) / rx;
120 vy = (-y1 - cy1) / ry;
121
122 // Compute the angle start
123 n = sqrt((ux * ux) + (uy * uy));
124 p = ux; // (1 * ux) + (0 * uy)
125 sign = (uy < 0) ? -1d : 1d;
126 angleStart = sign * acos(p / n);
127
128 // Compute the angle extent
129 n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
130 p = ux * vx + uy * vy;
131 sign = (ux * vy - uy * vx < 0) ? -1d : 1d;
132 angleExtent = sign * Math.acos(p / n);
133
134 if(!sweepFlag && angleExtent > 0) {
135 angleExtent -= 2 *PI;
136 } else if (sweepFlag && angleExtent < 0) {
137 angleExtent += 2 *PI;
138 }
139 angleExtent %= 2 * PI;
140 angleStart %= 2 * PI;
141
142 angleExtent *= -1;
143 angleStart *= -1;
144
145 // Approximate the path with Beziér points
146 s = (angleExtent > 0) ? 1 : -1;
147 step = fabs (angleExtent) / (2 * fabs (angleExtent));
148
149 theta = PI - angleStart - angleExtent;
150
151 bezier_points[bi].type = 'L';
152 bezier_points[bi].svg_type = 'a';
153
154 bezier_points[bi].x0 = cx + rx * cos (theta);
155 bezier_points[bi].y0 = cy + ry * sin (theta);
156
157 bi++;
158
159 for (double a = 0; a < fabs (angleExtent); a += step) {
160 theta = PI - angleStart - angleExtent + s * a;
161
162 bezier_points[bi].type = 'S';
163 bezier_points[bi].svg_type = 'a';
164
165 bezier_points[bi].x0 = cx + rx * cos (theta);
166 bezier_points[bi].y0 = cy + ry * sin (theta);
167
168 bezier_points[bi].x1 = cx + rx * cos (theta + 1 * step / 4);
169 bezier_points[bi].y1 = cy + ry * sin (theta + 1 * step / 4);
170
171 bezier_points[bi].x2 = cx + rx * cos (theta + 2 * step / 4);
172 bezier_points[bi].y2 = cy + ry * sin (theta + 2 * step / 4);
173
174 bi++;
175 }
176 }
177
178 }
179
180