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