Line.h 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /*
  2. * Copyright (c) 2021, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/ByteString.h>
  8. #include <AK/Format.h>
  9. #include <AK/Optional.h>
  10. #include <LibGfx/Forward.h>
  11. #include <LibGfx/Point.h>
  12. #include <LibGfx/Rect.h>
  13. namespace Gfx {
  14. template<typename T>
  15. class Line {
  16. public:
  17. Line() = default;
  18. Line(Point<T> a, Point<T> b)
  19. : m_a(a)
  20. , m_b(b)
  21. {
  22. }
  23. template<typename U>
  24. Line(U a, U b)
  25. : m_a(a)
  26. , m_b(b)
  27. {
  28. }
  29. template<typename U>
  30. explicit Line(Line<U> const& other)
  31. : m_a(other.a())
  32. , m_b(other.b())
  33. {
  34. }
  35. bool intersects(Line const& other) const
  36. {
  37. return intersected(other).has_value();
  38. }
  39. Optional<Point<T>> intersected(Line const& other) const
  40. {
  41. auto cross_product = [](Point<T> const& p1, Point<T> const& p2) {
  42. return p1.x() * p2.y() - p1.y() * p2.x();
  43. };
  44. auto r = m_b - m_a;
  45. auto s = other.m_b - other.m_a;
  46. auto delta_a = other.m_a - m_a;
  47. auto num = cross_product(delta_a, r);
  48. auto denom = cross_product(r, s);
  49. if (denom == 0) {
  50. if (num == 0) {
  51. // Lines are collinear, check if line ends are touching
  52. if (m_a == other.m_a || m_a == other.m_b)
  53. return m_a;
  54. if (m_b == other.m_a || m_b == other.m_b)
  55. return m_b;
  56. // Check if they're overlapping
  57. if (!(m_b.x() - m_a.x() < 0 && m_b.x() - other.m_a.x() < 0 && other.m_b.x() - m_a.x() && other.m_b.x() - other.m_a.x())) {
  58. // Overlapping
  59. // TODO find center point?
  60. }
  61. if (!(m_b.y() - m_a.y() < 0 && m_b.y() - other.m_a.y() < 0 && other.m_b.y() - m_a.y() && other.m_b.y() - other.m_a.y())) {
  62. // Overlapping
  63. // TODO find center point?
  64. }
  65. return {};
  66. } else {
  67. // Lines are parallel and not intersecting
  68. return {};
  69. }
  70. }
  71. auto u = static_cast<float>(num) / static_cast<float>(denom);
  72. if (u < 0.0f || u > 1.0f) {
  73. // Lines are not parallel and don't intersect
  74. return {};
  75. }
  76. auto t = static_cast<float>(cross_product(delta_a, s)) / static_cast<float>(denom);
  77. if (t < 0.0f || t > 1.0f) {
  78. // Lines are not parallel and don't intersect
  79. return {};
  80. }
  81. // TODO: round if we're dealing with int
  82. return Point<T> { m_a.x() + static_cast<T>(t * r.x()), m_a.y() + static_cast<T>(t * r.y()) };
  83. }
  84. float length() const
  85. {
  86. return m_a.distance_from(m_b);
  87. }
  88. Point<T> closest_to(Point<T> const& point) const
  89. {
  90. if (m_a == m_b)
  91. return m_a;
  92. auto delta_a = point.x() - m_a.x();
  93. auto delta_b = point.y() - m_a.y();
  94. auto delta_c = m_b.x() - m_a.x();
  95. auto delta_d = m_b.y() - m_a.y();
  96. auto len_sq = delta_c * delta_c + delta_d * delta_d;
  97. float param = -1.0;
  98. if (len_sq != 0)
  99. param = static_cast<float>(delta_a * delta_c + delta_b * delta_d) / static_cast<float>(len_sq);
  100. if (param < 0)
  101. return m_a;
  102. if (param > 1)
  103. return m_b;
  104. // TODO: round if we're dealing with int
  105. return { static_cast<T>(m_a.x() + param * delta_c), static_cast<T>(m_a.y() + param * delta_d) };
  106. }
  107. Line<T> shortest_line_to(Point<T> const& point) const
  108. {
  109. return { closest_to(point), point };
  110. }
  111. float distance_to(Point<T> const& point) const
  112. {
  113. return shortest_line_to(point).length();
  114. }
  115. Point<T> const& a() const { return m_a; }
  116. Point<T> const& b() const { return m_b; }
  117. Line<T> rotated(float radians)
  118. {
  119. Gfx::AffineTransform rotation_transform;
  120. rotation_transform.rotate_radians(radians);
  121. Line<T> line = *this;
  122. line.set_a(line.a().transformed(rotation_transform));
  123. line.set_b(line.b().transformed(rotation_transform));
  124. return line;
  125. }
  126. void set_a(Point<T> const& a) { m_a = a; }
  127. void set_b(Point<T> const& b) { m_b = b; }
  128. Line<T> scaled(T sx, T sy) const
  129. {
  130. Line<T> line = *this;
  131. line.set_a(line.a().scaled(sx, sy));
  132. line.set_b(line.b().scaled(sx, sy));
  133. return line;
  134. }
  135. Line<T> translated(Point<T> const& delta) const
  136. {
  137. Line<T> line = *this;
  138. line.set_a(line.a().translated(delta));
  139. line.set_b(line.b().translated(delta));
  140. return line;
  141. }
  142. template<typename U>
  143. requires(!IsSame<T, U>)
  144. [[nodiscard]] ALWAYS_INLINE constexpr Line<U> to_type() const
  145. {
  146. return Line<U>(*this);
  147. }
  148. ByteString to_byte_string() const;
  149. private:
  150. Point<T> m_a;
  151. Point<T> m_b;
  152. };
  153. template<>
  154. inline ByteString IntLine::to_byte_string() const
  155. {
  156. return ByteString::formatted("[{},{} -> {},{}]", m_a.x(), m_a.y(), m_b.x(), m_b.y());
  157. }
  158. template<>
  159. inline ByteString FloatLine::to_byte_string() const
  160. {
  161. return ByteString::formatted("[{},{} -> {},{}]", m_a.x(), m_a.y(), m_b.x(), m_b.y());
  162. }
  163. }
  164. namespace AK {
  165. template<typename T>
  166. struct Formatter<Gfx::Line<T>> : Formatter<FormatString> {
  167. ErrorOr<void> format(FormatBuilder& builder, Gfx::Line<T> const& value)
  168. {
  169. return Formatter<FormatString>::format(builder, "[{},{} -> {},{}]"sv, value.a().x(), value.a().y(), value.b().x(), value.b().y());
  170. }
  171. };
  172. }