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 : }
|