1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
|
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_RECTANGLE_H_INCLUDED
#define JUCE_RECTANGLE_H_INCLUDED
//==============================================================================
/**
Manages a rectangle and allows geometric operations to be performed on it.
@see RectangleList, Path, Line, Point
*/
template <typename ValueType>
class Rectangle
{
public:
//==============================================================================
/** Creates a rectangle of zero size.
The default coordinates will be (0, 0, 0, 0).
*/
Rectangle() noexcept
: w(), h()
{
}
/** Creates a copy of another rectangle. */
Rectangle (const Rectangle& other) noexcept
: pos (other.pos), w (other.w), h (other.h)
{
}
/** Creates a rectangle with a given position and size. */
Rectangle (ValueType initialX, ValueType initialY,
ValueType width, ValueType height) noexcept
: pos (initialX, initialY),
w (width), h (height)
{
}
/** Creates a rectangle with a given size, and a position of (0, 0). */
Rectangle (ValueType width, ValueType height) noexcept
: w (width), h (height)
{
}
/** Creates a Rectangle from the positions of two opposite corners. */
Rectangle (Point<ValueType> corner1, Point<ValueType> corner2) noexcept
: pos (jmin (corner1.x, corner2.x),
jmin (corner1.y, corner2.y)),
w (corner1.x - corner2.x),
h (corner1.y - corner2.y)
{
if (w < ValueType()) w = -w;
if (h < ValueType()) h = -h;
}
/** Creates a Rectangle from a set of left, right, top, bottom coordinates.
The right and bottom values must be larger than the left and top ones, or the resulting
rectangle will have a negative size.
*/
static Rectangle leftTopRightBottom (ValueType left, ValueType top,
ValueType right, ValueType bottom) noexcept
{
return Rectangle (left, top, right - left, bottom - top);
}
Rectangle& operator= (const Rectangle& other) noexcept
{
pos = other.pos;
w = other.w; h = other.h;
return *this;
}
/** Destructor. */
~Rectangle() noexcept {}
//==============================================================================
/** Returns true if the rectangle's width or height are zero or less */
bool isEmpty() const noexcept { return w <= ValueType() || h <= ValueType(); }
/** Returns true if the rectangle's values are all finite numbers, i.e. not NaN or infinity. */
inline bool isFinite() const noexcept { return pos.isFinite() && juce_isfinite(w) && juce_isfinite(h); }
/** Returns the x coordinate of the rectangle's left-hand-side. */
inline ValueType getX() const noexcept { return pos.x; }
/** Returns the y coordinate of the rectangle's top edge. */
inline ValueType getY() const noexcept { return pos.y; }
/** Returns the width of the rectangle. */
inline ValueType getWidth() const noexcept { return w; }
/** Returns the height of the rectangle. */
inline ValueType getHeight() const noexcept { return h; }
/** Returns the x coordinate of the rectangle's right-hand-side. */
inline ValueType getRight() const noexcept { return pos.x + w; }
/** Returns the y coordinate of the rectangle's bottom edge. */
inline ValueType getBottom() const noexcept { return pos.y + h; }
/** Returns the x coordinate of the rectangle's centre. */
ValueType getCentreX() const noexcept { return pos.x + w / (ValueType) 2; }
/** Returns the y coordinate of the rectangle's centre. */
ValueType getCentreY() const noexcept { return pos.y + h / (ValueType) 2; }
/** Returns the centre point of the rectangle. */
Point<ValueType> getCentre() const noexcept { return Point<ValueType> (pos.x + w / (ValueType) 2,
pos.y + h / (ValueType) 2); }
/** Returns the aspect ratio of the rectangle's width / height.
If widthOverHeight is true, it returns width / height; if widthOverHeight is false,
it returns height / width. */
ValueType getAspectRatio (bool widthOverHeight = true) const noexcept { return widthOverHeight ? w / h : h / w; }
//==============================================================================
/** Returns the rectangle's top-left position as a Point. */
inline Point<ValueType> getPosition() const noexcept { return pos; }
/** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */
inline void setPosition (Point<ValueType> newPos) noexcept { pos = newPos; }
/** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */
inline void setPosition (ValueType newX, ValueType newY) noexcept { pos.setXY (newX, newY); }
/** Returns the rectangle's top-left position as a Point. */
Point<ValueType> getTopLeft() const noexcept { return pos; }
/** Returns the rectangle's top-right position as a Point. */
Point<ValueType> getTopRight() const noexcept { return Point<ValueType> (pos.x + w, pos.y); }
/** Returns the rectangle's bottom-left position as a Point. */
Point<ValueType> getBottomLeft() const noexcept { return Point<ValueType> (pos.x, pos.y + h); }
/** Returns the rectangle's bottom-right position as a Point. */
Point<ValueType> getBottomRight() const noexcept { return Point<ValueType> (pos.x + w, pos.y + h); }
/** Returns the rectangle's left and right positions as a Range. */
Range<ValueType> getHorizontalRange() const noexcept { return Range<ValueType>::withStartAndLength (pos.x, w); }
/** Returns the rectangle's top and bottom positions as a Range. */
Range<ValueType> getVerticalRange() const noexcept { return Range<ValueType>::withStartAndLength (pos.y, h); }
/** Changes the rectangle's size, leaving the position of its top-left corner unchanged. */
void setSize (ValueType newWidth, ValueType newHeight) noexcept { w = newWidth; h = newHeight; }
/** Changes all the rectangle's coordinates. */
void setBounds (ValueType newX, ValueType newY,
ValueType newWidth, ValueType newHeight) noexcept { pos.x = newX; pos.y = newY; w = newWidth; h = newHeight; }
/** Changes the rectangle's X coordinate */
inline void setX (ValueType newX) noexcept { pos.x = newX; }
/** Changes the rectangle's Y coordinate */
inline void setY (ValueType newY) noexcept { pos.y = newY; }
/** Changes the rectangle's width */
inline void setWidth (ValueType newWidth) noexcept { w = newWidth; }
/** Changes the rectangle's height */
inline void setHeight (ValueType newHeight) noexcept { h = newHeight; }
/** Changes the position of the rectangle's centre (leaving its size unchanged). */
inline void setCentre (ValueType newCentreX, ValueType newCentreY) noexcept { pos.x = newCentreX - w / (ValueType) 2;
pos.y = newCentreY - h / (ValueType) 2; }
/** Changes the position of the rectangle's centre (leaving its size unchanged). */
inline void setCentre (Point<ValueType> newCentre) noexcept { setCentre (newCentre.x, newCentre.y); }
/** Changes the position of the rectangle's left and right edges. */
void setHorizontalRange (Range<ValueType> range) noexcept { pos.x = range.getStart(); w = range.getLength(); }
/** Changes the position of the rectangle's top and bottom edges. */
void setVerticalRange (Range<ValueType> range) noexcept { pos.y = range.getStart(); h = range.getLength(); }
/** Returns a rectangle which has the same size and y-position as this one, but with a different x-position. */
Rectangle withX (ValueType newX) const noexcept { return Rectangle (newX, pos.y, w, h); }
/** Returns a rectangle which has the same size and x-position as this one, but with a different y-position. */
Rectangle withY (ValueType newY) const noexcept { return Rectangle (pos.x, newY, w, h); }
/** Returns a rectangle with the same size as this one, but a new position. */
Rectangle withPosition (ValueType newX, ValueType newY) const noexcept { return Rectangle (newX, newY, w, h); }
/** Returns a rectangle with the same size as this one, but a new position. */
Rectangle withPosition (Point<ValueType> newPos) const noexcept { return Rectangle (newPos.x, newPos.y, w, h); }
/** Returns a rectangle whose size is the same as this one, but whose top-left position is (0, 0). */
Rectangle withZeroOrigin() const noexcept { return Rectangle (w, h); }
/** Returns a rectangle with the same size as this one, but a new centre position. */
Rectangle withCentre (Point<ValueType> newCentre) const noexcept { return Rectangle (newCentre.x - w / (ValueType) 2,
newCentre.y - h / (ValueType) 2, w, h); }
/** Returns a rectangle which has the same position and height as this one, but with a different width. */
Rectangle withWidth (ValueType newWidth) const noexcept { return Rectangle (pos.x, pos.y, newWidth, h); }
/** Returns a rectangle which has the same position and width as this one, but with a different height. */
Rectangle withHeight (ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, w, newHeight); }
/** Returns a rectangle with the same top-left position as this one, but a new size. */
Rectangle withSize (ValueType newWidth, ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, newWidth, newHeight); }
/** Returns a rectangle with the same centre position as this one, but a new size. */
Rectangle withSizeKeepingCentre (ValueType newWidth, ValueType newHeight) const noexcept { return Rectangle (pos.x + (w - newWidth) / (ValueType) 2,
pos.y + (h - newHeight) / (ValueType) 2, newWidth, newHeight); }
/** Moves the x position, adjusting the width so that the right-hand edge remains in the same place.
If the x is moved to be on the right of the current right-hand edge, the width will be set to zero.
@see withLeft
*/
void setLeft (ValueType newLeft) noexcept { w = jmax (ValueType(), pos.x + w - newLeft); pos.x = newLeft; }
/** Returns a new rectangle with a different x position, but the same right-hand edge as this one.
If the new x is beyond the right of the current right-hand edge, the width will be set to zero.
@see setLeft
*/
Rectangle withLeft (ValueType newLeft) const noexcept { return Rectangle (newLeft, pos.y, jmax (ValueType(), pos.x + w - newLeft), h); }
/** Moves the y position, adjusting the height so that the bottom edge remains in the same place.
If the y is moved to be below the current bottom edge, the height will be set to zero.
@see withTop
*/
void setTop (ValueType newTop) noexcept { h = jmax (ValueType(), pos.y + h - newTop); pos.y = newTop; }
/** Returns a new rectangle with a different y position, but the same bottom edge as this one.
If the new y is beyond the bottom of the current rectangle, the height will be set to zero.
@see setTop
*/
Rectangle withTop (ValueType newTop) const noexcept { return Rectangle (pos.x, newTop, w, jmax (ValueType(), pos.y + h - newTop)); }
/** Adjusts the width so that the right-hand edge of the rectangle has this new value.
If the new right is below the current X value, the X will be pushed down to match it.
@see getRight, withRight
*/
void setRight (ValueType newRight) noexcept { pos.x = jmin (pos.x, newRight); w = newRight - pos.x; }
/** Returns a new rectangle with a different right-hand edge position, but the same left-hand edge as this one.
If the new right edge is below the current left-hand edge, the width will be set to zero.
@see setRight
*/
Rectangle withRight (ValueType newRight) const noexcept { return Rectangle (jmin (pos.x, newRight), pos.y, jmax (ValueType(), newRight - pos.x), h); }
/** Adjusts the height so that the bottom edge of the rectangle has this new value.
If the new bottom is lower than the current Y value, the Y will be pushed down to match it.
@see getBottom, withBottom
*/
void setBottom (ValueType newBottom) noexcept { pos.y = jmin (pos.y, newBottom); h = newBottom - pos.y; }
/** Returns a new rectangle with a different bottom edge position, but the same top edge as this one.
If the new y is beyond the bottom of the current rectangle, the height will be set to zero.
@see setBottom
*/
Rectangle withBottom (ValueType newBottom) const noexcept { return Rectangle (pos.x, jmin (pos.y, newBottom), w, jmax (ValueType(), newBottom - pos.y)); }
/** Returns a version of this rectangle with the given amount removed from its left-hand edge. */
Rectangle withTrimmedLeft (ValueType amountToRemove) const noexcept { return withLeft (pos.x + amountToRemove); }
/** Returns a version of this rectangle with the given amount removed from its right-hand edge. */
Rectangle withTrimmedRight (ValueType amountToRemove) const noexcept { return withWidth (w - amountToRemove); }
/** Returns a version of this rectangle with the given amount removed from its top edge. */
Rectangle withTrimmedTop (ValueType amountToRemove) const noexcept { return withTop (pos.y + amountToRemove); }
/** Returns a version of this rectangle with the given amount removed from its bottom edge. */
Rectangle withTrimmedBottom (ValueType amountToRemove) const noexcept { return withHeight (h - amountToRemove); }
//==============================================================================
/** Moves the rectangle's position by adding amount to its x and y coordinates. */
void translate (ValueType deltaX,
ValueType deltaY) noexcept
{
pos.x += deltaX;
pos.y += deltaY;
}
/** Returns a rectangle which is the same as this one moved by a given amount. */
Rectangle translated (ValueType deltaX,
ValueType deltaY) const noexcept
{
return Rectangle (pos.x + deltaX, pos.y + deltaY, w, h);
}
/** Returns a rectangle which is the same as this one moved by a given amount. */
Rectangle operator+ (Point<ValueType> deltaPosition) const noexcept
{
return Rectangle (pos.x + deltaPosition.x, pos.y + deltaPosition.y, w, h);
}
/** Moves this rectangle by a given amount. */
Rectangle& operator+= (Point<ValueType> deltaPosition) noexcept
{
pos += deltaPosition;
return *this;
}
/** Returns a rectangle which is the same as this one moved by a given amount. */
Rectangle operator- (Point<ValueType> deltaPosition) const noexcept
{
return Rectangle (pos.x - deltaPosition.x, pos.y - deltaPosition.y, w, h);
}
/** Moves this rectangle by a given amount. */
Rectangle& operator-= (Point<ValueType> deltaPosition) noexcept
{
pos -= deltaPosition;
return *this;
}
/** Returns a rectangle that has been scaled by the given amount, centred around the origin.
Note that if the rectangle has int coordinates and it's scaled by a
floating-point amount, then the result will be converted back to integer
coordinates using getSmallestIntegerContainer().
*/
template <typename FloatType>
Rectangle operator* (FloatType scaleFactor) const noexcept
{
Rectangle r (*this);
r *= scaleFactor;
return r;
}
/** Scales this rectangle by the given amount, centred around the origin.
Note that if the rectangle has int coordinates and it's scaled by a
floating-point amount, then the result will be converted back to integer
coordinates using getSmallestIntegerContainer().
*/
template <typename FloatType>
Rectangle operator*= (FloatType scaleFactor) noexcept
{
Rectangle<FloatType> (pos.x * scaleFactor,
pos.y * scaleFactor,
w * scaleFactor,
h * scaleFactor).copyWithRounding (*this);
return *this;
}
/** Scales this rectangle by the given X and Y factors, centred around the origin.
Note that if the rectangle has int coordinates and it's scaled by a
floating-point amount, then the result will be converted back to integer
coordinates using getSmallestIntegerContainer().
*/
template <typename FloatType>
Rectangle operator*= (Point<FloatType> scaleFactor) noexcept
{
Rectangle<FloatType> (pos.x * scaleFactor.x,
pos.y * scaleFactor.y,
w * scaleFactor.x,
h * scaleFactor.y).copyWithRounding (*this);
return *this;
}
/** Scales this rectangle by the given amount, centred around the origin. */
template <typename FloatType>
Rectangle operator/ (FloatType scaleFactor) const noexcept
{
Rectangle r (*this);
r /= scaleFactor;
return r;
}
/** Scales this rectangle by the given amount, centred around the origin. */
template <typename FloatType>
Rectangle operator/= (FloatType scaleFactor) noexcept
{
Rectangle<FloatType> (pos.x / scaleFactor,
pos.y / scaleFactor,
w / scaleFactor,
h / scaleFactor).copyWithRounding (*this);
return *this;
}
/** Scales this rectangle by the given X and Y factors, centred around the origin. */
template <typename FloatType>
Rectangle operator/= (Point<FloatType> scaleFactor) noexcept
{
Rectangle<FloatType> (pos.x / scaleFactor.x,
pos.y / scaleFactor.y,
w / scaleFactor.x,
h / scaleFactor.y).copyWithRounding (*this);
return *this;
}
/** Expands the rectangle by a given amount.
Effectively, its new size is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2).
@see expanded, reduce, reduced
*/
void expand (ValueType deltaX,
ValueType deltaY) noexcept
{
const ValueType nw = jmax (ValueType(), w + deltaX * 2);
const ValueType nh = jmax (ValueType(), h + deltaY * 2);
setBounds (pos.x - deltaX, pos.y - deltaY, nw, nh);
}
/** Returns a rectangle that is larger than this one by a given amount.
Effectively, the rectangle returned is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2).
@see expand, reduce, reduced
*/
Rectangle expanded (ValueType deltaX,
ValueType deltaY) const noexcept
{
const ValueType nw = jmax (ValueType(), w + deltaX * 2);
const ValueType nh = jmax (ValueType(), h + deltaY * 2);
return Rectangle (pos.x - deltaX, pos.y - deltaY, nw, nh);
}
/** Returns a rectangle that is larger than this one by a given amount.
Effectively, the rectangle returned is (x - delta, y - delta, w + delta * 2, h + delta * 2).
@see expand, reduce, reduced
*/
Rectangle expanded (ValueType delta) const noexcept
{
return expanded (delta, delta);
}
/** Shrinks the rectangle by a given amount.
Effectively, its new size is (x + deltaX, y + deltaY, w - deltaX * 2, h - deltaY * 2).
@see reduced, expand, expanded
*/
void reduce (ValueType deltaX,
ValueType deltaY) noexcept
{
expand (-deltaX, -deltaY);
}
/** Returns a rectangle that is smaller than this one by a given amount.
Effectively, the rectangle returned is (x + deltaX, y + deltaY, w - deltaX * 2, h - deltaY * 2).
@see reduce, expand, expanded
*/
Rectangle reduced (ValueType deltaX,
ValueType deltaY) const noexcept
{
return expanded (-deltaX, -deltaY);
}
/** Returns a rectangle that is smaller than this one by a given amount.
Effectively, the rectangle returned is (x + delta, y + delta, w - delta * 2, h - delta * 2).
@see reduce, expand, expanded
*/
Rectangle reduced (ValueType delta) const noexcept
{
return reduced (delta, delta);
}
/** Removes a strip from the top of this rectangle, reducing this rectangle
by the specified amount and returning the section that was removed.
E.g. if this rectangle is (100, 100, 300, 300) and amountToRemove is 50, this will
return (100, 100, 300, 50) and leave this rectangle as (100, 150, 300, 250).
If amountToRemove is greater than the height of this rectangle, it'll be clipped to
that value.
*/
Rectangle removeFromTop (ValueType amountToRemove) noexcept
{
const Rectangle r (pos.x, pos.y, w, jmin (amountToRemove, h));
pos.y += r.h; h -= r.h;
return r;
}
/** Removes a strip from the left-hand edge of this rectangle, reducing this rectangle
by the specified amount and returning the section that was removed.
E.g. if this rectangle is (100, 100, 300, 300) and amountToRemove is 50, this will
return (100, 100, 50, 300) and leave this rectangle as (150, 100, 250, 300).
If amountToRemove is greater than the width of this rectangle, it'll be clipped to
that value.
*/
Rectangle removeFromLeft (ValueType amountToRemove) noexcept
{
const Rectangle r (pos.x, pos.y, jmin (amountToRemove, w), h);
pos.x += r.w; w -= r.w;
return r;
}
/** Removes a strip from the right-hand edge of this rectangle, reducing this rectangle
by the specified amount and returning the section that was removed.
E.g. if this rectangle is (100, 100, 300, 300) and amountToRemove is 50, this will
return (250, 100, 50, 300) and leave this rectangle as (100, 100, 250, 300).
If amountToRemove is greater than the width of this rectangle, it'll be clipped to
that value.
*/
Rectangle removeFromRight (ValueType amountToRemove) noexcept
{
amountToRemove = jmin (amountToRemove, w);
const Rectangle r (pos.x + w - amountToRemove, pos.y, amountToRemove, h);
w -= amountToRemove;
return r;
}
/** Removes a strip from the bottom of this rectangle, reducing this rectangle
by the specified amount and returning the section that was removed.
E.g. if this rectangle is (100, 100, 300, 300) and amountToRemove is 50, this will
return (100, 250, 300, 50) and leave this rectangle as (100, 100, 300, 250).
If amountToRemove is greater than the height of this rectangle, it'll be clipped to
that value.
*/
Rectangle removeFromBottom (ValueType amountToRemove) noexcept
{
amountToRemove = jmin (amountToRemove, h);
const Rectangle r (pos.x, pos.y + h - amountToRemove, w, amountToRemove);
h -= amountToRemove;
return r;
}
//==============================================================================
/** Returns true if the two rectangles are identical. */
bool operator== (const Rectangle& other) const noexcept { return pos == other.pos && w == other.w && h == other.h; }
/** Returns true if the two rectangles are not identical. */
bool operator!= (const Rectangle& other) const noexcept { return pos != other.pos || w != other.w || h != other.h; }
/** Returns true if this coordinate is inside the rectangle. */
bool contains (ValueType xCoord, ValueType yCoord) const noexcept
{
return xCoord >= pos.x && yCoord >= pos.y && xCoord < pos.x + w && yCoord < pos.y + h;
}
/** Returns true if this coordinate is inside the rectangle. */
bool contains (Point<ValueType> point) const noexcept
{
return point.x >= pos.x && point.y >= pos.y && point.x < pos.x + w && point.y < pos.y + h;
}
/** Returns true if this other rectangle is completely inside this one. */
bool contains (const Rectangle& other) const noexcept
{
return pos.x <= other.pos.x && pos.y <= other.pos.y
&& pos.x + w >= other.pos.x + other.w && pos.y + h >= other.pos.y + other.h;
}
/** Returns the nearest point to the specified point that lies within this rectangle. */
Point<ValueType> getConstrainedPoint (Point<ValueType> point) const noexcept
{
return Point<ValueType> (jlimit (pos.x, pos.x + w, point.x),
jlimit (pos.y, pos.y + h, point.y));
}
/** Returns a point within this rectangle, specified as proportional coordinates.
The relative X and Y values should be between 0 and 1, where 0 is the left or
top of this rectangle, and 1 is the right or bottom. (Out-of-bounds values
will return a point outside the rectangle).
*/
Point<ValueType> getRelativePoint (double relativeX, double relativeY) const noexcept
{
return Point<ValueType> (pos.x + static_cast <ValueType> (w * relativeX),
pos.y + static_cast <ValueType> (h * relativeY));
}
/** Returns true if any part of another rectangle overlaps this one. */
bool intersects (const Rectangle& other) const noexcept
{
return pos.x + w > other.pos.x
&& pos.y + h > other.pos.y
&& pos.x < other.pos.x + other.w
&& pos.y < other.pos.y + other.h
&& w > ValueType() && h > ValueType()
&& other.w > ValueType() && other.h > ValueType();
}
/** Returns true if any part of the given line lies inside this rectangle. */
bool intersects (const Line<ValueType>& line) const noexcept
{
return contains (line.getStart()) || contains (line.getEnd())
|| line.intersects (Line<ValueType> (getTopLeft(), getTopRight()))
|| line.intersects (Line<ValueType> (getTopRight(), getBottomRight()))
|| line.intersects (Line<ValueType> (getBottomRight(), getBottomLeft()))
|| line.intersects (Line<ValueType> (getBottomLeft(), getTopLeft()));
}
/** Returns the region that is the overlap between this and another rectangle.
If the two rectangles don't overlap, the rectangle returned will be empty.
*/
Rectangle getIntersection (const Rectangle& other) const noexcept
{
const ValueType nx = jmax (pos.x, other.pos.x);
const ValueType ny = jmax (pos.y, other.pos.y);
const ValueType nw = jmin (pos.x + w, other.pos.x + other.w) - nx;
const ValueType nh = jmin (pos.y + h, other.pos.y + other.h) - ny;
if (nw >= ValueType() && nh >= ValueType())
return Rectangle (nx, ny, nw, nh);
return Rectangle();
}
/** Clips a set of rectangle coordinates so that they lie only within this one.
This is a non-static version of intersectRectangles().
Returns false if the two rectangles didn't overlap.
*/
bool intersectRectangle (ValueType& otherX, ValueType& otherY, ValueType& otherW, ValueType& otherH) const noexcept
{
const ValueType maxX (jmax (otherX, pos.x));
otherW = jmin (otherX + otherW, pos.x + w) - maxX;
if (otherW > ValueType())
{
const ValueType maxY (jmax (otherY, pos.y));
otherH = jmin (otherY + otherH, pos.y + h) - maxY;
if (otherH > ValueType())
{
otherX = maxX; otherY = maxY;
return true;
}
}
return false;
}
/** Clips a rectangle so that it lies only within this one.
Returns false if the two rectangles didn't overlap.
*/
bool intersectRectangle (Rectangle<ValueType>& rectangleToClip) const noexcept
{
return intersectRectangle (rectangleToClip.pos.x, rectangleToClip.pos.y,
rectangleToClip.w, rectangleToClip.h);
}
/** Returns the smallest rectangle that contains both this one and the one passed-in.
If either this or the other rectangle are empty, they will not be counted as
part of the resulting region.
*/
Rectangle getUnion (const Rectangle& other) const noexcept
{
if (other.isEmpty()) return *this;
if (isEmpty()) return other;
const ValueType newX = jmin (pos.x, other.pos.x);
const ValueType newY = jmin (pos.y, other.pos.y);
return Rectangle (newX, newY,
jmax (pos.x + w, other.pos.x + other.w) - newX,
jmax (pos.y + h, other.pos.y + other.h) - newY);
}
/** If this rectangle merged with another one results in a simple rectangle, this
will set this rectangle to the result, and return true.
Returns false and does nothing to this rectangle if the two rectangles don't overlap,
or if they form a complex region.
*/
bool enlargeIfAdjacent (const Rectangle& other) noexcept
{
if (pos.x == other.pos.x && getRight() == other.getRight()
&& (other.getBottom() >= pos.y && other.pos.y <= getBottom()))
{
const ValueType newY = jmin (pos.y, other.pos.y);
h = jmax (getBottom(), other.getBottom()) - newY;
pos.y = newY;
return true;
}
if (pos.y == other.pos.y && getBottom() == other.getBottom()
&& (other.getRight() >= pos.x && other.pos.x <= getRight()))
{
const ValueType newX = jmin (pos.x, other.pos.x);
w = jmax (getRight(), other.getRight()) - newX;
pos.x = newX;
return true;
}
return false;
}
/** If after removing another rectangle from this one the result is a simple rectangle,
this will set this object's bounds to be the result, and return true.
Returns false and does nothing to this rectangle if the two rectangles don't overlap,
or if removing the other one would form a complex region.
*/
bool reduceIfPartlyContainedIn (const Rectangle& other) noexcept
{
int inside = 0;
const ValueType otherR (other.getRight());
if (pos.x >= other.pos.x && pos.x < otherR) inside = 1;
const ValueType otherB (other.getBottom());
if (pos.y >= other.pos.y && pos.y < otherB) inside |= 2;
const ValueType r (pos.x + w);
if (r >= other.pos.x && r < otherR) inside |= 4;
const ValueType b (pos.y + h);
if (b >= other.pos.y && b < otherB) inside |= 8;
switch (inside)
{
case 1 + 2 + 8: w = r - otherR; pos.x = otherR; return true;
case 1 + 2 + 4: h = b - otherB; pos.y = otherB; return true;
case 2 + 4 + 8: w = other.pos.x - pos.x; return true;
case 1 + 4 + 8: h = other.pos.y - pos.y; return true;
}
return false;
}
/** Tries to fit this rectangle within a target area, returning the result.
If this rectangle is not completely inside the target area, then it'll be
shifted (without changing its size) so that it lies within the target. If it
is larger than the target rectangle in either dimension, then that dimension
will be reduced to fit within the target.
*/
Rectangle constrainedWithin (const Rectangle& areaToFitWithin) const noexcept
{
const ValueType newW (jmin (w, areaToFitWithin.getWidth()));
const ValueType newH (jmin (h, areaToFitWithin.getHeight()));
return Rectangle (jlimit (areaToFitWithin.getX(), areaToFitWithin.getRight() - newW, pos.x),
jlimit (areaToFitWithin.getY(), areaToFitWithin.getBottom() - newH, pos.y),
newW, newH);
}
/** Returns the smallest rectangle that can contain the shape created by applying
a transform to this rectangle.
This should only be used on floating point rectangles.
*/
Rectangle transformedBy (const AffineTransform& transform) const noexcept
{
typedef typename TypeHelpers::SmallestFloatType<ValueType>::type FloatType;
FloatType x1 = static_cast<FloatType> (pos.x), y1 = static_cast<FloatType> (pos.y);
FloatType x2 = static_cast<FloatType> (pos.x + w), y2 = static_cast<FloatType> (pos.y);
FloatType x3 = static_cast<FloatType> (pos.x), y3 = static_cast<FloatType> (pos.y + h);
FloatType x4 = static_cast<FloatType> (x2), y4 = static_cast<FloatType> (y3);
transform.transformPoints (x1, y1, x2, y2);
transform.transformPoints (x3, y3, x4, y4);
const FloatType rx1 = jmin (x1, x2, x3, x4);
const FloatType rx2 = jmax (x1, x2, x3, x4);
const FloatType ry1 = jmin (y1, y2, y3, y4);
const FloatType ry2 = jmax (y1, y2, y3, y4);
Rectangle r;
Rectangle<FloatType> (rx1, ry1, rx2 - rx1, ry2 - ry1).copyWithRounding (r);
return r;
}
/** Returns the smallest integer-aligned rectangle that completely contains this one.
This is only relevent for floating-point rectangles, of course.
@see toFloat()
*/
Rectangle<int> getSmallestIntegerContainer() const noexcept
{
const int x1 = floorAsInt (pos.x);
const int y1 = floorAsInt (pos.y);
const int x2 = ceilAsInt (pos.x + w);
const int y2 = ceilAsInt (pos.y + h);
return Rectangle<int> (x1, y1, x2 - x1, y2 - y1);
}
/** Casts this rectangle to a Rectangle<float>.
@see getSmallestIntegerContainer
*/
Rectangle<float> toFloat() const noexcept
{
return Rectangle<float> (static_cast<float> (pos.x), static_cast<float> (pos.y),
static_cast<float> (w), static_cast<float> (h));
}
/** Casts this rectangle to a Rectangle<double>.
@see getSmallestIntegerContainer
*/
Rectangle<double> toDouble() const noexcept
{
return Rectangle<double> (static_cast<double> (pos.x), static_cast<double> (pos.y),
static_cast<double> (w), static_cast<double> (h));
}
/** Casts this rectangle to a Rectangle with the given type.
If the target type is a conversion from float to int, then the conversion
will be done using getSmallestIntegerContainer().
*/
template <typename TargetType>
Rectangle<TargetType> toType() const noexcept
{
Rectangle<TargetType> r;
copyWithRounding (r);
return r;
}
/** Returns the smallest Rectangle that can contain a set of points. */
static Rectangle findAreaContainingPoints (const Point<ValueType>* const points, const int numPoints) noexcept
{
if (numPoints == 0)
return Rectangle();
ValueType minX (points[0].x);
ValueType maxX (minX);
ValueType minY (points[0].y);
ValueType maxY (minY);
for (int i = 1; i < numPoints; ++i)
{
minX = jmin (minX, points[i].x);
maxX = jmax (maxX, points[i].x);
minY = jmin (minY, points[i].y);
maxY = jmax (maxY, points[i].y);
}
return Rectangle (minX, minY, maxX - minX, maxY - minY);
}
//==============================================================================
/** Static utility to intersect two sets of rectangular coordinates.
Returns false if the two regions didn't overlap.
@see intersectRectangle
*/
static bool intersectRectangles (ValueType& x1, ValueType& y1, ValueType& w1, ValueType& h1,
const ValueType x2, const ValueType y2, const ValueType w2, const ValueType h2) noexcept
{
const ValueType x (jmax (x1, x2));
w1 = jmin (x1 + w1, x2 + w2) - x;
if (w1 > ValueType())
{
const ValueType y (jmax (y1, y2));
h1 = jmin (y1 + h1, y2 + h2) - y;
if (h1 > ValueType())
{
x1 = x; y1 = y;
return true;
}
}
return false;
}
//==============================================================================
/** Creates a string describing this rectangle.
The string will be of the form "x y width height", e.g. "100 100 400 200".
Coupled with the fromString() method, this is very handy for things like
storing rectangles (particularly component positions) in XML attributes.
@see fromString
*/
String toString() const
{
String s;
s.preallocateBytes (32);
s << pos.x << ' ' << pos.y << ' ' << w << ' ' << h;
return s;
}
/** Parses a string containing a rectangle's details.
The string should contain 4 integer tokens, in the form "x y width height". They
can be comma or whitespace separated.
This method is intended to go with the toString() method, to form an easy way
of saving/loading rectangles as strings.
@see toString
*/
static Rectangle fromString (StringRef stringVersion)
{
StringArray toks;
toks.addTokens (stringVersion.text.findEndOfWhitespace(), ",; \t\r\n", "");
return Rectangle (parseIntAfterSpace (toks[0]),
parseIntAfterSpace (toks[1]),
parseIntAfterSpace (toks[2]),
parseIntAfterSpace (toks[3]));
}
#ifndef DOXYGEN
// This has been renamed by transformedBy, in order to match the method names used in the Point class.
JUCE_DEPRECATED_WITH_BODY (Rectangle transformed (const AffineTransform& t) const noexcept, { return transformedBy (t); })
#endif
private:
template <typename OtherType> friend class Rectangle;
Point<ValueType> pos;
ValueType w, h;
static int parseIntAfterSpace (StringRef s) noexcept
{ return s.text.findEndOfWhitespace().getIntValue32(); }
void copyWithRounding (Rectangle<int>& result) const noexcept { result = getSmallestIntegerContainer(); }
void copyWithRounding (Rectangle<float>& result) const noexcept { result = toFloat(); }
void copyWithRounding (Rectangle<double>& result) const noexcept { result = toDouble(); }
static int floorAsInt (int n) noexcept { return n; }
static int floorAsInt (float n) noexcept { return (int) std::floor (n); }
static int floorAsInt (double n) noexcept { return (int) std::floor (n); }
static int ceilAsInt (int n) noexcept { return n; }
static int ceilAsInt (float n) noexcept { return (int) std::ceil (n); }
static int ceilAsInt (double n) noexcept { return (int) std::ceil (n); }
};
#endif // JUCE_RECTANGLE_H_INCLUDED
|