Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

juce_Rectangle.h 44KB

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