diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index deb6be0a84..3de54bf052 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -85,8 +85,6 @@ #if defined (__ppc__) || defined (__ppc64__) #define JUCE_PPC 1 - #undef MAC_OS_X_VERSION_MAX_ALLOWED - #define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_4 #else #define JUCE_INTEL 1 #endif @@ -80044,12 +80042,12 @@ void Graphics::drawLine (const float startX, const float startY, fillPath (p); } -void Graphics::drawLine (const Line& line) const +void Graphics::drawLine (const Line& line) const { drawLine (line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY()); } -void Graphics::drawLine (const Line& line, const float lineThickness) const +void Graphics::drawLine (const Line& line, const float lineThickness) const { drawLine (line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY(), lineThickness); } @@ -80893,48 +80891,40 @@ public: : lookupTable (lookupTable_), numEntries (numEntries_) { jassert (numEntries_ >= 0); - float x1 = gradient.x1; - float y1 = gradient.y1; - float x2 = gradient.x2; - float y2 = gradient.y2; + Point p1 (gradient.x1, gradient.y1); + Point p2 (gradient.x2, gradient.y2); if (! transform.isIdentity()) { - const Line l (x2, y2, x1, y1); - const Point p3 = l.getPointAlongLine (0.0f, 100.0f); - float x3 = p3.getX(); - float y3 = p3.getY(); + const Line l (p2, p1); + Point p3 = l.getPointAlongLine (0.0f, 100.0f); - transform.transformPoint (x1, y1); - transform.transformPoint (x2, y2); - transform.transformPoint (x3, y3); + p1.applyTransform (transform); + p2.applyTransform (transform); + p3.applyTransform (transform); - const Line l2 (x2, y2, x3, y3); - const float prop = l2.findNearestPointTo (x1, y1); - const Point newP2 (l2.getPointAlongLineProportionally (prop)); - - x2 = newP2.getX(); - y2 = newP2.getY(); + const Line l2 (p2, p3); + p2 = l2.getPointAlongLineProportionally (l2.findNearestPointTo (p1)); } - vertical = fabs (x1 - x2) < 0.001f; - horizontal = fabs (y1 - y2) < 0.001f; + vertical = fabs (p1.getX() - p2.getX()) < 0.001f; + horizontal = fabs (p1.getY() - p2.getY()) < 0.001f; if (vertical) { - scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (y2 - y1)); - start = roundToInt (y1 * scale); + scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.getY() - p1.getY())); + start = roundToInt (p1.getY() * scale); } else if (horizontal) { - scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (x2 - x1)); - start = roundToInt (x1 * scale); + scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.getX() - p1.getX())); + start = roundToInt (p1.getX() * scale); } else { - grad = (y2 - y1) / (double) (x1 - x2); - yTerm = y1 - x1 / grad; - scale = roundToInt ((numEntries << (int) numScaleBits) / (yTerm * grad - (y2 * grad - x2))); + grad = (p2.getY() - p1.getY()) / (double) (p1.getX() - p2.getX()); + yTerm = p1.getY() - p1.getX() / grad; + scale = roundToInt ((numEntries << (int) numScaleBits) / (yTerm * grad - (p2.getY() * grad - p2.getX()))); grad *= scale; } } @@ -86885,364 +86875,6 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_Line.cpp ***/ BEGIN_JUCE_NAMESPACE -static bool juce_lineIntersection (const float x1, const float y1, - const float x2, const float y2, - const float x3, const float y3, - const float x4, const float y4, - float& intersectionX, - float& intersectionY) throw() -{ - if (x2 != x3 || y2 != y3) - { - const float dx1 = x2 - x1; - const float dy1 = y2 - y1; - const float dx2 = x4 - x3; - const float dy2 = y4 - y3; - const float divisor = dx1 * dy2 - dx2 * dy1; - - if (divisor == 0) - { - if (! ((dx1 == 0 && dy1 == 0) || (dx2 == 0 && dy2 == 0))) - { - if (dy1 == 0 && dy2 != 0) - { - const float along = (y1 - y3) / dy2; - intersectionX = x3 + along * dx2; - intersectionY = y1; - - return along >= 0 && along <= 1.0f; - } - else if (dy2 == 0 && dy1 != 0) - { - const float along = (y3 - y1) / dy1; - intersectionX = x1 + along * dx1; - intersectionY = y3; - - return along >= 0 && along <= 1.0f; - } - else if (dx1 == 0 && dx2 != 0) - { - const float along = (x1 - x3) / dx2; - intersectionX = x1; - intersectionY = y3 + along * dy2; - - return along >= 0 && along <= 1.0f; - } - else if (dx2 == 0 && dx1 != 0) - { - const float along = (x3 - x1) / dx1; - intersectionX = x3; - intersectionY = y1 + along * dy1; - - return along >= 0 && along <= 1.0f; - } - } - - intersectionX = 0.5f * (x2 + x3); - intersectionY = 0.5f * (y2 + y3); - - return false; - } - - const float along1 = ((y1 - y3) * dx2 - (x1 - x3) * dy2) / divisor; - - intersectionX = x1 + along1 * dx1; - intersectionY = y1 + along1 * dy1; - - if (along1 < 0 || along1 > 1.0f) - return false; - - const float along2 = ((y1 - y3) * dx1 - (x1 - x3) * dy1) / divisor; - - return along2 >= 0 && along2 <= 1.0f; - } - - intersectionX = x2; - intersectionY = y2; - return true; -} - -Line::Line() throw() - : startX (0.0f), - startY (0.0f), - endX (0.0f), - endY (0.0f) -{ -} - -Line::Line (const Line& other) throw() - : startX (other.startX), - startY (other.startY), - endX (other.endX), - endY (other.endY) -{ -} - -Line::Line (const float startX_, const float startY_, - const float endX_, const float endY_) throw() - : startX (startX_), - startY (startY_), - endX (endX_), - endY (endY_) -{ -} - -Line::Line (const Point& start, - const Point& end) throw() - : startX (start.getX()), - startY (start.getY()), - endX (end.getX()), - endY (end.getY()) -{ -} - -Line& Line::operator= (const Line& other) throw() -{ - startX = other.startX; - startY = other.startY; - endX = other.endX; - endY = other.endY; - - return *this; -} - -Line::~Line() throw() -{ -} - -const Point Line::getStart() const throw() -{ - return Point (startX, startY); -} - -const Point Line::getEnd() const throw() -{ - return Point (endX, endY); -} - -void Line::setStart (const float newStartX, - const float newStartY) throw() -{ - startX = newStartX; - startY = newStartY; -} - -void Line::setStart (const Point& newStart) throw() -{ - startX = newStart.getX(); - startY = newStart.getY(); -} - -void Line::setEnd (const float newEndX, - const float newEndY) throw() -{ - endX = newEndX; - endY = newEndY; -} - -void Line::setEnd (const Point& newEnd) throw() -{ - endX = newEnd.getX(); - endY = newEnd.getY(); -} - -bool Line::operator== (const Line& other) const throw() -{ - return startX == other.startX - && startY == other.startY - && endX == other.endX - && endY == other.endY; -} - -bool Line::operator!= (const Line& other) const throw() -{ - return startX != other.startX - || startY != other.startY - || endX != other.endX - || endY != other.endY; -} - -void Line::applyTransform (const AffineTransform& transform) throw() -{ - transform.transformPoint (startX, startY); - transform.transformPoint (endX, endY); -} - -float Line::getLength() const throw() -{ - return (float) juce_hypot (startX - endX, - startY - endY); -} - -float Line::getAngle() const throw() -{ - return atan2f (endX - startX, - endY - startY); -} - -const Point Line::getPointAlongLine (const float distanceFromStart) const throw() -{ - const float alpha = distanceFromStart / getLength(); - - return Point (startX + (endX - startX) * alpha, - startY + (endY - startY) * alpha); -} - -const Point Line::getPointAlongLine (const float offsetX, - const float offsetY) const throw() -{ - const float dx = endX - startX; - const float dy = endY - startY; - const double length = juce_hypot (dx, dy); - - if (length == 0) - return Point (startX, startY); - else - return Point (startX + (float) (((dx * offsetX) - (dy * offsetY)) / length), - startY + (float) (((dy * offsetX) + (dx * offsetY)) / length)); -} - -const Point Line::getPointAlongLineProportionally (const float alpha) const throw() -{ - return Point (startX + (endX - startX) * alpha, - startY + (endY - startY) * alpha); -} - -float Line::getDistanceFromLine (const float x, - const float y) const throw() -{ - const double dx = endX - startX; - const double dy = endY - startY; - const double length = dx * dx + dy * dy; - - if (length > 0) - { - const double prop = ((x - startX) * dx + (y - startY) * dy) / length; - - if (prop >= 0.0f && prop < 1.0f) - { - return (float) juce_hypot (x - (startX + prop * dx), - y - (startY + prop * dy)); - } - } - - return (float) jmin (juce_hypot (x - startX, y - startY), - juce_hypot (x - endX, y - endY)); -} - -float Line::findNearestPointTo (const float x, - const float y) const throw() -{ - const double dx = endX - startX; - const double dy = endY - startY; - const double length = dx * dx + dy * dy; - - if (length <= 0.0) - return 0.0f; - - return jlimit (0.0f, 1.0f, - (float) (((x - startX) * dx + (y - startY) * dy) / length)); -} - -const Line Line::withShortenedStart (const float distanceToShortenBy) const throw() -{ - const float length = getLength(); - - return Line (getPointAlongLine (jmin (distanceToShortenBy, length)), - getEnd()); -} - -const Line Line::withShortenedEnd (const float distanceToShortenBy) const throw() -{ - const float length = getLength(); - - return Line (getStart(), - getPointAlongLine (length - jmin (distanceToShortenBy, length))); -} - -bool Line::clipToPath (const Path& path, - const bool keepSectionOutsidePath) throw() -{ - const bool startInside = path.contains (startX, startY); - const bool endInside = path.contains (endX, endY); - - if (startInside == endInside) - { - if (keepSectionOutsidePath != startInside) - { - // entirely outside the path - return false; - } - else - { - // entirely inside the path - startX = 0.0f; - startY = 0.0f; - endX = 0.0f; - endY = 0.0f; - - return true; - } - } - else - { - bool changed = false; - PathFlatteningIterator iter (path, AffineTransform::identity); - - while (iter.next()) - { - float ix, iy; - - if (intersects (Line (iter.x1, iter.y1, - iter.x2, iter.y2), - ix, iy)) - { - if ((startInside && keepSectionOutsidePath) - || (endInside && ! keepSectionOutsidePath)) - { - setStart (ix, iy); - } - else - { - setEnd (ix, iy); - } - - changed = true; - } - } - - return changed; - } -} - -bool Line::intersects (const Line& line, - float& intersectionX, - float& intersectionY) const throw() -{ - return juce_lineIntersection (startX, startY, - endX, endY, - line.startX, line.startY, - line.endX, line.endY, - intersectionX, - intersectionY); -} - -bool Line::isVertical() const throw() -{ - return startX == endX; -} - -bool Line::isHorizontal() const throw() -{ - return startY == endY; -} - -bool Line::isPointAbove (const float x, const float y) const throw() -{ - return startX != endX - && y < ((endY - startY) * (x - startX)) / (endX - startX) + startY; -} - END_JUCE_NAMESPACE /*** End of inlined file: juce_Line.cpp ***/ @@ -88243,8 +87875,7 @@ bool Path::contains (const float x, const float y, const float tolerence) const while (i.next()) { - if ((i.y1 <= y && i.y2 > y) - || (i.y2 <= y && i.y1 > y)) + if ((i.y1 <= y && i.y2 > y) || (i.y2 <= y && i.y1 > y)) { const float intersectX = i.x1 + (i.x2 - i.x1) * (y - i.y1) / (i.y2 - i.y1); @@ -88258,30 +87889,58 @@ bool Path::contains (const float x, const float y, const float tolerence) const } } - return (useNonZeroWinding) ? (negativeCrossings != positiveCrossings) - : ((negativeCrossings + positiveCrossings) & 1) != 0; + return useNonZeroWinding ? (negativeCrossings != positiveCrossings) + : ((negativeCrossings + positiveCrossings) & 1) != 0; } -bool Path::intersectsLine (const float x1, const float y1, - const float x2, const float y2, - const float tolerence) +bool Path::contains (const Point& point, const float tolerence) const +{ + return contains (point.getX(), point.getY(), tolerence); +} + +bool Path::intersectsLine (const Line& line, const float tolerence) { PathFlatteningIterator i (*this, AffineTransform::identity, tolerence); - - const Line line1 (x1, y1, x2, y2); + Point intersection; while (i.next()) - { - const Line line2 (i.x1, i.y1, i.x2, i.y2); - - float ix, iy; - if (line1.intersects (line2, ix, iy)) + if (line.intersects (Line (i.x1, i.y1, i.x2, i.y2), intersection)) return true; - } return false; } +const Line Path::getClippedLine (const Line& line, const bool keepSectionOutsidePath) const +{ + Line result (line); + const bool startInside = contains (line.getStart()); + const bool endInside = contains (line.getEnd()); + + if (startInside == endInside) + { + if (keepSectionOutsidePath == startInside) + result = Line(); + } + else + { + PathFlatteningIterator i (*this, AffineTransform::identity); + Point intersection; + + while (i.next()) + { + if (line.intersects (Line (i.x1, i.y1, i.x2, i.y2), intersection)) + { + if ((startInside && keepSectionOutsidePath) || (endInside && ! keepSectionOutsidePath)) + result.setStart (intersection); + else + result.setEnd (intersection); + } + } + } + + return result; +} + const Path Path::createPathWithRoundedCorners (const float cornerRadius) const { if (cornerRadius <= 0.01f) diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 456a2971c3..4b051d99ed 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -105,8 +105,6 @@ #if defined (__ppc__) || defined (__ppc64__) #define JUCE_PPC 1 - #undef MAC_OS_X_VERSION_MAX_ALLOWED - #define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_4 #else #define JUCE_INTEL 1 #endif @@ -9641,6 +9639,10 @@ public: bool isOrigin() const throw() { return x == ValueType() && y == ValueType(); } + const Point withX (const ValueType newX) const throw() { return Point (newX, y); } + + const Point withY (const ValueType newY) const throw() { return Point (x, newY); } + void setXY (const ValueType newX, const ValueType newY) throw() { x = newX; y = newY; } void addXY (const ValueType xToAdd, const ValueType yToAdd) throw() { x += xToAdd; y += yToAdd; } @@ -9653,10 +9655,22 @@ public: Point& operator-= (const Point& other) throw() { x -= other.x; y -= other.y; return *this; } + const Point operator* (const ValueType multiplier) const throw() { return Point (x * multiplier, y * multiplier); } + + Point& operator*= (const ValueType multiplier) throw() { x *= multiplier; y *= multiplier; return *this; } + + const Point operator/ (const ValueType divisor) const throw() { return Point (x / divisor, y / divisor); } + + Point& operator/= (const ValueType divisor) throw() { x /= divisor; y /= divisor; return *this; } + const Point operator-() const throw() { return Point (-x, -y); } + ValueType getDistanceFromOrigin() const throw() { return (ValueType) juce_hypot (x, y); } + ValueType getDistanceFrom (const Point& other) const throw() { return (ValueType) juce_hypot (x - other.x, y - other.y); } + ValueType getAngleToPoint (const Point& other) const throw() { return (ValueType) atan2 (other.x - x, other.y - y); } + void applyTransform (const AffineTransform& transform) throw() { transform.transformPoint (x, y); } const String toString() const { return String (x) + ", " + String (y); } @@ -9981,6 +9995,225 @@ public: #define __JUCE_PATH_JUCEHEADER__ +/*** Start of inlined file: juce_Line.h ***/ +#ifndef __JUCE_LINE_JUCEHEADER__ +#define __JUCE_LINE_JUCEHEADER__ + +template +class Line +{ +public: + + Line() throw() {} + + Line (const Line& other) throw() + : start (other.start), + end (other.end) + { + } + + Line (ValueType startX, ValueType startY, ValueType endX, ValueType endY) throw() + : start (startX, startY), + end (endX, endY) + { + } + + Line (const Point& startPoint, + const Point& endPoint) throw() + : start (startPoint), + end (endPoint) + { + } + + Line& operator= (const Line& other) throw() + { + start = other.start; + end = other.end; + return *this; + } + + ~Line() throw() {} + + inline ValueType getStartX() const throw() { return start.getX(); } + + inline ValueType getStartY() const throw() { return start.getY(); } + + inline ValueType getEndX() const throw() { return end.getX(); } + + inline ValueType getEndY() const throw() { return end.getY(); } + + inline const Point& getStart() const throw() { return start; } + + inline const Point& getEnd() const throw() { return end; } + + void setStart (ValueType newStartX, ValueType newStartY) throw() { start.setXY (newStartX, newStartY); } + + void setEnd (ValueType newEndX, ValueType newEndY) throw() { end.setXY (newEndX, newEndY); } + + void setStart (const Point& newStart) throw() { start = newStart; } + + void setEnd (const Point& newEnd) throw() { end = newEnd; } + + void applyTransform (const AffineTransform& transform) throw() + { + start.applyTransform (transform); + end.applyTransform (transform); + } + + ValueType getLength() const throw() { return start.getDistanceFrom (end); } + + bool isVertical() const throw() { return start.getX() == end.getX(); } + + bool isHorizontal() const throw() { return start.getY() == end.getY(); } + + ValueType getAngle() const throw() { return start.getAngleToPoint (end); } + + bool operator== (const Line& other) const throw() { return start == other.start && end == other.end; } + + bool operator!= (const Line& other) const throw() { return start != other.start || end != other.end; } + + bool intersects (const Line& line, Point& intersection) const throw() + { + return findIntersection (start, end, line.start, line.end, intersection); + } + + const Point getPointAlongLine (ValueType distanceFromStart) const throw() + { + return start + (end - start) * (distanceFromStart / getLength()); + } + + const Point getPointAlongLine (ValueType distanceFromStart, + ValueType perpendicularDistance) const throw() + { + const Point delta (end - start); + const double length = juce_hypot (delta.getX(), delta.getY()); + if (length == 0) + return start; + + return Point (start.getX() + (ValueType) ((delta.getX() * distanceFromStart - delta.getY() * perpendicularDistance) / length), + start.getY() + (ValueType) ((delta.getY() * distanceFromStart + delta.getX() * perpendicularDistance) / length)); + } + + const Point getPointAlongLineProportionally (ValueType proportionOfLength) const throw() + { + return start + (end - start) * proportionOfLength; + } + + ValueType getDistanceFromLine (const Point& point) const throw() + { + const Point delta (end - start); + const double length = delta.getX() * delta.getX() + delta.getY() * delta.getY(); + + if (length > 0) + { + const double prop = ((point.getX() - start.getX()) * delta.getX() + + (point.getY() - start.getY()) * delta.getY()) / length; + + if (prop >= 0 && prop <= 1.0) + return point.getDistanceFrom (start + delta * (ValueType) prop); + } + + return jmin (point.getDistanceFrom (start), + point.getDistanceFrom (end)); + } + + ValueType findNearestPointTo (const Point& point) const throw() + { + const Point delta (end - start); + const double length = delta.getX() * delta.getX() + delta.getY() * delta.getY(); + + return length <= 0 ? 0 + : jlimit ((ValueType) 0, (ValueType) 1, + (ValueType) (((point.getX() - start.getX()) * delta.getX() + + (point.getY() - start.getY()) * delta.getY()) / length)); + } + + bool isPointAbove (const Point& point) const throw() + { + return start.getX() != end.getX() + && point.getY() < ((end.getY() - start.getY()) + * (point.getX() - start.getX())) / (end.getX() - start.getX()) + start.getY(); + } + + const Line withShortenedStart (ValueType distanceToShortenBy) const throw() + { + return Line (getPointAlongLine (jmin (distanceToShortenBy, getLength())), end); + } + + const Line withShortenedEnd (ValueType distanceToShortenBy) const throw() + { + const ValueType length = getLength(); + return Line (start, getPointAlongLine (length - jmin (distanceToShortenBy, length))); + } + + juce_UseDebuggingNewOperator + +private: + Point start, end; + + static bool findIntersection (const Point& p1, const Point& p2, + const Point& p3, const Point& p4, + Point& intersection) throw() + { + if (p2 == p3) + { + intersection = p2; + return true; + } + + const Point d1 (p2 - p1); + const Point d2 (p4 - p2); + const ValueType divisor = d1.getX() * d2.getY() - d2.getX() * d1.getY(); + + if (divisor == 0) + { + if (! (d1.isOrigin() || d2.isOrigin())) + { + if (d1.getY() == 0 && d2.getY() != 0) + { + const ValueType along = (p1.getY() - p3.getY()) / d2.getY(); + intersection = p1.withX (p3.getX() + along * d2.getX()); + return along >= 0 && along <= (ValueType) 1; + } + else if (d2.getY() == 0 && d1.getY() != 0) + { + const ValueType along = (p3.getY() - p1.getY()) / d1.getY(); + intersection = p3.withX (p1.getX() + along * d1.getX()); + return along >= 0 && along <= (ValueType) 1; + } + else if (d1.getX() == 0 && d2.getX() != 0) + { + const ValueType along = (p1.getX() - p3.getX()) / d2.getX(); + intersection = p1.withY (p3.getY() + along * d2.getY()); + return along >= 0 && along <= (ValueType) 1; + } + else if (d2.getX() == 0 && d1.getX() != 0) + { + const ValueType along = (p3.getX() - p1.getX()) / d1.getX(); + intersection = p3.withY (p1.getY() + along * d1.getY()); + return along >= 0 && along <= (ValueType) 1; + } + } + + intersection = (p2 + p3) / (ValueType) 2; + return false; + } + + const ValueType along1 = ((p1.getY() - p3.getY()) * d2.getX() - (p1.getX() - p3.getX()) * d2.getY()) / divisor; + intersection = p1 + d1 * along1; + + if (along1 < 0 || along1 > (ValueType) 1) + return false; + + const ValueType along2 = ((p1.getY() - p3.getY()) * d1.getX() - (p1.getX() - p3.getX()) * d1.getY()) / divisor; + return along2 >= 0 && along2 <= (ValueType) 1; + } +}; + +#endif // __JUCE_LINE_JUCEHEADER__ +/*** End of inlined file: juce_Line.h ***/ + + /*** Start of inlined file: juce_Rectangle.h ***/ #ifndef __JUCE_RECTANGLE_JUCEHEADER__ #define __JUCE_RECTANGLE_JUCEHEADER__ @@ -10602,10 +10835,14 @@ public: bool contains (float x, float y, float tolerence = 10.0f) const; - bool intersectsLine (float x1, float y1, - float x2, float y2, + bool contains (const Point& point, + float tolerence = 10.0f) const; + + bool intersectsLine (const Line& line, float tolerence = 10.0f); + const Line getClippedLine (const Line& line, bool keepSectionOutsidePath) const; + void clear() throw(); void startNewSubPath (float startX, float startY); @@ -11070,99 +11307,6 @@ private: /*** End of inlined file: juce_PathStrokeType.h ***/ -/*** Start of inlined file: juce_Line.h ***/ -#ifndef __JUCE_LINE_JUCEHEADER__ -#define __JUCE_LINE_JUCEHEADER__ - -class JUCE_API Line -{ -public: - - Line() throw(); - - Line (const Line& other) throw(); - - Line (float startX, - float startY, - float endX, - float endY) throw(); - - Line (const Point& start, - const Point& end) throw(); - - Line& operator= (const Line& other) throw(); - - ~Line() throw(); - - inline float getStartX() const throw() { return startX; } - - inline float getStartY() const throw() { return startY; } - - inline float getEndX() const throw() { return endX; } - - inline float getEndY() const throw() { return endY; } - - const Point getStart() const throw(); - - const Point getEnd() const throw(); - - void setStart (float newStartX, - float newStartY) throw(); - - void setEnd (float newEndX, - float newEndY) throw(); - - void setStart (const Point& newStart) throw(); - - void setEnd (const Point& newEnd) throw(); - - void applyTransform (const AffineTransform& transform) throw(); - - float getLength() const throw(); - - bool isVertical() const throw(); - - bool isHorizontal() const throw(); - - float getAngle() const throw(); - - bool operator== (const Line& other) const throw(); - - bool operator!= (const Line& other) const throw(); - - bool intersects (const Line& line, - float& intersectionX, - float& intersectionY) const throw(); - - const Point getPointAlongLine (float distanceFromStart) const throw(); - - const Point getPointAlongLine (float distanceFromStart, - float perpendicularDistance) const throw(); - - const Point getPointAlongLineProportionally (float proportionOfLength) const throw(); - - float getDistanceFromLine (float x, float y) const throw(); - - float findNearestPointTo (float x, float y) const throw(); - - bool isPointAbove (float x, float y) const throw(); - - const Line withShortenedStart (float distanceToShortenBy) const throw(); - - const Line withShortenedEnd (float distanceToShortenBy) const throw(); - - bool clipToPath (const Path& path, bool keepSectionOutsidePath) throw(); - - juce_UseDebuggingNewOperator - -private: - float startX, startY, endX, endY; -}; - -#endif // __JUCE_LINE_JUCEHEADER__ -/*** End of inlined file: juce_Line.h ***/ - - /*** Start of inlined file: juce_Colours.h ***/ #ifndef __JUCE_COLOURS_JUCEHEADER__ #define __JUCE_COLOURS_JUCEHEADER__ @@ -12097,9 +12241,9 @@ public: void drawLine (float startX, float startY, float endX, float endY, float lineThickness) const; - void drawLine (const Line& line) const; + void drawLine (const Line& line) const; - void drawLine (const Line& line, float lineThickness) const; + void drawLine (const Line& line, float lineThickness) const; void drawDashedLine (float startX, float startY, float endX, float endY, diff --git a/src/core/juce_TargetPlatform.h b/src/core/juce_TargetPlatform.h index 363ec12166..d126bb9ab3 100644 --- a/src/core/juce_TargetPlatform.h +++ b/src/core/juce_TargetPlatform.h @@ -96,8 +96,6 @@ #if defined (__ppc__) || defined (__ppc64__) #define JUCE_PPC 1 - #undef MAC_OS_X_VERSION_MAX_ALLOWED - #define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_4 #else #define JUCE_INTEL 1 #endif diff --git a/src/gui/graphics/contexts/juce_Graphics.cpp b/src/gui/graphics/contexts/juce_Graphics.cpp index f82e5b9b18..7191a21efd 100644 --- a/src/gui/graphics/contexts/juce_Graphics.cpp +++ b/src/gui/graphics/contexts/juce_Graphics.cpp @@ -544,12 +544,12 @@ void Graphics::drawLine (const float startX, const float startY, fillPath (p); } -void Graphics::drawLine (const Line& line) const +void Graphics::drawLine (const Line& line) const { drawLine (line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY()); } -void Graphics::drawLine (const Line& line, const float lineThickness) const +void Graphics::drawLine (const Line& line, const float lineThickness) const { drawLine (line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY(), lineThickness); } diff --git a/src/gui/graphics/contexts/juce_Graphics.h b/src/gui/graphics/contexts/juce_Graphics.h index bd46b6d56e..6303898127 100644 --- a/src/gui/graphics/contexts/juce_Graphics.h +++ b/src/gui/graphics/contexts/juce_Graphics.h @@ -359,13 +359,13 @@ public: The line is 1 pixel wide and drawn with the current colour or brush. */ - void drawLine (const Line& line) const; + void drawLine (const Line& line) const; /** Draws a line between two points with a given thickness. @see Path::addLineSegment */ - void drawLine (const Line& line, float lineThickness) const; + void drawLine (const Line& line, float lineThickness) const; /** Draws a dashed line using a custom set of dash-lengths. diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index 5761e7ee15..7361a22a65 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -177,48 +177,40 @@ public: : lookupTable (lookupTable_), numEntries (numEntries_) { jassert (numEntries_ >= 0); - float x1 = gradient.x1; - float y1 = gradient.y1; - float x2 = gradient.x2; - float y2 = gradient.y2; + Point p1 (gradient.x1, gradient.y1); + Point p2 (gradient.x2, gradient.y2); if (! transform.isIdentity()) { - const Line l (x2, y2, x1, y1); - const Point p3 = l.getPointAlongLine (0.0f, 100.0f); - float x3 = p3.getX(); - float y3 = p3.getY(); + const Line l (p2, p1); + Point p3 = l.getPointAlongLine (0.0f, 100.0f); - transform.transformPoint (x1, y1); - transform.transformPoint (x2, y2); - transform.transformPoint (x3, y3); + p1.applyTransform (transform); + p2.applyTransform (transform); + p3.applyTransform (transform); - const Line l2 (x2, y2, x3, y3); - const float prop = l2.findNearestPointTo (x1, y1); - const Point newP2 (l2.getPointAlongLineProportionally (prop)); - - x2 = newP2.getX(); - y2 = newP2.getY(); + const Line l2 (p2, p3); + p2 = l2.getPointAlongLineProportionally (l2.findNearestPointTo (p1)); } - vertical = fabs (x1 - x2) < 0.001f; - horizontal = fabs (y1 - y2) < 0.001f; + vertical = fabs (p1.getX() - p2.getX()) < 0.001f; + horizontal = fabs (p1.getY() - p2.getY()) < 0.001f; if (vertical) { - scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (y2 - y1)); - start = roundToInt (y1 * scale); + scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.getY() - p1.getY())); + start = roundToInt (p1.getY() * scale); } else if (horizontal) { - scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (x2 - x1)); - start = roundToInt (x1 * scale); + scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.getX() - p1.getX())); + start = roundToInt (p1.getX() * scale); } else { - grad = (y2 - y1) / (double) (x1 - x2); - yTerm = y1 - x1 / grad; - scale = roundToInt ((numEntries << (int) numScaleBits) / (yTerm * grad - (y2 * grad - x2))); + grad = (p2.getY() - p1.getY()) / (double) (p1.getX() - p2.getX()); + yTerm = p1.getY() - p1.getX() / grad; + scale = roundToInt ((numEntries << (int) numScaleBits) / (yTerm * grad - (p2.getY() * grad - p2.getX()))); grad *= scale; } } diff --git a/src/gui/graphics/geometry/juce_Line.cpp b/src/gui/graphics/geometry/juce_Line.cpp index 7c51e54aa8..5676580deb 100644 --- a/src/gui/graphics/geometry/juce_Line.cpp +++ b/src/gui/graphics/geometry/juce_Line.cpp @@ -27,375 +27,7 @@ BEGIN_JUCE_NAMESPACE - #include "juce_Line.h" -#include "juce_PathIterator.h" - - -//============================================================================== -static bool juce_lineIntersection (const float x1, const float y1, - const float x2, const float y2, - const float x3, const float y3, - const float x4, const float y4, - float& intersectionX, - float& intersectionY) throw() -{ - if (x2 != x3 || y2 != y3) - { - const float dx1 = x2 - x1; - const float dy1 = y2 - y1; - const float dx2 = x4 - x3; - const float dy2 = y4 - y3; - const float divisor = dx1 * dy2 - dx2 * dy1; - - if (divisor == 0) - { - if (! ((dx1 == 0 && dy1 == 0) || (dx2 == 0 && dy2 == 0))) - { - if (dy1 == 0 && dy2 != 0) - { - const float along = (y1 - y3) / dy2; - intersectionX = x3 + along * dx2; - intersectionY = y1; - - return along >= 0 && along <= 1.0f; - } - else if (dy2 == 0 && dy1 != 0) - { - const float along = (y3 - y1) / dy1; - intersectionX = x1 + along * dx1; - intersectionY = y3; - - return along >= 0 && along <= 1.0f; - } - else if (dx1 == 0 && dx2 != 0) - { - const float along = (x1 - x3) / dx2; - intersectionX = x1; - intersectionY = y3 + along * dy2; - - return along >= 0 && along <= 1.0f; - } - else if (dx2 == 0 && dx1 != 0) - { - const float along = (x3 - x1) / dx1; - intersectionX = x3; - intersectionY = y1 + along * dy1; - - return along >= 0 && along <= 1.0f; - } - } - - intersectionX = 0.5f * (x2 + x3); - intersectionY = 0.5f * (y2 + y3); - - return false; - } - - const float along1 = ((y1 - y3) * dx2 - (x1 - x3) * dy2) / divisor; - - intersectionX = x1 + along1 * dx1; - intersectionY = y1 + along1 * dy1; - - if (along1 < 0 || along1 > 1.0f) - return false; - - const float along2 = ((y1 - y3) * dx1 - (x1 - x3) * dy1) / divisor; - - return along2 >= 0 && along2 <= 1.0f; - } - - intersectionX = x2; - intersectionY = y2; - return true; -} - -//============================================================================== -Line::Line() throw() - : startX (0.0f), - startY (0.0f), - endX (0.0f), - endY (0.0f) -{ -} - -Line::Line (const Line& other) throw() - : startX (other.startX), - startY (other.startY), - endX (other.endX), - endY (other.endY) -{ -} - -Line::Line (const float startX_, const float startY_, - const float endX_, const float endY_) throw() - : startX (startX_), - startY (startY_), - endX (endX_), - endY (endY_) -{ -} - -Line::Line (const Point& start, - const Point& end) throw() - : startX (start.getX()), - startY (start.getY()), - endX (end.getX()), - endY (end.getY()) -{ -} - -Line& Line::operator= (const Line& other) throw() -{ - startX = other.startX; - startY = other.startY; - endX = other.endX; - endY = other.endY; - - return *this; -} - -Line::~Line() throw() -{ -} - -//============================================================================== -const Point Line::getStart() const throw() -{ - return Point (startX, startY); -} - -const Point Line::getEnd() const throw() -{ - return Point (endX, endY); -} - -void Line::setStart (const float newStartX, - const float newStartY) throw() -{ - startX = newStartX; - startY = newStartY; -} - -void Line::setStart (const Point& newStart) throw() -{ - startX = newStart.getX(); - startY = newStart.getY(); -} - -void Line::setEnd (const float newEndX, - const float newEndY) throw() -{ - endX = newEndX; - endY = newEndY; -} - -void Line::setEnd (const Point& newEnd) throw() -{ - endX = newEnd.getX(); - endY = newEnd.getY(); -} - -bool Line::operator== (const Line& other) const throw() -{ - return startX == other.startX - && startY == other.startY - && endX == other.endX - && endY == other.endY; -} - -bool Line::operator!= (const Line& other) const throw() -{ - return startX != other.startX - || startY != other.startY - || endX != other.endX - || endY != other.endY; -} - -//============================================================================== -void Line::applyTransform (const AffineTransform& transform) throw() -{ - transform.transformPoint (startX, startY); - transform.transformPoint (endX, endY); -} - -//============================================================================== -float Line::getLength() const throw() -{ - return (float) juce_hypot (startX - endX, - startY - endY); -} - -float Line::getAngle() const throw() -{ - return atan2f (endX - startX, - endY - startY); -} - -const Point Line::getPointAlongLine (const float distanceFromStart) const throw() -{ - const float alpha = distanceFromStart / getLength(); - - return Point (startX + (endX - startX) * alpha, - startY + (endY - startY) * alpha); -} - -const Point Line::getPointAlongLine (const float offsetX, - const float offsetY) const throw() -{ - const float dx = endX - startX; - const float dy = endY - startY; - const double length = juce_hypot (dx, dy); - - if (length == 0) - return Point (startX, startY); - else - return Point (startX + (float) (((dx * offsetX) - (dy * offsetY)) / length), - startY + (float) (((dy * offsetX) + (dx * offsetY)) / length)); -} - -const Point Line::getPointAlongLineProportionally (const float alpha) const throw() -{ - return Point (startX + (endX - startX) * alpha, - startY + (endY - startY) * alpha); -} - -float Line::getDistanceFromLine (const float x, - const float y) const throw() -{ - const double dx = endX - startX; - const double dy = endY - startY; - const double length = dx * dx + dy * dy; - - if (length > 0) - { - const double prop = ((x - startX) * dx + (y - startY) * dy) / length; - - if (prop >= 0.0f && prop < 1.0f) - { - return (float) juce_hypot (x - (startX + prop * dx), - y - (startY + prop * dy)); - } - } - - return (float) jmin (juce_hypot (x - startX, y - startY), - juce_hypot (x - endX, y - endY)); -} - -float Line::findNearestPointTo (const float x, - const float y) const throw() -{ - const double dx = endX - startX; - const double dy = endY - startY; - const double length = dx * dx + dy * dy; - - if (length <= 0.0) - return 0.0f; - - return jlimit (0.0f, 1.0f, - (float) (((x - startX) * dx + (y - startY) * dy) / length)); -} - -const Line Line::withShortenedStart (const float distanceToShortenBy) const throw() -{ - const float length = getLength(); - - return Line (getPointAlongLine (jmin (distanceToShortenBy, length)), - getEnd()); -} - -const Line Line::withShortenedEnd (const float distanceToShortenBy) const throw() -{ - const float length = getLength(); - - return Line (getStart(), - getPointAlongLine (length - jmin (distanceToShortenBy, length))); -} - -//============================================================================== -bool Line::clipToPath (const Path& path, - const bool keepSectionOutsidePath) throw() -{ - const bool startInside = path.contains (startX, startY); - const bool endInside = path.contains (endX, endY); - - if (startInside == endInside) - { - if (keepSectionOutsidePath != startInside) - { - // entirely outside the path - return false; - } - else - { - // entirely inside the path - startX = 0.0f; - startY = 0.0f; - endX = 0.0f; - endY = 0.0f; - - return true; - } - } - else - { - bool changed = false; - PathFlatteningIterator iter (path, AffineTransform::identity); - - while (iter.next()) - { - float ix, iy; - - if (intersects (Line (iter.x1, iter.y1, - iter.x2, iter.y2), - ix, iy)) - { - if ((startInside && keepSectionOutsidePath) - || (endInside && ! keepSectionOutsidePath)) - { - setStart (ix, iy); - } - else - { - setEnd (ix, iy); - } - - changed = true; - } - } - - return changed; - } -} - -//============================================================================== -bool Line::intersects (const Line& line, - float& intersectionX, - float& intersectionY) const throw() -{ - return juce_lineIntersection (startX, startY, - endX, endY, - line.startX, line.startY, - line.endX, line.endY, - intersectionX, - intersectionY); -} - -bool Line::isVertical() const throw() -{ - return startX == endX; -} - -bool Line::isHorizontal() const throw() -{ - return startY == endY; -} - -bool Line::isPointAbove (const float x, const float y) const throw() -{ - return startX != endX - && y < ((endY - startY) * (x - startX)) / (endX - startX) + startY; -} END_JUCE_NAMESPACE diff --git a/src/gui/graphics/geometry/juce_Line.h b/src/gui/graphics/geometry/juce_Line.h index 4f41c2e353..11381e46dc 100644 --- a/src/gui/graphics/geometry/juce_Line.h +++ b/src/gui/graphics/geometry/juce_Line.h @@ -26,122 +26,138 @@ #ifndef __JUCE_LINE_JUCEHEADER__ #define __JUCE_LINE_JUCEHEADER__ -#include "juce_Path.h" #include "juce_Point.h" //============================================================================== /** - Represents a line, using 32-bit float co-ordinates. + Represents a line. This class contains a bunch of useful methods for various geometric tasks. @see Point, Rectangle, Path, Graphics::drawLine */ -class JUCE_API Line +template +class Line { public: //============================================================================== /** Creates a line, using (0, 0) as its start and end points. */ - Line() throw(); + Line() throw() {} /** Creates a copy of another line. */ - Line (const Line& other) throw(); + Line (const Line& other) throw() + : start (other.start), + end (other.end) + { + } /** Creates a line based on the co-ordinates of its start and end points. */ - Line (float startX, - float startY, - float endX, - float endY) throw(); + Line (ValueType startX, ValueType startY, ValueType endX, ValueType endY) throw() + : start (startX, startY), + end (endX, endY) + { + } /** Creates a line from its start and end points. */ - Line (const Point& start, - const Point& end) throw(); + Line (const Point& startPoint, + const Point& endPoint) throw() + : start (startPoint), + end (endPoint) + { + } /** Copies a line from another one. */ - Line& operator= (const Line& other) throw(); + Line& operator= (const Line& other) throw() + { + start = other.start; + end = other.end; + return *this; + } /** Destructor. */ - ~Line() throw(); + ~Line() throw() {} //============================================================================== /** Returns the x co-ordinate of the line's start point. */ - inline float getStartX() const throw() { return startX; } + inline ValueType getStartX() const throw() { return start.getX(); } /** Returns the y co-ordinate of the line's start point. */ - inline float getStartY() const throw() { return startY; } + inline ValueType getStartY() const throw() { return start.getY(); } /** Returns the x co-ordinate of the line's end point. */ - inline float getEndX() const throw() { return endX; } + inline ValueType getEndX() const throw() { return end.getX(); } /** Returns the y co-ordinate of the line's end point. */ - inline float getEndY() const throw() { return endY; } + inline ValueType getEndY() const throw() { return end.getY(); } /** Returns the line's start point. */ - const Point getStart() const throw(); + inline const Point& getStart() const throw() { return start; } /** Returns the line's end point. */ - const Point getEnd() const throw(); + inline const Point& getEnd() const throw() { return end; } /** Changes this line's start point */ - void setStart (float newStartX, - float newStartY) throw(); + void setStart (ValueType newStartX, ValueType newStartY) throw() { start.setXY (newStartX, newStartY); } /** Changes this line's end point */ - void setEnd (float newEndX, - float newEndY) throw(); + void setEnd (ValueType newEndX, ValueType newEndY) throw() { end.setXY (newEndX, newEndY); } /** Changes this line's start point */ - void setStart (const Point& newStart) throw(); + void setStart (const Point& newStart) throw() { start = newStart; } /** Changes this line's end point */ - void setEnd (const Point& newEnd) throw(); + void setEnd (const Point& newEnd) throw() { end = newEnd; } /** Applies an affine transform to the line's start and end points. */ - void applyTransform (const AffineTransform& transform) throw(); + void applyTransform (const AffineTransform& transform) throw() + { + start.applyTransform (transform); + end.applyTransform (transform); + } //============================================================================== /** Returns the length of the line. */ - float getLength() const throw(); + ValueType getLength() const throw() { return start.getDistanceFrom (end); } /** Returns true if the line's start and end x co-ordinates are the same. */ - bool isVertical() const throw(); + bool isVertical() const throw() { return start.getX() == end.getX(); } /** Returns true if the line's start and end y co-ordinates are the same. */ - bool isHorizontal() const throw(); + bool isHorizontal() const throw() { return start.getY() == end.getY(); } /** Returns the line's angle. This value is the number of radians clockwise from the 3 o'clock direction, where the line's start point is considered to be at the centre. */ - float getAngle() const throw(); + ValueType getAngle() const throw() { return start.getAngleToPoint (end); } //============================================================================== /** Compares two lines. */ - bool operator== (const Line& other) const throw(); + bool operator== (const Line& other) const throw() { return start == other.start && end == other.end; } /** Compares two lines. */ - bool operator!= (const Line& other) const throw(); + bool operator!= (const Line& other) const throw() { return start != other.start || end != other.end; } //============================================================================== /** Finds the intersection between two lines. @param line the other line - @param intersectionX the x co-ordinate of the point where the lines meet (or + @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. - @param intersectionY the y co-ordinate of the point where the lines meet @returns true if the line segments intersect; false if they dont. Even if they don't intersect, the intersection co-ordinates returned will still be valid */ - bool intersects (const Line& line, - float& intersectionX, - float& intersectionY) const throw(); + bool intersects (const Line& line, Point& intersection) const throw() + { + return findIntersection (start, end, line.start, line.end, intersection); + } //============================================================================== /** Returns the location of the point which is a given distance along this line. @@ -151,7 +167,10 @@ public: than the line itself @see getPointAlongLineProportionally */ - const Point getPointAlongLine (float distanceFromStart) const throw(); + const Point getPointAlongLine (ValueType distanceFromStart) const throw() + { + return start + (end - start) * (distanceFromStart / getLength()); + } /** Returns a point which is a certain distance along and to the side of this line. @@ -166,8 +185,17 @@ public: end, then a positive value here will move to the right, negative value move to the left. */ - const Point getPointAlongLine (float distanceFromStart, - float perpendicularDistance) const throw(); + const Point getPointAlongLine (ValueType distanceFromStart, + ValueType perpendicularDistance) const throw() + { + const Point delta (end - start); + const double length = juce_hypot (delta.getX(), delta.getY()); + if (length == 0) + return start; + + return Point (start.getX() + (ValueType) ((delta.getX() * distanceFromStart - delta.getY() * perpendicularDistance) / length), + start.getY() + (ValueType) ((delta.getY() * distanceFromStart + delta.getX() * perpendicularDistance) / length)); + } /** Returns the location of the point which is a given distance along this line proportional to the line's length. @@ -179,7 +207,10 @@ public: can be negative or greater than 1.0). @see getPointAlongLine */ - const Point getPointAlongLineProportionally (float proportionOfLength) const throw(); + const Point getPointAlongLineProportionally (ValueType proportionOfLength) const throw() + { + return start + (end - start) * proportionOfLength; + } /** Returns the smallest distance between this line segment and a given point. @@ -187,12 +218,26 @@ public: 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. - @param x x position of the point to test - @param y y position of the point to test @returns the point's distance from the line @see getPositionAlongLineOfNearestPoint */ - float getDistanceFromLine (float x, float y) const throw(); + ValueType getDistanceFromLine (const Point& point) const throw() + { + const Point delta (end - start); + const double length = delta.getX() * delta.getX() + delta.getY() * delta.getY(); + + if (length > 0) + { + const double prop = ((point.getX() - start.getX()) * delta.getX() + + (point.getY() - start.getY()) * delta.getY()) / length; + + if (prop >= 0 && prop <= 1.0) + return point.getDistanceFrom (start + delta * (ValueType) prop); + } + + return jmin (point.getDistanceFrom (start), + point.getDistanceFrom (end)); + } /** Finds the point on this line which is nearest to a given point, and returns its position as a proportional position along the line. @@ -204,7 +249,16 @@ public: turn this number into a position, use getPointAlongLineProportionally(). @see getDistanceFromLine, getPointAlongLineProportionally */ - float findNearestPointTo (float x, float y) const throw(); + ValueType findNearestPointTo (const Point& point) const throw() + { + const Point delta (end - start); + const double length = delta.getX() * delta.getX() + delta.getY() * delta.getY(); + + return length <= 0 ? 0 + : jlimit ((ValueType) 0, (ValueType) 1, + (ValueType) (((point.getX() - start.getX()) * delta.getX() + + (point.getY() - start.getY()) * delta.getY()) / length)); + } /** Returns true if the given point lies above this line. @@ -212,7 +266,12 @@ public: coordinate of this line at the given x (assuming the line extends infinitely in both directions). */ - bool isPointAbove (float x, float y) const throw(); + bool isPointAbove (const Point& point) const throw() + { + return start.getX() != end.getX() + && point.getY() < ((end.getY() - start.getY()) + * (point.getX() - start.getX())) / (end.getX() - start.getX()) + start.getY(); + } //============================================================================== /** Returns a shortened copy of this line. @@ -220,36 +279,86 @@ public: 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. */ - const Line withShortenedStart (float distanceToShortenBy) const throw(); + const Line withShortenedStart (ValueType distanceToShortenBy) const throw() + { + return Line (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. */ - const Line withShortenedEnd (float distanceToShortenBy) const throw(); - - /** Cuts off parts of this line to keep the parts that are either inside or - outside a path. - - Note that this isn't smart enough to cope with situations where the - line would need to be cut into multiple pieces to correctly clip against - a re-entrant shape. - - @param path the path to clip against - @param keepSectionOutsidePath if true, it's the section outside the path - that will be kept; if false its the section inside - the path - @returns true if the line was changed. - */ - bool clipToPath (const Path& path, bool keepSectionOutsidePath) throw(); + const Line withShortenedEnd (ValueType distanceToShortenBy) const throw() + { + const ValueType length = getLength(); + return Line (start, getPointAlongLine (length - jmin (distanceToShortenBy, length))); + } //============================================================================== juce_UseDebuggingNewOperator private: - float startX, startY, endX, endY; + Point start, end; + + static bool findIntersection (const Point& p1, const Point& p2, + const Point& p3, const Point& p4, + Point& intersection) throw() + { + if (p2 == p3) + { + intersection = p2; + return true; + } + + const Point d1 (p2 - p1); + const Point d2 (p4 - p2); + const ValueType divisor = d1.getX() * d2.getY() - d2.getX() * d1.getY(); + + if (divisor == 0) + { + if (! (d1.isOrigin() || d2.isOrigin())) + { + if (d1.getY() == 0 && d2.getY() != 0) + { + const ValueType along = (p1.getY() - p3.getY()) / d2.getY(); + intersection = p1.withX (p3.getX() + along * d2.getX()); + return along >= 0 && along <= (ValueType) 1; + } + else if (d2.getY() == 0 && d1.getY() != 0) + { + const ValueType along = (p3.getY() - p1.getY()) / d1.getY(); + intersection = p3.withX (p1.getX() + along * d1.getX()); + return along >= 0 && along <= (ValueType) 1; + } + else if (d1.getX() == 0 && d2.getX() != 0) + { + const ValueType along = (p1.getX() - p3.getX()) / d2.getX(); + intersection = p1.withY (p3.getY() + along * d2.getY()); + return along >= 0 && along <= (ValueType) 1; + } + else if (d2.getX() == 0 && d1.getX() != 0) + { + const ValueType along = (p3.getX() - p1.getX()) / d1.getX(); + intersection = p3.withY (p1.getY() + along * d1.getY()); + return along >= 0 && along <= (ValueType) 1; + } + } + + intersection = (p2 + p3) / (ValueType) 2; + return false; + } + + const ValueType along1 = ((p1.getY() - p3.getY()) * d2.getX() - (p1.getX() - p3.getX()) * d2.getY()) / divisor; + intersection = p1 + d1 * along1; + + if (along1 < 0 || along1 > (ValueType) 1) + return false; + + const ValueType along2 = ((p1.getY() - p3.getY()) * d1.getX() - (p1.getX() - p3.getX()) * d1.getY()) / divisor; + return along2 >= 0 && along2 <= (ValueType) 1; + } }; diff --git a/src/gui/graphics/geometry/juce_Path.cpp b/src/gui/graphics/geometry/juce_Path.cpp index 305f535af6..339151e8c8 100644 --- a/src/gui/graphics/geometry/juce_Path.cpp +++ b/src/gui/graphics/geometry/juce_Path.cpp @@ -1040,8 +1040,7 @@ bool Path::contains (const float x, const float y, const float tolerence) const while (i.next()) { - if ((i.y1 <= y && i.y2 > y) - || (i.y2 <= y && i.y1 > y)) + if ((i.y1 <= y && i.y2 > y) || (i.y2 <= y && i.y1 > y)) { const float intersectX = i.x1 + (i.x2 - i.x1) * (y - i.y1) / (i.y2 - i.y1); @@ -1055,30 +1054,58 @@ bool Path::contains (const float x, const float y, const float tolerence) const } } - return (useNonZeroWinding) ? (negativeCrossings != positiveCrossings) - : ((negativeCrossings + positiveCrossings) & 1) != 0; + return useNonZeroWinding ? (negativeCrossings != positiveCrossings) + : ((negativeCrossings + positiveCrossings) & 1) != 0; } -bool Path::intersectsLine (const float x1, const float y1, - const float x2, const float y2, - const float tolerence) +bool Path::contains (const Point& point, const float tolerence) const +{ + return contains (point.getX(), point.getY(), tolerence); +} + +bool Path::intersectsLine (const Line& line, const float tolerence) { PathFlatteningIterator i (*this, AffineTransform::identity, tolerence); - - const Line line1 (x1, y1, x2, y2); + Point intersection; while (i.next()) - { - const Line line2 (i.x1, i.y1, i.x2, i.y2); - - float ix, iy; - if (line1.intersects (line2, ix, iy)) + if (line.intersects (Line (i.x1, i.y1, i.x2, i.y2), intersection)) return true; - } return false; } +const Line Path::getClippedLine (const Line& line, const bool keepSectionOutsidePath) const +{ + Line result (line); + const bool startInside = contains (line.getStart()); + const bool endInside = contains (line.getEnd()); + + if (startInside == endInside) + { + if (keepSectionOutsidePath == startInside) + result = Line(); + } + else + { + PathFlatteningIterator i (*this, AffineTransform::identity); + Point intersection; + + while (i.next()) + { + if (line.intersects (Line (i.x1, i.y1, i.x2, i.y2), intersection)) + { + if ((startInside && keepSectionOutsidePath) || (endInside && ! keepSectionOutsidePath)) + result.setStart (intersection); + else + result.setEnd (intersection); + } + } + } + + return result; +} + //============================================================================== const Path Path::createPathWithRoundedCorners (const float cornerRadius) const { diff --git a/src/gui/graphics/geometry/juce_Path.h b/src/gui/graphics/geometry/juce_Path.h index 14f32b9242..22ea0bb9b5 100644 --- a/src/gui/graphics/geometry/juce_Path.h +++ b/src/gui/graphics/geometry/juce_Path.h @@ -27,7 +27,7 @@ #define __JUCE_PATH_JUCEHEADER__ #include "juce_AffineTransform.h" -#include "juce_Point.h" +#include "juce_Line.h" #include "juce_Rectangle.h" #include "../contexts/juce_Justification.h" #include "../contexts/juce_EdgeTable.h" @@ -114,6 +114,22 @@ public: bool contains (float x, float y, float tolerence = 10.0f) const; + /** Checks whether a point lies within the path. + + This is only relevent for closed paths (see closeSubPath()), and + may produce false results if used on a path which has open sub-paths. + + The path's winding rule is taken into account by this method. + + The tolerence parameter is passed to the PathFlatteningIterator that + is used to trace the path - for more info about it, see the notes for + the PathFlatteningIterator constructor. + + @see closeSubPath, setUsingNonZeroWinding + */ + bool contains (const Point& point, + float tolerence = 10.0f) const; + /** Checks whether a line crosses the path. This will return positive if the line crosses any of the paths constituent @@ -124,10 +140,22 @@ public: is used to trace the path - for more info about it, see the notes for the PathFlatteningIterator constructor. */ - bool intersectsLine (float x1, float y1, - float x2, float y2, + bool intersectsLine (const Line& line, float tolerence = 10.0f); + /** Cuts off parts of a line to keep the parts that are either inside or + outside this path. + + Note that this isn't smart enough to cope with situations where the + line would need to be cut into multiple pieces to correctly clip against + a re-entrant shape. + + @param keepSectionOutsidePath if true, it's the section outside the path + that will be kept; if false its the section inside + the path + */ + const Line getClippedLine (const Line& line, bool keepSectionOutsidePath) const; + //============================================================================== /** Removes all lines and curves, resetting the path completely. */ void clear() throw(); diff --git a/src/gui/graphics/geometry/juce_Point.h b/src/gui/graphics/geometry/juce_Point.h index dca9f5446e..e14beeb3a9 100644 --- a/src/gui/graphics/geometry/juce_Point.h +++ b/src/gui/graphics/geometry/juce_Point.h @@ -71,6 +71,12 @@ public: /** Returns true if the point is (0, 0). */ bool isOrigin() const throw() { return x == ValueType() && y == ValueType(); } + /** Returns a point which has the same Y position as this one, but a new X. */ + const Point withX (const ValueType newX) const throw() { return Point (newX, y); } + + /** Returns a point which has the same X position as this one, but a new Y. */ + const Point withY (const ValueType newY) const throw() { return Point (x, newY); } + /** Changes the point's x and y co-ordinates. */ void setXY (const ValueType newX, const ValueType newY) throw() { x = newX; y = newY; } @@ -89,12 +95,34 @@ public: /** Subtracts another point's co-ordinates to this one. */ Point& operator-= (const Point& other) throw() { x -= other.x; y -= other.y; return *this; } + /** Returns a point whose coordinates are multiplied by a given value. */ + const Point operator* (const ValueType multiplier) const throw() { return Point (x * multiplier, y * multiplier); } + + /** Multiplies the point's co-ordinates by a value. */ + Point& operator*= (const ValueType multiplier) throw() { x *= multiplier; y *= multiplier; return *this; } + + /** Returns a point whose coordinates are divided by a given value. */ + const Point operator/ (const ValueType divisor) const throw() { return Point (x / divisor, y / divisor); } + + /** Divides the point's co-ordinates by a value. */ + Point& operator/= (const ValueType divisor) throw() { x /= divisor; y /= divisor; return *this; } + /** Returns the inverse of this point. */ const Point operator-() const throw() { return Point (-x, -y); } + /** Returns the straight-line distance between this point and another one. */ + ValueType getDistanceFromOrigin() const throw() { return (ValueType) juce_hypot (x, y); } + /** Returns the straight-line distance between this point and another one. */ ValueType getDistanceFrom (const Point& other) const throw() { return (ValueType) juce_hypot (x - other.x, y - other.y); } + /** Returns the angle from this point to another one. + + The return value is the number of radians clockwise from the 3 o'clock direction, + where this point is the centre and the other point is on the radius. + */ + ValueType getAngleToPoint (const Point& other) const throw() { return (ValueType) atan2 (other.x - x, other.y - y); } + /** Uses a transform to change the point's co-ordinates. This will only compile if ValueType = float! @see AffineTransform::transformPoint