mirror of
https://github.com/RGBCube/serenity
synced 2025-06-01 02:38:13 +00:00
LibGfx: Add a Line class and a Rect<T>::RelativeLocation class
These helpers will be useful in preparation for supporting multiple displays, e.g. to measure distances to other screens or figure out where rectangles are located relative to each other.
This commit is contained in:
parent
20c066b8e0
commit
499c33ae0c
4 changed files with 458 additions and 0 deletions
|
@ -7,12 +7,183 @@
|
|||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Line.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibIPC/Decoder.h>
|
||||
#include <LibIPC/Encoder.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
template<typename T>
|
||||
|
||||
Rect<T>::RelativeLocation::RelativeLocation(Rect<T> const& base_rect, Rect<T> const& other_rect)
|
||||
{
|
||||
if (base_rect.is_empty() || other_rect.is_empty())
|
||||
return;
|
||||
auto parts = base_rect.shatter(other_rect);
|
||||
for (auto& part : parts) {
|
||||
if (part.x() < other_rect.x()) {
|
||||
if (part.y() < other_rect.y())
|
||||
m_top_left = true;
|
||||
if ((part.y() >= other_rect.y() && part.y() < other_rect.bottom()) || (part.y() <= other_rect.bottom() && part.bottom() > other_rect.y()))
|
||||
m_left = true;
|
||||
if (part.y() >= other_rect.bottom() || part.bottom() > other_rect.y())
|
||||
m_bottom_left = true;
|
||||
}
|
||||
if (part.x() >= other_rect.x() || part.right() > other_rect.x()) {
|
||||
if (part.y() < other_rect.y())
|
||||
m_top = true;
|
||||
if (part.y() >= other_rect.bottom() || part.bottom() > other_rect.bottom())
|
||||
m_bottom = true;
|
||||
}
|
||||
if (part.x() >= other_rect.right() || part.right() > other_rect.right()) {
|
||||
if (part.y() < other_rect.y())
|
||||
m_top_right = true;
|
||||
if ((part.y() >= other_rect.y() && part.y() < other_rect.bottom()) || (part.y() <= other_rect.bottom() && part.bottom() > other_rect.y()))
|
||||
m_right = true;
|
||||
if (part.y() >= other_rect.bottom() || part.bottom() > other_rect.y())
|
||||
m_bottom_right = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Vector<Point<T>, 2> Rect<T>::intersected(Line<T> const& line) const
|
||||
{
|
||||
if (is_empty())
|
||||
return {};
|
||||
Vector<Point<T>, 2> points;
|
||||
if (auto point = line.intersected({ top_left(), top_right() }); point.has_value())
|
||||
points.append({ point.value().x(), y() });
|
||||
if (auto point = line.intersected({ bottom_left(), bottom_right() }); point.has_value()) {
|
||||
points.append({ point.value().x(), bottom() });
|
||||
if (points.size() == 2)
|
||||
return points;
|
||||
}
|
||||
if (height() > 2) {
|
||||
if (auto point = line.intersected({ { x(), y() + 1 }, { x(), bottom() - 1 } }); point.has_value()) {
|
||||
points.append({ x(), point.value().y() });
|
||||
if (points.size() == 2)
|
||||
return points;
|
||||
}
|
||||
if (auto point = line.intersected({ { right(), y() + 1 }, { right(), bottom() - 1 } }); point.has_value())
|
||||
points.append({ right(), point.value().y() });
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
float Rect<T>::center_point_distance_to(Rect<T> const& other) const
|
||||
{
|
||||
return Line { center(), other.center() }.length();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Vector<Point<T>, 2> Rect<T>::closest_outside_center_points(Rect<T> const& other) const
|
||||
{
|
||||
if (intersects(other))
|
||||
return {};
|
||||
Line centers_line { center(), other.center() };
|
||||
auto points_this = intersected(centers_line);
|
||||
VERIFY(points_this.size() == 1);
|
||||
auto points_other = other.intersected(centers_line);
|
||||
VERIFY(points_other.size() == 1);
|
||||
return { points_this[0], points_other[0] };
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
float Rect<T>::outside_center_point_distance_to(Rect<T> const& other) const
|
||||
{
|
||||
auto points = closest_outside_center_points(other);
|
||||
if (points.is_empty())
|
||||
return 0.0;
|
||||
return Line { points[0], points[0] }.length();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Rect<T> Rect<T>::constrained_to(Rect<T> const& constrain_rect) const
|
||||
{
|
||||
if (constrain_rect.contains(*this))
|
||||
return *this;
|
||||
T move_x = 0, move_y = 0;
|
||||
if (right() > constrain_rect.right())
|
||||
move_x = constrain_rect.right() - right();
|
||||
if (bottom() > constrain_rect.bottom())
|
||||
move_y = constrain_rect.bottom() - bottom();
|
||||
if (x() < constrain_rect.x())
|
||||
move_x = x() - constrain_rect.x();
|
||||
if (y() < constrain_rect.y())
|
||||
move_y = y() - constrain_rect.y();
|
||||
auto rect = *this;
|
||||
if (move_x != 0 || move_y != 0)
|
||||
rect.translate_by(move_x, move_y);
|
||||
return rect;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Rect<T> Rect<T>::aligned_within(Size<T> const& rect_size, Point<T> const& align_at, TextAlignment alignment) const
|
||||
{
|
||||
if (rect_size.is_empty())
|
||||
return {};
|
||||
if (!size().contains(rect_size))
|
||||
return {};
|
||||
if (!contains(align_at))
|
||||
return {};
|
||||
|
||||
Rect<T> rect;
|
||||
switch (alignment) {
|
||||
case TextAlignment::TopLeft:
|
||||
rect = { align_at, rect_size };
|
||||
break;
|
||||
case TextAlignment::CenterLeft:
|
||||
rect = { { align_at.x(), align_at.y() - rect_size.height() / 2 }, rect_size };
|
||||
break;
|
||||
case TextAlignment::Center:
|
||||
rect = { { align_at.x() - rect_size.width() / 2, align_at.y() - rect_size.height() / 2 }, rect_size };
|
||||
break;
|
||||
case TextAlignment::CenterRight:
|
||||
rect = { { align_at.x() - rect_size.width() / 2, align_at.y() }, rect_size };
|
||||
break;
|
||||
case TextAlignment::TopRight:
|
||||
rect = { { align_at.x() - rect_size.width(), align_at.y() }, rect_size };
|
||||
break;
|
||||
case TextAlignment::BottomLeft:
|
||||
rect = { { align_at.x(), align_at.y() - rect_size.width() }, rect_size };
|
||||
break;
|
||||
case TextAlignment::BottomRight:
|
||||
rect = { { align_at.x() - rect_size.width(), align_at.y() - rect_size.width() }, rect_size };
|
||||
break;
|
||||
}
|
||||
return rect.constrained_to(*this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Point<T> Rect<T>::closest_to(Point<T> const& point) const
|
||||
{
|
||||
if (is_empty())
|
||||
return {};
|
||||
Optional<Point<T>> closest_point;
|
||||
float closest_distance = 0.0;
|
||||
auto check_distance = [&](const Line<T>& line) {
|
||||
auto point_on_line = line.closest_to(point);
|
||||
auto distance = Line { point_on_line, point }.length();
|
||||
if (!closest_point.has_value() || distance < closest_distance) {
|
||||
closest_point = point_on_line;
|
||||
closest_distance = distance;
|
||||
}
|
||||
};
|
||||
|
||||
check_distance({ top_left(), top_right() });
|
||||
check_distance({ bottom_left(), bottom_right() });
|
||||
if (height() > 2) {
|
||||
check_distance({ { x(), y() + 1 }, { x(), bottom() - 1 } });
|
||||
check_distance({ { right(), y() + 1 }, { right(), bottom() - 1 } });
|
||||
}
|
||||
VERIFY(closest_point.has_value());
|
||||
VERIFY(side(closest_point.value()) != Side::None);
|
||||
return closest_point.value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Rect<T>::intersect(Rect<T> const& other)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue