Rect.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. /*
  2. * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/Format.h>
  8. #include <LibGfx/AffineTransform.h>
  9. #include <LibGfx/Orientation.h>
  10. #include <LibGfx/Point.h>
  11. #include <LibGfx/Size.h>
  12. #include <LibGfx/TextAlignment.h>
  13. #include <math.h>
  14. namespace Gfx {
  15. template<typename T>
  16. T abst(T value)
  17. {
  18. return value < 0 ? -value : value;
  19. }
  20. template<typename T>
  21. class Rect {
  22. public:
  23. Rect() = default;
  24. Rect(T x, T y, T width, T height)
  25. : m_location(x, y)
  26. , m_size(width, height)
  27. {
  28. }
  29. template<typename U>
  30. Rect(U x, U y, U width, U height)
  31. : m_location(x, y)
  32. , m_size(width, height)
  33. {
  34. }
  35. Rect(Point<T> const& location, Size<T> const& size)
  36. : m_location(location)
  37. , m_size(size)
  38. {
  39. }
  40. template<typename U>
  41. Rect(Point<U> const& location, Size<U> const& size)
  42. : m_location(location)
  43. , m_size(size)
  44. {
  45. }
  46. template<typename U>
  47. explicit Rect(Rect<U> const& other)
  48. : m_location(other.location())
  49. , m_size(other.size())
  50. {
  51. }
  52. [[nodiscard]] ALWAYS_INLINE T x() const { return location().x(); }
  53. [[nodiscard]] ALWAYS_INLINE T y() const { return location().y(); }
  54. [[nodiscard]] ALWAYS_INLINE T width() const { return m_size.width(); }
  55. [[nodiscard]] ALWAYS_INLINE T height() const { return m_size.height(); }
  56. ALWAYS_INLINE void set_x(T x) { m_location.set_x(x); }
  57. ALWAYS_INLINE void set_y(T y) { m_location.set_y(y); }
  58. ALWAYS_INLINE void set_width(T width) { m_size.set_width(width); }
  59. ALWAYS_INLINE void set_height(T height) { m_size.set_height(height); }
  60. [[nodiscard]] ALWAYS_INLINE Point<T> const& location() const { return m_location; }
  61. [[nodiscard]] ALWAYS_INLINE Size<T> const& size() const { return m_size; }
  62. [[nodiscard]] ALWAYS_INLINE bool is_null() const { return width() == 0 && height() == 0; }
  63. [[nodiscard]] ALWAYS_INLINE bool is_empty() const { return width() <= 0 || height() <= 0; }
  64. ALWAYS_INLINE void translate_by(T dx, T dy) { m_location.translate_by(dx, dy); }
  65. ALWAYS_INLINE void translate_by(T dboth) { m_location.translate_by(dboth); }
  66. ALWAYS_INLINE void translate_by(Point<T> const& delta) { m_location.translate_by(delta); }
  67. ALWAYS_INLINE void scale_by(T dx, T dy)
  68. {
  69. m_location.scale_by(dx, dy);
  70. m_size.scale_by(dx, dy);
  71. }
  72. ALWAYS_INLINE void scale_by(T dboth) { scale_by(dboth, dboth); }
  73. ALWAYS_INLINE void scale_by(Point<T> const& delta) { scale_by(delta.x(), delta.y()); }
  74. void transform_by(AffineTransform const& transform) { *this = transform.map(*this); }
  75. [[nodiscard]] Point<T> center() const
  76. {
  77. return { x() + width() / 2, y() + height() / 2 };
  78. }
  79. ALWAYS_INLINE void set_location(Point<T> const& location)
  80. {
  81. m_location = location;
  82. }
  83. ALWAYS_INLINE void set_size(Size<T> const& size)
  84. {
  85. m_size = size;
  86. }
  87. void set_size_around(Size<T> const&, Point<T> const& fixed_point);
  88. void set_size(T width, T height)
  89. {
  90. m_size.set_width(width);
  91. m_size.set_height(height);
  92. }
  93. void inflate(T w, T h)
  94. {
  95. set_x(x() - w / 2);
  96. set_width(width() + w);
  97. set_y(y() - h / 2);
  98. set_height(height() + h);
  99. }
  100. void inflate(Size<T> const& size)
  101. {
  102. set_x(x() - size.width() / 2);
  103. set_width(width() + size.width());
  104. set_y(y() - size.height() / 2);
  105. set_height(height() + size.height());
  106. }
  107. void shrink(T w, T h)
  108. {
  109. set_x(x() + w / 2);
  110. set_width(width() - w);
  111. set_y(y() + h / 2);
  112. set_height(height() - h);
  113. }
  114. void shrink(Size<T> const& size)
  115. {
  116. set_x(x() + size.width() / 2);
  117. set_width(width() - size.width());
  118. set_y(y() + size.height() / 2);
  119. set_height(height() - size.height());
  120. }
  121. [[nodiscard]] Rect<T> translated(T dx, T dy) const
  122. {
  123. Rect<T> rect = *this;
  124. rect.translate_by(dx, dy);
  125. return rect;
  126. }
  127. [[nodiscard]] Rect<T> translated(Point<T> const& delta) const
  128. {
  129. Rect<T> rect = *this;
  130. rect.translate_by(delta);
  131. return rect;
  132. }
  133. [[nodiscard]] Rect<T> scaled(T sx, T sy) const
  134. {
  135. Rect<T> rect = *this;
  136. rect.scale_by(sx, sy);
  137. return rect;
  138. }
  139. [[nodiscard]] Rect<T> scaled(Point<T> const& s) const
  140. {
  141. Rect<T> rect = *this;
  142. rect.scale_by(s);
  143. return rect;
  144. }
  145. [[nodiscard]] Rect<T> transformed(AffineTransform const& transform) const
  146. {
  147. Rect<T> rect = *this;
  148. rect.transform_by(transform);
  149. return rect;
  150. }
  151. [[nodiscard]] Rect<T> shrunken(T w, T h) const
  152. {
  153. Rect<T> rect = *this;
  154. rect.shrink(w, h);
  155. return rect;
  156. }
  157. [[nodiscard]] Rect<T> shrunken(Size<T> const& size) const
  158. {
  159. Rect<T> rect = *this;
  160. rect.shrink(size);
  161. return rect;
  162. }
  163. [[nodiscard]] Rect<T> inflated(T w, T h) const
  164. {
  165. Rect<T> rect = *this;
  166. rect.inflate(w, h);
  167. return rect;
  168. }
  169. [[nodiscard]] Rect<T> inflated(Size<T> const& size) const
  170. {
  171. Rect<T> rect = *this;
  172. rect.inflate(size);
  173. return rect;
  174. }
  175. Rect<T> take_from_right(T w)
  176. {
  177. if (w > width())
  178. w = width();
  179. Rect<T> rect = *this;
  180. set_width(width() - w);
  181. rect.set_x(x() + width());
  182. rect.set_width(w);
  183. return rect;
  184. }
  185. Rect<T> take_from_left(T w)
  186. {
  187. if (w > width())
  188. w = width();
  189. Rect<T> rect = *this;
  190. set_x(x() + w);
  191. set_width(width() - w);
  192. rect.set_width(w);
  193. return rect;
  194. }
  195. Rect<T> take_from_top(T h)
  196. {
  197. if (h > height())
  198. h = height();
  199. Rect<T> rect = *this;
  200. set_y(y() + h);
  201. set_height(height() - h);
  202. rect.set_height(h);
  203. return rect;
  204. }
  205. Rect<T> take_from_bottom(T h)
  206. {
  207. if (h > height())
  208. h = height();
  209. Rect<T> rect = *this;
  210. set_height(height() - h);
  211. rect.set_y(y() + height());
  212. rect.set_height(h);
  213. return rect;
  214. }
  215. [[nodiscard]] bool contains_vertically(T y) const
  216. {
  217. return y >= top() && y <= bottom();
  218. }
  219. [[nodiscard]] bool contains_horizontally(T x) const
  220. {
  221. return x >= left() && x <= right();
  222. }
  223. [[nodiscard]] bool contains(T x, T y) const
  224. {
  225. return x >= m_location.x() && x <= right() && y >= m_location.y() && y <= bottom();
  226. }
  227. [[nodiscard]] ALWAYS_INLINE bool contains(Point<T> const& point) const
  228. {
  229. return contains(point.x(), point.y());
  230. }
  231. [[nodiscard]] bool contains(Rect<T> const& other) const
  232. {
  233. return left() <= other.left()
  234. && right() >= other.right()
  235. && top() <= other.top()
  236. && bottom() >= other.bottom();
  237. }
  238. template<typename Container>
  239. [[nodiscard]] bool contains(Container const& others) const
  240. {
  241. bool have_any = false;
  242. for (auto const& other : others) {
  243. if (!contains(other))
  244. return false;
  245. have_any = true;
  246. }
  247. return have_any;
  248. }
  249. [[nodiscard]] ALWAYS_INLINE int primary_offset_for_orientation(Orientation orientation) const { return m_location.primary_offset_for_orientation(orientation); }
  250. ALWAYS_INLINE void set_primary_offset_for_orientation(Orientation orientation, int value) { m_location.set_primary_offset_for_orientation(orientation, value); }
  251. [[nodiscard]] ALWAYS_INLINE int secondary_offset_for_orientation(Orientation orientation) const { return m_location.secondary_offset_for_orientation(orientation); }
  252. ALWAYS_INLINE void set_secondary_offset_for_orientation(Orientation orientation, int value) { m_location.set_secondary_offset_for_orientation(orientation, value); }
  253. [[nodiscard]] ALWAYS_INLINE int primary_size_for_orientation(Orientation orientation) const { return m_size.primary_size_for_orientation(orientation); }
  254. [[nodiscard]] ALWAYS_INLINE int secondary_size_for_orientation(Orientation orientation) const { return m_size.secondary_size_for_orientation(orientation); }
  255. ALWAYS_INLINE void set_primary_size_for_orientation(Orientation orientation, int value) { m_size.set_primary_size_for_orientation(orientation, value); }
  256. ALWAYS_INLINE void set_secondary_size_for_orientation(Orientation orientation, int value) { m_size.set_secondary_size_for_orientation(orientation, value); }
  257. [[nodiscard]] T first_edge_for_orientation(Orientation orientation) const
  258. {
  259. if (orientation == Orientation::Vertical)
  260. return top();
  261. return left();
  262. }
  263. [[nodiscard]] T last_edge_for_orientation(Orientation orientation) const
  264. {
  265. if (orientation == Orientation::Vertical)
  266. return bottom();
  267. return right();
  268. }
  269. [[nodiscard]] ALWAYS_INLINE T left() const { return x(); }
  270. [[nodiscard]] ALWAYS_INLINE T right() const { return x() + width() - 1; }
  271. [[nodiscard]] ALWAYS_INLINE T top() const { return y(); }
  272. [[nodiscard]] ALWAYS_INLINE T bottom() const { return y() + height() - 1; }
  273. ALWAYS_INLINE void set_left(T left)
  274. {
  275. set_x(left);
  276. }
  277. ALWAYS_INLINE void set_top(T top)
  278. {
  279. set_y(top);
  280. }
  281. ALWAYS_INLINE void set_right(T right)
  282. {
  283. set_width(right - x() + 1);
  284. }
  285. ALWAYS_INLINE void set_bottom(T bottom)
  286. {
  287. set_height(bottom - y() + 1);
  288. }
  289. void set_right_without_resize(T new_right)
  290. {
  291. int delta = new_right - right();
  292. translate_by(delta, 0);
  293. }
  294. void set_bottom_without_resize(T new_bottom)
  295. {
  296. int delta = new_bottom - bottom();
  297. translate_by(0, delta);
  298. }
  299. [[nodiscard]] bool intersects_vertically(Rect<T> const& other) const
  300. {
  301. return top() <= other.bottom() && other.top() <= bottom();
  302. }
  303. [[nodiscard]] bool intersects_horizontally(Rect<T> const& other) const
  304. {
  305. return left() <= other.right() && other.left() <= right();
  306. }
  307. [[nodiscard]] bool intersects(Rect<T> const& other) const
  308. {
  309. return left() <= other.right()
  310. && other.left() <= right()
  311. && top() <= other.bottom()
  312. && other.top() <= bottom();
  313. }
  314. template<typename Container>
  315. [[nodiscard]] bool intersects(Container const& others) const
  316. {
  317. for (auto const& other : others) {
  318. if (intersects(other))
  319. return true;
  320. }
  321. return false;
  322. }
  323. template<typename Container, typename Function>
  324. IterationDecision for_each_intersected(Container const& others, Function f) const
  325. {
  326. if (is_empty())
  327. return IterationDecision::Continue;
  328. for (auto const& other : others) {
  329. auto intersected_rect = intersected(other);
  330. if (!intersected_rect.is_empty()) {
  331. IterationDecision decision = f(intersected_rect);
  332. if (decision != IterationDecision::Continue)
  333. return decision;
  334. }
  335. }
  336. return IterationDecision::Continue;
  337. }
  338. [[nodiscard]] Vector<Rect<T>, 4> shatter(Rect<T> const& hammer) const;
  339. template<class U>
  340. [[nodiscard]] bool operator==(Rect<U> const& other) const
  341. {
  342. return location() == other.location() && size() == other.size();
  343. }
  344. template<class U>
  345. [[nodiscard]] bool operator!=(Rect<U> const& other) const
  346. {
  347. return !(*this == other);
  348. }
  349. [[nodiscard]] Rect<T> operator*(T factor) const { return { m_location * factor, m_size * factor }; }
  350. Rect<T>& operator*=(T factor)
  351. {
  352. m_location *= factor;
  353. m_size *= factor;
  354. return *this;
  355. }
  356. void intersect(Rect<T> const&);
  357. [[nodiscard]] static Rect<T> centered_on(Point<T> const& center, Size<T> const& size)
  358. {
  359. return { { center.x() - size.width() / 2, center.y() - size.height() / 2 }, size };
  360. }
  361. [[nodiscard]] static Rect<T> from_two_points(Point<T> const& a, Point<T> const& b)
  362. {
  363. return { min(a.x(), b.x()), min(a.y(), b.y()), abst(a.x() - b.x()), abst(a.y() - b.y()) };
  364. }
  365. [[nodiscard]] static Rect<T> intersection(Rect<T> const& a, Rect<T> const& b)
  366. {
  367. Rect<T> r = a;
  368. r.intersect(b);
  369. return r;
  370. }
  371. [[nodiscard]] ALWAYS_INLINE Rect<T> intersected(Rect<T> const& other) const
  372. {
  373. return intersection(*this, other);
  374. }
  375. [[nodiscard]] Vector<Point<T>, 2> intersected(Line<T> const&) const;
  376. [[nodiscard]] float center_point_distance_to(Rect<T> const&) const;
  377. [[nodiscard]] Vector<Point<T>, 2> closest_outside_center_points(Rect<T> const&) const;
  378. [[nodiscard]] float outside_center_point_distance_to(Rect<T> const&) const;
  379. [[nodiscard]] Rect<T> constrained_to(Rect<T> const&) const;
  380. [[nodiscard]] Rect<T> aligned_within(Size<T> const&, Point<T> const&, TextAlignment = TextAlignment::Center) const;
  381. [[nodiscard]] Point<T> closest_to(Point<T> const&) const;
  382. class RelativeLocation {
  383. friend class Rect<T>;
  384. RelativeLocation(Rect<T> const& base_rect, Rect<T> const& other_rect);
  385. public:
  386. RelativeLocation() = default;
  387. bool top_left() const { return m_top_left; }
  388. bool top() const { return m_top; }
  389. bool top_right() const { return m_top_right; }
  390. bool left() const { return m_left; }
  391. bool right() const { return m_right; }
  392. bool bottom_left() const { return m_bottom_left; }
  393. bool bottom() const { return m_bottom; }
  394. bool bottom_right() const { return m_bottom_right; }
  395. bool anywhere_above() const { return m_top_left || m_top || m_top_right; }
  396. bool anywhere_below() const { return m_bottom_left || m_bottom || m_bottom_right; }
  397. bool anywhere_left() const { return m_top_left || m_left || m_bottom_left; }
  398. bool anywhere_right() const { return m_top_right || m_right || m_bottom_right; }
  399. private:
  400. bool m_top_left : 1 { false };
  401. bool m_top : 1 { false };
  402. bool m_top_right : 1 { false };
  403. bool m_left : 1 { false };
  404. bool m_right : 1 { false };
  405. bool m_bottom_left : 1 { false };
  406. bool m_bottom : 1 { false };
  407. bool m_bottom_right : 1 { false };
  408. };
  409. [[nodiscard]] RelativeLocation relative_location_to(Rect<T> const& other) const
  410. {
  411. return RelativeLocation(*this, other);
  412. }
  413. enum class Side {
  414. None = 0,
  415. Left,
  416. Top,
  417. Right,
  418. Bottom
  419. };
  420. [[nodiscard]] Side side(Point<T> const& point) const
  421. {
  422. if (is_empty())
  423. return Side::None;
  424. if (point.y() == y() || point.y() == bottom())
  425. return (point.x() >= x() && point.x() <= right()) ? (point.y() == y() ? Side::Top : Side::Bottom) : Side::None;
  426. if (point.x() == x() || point.x() == right())
  427. return (point.y() > y() && point.y() < bottom()) ? (point.x() == x() ? Side::Left : Side::Right) : Side::None;
  428. return Side::None;
  429. }
  430. [[nodiscard]] Rect<T> rect_on_side(Side side, Rect<T> const& other) const
  431. {
  432. switch (side) {
  433. case Side::None:
  434. break;
  435. case Side::Left:
  436. // Return the area in other that is to the left of this rect
  437. if (other.x() < x()) {
  438. if (other.right() >= x())
  439. return { other.location(), { x() - other.x(), other.height() } };
  440. else
  441. return other;
  442. }
  443. break;
  444. case Side::Top:
  445. // Return the area in other that is above this rect
  446. if (other.y() < y()) {
  447. if (other.bottom() >= y())
  448. return { other.location(), { other.width(), y() - other.y() } };
  449. else
  450. return other;
  451. }
  452. break;
  453. case Side::Right:
  454. // Return the area in other that is to the right of this rect
  455. if (other.right() >= x()) {
  456. if (other.x() <= right())
  457. return { { right() + 1, other.y() }, { other.width() - (right() - other.x()), other.height() } };
  458. else
  459. return other;
  460. }
  461. break;
  462. case Side::Bottom:
  463. // Return the area in other that is below this rect
  464. if (other.bottom() >= y()) {
  465. if (other.y() <= bottom())
  466. return { { other.x(), bottom() + 1 }, { other.width(), other.height() - (bottom() - other.y()) } };
  467. else
  468. return other;
  469. }
  470. break;
  471. }
  472. return {};
  473. }
  474. [[nodiscard]] bool is_adjacent(Rect<T> const& other) const
  475. {
  476. if (is_empty() || other.is_empty())
  477. return false;
  478. if (intersects(other))
  479. return false;
  480. if (other.x() + other.width() == x() || other.x() == x() + width())
  481. return max(top(), other.top()) <= min(bottom(), other.bottom());
  482. if (other.y() + other.height() == y() || other.y() == y() + height())
  483. return max(left(), other.left()) <= min(right(), other.right());
  484. return false;
  485. }
  486. [[nodiscard]] static Rect<T> centered_at(Point<T> const& point, Size<T> const& size)
  487. {
  488. return { { point.x() - size.width() / 2, point.y() - size.height() / 2 }, size };
  489. }
  490. [[nodiscard]] Rect<T> united(Rect<T> const&) const;
  491. [[nodiscard]] Point<T> top_left() const { return { left(), top() }; }
  492. [[nodiscard]] Point<T> top_right() const { return { right(), top() }; }
  493. [[nodiscard]] Point<T> bottom_left() const { return { left(), bottom() }; }
  494. [[nodiscard]] Point<T> bottom_right() const { return { right(), bottom() }; }
  495. void align_within(Rect<T> const&, TextAlignment);
  496. void center_within(Rect<T> const& other)
  497. {
  498. center_horizontally_within(other);
  499. center_vertically_within(other);
  500. }
  501. void center_horizontally_within(Rect<T> const& other)
  502. {
  503. set_x(other.center().x() - width() / 2);
  504. }
  505. void center_vertically_within(Rect<T> const& other)
  506. {
  507. set_y(other.center().y() - height() / 2);
  508. }
  509. template<typename U>
  510. [[nodiscard]] ALWAYS_INLINE Rect<U> to_type() const
  511. {
  512. return Rect<U>(*this);
  513. }
  514. [[nodiscard]] String to_string() const;
  515. private:
  516. Point<T> m_location;
  517. Size<T> m_size;
  518. };
  519. using IntRect = Rect<int>;
  520. using FloatRect = Rect<float>;
  521. [[nodiscard]] ALWAYS_INLINE IntRect enclosing_int_rect(FloatRect const& float_rect)
  522. {
  523. int x1 = floorf(float_rect.x());
  524. int y1 = floorf(float_rect.y());
  525. int x2 = ceilf(float_rect.x() + float_rect.width());
  526. int y2 = ceilf(float_rect.y() + float_rect.height());
  527. return Gfx::IntRect::from_two_points({ x1, y1 }, { x2, y2 });
  528. }
  529. }
  530. namespace AK {
  531. template<typename T>
  532. struct Formatter<Gfx::Rect<T>> : Formatter<StringView> {
  533. void format(FormatBuilder& builder, const Gfx::Rect<T>& value)
  534. {
  535. Formatter<StringView>::format(builder, value.to_string());
  536. }
  537. };
  538. }
  539. namespace IPC {
  540. bool decode(Decoder&, Gfx::IntRect&);
  541. bool encode(Encoder&, const Gfx::IntRect&);
  542. }