| 
									
										
										
										
											2021-06-13 06:13:06 -06:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2021, the SerenityOS developers. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #pragma once
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  | #include <AK/DeprecatedString.h>
 | 
					
						
							| 
									
										
										
										
											2022-10-26 20:26:14 +01:00
										 |  |  | #include <AK/Format.h>
 | 
					
						
							| 
									
										
										
										
											2021-06-13 06:13:06 -06:00
										 |  |  | #include <AK/Optional.h>
 | 
					
						
							|  |  |  | #include <LibGfx/Forward.h>
 | 
					
						
							|  |  |  | #include <LibGfx/Point.h>
 | 
					
						
							|  |  |  | #include <LibGfx/Rect.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Gfx { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template<typename T> | 
					
						
							|  |  |  | class Line { | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2022-03-14 13:26:37 -06:00
										 |  |  |     Line() = default; | 
					
						
							| 
									
										
										
										
											2021-06-13 06:13:06 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Line(Point<T> a, Point<T> b) | 
					
						
							|  |  |  |         : m_a(a) | 
					
						
							|  |  |  |         , m_b(b) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     template<typename U> | 
					
						
							|  |  |  |     Line(U a, U b) | 
					
						
							|  |  |  |         : m_a(a) | 
					
						
							|  |  |  |         , m_b(b) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     template<typename U> | 
					
						
							|  |  |  |     explicit Line(Line<U> const& other) | 
					
						
							|  |  |  |         : m_a(other.a()) | 
					
						
							|  |  |  |         , m_b(other.b()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool intersects(Line const& other) const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return intersected(other).has_value(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Optional<Point<T>> intersected(Line const& other) const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         auto cross_product = [](Point<T> const& p1, Point<T> const& p2) { | 
					
						
							|  |  |  |             return p1.x() * p2.y() - p1.y() * p2.x(); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         auto r = m_b - m_a; | 
					
						
							|  |  |  |         auto s = other.m_b - other.m_a; | 
					
						
							|  |  |  |         auto delta_a = other.m_a - m_a; | 
					
						
							|  |  |  |         auto num = cross_product(delta_a, r); | 
					
						
							|  |  |  |         auto denom = cross_product(r, s); | 
					
						
							|  |  |  |         if (denom == 0) { | 
					
						
							|  |  |  |             if (num == 0) { | 
					
						
							|  |  |  |                 // Lines are collinear, check if line ends are touching
 | 
					
						
							|  |  |  |                 if (m_a == other.m_a || m_a == other.m_b) | 
					
						
							|  |  |  |                     return m_a; | 
					
						
							|  |  |  |                 if (m_b == other.m_a || m_b == other.m_b) | 
					
						
							|  |  |  |                     return m_b; | 
					
						
							|  |  |  |                 // Check if they're overlapping
 | 
					
						
							|  |  |  |                 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())) { | 
					
						
							|  |  |  |                     // Overlapping
 | 
					
						
							|  |  |  |                     // TODO find center point?
 | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 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())) { | 
					
						
							|  |  |  |                     // Overlapping
 | 
					
						
							|  |  |  |                     // TODO find center point?
 | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return {}; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 // Lines are parallel and not intersecting
 | 
					
						
							|  |  |  |                 return {}; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         auto u = static_cast<float>(num) / static_cast<float>(denom); | 
					
						
							|  |  |  |         if (u < 0.0f || u > 1.0f) { | 
					
						
							|  |  |  |             // Lines are not parallel and don't intersect
 | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         auto t = static_cast<float>(cross_product(delta_a, s)) / static_cast<float>(denom); | 
					
						
							|  |  |  |         if (t < 0.0f || t > 1.0f) { | 
					
						
							|  |  |  |             // Lines are not parallel and don't intersect
 | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // TODO: round if we're dealing with int
 | 
					
						
							|  |  |  |         return Point<T> { m_a.x() + static_cast<T>(t * r.x()), m_a.y() + static_cast<T>(t * r.y()) }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     float length() const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return m_a.distance_from(m_b); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Point<T> closest_to(Point<T> const& point) const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (m_a == m_b) | 
					
						
							|  |  |  |             return m_a; | 
					
						
							|  |  |  |         auto delta_a = point.x() - m_a.x(); | 
					
						
							|  |  |  |         auto delta_b = point.y() - m_a.y(); | 
					
						
							|  |  |  |         auto delta_c = m_b.x() - m_a.x(); | 
					
						
							|  |  |  |         auto delta_d = m_b.y() - m_a.y(); | 
					
						
							|  |  |  |         auto len_sq = delta_c * delta_c + delta_d * delta_d; | 
					
						
							|  |  |  |         float param = -1.0; | 
					
						
							|  |  |  |         if (len_sq != 0) | 
					
						
							|  |  |  |             param = static_cast<float>(delta_a * delta_c + delta_b * delta_d) / static_cast<float>(len_sq); | 
					
						
							|  |  |  |         if (param < 0) | 
					
						
							|  |  |  |             return m_a; | 
					
						
							|  |  |  |         if (param > 1) | 
					
						
							|  |  |  |             return m_b; | 
					
						
							|  |  |  |         // TODO: round if we're dealing with int
 | 
					
						
							|  |  |  |         return { static_cast<T>(m_a.x() + param * delta_c), static_cast<T>(m_a.y() + param * delta_d) }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Line<T> shortest_line_to(Point<T> const& point) const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return { closest_to(point), point }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     float distance_to(Point<T> const& point) const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return shortest_line_to(point).length(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Point<T> const& a() const { return m_a; } | 
					
						
							|  |  |  |     Point<T> const& b() const { return m_b; } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-22 19:15:50 +01:00
										 |  |  |     Line<T> rotated(float radians) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         Gfx::AffineTransform rotation_transform; | 
					
						
							|  |  |  |         rotation_transform.rotate_radians(radians); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Line<T> line = *this; | 
					
						
							|  |  |  |         line.set_a(line.a().transformed(rotation_transform)); | 
					
						
							|  |  |  |         line.set_b(line.b().transformed(rotation_transform)); | 
					
						
							|  |  |  |         return line; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-13 06:13:06 -06:00
										 |  |  |     void set_a(Point<T> const& a) { m_a = a; } | 
					
						
							|  |  |  |     void set_b(Point<T> const& b) { m_b = b; } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-22 19:15:50 +01:00
										 |  |  |     Line<T> scaled(T sx, T sy) const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         Line<T> line = *this; | 
					
						
							|  |  |  |         line.set_a(line.a().scaled(sx, sy)); | 
					
						
							|  |  |  |         line.set_b(line.b().scaled(sx, sy)); | 
					
						
							|  |  |  |         return line; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Line<T> translated(Point<T> const& delta) const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         Line<T> line = *this; | 
					
						
							|  |  |  |         line.set_a(line.a().translated(delta)); | 
					
						
							|  |  |  |         line.set_b(line.b().translated(delta)); | 
					
						
							|  |  |  |         return line; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-24 16:04:45 +00:00
										 |  |  |     template<typename U> | 
					
						
							|  |  |  |     requires(!IsSame<T, U>) | 
					
						
							| 
									
										
										
										
											2022-10-17 00:06:11 +02:00
										 |  |  |     [[nodiscard]] ALWAYS_INLINE constexpr Line<U> to_type() const | 
					
						
							| 
									
										
										
										
											2022-11-24 16:04:45 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |         return Line<U>(*this); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  |     DeprecatedString to_deprecated_string() const; | 
					
						
							| 
									
										
										
										
											2021-06-13 06:13:06 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |     Point<T> m_a; | 
					
						
							|  |  |  |     Point<T> m_b; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template<> | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  | inline DeprecatedString IntLine::to_deprecated_string() const | 
					
						
							| 
									
										
										
										
											2021-06-13 06:13:06 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     return DeprecatedString::formatted("[{},{} -> {},{}]", m_a.x(), m_a.y(), m_b.x(), m_b.y()); | 
					
						
							| 
									
										
										
										
											2021-06-13 06:13:06 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template<> | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  | inline DeprecatedString FloatLine::to_deprecated_string() const | 
					
						
							| 
									
										
										
										
											2021-06-13 06:13:06 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     return DeprecatedString::formatted("[{},{} -> {},{}]", m_a.x(), m_a.y(), m_b.x(), m_b.y()); | 
					
						
							| 
									
										
										
										
											2021-06-13 06:13:06 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-10-26 20:26:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace AK { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template<typename T> | 
					
						
							|  |  |  | struct Formatter<Gfx::Line<T>> : Formatter<FormatString> { | 
					
						
							|  |  |  |     ErrorOr<void> format(FormatBuilder& builder, Gfx::Line<T> const& value) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return Formatter<FormatString>::format(builder, "[{},{} -> {},{}]"sv, value.a().x(), value.a().y(), value.b().x(), value.b().y()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |