mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibGfx+LibWeb: Delete unused Line class and Rect methods
This commit is contained in:
parent
aa5b3ff95a
commit
ed921b66f5
Notes:
github-actions[bot]
2025-11-04 22:17:01 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: ed921b66f5
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6697
8 changed files with 4 additions and 597 deletions
|
|
@ -19,9 +19,6 @@ class GlyphRun;
|
|||
class ImageDecoder;
|
||||
struct FontPixelMetrics;
|
||||
|
||||
template<typename T>
|
||||
class Line;
|
||||
|
||||
class Painter;
|
||||
class PaintingSurface;
|
||||
class Palette;
|
||||
|
|
@ -46,9 +43,6 @@ class Rect;
|
|||
template<typename T>
|
||||
class Quad;
|
||||
|
||||
using IntLine = Line<int>;
|
||||
using FloatLine = Line<float>;
|
||||
|
||||
using IntRect = Rect<int>;
|
||||
using FloatRect = Rect<float>;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibGfx/ImageFormats/TinyVGLoader.h>
|
||||
#include <LibGfx/Line.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <LibGfx/Path.h>
|
||||
#include <LibGfx/Point.h>
|
||||
|
|
@ -249,11 +248,6 @@ public:
|
|||
return FloatRect { TRY(read_unit()), TRY(read_unit()), TRY(read_unit()), TRY(read_unit()) };
|
||||
}
|
||||
|
||||
ErrorOr<FloatLine> read_line()
|
||||
{
|
||||
return FloatLine { TRY(read_point()), TRY(read_point()) };
|
||||
}
|
||||
|
||||
ErrorOr<Path> read_path(u32 segment_count)
|
||||
{
|
||||
Path path;
|
||||
|
|
@ -414,9 +408,8 @@ ErrorOr<NonnullRefPtr<TinyVGDecodedImageData>> TinyVGDecodedImageData::decode(St
|
|||
auto header = TRY(reader.read_draw_command_header(style_type));
|
||||
Path path;
|
||||
for (u32 i = 0; i < header.count; i++) {
|
||||
auto line = TRY(reader.read_line());
|
||||
path.move_to(line.a());
|
||||
path.line_to(line.b());
|
||||
path.move_to(TRY(reader.read_point()));
|
||||
path.line_to(TRY(reader.read_point()));
|
||||
}
|
||||
TRY(draw_commands.try_append(DrawCommand { move(path), {}, move(header.line_style), header.line_width }));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,199 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Point.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
template<typename T>
|
||||
class Line {
|
||||
public:
|
||||
Line() = default;
|
||||
|
||||
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; }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void set_a(Point<T> const& a) { m_a = a; }
|
||||
void set_b(Point<T> const& b) { m_b = b; }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
requires(!IsSame<T, U>)
|
||||
[[nodiscard]] ALWAYS_INLINE constexpr Line<U> to_type() const
|
||||
{
|
||||
return Line<U>(*this);
|
||||
}
|
||||
|
||||
ByteString to_byte_string() const;
|
||||
|
||||
private:
|
||||
Point<T> m_a;
|
||||
Point<T> m_b;
|
||||
};
|
||||
|
||||
template<>
|
||||
inline ByteString IntLine::to_byte_string() const
|
||||
{
|
||||
return ByteString::formatted("[{},{} -> {},{}]", m_a.x(), m_a.y(), m_b.x(), m_b.y());
|
||||
}
|
||||
|
||||
template<>
|
||||
inline ByteString FloatLine::to_byte_string() const
|
||||
{
|
||||
return ByteString::formatted("[{},{} -> {},{}]", m_a.x(), m_a.y(), m_b.x(), m_b.y());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@
|
|||
#include <AK/Format.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/AffineTransform.h>
|
||||
#include <LibGfx/Line.h>
|
||||
#include <LibGfx/Orientation.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGfx/Size.h>
|
||||
|
|
@ -411,65 +410,6 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
template<typename Container, typename Function>
|
||||
IterationDecision for_each_intersected(Container const& others, Function f) const
|
||||
{
|
||||
if (is_empty())
|
||||
return IterationDecision::Continue;
|
||||
for (auto const& other : others) {
|
||||
auto intersected_rect = intersected(other);
|
||||
if (!intersected_rect.is_empty()) {
|
||||
IterationDecision decision = f(intersected_rect);
|
||||
if (decision != IterationDecision::Continue)
|
||||
return decision;
|
||||
}
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
[[nodiscard]] Vector<Rect<T>, 4> shatter(Rect<T> const& hammer) const
|
||||
{
|
||||
Vector<Rect<T>, 4> pieces;
|
||||
if (!intersects(hammer)) {
|
||||
pieces.unchecked_append(*this);
|
||||
return pieces;
|
||||
}
|
||||
Rect<T> top_shard {
|
||||
x(),
|
||||
y(),
|
||||
width(),
|
||||
hammer.y() - y(),
|
||||
};
|
||||
Rect<T> bottom_shard {
|
||||
x(),
|
||||
hammer.bottom(),
|
||||
width(),
|
||||
bottom() - hammer.bottom(),
|
||||
};
|
||||
Rect<T> left_shard {
|
||||
x(),
|
||||
max(hammer.y(), y()),
|
||||
hammer.x() - x(),
|
||||
min(hammer.bottom(), bottom()) - max(hammer.y(), y()),
|
||||
};
|
||||
Rect<T> right_shard {
|
||||
hammer.right(),
|
||||
max(hammer.y(), y()),
|
||||
right() - hammer.right(),
|
||||
min(hammer.bottom(), bottom()) - max(hammer.y(), y()),
|
||||
};
|
||||
if (!top_shard.is_empty())
|
||||
pieces.unchecked_append(top_shard);
|
||||
if (!bottom_shard.is_empty())
|
||||
pieces.unchecked_append(bottom_shard);
|
||||
if (!left_shard.is_empty())
|
||||
pieces.unchecked_append(left_shard);
|
||||
if (!right_shard.is_empty())
|
||||
pieces.unchecked_append(right_shard);
|
||||
|
||||
return pieces;
|
||||
}
|
||||
|
||||
template<class U>
|
||||
[[nodiscard]] bool operator==(Rect<U> const& other) const
|
||||
{
|
||||
|
|
@ -526,30 +466,6 @@ public:
|
|||
return intersection(*this, other);
|
||||
}
|
||||
|
||||
[[nodiscard]] Vector<Point<T>, 2> 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() - 1 });
|
||||
if (points.size() == 2)
|
||||
return points;
|
||||
}
|
||||
if (height() > 2) {
|
||||
if (auto point = line.intersected({ { x(), y() + 1 }, { x(), bottom() - 2 } }); point.has_value()) {
|
||||
points.append({ x(), point.value().y() });
|
||||
if (points.size() == 2)
|
||||
return points;
|
||||
}
|
||||
if (auto point = line.intersected({ { right() - 1, y() + 1 }, { right() - 1, bottom() - 2 } }); point.has_value())
|
||||
points.append({ right() - 1, point.value().y() });
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
[[nodiscard]] Gfx::Rect<U> interpolated_to(Gfx::Rect<T> const& to, float factor) const
|
||||
{
|
||||
|
|
@ -568,274 +484,6 @@ public:
|
|||
return { interpolated_left, interpolated_top, interpolated_right - interpolated_left, interpolated_bottom - interpolated_top };
|
||||
}
|
||||
|
||||
[[nodiscard]] float center_point_distance_to(Rect<T> const& other) const
|
||||
{
|
||||
return Line { center(), other.center() }.length();
|
||||
}
|
||||
|
||||
[[nodiscard]] Vector<Point<T>, 2> 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] };
|
||||
}
|
||||
|
||||
[[nodiscard]] float outside_center_point_distance_to(Rect<T> const& other) const
|
||||
{
|
||||
auto points = closest_outside_center_points(other);
|
||||
if (points.is_empty())
|
||||
return 0.f;
|
||||
return Line { points[0], points[0] }.length();
|
||||
}
|
||||
|
||||
[[nodiscard]] 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 = constrain_rect.x() - x();
|
||||
if (y() < constrain_rect.y())
|
||||
move_y = constrain_rect.y() - y();
|
||||
auto rect = *this;
|
||||
if (move_x != 0 || move_y != 0)
|
||||
rect.translate_by(move_x, move_y);
|
||||
return rect;
|
||||
}
|
||||
|
||||
[[nodiscard]] Point<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 = [&](Line<T> const& 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().moved_left(1) });
|
||||
check_distance({ bottom_left().moved_up(1), bottom_right().translated(-1) });
|
||||
if (height() > 2) {
|
||||
check_distance({ { x(), y() + 1 }, { x(), bottom() - 2 } });
|
||||
check_distance({ { right() - 1, y() + 1 }, { right() - 1, bottom() - 2 } });
|
||||
}
|
||||
VERIFY(closest_point.has_value());
|
||||
VERIFY(side(closest_point.value()) != Side::None);
|
||||
return closest_point.value();
|
||||
}
|
||||
|
||||
class RelativeLocation {
|
||||
friend class Rect<T>;
|
||||
|
||||
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() - 1) || (part.y() < other_rect.bottom() && part.bottom() - 1 > other_rect.y()))
|
||||
m_left = true;
|
||||
if (part.y() >= other_rect.bottom() - 1 || part.bottom() - 1 > other_rect.y())
|
||||
m_bottom_left = true;
|
||||
}
|
||||
if (part.x() >= other_rect.x() || part.right() - 1 > other_rect.x()) {
|
||||
if (part.y() < other_rect.y())
|
||||
m_top = true;
|
||||
if (part.y() >= other_rect.bottom() - 1 || part.bottom() > other_rect.bottom())
|
||||
m_bottom = true;
|
||||
}
|
||||
if (part.x() >= other_rect.right() - 1 || 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() - 1) || (part.y() < other_rect.bottom() && part.bottom() - 1 > other_rect.y()))
|
||||
m_right = true;
|
||||
if (part.y() >= other_rect.bottom() - 1 || part.bottom() - 1 > other_rect.y())
|
||||
m_bottom_right = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
RelativeLocation() = default;
|
||||
|
||||
bool top_left() const { return m_top_left; }
|
||||
bool top() const { return m_top; }
|
||||
bool top_right() const { return m_top_right; }
|
||||
bool left() const { return m_left; }
|
||||
bool right() const { return m_right; }
|
||||
bool bottom_left() const { return m_bottom_left; }
|
||||
bool bottom() const { return m_bottom; }
|
||||
bool bottom_right() const { return m_bottom_right; }
|
||||
bool anywhere_above() const { return m_top_left || m_top || m_top_right; }
|
||||
bool anywhere_below() const { return m_bottom_left || m_bottom || m_bottom_right; }
|
||||
bool anywhere_left() const { return m_top_left || m_left || m_bottom_left; }
|
||||
bool anywhere_right() const { return m_top_right || m_right || m_bottom_right; }
|
||||
|
||||
private:
|
||||
bool m_top_left : 1 { false };
|
||||
bool m_top : 1 { false };
|
||||
bool m_top_right : 1 { false };
|
||||
bool m_left : 1 { false };
|
||||
bool m_right : 1 { false };
|
||||
bool m_bottom_left : 1 { false };
|
||||
bool m_bottom : 1 { false };
|
||||
bool m_bottom_right : 1 { false };
|
||||
};
|
||||
[[nodiscard]] RelativeLocation relative_location_to(Rect<T> const& other) const
|
||||
{
|
||||
return RelativeLocation(*this, other);
|
||||
}
|
||||
|
||||
enum class Side {
|
||||
None = 0,
|
||||
Left,
|
||||
Top,
|
||||
Right,
|
||||
Bottom
|
||||
};
|
||||
[[nodiscard]] Side side(Point<T> const& point) const
|
||||
{
|
||||
if (is_empty())
|
||||
return Side::None;
|
||||
if (point.y() == y() || point.y() == bottom() - 1)
|
||||
return (point.x() >= x() && point.x() < right()) ? (point.y() == y() ? Side::Top : Side::Bottom) : Side::None;
|
||||
if (point.x() == x() || point.x() == right() - 1)
|
||||
return (point.y() > y() && point.y() < bottom()) ? (point.x() == x() ? Side::Left : Side::Right) : Side::None;
|
||||
return Side::None;
|
||||
}
|
||||
|
||||
[[nodiscard]] Rect<T> rect_on_side(Side side, Rect<T> const& other) const
|
||||
{
|
||||
switch (side) {
|
||||
case Side::None:
|
||||
break;
|
||||
case Side::Left:
|
||||
// Return the area in other that is to the left of this rect
|
||||
if (other.x() < x()) {
|
||||
if (other.right() > x())
|
||||
return { other.location(), { x() - other.x(), other.height() } };
|
||||
else
|
||||
return other;
|
||||
}
|
||||
break;
|
||||
case Side::Top:
|
||||
// Return the area in other that is above this rect
|
||||
if (other.y() < y()) {
|
||||
if (other.bottom() > y())
|
||||
return { other.location(), { other.width(), y() - other.y() } };
|
||||
else
|
||||
return other;
|
||||
}
|
||||
break;
|
||||
case Side::Right:
|
||||
// Return the area in other that is to the right of this rect
|
||||
if (other.right() > x()) {
|
||||
if (other.x() < right())
|
||||
return { { right(), other.y() }, { other.width() - (right() - 1 - other.x()), other.height() } };
|
||||
else
|
||||
return other;
|
||||
}
|
||||
break;
|
||||
case Side::Bottom:
|
||||
// Return the area in other that is below this rect
|
||||
if (other.bottom() > y()) {
|
||||
if (other.y() < bottom())
|
||||
return { { other.x(), bottom() }, { other.width(), other.height() - (bottom() - 1 - other.y()) } };
|
||||
else
|
||||
return other;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
static bool disperse(Container& rects)
|
||||
{
|
||||
auto has_intersecting = [&]() {
|
||||
for (auto& rect : rects) {
|
||||
for (auto& other_rect : rects) {
|
||||
if (&rect == &other_rect)
|
||||
continue;
|
||||
if (rect.intersects(other_rect))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (!has_intersecting())
|
||||
return false;
|
||||
|
||||
auto calc_delta = [&](Rect<T> const& rect) -> Point<T> {
|
||||
auto rect_center = rect.center();
|
||||
Point<T> center_sum;
|
||||
for (auto& other_rect : rects) {
|
||||
if (&other_rect == &rect)
|
||||
continue;
|
||||
if (rect.intersects(other_rect))
|
||||
center_sum += rect_center - other_rect.center();
|
||||
}
|
||||
double m = sqrt((double)center_sum.x() * (double)center_sum.x() + (double)center_sum.y() * (double)center_sum.y());
|
||||
if (m != 0.0)
|
||||
return { (double)center_sum.x() / m + 0.5, (double)center_sum.y() / m + 0.5 };
|
||||
return {};
|
||||
};
|
||||
|
||||
Vector<Point<T>, 8> deltas;
|
||||
do {
|
||||
bool changes = false;
|
||||
|
||||
deltas.clear_with_capacity();
|
||||
for (auto& rect : rects) {
|
||||
auto delta = calc_delta(rect);
|
||||
if (!delta.is_zero())
|
||||
changes = true;
|
||||
deltas.append(delta);
|
||||
}
|
||||
|
||||
// TODO: If we have no changes we would loop infinitely!
|
||||
// Figure out some way to resolve this. Maybe randomly moving an intersecting rect?
|
||||
VERIFY(changes);
|
||||
|
||||
size_t i = 0;
|
||||
for (auto& rect : rects)
|
||||
rect.translate_by(deltas[i++]);
|
||||
|
||||
} while (has_intersecting());
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_adjacent(Rect<T> const& other) const
|
||||
{
|
||||
if (is_empty() || other.is_empty())
|
||||
return false;
|
||||
if (intersects(other))
|
||||
return false;
|
||||
if (other.right() == x() || other.x() == right())
|
||||
return max(top(), other.top()) < min(bottom(), other.bottom());
|
||||
if (other.bottom() == y() || other.y() == bottom())
|
||||
return max(left(), other.left()) < min(right(), other.right());
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] static Rect<T> centered_at(Point<T> const& point, Size<T> const& size)
|
||||
{
|
||||
return { { point.x() - size.width() / 2, point.y() - size.height() / 2 }, size };
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibGC/Weak.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/PixelUnits.h>
|
||||
|
|
|
|||
|
|
@ -415,12 +415,10 @@ constexpr CSSPixelFraction operator/(CSSPixels left, T right) { return left / CS
|
|||
inline float operator/(CSSPixels left, float right) { return left.to_float() / right; }
|
||||
inline double operator/(CSSPixels left, double right) { return left.to_double() / right; }
|
||||
|
||||
using CSSPixelLine = Gfx::Line<CSSPixels>;
|
||||
using CSSPixelPoint = Gfx::Point<CSSPixels>;
|
||||
using CSSPixelRect = Gfx::Rect<CSSPixels>;
|
||||
using CSSPixelSize = Gfx::Size<CSSPixels>;
|
||||
|
||||
using DevicePixelLine = Gfx::Line<DevicePixels>;
|
||||
using DevicePixelPoint = Gfx::Point<DevicePixels>;
|
||||
using DevicePixelRect = Gfx::Rect<DevicePixels>;
|
||||
using DevicePixelSize = Gfx::Size<DevicePixels>;
|
||||
|
|
|
|||
|
|
@ -28,35 +28,6 @@ TEST_CASE(rect_contains_vertically)
|
|||
EXPECT(!rect.contains_vertically(100.f));
|
||||
}
|
||||
|
||||
TEST_CASE(rect_shatter)
|
||||
{
|
||||
Gfx::IntRect glass_plate = { 0, 0, 100, 100 };
|
||||
Gfx::IntRect hammer = { 30, 40, 40, 10 };
|
||||
|
||||
auto shards = glass_plate.shatter(hammer);
|
||||
EXPECT(!shards.is_empty());
|
||||
|
||||
int total_shard_area = 0;
|
||||
for (auto shard : shards) {
|
||||
EXPECT(glass_plate.contains(shard));
|
||||
EXPECT(!hammer.intersects(shard));
|
||||
total_shard_area += shard.size().area();
|
||||
}
|
||||
|
||||
EXPECT_EQ(glass_plate.size().area() - hammer.size().area(), total_shard_area);
|
||||
}
|
||||
|
||||
TEST_CASE(rect_closest_to)
|
||||
{
|
||||
Gfx::IntRect const screen_rect = { 0, 0, 960, 540 };
|
||||
Gfx::Point<int> p = { 460, 592 }; // point is below the rect
|
||||
Gfx::Point<int> closest = screen_rect.closest_to(p);
|
||||
EXPECT_EQ(screen_rect.side(closest), Gfx::IntRect::Side::Bottom);
|
||||
p = { 960, 0 }; // point exactly on top right corner
|
||||
closest = screen_rect.closest_to(p);
|
||||
EXPECT_EQ(screen_rect.side(closest), Gfx::IntRect::Side::Top);
|
||||
}
|
||||
|
||||
TEST_CASE(rect_unite)
|
||||
{
|
||||
Gfx::IntRect rect_a { 10, 10, 100, 100 };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue