LCOV - code coverage report
Current view: top level - core/tess - SPTessLine.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 211 274 77.0 %
Date: 2024-05-12 00:16:13 Functions: 15 17 88.2 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #include "SPTessLine.h"
      25             : #include "SPTessSimd.hpp"
      26             : #include "SPVec4.h"
      27             : 
      28             : namespace STAPPLER_VERSIONIZED stappler::geom {
      29             : 
      30     4402963 : constexpr size_t getMaxRecursionDepth() { return 16; }
      31             : 
      32             : // based on:
      33             : // http://www.antigrain.com/research/adaptive_bezier/index.html
      34             : // https://www.khronos.org/registry/OpenVG/specs/openvg_1_0_1.pdf
      35             : // http://www.diva-portal.org/smash/get/diva2:565821/FULLTEXT01.pdf
      36             : // https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
      37             : 
      38             : struct EllipseData {
      39             :         float cx;
      40             :         float cy;
      41             :         float rx;
      42             :         float ry;
      43             :         float r_sq;
      44             :         float cos_phi;
      45             :         float sin_phi;
      46             : };
      47             : 
      48      128406 : static inline float draw_approx_err_sq(float e) {
      49      128406 :         e = (1.0f / e);
      50      128406 :         return e * e;
      51             : }
      52             : 
      53      131110 : static inline float draw_dist_sq(float x1, float y1, float x2, float y2) {
      54      131110 :         const float dx = x2 - x1, dy = y2 - y1;
      55      131110 :         return dx * dx + dy * dy;
      56             : }
      57             : 
      58      112725 : static inline float draw_angle(float v1_x, float v1_y, float v2_x, float v2_y) {
      59      112725 :         return atan2f(v1_x * v2_y - v1_y * v2_x, v1_x * v2_x + v1_y * v2_y);
      60             : }
      61             : 
      62           0 : static void drawQuadBezierRecursive(LineDrawer &drawer, float x0, float y0, float x1, float y1,
      63             :                 float x2, float y2, size_t depth) {
      64           0 :         if (depth >= getMaxRecursionDepth()) {
      65             :                 return;
      66             :         }
      67             : 
      68           0 :         const float x01_mid = (x0 + x1) / 2, y01_mid = (y0 + y1) / 2; // between 0 and 1
      69           0 :         const float x12_mid = (x1 + x2) / 2, y12_mid = (y1 + y2) / 2; // between 1 and 2
      70           0 :         const float x_mid = (x01_mid + x12_mid) / 2, y_mid = (y01_mid + y12_mid) / 2; // midpoint on curve
      71             : 
      72           0 :         const float dx = x2 - x0, dy = y2 - y0;
      73           0 :         const float d = fabsf(((x1 - x2) * dy - (y1 - y2) * dx)) * 2.0f; //* 0.5f;
      74             : 
      75           0 :         if (d > std::numeric_limits<float>::epsilon()) { // Regular case
      76           0 :                 const float d_sq = (d * d) / (dx * dx + dy * dy);
      77           0 :                 if (d_sq <= drawer.distanceError) {
      78           0 :                         if (drawer.angularError < std::numeric_limits<float>::epsilon()) {
      79           0 :                                 drawer.push((x1 + x_mid) / 2, (y1 + y_mid) / 2);
      80           0 :                                 return;
      81             :                         } else {
      82             :                                 // Curvature condition  (we need it for offset curve)
      83           0 :                                 const float da = fabsf(atan2f(y2 - y1, x2 - x1) - atan2f(y1 - y0, x1 - x0));
      84           0 :                                 if (std::min(da, float(2 * M_PI - da)) < drawer.angularError) {
      85           0 :                                         drawer.push((x1 + x_mid) / 2, (y1 + y_mid) / 2);
      86           0 :                                         return;
      87             :                                 }
      88             :                         }
      89             :                 }
      90             :         } else {
      91             :                 float sd;
      92           0 :                 const float da = dx * dx + dy * dy;
      93           0 :                 if (da == 0) {
      94           0 :                         sd = draw_dist_sq(x0, y0, x1, y1);
      95             :                 } else {
      96           0 :                         sd = ((x1 - x0) * dx + (y1 - y0) * dy) / da;
      97           0 :                         if (sd > 0 && sd < 1) {
      98             :                                 return; // degraded case
      99             :                         }
     100           0 :                         if (sd <= 0) {
     101           0 :                                 sd = draw_dist_sq(x1, y1, x0, y0);
     102           0 :                         } else if(sd >= 1) {
     103           0 :                                 sd = draw_dist_sq(x1, y1, x2, y2);
     104             :                         } else {
     105           0 :                                 sd = draw_dist_sq(x1, y1, x0 + sd * dx, y0 + sd * dy);
     106             :                         }
     107             :                 }
     108           0 :                 if (sd < drawer.distanceError) {
     109           0 :                         drawer.push(x1, y1);
     110           0 :                         return;
     111             :                 }
     112             :         }
     113             : 
     114           0 :         drawQuadBezierRecursive(drawer, x0, y0, x01_mid, y01_mid, x_mid, y_mid, depth + 1);
     115             :         drawQuadBezierRecursive(drawer, x_mid, y_mid, x12_mid, y12_mid, x2, y2, depth + 1);
     116             : }
     117             : 
     118     2612387 : static void drawCubicBezierRecursive(LineDrawer &drawer, float x0, float y0, float x1, float y1,
     119             :                 float x2, float y2, float x3, float y3, size_t depth) {
     120     4290132 :         if (depth >= getMaxRecursionDepth()) {
     121             :                 return;
     122             :         }
     123             : 
     124     4290132 :         const float x01_mid = (x0 + x1) / 2, y01_mid = (y0 + y1) / 2; // between 0 and 1
     125     4290132 :         const float x12_mid = (x1 + x2) / 2, y12_mid = (y1 + y2) / 2;// between 1 and 2
     126     4290132 :         const float x23_mid = (x2 + x3) / 2, y23_mid = (y2 + y3) / 2;// between 2 and 3
     127             : 
     128     4290132 :         const float x012_mid = (x01_mid + x12_mid) / 2, y012_mid = (y01_mid + y12_mid) / 2;// bisect midpoint in 012
     129     4290132 :         const float x123_mid = (x12_mid + x23_mid) / 2, y123_mid = (y12_mid + y23_mid) / 2;// bisect midpoint in 123
     130             : 
     131     4290132 :         const float x_mid = (x012_mid + x123_mid) / 2, y_mid = (y012_mid + y123_mid) / 2;// midpoint on curve
     132             : 
     133     4290132 :         const float dx = x3 - x0, dy = y3 - y0;
     134     4290132 :         const float d1 = fabsf(((x1 - x3) * dy - (y1 - y3) * dx)) * 2.0f; // * 0.5f;// distance factor from 0-3 to 1
     135     4290132 :         const float d2 = fabsf(((x2 - x3) * dy - (y2 - y3) * dx)) * 2.0f; // * 0.5f;// distance factor from 0-3 to 2
     136             : 
     137     4290132 :         const bool significantPoint1 = d1 > std::numeric_limits<float>::epsilon();
     138     4269153 :         const bool significantPoint2 = d2 > std::numeric_limits<float>::epsilon();
     139             : 
     140     4271555 :         if (significantPoint1 && significantPoint1) {
     141     4263971 :                 const float d_sq = ((d1 + d2) * (d1 + d2)) / (dx * dx + dy * dy);
     142     4263971 :                 if (d_sq <= drawer.distanceError) {
     143     2603203 :                         if (drawer.angularError < std::numeric_limits<float>::epsilon()) {
     144     2604103 :                                 drawer.push(x12_mid, y12_mid);
     145     2608370 :                                 return;
     146             :                         }
     147             : 
     148           0 :                         const float tmp = atan2(y2 - y1, x2 - x1);
     149           0 :                         const float da1 = fabs(tmp - atan2(y1 - y0, x1 - x0));
     150           0 :                         const float da2 = fabs(atan2(y3 - y2, x3 - x2) - tmp);
     151           0 :                         const float da = std::min(da1, float(2 * M_PI - da1)) + std::min(da2, float(2 * M_PI - da2));
     152           0 :                         if (da < drawer.angularError) {
     153           0 :                                 drawer.push(x12_mid, y12_mid);
     154           0 :                                 return;
     155             :                         }
     156             :                 }
     157             :         } else if (significantPoint1) {
     158             :                 const float d_sq = (d1 * d1) / (dx * dx + dy * dy);
     159             :                 if (d_sq <= drawer.distanceError) {
     160             :                         if (drawer.angularError < std::numeric_limits<float>::epsilon()) {
     161             :                                 drawer.push(x12_mid, y12_mid);
     162             :                                 return;
     163             :                         } else {
     164             :                                 const float da = fabsf(atan2f(y2 - y1, x2 - x1) - atan2f(y1 - y0, x1 - x0));
     165             :                                 if (std::min(da, float(2 * M_PI - da)) < drawer.angularError) {
     166             :                                         drawer.push(x1, y1);
     167             :                                         drawer.push(x2, y2);
     168             :                                         return;
     169             :                                 }
     170             :                         }
     171             :                 }
     172        7584 :         } else if (significantPoint2) {
     173        5051 :                 const float d_sq = (d2 * d2) / (dx * dx + dy * dy);
     174        5051 :                 if (d_sq <= drawer.distanceError) {
     175        3682 :                         if (drawer.angularError < std::numeric_limits<float>::epsilon()) {
     176        3682 :                                 drawer.push(x12_mid, y12_mid);
     177        3682 :                                 return;
     178             :                         } else {
     179           0 :                                 const float da = fabsf(atan2f(y3 - y2, x3 - x2) - atan2f(y2 - y1, x2 - x1));
     180           0 :                                 if (std::min(da, float(2 * M_PI - da)) < drawer.angularError) {
     181           0 :                                         drawer.push(x1, y1);
     182           0 :                                         drawer.push(x2, y2);
     183           0 :                                         return;
     184             :                                 }
     185             :                         }
     186             :                 }
     187             :         } else {
     188             :                 float sd1, sd2;
     189        2533 :                 const float k = dx * dx + dy * dy;
     190        2533 :                 if (k == 0) {
     191         758 :                         sd1 = draw_dist_sq(x0, y0, x1, y1);
     192         758 :                         sd2 = draw_dist_sq(x3, y3, x2, y2);
     193             :                 } else {
     194        1775 :                         sd1 = ((x1 - x0) * dx + (y1 - y0) * dy) / k;
     195        1775 :                         sd2 = ((x2 - x0) * dx + (y2 - y0) * dy) / k;
     196        1775 :                         if (sd1 > 0 && sd1 < 1 && sd2 > 0 && sd2 < 1) {
     197             :                                 return;
     198             :                         }
     199             : 
     200         487 :                         if (sd1 <= 0) {
     201         293 :                                 sd1 = draw_dist_sq(x1, y1, x0, y0);
     202         194 :                         } else if (sd1 >= 1) {
     203           0 :                                 sd1 = draw_dist_sq(x1, y1, x3, y3);
     204             :                         } else {
     205         194 :                                 sd1 = draw_dist_sq(x1, y1, x0 + d1 * dx, y0 + d1 * dy);
     206             :                         }
     207             : 
     208         487 :                         if (sd2 <= 0) {
     209           0 :                                 sd2 = draw_dist_sq(x2, y2, x0, y0);
     210         487 :                         } else if (sd2 >= 1) {
     211         487 :                                 sd2 = draw_dist_sq(x2, y2, x3, y3);
     212             :                         } else {
     213           0 :                                 sd2 = draw_dist_sq(x2, y2, x0 + d2 * dx, y0 + d2 * dy);
     214             :                         }
     215             :                 }
     216        1245 :                 if (sd1 > sd2) {
     217         194 :                         if (sd1 < drawer.distanceError) {
     218         133 :                                 drawer.push(x1, y1);
     219         133 :                                 return;
     220             :                         }
     221             :                 } else {
     222        1051 :                         if (sd2 < drawer.distanceError) {
     223        1051 :                                 drawer.push(x2, y2);
     224        1051 :                                 return;
     225             :                         }
     226             :                 }
     227             :         }
     228             : 
     229     1662198 :         drawCubicBezierRecursive(drawer, x0, y0, x01_mid, y01_mid, x012_mid, y012_mid, x_mid, y_mid, depth + 1);
     230             :         drawCubicBezierRecursive(drawer, x_mid, y_mid, x123_mid, y123_mid, x23_mid, y23_mid, x3, y3, depth + 1);
     231             : }
     232             : 
     233      128316 : static void drawArcRecursive(LineDrawer &drawer, const EllipseData &e, float startAngle, float sweepAngle,
     234             :                 float x0, float y0, float x1, float y1, size_t depth) {
     235      128541 :         if (depth >= getMaxRecursionDepth()) {
     236             :                 return;
     237             :         }
     238             : 
     239      128541 :         const float x01_mid = (x0 + x1) / 2, y01_mid = (y0 + y1) / 2;
     240             : 
     241      128541 :         const float n_sweep = sweepAngle / 2.0f;
     242             : 
     243      128541 :         const float sx_ = e.rx * cosf(startAngle + n_sweep), sy_ = e.ry * sinf(startAngle + n_sweep);
     244      128541 :         const float sx = e.cx - (sx_ * e.cos_phi - sy_ * e.sin_phi);
     245      128541 :         const float sy = e.cy + (sx_ * e.sin_phi + sy_ * e.cos_phi);
     246             : 
     247      128541 :         const float d = draw_dist_sq(x01_mid, y01_mid, sx, sy);
     248             : 
     249      128541 :         if (d < drawer.distanceError) {
     250      128327 :                 if (drawer.angularError < std::numeric_limits<float>::epsilon()) {
     251      127956 :                         drawer.push(sx, sy);
     252      128220 :                         return;
     253             :                 } else {
     254           0 :                         const float y0_x0 = y0 / x0;
     255           0 :                         const float y1_x1 = y1 / x1;
     256           0 :                         const float da = fabs(atanf( e.r_sq * (y1_x1 - y0_x0) / (e.r_sq * e.r_sq * y0_x0 * y1_x1) ));
     257           0 :                         if (da < drawer.angularError) {
     258           0 :                                 drawer.push(sx, sy);
     259           0 :                                 return;
     260             :                         }
     261             :                 }
     262             :         }
     263             : 
     264         214 :         drawArcRecursive(drawer, e, startAngle, n_sweep, x0, y0, sx, sy, depth + 1);
     265         225 :         drawer.push(sx, sy);
     266             :         drawArcRecursive(drawer, e, startAngle + n_sweep, n_sweep, sx, sy, x1, y1, depth + 1);
     267             : }
     268             : 
     269       56390 : static void drawArcBegin(LineDrawer &drawer, float x0, float y0, float rx, float ry, float phi,
     270             :                 bool largeArc, bool sweep, float x1, float y1) {
     271       56390 :         rx = fabsf(rx); ry = fabsf(ry);
     272             : 
     273       56390 :         const float sin_phi = sinf(phi), cos_phi = cosf(phi);
     274             : 
     275       56390 :         const float x01_dst = (x0 - x1) / 2, y01_dst = (y0 - y1) / 2;
     276       56390 :         const float x1_ = cos_phi * x01_dst + sin_phi * y01_dst;
     277       56390 :         const float y1_ = - sin_phi * x01_dst + cos_phi * y01_dst;
     278             : 
     279       56390 :         const float lambda = (x1_ * x1_) / (rx * rx) + (y1_ * y1_) / (ry * ry);
     280       56390 :         if (lambda > 1.0f) {
     281           0 :                 rx = sqrtf(lambda) * rx; ry = sqrtf(lambda) * ry;
     282             :         }
     283             : 
     284       56390 :         const float rx_y1_ = (rx * rx * y1_ * y1_);
     285       56390 :         const float ry_x1_ = (ry * ry * x1_ * x1_);
     286       56390 :         const float c_st = sqrtf(((rx * rx * ry * ry) - rx_y1_ - ry_x1_) / (rx_y1_ + ry_x1_));
     287             : 
     288       56390 :         const float cx_ = (largeArc != sweep ? 1.0f : -1.0f) * c_st * rx * y1_ / ry;
     289       56390 :         const float cy_ = (largeArc != sweep ? -1.0f : 1.0f) * c_st * ry * x1_ / rx;
     290             : 
     291       56390 :         const float cx = cx_ * cos_phi - cy_ * sin_phi + (x0 + x1) / 2;
     292       56390 :         const float cy = cx_ * sin_phi + cy_ * cos_phi + (y0 + y1) / 2;
     293             : 
     294       56390 :         float startAngle = draw_angle(1.0f, 0.0f, - (x1_ - cx_) / rx, (y1_ - cy_) / ry);
     295       56417 :         float sweepAngle = draw_angle((x1_ - cx_) / rx, (y1_ - cy_) / ry, (-x1_ - cx_) / rx, (-y1_ - cy_) / ry);
     296             : 
     297             :         sweepAngle = (largeArc)
     298      112561 :                         ? std::max(fabsf(sweepAngle), float(M_PI * 2 - fabsf(sweepAngle)))
     299       56151 :                         : std::min(fabsf(sweepAngle), float(M_PI * 2 - fabsf(sweepAngle)));
     300             : 
     301       56410 :         if (rx > std::numeric_limits<float>::epsilon() && ry > std::numeric_limits<float>::epsilon()) {
     302       53027 :                 const float r_avg = (rx + ry) / 2.0f;
     303       53027 :                 const float err = (r_avg - sqrtf(drawer.distanceError)) / r_avg;
     304       53027 :                 if (err > M_SQRT1_2 * 0.5f - std::numeric_limits<float>::epsilon()) {
     305       35243 :                         const float pts = ceilf(sweepAngle / (acos(err) * 2.0f)) + 1;
     306       35243 :                         EllipseData d{ cx, cy, rx, ry, (rx * rx) / (ry * ry), cos_phi, sin_phi };
     307             : 
     308       35243 :                         sweepAngle = (sweep ? -1.0f : 1.0f) * sweepAngle;
     309             : 
     310       35243 :                         const float segmentAngle = sweepAngle / pts;
     311             : 
     312       35243 :                         uint32_t npts = uint32_t(pts);
     313      145456 :                         for (uint32_t i = 0; i < uint32_t(pts); ++ i) {
     314      110140 :                                 const float sx_ = d.rx * cosf(startAngle + segmentAngle), sy_ = d.ry * sinf(startAngle + segmentAngle);
     315      110140 :                                 const float sx = d.cx - (sx_ * d.cos_phi - sy_ * d.sin_phi);
     316      110140 :                                 const float sy = d.cy + (sx_ * d.sin_phi + sy_ * d.cos_phi);
     317             : 
     318      110140 :                                 drawArcRecursive(drawer, d, startAngle, segmentAngle, x0, y0, sx, sy, 0);
     319             :                                 startAngle += segmentAngle;
     320             : 
     321      110518 :                                 if (npts - 1 == i) {
     322       35298 :                                         drawer.push(x1, y1);
     323             :                                         x0 = x1; y0 = y1;
     324             :                                 } else {
     325       75220 :                                         drawer.push(sx, sy);
     326             :                                         x0 = sx; y0 = sy;
     327             :                                 }
     328             :                         }
     329             : 
     330             :                         return;
     331             :                 } else {
     332       17769 :                         EllipseData d{ cx, cy, rx, ry, (rx * rx) / (ry * ry), cos_phi, sin_phi };
     333       33469 :                         drawArcRecursive(drawer, d, startAngle, (sweep ? -1.0f : 1.0f) * sweepAngle, x0, y0, x1, y1, 0);
     334       17772 :                         drawer.push(x1, y1);
     335             :                 }
     336             :         }
     337             : }
     338             : 
     339      128444 : LineDrawer::LineDrawer(float e, Rc<Tesselator> &&fill, Rc<Tesselator> &&stroke,
     340      128444 :                 float w, LineJoin lineJoin, LineCup lineCup)
     341      513090 : : lineJoin(lineJoin), lineCup(lineCup), strokeWidth(w / 2.0f), fill(move(fill)), stroke(move(stroke)) {
     342      128422 :         if (fill) {
     343           0 :                 style |= DrawStyle::Fill;
     344             :         }
     345      128422 :         if (stroke) {
     346           0 :                 style |= DrawStyle::Stroke;
     347             :         }
     348             : 
     349      128422 :         if ((style & DrawStyle::Stroke) != DrawStyle::None) {
     350           0 :                 if (w > 1.0f) {
     351           0 :                         distanceError = draw_approx_err_sq(e * log2f(w));
     352             :                 } else {
     353           0 :                         distanceError = draw_approx_err_sq(e);
     354             :                 }
     355           0 :                 angularError = 0.5f;
     356             :         } else {
     357      128404 :                 distanceError = draw_approx_err_sq(e);
     358      128404 :                 angularError = 0.0f;
     359             :         }
     360             : 
     361      128404 :         buffer[0].next = &buffer[1]; buffer[0].prev = &buffer[2];
     362      128404 :         buffer[1].next = &buffer[2]; buffer[1].prev = &buffer[0];
     363      128404 :         buffer[2].next = &buffer[0]; buffer[2].prev = &buffer[1];
     364      128404 :         target = &buffer[0];
     365      128404 : }
     366             : 
     367      444867 : void LineDrawer::drawBegin(float x, float y) {
     368      444867 :         if (count > 0) {
     369        8746 :                 drawClose(false);
     370             :         }
     371             : 
     372      444867 :         if (fill) {
     373      179945 :                 fillCursor = fill->beginContour();
     374             :         }
     375             : 
     376      445636 :         if (stroke) {
     377      264950 :                 strokeCursor = stroke->beginContour();
     378             :         }
     379             : 
     380      445636 :         push(x, y);
     381      445451 : }
     382     1967253 : void LineDrawer::drawLine(float x, float y) {
     383     1967253 :         push(x, y);
     384     1969832 : }
     385           0 : void LineDrawer::drawQuadBezier(float x1, float y1, float x2, float y2) {
     386           0 :         drawQuadBezierRecursive(*this, target->point.x, target->point.y, x1, y1, x2, y2, 0);
     387           0 :         push(x2, y2);
     388           0 : }
     389      940680 : void LineDrawer::drawCubicBezier(float x1, float y1, float x2, float y2, float x3, float y3) {
     390      940680 :         drawCubicBezierRecursive(*this, target->point.x, target->point.y, x1, y1, x2, y2, x3, y3, 0);
     391      941653 :         push(x3, y3);
     392      941514 : }
     393       56386 : void LineDrawer::drawArc(float rx, float ry, float phi, bool largeArc, bool sweep, float x1, float y1) {
     394       56386 :         drawArcBegin(*this, target->point.x, target->point.y, rx, ry, phi, largeArc, sweep, x1, y1);
     395       56468 : }
     396      573109 : void LineDrawer::drawClose(bool closed) {
     397      573109 :         if (fill) {
     398      237522 :                 fill->pushVertex(fillCursor, target->point);
     399      237338 :                 fill->closeContour(fillCursor);
     400             :                 closed = true;
     401             :         }
     402             : 
     403      572836 :         if (stroke) {
     404      335625 :                 if (closed && count > 2) {
     405      259400 :                         pushStroke(target->prev->point, target->point, origin[0]);
     406      259400 :                         pushStroke(target->point, origin[0], origin[1]);
     407             : 
     408      259400 :                         stroke->closeStrokeContour(strokeCursor);
     409             :                 } else {
     410       76225 :                         auto norm = target->point - target->prev->point;
     411       76225 :                         norm.normalize();
     412       76225 :                         auto perp = norm.getRPerp();
     413       76225 :                         perp.negate();
     414             : 
     415       76225 :                         stroke->pushStrokeVertex(strokeCursor, target->point, perp * strokeWidth);
     416             :                 }
     417             :         }
     418             : 
     419      572836 :         count = 0;
     420      572836 : }
     421             : 
     422     6193660 : void LineDrawer::push(float x, float y) {
     423     6193660 :         if (count < 2) {
     424      890393 :                 origin[count] = Vec2(x, y);
     425             :         }
     426             : 
     427     6193707 :         if (fill) {
     428     2861260 :                 if (count > 0) {
     429     2682761 :                         fill->pushVertex(fillCursor, target->point);
     430             :                 }
     431             :         }
     432     6146111 :         if (stroke) {
     433     3334175 :                 if (count > 1) {
     434     2804325 :                         pushStroke(target->prev->point, target->point, Vec2(x, y));
     435             :                 }
     436             :         }
     437             : 
     438     6146111 :         target = target->next;
     439     6146111 :         target->point = Vec2(x, y);
     440     6200783 :         ++ count;
     441     6200783 : }
     442             : 
     443     3323125 : void LineDrawer::pushStroke(const Vec2 &v0, const Vec2 &v1, const Vec2 &v2) {
     444     3323125 :         Vec4 result;
     445     3323125 :         getVertexNormal(&v0.x, &v1.x, &v2.x, &result.x);
     446             : 
     447     3323125 :         float mod = copysign(result.y * strokeWidth, result.x);
     448     3323125 :         if (!strokeCursor.edge) {
     449      264875 :                 auto norm = v1 - v0;
     450      264875 :                 norm.normalize();
     451      264875 :                 auto perp = norm.getRPerp();
     452      264875 :                 perp.negate();
     453             : 
     454      264875 :                 stroke->pushStrokeVertex(strokeCursor, v0, perp * strokeWidth);
     455             :         }
     456             : 
     457     3323125 :         if (std::abs(result.y) < _miterLimit) {
     458     3318650 :                 stroke->pushStrokeVertex(strokeCursor, v1, Vec2(result.z * mod, result.w * mod));
     459             :         } else {
     460        4475 :                 auto l0 = v1.distanceSquared(v0);
     461        4475 :                 auto l2 = v1.distanceSquared(v2);
     462             : 
     463             :                 float qSquared;
     464        4475 :                 if (l0 > l2) {
     465        1700 :                         qSquared = l2 / (result.y * result.y - 1);
     466             :                 } else {
     467        2775 :                         qSquared = l0 / (result.y * result.y - 1);
     468             :                 }
     469             : 
     470        4475 :                 float inverseMiterLimitSq = result.y * result.y * qSquared;
     471        4475 :                 float offsetLengthSq = mod * mod;
     472             : 
     473        4475 :                 if (offsetLengthSq > inverseMiterLimitSq) {
     474        1825 :                         mod = copysign(sqrt(inverseMiterLimitSq), result.x);
     475             :                 }
     476             : 
     477        4475 :                 if (mod > 0.0f) {
     478             :                         do {
     479        3075 :                                 auto norm = v1 - v0;
     480        3075 :                                 norm.normalize();
     481        3075 :                                 auto perp = norm.getRPerp();
     482        3075 :                                 stroke->pushStrokeBottom(strokeCursor, v1 + perp * strokeWidth);
     483             :                         } while (0);
     484             : 
     485             :                         do {
     486        3075 :                                 auto norm = v2 - v1;
     487        3075 :                                 norm.normalize();
     488        3075 :                                 auto perp = norm.getRPerp();
     489        3075 :                                 stroke->pushStrokeBottom(strokeCursor, v1 + perp * strokeWidth);
     490             :                         } while (0);
     491             : 
     492        3075 :                         stroke->pushStrokeTop(strokeCursor, v1 + Vec2(result.z * mod, result.w * mod));
     493             :                 } else {
     494        1400 :                         stroke->pushStrokeBottom(strokeCursor, v1 - Vec2(result.z * mod, result.w * mod));
     495             : 
     496             :                         do {
     497        1400 :                                 auto norm = v1 - v0;
     498        1400 :                                 norm.normalize();
     499        1400 :                                 auto perp = norm.getRPerp();
     500        1400 :                                 stroke->pushStrokeTop(strokeCursor, v1 - perp * strokeWidth);
     501             :                         } while (0);
     502             : 
     503             :                         do {
     504        1400 :                                 auto norm = v2 - v1;
     505        1400 :                                 norm.normalize();
     506        1400 :                                 auto perp = norm.getRPerp();
     507        1400 :                                 stroke->pushStrokeTop(strokeCursor, v1 - perp * strokeWidth);
     508             :                         } while (0);
     509             : 
     510             :                 }
     511             :         }
     512             : 
     513     3323125 : }
     514             : 
     515             : }

Generated by: LCOV version 1.14