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_Path.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
29// tests that some coordinates aren't NaNs
30#define JUCE_CHECK_COORDS_ARE_VALID(x, y) \
31 jassert (! std::isnan (x) && ! std::isnan (y));
32
33//==============================================================================
34namespace PathHelpers
35{
36 const float ellipseAngularIncrement = 0.05f;
37
38 static String nextToken (String::CharPointerType& t)
39 {
40 t.incrementToEndOfWhitespace();
41
42 auto start = t;
43 size_t numChars = 0;
44
45 while (! (t.isEmpty() || t.isWhitespace()))
46 {
47 ++t;
48 ++numChars;
49 }
50
51 return { start, numChars };
52 }
53
54 inline double lengthOf (float x1, float y1, float x2, float y2) noexcept
55 {
56 return juce_hypot ((double) (x1 - x2), (double) (y1 - y2));
57 }
58}
59
60//==============================================================================
61
62const float Path::defaultToleranceForTesting = 1.0f;
63const float Path::defaultToleranceForMeasurement = 0.6f;
64
65static bool isMarker (float value, float marker) noexcept
66{
67 return exactlyEqual (value, marker);
68}
69
70//==============================================================================
71Path::PathBounds::PathBounds() noexcept
72{
73}
74
75Rectangle<float> Path::PathBounds::getRectangle() const noexcept
76{
77 return { pathXMin, pathYMin, pathXMax - pathXMin, pathYMax - pathYMin };
78}
79
80void Path::PathBounds::reset() noexcept
81{
82 pathXMin = pathYMin = pathYMax = pathXMax = 0;
83}
84
85void Path::PathBounds::reset (float x, float y) noexcept
86{
87 pathXMin = pathXMax = x;
88 pathYMin = pathYMax = y;
89}
90
91void Path::PathBounds::extend (float x, float y) noexcept
92{
93 if (x < pathXMin) pathXMin = x;
94 else if (x > pathXMax) pathXMax = x;
95
96 if (y < pathYMin) pathYMin = y;
97 else if (y > pathYMax) pathYMax = y;
98}
99
100//==============================================================================
102{
103}
104
106{
107}
108
110 : data (other.data),
111 bounds (other.bounds),
112 useNonZeroWinding (other.useNonZeroWinding)
113{
114}
115
117{
118 if (this != &other)
119 {
120 data = other.data;
121 bounds = other.bounds;
122 useNonZeroWinding = other.useNonZeroWinding;
123 }
124
125 return *this;
126}
127
129 : data (std::move (other.data)),
130 bounds (other.bounds),
131 useNonZeroWinding (other.useNonZeroWinding)
132{
133}
134
136{
137 data = std::move (other.data);
138 bounds = other.bounds;
139 useNonZeroWinding = other.useNonZeroWinding;
140 return *this;
141}
142
143bool Path::operator== (const Path& other) const noexcept { return useNonZeroWinding == other.useNonZeroWinding && data == other.data; }
144bool Path::operator!= (const Path& other) const noexcept { return ! operator== (other); }
145
146void Path::clear() noexcept
147{
148 data.clearQuick();
149 bounds.reset();
150}
151
153{
154 data.swapWith (other.data);
155 std::swap (bounds.pathXMin, other.bounds.pathXMin);
156 std::swap (bounds.pathXMax, other.bounds.pathXMax);
157 std::swap (bounds.pathYMin, other.bounds.pathYMin);
158 std::swap (bounds.pathYMax, other.bounds.pathYMax);
159 std::swap (useNonZeroWinding, other.useNonZeroWinding);
160}
161
162//==============================================================================
163void Path::setUsingNonZeroWinding (const bool isNonZero) noexcept
164{
165 useNonZeroWinding = isNonZero;
166}
167
168void Path::scaleToFit (float x, float y, float w, float h, bool preserveProportions) noexcept
169{
170 applyTransform (getTransformToScaleToFit (x, y, w, h, preserveProportions));
171}
172
173//==============================================================================
174bool Path::isEmpty() const noexcept
175{
176 for (auto i = data.begin(), e = data.end(); i != e; ++i)
177 {
178 auto type = *i;
179
180 if (isMarker (type, moveMarker))
181 {
182 i += 2;
183 }
184 else if (isMarker (type, lineMarker)
185 || isMarker (type, quadMarker)
186 || isMarker (type, cubicMarker))
187 {
188 return false;
189 }
190 }
191
192 return true;
193}
194
196{
197 return bounds.getRectangle();
198}
199
201{
202 return getBounds().transformedBy (transform);
203}
204
205//==============================================================================
210
211void Path::startNewSubPath (const float x, const float y)
212{
213 JUCE_CHECK_COORDS_ARE_VALID (x, y)
214
215 if (data.isEmpty())
216 bounds.reset (x, y);
217 else
218 bounds.extend (x, y);
219
220 data.add (moveMarker, x, y);
221}
222
224{
225 startNewSubPath (start.x, start.y);
226}
227
228void Path::lineTo (const float x, const float y)
229{
230 JUCE_CHECK_COORDS_ARE_VALID (x, y)
231
232 if (data.isEmpty())
233 startNewSubPath (0, 0);
234
235 data.add (lineMarker, x, y);
236 bounds.extend (x, y);
237}
238
240{
241 lineTo (end.x, end.y);
242}
243
244void Path::quadraticTo (const float x1, const float y1,
245 const float x2, const float y2)
246{
247 JUCE_CHECK_COORDS_ARE_VALID (x1, y1)
248 JUCE_CHECK_COORDS_ARE_VALID (x2, y2)
249
250 if (data.isEmpty())
251 startNewSubPath (0, 0);
252
253 data.add (quadMarker, x1, y1, x2, y2);
254 bounds.extend (x1, y1, x2, y2);
255}
256
258{
260 endPoint.x, endPoint.y);
261}
262
263void Path::cubicTo (const float x1, const float y1,
264 const float x2, const float y2,
265 const float x3, const float y3)
266{
267 JUCE_CHECK_COORDS_ARE_VALID (x1, y1)
268 JUCE_CHECK_COORDS_ARE_VALID (x2, y2)
269 JUCE_CHECK_COORDS_ARE_VALID (x3, y3)
270
271 if (data.isEmpty())
272 startNewSubPath (0, 0);
273
274 data.add (cubicMarker, x1, y1, x2, y2, x3, y3);
275 bounds.extend (x1, y1, x2, y2, x3, y3);
276}
277
280 Point<float> endPoint)
281{
284 endPoint.x, endPoint.y);
285}
286
288{
289 if (! (data.isEmpty() || isMarker (data.getLast(), closeSubPathMarker)))
290 data.add (closeSubPathMarker);
291}
292
294{
295 if (data.isEmpty())
296 return {};
297
298 auto* i = data.end() - 1;
299
300 if (isMarker (*i, closeSubPathMarker))
301 {
302 while (i != data.begin())
303 {
304 if (isMarker (*--i, moveMarker))
305 {
306 i += 2;
307 break;
308 }
309 }
310 }
311
312 if (i != data.begin())
313 return { *(i - 1), *i };
314
315 return {};
316}
317
318void Path::addRectangle (float x, float y, float w, float h)
319{
320 auto x1 = x, y1 = y, x2 = x + w, y2 = y + h;
321
322 if (w < 0) std::swap (x1, x2);
323 if (h < 0) std::swap (y1, y2);
324
325 if (data.isEmpty())
326 {
327 bounds.pathXMin = x1;
328 bounds.pathXMax = x2;
329 bounds.pathYMin = y1;
330 bounds.pathYMax = y2;
331 }
332 else
333 {
334 bounds.pathXMin = jmin (bounds.pathXMin, x1);
335 bounds.pathXMax = jmax (bounds.pathXMax, x2);
336 bounds.pathYMin = jmin (bounds.pathYMin, y1);
337 bounds.pathYMax = jmax (bounds.pathYMax, y2);
338 }
339
340 data.add (moveMarker, x1, y2,
341 lineMarker, x1, y1,
342 lineMarker, x2, y1,
343 lineMarker, x2, y2,
344 closeSubPathMarker);
345}
346
347void Path::addRoundedRectangle (float x, float y, float w, float h, float csx, float csy)
348{
349 addRoundedRectangle (x, y, w, h, csx, csy, true, true, true, true);
350}
351
352void Path::addRoundedRectangle (const float x, const float y, const float w, const float h,
353 float csx, float csy,
354 const bool curveTopLeft, const bool curveTopRight,
355 const bool curveBottomLeft, const bool curveBottomRight)
356{
357 csx = jmin (csx, w * 0.5f);
358 csy = jmin (csy, h * 0.5f);
359 auto cs45x = csx * 0.45f;
360 auto cs45y = csy * 0.45f;
361 auto x2 = x + w;
362 auto y2 = y + h;
363
364 if (curveTopLeft)
365 {
366 startNewSubPath (x, y + csy);
367 cubicTo (x, y + cs45y, x + cs45x, y, x + csx, y);
368 }
369 else
370 {
371 startNewSubPath (x, y);
372 }
373
374 if (curveTopRight)
375 {
376 lineTo (x2 - csx, y);
377 cubicTo (x2 - cs45x, y, x2, y + cs45y, x2, y + csy);
378 }
379 else
380 {
381 lineTo (x2, y);
382 }
383
385 {
386 lineTo (x2, y2 - csy);
387 cubicTo (x2, y2 - cs45y, x2 - cs45x, y2, x2 - csx, y2);
388 }
389 else
390 {
391 lineTo (x2, y2);
392 }
393
394 if (curveBottomLeft)
395 {
396 lineTo (x + csx, y2);
397 cubicTo (x + cs45x, y2, x, y2 - cs45y, x, y2 - csy);
398 }
399 else
400 {
401 lineTo (x, y2);
402 }
403
404 closeSubPath();
405}
406
407void Path::addRoundedRectangle (float x, float y, float w, float h, float cs)
408{
409 addRoundedRectangle (x, y, w, h, cs, cs);
410}
411
412void Path::addTriangle (float x1, float y1,
413 float x2, float y2,
414 float x3, float y3)
415{
416 addTriangle ({ x1, y1 },
417 { x2, y2 },
418 { x3, y3 });
419}
420
428
429void Path::addQuadrilateral (float x1, float y1,
430 float x2, float y2,
431 float x3, float y3,
432 float x4, float y4)
433{
434 startNewSubPath (x1, y1);
435 lineTo (x2, y2);
436 lineTo (x3, y3);
437 lineTo (x4, y4);
438 closeSubPath();
439}
440
441void Path::addEllipse (float x, float y, float w, float h)
442{
443 addEllipse ({ x, y, w, h });
444}
445
447{
448 auto hw = area.getWidth() * 0.5f;
449 auto hw55 = hw * 0.55f;
450 auto hh = area.getHeight() * 0.5f;
451 auto hh55 = hh * 0.55f;
452 auto cx = area.getX() + hw;
453 auto cy = area.getY() + hh;
454
455 startNewSubPath (cx, cy - hh);
456 cubicTo (cx + hw55, cy - hh, cx + hw, cy - hh55, cx + hw, cy);
457 cubicTo (cx + hw, cy + hh55, cx + hw55, cy + hh, cx, cy + hh);
458 cubicTo (cx - hw55, cy + hh, cx - hw, cy + hh55, cx - hw, cy);
459 cubicTo (cx - hw, cy - hh55, cx - hw55, cy - hh, cx, cy - hh);
460 closeSubPath();
461}
462
463void Path::addArc (float x, float y, float w, float h,
464 float fromRadians, float toRadians,
466{
467 auto radiusX = w / 2.0f;
468 auto radiusY = h / 2.0f;
469
471 y + radiusY,
473 0.0f,
476}
477
479 float radiusX, float radiusY,
480 float rotationOfEllipse,
481 float fromRadians, float toRadians,
483{
484 if (radiusX > 0.0f && radiusY > 0.0f)
485 {
486 Point<float> centre (centreX, centreY);
488 auto angle = fromRadians;
489
491 startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
492
494 {
496 angle += PathHelpers::ellipseAngularIncrement;
497
498 while (angle < toRadians)
499 {
500 lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
501 angle += PathHelpers::ellipseAngularIncrement;
502 }
503 }
504 else
505 {
507 angle -= PathHelpers::ellipseAngularIncrement;
508
509 while (angle > toRadians)
510 {
511 lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
512 angle -= PathHelpers::ellipseAngularIncrement;
513 }
514 }
515
516 lineTo (centre.getPointOnCircumference (radiusX, radiusY, toRadians).transformedBy (rotation));
517 }
518}
519
520void Path::addPieSegment (float x, float y, float width, float height,
521 float fromRadians, float toRadians,
523{
524 auto radiusX = width * 0.5f;
525 auto radiusY = height * 0.5f;
526 Point<float> centre (x + radiusX, y + radiusY);
527
529 addArc (x, y, width, height, fromRadians, toRadians);
530
531 if (std::abs (fromRadians - toRadians) > MathConstants<float>::pi * 1.999f)
532 {
533 closeSubPath();
534
536 {
539
541 addArc (centre.x - radiusX, centre.y - radiusY, radiusX * 2.0f, radiusY * 2.0f, toRadians, fromRadians);
542 }
543 }
544 else
545 {
547 {
550
551 addArc (centre.x - radiusX, centre.y - radiusY, radiusX * 2.0f, radiusY * 2.0f, toRadians, fromRadians);
552 }
553 else
554 {
555 lineTo (centre);
556 }
557 }
558
559 closeSubPath();
560}
561
574
575//==============================================================================
577{
578 auto reversed = line.reversed();
579 lineThickness *= 0.5f;
580
583 lineTo (reversed.getPointAlongLine (0, lineThickness));
584 lineTo (reversed.getPointAlongLine (0, -lineThickness));
585 closeSubPath();
586}
587
589 float arrowheadWidth, float arrowheadLength)
590{
591 auto reversed = line.reversed();
592 lineThickness *= 0.5f;
593 arrowheadWidth *= 0.5f;
595
598 lineTo (reversed.getPointAlongLine (arrowheadLength, lineThickness));
599 lineTo (reversed.getPointAlongLine (arrowheadLength, arrowheadWidth));
600 lineTo (line.getEnd());
601 lineTo (reversed.getPointAlongLine (arrowheadLength, -arrowheadWidth));
602 lineTo (reversed.getPointAlongLine (arrowheadLength, -lineThickness));
603 closeSubPath();
604}
605
607 float radius, float startAngle)
608{
609 jassert (numberOfSides > 1); // this would be silly.
610
611 if (numberOfSides > 1)
612 {
614
615 for (int i = 0; i < numberOfSides; ++i)
616 {
618 auto p = centre.getPointOnCircumference (radius, angle);
619
620 if (i == 0)
621 startNewSubPath (p);
622 else
623 lineTo (p);
624 }
625
626 closeSubPath();
627 }
628}
629
631 float outerRadius, float startAngle)
632{
633 jassert (numberOfPoints > 1); // this would be silly.
634
635 if (numberOfPoints > 1)
636 {
638
639 for (int i = 0; i < numberOfPoints; ++i)
640 {
642 auto p = centre.getPointOnCircumference (outerRadius, angle);
643
644 if (i == 0)
645 startNewSubPath (p);
646 else
647 lineTo (p);
648
650 }
651
652 closeSubPath();
653 }
654}
655
658 Point<float> arrowTip,
659 float cornerSize,
660 float arrowBaseWidth)
661{
662 auto halfW = bodyArea.getWidth() / 2.0f;
663 auto halfH = bodyArea.getHeight() / 2.0f;
664 auto cornerSizeW = jmin (cornerSize, halfW);
665 auto cornerSizeH = jmin (cornerSize, halfH);
666 auto cornerSizeW2 = 2.0f * cornerSizeW;
667 auto cornerSizeH2 = 2.0f * cornerSizeH;
668
669 startNewSubPath (bodyArea.getX() + cornerSizeW, bodyArea.getY());
670
671 auto targetLimit = bodyArea.reduced (jmin (halfW - 1.0f, cornerSizeW + arrowBaseWidth),
673
674 if (Rectangle<float> (targetLimit.getX(), maximumArea.getY(),
675 targetLimit.getWidth(), bodyArea.getY() - maximumArea.getY()).contains (arrowTip))
676 {
677 lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getY());
678 lineTo (arrowTip.x, arrowTip.y);
679 lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getY());
680 }
681
682 lineTo (bodyArea.getRight() - cornerSizeW, bodyArea.getY());
684
685 if (Rectangle<float> (bodyArea.getRight(), targetLimit.getY(),
686 maximumArea.getRight() - bodyArea.getRight(), targetLimit.getHeight()).contains (arrowTip))
687 {
688 lineTo (bodyArea.getRight(), arrowTip.y - arrowBaseWidth);
689 lineTo (arrowTip.x, arrowTip.y);
690 lineTo (bodyArea.getRight(), arrowTip.y + arrowBaseWidth);
691 }
692
693 lineTo (bodyArea.getRight(), bodyArea.getBottom() - cornerSizeH);
695
696 if (Rectangle<float> (targetLimit.getX(), bodyArea.getBottom(),
697 targetLimit.getWidth(), maximumArea.getBottom() - bodyArea.getBottom()).contains (arrowTip))
698 {
699 lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getBottom());
700 lineTo (arrowTip.x, arrowTip.y);
701 lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getBottom());
702 }
703
704 lineTo (bodyArea.getX() + cornerSizeW, bodyArea.getBottom());
706
707 if (Rectangle<float> (maximumArea.getX(), targetLimit.getY(),
708 bodyArea.getX() - maximumArea.getX(), targetLimit.getHeight()).contains (arrowTip))
709 {
710 lineTo (bodyArea.getX(), arrowTip.y + arrowBaseWidth);
711 lineTo (arrowTip.x, arrowTip.y);
712 lineTo (bodyArea.getX(), arrowTip.y - arrowBaseWidth);
713 }
714
715 lineTo (bodyArea.getX(), bodyArea.getY() + cornerSizeH);
717
718 closeSubPath();
719}
720
722{
723 const auto* d = other.data.begin();
724 const auto size = other.data.size();
725
726 for (int i = 0; i < size;)
727 {
728 const auto type = d[i++];
729
730 if (isMarker (type, moveMarker))
731 {
732 startNewSubPath (d[i], d[i + 1]);
733 i += 2;
734 }
735 else if (isMarker (type, lineMarker))
736 {
737 lineTo (d[i], d[i + 1]);
738 i += 2;
739 }
740 else if (isMarker (type, quadMarker))
741 {
742 quadraticTo (d[i], d[i + 1], d[i + 2], d[i + 3]);
743 i += 4;
744 }
745 else if (isMarker (type, cubicMarker))
746 {
747 cubicTo (d[i], d[i + 1], d[i + 2], d[i + 3], d[i + 4], d[i + 5]);
748 i += 6;
749 }
750 else if (isMarker (type, closeSubPathMarker))
751 {
752 closeSubPath();
753 }
754 else
755 {
756 // something's gone wrong with the element list!
758 }
759 }
760}
761
764{
765 const auto* d = other.data.begin();
766 const auto size = other.data.size();
767
768 for (int i = 0; i < size;)
769 {
770 const auto type = d[i++];
771
772 if (isMarker (type, closeSubPathMarker))
773 {
774 closeSubPath();
775 }
776 else
777 {
778 auto x = d[i++];
779 auto y = d[i++];
780 transformToApply.transformPoint (x, y);
781
782 if (isMarker (type, moveMarker))
783 {
784 startNewSubPath (x, y);
785 }
786 else if (isMarker (type, lineMarker))
787 {
788 lineTo (x, y);
789 }
790 else if (isMarker (type, quadMarker))
791 {
792 auto x2 = d[i++];
793 auto y2 = d[i++];
794 transformToApply.transformPoint (x2, y2);
795
796 quadraticTo (x, y, x2, y2);
797 }
798 else if (isMarker (type, cubicMarker))
799 {
800 auto x2 = d[i++];
801 auto y2 = d[i++];
802 auto x3 = d[i++];
803 auto y3 = d[i++];
804 transformToApply.transformPoints (x2, y2, x3, y3);
805
806 cubicTo (x, y, x2, y2, x3, y3);
807 }
808 else
809 {
810 // something's gone wrong with the element list!
812 }
813 }
814 }
815}
816
817//==============================================================================
818void Path::applyTransform (const AffineTransform& transform) noexcept
819{
820 bounds.reset();
821 bool firstPoint = true;
822 float* d = data.begin();
823 auto* end = data.end();
824
825 while (d < end)
826 {
827 auto type = *d++;
828
829 if (isMarker (type, moveMarker))
830 {
831 transform.transformPoint (d[0], d[1]);
832 JUCE_CHECK_COORDS_ARE_VALID (d[0], d[1])
833
834 if (firstPoint)
835 {
836 firstPoint = false;
837 bounds.reset (d[0], d[1]);
838 }
839 else
840 {
841 bounds.extend (d[0], d[1]);
842 }
843
844 d += 2;
845 }
846 else if (isMarker (type, lineMarker))
847 {
848 transform.transformPoint (d[0], d[1]);
849 JUCE_CHECK_COORDS_ARE_VALID (d[0], d[1])
850 bounds.extend (d[0], d[1]);
851 d += 2;
852 }
853 else if (isMarker (type, quadMarker))
854 {
855 transform.transformPoints (d[0], d[1], d[2], d[3]);
856 JUCE_CHECK_COORDS_ARE_VALID (d[0], d[1])
857 JUCE_CHECK_COORDS_ARE_VALID (d[2], d[3])
858 bounds.extend (d[0], d[1], d[2], d[3]);
859 d += 4;
860 }
861 else if (isMarker (type, cubicMarker))
862 {
863 transform.transformPoints (d[0], d[1], d[2], d[3], d[4], d[5]);
864 JUCE_CHECK_COORDS_ARE_VALID (d[0], d[1])
865 JUCE_CHECK_COORDS_ARE_VALID (d[2], d[3])
866 JUCE_CHECK_COORDS_ARE_VALID (d[4], d[5])
867 bounds.extend (d[0], d[1], d[2], d[3], d[4], d[5]);
868 d += 6;
869 }
870 }
871}
872
873
874//==============================================================================
876 Justification justification) const
877{
878 return getTransformToScaleToFit (area.getX(), area.getY(), area.getWidth(), area.getHeight(),
879 preserveProportions, justification);
880}
881
882AffineTransform Path::getTransformToScaleToFit (float x, float y, float w, float h,
883 bool preserveProportions,
884 Justification justification) const
885{
886 auto boundsRect = getBounds();
887
888 if (preserveProportions)
889 {
890 if (w <= 0 || h <= 0 || boundsRect.isEmpty())
891 return AffineTransform();
892
893 float newW, newH;
894 auto srcRatio = boundsRect.getHeight() / boundsRect.getWidth();
895
896 if (srcRatio > h / w)
897 {
898 newW = h / srcRatio;
899 newH = h;
900 }
901 else
902 {
903 newW = w;
904 newH = w * srcRatio;
905 }
906
907 auto newXCentre = x;
908 auto newYCentre = y;
909
910 if (justification.testFlags (Justification::left)) newXCentre += newW * 0.5f;
911 else if (justification.testFlags (Justification::right)) newXCentre += w - newW * 0.5f;
912 else newXCentre += w * 0.5f;
913
914 if (justification.testFlags (Justification::top)) newYCentre += newH * 0.5f;
915 else if (justification.testFlags (Justification::bottom)) newYCentre += h - newH * 0.5f;
916 else newYCentre += h * 0.5f;
917
918 return AffineTransform::translation (boundsRect.getWidth() * -0.5f - boundsRect.getX(),
919 boundsRect.getHeight() * -0.5f - boundsRect.getY())
920 .scaled (newW / boundsRect.getWidth(),
921 newH / boundsRect.getHeight())
923 }
924 else
925 {
926 return AffineTransform::translation (-boundsRect.getX(), -boundsRect.getY())
927 .scaled (w / boundsRect.getWidth(),
928 h / boundsRect.getHeight())
929 .translated (x, y);
930 }
931}
932
933//==============================================================================
934bool Path::contains (float x, float y, float tolerance) const
935{
936 if (x <= bounds.pathXMin || x >= bounds.pathXMax
937 || y <= bounds.pathYMin || y >= bounds.pathYMax)
938 return false;
939
941
942 int positiveCrossings = 0;
943 int negativeCrossings = 0;
944
945 while (i.next())
946 {
947 if ((i.y1 <= y && i.y2 > y) || (i.y2 <= y && i.y1 > y))
948 {
949 auto intersectX = i.x1 + (i.x2 - i.x1) * (y - i.y1) / (i.y2 - i.y1);
950
951 if (intersectX <= x)
952 {
953 if (i.y1 < i.y2)
955 else
957 }
958 }
959 }
960
961 return useNonZeroWinding ? (negativeCrossings != positiveCrossings)
962 : ((negativeCrossings + positiveCrossings) & 1) != 0;
963}
964
965bool Path::contains (Point<float> point, float tolerance) const
966{
967 return contains (point.x, point.y, tolerance);
968}
969
971{
974
975 while (i.next())
976 if (line.intersects (Line<float> (i.x1, i.y1, i.x2, i.y2), intersection))
977 return true;
978
979 return false;
980}
981
983{
984 Line<float> result (line);
985 const bool startInside = contains (line.getStart());
986 const bool endInside = contains (line.getEnd());
987
988 if (startInside == endInside)
989 {
991 result = Line<float>();
992 }
993 else
994 {
997
998 while (i.next())
999 {
1000 if (line.intersects ({ i.x1, i.y1, i.x2, i.y2 }, intersection))
1001 {
1003 result.setStart (intersection);
1004 else
1005 result.setEnd (intersection);
1006 }
1007 }
1008 }
1009
1010 return result;
1011}
1012
1013float Path::getLength (const AffineTransform& transform, float tolerance) const
1014{
1015 float length = 0;
1016 PathFlatteningIterator i (*this, transform, tolerance);
1017
1018 while (i.next())
1019 length += Line<float> (i.x1, i.y1, i.x2, i.y2).getLength();
1020
1021 return length;
1022}
1023
1025 const AffineTransform& transform,
1026 float tolerance) const
1027{
1028 PathFlatteningIterator i (*this, transform, tolerance);
1029
1030 while (i.next())
1031 {
1032 const Line<float> line (i.x1, i.y1, i.x2, i.y2);
1033 auto lineLength = line.getLength();
1034
1035 if (distanceFromStart <= lineLength)
1037
1038 distanceFromStart -= lineLength;
1039 }
1040
1041 return { i.x2, i.y2 };
1042}
1043
1045 const AffineTransform& transform,
1046 float tolerance) const
1047{
1048 PathFlatteningIterator i (*this, transform, tolerance);
1050 float length = 0;
1052
1053 while (i.next())
1054 {
1055 const Line<float> line (i.x1, i.y1, i.x2, i.y2);
1056 auto distance = line.getDistanceFromPoint (targetPoint, pointOnLine);
1057
1058 if (distance < bestDistance)
1059 {
1060 bestDistance = distance;
1061 bestPosition = length + pointOnLine.getDistanceFrom (line.getStart());
1063 }
1064
1065 length += line.getLength();
1066 }
1067
1068 return bestPosition;
1069}
1070
1071//==============================================================================
1073{
1074 if (cornerRadius <= 0.01f)
1075 return *this;
1076
1077 Path p;
1078 int n = 0, indexOfPathStart = 0, indexOfPathStartThis = 0;
1079 auto* elements = data.begin();
1080 bool lastWasLine = false, firstWasLine = false;
1081
1082 while (n < data.size())
1083 {
1084 auto type = elements[n++];
1085
1086 if (isMarker (type, moveMarker))
1087 {
1088 indexOfPathStart = p.data.size();
1089 indexOfPathStartThis = n - 1;
1090 auto x = elements[n++];
1091 auto y = elements[n++];
1092 p.startNewSubPath (x, y);
1093 lastWasLine = false;
1094 firstWasLine = (isMarker (elements[n], lineMarker));
1095 }
1096 else if (isMarker (type, lineMarker) || isMarker (type, closeSubPathMarker))
1097 {
1098 float startX = 0, startY = 0, joinX = 0, joinY = 0, endX, endY;
1099
1100 if (isMarker (type, lineMarker))
1101 {
1102 endX = elements[n++];
1103 endY = elements[n++];
1104
1105 if (n > 8)
1106 {
1107 startX = elements[n - 8];
1108 startY = elements[n - 7];
1109 joinX = elements[n - 5];
1110 joinY = elements[n - 4];
1111 }
1112 }
1113 else
1114 {
1115 endX = elements[indexOfPathStartThis + 1];
1116 endY = elements[indexOfPathStartThis + 2];
1117
1118 if (n > 6)
1119 {
1120 startX = elements[n - 6];
1121 startY = elements[n - 5];
1122 joinX = elements[n - 3];
1123 joinY = elements[n - 2];
1124 }
1125 }
1126
1127 if (lastWasLine)
1128 {
1129 auto len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY);
1130
1131 if (len1 > 0)
1132 {
1133 auto propNeeded = jmin (0.5, cornerRadius / len1);
1134
1135 *(p.data.end() - 2) = (float) (joinX - (joinX - startX) * propNeeded);
1136 *(p.data.end() - 1) = (float) (joinY - (joinY - startY) * propNeeded);
1137 }
1138
1139 auto len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY);
1140
1141 if (len2 > 0)
1142 {
1143 auto propNeeded = jmin (0.5, cornerRadius / len2);
1144
1146 (float) (joinX + (endX - joinX) * propNeeded),
1147 (float) (joinY + (endY - joinY) * propNeeded));
1148 }
1149
1150 p.lineTo (endX, endY);
1151 }
1152 else if (isMarker (type, lineMarker))
1153 {
1154 p.lineTo (endX, endY);
1155 lastWasLine = true;
1156 }
1157
1158 if (isMarker (type, closeSubPathMarker))
1159 {
1160 if (firstWasLine)
1161 {
1162 startX = elements[n - 3];
1163 startY = elements[n - 2];
1164 joinX = endX;
1165 joinY = endY;
1166 endX = elements[indexOfPathStartThis + 4];
1167 endY = elements[indexOfPathStartThis + 5];
1168
1169 auto len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY);
1170
1171 if (len1 > 0)
1172 {
1173 auto propNeeded = jmin (0.5, cornerRadius / len1);
1174
1175 *(p.data.end() - 2) = (float) (joinX - (joinX - startX) * propNeeded);
1176 *(p.data.end() - 1) = (float) (joinY - (joinY - startY) * propNeeded);
1177 }
1178
1179 auto len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY);
1180
1181 if (len2 > 0)
1182 {
1183 auto propNeeded = jmin (0.5, cornerRadius / len2);
1184
1185 endX = (float) (joinX + (endX - joinX) * propNeeded);
1186 endY = (float) (joinY + (endY - joinY) * propNeeded);
1187
1189
1190 p.data.begin()[indexOfPathStart + 1] = endX;
1191 p.data.begin()[indexOfPathStart + 2] = endY;
1192 }
1193 }
1194
1195 p.closeSubPath();
1196 }
1197 }
1198 else if (isMarker (type, quadMarker))
1199 {
1200 lastWasLine = false;
1201 auto x1 = elements[n++];
1202 auto y1 = elements[n++];
1203 auto x2 = elements[n++];
1204 auto y2 = elements[n++];
1205 p.quadraticTo (x1, y1, x2, y2);
1206 }
1207 else if (isMarker (type, cubicMarker))
1208 {
1209 lastWasLine = false;
1210 auto x1 = elements[n++];
1211 auto y1 = elements[n++];
1212 auto x2 = elements[n++];
1213 auto y2 = elements[n++];
1214 auto x3 = elements[n++];
1215 auto y3 = elements[n++];
1216 p.cubicTo (x1, y1, x2, y2, x3, y3);
1217 }
1218 }
1219
1220 return p;
1221}
1222
1223//==============================================================================
1225{
1226 while (! source.isExhausted())
1227 {
1228 switch (source.readByte())
1229 {
1230 case 'm':
1231 {
1232 auto x = source.readFloat();
1233 auto y = source.readFloat();
1234 startNewSubPath (x, y);
1235 break;
1236 }
1237
1238 case 'l':
1239 {
1240 auto x = source.readFloat();
1241 auto y = source.readFloat();
1242 lineTo (x, y);
1243 break;
1244 }
1245
1246 case 'q':
1247 {
1248 auto x1 = source.readFloat();
1249 auto y1 = source.readFloat();
1250 auto x2 = source.readFloat();
1251 auto y2 = source.readFloat();
1252 quadraticTo (x1, y1, x2, y2);
1253 break;
1254 }
1255
1256 case 'b':
1257 {
1258 auto x1 = source.readFloat();
1259 auto y1 = source.readFloat();
1260 auto x2 = source.readFloat();
1261 auto y2 = source.readFloat();
1262 auto x3 = source.readFloat();
1263 auto y3 = source.readFloat();
1264 cubicTo (x1, y1, x2, y2, x3, y3);
1265 break;
1266 }
1267
1268 case 'c':
1269 closeSubPath();
1270 break;
1271
1272 case 'n':
1273 useNonZeroWinding = true;
1274 break;
1275
1276 case 'z':
1277 useNonZeroWinding = false;
1278 break;
1279
1280 case 'e':
1281 return; // end of path marker
1282
1283 default:
1284 jassertfalse; // illegal char in the stream
1285 break;
1286 }
1287 }
1288}
1289
1290void Path::loadPathFromData (const void* const pathData, const size_t numberOfBytes)
1291{
1293 loadPathFromStream (in);
1294}
1295
1297{
1298 dest.writeByte (useNonZeroWinding ? 'n' : 'z');
1299
1300 for (auto* i = data.begin(); i != data.end();)
1301 {
1302 auto type = *i++;
1303
1304 if (isMarker (type, moveMarker))
1305 {
1306 dest.writeByte ('m');
1307 dest.writeFloat (*i++);
1308 dest.writeFloat (*i++);
1309 }
1310 else if (isMarker (type, lineMarker))
1311 {
1312 dest.writeByte ('l');
1313 dest.writeFloat (*i++);
1314 dest.writeFloat (*i++);
1315 }
1316 else if (isMarker (type, quadMarker))
1317 {
1318 dest.writeByte ('q');
1319 dest.writeFloat (*i++);
1320 dest.writeFloat (*i++);
1321 dest.writeFloat (*i++);
1322 dest.writeFloat (*i++);
1323 }
1324 else if (isMarker (type, cubicMarker))
1325 {
1326 dest.writeByte ('b');
1327 dest.writeFloat (*i++);
1328 dest.writeFloat (*i++);
1329 dest.writeFloat (*i++);
1330 dest.writeFloat (*i++);
1331 dest.writeFloat (*i++);
1332 dest.writeFloat (*i++);
1333 }
1334 else if (isMarker (type, closeSubPathMarker))
1335 {
1336 dest.writeByte ('c');
1337 }
1338 }
1339
1340 dest.writeByte ('e'); // marks the end-of-path
1341}
1342
1344{
1345 MemoryOutputStream s (2048);
1346 if (! useNonZeroWinding)
1347 s << 'a';
1348
1349 float lastMarker = 0.0f;
1350
1351 for (int i = 0; i < data.size();)
1352 {
1353 auto type = data.begin()[i++];
1354 char markerChar = 0;
1355 int numCoords = 0;
1356
1357 if (isMarker (type, moveMarker))
1358 {
1359 markerChar = 'm';
1360 numCoords = 2;
1361 }
1362 else if (isMarker (type, lineMarker))
1363 {
1364 markerChar = 'l';
1365 numCoords = 2;
1366 }
1367 else if (isMarker (type, quadMarker))
1368 {
1369 markerChar = 'q';
1370 numCoords = 4;
1371 }
1372 else if (isMarker (type, cubicMarker))
1373 {
1374 markerChar = 'c';
1375 numCoords = 6;
1376 }
1377 else
1378 {
1379 jassert (isMarker (type, closeSubPathMarker));
1380 markerChar = 'z';
1381 }
1382
1383 if (! isMarker (type, lastMarker))
1384 {
1385 if (s.getDataSize() != 0)
1386 s << ' ';
1387
1388 s << markerChar;
1389 lastMarker = type;
1390 }
1391
1392 while (--numCoords >= 0 && i < data.size())
1393 {
1394 String coord (data.begin()[i++], 3);
1395
1396 while (coord.endsWithChar ('0') && coord != "0")
1397 coord = coord.dropLastCharacters (1);
1398
1399 if (coord.endsWithChar ('.'))
1400 coord = coord.dropLastCharacters (1);
1401
1402 if (s.getDataSize() != 0)
1403 s << ' ';
1404
1405 s << coord;
1406 }
1407 }
1408
1409 return s.toUTF8();
1410}
1411
1413{
1414 clear();
1416
1417 auto t = stringVersion.text;
1418 juce_wchar marker = 'm';
1419 int numValues = 2;
1420 float values[6];
1421
1422 for (;;)
1423 {
1424 auto token = PathHelpers::nextToken (t);
1425 auto firstChar = token[0];
1426 int startNum = 0;
1427
1428 if (firstChar == 0)
1429 break;
1430
1431 if (firstChar == 'm' || firstChar == 'l')
1432 {
1433 marker = firstChar;
1434 numValues = 2;
1435 }
1436 else if (firstChar == 'q')
1437 {
1438 marker = firstChar;
1439 numValues = 4;
1440 }
1441 else if (firstChar == 'c')
1442 {
1443 marker = firstChar;
1444 numValues = 6;
1445 }
1446 else if (firstChar == 'z')
1447 {
1448 marker = firstChar;
1449 numValues = 0;
1450 }
1451 else if (firstChar == 'a')
1452 {
1453 setUsingNonZeroWinding (false);
1454 continue;
1455 }
1456 else
1457 {
1458 ++startNum;
1459 values [0] = token.getFloatValue();
1460 }
1461
1462 for (int i = startNum; i < numValues; ++i)
1463 values [i] = PathHelpers::nextToken (t).getFloatValue();
1464
1465 switch (marker)
1466 {
1467 case 'm': startNewSubPath (values[0], values[1]); break;
1468 case 'l': lineTo (values[0], values[1]); break;
1469 case 'q': quadraticTo (values[0], values[1], values[2], values[3]); break;
1470 case 'c': cubicTo (values[0], values[1], values[2], values[3], values[4], values[5]); break;
1471 case 'z': closeSubPath(); break;
1472 default: jassertfalse; break; // illegal string format?
1473 }
1474 }
1475}
1476
1477//==============================================================================
1478Path::Iterator::Iterator (const Path& p) noexcept
1479 : elementType (startNewSubPath), path (p), index (path.data.begin())
1480{
1481}
1482
1483Path::Iterator::~Iterator() noexcept
1484{
1485}
1486
1488{
1489 if (index != path.data.end())
1490 {
1491 auto type = *index++;
1492
1493 if (isMarker (type, moveMarker))
1494 {
1495 elementType = startNewSubPath;
1496 x1 = *index++;
1497 y1 = *index++;
1498 }
1499 else if (isMarker (type, lineMarker))
1500 {
1501 elementType = lineTo;
1502 x1 = *index++;
1503 y1 = *index++;
1504 }
1505 else if (isMarker (type, quadMarker))
1506 {
1507 elementType = quadraticTo;
1508 x1 = *index++;
1509 y1 = *index++;
1510 x2 = *index++;
1511 y2 = *index++;
1512 }
1513 else if (isMarker (type, cubicMarker))
1514 {
1515 elementType = cubicTo;
1516 x1 = *index++;
1517 y1 = *index++;
1518 x2 = *index++;
1519 y2 = *index++;
1520 x3 = *index++;
1521 y3 = *index++;
1522 }
1523 else if (isMarker (type, closeSubPathMarker))
1524 {
1525 elementType = closePath;
1526 }
1527
1528 return true;
1529 }
1530
1531 return false;
1532}
1533
1534#undef JUCE_CHECK_COORDS_ARE_VALID
1535
1536} // namespace juce
Represents a 2D affine-transformation matrix.
AffineTransform scaled(float factorX, float factorY) const noexcept
Returns a transform which is the same as this one followed by a re-scaling.
static AffineTransform translation(float deltaX, float deltaY) noexcept
Returns a new transform which is a translation.
AffineTransform translated(float deltaX, float deltaY) const noexcept
Returns a new transform which is the same as this one followed by a translation.
static AffineTransform rotation(float angleInRadians) noexcept
Returns a new transform which is a rotation about (0, 0).
bool isEmpty() const noexcept
Returns true if the array is empty, false otherwise.
Definition juce_Array.h:222
void ensureStorageAllocated(int minNumElements)
Increases the array's internal storage to hold a minimum number of elements.
void clearQuick()
Removes all elements from the array without freeing the array's allocated storage.
Definition juce_Array.h:198
int size() const noexcept
Returns the current number of elements in the array.
Definition juce_Array.h:215
ElementType * begin() noexcept
Returns a pointer to the first element in the array.
Definition juce_Array.h:328
ElementType * end() noexcept
Returns a pointer to the element which follows the last element in the array.
Definition juce_Array.h:344
void add(const ElementType &newElement)
Appends a new element at the end of the array.
Definition juce_Array.h:418
ElementType * data() noexcept
Returns a pointer to the first element in the array.
Definition juce_Array.h:360
ElementType getLast() const noexcept
Returns the last element in the array, or a default value if the array is empty.
Definition juce_Array.h:300
The base class for streams that read data.
virtual float readFloat()
Reads four bytes as a 32-bit floating point value.
virtual bool isExhausted()=0
Returns true if the stream has no more data to read.
virtual char readByte()
Reads a byte from the stream.
Represents a type of justification to be used when positioning graphical items.
@ left
Indicates that the item should be aligned against the left edge of the available space.
@ bottom
Indicates that the item should be aligned against the bottom edge of the available space.
@ top
Indicates that the item should be aligned against the top edge of the available space.
@ right
Indicates that the item should be aligned against the right edge of the available space.
bool testFlags(int flagsToTest) const noexcept
Tests a set of flags for this object.
Represents a line.
Definition juce_Line.h:47
Line reversed() const noexcept
Returns a line that is the same as this one, but with the start and end reversed,.
Definition juce_Line.h:106
void setStart(ValueType newStartX, ValueType newStartY) noexcept
Changes this line's start point.
Definition juce_Line.h:94
Point< ValueType > getPointAlongLine(ValueType distanceFromStart) const noexcept
Returns the location of the point which is a given distance along this line.
Definition juce_Line.h:204
ValueType getLength() const noexcept
Returns the length of the line.
Definition juce_Line.h:117
ValueType getDistanceFromPoint(Point< ValueType > targetPoint, Point< ValueType > &pointOnLine) const noexcept
Returns the smallest distance between this line segment and a given point.
Definition juce_Line.h:262
void setEnd(ValueType newEndX, ValueType newEndY) noexcept
Changes this line's end point.
Definition juce_Line.h:97
Point< ValueType > getEnd() const noexcept
Returns the line's end point.
Definition juce_Line.h:91
bool intersects(Line line, Point< ValueType > &intersection) const noexcept
Finds the intersection between two lines.
Definition juce_Line.h:184
Point< ValueType > getStart() const noexcept
Returns the line's start point.
Definition juce_Line.h:88
Allows a block of data to be accessed as a stream.
Writes data to an internal memory buffer, which grows as required.
String toUTF8() const
Returns a String created from the (UTF8) data that has been written to the stream.
size_t getDataSize() const noexcept
Returns the number of bytes of data that have been written to the stream.
The base class for streams that write data to some kind of destination.
virtual bool writeFloat(float value)
Writes a 32-bit floating point value to the stream in a binary format.
virtual bool writeByte(char byte)
Writes a single byte to the stream.
Flattens a Path object into a series of straight-line sections.
bool next()
Fetches the next line segment from the path.
float y2
The y position of the end of the current line segment.
float x2
The x position of the end of the current line segment.
float y1
The y position of the start of the current line segment.
float x1
The x position of the start of the current line segment.
bool next() noexcept
Moves onto the next element in the 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
float getNearestPoint(Point< float > targetPoint, Point< float > &pointOnPath, const AffineTransform &transform=AffineTransform(), float tolerance=defaultToleranceForMeasurement) const
Finds the point along the path which is nearest to a given position.
float getLength(const AffineTransform &transform=AffineTransform(), float tolerance=defaultToleranceForMeasurement) const
Returns the length of the path.
bool contains(float x, float y, float tolerance=defaultToleranceForTesting) const
Checks whether a point lies within the path.
void loadPathFromData(const void *data, size_t numberOfBytes)
Loads a stored path from a block of data.
void addRoundedRectangle(float x, float y, float width, float height, float cornerSize)
Adds a rectangle with rounded corners to the path.
void startNewSubPath(float startX, float startY)
Begins a new subpath with a given starting position.
~Path()
Destructor.
void addPieSegment(float x, float y, float width, float height, float fromRadians, float toRadians, float innerCircleProportionalSize)
Adds a "pie-chart" shape to the path.
void addTriangle(float x1, float y1, float x2, float y2, float x3, float y3)
Adds a triangle to the path.
void addEllipse(float x, float y, float width, float height)
Adds an ellipse to the path.
void preallocateSpace(int numExtraCoordsToMakeSpaceFor)
Preallocates enough space for adding the given number of coordinates to the path.
Point< float > getCurrentPosition() const
Returns the last point that was added to the path by one of the drawing methods.
Path()
Creates an empty path.
void addPath(const Path &pathToAppend)
Adds another path to this one.
void quadraticTo(float controlPointX, float controlPointY, float endPointX, float endPointY)
Adds a quadratic bezier curve from the shape's last position to a new position.
AffineTransform getTransformToScaleToFit(float x, float y, float width, float height, bool preserveProportions, Justification justificationType=Justification::centred) const
Returns a transform that can be used to rescale the path to fit into a given space.
void cubicTo(float controlPoint1X, float controlPoint1Y, float controlPoint2X, float controlPoint2Y, float endPointX, float endPointY)
Adds a cubic bezier curve from the shape's last position to a new position.
Path & operator=(const Path &)
Copies this path from another one.
Rectangle< float > getBoundsTransformed(const AffineTransform &transform) const noexcept
Returns the smallest rectangle that contains all points within the path after it's been transformed w...
bool intersectsLine(Line< float > line, float tolerance=defaultToleranceForTesting) const
Checks whether a line crosses the path.
void addRectangle(float x, float y, float width, float height)
Adds a rectangle to the path.
void scaleToFit(float x, float y, float width, float height, bool preserveProportions) noexcept
Rescales this path to make it fit neatly into a given space.
void addBubble(Rectangle< float > bodyArea, Rectangle< float > maximumArea, Point< float > arrowTipPosition, float cornerSize, float arrowBaseWidth)
Adds a speech-bubble shape to the path.
void clear() noexcept
Removes all lines and curves, resetting the path completely.
void applyTransform(const AffineTransform &transform) noexcept
Applies a 2D transform to all the vertices in the path.
void addCentredArc(float centreX, float centreY, float radiusX, float radiusY, float rotationOfEllipse, float fromRadians, float toRadians, bool startAsNewSubPath=false)
Adds an arc which is centred at a given point, and can have a rotation specified.
void restoreFromString(StringRef stringVersion)
Restores this path from a string that was created with the toString() method.
void closeSubPath()
Closes a the current sub-path with a line back to its start-point.
void writePathToStream(OutputStream &destination) const
Stores the path by writing it out to a stream.
Line< float > getClippedLine(Line< float > line, bool keepSectionOutsidePath) const
Cuts off parts of a line to keep the parts that are either inside or outside this path.
Rectangle< float > getBounds() const noexcept
Returns the smallest rectangle that contains all points within the path.
String toString() const
Creates a string containing a textual representation of this path.
Point< float > getPointAlongPath(float distanceFromStart, const AffineTransform &transform=AffineTransform(), float tolerance=defaultToleranceForMeasurement) const
Returns a point that is the specified distance along the path.
void lineTo(float endX, float endY)
Adds a line from the shape's last position to a new end-point.
Path createPathWithRoundedCorners(float cornerRadius) const
Creates a version of this path where all sharp corners have been replaced by curves.
void addArc(float x, float y, float width, float height, float fromRadians, float toRadians, bool startAsNewSubPath=false)
Adds an elliptical arc to the current path.
void addLineSegment(Line< float > line, float lineThickness)
Adds a line with a specified thickness.
void addStar(Point< float > centre, int numberOfPoints, float innerRadius, float outerRadius, float startAngle=0.0f)
Adds a star shape to the path.
void swapWithPath(Path &) noexcept
Swaps the contents of this path with another one.
void loadPathFromStream(InputStream &source)
Loads a stored path from a data stream.
void addArrow(Line< float > line, float lineThickness, float arrowheadWidth, float arrowheadLength)
Adds a line with an arrowhead on the end.
void setUsingNonZeroWinding(bool isNonZeroWinding) noexcept
Changes the winding-rule to be used when filling the path.
bool isEmpty() const noexcept
Returns true if the path doesn't contain any lines or curves.
void addQuadrilateral(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4)
Adds a quadrilateral to the path.
void addPolygon(Point< float > centre, int numberOfSides, float radius, float startAngle=0.0f)
Adds a polygon shape to the path.
A pair of (x, y) coordinates.
Definition juce_Point.h:42
ValueType y
The point's Y coordinate.
Definition juce_Point.h:252
ValueType x
The point's X coordinate.
Definition juce_Point.h:251
Point< FloatType > getPointOnCircumference(float radius, float angle) const noexcept
Taking this point to be the centre of a circle, this returns a point on its circumference.
Definition juce_Point.h:199
Manages a rectangle and allows geometric operations to be performed on it.
bool contains(ValueType xCoord, ValueType yCoord) const noexcept
Returns true if this coordinate is inside the rectangle.
ValueType getX() const noexcept
Returns the x coordinate of the rectangle's left-hand-side.
ValueType getWidth() const noexcept
Returns the width of the rectangle.
ValueType getY() const noexcept
Returns the y coordinate of the rectangle's top edge.
Rectangle transformedBy(const AffineTransform &transform) const noexcept
Returns the smallest rectangle that can contain the shape created by applying a transform to this rec...
ValueType getHeight() const noexcept
Returns the height of the rectangle.
A simple class for holding temporary references to a string literal or String.
The JUCE String class!
Definition juce_String.h:53
CharPointer_UTF8 CharPointerType
This is the character encoding type used internally to store the string.
#define jassert(expression)
Platform-independent assertion macro.
#define jassertfalse
This will always cause an assertion failure.
typedef float
T max(T... args)
JUCE Namespace.
wchar_t juce_wchar
A platform-independent 32-bit unicode character type.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr bool exactlyEqual(Type a, Type b)
Equivalent to operator==, but suppresses float-equality warnings.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Returns a default-constructed sentinel value.
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
Commonly used mathematical constants.
T swap(T... args)
y1