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 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})
|
||||
|
|
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