Skip to content

Commit

Permalink
Math: Some more box improvements and test cases (#3)
Browse files Browse the repository at this point in the history
* Added some constants to handle floating point presicion comparisons and other calculations plus some refactoring

* Removed validation

* Added comments to understand how box header works

* Extended the EXPECT macro to evaluate Vector2D test cases

* Added box.cpp test cases

* Applied clang-format
  • Loading branch information
JassonCordones authored Jun 24, 2024
1 parent ff343e0 commit 7a2c2c9
Show file tree
Hide file tree
Showing 4 changed files with 268 additions and 112 deletions.
272 changes: 174 additions & 98 deletions include/hyprutils/math/Box.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,104 +3,180 @@
#include "./Vector2D.hpp"
#include "./Misc.hpp"

namespace Hyprutils {
namespace Math {
struct SBoxExtents {
Vector2D topLeft;
Vector2D bottomRight;

//
SBoxExtents operator*(const double& scale) const {
return SBoxExtents{topLeft * scale, bottomRight * scale};
}

SBoxExtents round() {
return {topLeft.round(), bottomRight.round()};
}

bool operator==(const SBoxExtents& other) const {
return topLeft == other.topLeft && bottomRight == other.bottomRight;
}

void addExtents(const SBoxExtents& other) {
topLeft = topLeft.getComponentMax(other.topLeft);
bottomRight = bottomRight.getComponentMax(other.bottomRight);
}
namespace Hyprutils::Math {

/**
* @brief Represents the extents of a bounding box.
*/
struct SBoxExtents {
Vector2D topLeft;
Vector2D bottomRight;

/**
* @brief Scales the extents by a given factor.
* @param scale The scaling factor.
* @return Scaled SBoxExtents.
*/
SBoxExtents operator*(const double& scale) const {
return SBoxExtents{topLeft * scale, bottomRight * scale};
}
/**
* @brief Rounds the coordinates of the extents.
* @return Rounded SBoxExtents.
*/
SBoxExtents round() {
return {topLeft.round(), bottomRight.round()};
}
/**
* @brief Checks equality between two SBoxExtents objects.
* @param other Another SBoxExtents object to compare.
* @return True if both SBoxExtents are equal, false otherwise.
*/
bool operator==(const SBoxExtents& other) const {
return topLeft == other.topLeft && bottomRight == other.bottomRight;
}

/**
* @brief Adjusts the extents to encompass another SBoxExtents.
* @param other Another SBoxExtents to add to this one.
*/
void addExtents(const SBoxExtents& other) {
topLeft = topLeft.getComponentMax(other.topLeft);
bottomRight = bottomRight.getComponentMax(other.bottomRight);
}
};

/**
* @brief Represents a 2D bounding box.
*/
class CBox {
public:
/**
* @brief Constructs a CBox with specified position and dimensions.
* @param x_ X-coordinate of the top-left corner.
* @param y_ Y-coordinate of the top-left corner.
* @param w_ Width of the box.
* @param h_ Height of the box.
*/
CBox(double x_, double y_, double w_, double h_) {
x = x_;
y = y_;
w = w_;
h = h_;
}
/**
* @brief Default constructor. Initializes an empty box (0 width, 0 height).
*/
CBox() {
w = 0;
h = 0;
}
/**
* @brief Constructs a CBox with uniform dimensions.
* @param d Dimensions to apply uniformly (x, y, width, height).
*/
CBox(const double d) {
x = d;
y = d;
w = d;
h = d;
}
/**
* @brief Constructs a CBox from a position and size vector.
* @param pos Position vector representing the top-left corner.
* @param size Size vector representing width and height.
*/
CBox(const Vector2D& pos, const Vector2D& size) {
x = pos.x;
y = pos.y;
w = size.x;
h = size.y;
}

// Geometric operations
CBox& applyFromWlr();
CBox& scale(double scale);
CBox& scaleFromCenter(double scale);
CBox& scale(const Vector2D& scale);
CBox& translate(const Vector2D& vec);
CBox& round();
CBox& transform(const eTransform t, double w, double h);
CBox& addExtents(const SBoxExtents& e);
CBox& expand(const double& value);
CBox& noNegativeSize();

CBox copy() const;
CBox intersection(const CBox& other) const;
bool overlaps(const CBox& other) const;
bool inside(const CBox& bound) const;

/**
* @brief Computes the extents of the box relative to another box.
* @param small Another CBox to compare against.
* @return SBoxExtents representing the extents of the box relative to 'small'.
*/
SBoxExtents extentsFrom(const CBox&); // this is the big box

/**
* @brief Calculates the middle point of the box.
* @return Vector2D representing the middle point.
*/
Vector2D middle() const;

/**
* @brief Retrieves the position of the top-left corner of the box.
* @return Vector2D representing the position.
*/
Vector2D pos() const;

/**
* @brief Retrieves the size (width and height) of the box.
* @return Vector2D representing the size.
*/
Vector2D size() const;

/**
* @brief Finds the closest point within the box to a given vector.
* @param vec Vector from which to find the closest point.
* @return Vector2D representing the closest point within the box.
*/
Vector2D closestPoint(const Vector2D& vec) const;

/**
* @brief Checks if a given point is inside the box.
* @param vec Vector representing the point to check.
* @return True if the point is inside the box, false otherwise.
*/
bool containsPoint(const Vector2D& vec) const;

/**
* @brief Checks if the box is empty (zero width or height).
* @return True if the box is empty, false otherwise.
*/
bool empty() const;

double x = 0, y = 0; // Position of the top-left corner of the box.
union {
double w;
double width;
};

class CBox {
public:
CBox(double x_, double y_, double w_, double h_) {
x = x_;
y = y_;
w = w_;
h = h_;
}

CBox() {
w = 0;
h = 0;
}

CBox(const double d) {
x = d;
y = d;
w = d;
h = d;
}

CBox(const Vector2D& pos, const Vector2D& size) {
x = pos.x;
y = pos.y;
w = size.x;
h = size.y;
}

CBox& applyFromWlr();
CBox& scale(double scale);
CBox& scaleFromCenter(double scale);
CBox& scale(const Vector2D& scale);
CBox& translate(const Vector2D& vec);
CBox& round();
CBox& transform(const eTransform t, double w, double h);
CBox& addExtents(const SBoxExtents& e);
CBox& expand(const double& value);
CBox& noNegativeSize();

CBox copy() const;
CBox intersection(const CBox& other) const;
bool overlaps(const CBox& other) const;
bool inside(const CBox& bound) const;

SBoxExtents extentsFrom(const CBox&); // this is the big box

Vector2D middle() const;
Vector2D pos() const;
Vector2D size() const;
Vector2D closestPoint(const Vector2D& vec) const;

bool containsPoint(const Vector2D& vec) const;
bool empty() const;

double x = 0, y = 0;
union {
double w;
double width;
};
union {
double h;
double height;
};

double rot = 0; /* rad, ccw */

//
bool operator==(const CBox& rhs) const {
return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h;
}

private:
CBox roundInternal();
union {
double h;
double height;
};
}

double rot = 0; //< Rotation angle of the box in radians (counterclockwise).

/**
* @brief Checks equality between two CBox objects.
* @param rhs Another CBox object to compare.
* @return True if both CBox objects are equal, false otherwise.
*/
bool operator==(const CBox& rhs) const {
return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h;
}

private:
CBox roundInternal();
};
}
26 changes: 13 additions & 13 deletions src/math/Box.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

using namespace Hyprutils::Math;

constexpr double HALF = 0.5;
constexpr double DOUBLE = 2.0;
constexpr double HALF = 0.5;
constexpr double DOUBLE = 2.0;
constexpr double EPSILON = 1e-9;

CBox& Hyprutils::Math::CBox::scale(double scale) {
CBox& Hyprutils::Math::CBox::scale(double scale) {
x *= scale;
y *= scale;
w *= scale;
Expand Down Expand Up @@ -51,13 +51,13 @@ bool Hyprutils::Math::CBox::empty() const {
CBox& Hyprutils::Math::CBox::round() {
double roundedX = std::round(x);
double roundedY = std::round(y);
double newW = x + w - roundedX;
double newH = y + h - roundedY;
x = roundedX;
y = roundedY;
w = std::round(newW);
h = std::round(newH);
double newW = x + w - roundedX;
double newH = y + h - roundedY;

x = roundedX;
y = roundedY;
w = std::round(newW);
h = std::round(newH);

return *this;
}
Expand Down Expand Up @@ -179,10 +179,10 @@ bool Hyprutils::Math::CBox::inside(const CBox& bound) const {
}

CBox Hyprutils::Math::CBox::roundInternal() {
double flooredX = std::floor(x);
double flooredX = std::floor(x);
double flooredY = std::floor(y);
double newW = x + w - flooredX;
double newH = y + h - flooredY;
double newW = x + w - flooredX;
double newH = y + h - flooredY;

return CBox{flooredX, flooredY, std::floor(newW), std::floor(newH)};
}
Expand Down
Loading

0 comments on commit 7a2c2c9

Please sign in to comment.