JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_PathStrokeType.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
30 : thickness (strokeThickness), jointStyle (mitered), endStyle (butt)
31{
32}
33
35 : thickness (strokeThickness), jointStyle (joint), endStyle (end)
36{
37}
38
40 : thickness (other.thickness),
41 jointStyle (other.jointStyle),
42 endStyle (other.endStyle)
43{
44}
45
47{
48 thickness = other.thickness;
49 jointStyle = other.jointStyle;
50 endStyle = other.endStyle;
51 return *this;
52}
53
57
59{
60 const auto tie = [] (const PathStrokeType& p) { return std::tie (p.thickness, p.jointStyle, p.endStyle); };
61 return tie (*this) == tie (other);
62}
63
65{
66 return ! operator== (other);
67}
68
69//==============================================================================
70namespace PathStrokeHelpers
71{
73 {
74 Point<float> point;
75 float distanceBeyondLine1EndSquared;
76 bool intersects;
77 };
78
79 static LineIntersection lineIntersection (const float x1, const float y1,
80 const float x2, const float y2,
81 const float x3, const float y3,
82 const float x4, const float y4)
83 {
84 if (! approximatelyEqual (x2, x3) || ! approximatelyEqual (y2, y3))
85 {
86 const auto dx1 = x2 - x1;
87 const auto dy1 = y2 - y1;
88 const auto dx2 = x4 - x3;
89 const auto dy2 = y4 - y3;
90 const auto divisor = dx1 * dy2 - dx2 * dy1;
91
92 if (approximatelyEqual (divisor, 0.0f))
93 {
94 if (! ((approximatelyEqual (dx1, 0.0f) && approximatelyEqual (dy1, 0.0f))
95 || (approximatelyEqual (dx2, 0.0f) && approximatelyEqual (dy2, 0.0f))))
96 {
97 if (approximatelyEqual (dy1, 0.0f) && ! approximatelyEqual (dy2, 0.0f))
98 {
99 const auto along = (y1 - y3) / dy2;
100 const auto intersectionX = x3 + along * dx2;
101 const auto intersectionY = y1;
102
103 const auto distance = square (intersectionX - x2);
104 const auto distanceBeyondLine1EndSquared = (x2 > x1) == (intersectionX < x2)
105 ? -distance
106 : distance;
107
108 return { { intersectionX, intersectionY },
109 distanceBeyondLine1EndSquared,
110 along >= 0 && along <= 1.0f };
111 }
112
113 if (approximatelyEqual (dy2, 0.0f) && ! approximatelyEqual (dy1, 0.0f))
114 {
115 const auto along = (y3 - y1) / dy1;
116 const auto intersectionX = x1 + along * dx1;
117 const auto intersectionY = y3;
118
119 const auto distance = square ((along - 1.0f) * dx1);
120 const auto distanceBeyondLine1EndSquared = along < 1.0f ? -distance : distance;
121
122 return { { intersectionX, intersectionY },
123 distanceBeyondLine1EndSquared,
124 along >= 0 && along <= 1.0f };
125 }
126
127 if (approximatelyEqual (dx1, 0.0f) && ! approximatelyEqual (dx2, 0.0f))
128 {
129 const auto along = (x1 - x3) / dx2;
130 const auto intersectionX = x1;
131 const auto intersectionY = y3 + along * dy2;
132
133 const auto distance = square (intersectionY - y2);
134 const auto distanceBeyondLine1EndSquared = (y2 > y1) == (intersectionY < y2)
135 ? -distance
136 : distance;
137
138 return { { intersectionX, intersectionY },
139 distanceBeyondLine1EndSquared,
140 along >= 0 && along <= 1.0f };
141 }
142
143 if (approximatelyEqual (dx2, 0.0f) && ! approximatelyEqual (dx1, 0.0f))
144 {
145 const auto along = (x3 - x1) / dx1;
146 const auto intersectionX = x3;
147 const auto intersectionY = y1 + along * dy1;
148
149 const auto distance = square ((along - 1.0f) * dy1);
150 const auto distanceBeyondLine1EndSquared = along < 1.0f ? -distance : distance;
151
152 return { { intersectionX, intersectionY },
153 distanceBeyondLine1EndSquared,
154 along >= 0 && along <= 1.0f };
155 }
156 }
157
158 const auto intersectionX = 0.5f * (x2 + x3);
159 const auto intersectionY = 0.5f * (y2 + y3);
160
161 const auto distanceBeyondLine1EndSquared = 0.0f;
162
163 return { { intersectionX, intersectionY },
164 distanceBeyondLine1EndSquared,
165 false };
166 }
167
168 const auto along = ((y1 - y3) * dx2 - (x1 - x3) * dy2) / divisor;
169
170 const auto intersectionX = x1 + along * dx1;
171 const auto intersectionY = y1 + along * dy1;
172
173 if (along >= 0 && along <= 1.0f)
174 {
175 const auto along2 = ((y1 - y3) * dx1 - (x1 - x3) * dy1) / divisor;
176
177 if (along2 >= 0 && along2 <= 1.0f)
178 {
179 return { { intersectionX, intersectionY },
180 0.0f,
181 true };
182 }
183 }
184
185 const auto distance = square (along - 1.0f) * (dx1 * dx1 + dy1 * dy1);
186 const auto distanceBeyondLine1EndSquared = along < 1.0f ? -distance : distance;
187
188 return { { intersectionX, intersectionY },
189 distanceBeyondLine1EndSquared,
190 false };
191 }
192
193 return { Point { x2, y2 }, 0.0f, true };
194 }
195
196 static void addEdgeAndJoint (Path& destPath,
197 const PathStrokeType::JointStyle style,
198 const float maxMiterExtensionSquared, const float width,
199 const float x1, const float y1,
200 const float x2, const float y2,
201 const float x3, const float y3,
202 const float x4, const float y4,
203 const float midX, const float midY)
204 {
205 if (style == PathStrokeType::beveled
206 || (approximatelyEqual (x3, x4) && approximatelyEqual (y3, y4))
207 || (approximatelyEqual (x1, x2) && approximatelyEqual (y1, y2)))
208 {
209 destPath.lineTo (x2, y2);
210 destPath.lineTo (x3, y3);
211 }
212 else
213 {
214 const auto intersection = lineIntersection (x1, y1, x2, y2, x3, y3, x4, y4);
215
216 // if they intersect, use this point..
217 if (intersection.intersects)
218 {
219 destPath.lineTo (intersection.point);
220 }
221 else
222 {
223 if (style == PathStrokeType::mitered)
224 {
225 if (0.0f < intersection.distanceBeyondLine1EndSquared
226 && intersection.distanceBeyondLine1EndSquared < maxMiterExtensionSquared)
227 {
228 destPath.lineTo (intersection.point);
229 }
230 else
231 {
232 // the end sticks out too far, so just use a blunt joint
233 destPath.lineTo (x2, y2);
234 destPath.lineTo (x3, y3);
235 }
236 }
237 else
238 {
239 // curved joints
240 float angle1 = std::atan2 (x2 - midX, y2 - midY);
241 float angle2 = std::atan2 (x3 - midX, y3 - midY);
242 const float angleIncrement = 0.1f;
243
244 destPath.lineTo (x2, y2);
245
246 if (std::abs (angle1 - angle2) > angleIncrement)
247 {
250 {
251 if (angle2 > angle1)
253
255
257 while (angle1 > angle2)
258 {
259 destPath.lineTo (midX + width * std::sin (angle1),
260 midY + width * std::cos (angle1));
261
263 }
264 }
265 else
266 {
267 if (angle1 > angle2)
269
271
273 while (angle1 < angle2)
274 {
275 destPath.lineTo (midX + width * std::sin (angle1),
276 midY + width * std::cos (angle1));
277
279 }
280 }
281 }
282
283 destPath.lineTo (x3, y3);
284 }
285 }
286 }
287 }
288
289 static void addLineEnd (Path& destPath,
290 const PathStrokeType::EndCapStyle style,
291 const float x1, const float y1,
292 const float x2, const float y2,
293 const float width)
294 {
295 if (style == PathStrokeType::butt)
296 {
297 destPath.lineTo (x2, y2);
298 }
299 else
300 {
301 float offx1, offy1, offx2, offy2;
302
303 auto dx = x2 - x1;
304 auto dy = y2 - y1;
305 auto len = juce_hypot (dx, dy);
306
307 if (approximatelyEqual (len, 0.0f))
308 {
309 offx1 = offx2 = x1;
310 offy1 = offy2 = y1;
311 }
312 else
313 {
314 auto offset = width / len;
315 dx *= offset;
316 dy *= offset;
317
318 offx1 = x1 + dy;
319 offy1 = y1 - dx;
320 offx2 = x2 + dy;
321 offy2 = y2 - dx;
322 }
323
324 if (style == PathStrokeType::square)
325 {
326 // square ends
327 destPath.lineTo (offx1, offy1);
328 destPath.lineTo (offx2, offy2);
329 destPath.lineTo (x2, y2);
330 }
331 else
332 {
333 // rounded ends
334 auto midx = (offx1 + offx2) * 0.5f;
335 auto midy = (offy1 + offy2) * 0.5f;
336
337 destPath.cubicTo (x1 + (offx1 - x1) * 0.55f, y1 + (offy1 - y1) * 0.55f,
338 offx1 + (midx - offx1) * 0.45f, offy1 + (midy - offy1) * 0.45f,
339 midx, midy);
340
341 destPath.cubicTo (midx + (offx2 - midx) * 0.55f, midy + (offy2 - midy) * 0.55f,
342 offx2 + (x2 - offx2) * 0.45f, offy2 + (y2 - offy2) * 0.45f,
343 x2, y2);
344 }
345 }
346 }
347
349 {
350 float startWidth, startLength;
351 float endWidth, endLength;
352 };
353
354 static void addArrowhead (Path& destPath,
355 const float x1, const float y1,
356 const float x2, const float y2,
357 const float tipX, const float tipY,
358 const float width,
359 const float arrowheadWidth)
360 {
361 Line<float> line (x1, y1, x2, y2);
362 destPath.lineTo (line.getPointAlongLine (-(arrowheadWidth / 2.0f - width), 0));
363 destPath.lineTo (tipX, tipY);
364 destPath.lineTo (line.getPointAlongLine (arrowheadWidth - (arrowheadWidth / 2.0f - width), 0));
365 destPath.lineTo (x2, y2);
366 }
367
369 {
370 float x1, y1, x2, y2; // original line
371 float lx1, ly1, lx2, ly2; // the left-hand stroke
372 float rx1, ry1, rx2, ry2; // the right-hand stroke
373 };
374
375 static void shortenSubPath (Array<LineSection>& subPath, float amountAtStart, float amountAtEnd)
376 {
377 while (amountAtEnd > 0 && subPath.size() > 0)
378 {
379 auto& l = subPath.getReference (subPath.size() - 1);
380 auto dx = l.rx2 - l.rx1;
381 auto dy = l.ry2 - l.ry1;
382 auto len = juce_hypot (dx, dy);
383
384 if (len <= amountAtEnd && subPath.size() > 1)
385 {
386 LineSection& prev = subPath.getReference (subPath.size() - 2);
387 prev.x2 = l.x2;
388 prev.y2 = l.y2;
389 subPath.removeLast();
390 amountAtEnd -= len;
391 }
392 else
393 {
394 auto prop = jmin (0.9999f, amountAtEnd / len);
395 dx *= prop;
396 dy *= prop;
397 l.rx1 += dx;
398 l.ry1 += dy;
399 l.lx2 += dx;
400 l.ly2 += dy;
401 break;
402 }
403 }
404
405 while (amountAtStart > 0 && subPath.size() > 0)
406 {
407 auto& l = subPath.getReference (0);
408 auto dx = l.rx2 - l.rx1;
409 auto dy = l.ry2 - l.ry1;
410 auto len = juce_hypot (dx, dy);
411
412 if (len <= amountAtStart && subPath.size() > 1)
413 {
414 LineSection& next = subPath.getReference (1);
415 next.x1 = l.x1;
416 next.y1 = l.y1;
417 subPath.remove (0);
418 amountAtStart -= len;
419 }
420 else
421 {
422 auto prop = jmin (0.9999f, amountAtStart / len);
423 dx *= prop;
424 dy *= prop;
425 l.rx2 -= dx;
426 l.ry2 -= dy;
427 l.lx1 -= dx;
428 l.ly1 -= dy;
429 break;
430 }
431 }
432 }
433
434 static void addSubPath (Path& destPath, Array<LineSection>& subPath,
435 const bool isClosed, const float width, const float maxMiterExtensionSquared,
436 const PathStrokeType::JointStyle jointStyle, const PathStrokeType::EndCapStyle endStyle,
437 const Arrowhead* const arrowhead)
438 {
439 jassert (subPath.size() > 0);
440
441 if (arrowhead != nullptr)
442 shortenSubPath (subPath, arrowhead->startLength, arrowhead->endLength);
443
444 auto& firstLine = subPath.getReference (0);
445
446 auto lastX1 = firstLine.lx1;
447 auto lastY1 = firstLine.ly1;
448 auto lastX2 = firstLine.lx2;
449 auto lastY2 = firstLine.ly2;
450
451 if (isClosed)
452 {
453 destPath.startNewSubPath (lastX1, lastY1);
454 }
455 else
456 {
457 destPath.startNewSubPath (firstLine.rx2, firstLine.ry2);
458
459 if (arrowhead != nullptr && arrowhead->startWidth > 0.0f)
460 addArrowhead (destPath, firstLine.rx2, firstLine.ry2, lastX1, lastY1, firstLine.x1, firstLine.y1,
461 width, arrowhead->startWidth);
462 else
463 addLineEnd (destPath, endStyle, firstLine.rx2, firstLine.ry2, lastX1, lastY1, width);
464 }
465
466 for (int i = 1; i < subPath.size(); ++i)
467 {
468 const LineSection& l = subPath.getReference (i);
469
470 addEdgeAndJoint (destPath, jointStyle,
473 l.lx1, l.ly1, l.lx2, l.ly2,
474 l.x1, l.y1);
475
476 lastX1 = l.lx1;
477 lastY1 = l.ly1;
478 lastX2 = l.lx2;
479 lastY2 = l.ly2;
480 }
481
482 auto& lastLine = subPath.getReference (subPath.size() - 1);
483
484 if (isClosed)
485 {
486 auto& l = subPath.getReference (0);
487
488 addEdgeAndJoint (destPath, jointStyle,
491 l.lx1, l.ly1, l.lx2, l.ly2,
492 l.x1, l.y1);
493
494 destPath.closeSubPath();
495 destPath.startNewSubPath (lastLine.rx1, lastLine.ry1);
496 }
497 else
498 {
499 destPath.lineTo (lastX2, lastY2);
500
501 if (arrowhead != nullptr && arrowhead->endWidth > 0.0f)
502 addArrowhead (destPath, lastX2, lastY2, lastLine.rx1, lastLine.ry1, lastLine.x2, lastLine.y2,
503 width, arrowhead->endWidth);
504 else
505 addLineEnd (destPath, endStyle, lastX2, lastY2, lastLine.rx1, lastLine.ry1, width);
506 }
507
508 lastX1 = lastLine.rx1;
509 lastY1 = lastLine.ry1;
510 lastX2 = lastLine.rx2;
511 lastY2 = lastLine.ry2;
512
513 for (int i = subPath.size() - 1; --i >= 0;)
514 {
515 auto& l = subPath.getReference (i);
516
517 addEdgeAndJoint (destPath, jointStyle,
520 l.rx1, l.ry1, l.rx2, l.ry2,
521 l.x2, l.y2);
522
523 lastX1 = l.rx1;
524 lastY1 = l.ry1;
525 lastX2 = l.rx2;
526 lastY2 = l.ry2;
527 }
528
529 if (isClosed)
530 {
531 addEdgeAndJoint (destPath, jointStyle,
534 lastLine.rx1, lastLine.ry1, lastLine.rx2, lastLine.ry2,
535 lastLine.x2, lastLine.y2);
536 }
537 else
538 {
539 // do the last line
540 destPath.lineTo (lastX2, lastY2);
541 }
542
543 destPath.closeSubPath();
544 }
545
546 static void createStroke (const float thickness, const PathStrokeType::JointStyle jointStyle,
547 const PathStrokeType::EndCapStyle endStyle,
548 Path& destPath, const Path& source,
549 const AffineTransform& transform,
550 const float extraAccuracy, const Arrowhead* const arrowhead)
551 {
553
554 if (thickness <= 0)
555 {
556 destPath.clear();
557 return;
558 }
559
560 const Path* sourcePath = &source;
561 Path temp;
562
563 if (sourcePath == &destPath)
564 {
565 destPath.swapWithPath (temp);
566 sourcePath = &temp;
567 }
568 else
569 {
570 destPath.clear();
571 }
572
573 destPath.setUsingNonZeroWinding (true);
574
575 const float maxMiterExtensionSquared = 9.0f * thickness * thickness;
576 const float width = 0.5f * thickness;
577
578 // Iterate the path, creating a list of the
579 // left/right-hand lines along either side of it...
580 PathFlatteningIterator it (*sourcePath, transform, Path::defaultToleranceForMeasurement / extraAccuracy);
581
583 subPath.ensureStorageAllocated (512);
584 LineSection l;
585 l.x1 = 0;
586 l.y1 = 0;
587
588 const float minSegmentLength = 0.0001f;
589
590 while (it.next())
591 {
592 if (it.subPathIndex == 0)
593 {
594 if (subPath.size() > 0)
595 {
596 addSubPath (destPath, subPath, false, width, maxMiterExtensionSquared, jointStyle, endStyle, arrowhead);
597 subPath.clearQuick();
598 }
599
600 l.x1 = it.x1;
601 l.y1 = it.y1;
602 }
603
604 l.x2 = it.x2;
605 l.y2 = it.y2;
606
607 float dx = l.x2 - l.x1;
608 float dy = l.y2 - l.y1;
609
610 auto hypotSquared = dx * dx + dy * dy;
611
612 if (it.closesSubPath || hypotSquared > minSegmentLength || it.isLastInSubpath())
613 {
614 auto len = std::sqrt (hypotSquared);
615
616 if (approximatelyEqual (len, 0.0f))
617 {
618 l.rx1 = l.rx2 = l.lx1 = l.lx2 = l.x1;
619 l.ry1 = l.ry2 = l.ly1 = l.ly2 = l.y1;
620 }
621 else
622 {
623 auto offset = width / len;
624 dx *= offset;
625 dy *= offset;
626
627 l.rx2 = l.x1 - dy;
628 l.ry2 = l.y1 + dx;
629 l.lx1 = l.x1 + dy;
630 l.ly1 = l.y1 - dx;
631
632 l.lx2 = l.x2 + dy;
633 l.ly2 = l.y2 - dx;
634 l.rx1 = l.x2 - dy;
635 l.ry1 = l.y2 + dx;
636 }
637
638 subPath.add (l);
639
640 if (it.closesSubPath)
641 {
642 addSubPath (destPath, subPath, true, width, maxMiterExtensionSquared, jointStyle, endStyle, arrowhead);
643 subPath.clearQuick();
644 }
645 else
646 {
647 l.x1 = it.x2;
648 l.y1 = it.y2;
649 }
650 }
651 }
652
653 if (subPath.size() > 0)
654 addSubPath (destPath, subPath, false, width, maxMiterExtensionSquared, jointStyle, endStyle, arrowhead);
655 }
656}
657
659 const AffineTransform& transform, float extraAccuracy) const
660{
661 PathStrokeHelpers::createStroke (thickness, jointStyle, endStyle, destPath, sourcePath,
662 transform, extraAccuracy, nullptr);
663}
664
666 const Path& sourcePath,
667 const float* dashLengths,
668 int numDashLengths,
669 const AffineTransform& transform,
670 float extraAccuracy) const
671{
673
674 if (thickness <= 0)
675 return;
676
678 PathFlatteningIterator it (sourcePath, transform, Path::defaultToleranceForMeasurement / extraAccuracy);
679
680 bool first = true;
681 int dashNum = 0;
682 float pos = 0.0f, lineLen = 0.0f, lineEndPos = 0.0f;
683 float dx = 0.0f, dy = 0.0f;
684
685 for (;;)
686 {
687 const bool isSolid = ((dashNum & 1) == 0);
688 const float dashLen = dashLengths [dashNum++ % numDashLengths];
689
690 jassert (dashLen >= 0); // must be a positive increment!
691 if (dashLen <= 0)
692 continue;
693
694 pos += dashLen;
695
696 while (pos > lineEndPos)
697 {
698 if (! it.next())
699 {
700 if (isSolid && ! first)
701 newDestPath.lineTo (it.x2, it.y2);
702
704 return;
705 }
706
707 if (isSolid && ! first)
708 newDestPath.lineTo (it.x1, it.y1);
709 else
710 newDestPath.startNewSubPath (it.x1, it.y1);
711
712 dx = it.x2 - it.x1;
713 dy = it.y2 - it.y1;
714 lineLen = juce_hypot (dx, dy);
716 first = it.closesSubPath;
717 }
718
719 const float alpha = (pos - (lineEndPos - lineLen)) / lineLen;
720
721 if (isSolid)
722 newDestPath.lineTo (it.x1 + dx * alpha,
723 it.y1 + dy * alpha);
724 else
725 newDestPath.startNewSubPath (it.x1 + dx * alpha,
726 it.y1 + dy * alpha);
727 }
728}
729
731 const Path& sourcePath,
732 const float arrowheadStartWidth, const float arrowheadStartLength,
733 const float arrowheadEndWidth, const float arrowheadEndLength,
734 const AffineTransform& transform,
735 const float extraAccuracy) const
736{
738 head.startWidth = arrowheadStartWidth;
739 head.startLength = arrowheadStartLength;
740 head.endWidth = arrowheadEndWidth;
741 head.endLength = arrowheadEndLength;
742
743 PathStrokeHelpers::createStroke (thickness, jointStyle, endStyle,
744 destPath, sourcePath, transform, extraAccuracy, &head);
745}
746
747} // namespace juce
T atan2(T... args)
Represents a 2D affine-transformation matrix.
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:56
Represents a line.
Definition juce_Line.h:47
Flattens a Path object into a series of straight-line sections.
Describes a type of stroke used to render a solid outline along a path.
bool operator==(const PathStrokeType &) const noexcept
Compares the stroke thickness, joint and end styles of two stroke types.
JointStyle
The type of shape to use for the corners between two adjacent line segments.
@ beveled
Indicates that corners should be drawn with a line flattening their outside edge.
@ mitered
Indicates that corners should be drawn with sharp joints.
void createStrokedPath(Path &destPath, const Path &sourcePath, const AffineTransform &transform=AffineTransform(), float extraAccuracy=1.0f) const
Applies this stroke type to a path and returns the resultant stroke as another Path.
PathStrokeType(float strokeThickness) noexcept
Creates a stroke type with a given line-width, and default joint/end styles.
EndCapStyle
The type shape to use for the ends of lines.
@ square
Ends of lines are flat, but stick out beyond the end point for half the thickness of the stroke.
@ butt
Ends of lines are flat and don't extend beyond the end point.
void createDashedStroke(Path &destPath, const Path &sourcePath, const float *dashLengths, int numDashLengths, const AffineTransform &transform=AffineTransform(), float extraAccuracy=1.0f) const
Applies this stroke type to a path, creating a dashed line.
bool operator!=(const PathStrokeType &) const noexcept
Compares the stroke thickness, joint and end styles of two stroke types.
~PathStrokeType() noexcept
Destructor.
PathStrokeType & operator=(const PathStrokeType &) noexcept
Copies another stroke onto this one.
void createStrokeWithArrowheads(Path &destPath, const Path &sourcePath, float arrowheadStartWidth, float arrowheadStartLength, float arrowheadEndWidth, float arrowheadEndLength, const AffineTransform &transform=AffineTransform(), float extraAccuracy=1.0f) const
Applies this stroke type to a path and returns the resultant stroke as another Path.
A path is a sequence of lines and curves that may either form a closed shape or be open-ended.
Definition juce_Path.h:65
A pair of (x, y) coordinates.
Definition juce_Point.h:42
T cos(T... args)
T distance(T... args)
#define jassert(expression)
Platform-independent assertion macro.
JUCE Namespace.
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Returns a default-constructed sentinel value.
constexpr NumericType square(NumericType n) noexcept
Returns the square of its argument.
Type juce_hypot(Type a, Type b) noexcept
Using juce_hypot is easier than dealing with the different types of hypot function that are provided ...
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
T next(T... args)
T sin(T... args)
T sqrt(T... args)
static constexpr FloatType twoPi
A predefined value for 2 * Pi.
static constexpr FloatType pi
A predefined value for Pi.
T tie(T... args)
y1