Files
NXKit/Submodules/UIKit/lib/Geometry.cpp
T
Daniil Vinogradov bc48a8f62e WIP: CGImage
2024-12-14 20:15:23 +01:00

289 lines
8.4 KiB
C++

#include <Geometry.h>
#include <algorithm>
#include "NXAffineTransform.h"
#include "NXTransform3D.h"
using namespace NXKit;
// MARK: - PRIVATE -
float min(NXFloat a, NXFloat b, NXFloat c, NXFloat d) {
auto minValue = (a < b) ? a : b;
minValue = (minValue < c) ? minValue : c;
minValue = (minValue < d) ? minValue : d;
return minValue;
}
float max(NXFloat a, NXFloat b, NXFloat c, NXFloat d) {
auto maxValue = (a > b) ? a : b;
maxValue = (maxValue > c) ? maxValue : c;
maxValue = (maxValue > d) ? maxValue : d;
return maxValue;
}
float isEqual(NXFloat val1, NXFloat val2) {
if (isnan(val1) && isnan(val2))
return true;
return val1 == val2;
}
// MARK: - POINT -
NXPoint NXPoint::zero = NXPoint();
NXPoint::NXPoint(): x(0), y(0) { }
NXPoint::NXPoint(NXFloat x, NXFloat y): x(x), y(y) { }
bool NXPoint::operator==(const NXPoint& rhs) const {
return this->x == rhs.x && this->y == rhs.y;
}
NXPoint NXPoint::operator+(const NXPoint& first) const {
return NXPoint(x + first.x, y + first.y);
}
NXPoint NXPoint::operator-(const NXPoint& first) const {
return NXPoint(x - first.x, y - first.y);
}
NXPoint& NXPoint::operator+=(const NXPoint& rhs) {
this->x += rhs.x;
this->y += rhs.y;
return *this;
}
NXPoint& NXPoint::operator-=(const NXPoint& rhs) {
this->x -= rhs.x;
this->y -= rhs.y;
return *this;
}
NXPoint NXPoint::operator/(const NXFloat& rhs) {
auto res = *this;
res.x /= rhs;
res.y /= rhs;
return res;
}
NXPoint NXPoint::operator*(const NXFloat& rhs) {
auto res = *this;
res.x *= rhs;
res.y *= rhs;
return res;
}
NXPoint NXPoint::applying(const NXAffineTransform& t) const {
return NXPoint(
x * t.m11 + y * t.m21 + t.tX,
x * t.m12 + y * t.m22 + t.tY
);
}
bool NXPoint::valid() {
return !isnan(this->x) && !isnan(this->y);
}
NXFloat NXPoint::magnitude() {
return sqrt(x * x + y * y);
}
// MARK: - NXSize
NXSize::NXSize(): NXSize(0, 0) {}
NXSize::NXSize(NXFloat width, NXFloat height): width(width), height(height) {}
bool NXSize::operator==(const NXSize& rhs) const {
return isEqual(this->width, rhs.width) && isEqual(this->height, rhs.height);
}
bool NXSize::operator!=(const NXSize& rhs) const {
return !isEqual(this->width, rhs.width) || !isEqual(this->height, rhs.height);
}
NXSize NXSize::operator+(const NXSize& first) const {
return NXSize(width + first.width, height + first.height);
}
NXSize NXSize::operator-(const NXSize& first) const {
return NXSize(width - first.width, height - first.height);
}
NXSize& NXSize::operator+=(const NXSize& rhs) {
this->width += rhs.width;
this->height += rhs.height;
return *this;
}
NXSize& NXSize::operator-=(const NXSize& rhs) {
this->width -= rhs.width;
this->height -= rhs.height;
return *this;
}
NXSize NXSize::operator*(const NXFloat& first) const {
return NXSize(width * first, height * first);
}
NXSize NXSize::operator/(const NXFloat& first) const {
return NXSize(width / first, height / first);
}
NXSize& NXSize::operator*=(const NXFloat& rhs) {
this->width *= rhs;
this->height *= rhs;
return *this;
}
NXSize& NXSize::operator/=(const NXFloat& rhs) {
this->width /= rhs;
this->height /= rhs;
return *this;
}
bool NXSize::valid() {
return !isnan(this->width) && !isnan(this->height);
}
// MARK: - RECT -
NXRect::NXRect(): origin(), size() { }
NXRect::NXRect(NXPoint origin, NXSize size): origin(origin), size(size) { }
NXRect::NXRect(float x, float y, float width, float height): origin(x, y), size(width, height) { }
float NXRect::width() const { return size.width; }
float NXRect::height() const { return size.height; }
float NXRect::minX() const { return origin.x; }
float NXRect::midX() const { return origin.x + size.width / 2; }
float NXRect::maxX() const { return origin.x + size.width; }
float NXRect::minY() const { return origin.y; }
float NXRect::midY() const { return origin.y + size.height / 2; }
float NXRect::maxY() const { return origin.y + size.height; }
void NXRect::setWidth(float newValue) { size.width = newValue; }
void NXRect::setHeight(float newValue) { size.height = newValue; }
void NXRect::setMinX(float newValue) { origin.x = newValue; }
void NXRect::setMidX(float newValue) { origin.x = newValue - (size.width / 2); }
void NXRect::setMaxX(float newValue) { origin.x = newValue - size.width; }
void NXRect::setMinY(float newValue) { origin.y = newValue; }
void NXRect::setMidY(float newValue) { origin.y = newValue - (size.height / 2); }
void NXRect::setMaxY(float newValue) { origin.y = newValue - size.height; }
bool NXRect::contains(NXPoint point) {
return
(point.x >= minX()) && (point.x < maxX()) &&
(point.y >= minY()) && (point.y < maxY());
}
bool NXRect::intersects(const NXRect& other) const {
return !((minX() > other.maxX() || maxX() < other.minX()) || (minY() > other.maxY() || maxY() < other.minY()));
}
NXRect& NXRect::offsetBy(const NXPoint& offset) {
origin.x += offset.x;
origin.y += offset.y;
return *this;
}
NXRect& NXRect::offsetBy(const float& offsetX, const float& offsetY) {
origin.x += offsetX;
origin.y += offsetY;
return *this;
}
bool NXRect::operator==(const NXRect& rhs) const {
return
this->origin.x == rhs.origin.x && this->origin.y == rhs.origin.y &&
this->size.width == rhs.size.width && this->size.height == rhs.size.height;
}
NXRect NXRect::operator+(const NXRect& rhs) const {
return NXRect(
this->origin.x + rhs.origin.x,
this->origin.y + rhs.origin.y,
this->size.width + rhs.size.width,
this->size.height + rhs.size.height
);
}
NXRect NXRect::operator-(const NXRect& rhs) const {
return NXRect(
this->origin.x - rhs.origin.x,
this->origin.y - rhs.origin.y,
this->size.width - rhs.size.width,
this->size.height - rhs.size.height
);
}
NXRect NXRect::operator*(const float& rhs) const {
return NXRect(
this->origin.x * rhs,
this->origin.y * rhs,
this->size.width * rhs,
this->size.height * rhs
);
}
NXRect NXRect::applying(NXAffineTransform t) {
if (t.isIdentity()) { return *this; }
auto newTopLeft = NXPoint(minX(), minY()).applying(t);
auto newTopRight = NXPoint(maxX(), minY()).applying(t);
auto newBottomLeft = NXPoint(minX(), maxY()).applying(t);
auto newBottomRight = NXPoint(maxX(), maxY()).applying(t);
auto newMinX = min(newTopLeft.x, newTopRight.x, newBottomLeft.x, newBottomRight.x);
auto newMaxX = max(newTopLeft.x, newTopRight.x, newBottomLeft.x, newBottomRight.x);
auto newMinY = min(newTopLeft.y, newTopRight.y, newBottomLeft.y, newBottomRight.y);
auto newMaxY = max(newTopLeft.y, newTopRight.y, newBottomLeft.y, newBottomRight.y);
// XXX: What happens if the point that was furthest left is now on the right (because of a rotation)?
// i.e. Should do we return a normalised rect or one with a negative width?
return NXRect(newMinX,
newMinY,
newMaxX - newMinX,
newMaxY - newMinY);
}
NXRect NXRect::applying(NXTransform3D t) {
if (t == NXTransform3DIdentity) { return *this; }
auto topLeft = t.transformingVector(minX(), minY(), 0);
auto topRight = t.transformingVector(maxX(), minY(), 0);
auto bottomLeft = t.transformingVector(minX(), maxY(), 0);
auto bottomRight = t.transformingVector(maxX(), maxY(), 0);
auto newMinX = min(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x);
auto newMaxX = max(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x);
auto newMinY = min(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y);
auto newMaxY = max(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y);
return NXRect(newMinX, newMinY, newMaxX - newMinX, newMaxY - newMinY);
}
NXRect NXRect::intersection(NXRect other) const {
auto largestMinX = fmaxf(minX(), other.minX());
auto largestMinY = fmaxf(minY(), other.minY());
auto smallestMaxX = fmaxf(maxX(), other.maxX());
auto smallestMaxY = fmaxf(maxY(), other.maxY());
auto width = smallestMaxX - largestMinX;
auto height = smallestMaxY - largestMinY;
if (width > 0 && height > 0) {
// The intersection rectangle has dimensions, i.e. there is an intersection:
return NXRect(largestMinX, largestMinY, width, height);
} else {
return null;
}
}
bool NXRect::isNull() const {
return *this == null;
}
NXRect NXRect::null = NXRect(INFINITY, INFINITY, 0, 0);