1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "gfx_utils/diagram/vertexprimitive/geometry_bezier_arc.h"
17
18 namespace OHOS {
19 #if defined(GRAPHIC_ENABLE_BEZIER_ARC_FLAG) && GRAPHIC_ENABLE_BEZIER_ARC_FLAG
20 const uint16_t BEZIER_ARC_SETUP = 2;
21
22 const uint16_t BEZIER_ARC_VERTICES_SIZE_STEP = 6;
23
24 const uint16_t BEZIER_ARC_POINTS = 4;
25 /* Limit Value of Bezier Arc */
26 const float BEZIER_ARC_ANGLE_EPSILON = 0.01f;
27
28 const float BEZIER_ARC_DELTAX = 4.0f;
29
30 const float BEZIER_ARC_EQUAL_DIVISION = 3.0f;
31
32 const float BEZIER_ARC_RADIICHECK = 10.0f;
33
ArcToBezier(float cx,float cy,float rx,float ry,float startAngle,float sweepAngle,float * curve)34 void ArcToBezier(float cx, float cy, float rx, float ry,
35 float startAngle, float sweepAngle,
36 float* curve)
37 {
38 float y0 = Sin((sweepAngle / FLOATNUM) * RADIAN_TO_ANGLE);
39 float x0 = Cos((sweepAngle / FLOATNUM) * RADIAN_TO_ANGLE);
40 float tx = (1.0f - x0) * BEZIER_ARC_DELTAX / BEZIER_ARC_EQUAL_DIVISION;
41 if (y0 == 0) {
42 y0 = y0 + VERTEX_DIST_EPSILON;
43 }
44 float ty = y0 - tx * x0 / y0;
45 float px[BEZIER_ARC_POINTS];
46 float py[BEZIER_ARC_POINTS];
47 px[0] = x0;
48 py[0] = -y0;
49 px[1] = x0 + tx;
50 py[1] = -ty;
51 px[2] = x0 + tx;
52 py[2] = ty;
53 px[3] = x0;
54 py[3] = y0;
55
56 float cosVal = Cos((startAngle + sweepAngle / FLOATNUM) * RADIAN_TO_ANGLE);
57 float sinVal = Sin((startAngle + sweepAngle / FLOATNUM) * RADIAN_TO_ANGLE);
58
59 for (uint16_t i = 0; i < BEZIER_ARC_POINTS; i++) {
60 curve[i * BEZIER_ARC_SETUP] = cx + rx * (px[i] * cosVal - py[i] * sinVal);
61 curve[i * BEZIER_ARC_SETUP + 1] = cy + ry * (px[i] * sinVal + py[i] * cosVal);
62 }
63 }
64
Init(float centerX,float centerY,float rx,float ry,float startAngle,float sweepAngle)65 void BezierArc::Init(float centerX, float centerY,
66 float rx, float ry,
67 float startAngle,
68 float sweepAngle)
69 {
70 startAngle = Fmod(startAngle, FLOATNUM * PI);
71 if (sweepAngle <= -FLOATNUM * PI) {
72 sweepAngle = -FLOATNUM * PI;
73 }
74 if (sweepAngle >= FLOATNUM * PI) {
75 sweepAngle = FLOATNUM * PI;
76 }
77 if (MATH_ABS(sweepAngle) < 1e-10) {
78 numberVertices_ = BEZIER_ARC_POINTS;
79 currentCommand_ = PATH_CMD_LINE_TO;
80 arrayVertices_[0] = centerX + rx * Cos(startAngle * RADIAN_TO_ANGLE);
81 arrayVertices_[1] = centerY + ry * Sin(startAngle * RADIAN_TO_ANGLE);
82 arrayVertices_[2] = centerX + rx * Cos((startAngle + sweepAngle) * RADIAN_TO_ANGLE);
83 arrayVertices_[3] = centerY + ry * Sin((startAngle + sweepAngle) * RADIAN_TO_ANGLE);
84 return;
85 }
86
87 float prevSweep;
88 float totalSweep = 0.0f;
89 float localSweep = 0.0f;
90 numberVertices_ = BEZIER_ARC_SETUP;
91 currentCommand_ = PATH_CMD_CURVE4;
92 bool done = false;
93 do {
94 if (sweepAngle < 0.0f) {
95 prevSweep = totalSweep;
96 totalSweep -= PI * HALFNUM;
97 localSweep = -PI * HALFNUM;
98 if (totalSweep <= sweepAngle + BEZIER_ARC_ANGLE_EPSILON) {
99 localSweep = sweepAngle - prevSweep;
100 done = true;
101 }
102 } else {
103 prevSweep = totalSweep;
104 totalSweep += PI * HALFNUM;
105 localSweep = PI * HALFNUM;
106 if (totalSweep >= sweepAngle - BEZIER_ARC_ANGLE_EPSILON) {
107 localSweep = sweepAngle - prevSweep;
108 done = true;
109 }
110 }
111
112 ArcToBezier(centerX, centerY, rx, ry, startAngle, localSweep, arrayVertices_
113 + numberVertices_ - BEZIER_ARC_SETUP);
114
115 startAngle += localSweep;
116 numberVertices_ += BEZIER_ARC_VERTICES_SIZE_STEP;
117 } while (numberVertices_ < BEZIER_ARC_VERTEX_NUM && !done);
118 }
119
Init(float x0,float y0,float rx,float ry,float angle,bool largeArcFlag,bool sweepFlag,float x2,float y2)120 void BezierArcSvg::Init(float x0, float y0,
121 float rx, float ry,
122 float angle,
123 bool largeArcFlag,
124 bool sweepFlag,
125 float x2, float y2)
126 {
127 if (ry < 0.0f) {
128 ry = -ry;
129 }
130 if (rx < 0.0f) {
131 rx = -rx;
132 }
133 isRadiusJoinPath_ = true;
134 float delatY2 = (y0 - y2) / FLOATNUM;
135 float delatX2 = (x0 - x2) / FLOATNUM;
136 float sinA = Sin(angle * RADIAN_TO_ANGLE);
137 float cosA = Cos(angle * RADIAN_TO_ANGLE);
138 float y1 = -sinA * delatX2 + cosA * delatY2;
139 float x1 = cosA * delatX2 + sinA * delatY2;
140 float prx = rx * rx;
141 float pry = ry * ry;
142 float px1 = x1 * x1;
143 float py1 = y1 * y1;
144 float radiiCheck = px1 / prx + py1 / pry;
145 if (radiiCheck > 1.0f) {
146 ry = Sqrt(radiiCheck) * ry;
147 rx = Sqrt(radiiCheck) * rx;
148 pry = ry * ry;
149 prx = rx * rx;
150 if (radiiCheck > BEZIER_ARC_RADIICHECK) {
151 isRadiusJoinPath_ = false;
152 }
153 }
154 float sign = (largeArcFlag == sweepFlag) ? -1.0f : 1.0f;
155 float sq = (prx * pry - prx * py1 - pry * px1) / (prx * py1 + pry * px1);
156 float coef = sign * Sqrt((sq < 0) ? 0 : sq);
157 if (ry == 0) {
158 ry += VERTEX_DIST_EPSILON;
159 }
160 if (rx == 0) {
161 rx += VERTEX_DIST_EPSILON;
162 }
163 float cx1 = coef * ((rx * y1) / ry);
164 float cy1 = coef * -((ry * x1) / rx);
165 float sx2 = (x0 + x2) / FLOATNUM;
166 float sy2 = (y0 + y2) / FLOATNUM;
167 float cx = sx2 + (cosA * cx1 - sinA * cy1);
168 float cy = sy2 + (sinA * cx1 + cosA * cy1);
169 float ux = (x1 - cx1) / rx;
170 float uy = (y1 - cy1) / ry;
171 float vx = (-x1 - cx1) / rx;
172 float vy = (-y1 - cy1) / ry;
173 float p = ux;
174 float n = Sqrt(ux * ux + uy * uy);
175 sign = (uy < 0) ? -1.0f : 1.0f;
176 if (n == 0) {
177 n += VERTEX_DIST_EPSILON;
178 }
179 float v = p / n;
180 if (v > 1.0f) {
181 v = 1.0f;
182 }
183 if (v < -1.0f) {
184 v = -1.0f;
185 }
186 float startAngle = sign * Acos(v);
187 n = Sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
188 p = ux * vx + uy * vy;
189 sign = (ux * vy - uy * vx < 0) ? -1.0f : 1.0f;
190 if (n == 0) {
191 n += VERTEX_DIST_EPSILON;
192 }
193 v = p / n;
194 if (v < -1.0f) {
195 v = -1.0f;
196 }
197 if (v > 1.0f) {
198 v = 1.0f;
199 }
200 float sweepAngle = sign * Acos(v);
201 if (!sweepFlag && sweepAngle > 0.0f) {
202 sweepAngle -= PI * FLOATNUM;
203 } else if (sweepFlag && sweepAngle < 0.0f) {
204 sweepAngle += PI * FLOATNUM;
205 }
206 bezierArcModel_.Init(0.0f, 0.0f, rx, ry, startAngle, sweepAngle);
207 TransAffine mtx = TransAffine::TransAffineRotation(angle);
208 mtx *= TransAffine::TransAffineTranslation(cx, cy);
209 uint32_t limit = bezierArcModel_.GetNumberVertices() - BEZIER_ARC_SETUP;
210 for (uint32_t i = BEZIER_ARC_SETUP; i < limit; i += BEZIER_ARC_SETUP) {
211 mtx.Transform(bezierArcModel_.GetVertices() + i, bezierArcModel_.GetVertices() + i + 1);
212 }
213 bezierArcModel_.GetVertices()[0] = x0;
214 bezierArcModel_.GetVertices()[1] = y0;
215 if (bezierArcModel_.GetNumberVertices() > BEZIER_ARC_SETUP) {
216 bezierArcModel_.GetVertices()[bezierArcModel_.GetNumberVertices() - BEZIER_ARC_SETUP] = x2;
217 bezierArcModel_.GetVertices()[bezierArcModel_.GetNumberVertices() - 1] = y2;
218 }
219 }
220 #endif
221 } // namespace OHOS
222