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