30#define JUCE_CHECK_COORDS_ARE_VALID(x, y) \
31 jassert (! std::isnan (x) && ! std::isnan (y));
36 const float ellipseAngularIncrement = 0.05f;
40 t.incrementToEndOfWhitespace();
45 while (! (t.isEmpty() || t.isWhitespace()))
51 return { start, numChars };
54 inline double lengthOf (
float x1,
float y1,
float x2,
float y2)
noexcept
56 return juce_hypot ((
double) (x1 - x2), (
double) (y1 - y2));
62const float Path::defaultToleranceForTesting = 1.0f;
63const float Path::defaultToleranceForMeasurement = 0.6f;
65static bool isMarker (
float value,
float marker)
noexcept
71Path::PathBounds::PathBounds() noexcept
75Rectangle<float> Path::PathBounds::getRectangle()
const noexcept
77 return { pathXMin, pathYMin, pathXMax - pathXMin, pathYMax - pathYMin };
80void Path::PathBounds::reset() noexcept
82 pathXMin = pathYMin = pathYMax = pathXMax = 0;
85void Path::PathBounds::reset (
float x,
float y)
noexcept
87 pathXMin = pathXMax = x;
88 pathYMin = pathYMax = y;
91void Path::PathBounds::extend (
float x,
float y)
noexcept
93 if (x < pathXMin) pathXMin = x;
94 else if (x > pathXMax) pathXMax = x;
96 if (y < pathYMin) pathYMin = y;
97 else if (y > pathYMax) pathYMax = y;
111 bounds (
other.bounds),
112 useNonZeroWinding (
other.useNonZeroWinding)
121 bounds =
other.bounds;
122 useNonZeroWinding =
other.useNonZeroWinding;
129 : data (std::move (
other.data)),
130 bounds (
other.bounds),
131 useNonZeroWinding (
other.useNonZeroWinding)
137 data = std::move (
other.data);
138 bounds =
other.bounds;
139 useNonZeroWinding =
other.useNonZeroWinding;
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); }
154 data.swapWith (
other.data);
165 useNonZeroWinding = isNonZero;
168void Path::scaleToFit (
float x,
float y,
float w,
float h,
bool preserveProportions)
noexcept
170 applyTransform (getTransformToScaleToFit (x, y, w, h, preserveProportions));
176 for (
auto i = data.
begin(), e = data.
end(); i != e; ++i)
180 if (isMarker (type, moveMarker))
184 else if (isMarker (type, lineMarker)
185 || isMarker (type, quadMarker)
186 || isMarker (type, cubicMarker))
197 return bounds.getRectangle();
213 JUCE_CHECK_COORDS_ARE_VALID (x, y)
218 bounds.extend (x, y);
220 data.
add (moveMarker, x, y);
230 JUCE_CHECK_COORDS_ARE_VALID (x, y)
235 data.
add (lineMarker, x, y);
236 bounds.extend (x, y);
245 const float x2,
const float y2)
247 JUCE_CHECK_COORDS_ARE_VALID (x1,
y1)
248 JUCE_CHECK_COORDS_ARE_VALID (x2, y2)
253 data.
add (quadMarker, x1,
y1, x2, y2);
254 bounds.extend (x1,
y1, x2, y2);
260 endPoint.
x, endPoint.
y);
264 const float x2,
const float y2,
265 const float x3,
const float y3)
267 JUCE_CHECK_COORDS_ARE_VALID (x1,
y1)
268 JUCE_CHECK_COORDS_ARE_VALID (x2, y2)
269 JUCE_CHECK_COORDS_ARE_VALID (x3, y3)
274 data.
add (cubicMarker, x1,
y1, x2, y2, x3, y3);
275 bounds.extend (x1,
y1, x2, y2, x3, y3);
284 endPoint.
x, endPoint.
y);
289 if (! (data.
isEmpty() || isMarker (data.
getLast(), closeSubPathMarker)))
290 data.
add (closeSubPathMarker);
298 auto* i = data.
end() - 1;
300 if (isMarker (*i, closeSubPathMarker))
302 while (i != data.
begin())
304 if (isMarker (*--i, moveMarker))
312 if (i != data.
begin())
313 return { *(i - 1), *i };
320 auto x1 = x,
y1 = y, x2 = x + w, y2 = y + h;
327 bounds.pathXMin = x1;
328 bounds.pathXMax = x2;
329 bounds.pathYMin =
y1;
330 bounds.pathYMax = y2;
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);
340 data.
add (moveMarker, x1, y2,
496 angle += PathHelpers::ellipseAngularIncrement;
501 angle += PathHelpers::ellipseAngularIncrement;
507 angle -= PathHelpers::ellipseAngularIncrement;
512 angle -= PathHelpers::ellipseAngularIncrement;
723 const auto* d =
other.data.begin();
724 const auto size =
other.data.size();
726 for (
int i = 0; i < size;)
728 const auto type = d[i++];
730 if (isMarker (type, moveMarker))
735 else if (isMarker (type, lineMarker))
740 else if (isMarker (type, quadMarker))
745 else if (isMarker (type, cubicMarker))
747 cubicTo (d[i], d[i + 1], d[i + 2], d[i + 3], d[i + 4], d[i + 5]);
750 else if (isMarker (type, closeSubPathMarker))
765 const auto* d =
other.data.begin();
766 const auto size =
other.data.size();
768 for (
int i = 0; i < size;)
770 const auto type = d[i++];
772 if (isMarker (type, closeSubPathMarker))
782 if (isMarker (type, moveMarker))
786 else if (isMarker (type, lineMarker))
790 else if (isMarker (type, quadMarker))
798 else if (isMarker (type, cubicMarker))
806 cubicTo (x, y, x2, y2, x3, y3);
822 float* d = data.begin();
823 auto*
end = data.end();
829 if (isMarker (type, moveMarker))
831 transform.transformPoint (d[0], d[1]);
832 JUCE_CHECK_COORDS_ARE_VALID (d[0], d[1])
837 bounds.reset (d[0], d[1]);
841 bounds.extend (d[0], d[1]);
846 else if (isMarker (type, lineMarker))
848 transform.transformPoint (d[0], d[1]);
849 JUCE_CHECK_COORDS_ARE_VALID (d[0], d[1])
850 bounds.extend (d[0], d[1]);
853 else if (isMarker (type, quadMarker))
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]);
861 else if (isMarker (type, cubicMarker))
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]);
879 preserveProportions, justification);
883 bool preserveProportions,
888 if (preserveProportions)
947 if ((i.
y1 <= y && i.
y2 > y) || (i.
y2 <= y && i.
y1 > y))
1041 return { i.
x2, i.
y2 };
1079 auto* elements = data.
begin();
1082 while (n < data.
size())
1084 auto type = elements[n++];
1086 if (isMarker (type, moveMarker))
1090 auto x = elements[n++];
1091 auto y = elements[n++];
1096 else if (isMarker (type, lineMarker) || isMarker (type, closeSubPathMarker))
1100 if (isMarker (type, lineMarker))
1102 endX = elements[n++];
1103 endY = elements[n++];
1107 startX = elements[n - 8];
1108 startY = elements[n - 7];
1109 joinX = elements[n - 5];
1110 joinY = elements[n - 4];
1120 startX = elements[n - 6];
1121 startY = elements[n - 5];
1122 joinX = elements[n - 3];
1123 joinY = elements[n - 2];
1152 else if (isMarker (type, lineMarker))
1158 if (isMarker (type, closeSubPathMarker))
1162 startX = elements[n - 3];
1163 startY = elements[n - 2];
1198 else if (isMarker (type, quadMarker))
1201 auto x1 = elements[n++];
1202 auto y1 = elements[n++];
1203 auto x2 = elements[n++];
1204 auto y2 = elements[n++];
1207 else if (isMarker (type, cubicMarker))
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++];
1273 useNonZeroWinding =
true;
1277 useNonZeroWinding =
false;
1298 dest.
writeByte (useNonZeroWinding ?
'n' :
'z');
1300 for (
auto* i = data.
begin(); i != data.
end();)
1304 if (isMarker (type, moveMarker))
1310 else if (isMarker (type, lineMarker))
1316 else if (isMarker (type, quadMarker))
1324 else if (isMarker (type, cubicMarker))
1334 else if (isMarker (type, closeSubPathMarker))
1346 if (! useNonZeroWinding)
1351 for (
int i = 0; i < data.
size();)
1353 auto type = data.
begin()[i++];
1357 if (isMarker (type, moveMarker))
1362 else if (isMarker (type, lineMarker))
1367 else if (isMarker (type, quadMarker))
1372 else if (isMarker (type, cubicMarker))
1379 jassert (isMarker (type, closeSubPathMarker));
1396 while (
coord.endsWithChar (
'0') &&
coord !=
"0")
1399 if (
coord.endsWithChar (
'.'))
1424 auto token = PathHelpers::nextToken (t);
1459 values [0] =
token.getFloatValue();
1463 values [i] = PathHelpers::nextToken (t).getFloatValue();
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;
1478Path::Iterator::Iterator (
const Path& p) noexcept
1479 : elementType (startNewSubPath), path (p), index (path.data.begin())
1483Path::Iterator::~Iterator() noexcept
1489 if (index != path.data.end())
1491 auto type = *index++;
1493 if (isMarker (type, moveMarker))
1499 else if (isMarker (type, lineMarker))
1505 else if (isMarker (type, quadMarker))
1513 else if (isMarker (type, cubicMarker))
1523 else if (isMarker (type, closeSubPathMarker))
1525 elementType = closePath;
1534#undef JUCE_CHECK_COORDS_ARE_VALID
bool isEmpty() const noexcept
Returns true if the array is empty, false otherwise.
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.
int size() const noexcept
Returns the current number of elements in the array.
ElementType * begin() noexcept
Returns a pointer to the first element in the array.
ElementType * end() noexcept
Returns a pointer to the element which follows the last element in the array.
void add(const ElementType &newElement)
Appends a new element at the end of the array.
ElementType * data() noexcept
Returns a pointer to the first element in the array.
ElementType getLast() const noexcept
Returns the last element in the array, or a default value if the array is empty.
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.
Line reversed() const noexcept
Returns a line that is the same as this one, but with the start and end reversed,.
void setStart(ValueType newStartX, ValueType newStartY) noexcept
Changes this line's start point.
Point< ValueType > getPointAlongLine(ValueType distanceFromStart) const noexcept
Returns the location of the point which is a given distance along this line.
ValueType getLength() const noexcept
Returns the length of the line.
ValueType getDistanceFromPoint(Point< ValueType > targetPoint, Point< ValueType > &pointOnLine) const noexcept
Returns the smallest distance between this line segment and a given point.
void setEnd(ValueType newEndX, ValueType newEndY) noexcept
Changes this line's end point.
Point< ValueType > getEnd() const noexcept
Returns the line's end point.
bool intersects(Line line, Point< ValueType > &intersection) const noexcept
Finds the intersection between two lines.
Point< ValueType > getStart() const noexcept
Returns the line's start point.
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.
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.
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.
ValueType y
The point's Y coordinate.
ValueType x
The point's X coordinate.
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.
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.
CharPointer_UTF8 CharPointerType
This is the character encoding type used internally to store the string.
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...
Commonly used mathematical constants.