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