New upstream version 0.1.5
This commit is contained in:
parent
2e14f5bcf3
commit
d322427bc5
|
|
@ -17,7 +17,7 @@ jobs:
|
|||
run: |
|
||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||
pacman --noconfirm --noprogressbar -Syyu
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ pixman
|
||||
|
||||
- name: Build hyprutils with gcc
|
||||
run: |
|
||||
|
|
@ -44,7 +44,7 @@ jobs:
|
|||
run: |
|
||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||
pacman --noconfirm --noprogressbar -Syyu
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ pixman
|
||||
|
||||
- name: Build hyprutils with clang
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
set(HYPRUTILS_VERSION "0.1.3")
|
||||
set(HYPRUTILS_VERSION "0.1.5")
|
||||
add_compile_definitions(HYPRUTILS_VERSION="${HYPRUTILS_VERSION}")
|
||||
|
||||
project(hyprutils
|
||||
|
|
@ -30,6 +30,9 @@ endif()
|
|||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "include/*.hpp")
|
||||
file(GLOB_RECURSE PUBLIC_HEADERS CONFIGURE_DEPENDS "include/*.hpp")
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET pixman-1)
|
||||
|
||||
add_library(hyprutils SHARED ${SRCFILES})
|
||||
target_include_directories( hyprutils
|
||||
PUBLIC "./include"
|
||||
|
|
@ -39,25 +42,31 @@ set_target_properties(hyprutils PROPERTIES
|
|||
VERSION ${hyprutils_VERSION}
|
||||
SOVERSION 0
|
||||
)
|
||||
target_link_libraries(hyprutils PkgConfig::deps)
|
||||
|
||||
# tests
|
||||
add_custom_target(tests)
|
||||
|
||||
add_executable(hyprutils_memory "tests/memory.cpp")
|
||||
target_link_libraries(hyprutils_memory PRIVATE hyprutils)
|
||||
target_link_libraries(hyprutils_memory PRIVATE hyprutils PkgConfig::deps)
|
||||
add_test(NAME "Memory" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprutils_memory "memory")
|
||||
add_dependencies(tests hyprutils_memory)
|
||||
|
||||
add_executable(hyprutils_string "tests/string.cpp")
|
||||
target_link_libraries(hyprutils_string PRIVATE hyprutils)
|
||||
target_link_libraries(hyprutils_string PRIVATE hyprutils PkgConfig::deps)
|
||||
add_test(NAME "String" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprutils_string "string")
|
||||
add_dependencies(tests hyprutils_string)
|
||||
|
||||
add_executable(hyprutils_signal "tests/signal.cpp")
|
||||
target_link_libraries(hyprutils_signal PRIVATE hyprutils)
|
||||
target_link_libraries(hyprutils_signal PRIVATE hyprutils PkgConfig::deps)
|
||||
add_test(NAME "Signal" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprutils_signal "signal")
|
||||
add_dependencies(tests hyprutils_signal)
|
||||
|
||||
add_executable(hyprutils_math "tests/math.cpp")
|
||||
target_link_libraries(hyprutils_math PRIVATE hyprutils PkgConfig::deps)
|
||||
add_test(NAME "Math" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprutils_math "math")
|
||||
add_dependencies(tests hyprutils_math)
|
||||
|
||||
# Installation
|
||||
install(TARGETS hyprutils)
|
||||
install(DIRECTORY "include/hyprutils" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ Hyprutils depends on the ABI stability of the stdlib implementation of your comp
|
|||
## Building
|
||||
|
||||
```sh
|
||||
git clone https://github.com/hyprwm/hyprutils.git
|
||||
cd hyprutils/
|
||||
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
|
||||
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf _NPROCESSORS_CONF`
|
||||
sudo cmake --install build
|
||||
```
|
||||
|
|
|
|||
|
|
@ -0,0 +1,182 @@
|
|||
#pragma once
|
||||
|
||||
#include "./Vector2D.hpp"
|
||||
#include "./Misc.hpp"
|
||||
|
||||
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;
|
||||
};
|
||||
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();
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
namespace Hyprutils {
|
||||
namespace Math {
|
||||
enum eTransform {
|
||||
HYPRUTILS_TRANSFORM_NORMAL = 0,
|
||||
HYPRUTILS_TRANSFORM_90 = 1,
|
||||
HYPRUTILS_TRANSFORM_180 = 2,
|
||||
HYPRUTILS_TRANSFORM_270 = 3,
|
||||
HYPRUTILS_TRANSFORM_FLIPPED = 4,
|
||||
HYPRUTILS_TRANSFORM_FLIPPED_90 = 5,
|
||||
HYPRUTILS_TRANSFORM_FLIPPED_180 = 6,
|
||||
HYPRUTILS_TRANSFORM_FLIPPED_270 = 7,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include <pixman.h>
|
||||
#include <vector>
|
||||
#include "Vector2D.hpp"
|
||||
#include "Box.hpp"
|
||||
|
||||
namespace Hyprutils {
|
||||
namespace Math {
|
||||
class CRegion {
|
||||
public:
|
||||
/* Create an empty region */
|
||||
CRegion();
|
||||
/* Create from a reference. Copies, does not own. */
|
||||
CRegion(const pixman_region32_t* const ref);
|
||||
/* Create from a box */
|
||||
CRegion(double x, double y, double w, double h);
|
||||
/* Create from a CBox */
|
||||
CRegion(const CBox& box);
|
||||
/* Create from a pixman_box32_t */
|
||||
CRegion(pixman_box32_t* box);
|
||||
|
||||
CRegion(const CRegion&);
|
||||
CRegion(CRegion&&);
|
||||
|
||||
~CRegion();
|
||||
|
||||
CRegion& operator=(CRegion&& other) {
|
||||
pixman_region32_copy(&m_rRegion, other.pixman());
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& operator=(CRegion& other) {
|
||||
pixman_region32_copy(&m_rRegion, other.pixman());
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& clear();
|
||||
CRegion& set(const CRegion& other);
|
||||
CRegion& add(const CRegion& other);
|
||||
CRegion& add(double x, double y, double w, double h);
|
||||
CRegion& add(const CBox& other);
|
||||
CRegion& subtract(const CRegion& other);
|
||||
CRegion& intersect(const CRegion& other);
|
||||
CRegion& intersect(double x, double y, double w, double h);
|
||||
CRegion& translate(const Vector2D& vec);
|
||||
CRegion& transform(const eTransform t, double w, double h);
|
||||
CRegion& invert(pixman_box32_t* box);
|
||||
CRegion& invert(const CBox& box);
|
||||
CRegion& scale(float scale);
|
||||
CRegion& scale(const Vector2D& scale);
|
||||
CRegion& rationalize();
|
||||
CBox getExtents();
|
||||
bool containsPoint(const Vector2D& vec) const;
|
||||
bool empty() const;
|
||||
Vector2D closestPoint(const Vector2D& vec) const;
|
||||
CRegion copy() const;
|
||||
|
||||
std::vector<pixman_box32_t> getRects() const;
|
||||
|
||||
//
|
||||
pixman_region32_t* pixman() {
|
||||
return &m_rRegion;
|
||||
}
|
||||
|
||||
private:
|
||||
pixman_region32_t m_rRegion;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
#pragma once
|
||||
|
||||
#include <format>
|
||||
#include <string>
|
||||
|
||||
namespace Hyprutils {
|
||||
namespace Math {
|
||||
class Vector2D {
|
||||
public:
|
||||
Vector2D(double, double);
|
||||
Vector2D(int, int);
|
||||
Vector2D();
|
||||
~Vector2D();
|
||||
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
|
||||
// returns the scale
|
||||
double normalize();
|
||||
|
||||
Vector2D operator+(const Vector2D& a) const {
|
||||
return Vector2D(this->x + a.x, this->y + a.y);
|
||||
}
|
||||
Vector2D operator-(const Vector2D& a) const {
|
||||
return Vector2D(this->x - a.x, this->y - a.y);
|
||||
}
|
||||
Vector2D operator-() const {
|
||||
return Vector2D(-this->x, -this->y);
|
||||
}
|
||||
Vector2D operator*(const double& a) const {
|
||||
return Vector2D(this->x * a, this->y * a);
|
||||
}
|
||||
Vector2D operator/(const double& a) const {
|
||||
return Vector2D(this->x / a, this->y / a);
|
||||
}
|
||||
|
||||
bool operator==(const Vector2D& a) const {
|
||||
return a.x == x && a.y == y;
|
||||
}
|
||||
|
||||
bool operator!=(const Vector2D& a) const {
|
||||
return a.x != x || a.y != y;
|
||||
}
|
||||
|
||||
Vector2D operator*(const Vector2D& a) const {
|
||||
return Vector2D(this->x * a.x, this->y * a.y);
|
||||
}
|
||||
|
||||
Vector2D operator/(const Vector2D& a) const {
|
||||
return Vector2D(this->x / a.x, this->y / a.y);
|
||||
}
|
||||
|
||||
bool operator>(const Vector2D& a) const {
|
||||
return this->x > a.x && this->y > a.y;
|
||||
}
|
||||
|
||||
bool operator<(const Vector2D& a) const {
|
||||
return this->x < a.x && this->y < a.y;
|
||||
}
|
||||
Vector2D& operator+=(const Vector2D& a) {
|
||||
this->x += a.x;
|
||||
this->y += a.y;
|
||||
return *this;
|
||||
}
|
||||
Vector2D& operator-=(const Vector2D& a) {
|
||||
this->x -= a.x;
|
||||
this->y -= a.y;
|
||||
return *this;
|
||||
}
|
||||
Vector2D& operator*=(const Vector2D& a) {
|
||||
this->x *= a.x;
|
||||
this->y *= a.y;
|
||||
return *this;
|
||||
}
|
||||
Vector2D& operator/=(const Vector2D& a) {
|
||||
this->x /= a.x;
|
||||
this->y /= a.y;
|
||||
return *this;
|
||||
}
|
||||
Vector2D& operator*=(const double& a) {
|
||||
this->x *= a;
|
||||
this->y *= a;
|
||||
return *this;
|
||||
}
|
||||
Vector2D& operator/=(const double& a) {
|
||||
this->x /= a;
|
||||
this->y /= a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
double distance(const Vector2D& other) const;
|
||||
double distanceSq(const Vector2D& other) const;
|
||||
double size() const;
|
||||
Vector2D clamp(const Vector2D& min, const Vector2D& max = Vector2D{-1, -1}) const;
|
||||
|
||||
Vector2D floor() const;
|
||||
Vector2D round() const;
|
||||
|
||||
Vector2D getComponentMax(const Vector2D& other) const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// absolutely ridiculous formatter spec parsing
|
||||
#define AQ_FORMAT_PARSE(specs__, type__) \
|
||||
template <typename FormatContext> \
|
||||
constexpr auto parse(FormatContext& ctx) { \
|
||||
auto it = ctx.begin(); \
|
||||
for (; it != ctx.end() && *it != '}'; it++) { \
|
||||
switch (*it) { specs__ default : throw std::format_error("invalid format specification"); } \
|
||||
} \
|
||||
return it; \
|
||||
}
|
||||
|
||||
#define AQ_FORMAT_FLAG(spec__, flag__) \
|
||||
case spec__: (flag__) = true; break;
|
||||
|
||||
#define AQ_FORMAT_NUMBER(buf__) \
|
||||
case '0': \
|
||||
case '1': \
|
||||
case '2': \
|
||||
case '3': \
|
||||
case '4': \
|
||||
case '5': \
|
||||
case '6': \
|
||||
case '7': \
|
||||
case '8': \
|
||||
case '9': (buf__).push_back(*it); break;
|
||||
|
||||
/**
|
||||
format specification
|
||||
- 'j', as json array
|
||||
- 'X', same as std::format("{}x{}", vec.x, vec.y)
|
||||
- number, floating point precision, use `0` to format as integer
|
||||
*/
|
||||
template <typename CharT>
|
||||
struct std::formatter<Hyprutils::Math::Vector2D, CharT> : std::formatter<CharT> {
|
||||
bool formatJson = false;
|
||||
bool formatX = false;
|
||||
std::string precision = "";
|
||||
AQ_FORMAT_PARSE(AQ_FORMAT_FLAG('j', formatJson) //
|
||||
AQ_FORMAT_FLAG('X', formatX) //
|
||||
AQ_FORMAT_NUMBER(precision),
|
||||
Hyprutils::Math::Vector2D)
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const Hyprutils::Math::Vector2D& vec, FormatContext& ctx) const {
|
||||
std::string formatString = precision.empty() ? "{}" : std::format("{{:.{}f}}", precision);
|
||||
|
||||
if (formatJson)
|
||||
formatString = std::format("[{0}, {0}]", formatString);
|
||||
else if (formatX)
|
||||
formatString = std::format("{0}x{0}", formatString);
|
||||
else
|
||||
formatString = std::format("[Vector2D: x: {0}, y: {0}]", formatString);
|
||||
try {
|
||||
string buf = std::vformat(formatString, std::make_format_args(vec.x, vec.y));
|
||||
return std::format_to(ctx.out(), "{}", buf);
|
||||
} catch (std::format_error& e) { return std::format_to(ctx.out(), "[{}, {}]", vec.x, vec.y); }
|
||||
}
|
||||
};
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
lib,
|
||||
stdenv,
|
||||
cmake,
|
||||
pkg-config,
|
||||
pixman,
|
||||
version ? "git",
|
||||
doCheck ? false,
|
||||
}:
|
||||
|
|
@ -10,7 +12,14 @@ stdenv.mkDerivation {
|
|||
inherit version doCheck;
|
||||
src = ../.;
|
||||
|
||||
nativeBuildInputs = [cmake];
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
pkg-config
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
pixman
|
||||
];
|
||||
|
||||
outputs = ["out" "dev"];
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,225 @@
|
|||
#include <hyprutils/math/Box.hpp>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#define VECINRECT(vec, x1, y1, x2, y2) ((vec).x >= (x1) && (vec).x < (x2) && (vec).y >= (y1) && (vec).y < (y2))
|
||||
|
||||
using namespace Hyprutils::Math;
|
||||
|
||||
constexpr double HALF = 0.5;
|
||||
constexpr double DOUBLE = 2.0;
|
||||
constexpr double EPSILON = 1e-9;
|
||||
|
||||
CBox& Hyprutils::Math::CBox::scale(double scale) {
|
||||
x *= scale;
|
||||
y *= scale;
|
||||
w *= scale;
|
||||
h *= scale;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CBox& Hyprutils::Math::CBox::scale(const Vector2D& scale) {
|
||||
x *= scale.x;
|
||||
y *= scale.y;
|
||||
w *= scale.x;
|
||||
h *= scale.y;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CBox& Hyprutils::Math::CBox::translate(const Vector2D& vec) {
|
||||
x += vec.x;
|
||||
y += vec.y;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector2D Hyprutils::Math::CBox::middle() const {
|
||||
return Vector2D{x + w * HALF, y + h * HALF};
|
||||
}
|
||||
|
||||
bool Hyprutils::Math::CBox::containsPoint(const Vector2D& vec) const {
|
||||
return VECINRECT(vec, x, y, x + w, y + h);
|
||||
}
|
||||
|
||||
bool Hyprutils::Math::CBox::empty() const {
|
||||
return std::fabs(w) < EPSILON || std::fabs(h) < EPSILON;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CBox& Hyprutils::Math::CBox::transform(const eTransform t, double w, double h) {
|
||||
|
||||
CBox temp = *this;
|
||||
|
||||
if (t % 2 == 0) {
|
||||
width = temp.width;
|
||||
height = temp.height;
|
||||
} else {
|
||||
width = temp.height;
|
||||
height = temp.width;
|
||||
}
|
||||
|
||||
switch (t) {
|
||||
case HYPRUTILS_TRANSFORM_NORMAL:
|
||||
x = temp.x;
|
||||
y = temp.y;
|
||||
break;
|
||||
case HYPRUTILS_TRANSFORM_90:
|
||||
x = h - temp.y - temp.height;
|
||||
y = temp.x;
|
||||
break;
|
||||
case HYPRUTILS_TRANSFORM_180:
|
||||
x = w - temp.x - temp.width;
|
||||
y = h - temp.y - temp.height;
|
||||
break;
|
||||
case HYPRUTILS_TRANSFORM_270:
|
||||
x = temp.y;
|
||||
y = w - temp.x - temp.width;
|
||||
break;
|
||||
case HYPRUTILS_TRANSFORM_FLIPPED:
|
||||
x = w - temp.x - temp.width;
|
||||
y = temp.y;
|
||||
break;
|
||||
case HYPRUTILS_TRANSFORM_FLIPPED_90:
|
||||
x = temp.y;
|
||||
y = temp.x;
|
||||
break;
|
||||
case HYPRUTILS_TRANSFORM_FLIPPED_180:
|
||||
x = temp.x;
|
||||
y = h - temp.y - temp.height;
|
||||
break;
|
||||
case HYPRUTILS_TRANSFORM_FLIPPED_270:
|
||||
x = h - temp.y - temp.height;
|
||||
y = w - temp.x - temp.width;
|
||||
break;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CBox& Hyprutils::Math::CBox::addExtents(const SBoxExtents& e) {
|
||||
x -= e.topLeft.x;
|
||||
y -= e.topLeft.y;
|
||||
w += e.topLeft.x + e.bottomRight.x;
|
||||
h += e.topLeft.y + e.bottomRight.y;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CBox& Hyprutils::Math::CBox::scaleFromCenter(double scale) {
|
||||
double oldW = w, oldH = h;
|
||||
|
||||
w *= scale;
|
||||
h *= scale;
|
||||
|
||||
x -= (w - oldW) * HALF;
|
||||
y -= (h - oldH) * HALF;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CBox& Hyprutils::Math::CBox::expand(const double& value) {
|
||||
x -= value;
|
||||
y -= value;
|
||||
w += value * DOUBLE;
|
||||
h += value * DOUBLE;
|
||||
|
||||
if (w <= EPSILON || h <= EPSILON) {
|
||||
w = 0;
|
||||
h = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CBox& Hyprutils::Math::CBox::noNegativeSize() {
|
||||
w = std::clamp(w, 0.0, std::numeric_limits<double>::infinity());
|
||||
h = std::clamp(h, 0.0, std::numeric_limits<double>::infinity());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CBox Hyprutils::Math::CBox::intersection(const CBox& other) const {
|
||||
const double newX = std::max(x, other.x);
|
||||
const double newY = std::max(y, other.y);
|
||||
const double newBottom = std::min(y + h, other.y + other.h);
|
||||
const double newRight = std::min(x + w, other.x + other.w);
|
||||
double newW = newRight - newX;
|
||||
double newH = newBottom - newY;
|
||||
|
||||
if (newW <= EPSILON || newH <= EPSILON) {
|
||||
newW = 0;
|
||||
newH = 0;
|
||||
}
|
||||
|
||||
return {newX, newY, newW, newH};
|
||||
}
|
||||
|
||||
bool Hyprutils::Math::CBox::overlaps(const CBox& other) const {
|
||||
return (other.x + other.w >= x) && (x + w >= other.x) && (other.y + other.h >= y) && (y + h >= other.y);
|
||||
}
|
||||
|
||||
bool Hyprutils::Math::CBox::inside(const CBox& bound) const {
|
||||
return bound.x < x && bound.y < y && x + w < bound.x + bound.w && y + h < bound.y + bound.h;
|
||||
}
|
||||
|
||||
CBox Hyprutils::Math::CBox::roundInternal() {
|
||||
double flooredX = std::floor(x);
|
||||
double flooredY = std::floor(y);
|
||||
double newW = x + w - flooredX;
|
||||
double newH = y + h - flooredY;
|
||||
|
||||
return CBox{flooredX, flooredY, std::floor(newW), std::floor(newH)};
|
||||
}
|
||||
|
||||
CBox Hyprutils::Math::CBox::copy() const {
|
||||
return CBox{*this};
|
||||
}
|
||||
|
||||
Vector2D Hyprutils::Math::CBox::pos() const {
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
Vector2D Hyprutils::Math::CBox::size() const {
|
||||
return {w, h};
|
||||
}
|
||||
|
||||
Vector2D Hyprutils::Math::CBox::closestPoint(const Vector2D& vec) const {
|
||||
if (containsPoint(vec))
|
||||
return vec;
|
||||
|
||||
Vector2D nv = vec;
|
||||
nv.x = std::clamp(nv.x, x, x + w);
|
||||
nv.y = std::clamp(nv.y, y, y + h);
|
||||
|
||||
if (std::fabs(nv.x - x) < EPSILON)
|
||||
nv.x = x;
|
||||
else if (std::fabs(nv.x - (x + w)) < EPSILON)
|
||||
nv.x = x + w;
|
||||
|
||||
if (std::fabs(nv.y - y) < EPSILON)
|
||||
nv.y = y;
|
||||
else if (std::fabs(nv.y - (y + h)) < EPSILON)
|
||||
nv.y = y + h;
|
||||
|
||||
return nv;
|
||||
}
|
||||
|
||||
SBoxExtents Hyprutils::Math::CBox::extentsFrom(const CBox& small) {
|
||||
return {{small.x - x, small.y - y}, {w - small.w - (small.x - x), h - small.h - (small.y - y)}};
|
||||
}
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
#include <hyprutils/math/Region.hpp>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Hyprutils::Math;
|
||||
|
||||
constexpr const int64_t MAX_REGION_SIDE = 10000000;
|
||||
|
||||
Hyprutils::Math::CRegion::CRegion() {
|
||||
pixman_region32_init(&m_rRegion);
|
||||
}
|
||||
|
||||
Hyprutils::Math::CRegion::CRegion(const pixman_region32_t* const ref) {
|
||||
pixman_region32_init(&m_rRegion);
|
||||
pixman_region32_copy(&m_rRegion, ref);
|
||||
}
|
||||
|
||||
Hyprutils::Math::CRegion::CRegion(double x, double y, double w, double h) {
|
||||
pixman_region32_init_rect(&m_rRegion, x, y, w, h);
|
||||
}
|
||||
|
||||
Hyprutils::Math::CRegion::CRegion(const CBox& box) {
|
||||
pixman_region32_init_rect(&m_rRegion, box.x, box.y, box.w, box.h);
|
||||
}
|
||||
|
||||
Hyprutils::Math::CRegion::CRegion(pixman_box32_t* box) {
|
||||
pixman_region32_init_rect(&m_rRegion, box->x1, box->y1, box->x2 - box->x1, box->y2 - box->y1);
|
||||
}
|
||||
|
||||
Hyprutils::Math::CRegion::CRegion(const CRegion& other) {
|
||||
pixman_region32_init(&m_rRegion);
|
||||
pixman_region32_copy(&m_rRegion, const_cast<CRegion*>(&other)->pixman());
|
||||
}
|
||||
|
||||
Hyprutils::Math::CRegion::CRegion(CRegion&& other) {
|
||||
pixman_region32_init(&m_rRegion);
|
||||
pixman_region32_copy(&m_rRegion, other.pixman());
|
||||
}
|
||||
|
||||
Hyprutils::Math::CRegion::~CRegion() {
|
||||
pixman_region32_fini(&m_rRegion);
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::clear() {
|
||||
pixman_region32_clear(&m_rRegion);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::set(const CRegion& other) {
|
||||
pixman_region32_copy(&m_rRegion, const_cast<CRegion*>(&other)->pixman());
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::add(const CRegion& other) {
|
||||
pixman_region32_union(&m_rRegion, &m_rRegion, const_cast<CRegion*>(&other)->pixman());
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::add(double x, double y, double w, double h) {
|
||||
pixman_region32_union_rect(&m_rRegion, &m_rRegion, x, y, w, h);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::add(const CBox& other) {
|
||||
pixman_region32_union_rect(&m_rRegion, &m_rRegion, other.x, other.y, other.w, other.h);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::subtract(const CRegion& other) {
|
||||
pixman_region32_subtract(&m_rRegion, &m_rRegion, const_cast<CRegion*>(&other)->pixman());
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::intersect(const CRegion& other) {
|
||||
pixman_region32_intersect(&m_rRegion, &m_rRegion, const_cast<CRegion*>(&other)->pixman());
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::intersect(double x, double y, double w, double h) {
|
||||
pixman_region32_intersect_rect(&m_rRegion, &m_rRegion, x, y, w, h);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::invert(pixman_box32_t* box) {
|
||||
pixman_region32_inverse(&m_rRegion, &m_rRegion, box);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::invert(const CBox& box) {
|
||||
pixman_box32 pixmanBox = {(int32_t)box.x, (int32_t)box.y, (int32_t)box.w + (int32_t)box.x, (int32_t)box.h + (int32_t)box.y};
|
||||
return this->invert(&pixmanBox);
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::translate(const Vector2D& vec) {
|
||||
pixman_region32_translate(&m_rRegion, vec.x, vec.y);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::transform(const eTransform t, double w, double h) {
|
||||
if (t == HYPRUTILS_TRANSFORM_NORMAL)
|
||||
return *this;
|
||||
|
||||
auto rects = getRects();
|
||||
|
||||
clear();
|
||||
|
||||
for (auto& r : rects) {
|
||||
CBox xfmd{(double)r.x1, (double)r.y1, (double)r.x2 - r.x1, (double)r.y2 - r.y1};
|
||||
xfmd.transform(t, w, h);
|
||||
add(xfmd);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::rationalize() {
|
||||
intersect(CBox{-MAX_REGION_SIDE, -MAX_REGION_SIDE, MAX_REGION_SIDE * 2, MAX_REGION_SIDE * 2});
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion Hyprutils::Math::CRegion::copy() const {
|
||||
return CRegion(*this);
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::scale(float scale_) {
|
||||
scale({scale_, scale_});
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRegion& Hyprutils::Math::CRegion::scale(const Vector2D& scale) {
|
||||
if (scale == Vector2D{1, 1})
|
||||
return *this;
|
||||
|
||||
auto rects = getRects();
|
||||
|
||||
clear();
|
||||
|
||||
for (auto& r : rects) {
|
||||
r.x1 = std::floor(r.x1 * scale.x);
|
||||
r.y1 = std::floor(r.y1 * scale.x);
|
||||
r.x2 = std::ceil(r.x2 * scale.x);
|
||||
r.y2 = std::ceil(r.y2 * scale.x);
|
||||
add(&r);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<pixman_box32_t> Hyprutils::Math::CRegion::getRects() const {
|
||||
std::vector<pixman_box32_t> result;
|
||||
|
||||
int rectsNum = 0;
|
||||
const auto RECTSARR = pixman_region32_rectangles(&m_rRegion, &rectsNum);
|
||||
|
||||
result.assign(RECTSARR, RECTSARR + rectsNum);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CBox Hyprutils::Math::CRegion::getExtents() {
|
||||
pixman_box32_t* box = pixman_region32_extents(&m_rRegion);
|
||||
return {(double)box->x1, (double)box->y1, (double)box->x2 - box->x1, (double)box->y2 - box->y1};
|
||||
}
|
||||
|
||||
bool Hyprutils::Math::CRegion::containsPoint(const Vector2D& vec) const {
|
||||
return pixman_region32_contains_point(&m_rRegion, vec.x, vec.y, nullptr);
|
||||
}
|
||||
|
||||
bool Hyprutils::Math::CRegion::empty() const {
|
||||
return !pixman_region32_not_empty(&m_rRegion);
|
||||
}
|
||||
|
||||
Vector2D Hyprutils::Math::CRegion::closestPoint(const Vector2D& vec) const {
|
||||
if (containsPoint(vec))
|
||||
return vec;
|
||||
|
||||
double bestDist = __FLT_MAX__;
|
||||
Vector2D leader = vec;
|
||||
|
||||
for (auto& box : getRects()) {
|
||||
double x = 0, y = 0;
|
||||
|
||||
if (vec.x >= box.x2)
|
||||
x = box.x2 - 1;
|
||||
else if (vec.x < box.x1)
|
||||
x = box.x1;
|
||||
else
|
||||
x = vec.x;
|
||||
|
||||
if (vec.y >= box.y2)
|
||||
y = box.y2 - 1;
|
||||
else if (vec.y < box.y1)
|
||||
y = box.y1;
|
||||
else
|
||||
y = vec.y;
|
||||
|
||||
double distance = pow(x, 2) + pow(y, 2);
|
||||
if (distance < bestDist) {
|
||||
bestDist = distance;
|
||||
leader = {x, y};
|
||||
}
|
||||
}
|
||||
|
||||
return leader;
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#include <hyprutils/math/Vector2D.hpp>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Hyprutils::Math;
|
||||
|
||||
Hyprutils::Math::Vector2D::Vector2D(double xx, double yy) {
|
||||
x = xx;
|
||||
y = yy;
|
||||
}
|
||||
|
||||
Hyprutils::Math::Vector2D::Vector2D(int xx, int yy) {
|
||||
x = (double)xx;
|
||||
y = (double)yy;
|
||||
}
|
||||
|
||||
Hyprutils::Math::Vector2D::Vector2D() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
Hyprutils::Math::Vector2D::~Vector2D() {}
|
||||
|
||||
double Hyprutils::Math::Vector2D::normalize() {
|
||||
// get max abs
|
||||
const auto max = std::abs(x) > std::abs(y) ? std::abs(x) : std::abs(y);
|
||||
|
||||
x /= max;
|
||||
y /= max;
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
Vector2D Hyprutils::Math::Vector2D::floor() const {
|
||||
return Vector2D(std::floor(x), std::floor(y));
|
||||
}
|
||||
|
||||
Vector2D Hyprutils::Math::Vector2D::round() const {
|
||||
return Vector2D(std::round(x), std::round(y));
|
||||
}
|
||||
|
||||
Vector2D Hyprutils::Math::Vector2D::clamp(const Vector2D& min, const Vector2D& max) const {
|
||||
return Vector2D(std::clamp(this->x, min.x, max.x < min.x ? INFINITY : max.x), std::clamp(this->y, min.y, max.y < min.y ? INFINITY : max.y));
|
||||
}
|
||||
|
||||
double Hyprutils::Math::Vector2D::distance(const Vector2D& other) const {
|
||||
return std::sqrt(distanceSq(other));
|
||||
}
|
||||
|
||||
double Hyprutils::Math::Vector2D::distanceSq(const Vector2D& other) const {
|
||||
return (x - other.x) * (x - other.x) + (y - other.y) * (y - other.y);
|
||||
}
|
||||
|
||||
double Hyprutils::Math::Vector2D::size() const {
|
||||
return std::sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
Vector2D Hyprutils::Math::Vector2D::getComponentMax(const Vector2D& other) const {
|
||||
return Vector2D(std::max(this->x, other.x), std::max(this->y, other.y));
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
#include <hyprutils/math/Region.hpp>
|
||||
#include "shared.hpp"
|
||||
|
||||
using namespace Hyprutils::Math;
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
CRegion rg = {0, 0, 100, 100};
|
||||
rg.add(CBox{{}, {20, 200}});
|
||||
|
||||
int ret = 0;
|
||||
|
||||
EXPECT(rg.getExtents().height, 200);
|
||||
EXPECT(rg.getExtents().width, 100);
|
||||
|
||||
rg.intersect(CBox{10, 10, 300, 300});
|
||||
|
||||
EXPECT(rg.getExtents().width, 90);
|
||||
EXPECT(rg.getExtents().height, 190);
|
||||
|
||||
/*Box.cpp test cases*/
|
||||
// Test default constructor and accessors
|
||||
{
|
||||
CBox box1;
|
||||
EXPECT(box1.x, 0);
|
||||
EXPECT(box1.y, 0);
|
||||
EXPECT(box1.width, 0);
|
||||
EXPECT(box1.height, 0);
|
||||
|
||||
// Test parameterized constructor and accessors
|
||||
CBox box2(10, 20, 30, 40);
|
||||
EXPECT(box2.x, 10);
|
||||
EXPECT(box2.y, 20);
|
||||
EXPECT(box2.width, 30);
|
||||
EXPECT(box2.height, 40);
|
||||
|
||||
// Test setters and getters
|
||||
box2.translate(Vector2D(5, -5));
|
||||
EXPECT_VECTOR2D(box2.pos(), Vector2D(15, 15));
|
||||
}
|
||||
|
||||
//Test Scaling and Transformation
|
||||
{
|
||||
CBox box(10, 10, 20, 30);
|
||||
|
||||
// Test scaling
|
||||
box.scale(2.0);
|
||||
EXPECT_VECTOR2D(box.size(), Vector2D(40, 60));
|
||||
EXPECT_VECTOR2D(box.pos(), Vector2D(20, 20));
|
||||
|
||||
// Test scaling from center
|
||||
box.scaleFromCenter(0.5);
|
||||
EXPECT_VECTOR2D(box.size(), Vector2D(20, 30));
|
||||
EXPECT_VECTOR2D(box.pos(), Vector2D(30, 35));
|
||||
|
||||
// Test transformation
|
||||
box.transform(HYPRUTILS_TRANSFORM_90, 100, 200);
|
||||
EXPECT_VECTOR2D(box.pos(), Vector2D(135, 30));
|
||||
EXPECT_VECTOR2D(box.size(), Vector2D(30, 20));
|
||||
|
||||
// Test Intersection and Extents
|
||||
}
|
||||
|
||||
{
|
||||
CBox box1(0, 0, 100, 100);
|
||||
CBox box2(50, 50, 100, 100);
|
||||
|
||||
CBox intersection = box1.intersection(box2);
|
||||
EXPECT_VECTOR2D(intersection.pos(), Vector2D(50, 50));
|
||||
EXPECT_VECTOR2D(intersection.size(), Vector2D(50, 50));
|
||||
|
||||
SBoxExtents extents = box1.extentsFrom(box2);
|
||||
EXPECT_VECTOR2D(extents.topLeft, Vector2D(50, 50));
|
||||
EXPECT_VECTOR2D(extents.bottomRight, Vector2D(-50, -50));
|
||||
}
|
||||
|
||||
// Test Boundary Conditions and Special Cases
|
||||
{
|
||||
CBox box(0, 0, 50, 50);
|
||||
|
||||
EXPECT(box.empty(), false);
|
||||
|
||||
EXPECT(box.containsPoint(Vector2D(25, 25)), true);
|
||||
EXPECT(box.containsPoint(Vector2D(60, 60)), false);
|
||||
EXPECT(box.overlaps(CBox(25, 25, 50, 50)), true);
|
||||
EXPECT(box.inside(CBox(0, 0, 100, 100)), false);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -18,3 +18,15 @@ namespace Colors {
|
|||
} else { \
|
||||
std::cout << Colors::GREEN << "Passed " << Colors::RESET << #expr << ". Got " << val << "\n"; \
|
||||
}
|
||||
#define EXPECT_VECTOR2D(expr, val) \
|
||||
do { \
|
||||
const auto& RESULT = expr; \
|
||||
const auto& EXPECTED = val; \
|
||||
if (!(std::abs(RESULT.x - EXPECTED.x) < 1e-6 && std::abs(RESULT.y - EXPECTED.y) < 1e-6)) { \
|
||||
std::cout << Colors::RED << "Failed: " << Colors::RESET << #expr << ", expected (" << EXPECTED.x << ", " << EXPECTED.y << ") but got (" << RESULT.x << ", " \
|
||||
<< RESULT.y << ")\n"; \
|
||||
ret = 1; \
|
||||
} else { \
|
||||
std::cout << Colors::GREEN << "Passed " << Colors::RESET << #expr << ". Got (" << RESULT.x << ", " << RESULT.y << ")\n"; \
|
||||
} \
|
||||
} while (0)
|
||||
|
|
|
|||
Loading…
Reference in New Issue