mirror of
https://github.com/hyprwm/hyprutils.git
synced 2024-12-22 10:49:48 +01:00
Math: init Region, Vector2D and Box
This commit is contained in:
parent
a8f9373474
commit
e9d4a99e13
9 changed files with 852 additions and 3 deletions
|
@ -30,6 +30,9 @@ endif()
|
||||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "include/*.hpp")
|
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "include/*.hpp")
|
||||||
file(GLOB_RECURSE PUBLIC_HEADERS CONFIGURE_DEPENDS "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})
|
add_library(hyprutils SHARED ${SRCFILES})
|
||||||
target_include_directories( hyprutils
|
target_include_directories( hyprutils
|
||||||
PUBLIC "./include"
|
PUBLIC "./include"
|
||||||
|
@ -39,25 +42,31 @@ set_target_properties(hyprutils PROPERTIES
|
||||||
VERSION ${hyprutils_VERSION}
|
VERSION ${hyprutils_VERSION}
|
||||||
SOVERSION 0
|
SOVERSION 0
|
||||||
)
|
)
|
||||||
|
target_link_libraries(hyprutils PkgConfig::deps)
|
||||||
|
|
||||||
# tests
|
# tests
|
||||||
add_custom_target(tests)
|
add_custom_target(tests)
|
||||||
|
|
||||||
add_executable(hyprutils_memory "tests/memory.cpp")
|
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_test(NAME "Memory" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprutils_memory "memory")
|
||||||
add_dependencies(tests hyprutils_memory)
|
add_dependencies(tests hyprutils_memory)
|
||||||
|
|
||||||
add_executable(hyprutils_string "tests/string.cpp")
|
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_test(NAME "String" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprutils_string "string")
|
||||||
add_dependencies(tests hyprutils_string)
|
add_dependencies(tests hyprutils_string)
|
||||||
|
|
||||||
add_executable(hyprutils_signal "tests/signal.cpp")
|
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_test(NAME "Signal" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprutils_signal "signal")
|
||||||
add_dependencies(tests hyprutils_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
|
# Installation
|
||||||
install(TARGETS hyprutils)
|
install(TARGETS hyprutils)
|
||||||
install(DIRECTORY "include/hyprutils" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
install(DIRECTORY "include/hyprutils" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||||
|
|
106
include/hyprutils/math/Box.hpp
Normal file
106
include/hyprutils/math/Box.hpp
Normal file
|
@ -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();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
16
include/hyprutils/math/Misc.hpp
Normal file
16
include/hyprutils/math/Misc.hpp
Normal file
|
@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
70
include/hyprutils/math/Region.hpp
Normal file
70
include/hyprutils/math/Region.hpp
Normal file
|
@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
164
include/hyprutils/math/Vector2D.hpp
Normal file
164
include/hyprutils/math/Vector2D.hpp
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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 <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 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 <typename CharT>
|
||||||
|
struct std::formatter<Hyprutils::Math::Vector2D, CharT> : std::formatter<CharT> {
|
||||||
|
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 <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); }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef FORMAT_PARSE
|
||||||
|
#undef FORMAT_FLAG
|
||||||
|
#undef FORMAT_NUMBER
|
204
src/math/Box.cpp
Normal file
204
src/math/Box.cpp
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
#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;
|
||||||
|
|
||||||
|
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<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 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)}};
|
||||||
|
}
|
204
src/math/Region.cpp
Normal file
204
src/math/Region.cpp
Normal file
|
@ -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;
|
||||||
|
}
|
55
src/math/Vector2D.cpp
Normal file
55
src/math/Vector2D.cpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#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() {
|
||||||
|
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));
|
||||||
|
}
|
21
tests/math.cpp
Normal file
21
tests/math.cpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#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);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
Loading…
Reference in a new issue