diff --git a/Libraries/LibDraw/FloatPoint.h b/Libraries/LibDraw/FloatPoint.h new file mode 100644 index 0000000000..4374b869b3 --- /dev/null +++ b/Libraries/LibDraw/FloatPoint.h @@ -0,0 +1,117 @@ +#pragma once + +#include +#include +#include + +class FloatRect; + +class FloatPoint { +public: + FloatPoint() {} + FloatPoint(float x, float y) + : m_x(x) + , m_y(y) + { + } + float x() const { return m_x; } + float y() const { return m_y; } + + void set_x(float x) { m_x = x; } + void set_y(float y) { m_y = y; } + + void move_by(float dx, float dy) + { + m_x += dx; + m_y += dy; + } + + void move_by(const FloatPoint& delta) + { + move_by(delta.x(), delta.y()); + } + + FloatPoint translated(const FloatPoint& delta) const + { + FloatPoint point = *this; + point.move_by(delta); + return point; + } + + FloatPoint translated(float dx, float dy) const + { + FloatPoint point = *this; + point.move_by(dx, dy); + return point; + } + + void constrain(const FloatRect&); + + bool operator==(const FloatPoint& other) const + { + return m_x == other.m_x + && m_y == other.m_y; + } + + bool operator!=(const FloatPoint& other) const + { + return !(*this == other); + } + + FloatPoint operator-() const { return { -m_x, -m_y }; } + + FloatPoint operator-(const FloatPoint& other) const { return { m_x - other.m_x, m_y - other.m_y }; } + FloatPoint& operator-=(const FloatPoint& other) + { + m_x -= other.m_x; + m_y -= other.m_y; + return *this; + } + + FloatPoint& operator+=(const FloatPoint& other) + { + m_x += other.m_x; + m_y += other.m_y; + return *this; + } + FloatPoint operator+(const FloatPoint& other) const { return { m_x + other.m_x, m_y + other.m_y }; } + + String to_string() const { return String::format("[%g,%g]", x(), y()); } + + bool is_null() const { return !m_x && !m_y; } + + float primary_offset_for_orientation(Orientation orientation) const + { + return orientation == Orientation::Vertical ? y() : x(); + } + + void set_primary_offset_for_orientation(Orientation orientation, float value) + { + if (orientation == Orientation::Vertical) + set_y(value); + else + set_x(value); + } + + float secondary_offset_for_orientation(Orientation orientation) const + { + return orientation == Orientation::Vertical ? x() : y(); + } + + void set_secondary_offset_for_orientation(Orientation orientation, float value) + { + if (orientation == Orientation::Vertical) + set_x(value); + else + set_y(value); + } + +private: + float m_x { 0 }; + float m_y { 0 }; +}; + +inline const LogStream& operator<<(const LogStream& stream, const FloatPoint& value) +{ + return stream << value.to_string(); +} diff --git a/Libraries/LibDraw/FloatRect.h b/Libraries/LibDraw/FloatRect.h new file mode 100644 index 0000000000..dc9cdf9cdc --- /dev/null +++ b/Libraries/LibDraw/FloatRect.h @@ -0,0 +1,300 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +class FloatRect { +public: + FloatRect() {} + FloatRect(float x, float y, float width, float height) + : m_location(x, y) + , m_size(width, height) + { + } + FloatRect(const FloatPoint& location, const FloatSize& size) + : m_location(location) + , m_size(size) + { + } + FloatRect(const FloatRect& other) + : m_location(other.m_location) + , m_size(other.m_size) + { + } + + bool is_null() const + { + return width() == 0 && height() == 0; + } + + bool is_empty() const + { + return width() <= 0 || height() <= 0; + } + + void move_by(float dx, float dy) + { + m_location.move_by(dx, dy); + } + + void move_by(const FloatPoint& delta) + { + m_location.move_by(delta); + } + + FloatPoint center() const + { + return { x() + width() / 2, y() + height() / 2 }; + } + + void set_location(const FloatPoint& location) + { + m_location = location; + } + + void set_size(const FloatSize& size) + { + m_size = size; + } + + void set_size(float width, float height) + { + m_size.set_width(width); + m_size.set_height(height); + } + + void inflate(float w, float h) + { + set_x(x() - w / 2); + set_width(width() + w); + set_y(y() - h / 2); + set_height(height() + h); + } + + void shrink(float w, float h) + { + set_x(x() + w / 2); + set_width(width() - w); + set_y(y() + h / 2); + set_height(height() - h); + } + + FloatRect shrunken(float w, float h) const + { + FloatRect rect = *this; + rect.shrink(w, h); + return rect; + } + + FloatRect inflated(float w, float h) const + { + FloatRect rect = *this; + rect.inflate(w, h); + return rect; + } + + FloatRect translated(float dx, float dy) const + { + FloatRect rect = *this; + rect.move_by(dx, dy); + return rect; + } + + FloatRect translated(const FloatPoint& delta) const + { + FloatRect rect = *this; + rect.move_by(delta); + return rect; + } + + bool contains_vertically(float y) const + { + return y >= top() && y <= bottom(); + } + + bool contains_horizontally(float x) const + { + return x >= left() && x <= right(); + } + + bool contains(float x, float y) const + { + return x >= m_location.x() && x <= right() && y >= m_location.y() && y <= bottom(); + } + + bool contains(const FloatPoint& point) const + { + return contains(point.x(), point.y()); + } + + bool contains(const FloatRect& other) const + { + return left() <= other.left() + && right() >= other.right() + && top() <= other.top() + && bottom() >= other.bottom(); + } + + float primary_offset_for_orientation(Orientation orientation) const { return m_location.primary_offset_for_orientation(orientation); } + void set_primary_offset_for_orientation(Orientation orientation, float value) { m_location.set_primary_offset_for_orientation(orientation, value); } + float secondary_offset_for_orientation(Orientation orientation) const { return m_location.secondary_offset_for_orientation(orientation); } + void set_secondary_offset_for_orientation(Orientation orientation, float value) { m_location.set_secondary_offset_for_orientation(orientation, value); } + + float primary_size_for_orientation(Orientation orientation) const { return m_size.primary_size_for_orientation(orientation); } + float secondary_size_for_orientation(Orientation orientation) const { return m_size.secondary_size_for_orientation(orientation); } + void set_primary_size_for_orientation(Orientation orientation, float value) { m_size.set_primary_size_for_orientation(orientation, value); } + void set_secondary_size_for_orientation(Orientation orientation, float value) { m_size.set_secondary_size_for_orientation(orientation, value); } + + float first_edge_for_orientation(Orientation orientation) const + { + if (orientation == Orientation::Vertical) + return top(); + return left(); + } + + float last_edge_for_orientation(Orientation orientation) const + { + if (orientation == Orientation::Vertical) + return bottom(); + return right(); + } + + float left() const { return x(); } + float right() const { return x() + width() - 1; } + float top() const { return y(); } + float bottom() const { return y() + height() - 1; } + + void set_left(float left) + { + set_x(left); + } + + void set_top(float top) + { + set_y(top); + } + + void set_right(float right) + { + set_width(right - x() + 1); + } + + void set_bottom(float bottom) + { + set_height(bottom - y() + 1); + } + + void set_right_without_resize(float new_right) + { + float delta = new_right - right(); + move_by(delta, 0); + } + + void set_bottom_without_resize(float new_bottom) + { + float delta = new_bottom - bottom(); + move_by(0, delta); + } + + bool intersects(const FloatRect& other) const + { + return left() <= other.right() + && other.left() <= right() + && top() <= other.bottom() + && other.top() <= bottom(); + } + + float x() const { return location().x(); } + float y() const { return location().y(); } + float width() const { return m_size.width(); } + float height() const { return m_size.height(); } + + void set_x(float x) { m_location.set_x(x); } + void set_y(float y) { m_location.set_y(y); } + void set_width(float width) { m_size.set_width(width); } + void set_height(float height) { m_size.set_height(height); } + + FloatPoint location() const { return m_location; } + FloatSize size() const { return m_size; } + + Vector shatter(const FloatRect& hammer) const; + + bool operator==(const FloatRect& other) const + { + return m_location == other.m_location + && m_size == other.m_size; + } + + void intersect(const FloatRect&); + + static FloatRect intersection(const FloatRect& a, const FloatRect& b) + { + FloatRect r(a); + r.intersect(b); + return r; + } + + FloatRect intersected(const FloatRect& other) const + { + return intersection(*this, other); + } + + FloatRect united(const FloatRect&) const; + + FloatPoint top_left() const { return { left(), top() }; } + FloatPoint top_right() const { return { right(), top() }; } + FloatPoint bottom_left() const { return { left(), bottom() }; } + FloatPoint bottom_right() const { return { right(), bottom() }; } + + void align_within(const FloatRect&, TextAlignment); + + void center_within(const FloatRect& other) + { + center_horizontally_within(other); + center_vertically_within(other); + } + + void center_horizontally_within(const FloatRect& other) + { + set_x(other.center().x() - width() / 2); + } + + void center_vertically_within(const FloatRect& other) + { + set_y(other.center().y() - height() / 2); + } + + String to_string() const { return String::format("[%g,%g %gx%g]", x(), y(), width(), height()); } + +private: + FloatPoint m_location; + FloatSize m_size; +}; + +inline void FloatPoint::constrain(const FloatRect& rect) +{ + if (x() < rect.left()) + set_x(rect.left()); + else if (x() > rect.right()) + set_x(rect.right()); + if (y() < rect.top()) + set_y(rect.top()); + else if (y() > rect.bottom()) + set_y(rect.bottom()); +} + +inline const LogStream& operator<<(const LogStream& stream, const FloatRect& value) +{ + return stream << value.to_string(); +} + +inline Rect enclosing_int_rect(const FloatRect& float_rect) +{ + return { (int)float_rect.x(), (int)float_rect.y(), (int)ceilf(float_rect.width()), (int)ceilf(float_rect.height()) }; +} diff --git a/Libraries/LibDraw/FloatSize.h b/Libraries/LibDraw/FloatSize.h new file mode 100644 index 0000000000..c1cd07867b --- /dev/null +++ b/Libraries/LibDraw/FloatSize.h @@ -0,0 +1,87 @@ +#pragma once + +#include +#include +#include + +class FloatSize { +public: + FloatSize() {} + FloatSize(float w, float h) + : m_width(w) + , m_height(h) + { + } + + bool is_null() const { return !m_width && !m_height; } + bool is_empty() const { return m_width <= 0 || m_height <= 0; } + + float width() const { return m_width; } + float height() const { return m_height; } + + float area() const { return width() * height(); } + + void set_width(float w) { m_width = w; } + void set_height(float h) { m_height = h; } + + bool operator==(const FloatSize& other) const + { + return m_width == other.m_width && m_height == other.m_height; + } + + bool operator!=(const FloatSize& other) const + { + return !(*this == other); + } + + FloatSize& operator-=(const FloatSize& other) + { + m_width -= other.m_width; + m_height -= other.m_height; + return *this; + } + + FloatSize& operator+=(const FloatSize& other) + { + m_width += other.m_width; + m_height += other.m_height; + return *this; + } + + float primary_size_for_orientation(Orientation orientation) const + { + return orientation == Orientation::Vertical ? height() : width(); + } + + void set_primary_size_for_orientation(Orientation orientation, float value) + { + if (orientation == Orientation::Vertical) + set_height(value); + else + set_width(value); + } + + float secondary_size_for_orientation(Orientation orientation) const + { + return orientation == Orientation::Vertical ? width() : height(); + } + + void set_secondary_size_for_orientation(Orientation orientation, float value) + { + if (orientation == Orientation::Vertical) + set_width(value); + else + set_height(value); + } + + String to_string() const { return String::format("[%gx%g]", m_width, m_height); } + +private: + float m_width { 0 }; + float m_height { 0 }; +}; + +inline const LogStream& operator<<(const LogStream& stream, const FloatSize& value) +{ + return stream << value.to_string(); +}