LMMS
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 (x == x && y == 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//==============================================================================
61const float Path::lineMarker = 100001.0f;
62const float Path::moveMarker = 100002.0f;
63const float Path::quadMarker = 100003.0f;
64const float Path::cubicMarker = 100004.0f;
65const float Path::closeSubPathMarker = 100005.0f;
66
67const float Path::defaultToleranceForTesting = 1.0f;
68const float Path::defaultToleranceForMeasurement = 0.6f;
69
70static bool isMarker (float value, float marker) noexcept
71{
72 return value == marker;
73}
74
75//==============================================================================
79
84
89
90void Path::PathBounds::reset (float x, float y) noexcept
91{
94}
95
96void Path::PathBounds::extend (float x, float y) noexcept
97{
98 if (x < pathXMin) pathXMin = x;
99 else if (x > pathXMax) pathXMax = x;
100
101 if (y < pathYMin) pathYMin = y;
102 else if (y > pathYMax) pathYMax = y;
103}
104
105//==============================================================================
107{
108}
109
111{
112}
113
114Path::Path (const Path& other)
115 : data (other.data),
116 bounds (other.bounds),
118{
119}
120
121Path& Path::operator= (const Path& other)
122{
123 if (this != &other)
124 {
125 data = other.data;
126 bounds = other.bounds;
128 }
129
130 return *this;
131}
132
134 : data (std::move (other.data)),
135 bounds (other.bounds),
136 useNonZeroWinding (other.useNonZeroWinding)
137{
138}
139
140Path& Path::operator= (Path&& other) noexcept
141{
142 data = std::move (other.data);
143 bounds = other.bounds;
144 useNonZeroWinding = other.useNonZeroWinding;
145 return *this;
146}
147
148bool Path::operator== (const Path& other) const noexcept { return useNonZeroWinding == other.useNonZeroWinding && data == other.data; }
149bool Path::operator!= (const Path& other) const noexcept { return ! operator== (other); }
150
152{
153 data.clearQuick();
154 bounds.reset();
155}
156
157void Path::swapWithPath (Path& other) noexcept
158{
159 data.swapWith (other.data);
160 std::swap (bounds.pathXMin, other.bounds.pathXMin);
161 std::swap (bounds.pathXMax, other.bounds.pathXMax);
162 std::swap (bounds.pathYMin, other.bounds.pathYMin);
163 std::swap (bounds.pathYMax, other.bounds.pathYMax);
164 std::swap (useNonZeroWinding, other.useNonZeroWinding);
165}
166
167//==============================================================================
168void Path::setUsingNonZeroWinding (const bool isNonZero) noexcept
169{
170 useNonZeroWinding = isNonZero;
171}
172
173void Path::scaleToFit (float x, float y, float w, float h, bool preserveProportions) noexcept
174{
175 applyTransform (getTransformToScaleToFit (x, y, w, h, preserveProportions));
176}
177
178//==============================================================================
180{
181 for (auto i = data.begin(), e = data.end(); i != e; ++i)
182 {
183 auto type = *i;
184
185 if (isMarker (type, moveMarker))
186 {
187 i += 2;
188 }
189 else if (isMarker (type, lineMarker)
192 {
193 return false;
194 }
195 }
196
197 return true;
198}
199
201{
202 return bounds.getRectangle();
203}
204
206{
207 return getBounds().transformedBy (transform);
208}
209
210//==============================================================================
211void Path::preallocateSpace (int numExtraCoordsToMakeSpaceFor)
212{
213 data.ensureStorageAllocated (data.size() + numExtraCoordsToMakeSpaceFor);
214}
215
216void Path::startNewSubPath (const float x, const float y)
217{
219
220 if (data.isEmpty())
221 bounds.reset (x, y);
222 else
223 bounds.extend (x, y);
224
225 data.add (moveMarker, x, y);
226}
227
232
233void Path::lineTo (const float x, const float y)
234{
236
237 if (data.isEmpty())
238 startNewSubPath (0, 0);
239
240 data.add (lineMarker, x, y);
241 bounds.extend (x, y);
242}
243
245{
246 lineTo (end.x, end.y);
247}
248
249void Path::quadraticTo (const float x1, const float y1,
250 const float x2, const float y2)
251{
254
255 if (data.isEmpty())
256 startNewSubPath (0, 0);
257
258 data.add (quadMarker, x1, y1, x2, y2);
259 bounds.extend (x1, y1, x2, y2);
260}
261
262void Path::quadraticTo (Point<float> controlPoint, Point<float> endPoint)
263{
264 quadraticTo (controlPoint.x, controlPoint.y,
265 endPoint.x, endPoint.y);
266}
267
268void Path::cubicTo (const float x1, const float y1,
269 const float x2, const float y2,
270 const float x3, const float y3)
271{
275
276 if (data.isEmpty())
277 startNewSubPath (0, 0);
278
279 data.add (cubicMarker, x1, y1, x2, y2, x3, y3);
280 bounds.extend (x1, y1, x2, y2, x3, y3);
281}
282
283void Path::cubicTo (Point<float> controlPoint1,
284 Point<float> controlPoint2,
285 Point<float> endPoint)
286{
287 cubicTo (controlPoint1.x, controlPoint1.y,
288 controlPoint2.x, controlPoint2.y,
289 endPoint.x, endPoint.y);
290}
291
293{
294 if (! (data.isEmpty() || isMarker (data.getLast(), closeSubPathMarker)))
296}
297
299{
300 if (data.isEmpty())
301 return {};
302
303 auto* i = data.end() - 1;
304
306 {
307 while (i != data.begin())
308 {
309 if (isMarker (*--i, moveMarker))
310 {
311 i += 2;
312 break;
313 }
314 }
315 }
316
317 if (i != data.begin())
318 return { *(i - 1), *i };
319
320 return {};
321}
322
323void Path::addRectangle (float x, float y, float w, float h)
324{
325 auto x1 = x, y1 = y, x2 = x + w, y2 = y + h;
326
327 if (w < 0) std::swap (x1, x2);
328 if (h < 0) std::swap (y1, y2);
329
330 if (data.isEmpty())
331 {
332 bounds.pathXMin = x1;
333 bounds.pathXMax = x2;
334 bounds.pathYMin = y1;
335 bounds.pathYMax = y2;
336 }
337 else
338 {
339 bounds.pathXMin = jmin (bounds.pathXMin, x1);
340 bounds.pathXMax = jmax (bounds.pathXMax, x2);
341 bounds.pathYMin = jmin (bounds.pathYMin, y1);
342 bounds.pathYMax = jmax (bounds.pathYMax, y2);
343 }
344
345 data.add (moveMarker, x1, y2,
346 lineMarker, x1, y1,
347 lineMarker, x2, y1,
348 lineMarker, x2, y2,
350}
351
352void Path::addRoundedRectangle (float x, float y, float w, float h, float csx, float csy)
353{
354 addRoundedRectangle (x, y, w, h, csx, csy, true, true, true, true);
355}
356
357void Path::addRoundedRectangle (const float x, const float y, const float w, const float h,
358 float csx, float csy,
359 const bool curveTopLeft, const bool curveTopRight,
360 const bool curveBottomLeft, const bool curveBottomRight)
361{
362 csx = jmin (csx, w * 0.5f);
363 csy = jmin (csy, h * 0.5f);
364 auto cs45x = csx * 0.45f;
365 auto cs45y = csy * 0.45f;
366 auto x2 = x + w;
367 auto y2 = y + h;
368
369 if (curveTopLeft)
370 {
371 startNewSubPath (x, y + csy);
372 cubicTo (x, y + cs45y, x + cs45x, y, x + csx, y);
373 }
374 else
375 {
377 }
378
379 if (curveTopRight)
380 {
381 lineTo (x2 - csx, y);
382 cubicTo (x2 - cs45x, y, x2, y + cs45y, x2, y + csy);
383 }
384 else
385 {
386 lineTo (x2, y);
387 }
388
389 if (curveBottomRight)
390 {
391 lineTo (x2, y2 - csy);
392 cubicTo (x2, y2 - cs45y, x2 - cs45x, y2, x2 - csx, y2);
393 }
394 else
395 {
396 lineTo (x2, y2);
397 }
398
399 if (curveBottomLeft)
400 {
401 lineTo (x + csx, y2);
402 cubicTo (x + cs45x, y2, x, y2 - cs45y, x, y2 - csy);
403 }
404 else
405 {
406 lineTo (x, y2);
407 }
408
409 closeSubPath();
410}
411
412void Path::addRoundedRectangle (float x, float y, float w, float h, float cs)
413{
414 addRoundedRectangle (x, y, w, h, cs, cs);
415}
416
417void Path::addTriangle (float x1, float y1,
418 float x2, float y2,
419 float x3, float y3)
420{
421 addTriangle ({ x1, y1 },
422 { x2, y2 },
423 { x3, y3 });
424}
425
427{
428 startNewSubPath (p1);
429 lineTo (p2);
430 lineTo (p3);
431 closeSubPath();
432}
433
434void Path::addQuadrilateral (float x1, float y1,
435 float x2, float y2,
436 float x3, float y3,
437 float x4, float y4)
438{
439 startNewSubPath (x1, y1);
440 lineTo (x2, y2);
441 lineTo (x3, y3);
442 lineTo (x4, y4);
443 closeSubPath();
444}
445
446void Path::addEllipse (float x, float y, float w, float h)
447{
448 addEllipse ({ x, y, w, h });
449}
450
452{
453 auto hw = area.getWidth() * 0.5f;
454 auto hw55 = hw * 0.55f;
455 auto hh = area.getHeight() * 0.5f;
456 auto hh55 = hh * 0.55f;
457 auto cx = area.getX() + hw;
458 auto cy = area.getY() + hh;
459
460 startNewSubPath (cx, cy - hh);
461 cubicTo (cx + hw55, cy - hh, cx + hw, cy - hh55, cx + hw, cy);
462 cubicTo (cx + hw, cy + hh55, cx + hw55, cy + hh, cx, cy + hh);
463 cubicTo (cx - hw55, cy + hh, cx - hw, cy + hh55, cx - hw, cy);
464 cubicTo (cx - hw, cy - hh55, cx - hw55, cy - hh, cx, cy - hh);
465 closeSubPath();
466}
467
468void Path::addArc (float x, float y, float w, float h,
469 float fromRadians, float toRadians,
470 bool startAsNewSubPath)
471{
472 auto radiusX = w / 2.0f;
473 auto radiusY = h / 2.0f;
474
475 addCentredArc (x + radiusX,
476 y + radiusY,
477 radiusX, radiusY,
478 0.0f,
479 fromRadians, toRadians,
480 startAsNewSubPath);
481}
482
483void Path::addCentredArc (float centreX, float centreY,
484 float radiusX, float radiusY,
485 float rotationOfEllipse,
486 float fromRadians, float toRadians,
487 bool startAsNewSubPath)
488{
489 if (radiusX > 0.0f && radiusY > 0.0f)
490 {
491 Point<float> centre (centreX, centreY);
492 auto rotation = AffineTransform::rotation (rotationOfEllipse, centreX, centreY);
493 auto angle = fromRadians;
494
495 if (startAsNewSubPath)
496 startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
497
498 if (fromRadians < toRadians)
499 {
500 if (startAsNewSubPath)
502
503 while (angle < toRadians)
504 {
505 lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
507 }
508 }
509 else
510 {
511 if (startAsNewSubPath)
513
514 while (angle > toRadians)
515 {
516 lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
518 }
519 }
520
521 lineTo (centre.getPointOnCircumference (radiusX, radiusY, toRadians).transformedBy (rotation));
522 }
523}
524
525void Path::addPieSegment (float x, float y, float width, float height,
526 float fromRadians, float toRadians,
527 float innerCircleProportionalSize)
528{
529 auto radiusX = width * 0.5f;
530 auto radiusY = height * 0.5f;
531 Point<float> centre (x + radiusX, y + radiusY);
532
533 startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, fromRadians));
534 addArc (x, y, width, height, fromRadians, toRadians);
535
536 if (std::abs (fromRadians - toRadians) > MathConstants<float>::pi * 1.999f)
537 {
538 closeSubPath();
539
540 if (innerCircleProportionalSize > 0)
541 {
542 radiusX *= innerCircleProportionalSize;
543 radiusY *= innerCircleProportionalSize;
544
545 startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, toRadians));
546 addArc (centre.x - radiusX, centre.y - radiusY, radiusX * 2.0f, radiusY * 2.0f, toRadians, fromRadians);
547 }
548 }
549 else
550 {
551 if (innerCircleProportionalSize > 0)
552 {
553 radiusX *= innerCircleProportionalSize;
554 radiusY *= innerCircleProportionalSize;
555
556 addArc (centre.x - radiusX, centre.y - radiusY, radiusX * 2.0f, radiusY * 2.0f, toRadians, fromRadians);
557 }
558 else
559 {
560 lineTo (centre);
561 }
562 }
563
564 closeSubPath();
565}
566
568 float fromRadians, float toRadians,
569 float innerCircleProportionalSize)
570{
571 addPieSegment (segmentBounds.getX(),
572 segmentBounds.getY(),
573 segmentBounds.getWidth(),
574 segmentBounds.getHeight(),
575 fromRadians,
576 toRadians,
577 innerCircleProportionalSize);
578}
579
580//==============================================================================
581void Path::addLineSegment (Line<float> line, float lineThickness)
582{
583 auto reversed = line.reversed();
584 lineThickness *= 0.5f;
585
586 startNewSubPath (line.getPointAlongLine (0, lineThickness));
587 lineTo (line.getPointAlongLine (0, -lineThickness));
588 lineTo (reversed.getPointAlongLine (0, lineThickness));
589 lineTo (reversed.getPointAlongLine (0, -lineThickness));
590 closeSubPath();
591}
592
593void Path::addArrow (Line<float> line, float lineThickness,
594 float arrowheadWidth, float arrowheadLength)
595{
596 auto reversed = line.reversed();
597 lineThickness *= 0.5f;
598 arrowheadWidth *= 0.5f;
599 arrowheadLength = jmin (arrowheadLength, 0.8f * line.getLength());
600
601 startNewSubPath (line.getPointAlongLine (0, lineThickness));
602 lineTo (line.getPointAlongLine (0, -lineThickness));
603 lineTo (reversed.getPointAlongLine (arrowheadLength, lineThickness));
604 lineTo (reversed.getPointAlongLine (arrowheadLength, arrowheadWidth));
605 lineTo (line.getEnd());
606 lineTo (reversed.getPointAlongLine (arrowheadLength, -arrowheadWidth));
607 lineTo (reversed.getPointAlongLine (arrowheadLength, -lineThickness));
608 closeSubPath();
609}
610
611void Path::addPolygon (Point<float> centre, int numberOfSides,
612 float radius, float startAngle)
613{
614 jassert (numberOfSides > 1); // this would be silly.
615
616 if (numberOfSides > 1)
617 {
618 auto angleBetweenPoints = MathConstants<float>::twoPi / (float) numberOfSides;
619
620 for (int i = 0; i < numberOfSides; ++i)
621 {
622 auto angle = startAngle + (float) i * angleBetweenPoints;
623 auto p = centre.getPointOnCircumference (radius, angle);
624
625 if (i == 0)
627 else
628 lineTo (p);
629 }
630
631 closeSubPath();
632 }
633}
634
635void Path::addStar (Point<float> centre, int numberOfPoints, float innerRadius,
636 float outerRadius, float startAngle)
637{
638 jassert (numberOfPoints > 1); // this would be silly.
639
640 if (numberOfPoints > 1)
641 {
642 auto angleBetweenPoints = MathConstants<float>::twoPi / (float) numberOfPoints;
643
644 for (int i = 0; i < numberOfPoints; ++i)
645 {
646 auto angle = startAngle + (float) i * angleBetweenPoints;
647 auto p = centre.getPointOnCircumference (outerRadius, angle);
648
649 if (i == 0)
651 else
652 lineTo (p);
653
654 lineTo (centre.getPointOnCircumference (innerRadius, angle + angleBetweenPoints * 0.5f));
655 }
656
657 closeSubPath();
658 }
659}
660
662 Rectangle<float> maximumArea,
663 Point<float> arrowTip,
664 float cornerSize,
665 float arrowBaseWidth)
666{
667 auto halfW = bodyArea.getWidth() / 2.0f;
668 auto halfH = bodyArea.getHeight() / 2.0f;
669 auto cornerSizeW = jmin (cornerSize, halfW);
670 auto cornerSizeH = jmin (cornerSize, halfH);
671 auto cornerSizeW2 = 2.0f * cornerSizeW;
672 auto cornerSizeH2 = 2.0f * cornerSizeH;
673
674 startNewSubPath (bodyArea.getX() + cornerSizeW, bodyArea.getY());
675
676 auto targetLimit = bodyArea.reduced (jmin (halfW - 1.0f, cornerSizeW + arrowBaseWidth),
677 jmin (halfH - 1.0f, cornerSizeH + arrowBaseWidth));
678
679 if (Rectangle<float> (targetLimit.getX(), maximumArea.getY(),
680 targetLimit.getWidth(), bodyArea.getY() - maximumArea.getY()).contains (arrowTip))
681 {
682 lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getY());
683 lineTo (arrowTip.x, arrowTip.y);
684 lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getY());
685 }
686
687 lineTo (bodyArea.getRight() - cornerSizeW, bodyArea.getY());
688 addArc (bodyArea.getRight() - cornerSizeW2, bodyArea.getY(), cornerSizeW2, cornerSizeH2, 0, MathConstants<float>::halfPi);
689
690 if (Rectangle<float> (bodyArea.getRight(), targetLimit.getY(),
691 maximumArea.getRight() - bodyArea.getRight(), targetLimit.getHeight()).contains (arrowTip))
692 {
693 lineTo (bodyArea.getRight(), arrowTip.y - arrowBaseWidth);
694 lineTo (arrowTip.x, arrowTip.y);
695 lineTo (bodyArea.getRight(), arrowTip.y + arrowBaseWidth);
696 }
697
698 lineTo (bodyArea.getRight(), bodyArea.getBottom() - cornerSizeH);
699 addArc (bodyArea.getRight() - cornerSizeW2, bodyArea.getBottom() - cornerSizeH2, cornerSizeW2, cornerSizeH2, MathConstants<float>::halfPi, MathConstants<float>::pi);
700
701 if (Rectangle<float> (targetLimit.getX(), bodyArea.getBottom(),
702 targetLimit.getWidth(), maximumArea.getBottom() - bodyArea.getBottom()).contains (arrowTip))
703 {
704 lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getBottom());
705 lineTo (arrowTip.x, arrowTip.y);
706 lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getBottom());
707 }
708
709 lineTo (bodyArea.getX() + cornerSizeW, bodyArea.getBottom());
710 addArc (bodyArea.getX(), bodyArea.getBottom() - cornerSizeH2, cornerSizeW2, cornerSizeH2, MathConstants<float>::pi, MathConstants<float>::pi * 1.5f);
711
712 if (Rectangle<float> (maximumArea.getX(), targetLimit.getY(),
713 bodyArea.getX() - maximumArea.getX(), targetLimit.getHeight()).contains (arrowTip))
714 {
715 lineTo (bodyArea.getX(), arrowTip.y + arrowBaseWidth);
716 lineTo (arrowTip.x, arrowTip.y);
717 lineTo (bodyArea.getX(), arrowTip.y - arrowBaseWidth);
718 }
719
720 lineTo (bodyArea.getX(), bodyArea.getY() + cornerSizeH);
721 addArc (bodyArea.getX(), bodyArea.getY(), cornerSizeW2, cornerSizeH2, MathConstants<float>::pi * 1.5f, MathConstants<float>::twoPi - 0.05f);
722
723 closeSubPath();
724}
725
726void Path::addPath (const Path& other)
727{
728 const auto* d = other.data.begin();
729
730 for (int i = 0; i < other.data.size();)
731 {
732 auto type = d[i++];
733
734 if (isMarker (type, moveMarker))
735 {
736 startNewSubPath (d[i], d[i + 1]);
737 i += 2;
738 }
739 else if (isMarker (type, lineMarker))
740 {
741 lineTo (d[i], d[i + 1]);
742 i += 2;
743 }
744 else if (isMarker (type, quadMarker))
745 {
746 quadraticTo (d[i], d[i + 1], d[i + 2], d[i + 3]);
747 i += 4;
748 }
749 else if (isMarker (type, cubicMarker))
750 {
751 cubicTo (d[i], d[i + 1], d[i + 2], d[i + 3], d[i + 4], d[i + 5]);
752 i += 6;
753 }
754 else if (isMarker (type, closeSubPathMarker))
755 {
756 closeSubPath();
757 }
758 else
759 {
760 // something's gone wrong with the element list!
762 }
763 }
764}
765
766void Path::addPath (const Path& other,
767 const AffineTransform& transformToApply)
768{
769 const auto* d = other.data.begin();
770
771 for (int i = 0; i < other.data.size();)
772 {
773 auto type = d[i++];
774
776 {
777 closeSubPath();
778 }
779 else
780 {
781 auto x = d[i++];
782 auto y = d[i++];
783 transformToApply.transformPoint (x, y);
784
785 if (isMarker (type, moveMarker))
786 {
788 }
789 else if (isMarker (type, lineMarker))
790 {
791 lineTo (x, y);
792 }
793 else if (isMarker (type, quadMarker))
794 {
795 auto x2 = d[i++];
796 auto y2 = d[i++];
797 transformToApply.transformPoint (x2, y2);
798
799 quadraticTo (x, y, x2, y2);
800 }
801 else if (isMarker (type, cubicMarker))
802 {
803 auto x2 = d[i++];
804 auto y2 = d[i++];
805 auto x3 = d[i++];
806 auto y3 = d[i++];
807 transformToApply.transformPoints (x2, y2, x3, y3);
808
809 cubicTo (x, y, x2, y2, x3, y3);
810 }
811 else
812 {
813 // something's gone wrong with the element list!
815 }
816 }
817 }
818}
819
820//==============================================================================
821void Path::applyTransform (const AffineTransform& transform) noexcept
822{
823 bounds.reset();
824 bool firstPoint = true;
825 float* d = data.begin();
826 auto* end = data.end();
827
828 while (d < end)
829 {
830 auto type = *d++;
831
832 if (isMarker (type, moveMarker))
833 {
834 transform.transformPoint (d[0], d[1]);
836
837 if (firstPoint)
838 {
839 firstPoint = false;
840 bounds.reset (d[0], d[1]);
841 }
842 else
843 {
844 bounds.extend (d[0], d[1]);
845 }
846
847 d += 2;
848 }
849 else if (isMarker (type, lineMarker))
850 {
851 transform.transformPoint (d[0], d[1]);
853 bounds.extend (d[0], d[1]);
854 d += 2;
855 }
856 else if (isMarker (type, quadMarker))
857 {
858 transform.transformPoints (d[0], d[1], d[2], d[3]);
861 bounds.extend (d[0], d[1], d[2], d[3]);
862 d += 4;
863 }
864 else if (isMarker (type, cubicMarker))
865 {
866 transform.transformPoints (d[0], d[1], d[2], d[3], d[4], d[5]);
870 bounds.extend (d[0], d[1], d[2], d[3], d[4], d[5]);
871 d += 6;
872 }
873 }
874}
875
876
877//==============================================================================
879 Justification justification) const
880{
881 return getTransformToScaleToFit (area.getX(), area.getY(), area.getWidth(), area.getHeight(),
882 preserveProportions, justification);
883}
884
886 bool preserveProportions,
887 Justification justification) const
888{
889 auto boundsRect = getBounds();
890
891 if (preserveProportions)
892 {
893 if (w <= 0 || h <= 0 || boundsRect.isEmpty())
894 return AffineTransform();
895
896 float newW, newH;
897 auto srcRatio = boundsRect.getHeight() / boundsRect.getWidth();
898
899 if (srcRatio > h / w)
900 {
901 newW = h / srcRatio;
902 newH = h;
903 }
904 else
905 {
906 newW = w;
907 newH = w * srcRatio;
908 }
909
910 auto newXCentre = x;
911 auto newYCentre = y;
912
913 if (justification.testFlags (Justification::left)) newXCentre += newW * 0.5f;
914 else if (justification.testFlags (Justification::right)) newXCentre += w - newW * 0.5f;
915 else newXCentre += w * 0.5f;
916
917 if (justification.testFlags (Justification::top)) newYCentre += newH * 0.5f;
918 else if (justification.testFlags (Justification::bottom)) newYCentre += h - newH * 0.5f;
919 else newYCentre += h * 0.5f;
920
921 return AffineTransform::translation (boundsRect.getWidth() * -0.5f - boundsRect.getX(),
922 boundsRect.getHeight() * -0.5f - boundsRect.getY())
923 .scaled (newW / boundsRect.getWidth(),
924 newH / boundsRect.getHeight())
925 .translated (newXCentre, newYCentre);
926 }
927 else
928 {
929 return AffineTransform::translation (-boundsRect.getX(), -boundsRect.getY())
930 .scaled (w / boundsRect.getWidth(),
931 h / boundsRect.getHeight())
932 .translated (x, y);
933 }
934}
935
936//==============================================================================
937bool Path::contains (float x, float y, float tolerance) const
938{
941 return false;
942
943 PathFlatteningIterator i (*this, AffineTransform(), tolerance);
944
945 int positiveCrossings = 0;
946 int negativeCrossings = 0;
947
948 while (i.next())
949 {
950 if ((i.y1 <= y && i.y2 > y) || (i.y2 <= y && i.y1 > y))
951 {
952 auto intersectX = i.x1 + (i.x2 - i.x1) * (y - i.y1) / (i.y2 - i.y1);
953
954 if (intersectX <= x)
955 {
956 if (i.y1 < i.y2)
957 ++positiveCrossings;
958 else
959 ++negativeCrossings;
960 }
961 }
962 }
963
964 return useNonZeroWinding ? (negativeCrossings != positiveCrossings)
965 : ((negativeCrossings + positiveCrossings) & 1) != 0;
966}
967
968bool Path::contains (Point<float> point, float tolerance) const
969{
970 return contains (point.x, point.y, tolerance);
971}
972
973bool Path::intersectsLine (Line<float> line, float tolerance) const
974{
975 PathFlatteningIterator i (*this, AffineTransform(), tolerance);
976 Point<float> intersection;
977
978 while (i.next())
979 if (line.intersects (Line<float> (i.x1, i.y1, i.x2, i.y2), intersection))
980 return true;
981
982 return false;
983}
984
985Line<float> Path::getClippedLine (Line<float> line, bool keepSectionOutsidePath) const
986{
987 Line<float> result (line);
988 const bool startInside = contains (line.getStart());
989 const bool endInside = contains (line.getEnd());
990
991 if (startInside == endInside)
992 {
993 if (keepSectionOutsidePath == startInside)
995 }
996 else
997 {
999 Point<float> intersection;
1000
1001 while (i.next())
1002 {
1003 if (line.intersects ({ i.x1, i.y1, i.x2, i.y2 }, intersection))
1004 {
1005 if ((startInside && keepSectionOutsidePath) || (endInside && ! keepSectionOutsidePath))
1006 result.setStart (intersection);
1007 else
1008 result.setEnd (intersection);
1009 }
1010 }
1011 }
1012
1013 return result;
1014}
1015
1016float Path::getLength (const AffineTransform& transform, float tolerance) const
1017{
1018 float length = 0;
1019 PathFlatteningIterator i (*this, transform, tolerance);
1020
1021 while (i.next())
1022 length += Line<float> (i.x1, i.y1, i.x2, i.y2).getLength();
1023
1024 return length;
1025}
1026
1027Point<float> Path::getPointAlongPath (float distanceFromStart,
1028 const AffineTransform& transform,
1029 float tolerance) const
1030{
1031 PathFlatteningIterator i (*this, transform, tolerance);
1032
1033 while (i.next())
1034 {
1035 const Line<float> line (i.x1, i.y1, i.x2, i.y2);
1036 auto lineLength = line.getLength();
1037
1038 if (distanceFromStart <= lineLength)
1039 return line.getPointAlongLine (distanceFromStart);
1040
1041 distanceFromStart -= lineLength;
1042 }
1043
1044 return { i.x2, i.y2 };
1045}
1046
1047float Path::getNearestPoint (Point<float> targetPoint, Point<float>& pointOnPath,
1048 const AffineTransform& transform,
1049 float tolerance) const
1050{
1051 PathFlatteningIterator i (*this, transform, tolerance);
1052 float bestPosition = 0, bestDistance = std::numeric_limits<float>::max();
1053 float length = 0;
1054 Point<float> pointOnLine;
1055
1056 while (i.next())
1057 {
1058 const Line<float> line (i.x1, i.y1, i.x2, i.y2);
1059 auto distance = line.getDistanceFromPoint (targetPoint, pointOnLine);
1060
1061 if (distance < bestDistance)
1062 {
1063 bestDistance = distance;
1064 bestPosition = length + pointOnLine.getDistanceFrom (line.getStart());
1065 pointOnPath = pointOnLine;
1066 }
1067
1068 length += line.getLength();
1069 }
1070
1071 return bestPosition;
1072}
1073
1074//==============================================================================
1075Path Path::createPathWithRoundedCorners (const float cornerRadius) const
1076{
1077 if (cornerRadius <= 0.01f)
1078 return *this;
1079
1080 Path p;
1081 int n = 0, indexOfPathStart = 0, indexOfPathStartThis = 0;
1082 auto* elements = data.begin();
1083 bool lastWasLine = false, firstWasLine = false;
1084
1085 while (n < data.size())
1086 {
1087 auto type = elements[n++];
1088
1089 if (isMarker (type, moveMarker))
1090 {
1091 indexOfPathStart = p.data.size();
1092 indexOfPathStartThis = n - 1;
1093 auto x = elements[n++];
1094 auto y = elements[n++];
1095 p.startNewSubPath (x, y);
1096 lastWasLine = false;
1097 firstWasLine = (isMarker (elements[n], lineMarker));
1098 }
1100 {
1101 float startX = 0, startY = 0, joinX = 0, joinY = 0, endX, endY;
1102
1103 if (isMarker (type, lineMarker))
1104 {
1105 endX = elements[n++];
1106 endY = elements[n++];
1107
1108 if (n > 8)
1109 {
1110 startX = elements[n - 8];
1111 startY = elements[n - 7];
1112 joinX = elements[n - 5];
1113 joinY = elements[n - 4];
1114 }
1115 }
1116 else
1117 {
1118 endX = elements[indexOfPathStartThis + 1];
1119 endY = elements[indexOfPathStartThis + 2];
1120
1121 if (n > 6)
1122 {
1123 startX = elements[n - 6];
1124 startY = elements[n - 5];
1125 joinX = elements[n - 3];
1126 joinY = elements[n - 2];
1127 }
1128 }
1129
1130 if (lastWasLine)
1131 {
1132 auto len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY);
1133
1134 if (len1 > 0)
1135 {
1136 auto propNeeded = jmin (0.5, cornerRadius / len1);
1137
1138 *(p.data.end() - 2) = (float) (joinX - (joinX - startX) * propNeeded);
1139 *(p.data.end() - 1) = (float) (joinY - (joinY - startY) * propNeeded);
1140 }
1141
1142 auto len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY);
1143
1144 if (len2 > 0)
1145 {
1146 auto propNeeded = jmin (0.5, cornerRadius / len2);
1147
1148 p.quadraticTo (joinX, joinY,
1149 (float) (joinX + (endX - joinX) * propNeeded),
1150 (float) (joinY + (endY - joinY) * propNeeded));
1151 }
1152
1153 p.lineTo (endX, endY);
1154 }
1155 else if (isMarker (type, lineMarker))
1156 {
1157 p.lineTo (endX, endY);
1158 lastWasLine = true;
1159 }
1160
1162 {
1163 if (firstWasLine)
1164 {
1165 startX = elements[n - 3];
1166 startY = elements[n - 2];
1167 joinX = endX;
1168 joinY = endY;
1169 endX = elements[indexOfPathStartThis + 4];
1170 endY = elements[indexOfPathStartThis + 5];
1171
1172 auto len1 = PathHelpers::lengthOf (startX, startY, joinX, joinY);
1173
1174 if (len1 > 0)
1175 {
1176 auto propNeeded = jmin (0.5, cornerRadius / len1);
1177
1178 *(p.data.end() - 2) = (float) (joinX - (joinX - startX) * propNeeded);
1179 *(p.data.end() - 1) = (float) (joinY - (joinY - startY) * propNeeded);
1180 }
1181
1182 auto len2 = PathHelpers::lengthOf (endX, endY, joinX, joinY);
1183
1184 if (len2 > 0)
1185 {
1186 auto propNeeded = jmin (0.5, cornerRadius / len2);
1187
1188 endX = (float) (joinX + (endX - joinX) * propNeeded);
1189 endY = (float) (joinY + (endY - joinY) * propNeeded);
1190
1191 p.quadraticTo (joinX, joinY, endX, endY);
1192
1193 p.data.begin()[indexOfPathStart + 1] = endX;
1194 p.data.begin()[indexOfPathStart + 2] = endY;
1195 }
1196 }
1197
1198 p.closeSubPath();
1199 }
1200 }
1201 else if (isMarker (type, quadMarker))
1202 {
1203 lastWasLine = false;
1204 auto x1 = elements[n++];
1205 auto y1 = elements[n++];
1206 auto x2 = elements[n++];
1207 auto y2 = elements[n++];
1208 p.quadraticTo (x1, y1, x2, y2);
1209 }
1210 else if (isMarker (type, cubicMarker))
1211 {
1212 lastWasLine = false;
1213 auto x1 = elements[n++];
1214 auto y1 = elements[n++];
1215 auto x2 = elements[n++];
1216 auto y2 = elements[n++];
1217 auto x3 = elements[n++];
1218 auto y3 = elements[n++];
1219 p.cubicTo (x1, y1, x2, y2, x3, y3);
1220 }
1221 }
1222
1223 return p;
1224}
1225
1226//==============================================================================
1228{
1229 while (! source.isExhausted())
1230 {
1231 switch (source.readByte())
1232 {
1233 case 'm':
1234 {
1235 auto x = source.readFloat();
1236 auto y = source.readFloat();
1237 startNewSubPath (x, y);
1238 break;
1239 }
1240
1241 case 'l':
1242 {
1243 auto x = source.readFloat();
1244 auto y = source.readFloat();
1245 lineTo (x, y);
1246 break;
1247 }
1248
1249 case 'q':
1250 {
1251 auto x1 = source.readFloat();
1252 auto y1 = source.readFloat();
1253 auto x2 = source.readFloat();
1254 auto y2 = source.readFloat();
1255 quadraticTo (x1, y1, x2, y2);
1256 break;
1257 }
1258
1259 case 'b':
1260 {
1261 auto x1 = source.readFloat();
1262 auto y1 = source.readFloat();
1263 auto x2 = source.readFloat();
1264 auto y2 = source.readFloat();
1265 auto x3 = source.readFloat();
1266 auto y3 = source.readFloat();
1267 cubicTo (x1, y1, x2, y2, x3, y3);
1268 break;
1269 }
1270
1271 case 'c':
1272 closeSubPath();
1273 break;
1274
1275 case 'n':
1276 useNonZeroWinding = true;
1277 break;
1278
1279 case 'z':
1280 useNonZeroWinding = false;
1281 break;
1282
1283 case 'e':
1284 return; // end of path marker
1285
1286 default:
1287 jassertfalse; // illegal char in the stream
1288 break;
1289 }
1290 }
1291}
1292
1293void Path::loadPathFromData (const void* const pathData, const size_t numberOfBytes)
1294{
1295 MemoryInputStream in (pathData, numberOfBytes, false);
1297}
1298
1300{
1301 dest.writeByte (useNonZeroWinding ? 'n' : 'z');
1302
1303 for (auto* i = data.begin(); i != data.end();)
1304 {
1305 auto type = *i++;
1306
1307 if (isMarker (type, moveMarker))
1308 {
1309 dest.writeByte ('m');
1310 dest.writeFloat (*i++);
1311 dest.writeFloat (*i++);
1312 }
1313 else if (isMarker (type, lineMarker))
1314 {
1315 dest.writeByte ('l');
1316 dest.writeFloat (*i++);
1317 dest.writeFloat (*i++);
1318 }
1319 else if (isMarker (type, quadMarker))
1320 {
1321 dest.writeByte ('q');
1322 dest.writeFloat (*i++);
1323 dest.writeFloat (*i++);
1324 dest.writeFloat (*i++);
1325 dest.writeFloat (*i++);
1326 }
1327 else if (isMarker (type, cubicMarker))
1328 {
1329 dest.writeByte ('b');
1330 dest.writeFloat (*i++);
1331 dest.writeFloat (*i++);
1332 dest.writeFloat (*i++);
1333 dest.writeFloat (*i++);
1334 dest.writeFloat (*i++);
1335 dest.writeFloat (*i++);
1336 }
1337 else if (isMarker (type, closeSubPathMarker))
1338 {
1339 dest.writeByte ('c');
1340 }
1341 }
1342
1343 dest.writeByte ('e'); // marks the end-of-path
1344}
1345
1347{
1348 MemoryOutputStream s (2048);
1349 if (! useNonZeroWinding)
1350 s << 'a';
1351
1352 float lastMarker = 0.0f;
1353
1354 for (int i = 0; i < data.size();)
1355 {
1356 auto type = data.begin()[i++];
1357 char markerChar = 0;
1358 int numCoords = 0;
1359
1360 if (isMarker (type, moveMarker))
1361 {
1362 markerChar = 'm';
1363 numCoords = 2;
1364 }
1365 else if (isMarker (type, lineMarker))
1366 {
1367 markerChar = 'l';
1368 numCoords = 2;
1369 }
1370 else if (isMarker (type, quadMarker))
1371 {
1372 markerChar = 'q';
1373 numCoords = 4;
1374 }
1375 else if (isMarker (type, cubicMarker))
1376 {
1377 markerChar = 'c';
1378 numCoords = 6;
1379 }
1380 else
1381 {
1383 markerChar = 'z';
1384 }
1385
1386 if (! isMarker (type, lastMarker))
1387 {
1388 if (s.getDataSize() != 0)
1389 s << ' ';
1390
1391 s << markerChar;
1392 lastMarker = type;
1393 }
1394
1395 while (--numCoords >= 0 && i < data.size())
1396 {
1397 String coord (data.begin()[i++], 3);
1398
1399 while (coord.endsWithChar ('0') && coord != "0")
1400 coord = coord.dropLastCharacters (1);
1401
1402 if (coord.endsWithChar ('.'))
1403 coord = coord.dropLastCharacters (1);
1404
1405 if (s.getDataSize() != 0)
1406 s << ' ';
1407
1408 s << coord;
1409 }
1410 }
1411
1412 return s.toUTF8();
1413}
1414
1416{
1417 clear();
1419
1420 auto t = stringVersion.text;
1421 juce_wchar marker = 'm';
1422 int numValues = 2;
1423 float values[6];
1424
1425 for (;;)
1426 {
1427 auto token = PathHelpers::nextToken (t);
1428 auto firstChar = token[0];
1429 int startNum = 0;
1430
1431 if (firstChar == 0)
1432 break;
1433
1434 if (firstChar == 'm' || firstChar == 'l')
1435 {
1436 marker = firstChar;
1437 numValues = 2;
1438 }
1439 else if (firstChar == 'q')
1440 {
1441 marker = firstChar;
1442 numValues = 4;
1443 }
1444 else if (firstChar == 'c')
1445 {
1446 marker = firstChar;
1447 numValues = 6;
1448 }
1449 else if (firstChar == 'z')
1450 {
1451 marker = firstChar;
1452 numValues = 0;
1453 }
1454 else if (firstChar == 'a')
1455 {
1456 setUsingNonZeroWinding (false);
1457 continue;
1458 }
1459 else
1460 {
1461 ++startNum;
1462 values [0] = token.getFloatValue();
1463 }
1464
1465 for (int i = startNum; i < numValues; ++i)
1466 values [i] = PathHelpers::nextToken (t).getFloatValue();
1467
1468 switch (marker)
1469 {
1470 case 'm': startNewSubPath (values[0], values[1]); break;
1471 case 'l': lineTo (values[0], values[1]); break;
1472 case 'q': quadraticTo (values[0], values[1], values[2], values[3]); break;
1473 case 'c': cubicTo (values[0], values[1], values[2], values[3], values[4], values[5]); break;
1474 case 'z': closeSubPath(); break;
1475 default: jassertfalse; break; // illegal string format?
1476 }
1477 }
1478}
1479
1480//==============================================================================
1482 : elementType (startNewSubPath), path (p), index (path.data.begin())
1483{
1484}
1485
1489
1491{
1492 if (index != path.data.end())
1493 {
1494 auto type = *index++;
1495
1496 if (isMarker (type, moveMarker))
1497 {
1499 x1 = *index++;
1500 y1 = *index++;
1501 }
1502 else if (isMarker (type, lineMarker))
1503 {
1505 x1 = *index++;
1506 y1 = *index++;
1507 }
1508 else if (isMarker (type, quadMarker))
1509 {
1511 x1 = *index++;
1512 y1 = *index++;
1513 x2 = *index++;
1514 y2 = *index++;
1515 }
1516 else if (isMarker (type, cubicMarker))
1517 {
1519 x1 = *index++;
1520 y1 = *index++;
1521 x2 = *index++;
1522 y2 = *index++;
1523 x3 = *index++;
1524 y3 = *index++;
1525 }
1526 else if (isMarker (type, closeSubPathMarker))
1527 {
1529 }
1530
1531 return true;
1532 }
1533
1534 return false;
1535}
1536
1537#undef JUCE_CHECK_COORDS_ARE_VALID
1538
1539} // namespace juce
Type jmin(const Type a, const Type b)
Definition MathsFunctions.h:60
Type jmax(const Type a, const Type b)
Definition MathsFunctions.h:48
#define noexcept
Definition DistrhoDefines.h:72
CAdPlugDatabase::CRecord::RecordType type
Definition adplugdb.cpp:93
Definition juce_AffineTransform.h:43
void transformPoint(ValueType &x, ValueType &y) const noexcept
Definition juce_AffineTransform.h:75
static AffineTransform translation(float deltaX, float deltaY) noexcept
Definition juce_AffineTransform.cpp:81
void transformPoints(ValueType &x1, ValueType &y1, ValueType &x2, ValueType &y2) const noexcept
Definition juce_AffineTransform.h:88
static AffineTransform rotation(float angleInRadians) noexcept
Definition juce_AffineTransform.cpp:106
int size() const noexcept
Definition juce_Array.h:215
ElementType * begin() noexcept
Definition juce_Array.h:328
Definition juce_InputStream.h:37
virtual float readFloat()
Definition juce_InputStream.cpp:153
virtual bool isExhausted()=0
virtual char readByte()
Definition juce_InputStream.cpp:56
Definition juce_Justification.h:41
@ left
Definition juce_Justification.h:108
@ bottom
Definition juce_Justification.h:122
@ top
Definition juce_Justification.h:119
@ right
Definition juce_Justification.h:111
bool testFlags(int flagsToTest) const noexcept
Definition juce_Justification.h:63
Definition juce_Line.h:47
Line reversed() const noexcept
Definition juce_Line.h:106
Point< ValueType > getPointAlongLine(ValueType distanceFromStart) const noexcept
Definition juce_Line.h:204
ValueType getLength() const noexcept
Definition juce_Line.h:117
ValueType getDistanceFromPoint(Point< ValueType > targetPoint, Point< ValueType > &pointOnLine) const noexcept
Definition juce_Line.h:262
Point< ValueType > getEnd() const noexcept
Definition juce_Line.h:91
bool intersects(Line line, Point< ValueType > &intersection) const noexcept
Definition juce_Line.h:184
Point< ValueType > getStart() const noexcept
Definition juce_Line.h:88
Definition juce_MemoryInputStream.h:36
Definition juce_MemoryOutputStream.h:36
Definition juce_OutputStream.h:38
virtual bool writeFloat(float value)
Definition juce_OutputStream.cpp:155
virtual bool writeByte(char byte)
Definition juce_OutputStream.cpp:83
const Path & path
Definition juce_Path.h:756
Iterator(const Path &path) noexcept
Definition juce_Path.cpp:1481
const float * index
Definition juce_Path.h:757
float y1
Definition juce_Path.h:752
float x1
Definition juce_Path.h:752
float y3
Definition juce_Path.h:752
float x2
Definition juce_Path.h:752
PathElementType elementType
Definition juce_Path.h:750
~Iterator() noexcept
Definition juce_Path.cpp:1486
bool next() noexcept
Definition juce_Path.cpp:1490
float x3
Definition juce_Path.h:752
float y2
Definition juce_Path.h:752
@ quadraticTo
Definition juce_Path.h:745
@ closePath
Definition juce_Path.h:747
@ lineTo
Definition juce_Path.h:744
@ cubicTo
Definition juce_Path.h:746
@ startNewSubPath
Definition juce_Path.h:743
float getNearestPoint(Point< float > targetPoint, Point< float > &pointOnPath, const AffineTransform &transform=AffineTransform(), float tolerance=defaultToleranceForMeasurement) const
Definition juce_Path.cpp:1047
float getLength(const AffineTransform &transform=AffineTransform(), float tolerance=defaultToleranceForMeasurement) const
Definition juce_Path.cpp:1016
void addBubble(Rectangle< float > bodyArea, Rectangle< float > maximumArea, const Point< float > arrowTipPosition, const float cornerSize, const float arrowBaseWidth)
Definition juce_Path.cpp:661
static const float cubicMarker
Definition juce_Path.h:833
bool contains(float x, float y, float tolerance=defaultToleranceForTesting) const
Definition juce_Path.cpp:937
void loadPathFromData(const void *data, size_t numberOfBytes)
Definition juce_Path.cpp:1293
void addRoundedRectangle(float x, float y, float width, float height, float cornerSize)
Definition juce_Path.cpp:412
void startNewSubPath(float startX, float startY)
Definition juce_Path.cpp:216
~Path()
Definition juce_Path.cpp:110
void addPieSegment(float x, float y, float width, float height, float fromRadians, float toRadians, float innerCircleProportionalSize)
Definition juce_Path.cpp:525
void addTriangle(float x1, float y1, float x2, float y2, float x3, float y3)
Definition juce_Path.cpp:417
static const float closeSubPathMarker
Definition juce_Path.h:834
void addEllipse(float x, float y, float width, float height)
Definition juce_Path.cpp:446
void preallocateSpace(int numExtraCoordsToMakeSpaceFor)
Definition juce_Path.cpp:211
Point< float > getCurrentPosition() const
Definition juce_Path.cpp:298
Path()
Definition juce_Path.cpp:106
void addPath(const Path &pathToAppend)
Definition juce_Path.cpp:726
void quadraticTo(float controlPointX, float controlPointY, float endPointX, float endPointY)
Definition juce_Path.cpp:249
friend class PathFlatteningIterator
Definition juce_Path.h:803
static const float quadMarker
Definition juce_Path.h:832
AffineTransform getTransformToScaleToFit(float x, float y, float width, float height, bool preserveProportions, Justification justificationType=Justification::centred) const
Definition juce_Path.cpp:885
void cubicTo(float controlPoint1X, float controlPoint1Y, float controlPoint2X, float controlPoint2Y, float endPointX, float endPointY)
Definition juce_Path.cpp:268
PathBounds bounds
Definition juce_Path.h:827
Rectangle< float > getBoundsTransformed(const AffineTransform &transform) const noexcept
Definition juce_Path.cpp:205
bool intersectsLine(Line< float > line, float tolerance=defaultToleranceForTesting) const
Definition juce_Path.cpp:973
void addRectangle(float x, float y, float width, float height)
Definition juce_Path.cpp:323
void scaleToFit(float x, float y, float width, float height, bool preserveProportions) noexcept
Definition juce_Path.cpp:173
void clear() noexcept
Definition juce_Path.cpp:151
void applyTransform(const AffineTransform &transform) noexcept
Definition juce_Path.cpp:821
void addCentredArc(float centreX, float centreY, float radiusX, float radiusY, float rotationOfEllipse, float fromRadians, float toRadians, bool startAsNewSubPath=false)
Definition juce_Path.cpp:483
static const float lineMarker
Definition juce_Path.h:830
void restoreFromString(StringRef stringVersion)
Definition juce_Path.cpp:1415
bool useNonZeroWinding
Definition juce_Path.h:828
void closeSubPath()
Definition juce_Path.cpp:292
void writePathToStream(OutputStream &destination) const
Definition juce_Path.cpp:1299
Line< float > getClippedLine(Line< float > line, bool keepSectionOutsidePath) const
Definition juce_Path.cpp:985
Rectangle< float > getBounds() const noexcept
Definition juce_Path.cpp:200
String toString() const
Definition juce_Path.cpp:1346
Point< float > getPointAlongPath(float distanceFromStart, const AffineTransform &transform=AffineTransform(), float tolerance=defaultToleranceForMeasurement) const
Definition juce_Path.cpp:1027
void lineTo(float endX, float endY)
Definition juce_Path.cpp:233
Path createPathWithRoundedCorners(float cornerRadius) const
Definition juce_Path.cpp:1075
static const float moveMarker
Definition juce_Path.h:831
void addArc(float x, float y, float width, float height, float fromRadians, float toRadians, bool startAsNewSubPath=false)
Definition juce_Path.cpp:468
void addLineSegment(Line< float > line, float lineThickness)
Definition juce_Path.cpp:581
void addStar(Point< float > centre, int numberOfPoints, float innerRadius, float outerRadius, float startAngle=0.0f)
Definition juce_Path.cpp:635
void swapWithPath(Path &) noexcept
Definition juce_Path.cpp:157
void loadPathFromStream(InputStream &source)
Definition juce_Path.cpp:1227
void addArrow(Line< float > line, float lineThickness, float arrowheadWidth, float arrowheadLength)
Definition juce_Path.cpp:593
void setUsingNonZeroWinding(bool isNonZeroWinding) noexcept
Definition juce_Path.cpp:168
Array< float > data
Definition juce_Path.h:807
bool isEmpty() const noexcept
Definition juce_Path.cpp:179
void addQuadrilateral(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4)
Definition juce_Path.cpp:434
void addPolygon(Point< float > centre, int numberOfSides, float radius, float startAngle=0.0f)
Definition juce_Path.cpp:611
Definition juce_Point.h:42
ValueType getDistanceFrom(Point other) const noexcept
Definition juce_Point.h:160
ValueType y
Definition juce_Point.h:247
ValueType x
Definition juce_Point.h:246
Point< FloatType > getPointOnCircumference(float radius, float angle) const noexcept
Definition juce_Point.h:194
Definition juce_Rectangle.h:67
ValueType getRight() const noexcept
Definition juce_Rectangle.h:139
bool contains(ValueType xCoord, ValueType yCoord) const noexcept
Definition juce_Rectangle.h:622
ValueType getHeight() const noexcept
Definition juce_Rectangle.h:136
ValueType getBottom() const noexcept
Definition juce_Rectangle.h:142
ValueType getX() const noexcept
Definition juce_Rectangle.h:127
ValueType getWidth() const noexcept
Definition juce_Rectangle.h:133
Rectangle reduced(ValueType deltaX, ValueType deltaY) const noexcept
Definition juce_Rectangle.h:485
ValueType getY() const noexcept
Definition juce_Rectangle.h:130
Definition juce_String.h:53
bool endsWithChar(juce_wchar character) const noexcept
Definition juce_String.cpp:1410
String dropLastCharacters(int numberToDrop) const
Definition juce_String.cpp:1555
Definition juce_StringRef.h:62
String::CharPointerType text
Definition juce_StringRef.h:130
* e
Definition inflate.c:1404
UINT_D64 w
Definition inflate.c:942
struct huft * t
Definition inflate.c:943
int y
Definition inflate.c:1588
unsigned d
Definition inflate.c:940
register unsigned i
Definition inflate.c:1575
unsigned s
Definition inflate.c:1555
unsigned x[BMAX+1]
Definition inflate.c:1586
static PuglViewHint int value
Definition pugl.h:1708
static int int height
Definition pugl.h:1594
static int width
Definition pugl.h:1593
virtual ASIOError start()=0
int marker
Definition jpeglib.h:950
JSAMPIMAGE data
Definition jpeglib.h:945
#define JUCE_CHECK_COORDS_ARE_VALID(x, y)
Definition juce_Path.cpp:30
#define jassert(expression)
#define jassertfalse
float in
Definition lilv_test.c:1460
Definition juce_Path.cpp:35
static String nextToken(String::CharPointerType &t)
Definition juce_Path.cpp:38
double lengthOf(float x1, float y1, float x2, float y2) noexcept
Definition juce_Path.cpp:54
const float ellipseAngularIncrement
Definition juce_Path.cpp:36
Definition carla_juce.cpp:31
constexpr Type jmin(Type a, Type b)
Definition juce_MathsFunctions.h:106
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Definition juce_RangedDirectoryIterator.h:184
static bool isMarker(float value, float marker) noexcept
Definition juce_Path.cpp:70
wchar_t juce_wchar
Definition juce_CharacterFunctions.h:42
Type juce_hypot(Type a, Type b) noexcept
Definition juce_MathsFunctions.h:352
png_uint_32 length
Definition png.c:2247
static constexpr FloatType halfPi
Definition juce_MathsFunctions.h:388
static constexpr FloatType twoPi
Definition juce_MathsFunctions.h:385
static constexpr FloatType pi
Definition juce_MathsFunctions.h:382
void reset() noexcept
Definition juce_Path.cpp:85
PathBounds() noexcept
Definition juce_Path.cpp:76
float pathYMin
Definition juce_Path.h:824
Rectangle< float > getRectangle() const noexcept
Definition juce_Path.cpp:80
void extend(float, float) noexcept
Definition juce_Path.cpp:96
float pathXMin
Definition juce_Path.h:824
float pathYMax
Definition juce_Path.h:824
float pathXMax
Definition juce_Path.h:824
int n
Definition crypt.c:458
uch * p
Definition crypt.c:594
uch h[RAND_HEAD_LEN]
Definition crypt.c:459
uch hh[RAND_HEAD_LEN]
Definition crypt.c:595
int result
Definition process.c:1455
#define const
Definition zconf.h:137