• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /**
17  * @file geometry_math_stroke.h
18  * @brief Defines
19  * @since 1.0
20  * @version 1.0
21  */
22 
23 #ifndef GRAPHIC_LITE_GEOERTY_MATH_STROKE_H
24 #define GRAPHIC_LITE_GEOERTY_MATH_STROKE_H
25 
26 #include "gfx_utils/diagram/common/common_math.h"
27 #include "gfx_utils/diagram/vertexprimitive/geometry_vertex_sequence.h"
28 #include "gfx_utils/graphic_math.h"
29 #include "gfx_utils/vector.h"
30 namespace OHOS {
31 /**
32  * @brief The style of the line end cap.
33  */
34 enum LineCap {
35     /** Add straight edges to each end of the line */
36     BUTT_CAP,
37     /** Add a square cap to each end of the line */
38     SQUARE_CAP,
39     /** Add a circular cap to each end of the line */
40     ROUND_CAP
41 };
42 
43 /**
44  * @brief The type of corner created when two lines intersect
45  */
46 enum LineJoin {
47     /** Create sharp corners */
48     MITER_JOIN = 0,
49     MITER_JOIN_REVERT = 1,
50     /** Create Fillets */
51     ROUND_JOIN = 2,
52     /** Create bevel */
53     BEVEL_JOIN = 3,
54     MITER_JOIN_ROUND = 4
55 };
56 
57 const float epsilon = 1e-6f;
58 
59 class GeometryMathStroke {
60 public:
GeometryMathStroke()61     GeometryMathStroke()
62         : strokeWidth_(ALPHA_HALF),
63           strokeWidthUsingAbs_(ALPHA_HALF),
64           strokeWidthPercentDivision_(ALPHA_HALF / BUF_SIZE),
65           strokeWidthSignal_(1),
66 #if defined(GRAPHIC_ENABLE_LINECAP_FLAG) && GRAPHIC_ENABLE_LINECAP_FLAG
67           lineCap_(BUTT_CAP),
68 #endif
69 #if defined(GRAPHIC_ENABLE_LINEJOIN_FLAG) && GRAPHIC_ENABLE_LINEJOIN_FLAG
70           lineJoin_(MITER_JOIN),
71           miterLimitMeasure_(DEFAULTMITERLIMIT),
72 #endif
73           approxScaleRadio_(1.0f) {}
74 
75 #if defined(GRAPHIC_ENABLE_LINECAP_FLAG) && GRAPHIC_ENABLE_LINECAP_FLAG
76     /**
77      * @brief SetLineCap Defines the end style of the line
78      */
SetLineCap(LineCap lineCapE)79     void SetLineCap(LineCap lineCapE)
80     {
81         lineCap_ = lineCapE;
82     }
83 
GetLineCap()84     LineCap GetLineCap() const
85     {
86         return lineCap_;
87     }
88     /**
89      * @brief Calculate end style.
90      * Pay attention to 90 degree rotation at both ends of the corner.
91      */
CalcCap(Graphic::Vector<PointF> & vertexConsumer,const VertexDist & vd0,const VertexDist & vd1,float len)92     void CalcCap(Graphic::Vector<PointF>& vertexConsumer, const VertexDist& vd0, const VertexDist& vd1, float len)
93     {
94         vertexConsumer.Clear();
95         if (len == 0.0f) {
96             len += VERTEX_DIST_EPSILON;
97         }
98         float dx1;
99         float dy1;
100         if (fabs(len) > epsilon) {
101             dx1 = (vd1.vertexYCoord - vd0.vertexYCoord) / len;
102             dy1 = (vd1.vertexXCoord - vd0.vertexXCoord) / len;
103         } else {
104             return;
105         }
106         float dx2 = 0;
107         float dy2 = 0;
108 
109         dx1 *= strokeWidth_;
110         dy1 *= strokeWidth_;
111 
112         if (lineCap_ != ROUND_CAP) {
113             if (lineCap_ == SQUARE_CAP) {
114                 dx2 = dy1 * strokeWidthSignal_;
115                 dy2 = dx1 * strokeWidthSignal_;
116             }
117             AddVertex(vertexConsumer, vd0.vertexXCoord - dx1 - dx2, vd0.vertexYCoord + dy1 - dy2);
118             AddVertex(vertexConsumer, vd0.vertexXCoord + dx1 - dx2, vd0.vertexYCoord - dy1 - dy2);
119         } else {
120             float deltaAngle = Acos(strokeWidthUsingAbs_ /
121                                         (strokeWidthUsingAbs_ +RADDALETAELPS / approxScaleRadio_)) * TWO_TIMES;
122             float angleStart;
123             int32_t nIndex;
124             int32_t divNumber = int32_t(PI / deltaAngle);
125 
126             deltaAngle = PI / (divNumber + 1);
127             AddVertex(vertexConsumer, vd0.vertexXCoord - dx1, vd0.vertexYCoord + dy1);
128             if (strokeWidthSignal_ > 0) {
129                 angleStart = FastAtan2F(dy1, -dx1);
130                 angleStart += deltaAngle;
131                 for (nIndex = 0; nIndex < divNumber; nIndex++) {
132                     AddVertex(vertexConsumer, vd0.vertexXCoord + Cos(angleStart * RADIAN_TO_ANGLE) * strokeWidth_,
133                               vd0.vertexYCoord + Sin(angleStart * RADIAN_TO_ANGLE) * strokeWidth_);
134                     angleStart += deltaAngle;
135                 }
136             } else {
137                 angleStart = FastAtan2F(-dy1, dx1);
138                 angleStart -= deltaAngle;
139                 for (nIndex = 0; nIndex < divNumber; nIndex++) {
140                     AddVertex(vertexConsumer, vd0.vertexXCoord + Cos(angleStart * RADIAN_TO_ANGLE) * strokeWidth_,
141                               vd0.vertexYCoord + Sin(angleStart * RADIAN_TO_ANGLE) * strokeWidth_);
142                     angleStart -= deltaAngle;
143                 }
144             }
145             AddVertex(vertexConsumer, vd0.vertexXCoord + dx1, vd0.vertexYCoord - dy1);
146         }
147     }
148 #endif
149 
150 #if defined(GRAPHIC_ENABLE_LINEJOIN_FLAG) && GRAPHIC_ENABLE_LINEJOIN_FLAG
151     /**
152      * @brief SetLineJoin Defines the type of corner created when two lines intersect.
153      * Pay attention to 90 degree rotation at both ends of the corner.
154      */
SetLineJoin(LineJoin lineJoinE)155     void SetLineJoin(LineJoin lineJoinE)
156     {
157         lineJoin_ = lineJoinE;
158     }
159 
160     /**
161      * @brief SetMiterLimit Sets the maximum miter length.
162      */
SetMiterLimit(float miterLimit)163     void SetMiterLimit(float miterLimit)
164     {
165         miterLimitMeasure_ = miterLimit;
166     }
167 
168     /**
169      * @brief Calculate intersections and corners.
170      * Pay attention to 90 degree rotation at both ends of the corner.
171      */
CalcJoin(Graphic::Vector<PointF> & vertexConsumer,const VertexDist & vertexDistBegin,const VertexDist & vertexDistMiddle,const VertexDist & vertexDistLast,float deltaLengthPrev,float deltaLengthLast)172     void CalcJoin(Graphic::Vector<PointF>& vertexConsumer,
173                   const VertexDist& vertexDistBegin,
174                   const VertexDist& vertexDistMiddle,
175                   const VertexDist& vertexDistLast,
176                   float deltaLengthPrev,
177                   float deltaLengthLast)
178     {
179         bool isEnable = true;
180         if (deltaLengthPrev == 0.0f) {
181             deltaLengthPrev += VERTEX_DIST_EPSILON;
182         }
183         if (deltaLengthLast == 0.0f) {
184             deltaLengthPrev += VERTEX_DIST_EPSILON;
185         }
186         float dx1 = 0;
187         float dy1 = 0;
188         float dx2 = 0;
189         float dy2 = 0;
190         if (fabs(deltaLengthPrev) > epsilon) {
191             dx1 = strokeWidth_ * (vertexDistMiddle.vertexYCoord - vertexDistBegin.vertexYCoord) / deltaLengthPrev;
192             dy1 = strokeWidth_ * (vertexDistMiddle.vertexXCoord - vertexDistBegin.vertexXCoord) / deltaLengthPrev;
193         } else {
194             isEnable = false;
195         }
196         if (isEnable && (fabs(deltaLengthLast) > epsilon)) {
197             dx2 = strokeWidth_ * (vertexDistLast.vertexYCoord - vertexDistMiddle.vertexYCoord) / deltaLengthLast;
198             dy2 = strokeWidth_ * (vertexDistLast.vertexXCoord - vertexDistMiddle.vertexXCoord) / deltaLengthLast;
199         } else {
200             isEnable = false;
201         }
202         if (!isEnable) {
203             return;
204         }
205         vertexConsumer.Clear();
206         float crossProduct =
207             CrossProduct(vertexDistBegin.vertexXCoord, vertexDistBegin.vertexYCoord, vertexDistMiddle.vertexXCoord,
208                          vertexDistMiddle.vertexYCoord, vertexDistLast.vertexXCoord, vertexDistLast.vertexYCoord);
209         if ((fabs(crossProduct) > epsilon) && (crossProduct > 0) == (strokeWidth_ > 0)) {
210             float limit =
211                 ((deltaLengthPrev < deltaLengthLast) ? deltaLengthPrev : deltaLengthLast) / strokeWidthUsingAbs_;
212             CalcMiter(vertexConsumer, vertexDistBegin, vertexDistMiddle, vertexDistLast, dx1, dy1, dx2, dy2,
213                       MITER_JOIN_REVERT, limit, 0);
214         } else {
215             float dx = (dx1 + dx2) * HALFNUM;
216             float dy = (dy1 + dy2) * HALFNUM;
217             float dbevel = Sqrt(dx * dx + dy * dy);
218             float lim = strokeWidthUsingAbs_ * miterLimitMeasure_;
219             bool isIntersection =
220                 CalcIntersection(vertexDistBegin.vertexXCoord + dx1, vertexDistBegin.vertexYCoord - dy1,
221                                  vertexDistMiddle.vertexXCoord + dx1, vertexDistMiddle.vertexYCoord - dy1,
222                                  vertexDistMiddle.vertexXCoord + dx2, vertexDistMiddle.vertexYCoord - dy2,
223                                  vertexDistLast.vertexXCoord + dx2, vertexDistLast.vertexYCoord - dy2, &dx, &dy);
224             LineJoin lineJoin = lineJoin_;
225             if (lineJoin == MITER_JOIN) {
226                 if (CalcDistance(vertexDistMiddle.vertexXCoord, vertexDistMiddle.vertexYCoord, dx, dy) > lim) {
227                     lineJoin = BEVEL_JOIN;
228                 }
229             }
230             bool isRoundOrBevel = false;
231             if (lineJoin == ROUND_JOIN || lineJoin == BEVEL_JOIN) {
232                 if (approxScaleRadio_ * (strokeWidthUsingAbs_ - dbevel) < strokeWidthPercentDivision_) {
233                     if (isIntersection) {
234                         AddVertex(vertexConsumer, dx, dy);
235                     } else {
236                         AddVertex(vertexConsumer, vertexDistMiddle.vertexXCoord + dx1,
237                                   vertexDistMiddle.vertexYCoord - dy1);
238                     }
239                     isRoundOrBevel = true;
240                 }
241             }
242             if (isRoundOrBevel) {
243                 return;
244             }
245 
246             switch (lineJoin) {
247                 case MITER_JOIN:
248                 case MITER_JOIN_REVERT:
249                 case MITER_JOIN_ROUND:
250                     CalcMiter(vertexConsumer, vertexDistBegin, vertexDistMiddle,
251                               vertexDistLast, dx1, dy1, dx2, dy2, lineJoin_, miterLimitMeasure_, dbevel);
252                     break;
253                 case ROUND_JOIN:
254                     CalcArc(vertexConsumer, vertexDistMiddle.vertexXCoord,
255                             vertexDistMiddle.vertexYCoord, dx1, -dy1, dx2, -dy2);
256                     break;
257 
258                 default:
259                     AddVertex(vertexConsumer, vertexDistMiddle.vertexXCoord + dx1,
260                               vertexDistMiddle.vertexYCoord - dy1);
261                     AddVertex(vertexConsumer, vertexDistMiddle.vertexXCoord + dx2,
262                               vertexDistMiddle.vertexYCoord - dy2);
263                     break;
264             }
265         }
266     }
267 
268     /**
269      * @brief Calculate miter length
270      */
CalcMiter(Graphic::Vector<PointF> & vertexConsumer,const VertexDist & vd0,const VertexDist & vd1,const VertexDist & vd2,float dx1,float dy1,float dx2,float dy2,LineJoin linejoin,float mlimit,float dbevel)271     void CalcMiter(Graphic::Vector<PointF>& vertexConsumer,
272                    const VertexDist& vd0,
273                    const VertexDist& vd1,
274                    const VertexDist& vd2,
275                    float dx1,
276                    float dy1,
277                    float dx2,
278                    float dy2,
279                    LineJoin linejoin,
280                    float mlimit,
281                    float dbevel)
282     {
283         float xi = vd1.vertexXCoord;
284         float yi = vd1.vertexYCoord;
285         float di = 1;
286         float lim = strokeWidthUsingAbs_ * mlimit;
287         bool miterLimitExceeded = true;
288         bool intersectionFailed = true;
289         if (CalcIntersection(vd0.vertexXCoord + dx1, vd0.vertexYCoord - dy1, vd1.vertexXCoord + dx1,
290                              vd1.vertexYCoord - dy1, vd1.vertexXCoord + dx2, vd1.vertexYCoord - dy2,
291                              vd2.vertexXCoord + dx2, vd2.vertexYCoord - dy2, &xi, &yi)) {
292             di = CalcDistance(vd1.vertexXCoord, vd1.vertexYCoord, xi, yi);
293             if (di <= lim) {
294                 AddVertex(vertexConsumer, xi, yi);
295                 miterLimitExceeded = false;
296             }
297             intersectionFailed = false;
298         } else {
299             float x2 = vd1.vertexXCoord + dx1;
300             float y2 = vd1.vertexYCoord - dy1;
301             if ((CrossProduct(vd0.vertexXCoord, vd0.vertexYCoord,
302                               vd1.vertexXCoord, vd1.vertexYCoord, x2, y2) < 0.0f) ==
303                 (CrossProduct(vd1.vertexXCoord, vd1.vertexYCoord,
304                               vd2.vertexXCoord, vd2.vertexYCoord, x2, y2) < 0.0f)) {
305                 AddVertex(vertexConsumer, vd1.vertexXCoord + dx1, vd1.vertexYCoord - dy1);
306                 miterLimitExceeded = false;
307             }
308         }
309 
310         if (miterLimitExceeded) {
311             switch (linejoin) {
312                 case MITER_JOIN_REVERT:
313                     AddVertex(vertexConsumer, vd1.vertexXCoord + dx1, vd1.vertexYCoord - dy1);
314                     AddVertex(vertexConsumer, vd1.vertexXCoord + dx2, vd1.vertexYCoord - dy2);
315                     break;
316                 case MITER_JOIN_ROUND:
317                     CalcArc(vertexConsumer, vd1.vertexXCoord, vd1.vertexYCoord, dx1, -dy1, dx2, -dy2);
318                     break;
319                 default:
320                     if (intersectionFailed) {
321                         mlimit *= strokeWidthSignal_;
322                         AddVertex(vertexConsumer, vd1.vertexXCoord + dx1 + dy1 * mlimit,
323                                   vd1.vertexYCoord - dy1 + dx1 * mlimit);
324                         AddVertex(vertexConsumer, vd1.vertexXCoord + dx2 - dy2 * mlimit,
325                                   vd1.vertexYCoord - dy2 - dx2 * mlimit);
326                     } else {
327                         float x1 = vd1.vertexXCoord + dx1;
328                         float y1 = vd1.vertexYCoord - dy1;
329                         float x2 = vd1.vertexXCoord + dx2;
330                         float y2 = vd1.vertexYCoord - dy2;
331                         di = (lim - dbevel) / (di - dbevel);
332                         AddVertex(vertexConsumer, x1 + (xi - x1) * di, y1 + (yi - y1) * di);
333                         AddVertex(vertexConsumer, x2 + (xi - x2) * di, y2 + (yi - y2) * di);
334                     }
335                     break;
336             }
337         }
338     }
CalcArc(Graphic::Vector<PointF> & vertexConsumer,float x,float y,float dx1,float dy1,float dx2,float dy2)339     void CalcArc(Graphic::Vector<PointF>& vertexConsumer, float x, float y, float dx1, float dy1, float dx2, float dy2)
340     {
341         const float limitScale = 0.125f;
342         float angleStart = FastAtan2F(dy1 * strokeWidthSignal_, dx1 * strokeWidthSignal_);
343         float angleEnd = FastAtan2F(dy2 * strokeWidthSignal_, dx2 * strokeWidthSignal_);
344         float deltaAngle = angleStart - angleEnd;
345         int32_t nIndex;
346         int32_t divNumber;
347 
348         deltaAngle = Acos(strokeWidthUsingAbs_ / (strokeWidthUsingAbs_ + limitScale / approxScaleRadio_))
349                 * TWO_TIMES;
350 
351         AddVertex(vertexConsumer, x + dx1, y + dy1);
352         if (strokeWidthSignal_ > 0) {
353             if (angleStart > angleEnd) {
354                 angleEnd += TWO_TIMES * PI;
355             }
356             divNumber = int32_t((angleEnd - angleStart) / deltaAngle);
357             deltaAngle = (angleEnd - angleStart) / (divNumber + 1);
358             angleStart += deltaAngle;
359             for (nIndex = 0; nIndex < divNumber; nIndex++) {
360                 AddVertex(vertexConsumer, x + Cos(angleStart * RADIAN_TO_ANGLE) * strokeWidth_,
361                           y + Sin(angleStart * RADIAN_TO_ANGLE) * strokeWidth_);
362                 angleStart += deltaAngle;
363             }
364         } else {
365             if (angleStart < angleEnd) {
366                 angleEnd -= TWO_TIMES * PI;
367             }
368             divNumber = int32_t((angleStart - angleEnd) / deltaAngle);
369             deltaAngle = (angleStart - angleEnd) / (divNumber + 1);
370             angleStart -= deltaAngle;
371             for (nIndex = 0; nIndex < divNumber; nIndex++) {
372                 AddVertex(vertexConsumer, x + Cos(angleStart * RADIAN_TO_ANGLE) * strokeWidth_,
373                           y + Sin(angleStart * RADIAN_TO_ANGLE) * strokeWidth_);
374                 angleStart -= deltaAngle;
375             }
376         }
377         AddVertex(vertexConsumer, x + dx2, y + dy2);
378     }
GetLineJoin()379     LineJoin GetLineJoin() const
380     {
381         return lineJoin_;
382     }
383     /**
384      * @brief GetMiterLimit Returns the maximum miter length
385      */
GetMiterLimit()386     float GetMiterLimit() const
387     {
388         return miterLimitMeasure_;
389     }
390 #endif
391 
392     /**
393      * @brief width Set area width
394      */
SetWidth(float width)395     void SetWidth(float width)
396     {
397         strokeWidth_ = width * ALPHA_HALF;
398         if (strokeWidth_ < 0) {
399             strokeWidthUsingAbs_ = -strokeWidth_;
400             strokeWidthSignal_ = -1;
401         } else {
402             strokeWidthUsingAbs_ = strokeWidth_;
403             strokeWidthSignal_ = 1;
404         }
405         strokeWidthPercentDivision_ = strokeWidth_ / BUF_SIZE;
406     }
407 
408     /**
409      * @brief Add approximation
410      */
SetApproximationScale(float approximationScale)411     void SetApproximationScale(float approximationScale)
412     {
413         approxScaleRadio_ = approximationScale;
414     }
415 
416     /**
417      * @brief width Return width
418      */
GetWidth()419     float GetWidth() const
420     {
421         return strokeWidth_ * TWO_TIMES;
422     }
423 
424     /**
425      * @brief Returns the set approximate value
426      */
GetApproximationScale()427     float GetApproximationScale() const
428     {
429         return approxScaleRadio_;
430     }
431 
432 private:
AddVertex(Graphic::Vector<PointF> & vertexConsumer,float x,float y)433     inline void AddVertex(Graphic::Vector<PointF>& vertexConsumer, float x, float y)
434     {
435         vertexConsumer.PushBack(PointF(x, y));
436     }
437 
438     float strokeWidth_;
439     float strokeWidthUsingAbs_;
440     float strokeWidthPercentDivision_;
441     int32_t strokeWidthSignal_;
442 #if defined(GRAPHIC_ENABLE_LINECAP_FLAG) && GRAPHIC_ENABLE_LINECAP_FLAG
443     LineCap lineCap_;
444 #endif
445 #if defined(GRAPHIC_ENABLE_LINEJOIN_FLAG) && GRAPHIC_ENABLE_LINEJOIN_FLAG
446     LineJoin lineJoin_;
447     float miterLimitMeasure_;
448 #endif
449     float approxScaleRadio_;
450 };
451 } // namespace OHOS
452 #endif
453