diff --git a/CMakeLists.txt b/CMakeLists.txt index 95e9bd4..f78b65f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}) diff --git a/include/hyprutils/math/Box.hpp b/include/hyprutils/math/Box.hpp new file mode 100644 index 0000000..ea974c5 --- /dev/null +++ b/include/hyprutils/math/Box.hpp @@ -0,0 +1,106 @@ +#pragma once + +#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); + } + }; + + 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(); + }; + } +} \ No newline at end of file diff --git a/include/hyprutils/math/Misc.hpp b/include/hyprutils/math/Misc.hpp new file mode 100644 index 0000000..33f446f --- /dev/null +++ b/include/hyprutils/math/Misc.hpp @@ -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, + }; + } +} \ No newline at end of file diff --git a/include/hyprutils/math/Region.hpp b/include/hyprutils/math/Region.hpp new file mode 100644 index 0000000..856489b --- /dev/null +++ b/include/hyprutils/math/Region.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#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 getRects() const; + + // + pixman_region32_t* pixman() { + return &m_rRegion; + } + + private: + pixman_region32_t m_rRegion; + }; + } +} diff --git a/include/hyprutils/math/Vector2D.hpp b/include/hyprutils/math/Vector2D.hpp new file mode 100644 index 0000000..ecc2fa8 --- /dev/null +++ b/include/hyprutils/math/Vector2D.hpp @@ -0,0 +1,164 @@ +#pragma once + +#include +#include + +namespace Hyprutils { + namespace Math { + class Vector2D { + public: + Vector2D(double, double); + 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 FORMAT_PARSE(specs__, type__) \ + template \ + 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 FORMAT_FLAG(spec__, flag__) \ + case spec__: (flag__) = true; break; + +#define 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 +struct std::formatter : std::formatter { + bool formatJson = false; + bool formatX = false; + std::string precision = ""; + FORMAT_PARSE(FORMAT_FLAG('j', formatJson) // + FORMAT_FLAG('X', formatX) // + FORMAT_NUMBER(precision), + Hyprutils::Math::Vector2D) + + template + 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); } + } +}; + +#undef FORMAT_PARSE +#undef FORMAT_FLAG +#undef FORMAT_NUMBER diff --git a/src/math/Box.cpp b/src/math/Box.cpp new file mode 100644 index 0000000..2fa77a6 --- /dev/null +++ b/src/math/Box.cpp @@ -0,0 +1,204 @@ +#include +#include +#include +#include + +#define VECINRECT(vec, x1, y1, x2, y2) ((vec).x >= (x1) && (vec).x < (x2) && (vec).y >= (y1) && (vec).y < (y2)) + +using namespace Hyprutils::Math; + +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 / 2.0, y + h / 2.0}; +} + +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 w == 0 || h == 0; +} + +CBox& Hyprutils::Math::CBox::round() { + float newW = x + w - std::round(x); + float newH = y + h - std::round(y); + x = std::round(x); + y = std::round(y); + 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) / 2.0; + y -= (h - oldH) / 2.0; + + return *this; +} + +CBox& Hyprutils::Math::CBox::expand(const double& value) { + x -= value; + y -= value; + w += value * 2.0; + h += value * 2.0; + + if (w <= 0 || h <= 0) { + w = 0; + h = 0; + } + + return *this; +} + +CBox& Hyprutils::Math::CBox::noNegativeSize() { + w = std::clamp(w, 0.0, std::numeric_limits::infinity()); + h = std::clamp(h, 0.0, std::numeric_limits::infinity()); + + return *this; +} + +CBox Hyprutils::Math::CBox::intersection(const CBox& other) const { + const float newX = std::max(x, other.x); + const float newY = std::max(y, other.y); + const float newBottom = std::min(y + h, other.y + other.h); + const float newRight = std::min(x + w, other.x + other.w); + float newW = newRight - newX; + float newH = newBottom - newY; + + if (newW <= 0 || newH <= 0) { + 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() { + float newW = x + w - std::floor(x); + float newH = y + h - std::floor(y); + + return CBox{std::floor(x), std::floor(y), 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); + 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)}}; +} diff --git a/src/math/Region.cpp b/src/math/Region.cpp new file mode 100644 index 0000000..81eda8b --- /dev/null +++ b/src/math/Region.cpp @@ -0,0 +1,204 @@ +#include +#include + +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(&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(&other)->pixman()); + return *this; +} + +CRegion& Hyprutils::Math::CRegion::add(const CRegion& other) { + pixman_region32_union(&m_rRegion, &m_rRegion, const_cast(&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(&other)->pixman()); + return *this; +} + +CRegion& Hyprutils::Math::CRegion::intersect(const CRegion& other) { + pixman_region32_intersect(&m_rRegion, &m_rRegion, const_cast(&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 Hyprutils::Math::CRegion::getRects() const { + std::vector 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; +} \ No newline at end of file diff --git a/src/math/Vector2D.cpp b/src/math/Vector2D.cpp new file mode 100644 index 0000000..d59fe5e --- /dev/null +++ b/src/math/Vector2D.cpp @@ -0,0 +1,55 @@ +#include +#include +#include + +using namespace Hyprutils::Math; + +Hyprutils::Math::Vector2D::Vector2D(double xx, double yy) { + x = xx; + y = 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)); +} diff --git a/tests/math.cpp b/tests/math.cpp new file mode 100644 index 0000000..cb47189 --- /dev/null +++ b/tests/math.cpp @@ -0,0 +1,21 @@ +#include +#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); + + return ret; +} \ No newline at end of file