423 lines
18 KiB
C++
423 lines
18 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library.
|
|
Copyright (c) 2017 - ROLI Ltd.
|
|
|
|
JUCE is an open source library subject to commercial or open-source
|
|
licensing.
|
|
|
|
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
|
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
|
27th April 2017).
|
|
|
|
End User License Agreement: www.juce.com/juce-5-licence
|
|
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
|
|
|
Or: You may also use this code under the terms of the GPL v3 (see
|
|
www.gnu.org/licenses).
|
|
|
|
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
|
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
|
DISCLAIMED.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
namespace juce
|
|
{
|
|
|
|
//==============================================================================
|
|
/**
|
|
Represents a line.
|
|
|
|
This class contains a bunch of useful methods for various geometric
|
|
tasks.
|
|
|
|
The ValueType template parameter should be a primitive type - float or double
|
|
are what it's designed for. Integer types will work in a basic way, but some methods
|
|
that perform mathematical operations may not compile, or they may not produce
|
|
sensible results.
|
|
|
|
@see Point, Rectangle, Path, Graphics::drawLine
|
|
|
|
@tags{Graphics}
|
|
*/
|
|
template <typename ValueType>
|
|
class Line
|
|
{
|
|
public:
|
|
//==============================================================================
|
|
/** Creates a line, using (0, 0) as its start and end points. */
|
|
Line() = default;
|
|
|
|
/** Creates a copy of another line. */
|
|
Line (const Line&) = default;
|
|
|
|
/** Creates a line based on the coordinates of its start and end points. */
|
|
Line (ValueType startX, ValueType startY, ValueType endX, ValueType endY) noexcept
|
|
: start (startX, startY), end (endX, endY)
|
|
{
|
|
}
|
|
|
|
/** Creates a line from its start and end points. */
|
|
Line (Point<ValueType> startPoint, Point<ValueType> endPoint) noexcept
|
|
: start (startPoint), end (endPoint)
|
|
{
|
|
}
|
|
|
|
/** Copies a line from another one. */
|
|
Line& operator= (const Line&) = default;
|
|
|
|
/** Destructor. */
|
|
~Line() = default;
|
|
|
|
//==============================================================================
|
|
/** Returns the x coordinate of the line's start point. */
|
|
inline ValueType getStartX() const noexcept { return start.x; }
|
|
|
|
/** Returns the y coordinate of the line's start point. */
|
|
inline ValueType getStartY() const noexcept { return start.y; }
|
|
|
|
/** Returns the x coordinate of the line's end point. */
|
|
inline ValueType getEndX() const noexcept { return end.x; }
|
|
|
|
/** Returns the y coordinate of the line's end point. */
|
|
inline ValueType getEndY() const noexcept { return end.y; }
|
|
|
|
/** Returns the line's start point. */
|
|
inline Point<ValueType> getStart() const noexcept { return start; }
|
|
|
|
/** Returns the line's end point. */
|
|
inline Point<ValueType> getEnd() const noexcept { return end; }
|
|
|
|
/** Changes this line's start point */
|
|
void setStart (ValueType newStartX, ValueType newStartY) noexcept { start.setXY (newStartX, newStartY); }
|
|
|
|
/** Changes this line's end point */
|
|
void setEnd (ValueType newEndX, ValueType newEndY) noexcept { end.setXY (newEndX, newEndY); }
|
|
|
|
/** Changes this line's start point */
|
|
void setStart (const Point<ValueType> newStart) noexcept { start = newStart; }
|
|
|
|
/** Changes this line's end point */
|
|
void setEnd (const Point<ValueType> newEnd) noexcept { end = newEnd; }
|
|
|
|
/** Returns a line that is the same as this one, but with the start and end reversed, */
|
|
Line reversed() const noexcept { return { end, start }; }
|
|
|
|
/** Applies an affine transform to the line's start and end points. */
|
|
void applyTransform (const AffineTransform& transform) noexcept
|
|
{
|
|
start.applyTransform (transform);
|
|
end.applyTransform (transform);
|
|
}
|
|
|
|
//==============================================================================
|
|
/** Returns the length of the line. */
|
|
ValueType getLength() const noexcept { return start.getDistanceFrom (end); }
|
|
|
|
/** Returns the length of the line. */
|
|
ValueType getLengthSquared() const noexcept { return start.getDistanceSquaredFrom (end); }
|
|
|
|
/** Returns true if the line's start and end x coordinates are the same. */
|
|
bool isVertical() const noexcept { return start.x == end.x; }
|
|
|
|
/** Returns true if the line's start and end y coordinates are the same. */
|
|
bool isHorizontal() const noexcept { return start.y == end.y; }
|
|
|
|
/** Returns the line's angle.
|
|
|
|
This value is the number of radians clockwise from the 12 o'clock direction,
|
|
where the line's start point is considered to be at the centre.
|
|
*/
|
|
typename Point<ValueType>::FloatType getAngle() const noexcept { return start.getAngleToPoint (end); }
|
|
|
|
/** Creates a line from a start point, length and angle.
|
|
|
|
This angle is the number of radians clockwise from the 12 o'clock direction,
|
|
where the line's start point is considered to be at the centre.
|
|
*/
|
|
static Line fromStartAndAngle (Point<ValueType> startPoint, ValueType length, ValueType angle) noexcept
|
|
{
|
|
return { startPoint, startPoint.getPointOnCircumference (length, angle) };
|
|
}
|
|
|
|
//==============================================================================
|
|
/** Casts this line to float coordinates. */
|
|
Line<float> toFloat() const noexcept { return { start.toFloat(), end.toFloat() }; }
|
|
|
|
/** Casts this line to double coordinates. */
|
|
Line<double> toDouble() const noexcept { return { start.toDouble(), end.toDouble() }; }
|
|
|
|
//==============================================================================
|
|
/** Compares two lines. */
|
|
bool operator== (Line other) const noexcept { return start == other.start && end == other.end; }
|
|
|
|
/** Compares two lines. */
|
|
bool operator!= (Line other) const noexcept { return start != other.start || end != other.end; }
|
|
|
|
//==============================================================================
|
|
/** Finds the intersection between two lines.
|
|
|
|
@param line the line to intersect with
|
|
@returns the point at which the lines intersect, even if this lies beyond the end of the lines
|
|
*/
|
|
Point<ValueType> getIntersection (Line line) const noexcept
|
|
{
|
|
Point<ValueType> p;
|
|
findIntersection (start, end, line.start, line.end, p);
|
|
return p;
|
|
}
|
|
|
|
/** Finds the intersection between two lines.
|
|
|
|
@param line the other line
|
|
@param intersection the position of the point where the lines meet (or
|
|
where they would meet if they were infinitely long)
|
|
the intersection (if the lines intersect). If the lines
|
|
are parallel, this will just be set to the position
|
|
of one of the line's endpoints.
|
|
@returns true if the line segments intersect; false if they don't. Even if they
|
|
don't intersect, the intersection coordinates returned will still
|
|
be valid
|
|
*/
|
|
bool intersects (Line line, Point<ValueType>& intersection) const noexcept
|
|
{
|
|
return findIntersection (start, end, line.start, line.end, intersection);
|
|
}
|
|
|
|
/** Returns true if this line intersects another. */
|
|
bool intersects (Line other) const noexcept
|
|
{
|
|
Point<ValueType> ignored;
|
|
return findIntersection (start, end, other.start, other.end, ignored);
|
|
}
|
|
|
|
//==============================================================================
|
|
/** Returns the location of the point which is a given distance along this line.
|
|
|
|
@param distanceFromStart the distance to move along the line from its
|
|
start point. This value can be negative or longer
|
|
than the line itself
|
|
@see getPointAlongLineProportionally
|
|
*/
|
|
Point<ValueType> getPointAlongLine (ValueType distanceFromStart) const noexcept
|
|
{
|
|
return start + (end - start) * (distanceFromStart / getLength());
|
|
}
|
|
|
|
/** Returns a point which is a certain distance along and to the side of this line.
|
|
|
|
This effectively moves a given distance along the line, then another distance
|
|
perpendicularly to this, and returns the resulting position.
|
|
|
|
@param distanceFromStart the distance to move along the line from its
|
|
start point. This value can be negative or longer
|
|
than the line itself
|
|
@param perpendicularDistance how far to move sideways from the line. If you're
|
|
looking along the line from its start towards its
|
|
end, then a positive value here will move to the
|
|
right, negative value move to the left.
|
|
*/
|
|
Point<ValueType> getPointAlongLine (ValueType distanceFromStart,
|
|
ValueType perpendicularDistance) const noexcept
|
|
{
|
|
auto delta = end - start;
|
|
auto length = juce_hypot ((double) delta.x,
|
|
(double) delta.y);
|
|
if (length <= 0)
|
|
return start;
|
|
|
|
return { start.x + static_cast<ValueType> ((delta.x * distanceFromStart - delta.y * perpendicularDistance) / length),
|
|
start.y + static_cast<ValueType> ((delta.y * distanceFromStart + delta.x * perpendicularDistance) / length) };
|
|
}
|
|
|
|
/** Returns the location of the point which is a given distance along this line
|
|
proportional to the line's length.
|
|
|
|
@param proportionOfLength the distance to move along the line from its
|
|
start point, in multiples of the line's length.
|
|
So a value of 0.0 will return the line's start point
|
|
and a value of 1.0 will return its end point. (This value
|
|
can be negative or greater than 1.0).
|
|
@see getPointAlongLine
|
|
*/
|
|
Point<ValueType> getPointAlongLineProportionally (typename Point<ValueType>::FloatType proportionOfLength) const noexcept
|
|
{
|
|
return start + (end - start) * proportionOfLength;
|
|
}
|
|
|
|
/** Returns the smallest distance between this line segment and a given point.
|
|
|
|
So if the point is close to the line, this will return the perpendicular
|
|
distance from the line; if the point is a long way beyond one of the line's
|
|
end-point's, it'll return the straight-line distance to the nearest end-point.
|
|
|
|
pointOnLine receives the position of the point that is found.
|
|
|
|
@returns the point's distance from the line
|
|
@see getPositionAlongLineOfNearestPoint
|
|
*/
|
|
ValueType getDistanceFromPoint (Point<ValueType> targetPoint,
|
|
Point<ValueType>& pointOnLine) const noexcept
|
|
{
|
|
auto delta = end - start;
|
|
auto length = delta.x * delta.x + delta.y * delta.y;
|
|
|
|
if (length > 0)
|
|
{
|
|
auto prop = ((targetPoint.x - start.x) * delta.x
|
|
+ (targetPoint.y - start.y) * delta.y) / (double) length;
|
|
|
|
if (prop >= 0 && prop <= 1.0)
|
|
{
|
|
pointOnLine = start + delta * prop;
|
|
return targetPoint.getDistanceFrom (pointOnLine);
|
|
}
|
|
}
|
|
|
|
auto fromStart = targetPoint.getDistanceFrom (start);
|
|
auto fromEnd = targetPoint.getDistanceFrom (end);
|
|
|
|
if (fromStart < fromEnd)
|
|
{
|
|
pointOnLine = start;
|
|
return fromStart;
|
|
}
|
|
|
|
pointOnLine = end;
|
|
return fromEnd;
|
|
}
|
|
|
|
/** Finds the point on this line which is nearest to a given point, and
|
|
returns its position as a proportional position along the line.
|
|
|
|
@returns a value 0 to 1.0 which is the distance along this line from the
|
|
line's start to the point which is nearest to the point passed-in. To
|
|
turn this number into a position, use getPointAlongLineProportionally().
|
|
@see getDistanceFromPoint, getPointAlongLineProportionally
|
|
*/
|
|
ValueType findNearestProportionalPositionTo (Point<ValueType> point) const noexcept
|
|
{
|
|
auto delta = end - start;
|
|
auto length = delta.x * delta.x + delta.y * delta.y;
|
|
|
|
return length <= 0 ? 0
|
|
: jlimit (ValueType(), static_cast<ValueType> (1),
|
|
static_cast<ValueType> ((((point.x - start.x) * delta.x
|
|
+ (point.y - start.y) * delta.y) / length)));
|
|
}
|
|
|
|
/** Finds the point on this line which is nearest to a given point.
|
|
@see getDistanceFromPoint, findNearestProportionalPositionTo
|
|
*/
|
|
Point<ValueType> findNearestPointTo (Point<ValueType> point) const noexcept
|
|
{
|
|
return getPointAlongLineProportionally (findNearestProportionalPositionTo (point));
|
|
}
|
|
|
|
/** Returns true if the given point lies above this line.
|
|
|
|
The return value is true if the point's y coordinate is less than the y
|
|
coordinate of this line at the given x (assuming the line extends infinitely
|
|
in both directions).
|
|
*/
|
|
bool isPointAbove (Point<ValueType> point) const noexcept
|
|
{
|
|
return start.x != end.x
|
|
&& point.y < ((end.y - start.y) * (point.x - start.x)) / (end.x - start.x) + start.y;
|
|
}
|
|
|
|
//==============================================================================
|
|
/** Returns a shortened copy of this line.
|
|
|
|
This will chop off part of the start of this line by a certain amount, (leaving the
|
|
end-point the same), and return the new line.
|
|
*/
|
|
Line withShortenedStart (ValueType distanceToShortenBy) const noexcept
|
|
{
|
|
return { getPointAlongLine (jmin (distanceToShortenBy, getLength())), end };
|
|
}
|
|
|
|
/** Returns a shortened copy of this line.
|
|
|
|
This will chop off part of the end of this line by a certain amount, (leaving the
|
|
start-point the same), and return the new line.
|
|
*/
|
|
Line withShortenedEnd (ValueType distanceToShortenBy) const noexcept
|
|
{
|
|
auto length = getLength();
|
|
return { start, getPointAlongLine (length - jmin (distanceToShortenBy, length)) };
|
|
}
|
|
|
|
private:
|
|
//==============================================================================
|
|
Point<ValueType> start, end;
|
|
|
|
static bool isZeroToOne (ValueType v) noexcept { return v >= 0 && v <= static_cast<ValueType> (1); }
|
|
|
|
static bool findIntersection (const Point<ValueType> p1, const Point<ValueType> p2,
|
|
const Point<ValueType> p3, const Point<ValueType> p4,
|
|
Point<ValueType>& intersection) noexcept
|
|
{
|
|
if (p2 == p3)
|
|
{
|
|
intersection = p2;
|
|
return true;
|
|
}
|
|
|
|
auto d1 = p2 - p1;
|
|
auto d2 = p4 - p3;
|
|
auto divisor = d1.x * d2.y - d2.x * d1.y;
|
|
|
|
if (divisor == 0)
|
|
{
|
|
if (! (d1.isOrigin() || d2.isOrigin()))
|
|
{
|
|
if (d1.y == 0 && d2.y != 0)
|
|
{
|
|
auto along = (p1.y - p3.y) / d2.y;
|
|
intersection = p1.withX (p3.x + along * d2.x);
|
|
return isZeroToOne (along);
|
|
}
|
|
|
|
if (d2.y == 0 && d1.y != 0)
|
|
{
|
|
auto along = (p3.y - p1.y) / d1.y;
|
|
intersection = p3.withX (p1.x + along * d1.x);
|
|
return isZeroToOne (along);
|
|
}
|
|
|
|
if (d1.x == 0 && d2.x != 0)
|
|
{
|
|
auto along = (p1.x - p3.x) / d2.x;
|
|
intersection = p1.withY (p3.y + along * d2.y);
|
|
return isZeroToOne (along);
|
|
}
|
|
|
|
if (d2.x == 0 && d1.x != 0)
|
|
{
|
|
auto along = (p3.x - p1.x) / d1.x;
|
|
intersection = p3.withY (p1.y + along * d1.y);
|
|
return isZeroToOne (along);
|
|
}
|
|
}
|
|
|
|
intersection = (p2 + p3) / static_cast<ValueType> (2);
|
|
return false;
|
|
}
|
|
|
|
auto along1 = ((p1.y - p3.y) * d2.x - (p1.x - p3.x) * d2.y) / divisor;
|
|
intersection = p1 + d1 * along1;
|
|
|
|
if (! isZeroToOne (along1))
|
|
return false;
|
|
|
|
auto along2 = ((p1.y - p3.y) * d1.x - (p1.x - p3.x) * d1.y) / divisor;
|
|
return isZeroToOne (along2);
|
|
}
|
|
};
|
|
|
|
} // namespace juce
|