#ifndef UI_GFX_GEOMETRY_RECT_H_
#define UI_GFX_GEOMETRY_RECT_H_
#include <cmath>
#include <iosfwd>
#include <string>
#include "base/component_export.h"
#include "base/containers/span.h"
#include "base/numerics/clamped_math.h"
#include "base/numerics/safe_conversions.h"
#include "build/build_config.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/outsets.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/vector2d.h"
#if BUILDFLAG(IS_WIN)
typedef struct tagRECT RECT;
#elif BUILDFLAG(IS_APPLE)
typedef struct CGRect CGRect;
#endif
namespace gfx {
class COMPONENT_EXPORT(GEOMETRY) Rect {
public:
constexpr Rect() = default;
constexpr Rect(int width, int height) : size_(width, height) {}
constexpr Rect(int x, int y, int width, int height)
: origin_(x, y),
size_(ClampWidthOrHeight(x, width), ClampWidthOrHeight(y, height)) {}
constexpr explicit Rect(const Size& size) : size_(size) {}
constexpr Rect(const Point& origin, const Size& size)
: origin_(origin),
size_(ClampWidthOrHeight(origin.x(), size.width()),
ClampWidthOrHeight(origin.y(), size.height())) {}
#if BUILDFLAG(IS_WIN)
explicit Rect(const RECT& r);
#elif BUILDFLAG(IS_APPLE)
explicit Rect(const CGRect& r);
#endif
#if BUILDFLAG(IS_WIN)
RECT ToRECT() const;
#elif BUILDFLAG(IS_APPLE)
CGRect ToCGRect() const;
#endif
constexpr int x() const { return origin_.x(); }
void set_x(int x) {
origin_.set_x(x);
size_.set_width(ClampWidthOrHeight(x, width()));
}
constexpr int y() const { return origin_.y(); }
void set_y(int y) {
origin_.set_y(y);
size_.set_height(ClampWidthOrHeight(y, height()));
}
constexpr int width() const { return size_.width(); }
void set_width(int width) { size_.set_width(ClampWidthOrHeight(x(), width)); }
constexpr int height() const { return size_.height(); }
void set_height(int height) {
size_.set_height(ClampWidthOrHeight(y(), height));
}
constexpr const Point& origin() const { return origin_; }
void set_origin(const Point& origin) {
origin_ = origin;
set_width(width());
set_height(height());
}
constexpr const Size& size() const { return size_; }
void set_size(const Size& size) {
set_width(size.width());
set_height(size.height());
}
constexpr int right() const { return x() + width(); }
constexpr int bottom() const { return y() + height(); }
constexpr Point top_right() const { return Point(right(), y()); }
constexpr Point bottom_left() const { return Point(x(), bottom()); }
constexpr Point bottom_right() const { return Point(right(), bottom()); }
constexpr Point left_center() const { return Point(x(), y() + height() / 2); }
constexpr Point top_center() const { return Point(x() + width() / 2, y()); }
constexpr Point right_center() const {
return Point(right(), y() + height() / 2);
}
constexpr Point bottom_center() const {
return Point(x() + width() / 2, bottom());
}
Vector2d OffsetFromOrigin() const { return Vector2d(x(), y()); }
void SetRect(int x, int y, int width, int height) {
origin_.SetPoint(x, y);
set_width(width);
set_height(height);
}
void SetByBounds(int left, int top, int right, int bottom) {
SetHorizontalBounds(left, right);
SetVerticalBounds(top, bottom);
}
void SetHorizontalBounds(int left, int right) {
set_x(left);
set_width(base::ClampSub(right, left));
if (this->right() != right) [[unlikely]] {
AdjustForSaturatedRight(right);
}
}
void SetVerticalBounds(int top, int bottom) {
set_y(top);
set_height(base::ClampSub(bottom, top));
if (this->bottom() != bottom) [[unlikely]] {
AdjustForSaturatedBottom(bottom);
}
}
void Inset(int inset) { Inset(Insets(inset)); }
void Inset(const Insets& insets);
void Outset(int outset) { Inset(-outset); }
void Outset(const Outsets& outsets) { Inset(outsets.ToInsets()); }
void Offset(int horizontal, int vertical) {
Offset(Vector2d(horizontal, vertical));
}
void Offset(const Vector2d& distance);
void operator+=(const Vector2d& offset) { Offset(offset); }
void operator-=(const Vector2d& offset) { Offset(-offset); }
Insets InsetsFrom(const Rect& inner) const;
bool IsEmpty() const { return size_.IsEmpty(); }
bool operator<(const Rect& other) const;
bool Contains(int point_x, int point_y) const;
bool Contains(const Point& point) const {
return Contains(point.x(), point.y());
}
bool Contains(const Rect& rect) const;
bool Intersects(const Rect& rect) const;
void Intersect(const Rect& rect);
bool InclusiveIntersect(const Rect& rect);
void Union(const Rect& rect);
void UnionEvenIfEmpty(const Rect& rect);
void Subtract(const Rect& rect);
void AdjustToFit(const Rect& rect);
Point CenterPoint() const;
void ToCenteredSize(const Size& size);
void ClampToCenteredSize(const Size& size);
void Transpose();
void SplitVertically(Rect& left_half, Rect& right_half) const;
void SplitHorizontally(Rect& top_half, Rect& bottom_half) const;
bool SharesEdgeWith(const Rect& rect) const;
int ManhattanDistanceToPoint(const Point& point) const;
int ManhattanInternalDistance(const Rect& rect) const;
std::string ToString() const;
bool ApproximatelyEqual(const Rect& rect, int tolerance) const;
friend bool operator==(const Rect&, const Rect&) = default;
private:
static constexpr int ClampWidthOrHeight(int x_or_y, int width_or_height) {
return base::ClampAdd(x_or_y, width_or_height) - x_or_y;
}
void AdjustForSaturatedRight(int right);
void AdjustForSaturatedBottom(int bottom);
gfx::Point origin_;
gfx::Size size_;
};
COMPONENT_EXPORT(GEOMETRY) Rect operator+(const Rect& lhs, const Vector2d& rhs);
COMPONENT_EXPORT(GEOMETRY) Rect operator-(const Rect& lhs, const Vector2d& rhs);
inline Rect operator+(const Vector2d& lhs, const Rect& rhs) {
return rhs + lhs;
}
COMPONENT_EXPORT(GEOMETRY) Rect IntersectRects(const Rect& a, const Rect& b);
COMPONENT_EXPORT(GEOMETRY) Rect UnionRects(const Rect& a, const Rect& b);
COMPONENT_EXPORT(GEOMETRY) Rect UnionRects(base::span<const Rect> rects);
COMPONENT_EXPORT(GEOMETRY)
Rect UnionRectsEvenIfEmpty(const Rect& a, const Rect& b);
COMPONENT_EXPORT(GEOMETRY) Rect SubtractRects(const Rect& a, const Rect& b);
COMPONENT_EXPORT(GEOMETRY) Rect BoundingRect(const Point& p1, const Point& p2);
inline Rect ScaleToEnclosingRect(const Rect& rect,
float x_scale,
float y_scale) {
if (x_scale == 1.f && y_scale == 1.f)
return rect;
int x = base::ClampFloor(rect.x() * x_scale);
int y = base::ClampFloor(rect.y() * y_scale);
int r = rect.width() == 0 ? x : base::ClampCeil(rect.right() * x_scale);
int b = rect.height() == 0 ? y : base::ClampCeil(rect.bottom() * y_scale);
Rect result;
result.SetByBounds(x, y, r, b);
return result;
}
inline Rect ScaleToEnclosingRect(const Rect& rect, float scale) {
return ScaleToEnclosingRect(rect, scale, scale);
}
inline Rect ScaleToEnclosedRect(const Rect& rect,
float x_scale,
float y_scale) {
if (x_scale == 1.f && y_scale == 1.f)
return rect;
int x = base::ClampCeil(rect.x() * x_scale);
int y = base::ClampCeil(rect.y() * y_scale);
int r = rect.width() == 0 ? x : base::ClampFloor(rect.right() * x_scale);
int b = rect.height() == 0 ? y : base::ClampFloor(rect.bottom() * y_scale);
Rect result;
result.SetByBounds(x, y, r, b);
return result;
}
inline Rect ScaleToEnclosedRect(const Rect& rect, float scale) {
return ScaleToEnclosedRect(rect, scale, scale);
}
inline Rect ScaleToRoundedRect(const Rect& rect, float x_scale, float y_scale) {
if (x_scale == 1.f && y_scale == 1.f)
return rect;
int x = base::ClampRound(rect.x() * x_scale);
int y = base::ClampRound(rect.y() * y_scale);
int r = rect.width() == 0 ? x : base::ClampRound(rect.right() * x_scale);
int b = rect.height() == 0 ? y : base::ClampRound(rect.bottom() * y_scale);
Rect result;
result.SetByBounds(x, y, r, b);
return result;
}
inline Rect ScaleToRoundedRect(const Rect& rect, float scale) {
return ScaleToRoundedRect(rect, scale, scale);
}
COMPONENT_EXPORT(GEOMETRY)
Rect ScaleToEnclosingRectIgnoringError(const Rect& rect,
float scale,
float error = 0.001f);
COMPONENT_EXPORT(GEOMETRY)
Rect MaximumCoveredRect(const Rect& a, const Rect& b);
void PrintTo(const Rect& rect, ::std::ostream* os);
}
#endif