diff --git a/.clang-format b/.clang-format index 1b9e01e..4d990a5 100644 --- a/.clang-format +++ b/.clang-format @@ -63,4 +63,4 @@ SpacesInContainerLiterals: false SpacesInParentheses: false SpacesInSquareBrackets: false TabWidth: 2 -UseTab: Never +UseTab: Always diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..d6370b7 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,148 @@ +# Generated from CLion Inspection settings +--- +Checks: '-*, +bugprone-argument-comment, +bugprone-assert-side-effect, +bugprone-bad-signal-to-kill-thread, +bugprone-branch-clone, +bugprone-copy-constructor-init, +bugprone-dangling-handle, +bugprone-dynamic-static-initializers, +bugprone-fold-init-type, +bugprone-forward-declaration-namespace, +bugprone-forwarding-reference-overload, +bugprone-inaccurate-erase, +bugprone-incorrect-roundings, +bugprone-integer-division, +bugprone-lambda-function-name, +bugprone-macro-parentheses, +bugprone-macro-repeated-side-effects, +bugprone-misplaced-operator-in-strlen-in-alloc, +bugprone-misplaced-pointer-arithmetic-in-alloc, +bugprone-misplaced-widening-cast, +bugprone-move-forwarding-reference, +bugprone-multiple-statement-macro, +bugprone-no-escape, +bugprone-not-null-terminated-result, +bugprone-parent-virtual-call, +bugprone-posix-return, +bugprone-reserved-identifier, +bugprone-sizeof-container, +bugprone-sizeof-expression, +bugprone-spuriously-wake-up-functions, +bugprone-string-constructor, +bugprone-string-integer-assignment, +bugprone-string-literal-with-embedded-nul, +bugprone-suspicious-enum-usage, +bugprone-suspicious-include, +bugprone-suspicious-memset-usage, +bugprone-suspicious-missing-comma, +bugprone-suspicious-semicolon, +bugprone-suspicious-string-compare, +bugprone-suspicious-memory-comparison, +bugprone-suspicious-realloc-usage, +bugprone-swapped-arguments, +bugprone-terminating-continue, +bugprone-throw-keyword-missing, +bugprone-too-small-loop-variable, +bugprone-undefined-memory-manipulation, +bugprone-undelegated-constructor, +bugprone-unhandled-self-assignment, +bugprone-unused-raii, +bugprone-unused-return-value, +bugprone-use-after-move, +bugprone-virtual-near-miss, +cert-dcl21-cpp, +cert-dcl58-cpp, +cert-err34-c, +cert-err52-cpp, +cert-err60-cpp, +cert-flp30-c, +cert-msc50-cpp, +cert-msc51-cpp, +cert-str34-c, +cppcoreguidelines-interfaces-global-init, +cppcoreguidelines-narrowing-conversions, +cppcoreguidelines-pro-type-member-init, +cppcoreguidelines-pro-type-static-cast-downcast, +cppcoreguidelines-slicing, +google-default-arguments, +google-explicit-constructor, +google-runtime-operator, +hicpp-exception-baseclass, +hicpp-multiway-paths-covered, +misc-misplaced-const, +misc-new-delete-overloads, +misc-no-recursion, +misc-non-copyable-objects, +misc-throw-by-value-catch-by-reference, +misc-unconventional-assign-operator, +misc-uniqueptr-reset-release, +modernize-avoid-bind, +modernize-concat-nested-namespaces, +modernize-deprecated-headers, +modernize-deprecated-ios-base-aliases, +modernize-loop-convert, +modernize-make-shared, +modernize-make-unique, +modernize-pass-by-value, +modernize-raw-string-literal, +modernize-redundant-void-arg, +modernize-replace-auto-ptr, +modernize-replace-disallow-copy-and-assign-macro, +modernize-replace-random-shuffle, +modernize-return-braced-init-list, +modernize-shrink-to-fit, +modernize-unary-static-assert, +modernize-use-auto, +modernize-use-bool-literals, +modernize-use-emplace, +modernize-use-equals-default, +modernize-use-equals-delete, +modernize-use-nodiscard, +modernize-use-noexcept, +modernize-use-nullptr, +modernize-use-override, +modernize-use-transparent-functors, +modernize-use-uncaught-exceptions, +mpi-buffer-deref, +mpi-type-mismatch, +openmp-use-default-none, +performance-faster-string-find, +performance-for-range-copy, +performance-implicit-conversion-in-loop, +performance-inefficient-algorithm, +performance-inefficient-string-concatenation, +performance-inefficient-vector-operation, +performance-move-const-arg, +performance-move-constructor-init, +performance-no-automatic-move, +performance-noexcept-move-constructor, +performance-trivially-destructible, +performance-type-promotion-in-math-fn, +performance-unnecessary-copy-initialization, +performance-unnecessary-value-param, +portability-simd-intrinsics, +readability-avoid-const-params-in-decls, +readability-const-return-type, +readability-container-size-empty, +readability-convert-member-functions-to-static, +readability-delete-null-pointer, +readability-deleted-default, +readability-inconsistent-declaration-parameter-name, +readability-make-member-function-const, +readability-misleading-indentation, +readability-misplaced-array-index, +readability-non-const-parameter, +readability-redundant-control-flow, +readability-redundant-declaration, +readability-redundant-function-ptr-dereference, +readability-redundant-smartptr-get, +readability-redundant-string-cstr, +readability-redundant-string-init, +readability-simplify-subscript-expr, +readability-static-accessed-through-instance, +readability-static-definition-in-anonymous-namespace, +readability-string-compare, +readability-uniqueptr-delete-release, +readability-use-anyofallof' \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 46552d6..e1931f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,4 +13,5 @@ add_compile_definitions(MEM_DEBUG) add_subdirectory(Modules) add_subdirectory(Utils) add_subdirectory(Containers) +add_subdirectory(Math) #add_subdirectory(Allocators) \ No newline at end of file diff --git a/Containers/public/Tree.hpp b/Containers/public/Tree.hpp index 1d80a11..dfeefc2 100644 --- a/Containers/public/Tree.hpp +++ b/Containers/public/Tree.hpp @@ -140,7 +140,7 @@ namespace tp { return left; } - // recursively returns valid left or right child or root + // recursively returns valid isLeft or isRight child or root Node* insertUtil(Node* head, KeyArg key, DataArg data) { Node* insertedNode; diff --git a/Math/CMakeLists.txt b/Math/CMakeLists.txt new file mode 100644 index 0000000..abe8459 --- /dev/null +++ b/Math/CMakeLists.txt @@ -0,0 +1,22 @@ + +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_CXX_STANDARD 23) + +project(Math) + +### ---------------------- Static Library --------------------- ### +file(GLOB SOURCES "./private/*.cpp") +file(GLOB HEADERS "./public/*.hpp") +add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS}) +target_include_directories(${PROJECT_NAME} PUBLIC ./public/) +target_link_libraries(${PROJECT_NAME} PUBLIC Utils) + +### -------------------------- Tests -------------------------- ### +enable_testing() +file(GLOB TEST_SOURCES "./tests/*.cpp") +add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES}) +target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils) +add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests) + +install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib) \ No newline at end of file diff --git a/Math/private/Camera.cpp b/Math/private/Camera.cpp new file mode 100644 index 0000000..d024aab --- /dev/null +++ b/Math/private/Camera.cpp @@ -0,0 +1,132 @@ +#include "Camera.hpp" + +using namespace tp; + +Camera::Camera() { + lookAtPoint({ 0, 0, 0 }, { 2, 0, 0 }, { 0, 0, 1 }); +} + +Vec3F Camera::getTarget() const { return mTarget; } + +void Camera::offset_target(halnf val) { mTarget += (mPos - mTarget).normalize() * val; } + +Vec3F Camera::getForward() const { return (mTarget - mPos).normalize(); } + +Vec3F Camera::getUp() const { return mUp; } + +Vec3F& Camera::getPos() { return mPos; } + +halnf Camera::getFar() const { return mFar; } + +halnf Camera::getNear() const { return mNear; } + +halnf Camera::getRatio() const { return mRatio; } + +void Camera::setRatio(halnf in) { mRatio = in; } + +void Camera::setFOV(halnf in) { mFOV = in; } + +halnf Camera::getFOV() const { return mFOV; } + +Mat4F Camera::calculateTransformationMatrix() { + return calculateProjectionMatrix() * calculateViewMatrix(); +} + +Mat4F Camera::calculateViewMatrix() { + const Vec3F& F = (mPos - mTarget).unitV(); + const Vec3F& S = mUp * F; + const Vec3F& U = F * S; + const Vec3F& P = mPos; + + Mat4F out; + out[0] = Vec4F(S.x, S.y, S.z, -P.dot(S)); + out[1] = Vec4F(U.x, U.y, U.z, -P.dot(U)); + out[2] = Vec4F(F.x, F.y, F.z, -P.dot(F)); + out[3] = Vec4F(0, 0, 0, 1); + return out; +} + +Mat4F Camera::calculateProjectionMatrix() const { + auto r = (halnf) sqrt(mRatio); + halnf c = 1 / r; + auto s = halnf(1.f / tan(mFOV / 2.f)); + + Mat4F out; + out[0] = Vec4F(s * r, 0, 0, 0); + out[1] = Vec4F(0, s * c, 0, 0); + out[2] = Vec4F(0, 0, -2.f / (mFar - mNear), -(mFar + mNear) / (mFar - mNear)); + out[3] = Vec4F(0, 0, -1, 0); + return out; +} + +Vec3F Camera::project(Vec2F normalized) { + auto camMat = calculateTransformationMatrix(); + auto inv = camMat.inv(); + + halnf z = halnf((((mTarget - mPos).length() - mNear) / (mFar - mNear) - 1.f / 2) * 2.f); + halnf w = halnf((mTarget - mPos).length()); + Vec4 world_pos4(normalized.x * w, normalized.y * w, z, w); + + return inv * world_pos4; +} + +Vec2F Camera::project(const Vec3F& world) { + Vec4F world_pos4(world.x, world.y, world.z, 1); + Vec4F transformed = calculateViewMatrix() * world_pos4; + transformed = calculateProjectionMatrix() * transformed; + return { transformed[0] / transformed[3], transformed[1] / transformed[3] }; +} + +Vec2F Camera::project(const tp::Vec3F& world, const tp::Mat4F& viewMat, const tp::Mat4F& projMat) { + Vec4F world_pos4(world.x, world.y, world.z, 1); + Vec4F transformed = viewMat * world_pos4; + transformed = projMat * transformed; + return { transformed[0] / transformed[3], transformed[1] / transformed[3] }; +} + +void Camera::lookAtPoint(const Vec3F& aTarget, const Vec3F& aPos, Vec3F aUp) { + if (aTarget == aPos) return; + mPos = aPos; + mTarget = aTarget; + Vec3F f = (mPos - mTarget).normalize(); + mUp = f * (aUp.normalize() * f); +} + +void Camera::zoom(halnf ratio) { + ratio = abs(ratio); + if (ratio < 0.1f) { + return; + } + if (abs((mPos - mTarget).length2()) < 0.05f && ratio < 1.f) { + return; + } + mPos = mTarget + (mPos - mTarget) * ratio; + lookAtPoint(mTarget, mPos, mUp); +} + +void Camera::move(Vec2F aPos, Vec2F aPrevPos) { + Vec3F p1 = project(aPrevPos); + Vec3F p2 = project(aPos); + Vec3F move = p1 - p2; + mPos += move; + mTarget += move; + lookAtPoint(mTarget, mPos, mUp); +} + +void Camera::rotate(halnf angleX, halnf angleY) { + Vec3F wup(0, 0, 1); + mPos -= mTarget; + + mat3f rotZ = mat3f::rotatorDir(wup, angleX); + mPos = rotZ * mPos; + mUp = rotZ * mUp; + + Vec3F f = mPos.unitV(); + Vec3F s = mUp * f; + + mPos = mat3f::rotatorDir(s, -angleY) * mPos; + + mPos += mTarget; + + lookAtPoint(mTarget, mPos, mUp); +} \ No newline at end of file diff --git a/Math/private/Color.cpp b/Math/private/Color.cpp new file mode 100644 index 0000000..22741e3 --- /dev/null +++ b/Math/private/Color.cpp @@ -0,0 +1,169 @@ + +#include "Color.hpp" + +using namespace tp; + +RGB::RGB() { r = g = b = 1.f; } +RGB::RGB(flt4 pr, flt4 pg, flt4 pb) { set(pr, pg, pb); } +RGB::RGB(flt4 val) { set(val, val, val); } + +void RGB::set(flt4 pr, flt4 pg, flt4 pb) { + r = pr; + b = pb; + g = pg; +} + +RGB::operator HSV() const { + + HSV out; + alnf min, max, delta; + + min = r < g ? r : g; + min = min < b ? min : b; + max = r > g ? r : g; + max = max > b ? max : b; + + out.v = (halnf) max; + delta = max - min; + + if (delta < 0.00001) { + out.s = 0; + // undefined, maybe nan? + out.h = 0; + return out; + } + if (max > 0.f) { + // NOTE: if Max is == 0, this divide would cause a crash + out.s = (halnf) (delta / max); + + } else { + // if max is 0, then r = g = b = 0 + // s = 0, h is undefined + out.s = 0.f; + out.h = 0.f; + return out; + } + if (r >= max) { + // between yellow & magenta + out.h = (halnf) ((g - b) / delta); + } else { + if (g >= max) { + // between cyan & yellow + out.h = (halnf) (2.0 + (b - r) / delta); + } else { + // between magenta & cyan + out.h = (halnf) (4.0 + (r - g) / delta); + } + } + + out.h *= 60.f; + + if (out.h < 0.0) { + out.h += 360.f; + } + + out.h = (halnf) (out.h / 360.f * (PI2)); + + return out; +} + +HSV::HSV() { h = s = v = 0.f; } + +HSV::HSV(flt4 ph, flt4 ps, flt4 pv) { set(ph, ps, pv); } + +void HSV::set(flt4 ph, flt4 ps, flt4 pv) { + h = ph; + s = ps; + v = pv; +} + +HSV::operator RGB() const { + alnf hh, p, q, t, ff; + alni i; + RGB out; + + if (s <= 0.0) { // < is bogus, just shuts up warnings + out.r = v; + out.g = v; + out.b = v; + return out; + } + + hh = h / (PI2) * 360; + + if (hh >= 360.0) { + hh = 0.0; + } + + hh /= 60.0; + i = (long) hh; + ff = hh - i; + p = v * (1.0 - s); + q = v * (1.0 - (s * ff)); + t = v * (1.0 - (s * (1.0 - ff))); + + switch (i) { + case 0: + out.r = (halnf) v; + out.g = (halnf) t; + out.b = (halnf) p; + break; + case 1: + out.r = (halnf) q; + out.g = (halnf) v; + out.b = (halnf) p; + break; + case 2: + out.r = (halnf) p; + out.g = (halnf) v; + out.b = (halnf) t; + break; + + case 3: + out.r = (halnf) p; + out.g = (halnf) q; + out.b = (halnf) v; + break; + case 4: + out.r = (halnf) t; + out.g = (halnf) p; + out.b = (halnf) v; + break; + case 5: + default: + out.r = (halnf) v; + out.g = (halnf) p; + out.b = (halnf) q; + break; + } + return out; +} + +/* +struct rgbab { + + uint4 RGBA; + + rgbab() { RGBA = 0xFFFFFFFF; } + rgbab(uint4 color) { RGBA = color; } + rgbab(uint1 r, uint1 b, uint1 g, uint1 a) { set(r, b, g, a); } + rgbab(const rgbab& in) { RGBA = in.RGBA; } + + void set(uint4 color) { RGBA = color; } + void set(uint1 r, uint1 b, uint1 g, uint1 a) { + RGBA = (uint1)(a); + RGBA <<= 8; RGBA |= (uint1)(r); + RGBA <<= 8; RGBA |= (uint1)(g); + RGBA <<= 8; RGBA |= (uint1)(b); + } + + operator rgbaf() { + rgbaf out; + out.r = uint1((RGBA & 0x00FF0000) >> 16) / 255.f; + out.g = uint1((RGBA & 0x0000FF00) >> 8) / 255.f; + out.b = uint1(RGBA & 0x000000FF) / 255.f; + out.a = uint1((RGBA & 0xFF000000) >> 24) / 255.f; + return out; + } +}; +*/ \ No newline at end of file diff --git a/Math/private/MathCommon.cpp b/Math/private/MathCommon.cpp new file mode 100644 index 0000000..4ee0ef7 --- /dev/null +++ b/Math/private/MathCommon.cpp @@ -0,0 +1,43 @@ + +#include "MathCommon.hpp" + +#include "ContainersCommon.hpp" + +static tp::ModuleManifest* sModuleDependencies[] = { + &tp::gModuleContainers, + nullptr +}; + +tp::ModuleManifest tp::gModuleMath = ModuleManifest("Math", nullptr, nullptr, sModuleDependencies); + +tp::alnf std_sin(tp::alnf radians); +tp::alnf std_tan(tp::alnf radians); +tp::alnf std_cos(tp::alnf radians); +tp::alnf std_acos(tp::alnf val); +tp::alnf std_sqrt(tp::alnf val); +tp::alnf std_rad(tp::alnf val); +tp::alnf std_deg(tp::alnf val); +tp::alnf std_atan2(tp::alnf X, tp::alnf Y); +tp::alnf std_atan(tp::alnf val); + +tp::alnf tp::sin(const tp::alnf radians) { return std_sin((halnf) radians); } +tp::alnf tp::tan(const tp::alnf radians) { return std_tan((halnf) radians); } +tp::alnf tp::cos(const tp::alnf radians) { return std_cos((halnf) radians); } +tp::alnf tp::acos(const tp::alnf val) { return std_acos((halnf) val); } +tp::alnf tp::sqrt(const tp::alnf val) { return std_sqrt((halnf) val); } +tp::alnf tp::rad(const tp::alnf val) { return val * (PI / 180.f); } +tp::alnf tp::deg(const tp::alnf val) { return val * (180.f / PI); } +tp::alnf tp::atan2(const tp::alnf X, const tp::alnf Y) { return std_atan2((halnf) X, (halnf) Y); } +tp::alnf tp::atan(const tp::alnf val) { return std_atan((halnf) val); } + +#include + +tp::alnf std_sin(const tp::alnf radians) { return sinf((tp::halnf)radians); } +tp::alnf std_tan(const tp::alnf radians) { return tanf((tp::halnf)radians); } +tp::alnf std_cos(const tp::alnf radians) { return cosf((tp::halnf)radians); } +tp::alnf std_acos(const tp::alnf val) { return acos((tp::halnf)val); } +tp::alnf std_sqrt(const tp::alnf val) { return sqrt((tp::halnf)val); } +tp::alnf std_rad(const tp::alnf val) { return val * (PI / 180.f); } +tp::alnf std_deg(const tp::alnf val) { return val * (180.f / PI); } +tp::alnf std_atan2(const tp::alnf X, const tp::alnf Y) { return atan2((tp::halnf)X, (tp::halnf)Y); } +tp::alnf std_atan(const tp::alnf val) { return atan((tp::halnf)val); } \ No newline at end of file diff --git a/Math/private/Ray.cpp b/Math/private/Ray.cpp new file mode 100644 index 0000000..d9f2cbd --- /dev/null +++ b/Math/private/Ray.cpp @@ -0,0 +1,9 @@ + +#include "Ray.hpp" + +using namespace tp; + +Ray::Ray(const Vec3F& aDir, const Vec3F& aPos) { + this->dir = aDir.unitV(); + this->pos = aPos; +} \ No newline at end of file diff --git a/Math/private/Topology.cpp b/Math/private/Topology.cpp new file mode 100644 index 0000000..e8c4c53 --- /dev/null +++ b/Math/private/Topology.cpp @@ -0,0 +1,105 @@ + +#include "Topology.hpp" + +void* operator new(std::size_t, void* in) { return in; } + +using namespace tp; + +Vec3F TrigCache::gHitPos; + +TrigCache::TrigCache() : mP1(0), mP2(0), mP3(0) {} + +TrigCache::TrigCache(ualni v1, ualni v2, ualni v3) : mP1(v1), mP2(v2), mP3(v3) {} + +TrigCache::TrigCache(const TrigCache& in) { + mP1 = in.mP1; + mP2 = in.mP2; + mP3 = in.mP3; + + mEdgeP1P2 = in.mEdgeP1P2; + mEdgeP1P3 = in.mEdgeP1P3; + + mOrigin = in.mOrigin; + mNormal = in.mNormal; +} + +void TrigCache::updateCache(const Buffer& points) { + mOrigin = points[mP1]; + mEdgeP1P2 = points[mP2] - points[mP1]; + mEdgeP1P3 = points[mP3] - points[mP1]; + mNormal = (points[mP2] - points[mP1]).cross(points[mP3] - points[mP1]).unitV(); +} + +bool TrigCache::castRay(const Ray& ray) const { + static Vec3F h, s, q; + static halnf a, f, u, v; + static halnf t; + + h = ray.dir.cross(mEdgeP1P3); + a = mEdgeP1P2.dot(h); + + if (a > -EPSILON && a < EPSILON) { + return false; + } + + f = 1.f / a; + s = ray.pos - mOrigin; + u = f * s.dot(h); + + if (u < 0.0 || u > 1.0) { + return false; + } + + q = s.cross(mEdgeP1P2); + v = f * ray.dir.dot(q); + + if (v < 0.f || u + v > 1.f) { + return false; + } + + t = f * mEdgeP1P3.dot(q); + if (t > EPSILON) { + gHitPos = ray.pos + ray.dir * t; + return true; + } else { + return false; + } +} + +const Vec3F& TrigCache::getHitPos() { return gHitPos; } + +const Vec3F& TrigCache::getNormal() const { return mNormal; } + +void Topology::addTrig(const Vec3F& v1, const Vec3F& v2, const Vec3F& v3) { + auto trigIdx = mPoints.size(); + + Buffer newPoints(3); + newPoints[0] = v1; + newPoints[1] = v2; + newPoints[2] = v3; + mPoints.append(newPoints); + + transformPoint(newPoints[0]); + transformPoint(newPoints[1]); + transformPoint(newPoints[2]); + mPointsTransformed.append(newPoints); + + TrigCache newTrig(trigIdx, trigIdx + 1, trigIdx + 2); + newTrig.updateCache(mPointsTransformed); + mTrigCaches.append(newTrig); +} + +void Topology::transformPoint(Vec3F& vert) { + mBasis.transform(vert); + vert += mOrigin; +} + +void Topology::updateTransformed() { + mPointsTransformed = mPoints; + for (auto idx : Range(mPointsTransformed.size())) { + transformPoint(mPointsTransformed[idx]); + } + for (auto idx : Range(mTrigCaches.size())) { + mTrigCaches[idx].updateCache(mPointsTransformed); + } +} \ No newline at end of file diff --git a/Math/private/Trig.cpp b/Math/private/Trig.cpp new file mode 100644 index 0000000..b4212da --- /dev/null +++ b/Math/private/Trig.cpp @@ -0,0 +1,72 @@ + +#include "Trig.hpp" + +#include "Ray.hpp" + +using namespace tp; + +Trig::Trig() { + MODULE_SANITY_CHECK(gModuleMath) + p1.assign(0.f, 0.f, 0.f); + p2.assign(0.f, 0.f, 0.f); + p3.assign(0.f, 0.f, 0.f); +} + +Trig::Trig(const Vec3F& v0, const Vec3F& v1, const Vec3F& v2) { + p1 = v0; + p2 = v1; + p3 = v2; +} + +void Trig::assign(const Vec3F& v0, const Vec3F& v1, const Vec3F& v2) { + p1 = v0; + p2 = v1; + p3 = v2; +} + +Trig::~Trig() = default; + +void Trig::normal(Vec3F& dir) const { + dir = (p2 - p1).cross(p3 - p1); + dir.normalize(); +} + +Vec3F edge1, edge2, h, s, q; +halnf a, f, u, v; +halnf t; + +bool Trig::rayHit(class Ray& ray, Vec3F& HitPos) const { + + edge1 = p2 - p1; + edge2 = p3 - p1; + + h = ray.dir.cross(edge2); + a = edge1.dot(h); + + if (a > -EPSILON && a < EPSILON) { + return false; + } + + f = 1.f / a; + s = ray.pos - p1; + u = f * s.dot(h); + + if (u < 0.0 || u > 1.0) { + return false; + } + + q = s.cross(edge1); + v = f * ray.dir.dot(q); + + if (v < 0.f || u + v > 1.f) { + return false; + } + + t = f * edge2.dot(q); + if (t > EPSILON) { + HitPos = ray.pos + ray.dir * t; + return true; + } else { + return false; + } +} diff --git a/Math/public/Camera.hpp b/Math/public/Camera.hpp new file mode 100644 index 0000000..01123af --- /dev/null +++ b/Math/public/Camera.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include "Mat.hpp" +#include "Ray.hpp" + +namespace tp { + class Camera { + Vec3F mPos, mTarget, mUp; + halnf mFOV = (halnf) (PI) / 4; + halnf mNear = 0.001f; + halnf mFar = 100.f; + halnf mRatio = 1.f; + + public: + Camera(); + ~Camera() = default; + + void setRatio(halnf ratio); + void setFOV(halnf fov); + + [[nodiscard]] Vec3F& getPos(); + [[nodiscard]] Vec3F getTarget() const; + [[nodiscard]] Vec3F getForward() const; + [[nodiscard]] Vec3F getUp() const; + [[nodiscard]] halnf getRatio() const; + [[nodiscard]] halnf getFOV() const; + [[nodiscard]] halnf getFar() const; + [[nodiscard]] halnf getNear() const; + + public: + void lookAtPoint(const Vec3F& aTarget, const Vec3F& aPos, Vec3F aUp); + void rotate(halnf anglex, halnf angleY); + void move(Vec2F aPos, Vec2F aPrevPos); + void zoom(halnf ratio); + void offset_target(halnf val); + + public: + [[nodiscard]] Mat4F calculateTransformationMatrix(); + [[nodiscard]] Mat calculateProjectionMatrix() const; + [[nodiscard]] Mat calculateViewMatrix(); + [[nodiscard]] Vec3F project(Vec2F normalized); + [[nodiscard]] Vec2F project(const Vec3F& world); + [[nodiscard]] static Vec2F project(const tp::Vec3F& world, const tp::Mat4F& viewMat, const tp::Mat4F& projMat); + + }; +} \ No newline at end of file diff --git a/Math/public/Color.hpp b/Math/public/Color.hpp new file mode 100644 index 0000000..3387fb7 --- /dev/null +++ b/Math/public/Color.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include "Vec.hpp" + +namespace tp { + + class RGB; + class HSV; + + class RGB { + public: + RGB(); + RGB(flt4 pr, flt4 pg, flt4 pb); + RGB(flt4 val); + + public: + void set(flt4 pr, flt4 pg, flt4 pb); + operator HSV() const; + + public: + halnf r, g, b; + }; + + class HSV { + public: + HSV(); + HSV(flt4 ph, flt4 ps, flt4 pv); + + public: + void set(flt4 ph, flt4 ps, flt4 pv); + operator RGB() const; + + public: + halnf h, s, v; + }; + + class RGBA { + public: + RGBA() : r(0), g(0), b(0), a(0) {} + RGBA(flt4 val) : rgbs(val), a(val) {} + RGBA(const RGB& RGBs, flt4 val) : rgbs(RGBs), a(val) {} + RGBA(flt4 pr, flt4 pg, flt4 pb, flt4 pa) : rgbs(pr, pg, pb), a(pa) {} + + public: + RGBA& operator=(const HSV& in) { + rgbs = in; + a = 1; + return *this; + } + + RGBA operator-(const RGBA& in) const { + auto const nr = tp::clamp(r - in.r, 0.f, 1.f); + auto const ng = tp::clamp(g - in.g, 0.f, 1.f); + auto const nb = tp::clamp(b - in.b, 0.f, 1.f); + auto const na = tp::clamp(a - in.a, 0.f, 1.f); + return { nr, ng, nb, na }; + } + + RGBA operator+(const RGBA& in) const { + auto const nr = tp::clamp(r + in.r, 0.f, 1.f); + auto const ng = tp::clamp(g + in.g, 0.f, 1.f); + auto const nb = tp::clamp(b + in.b, 0.f, 1.f); + auto const na = tp::clamp(a + in.a, 0.f, 1.f); + return { nr, ng, nb, na }; + } + + public: + flt4 a; + + union { + RGB rgbs; + struct { + flt4 r; + flt4 g; + flt4 b; + }; + }; + }; + + class HSVA { + HSV rgbs; + flt4 a = 1.f; + }; +} diff --git a/Math/public/Intersections.hpp b/Math/public/Intersections.hpp new file mode 100644 index 0000000..80de87f --- /dev/null +++ b/Math/public/Intersections.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "Vec.hpp" + +namespace tp { + + template + bool intersectLines2D(const Vec2& p1, const Vec2& p2, const Vec2& v1, const Vec2& v2, Vec2* out) { + auto a1 = p2.x - p1.x; + auto a2 = p2.y - p1.y; + + auto b1 = v2.x - v1.x; + auto b2 = v2.y - v1.y; + + auto c1 = v1.x - p1.x; + auto c2 = v1.y - p1.y; + + auto det = a2 * b1 - a1 * b2; + + auto t1 = ( -b2 * c1 + b1 * c2 ) / det; + auto t2 = ( -a2 * c1 + a1 * c2 ) / det; + + if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) { + if (out != nullptr) { + out->x = p1.x + (a1 * t1); + out->y = p1.y + (a2 * t1); + } + return true; + } + return false; + } + +} + diff --git a/Math/public/Mat.hpp b/Math/public/Mat.hpp new file mode 100644 index 0000000..d4c8fbb --- /dev/null +++ b/Math/public/Mat.hpp @@ -0,0 +1,768 @@ +#pragma once + +#include "Vec.hpp" + +namespace tp { + + template + class Mat { + typedef Vec MVec; + + private: + MVec mCol[tNColoumns]; + + public: + Mat() = default; + + explicit Mat(const Type& val) { + operator=(val); + } + + Mat(const Mat& in) { + operator=(in); + } + + MVec& operator[](alni i) { + DEBUG_ASSERT(i < tNColoumns && i >= 0) + return mCol[i]; + } + + const MVec& operator[](alni i) const { + DEBUG_ASSERT(i < tNColoumns && i >= 0) + return mCol[i]; + } + + Mat& operator=(const Mat& in) { + if (&in == this) return *this; + memcp(this, &in, sizeof(Mat)); + return *this; + } + + Mat& operator=(const Type& val) { + clear(val); + return *this; + } + + void clear(const Type& val) { + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + (*this)[i][j] = val; + } + } + } + + void setDiagonal(const Type& val) { + halni len = min(tNColoumns, tNRows); + for (halni i = 0; i < len; i++) { + (*this)[i][i] = val; + } + } + + Mat& fillRandom() { + DEBUG_ASSERT(0) + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + (*this)[i][j] = (Type) 0; + } + } + return *this; + } + + Mat operator-() { + Mat out; + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + out[i][j] = -(*this)[i][j]; + } + } + return out; + } + + Mat& operator+=(const Mat& in) { + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + (*this)[i][j] += in[i][j]; + } + } + return *this; + } + + Mat& operator-=(const Mat& in) { + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + (*this)[i][j] -= in[i][j]; + } + } + return *this; + } + + Mat& operator+=(const Type& val) { + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + (*this)[i][j] += val; + } + } + return *this; + } + + Mat& operator-=(const Type& val) { + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + (*this)[i][j] -= val; + } + } + return *this; + } + + Mat& operator/=(const Type& val) { + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + (*this)[i][j] /= val; + } + } + return *this; + } + + Mat& operator*=(const Type& val) { + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + (*this)[i][j] *= val; + } + } + return *this; + } + + Mat operator+(const Mat& in) { + Mat out; + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + out[i][j] = (*this)[i][j] + in[i]; + } + } + return out; + } + + Mat operator-(const Mat& in) { + Mat out; + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + out[i][j] = (*this)[i][j] - in[i]; + } + } + return out; + } + + Mat operator+(const Type& val) { + Mat out; + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + out[i][j] = (*this)[i][j] + val; + } + } + return out; + } + + Mat operator-(const Type& val) { + Mat out; + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + out[i][j] = (*this)[i][j] - val; + } + } + return out; + } + + Mat operator*(const Type& val) { + Mat out; + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + out[i][j] = (*this)[i][j] * val; + } + } + return out; + } + + Mat operator/(const Type& val) { + Mat out; + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = 0; j < tNRows; j++) { + out[i][j] = (*this)[i][j] / val; + } + } + return out; + } + + // Matrix Properties + MVec transform(const MVec& in) const { + static_assert(tNRows == tNColoumns); + MVec out; + for (halni i = 0; i < tNRows; i++) { + Type tmp = 0; + for (halni j = 0; j < tNColoumns; j++) { + tmp += (*this)[i][j] * in[j]; + } + out[i] = tmp; + } + return out; + } + + Mat transform(const Mat& in) const { + Mat out; + out.clear(0); + for (halni i = 0; i < tNRows; i++) { + for (halni j = 0; j < tNRows; j++) { + for (halni u = 0; u < tNRows; u++) { + out[i][j] += (*this)[i][u] * in[u][j]; + } + } + } + return out; + } + + Mat& transpose() { + static_assert( tNRows == tNColoumns); + for (halni i = 0; i < tNColoumns; i++) { + for (halni j = i + 1; j < tNColoumns; j++) { + swap((*this)[i][j], (*this)[j][i]); + } + } + return *this; + } + + Mat operator*(const Mat& in) const { + return transform(in); + } + + MVec operator*(const MVec& in) const { + return transform(in); + } + + Mat minor(halni p, halni q) { + Mat out; + halni i = 0, j = 0; + for (halni row = 0; row < tNRows; row++) { + for (halni col = 0; col < tNRows; col++) { + if (row != p && col != q) { + out[i][j++] = (*this)[row][col]; + if (j == tNRows - 1) { + j = 0; + i++; + } + } + } + } + return out; + } + + Type det() { + static_assert( tNRows == tNColoumns); + + Type out = 0; + + if (tNRows == 1) { + return (*this)[0][0]; + } + if (tNRows == 2) { + return ((*this)[0][0] * (*this)[1][1]) - ((*this)[1][0] * (*this)[0][1]); + } + + halni sign = 1; + for (int i = 0; i < tNRows; i++) { + out += sign * (*this)[0][i] * minor(0, i).det(); + sign = -sign; + } + return out; + } + + Mat cofactors() { + static_assert( tNRows == tNColoumns); + Mat out; + for (int i = 0; i < tNRows; i++) { + for (int j = 0; j < tNRows; j++) { + Type sign = (Type) ((i + j + 2) & 1 ? -1 : 1); + out[i][j] = minor(j, i).det() * sign; + } + } + return out; + } + + Mat inv() { + Type detV = det(); + DEBUG_ASSERT(detV) + return cofactors() /= detV; + } + }; + + template + using Mat2 = Mat; + using Mat2F = Mat2; + + template + class Mat { + typedef Vec MVec; + + MVec i; + MVec j; + + public: + + Mat() = default; + + explicit Mat(const Type& val) { + operator=(val); + } + + Mat(const MVec& pi, const MVec& pj) { + i.x = pi.x; + i.y = pi.y; + j.x = pj.x; + j.y = pj.y; + } + + Mat(Type ix, Type iy, Type jx, Type jy) { + i.x = ix; + i.y = iy; + j.x = jx; + j.y = jy; + } + + Mat(const Mat& in) { + operator=(in); + } + + MVec& operator[](alni idx) { + DEBUG_ASSERT(idx < 2 && idx >= 0) + return (&i)[idx]; + } + + const MVec& operator[](alni idx) const { + DEBUG_ASSERT(idx < 2 && idx >= 0) + return (&i)[idx]; + } + + Mat& operator=(const Mat& in) { + memcp(this, &in, sizeof(Mat2)); + return *this; + } + + Mat& operator=(const Type& val) { + clear(val); + return *this; + } + + void clear(const Type& val) { + i.x = val; + i.y = val; + j.x = val; + j.y = val; + } + + void setDiagonal(const Type& val) { + i.x = val; + j.y = val; + } + + Mat& fillRandom() { + DEBUG_ASSERT(0) + i.x = (Type) 0; + i.y = (Type) 0; + j.x = (Type) 0; + j.y = (Type) 0; + return *this; + } + + Mat operator-() { + return Mat(-i.x, -i.y, -j.x, -j.y); + } + + Mat& operator+=(const Mat& in) { + i.x += in.i.x; + i.y += in.i.y; + j.x += in.j.x; + j.y += in.j.y; + return *this; + } + + Mat& operator-=(const Mat& in) { + i.x -= in.i.x; + i.y -= in.i.y; + j.x -= in.j.x; + j.y -= in.j.y; + return *this; + } + + Mat& operator+=(Type val) { + i.x += val; + i.y += val; + j.x += val; + j.y += val; + return *this; + } + + Mat& operator-=(Type val) { + i.x -= val; + i.y -= val; + j.x -= val; + j.y -= val; + return *this; + } + + Mat& operator/=(Type val) { + i.x /= val; + i.y /= val; + j.x /= val; + j.y /= val; + return *this; + } + + Mat& operator*=(Type val) { + i.x *= val; + i.y *= val; + j.x *= val; + j.y *= val; + return *this; + } + + Mat operator+(const Mat& in) { + Mat out; + out.i.x = i.x + in.i.x; + out.i.y = i.y + in.i.y; + out.j.x = j.x + in.j.x; + out.j.y = j.y + in.j.y; + return out; + } + + Mat operator-(const Mat& in) { + Mat out; + out.i.x = i.x - in.i.x; + out.i.y = i.y - in.i.y; + out.j.x = j.x - in.j.x; + out.j.y = j.y - in.j.y; + return out; + } + + Mat operator+(const Type& val) { + Mat out; + out.i.x = i.x + val; + out.i.y = i.y + val; + out.j.x = j.x + val; + out.j.y = j.y + val; + return out; + } + + Mat operator-(const Type& val) { + Mat out; + out.i.x = i.x - val; + out.i.y = i.y - val; + out.j.x = j.x - val; + out.j.y = j.y - val; + return out; + } + + Mat operator*(const Type& val) { + Mat out; + out.i.x = i.x * val; + out.i.y = i.y * val; + out.j.x = j.x * val; + out.j.y = j.y * val; + return out; + } + + Mat operator/(const Type& val) { + Mat out; + out.i.x = i.x / val; + out.i.y = i.y / val; + out.j.x = j.x / val; + out.j.y = j.y / val; + return out; + } + + // Matrix Properties + MVec transform(const MVec& in) const { + return MVec(i.x * in.x + i.y * in.y, j.x * in.y + j.y * in.x); + } + + Mat transform(const Mat& in) const { + Mat out; + out.i.x = i.x * in.i.x + j.x * in.i.y; + out.i.y = i.y * in.i.x + j.y * in.i.y; + out.j.x = i.x * in.j.x + j.x * in.j.y; + out.j.y = i.y * in.j.x + j.y * in.j.y; + return out; + } + + Mat operator*(const Mat& in) { + return transform(in); + } + + Mat& transpose() { + swap(j.x, i.y); + return *this; + } + + Type det() { + return i.x * j.y - i.y * j.x; + } + + Mat cofactors() { + return Mat(j.y, -i.y, -j.x, i.x); + } + + Mat inv() { + Type detV = det(); + DEBUG_ASSERT(detV != 0) + return (cofactors() /= detV); + } + }; + + template + using mat3 = Mat; + using mat3f = mat3; + + template + class Mat { + typedef Vec3 vec; + + public: + vec I; + vec J; + vec K; + + public: + Mat() = default; + + explicit Mat(Type val) { + I.assign(val, 0.f, 0.f); + J.assign(0.f, val, 0.f); + K.assign(0.f, 0.f, val); + } + + Mat(const vec& i, const vec& j, const vec& k) { + I = i; + J = j; + K = k; + } + + Mat(const Mat& in) { + this->I = in.I; + this->J = in.J; + this->K = in.K; + } + + void assign(const vec& i, const vec& j, const vec& k) { + I = i; + J = j; + K = k; + } + + Mat& fillRandom() { + I.randf(); + J.randf(); + K.randf(); + return *this; + } + + vec& operator[](alni i) { + DEBUG_ASSERT(i < 3 && i >= 0) + return (&I)[i]; + } + + const vec& operator[](alni i) const { + DEBUG_ASSERT(i < 3 && i >= 0) + return (&I)[i]; + } + + // create on stack + Mat operator+(const Mat& in) { return Mat(I + in.I, J + in.J, K + in.K); } + Mat operator-(const Mat& in) { return Mat(I - in.I, J - in.J, K - in.K); } + Mat operator+(Type val) { return Mat(I + val, J + val, K + val); } + Mat operator-(Type val) { return Mat(I - val, J - val, K - val); } + Mat operator*(Type val) { return Mat(I * val, J * val, K * val); } + Mat operator/(Type val) { return Mat(I / val, J / val, K / val); } + + // write + Mat& operator=(const Mat& in) { + I = in.I; + J = in.J; + K = in.K; + return *this; + } + + Mat& operator+=(const Mat& in) { + I += in.I; + J += in.J; + K += in.K; + return *this; + } + + Mat& operator-=(const Mat& in) { + I -= in.I; + J -= in.J; + K -= in.K; + return *this; + } + + Mat& operator+=(Type val) { + I += val; + J += val; + K += val; + return *this; + } + + Mat& operator-=(Type val) { + I -= val; + J -= val; + K -= val; + return *this; + } + + Mat& operator*=(Type val) { + I *= val; + J *= val; + K *= val; + return *this; + } + + Mat& operator/=(Type val) { + I /= val; + J /= val; + K /= val; + return *this; + } + + // Matrix transformation + vec transform(const vec& in) { + return vec( + I.x * in.x + I.y * in.y + I.z * in.z, + J.x * in.x + J.y * in.y + J.z * in.z, + K.x * in.x + K.y * in.y + K.z * in.z + ); + } + + Mat transform(const Mat& in) { + return Mat( + {(in.I.x * I.x + in.I.y * J.x + in.I.z * K.x), (in.I.x * I.y + in.I.y * J.y + in.I.z * K.y), (in.I.x * I.z + in.I.y * J.z + in.I.z * K.z)}, + {(in.J.x * I.x + in.J.y * J.x + in.J.z * K.x), (in.J.x * I.y + in.J.y * J.y + in.J.z * K.y), (in.J.x * I.z + in.J.y * J.z + in.J.z * K.z)}, + {(in.K.x * I.x + in.K.y * J.x + in.K.z * K.x), (in.K.x * I.y + in.K.y * J.y + in.K.z * K.y), (in.K.x * I.z + in.K.y * J.z + in.K.z * K.z)} + ); + } + + vec operator*(const vec& in) { + return transform(in); + } + + Mat operator*(const Mat& in) { + return transform(in); + } + + Mat minor(halni, halni) { + Mat out; + return out; + } + + Mat& transpose() { + swap(I.y, J.x); + swap(I.z, K.x); + swap(J.z, K.y); + return *this; + } + + Type det() { + return ( + +I.x * (J.y * K.z - J.z * K.y) + - I.y * (J.x * K.z - J.z * K.x) + + I.z * (J.x * K.y - J.y * K.x) + ); + } + + Mat cofactors() { + return Mat( + vec(+(J.y * K.z - J.z * K.y), -(I.y * K.z - I.z * K.y), +(I.y * J.z - I.z * J.y)), + vec(-(J.x * K.z - J.z * K.x), +(I.x * K.z - I.z * K.x), -(I.x * J.z - I.z * J.x)), + vec(+(J.x * K.y - J.y * K.x), -(I.x * K.y - I.y * K.x), +(I.x * J.y - I.y * J.x)) + ); + } + + Mat inv() { + return cofactors() /= det(); + } + + Mat rotatorX(alnf angle) { + alnf cosA = (alnf) cos(angle); + alnf sinA = (alnf) sin(angle); + return { + {1, 0, 0}, + {0, cosA, -sinA}, + {0, sinA, cosA} + }; + } + + Mat rotatorY(alnf angle) { + alnf cosA = (alnf) cos(angle); + alnf sinA = (alnf) sin(angle); + return { + {cosA, 0, sinA}, + {0, 1, 0}, + {-sinA, 0, cosA} + }; + } + + Mat rotatorZ(alnf angle) { + alnf cosA = (alnf) cos(angle); + alnf sinA = (alnf) sin(angle); + return { + {cosA, -sinA, 0}, + {sinA, cosA, 0}, + {0, 0, 1} + }; + } + + static Mat rotatorDir(vec dir, alnf angle) { + dir.normalize(); + + Mat out; + alnf cosA = (alnf) cos(angle); + alnf sinA = (alnf) sin(angle); + alnf tmp = 1 - cosA; + + out.I.x = (Type) (cosA + dir.x * dir.x * tmp); + out.I.y = (Type) (dir.x * dir.y * tmp - dir.z * sinA); + out.I.z = (Type) (dir.x * dir.z * tmp + dir.y * sinA); + + out.J.x = (Type) (dir.y * dir.x * tmp + dir.z * sinA); + out.J.y = (Type) (cosA + dir.y * dir.y * tmp); + out.J.z = (Type) (dir.y * dir.z * tmp - dir.x * sinA); + + out.K.x = (Type) (dir.z * dir.x * tmp - dir.y * sinA); + out.K.y = (Type) (dir.z * dir.y * tmp + dir.x * sinA); + out.K.z = (Type) (cosA + dir.z * dir.z * tmp); + + return out; + } + }; + + template + using Mat4 = Mat; + + using Mat4F = Mat4; + using Mat4I = Mat4; + + template + class Mat< Type, 0, 0 > { + typedef Vec MVec; + MVec dummy; + + public: + MVec& operator[](alni) { + return dummy; + } + + const MVec& operator[](alni) const { + return dummy; + } + + Type det() { + return Type(); + } + }; +} \ No newline at end of file diff --git a/Math/public/MathCommon.hpp b/Math/public/MathCommon.hpp new file mode 100644 index 0000000..861723c --- /dev/null +++ b/Math/public/MathCommon.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "Utils.hpp" + +#define EPSILON 0.0000001 + +#define PI double(3.1415926535897932384626433832795) +#define PI2 (PI * 2) +#define PI13 (PI / 3) +#define PI23 (PI2 / 3) +#define PI43 (2 * PI2 / 3) + +#define SQRT2 1.4142135623730950488016887242 +#define EXP 2.7182818284590452353602874714 + +namespace tp { + extern ModuleManifest gModuleMath; + + alnf sin(alnf radians); + alnf tan(alnf radians); + alnf atan2(alnf X, alnf Y); + alnf atan(alnf val); + alnf cos(alnf radians); + alnf acos(alnf val); + alnf sqrt(alnf val); + alnf rad(alnf val); + alnf deg(alnf val); +} \ No newline at end of file diff --git a/Math/public/Ray.hpp b/Math/public/Ray.hpp new file mode 100644 index 0000000..ae90e61 --- /dev/null +++ b/Math/public/Ray.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "Vec.hpp" + +namespace tp { + + class Ray { + public: + Ray(const Vec3F& Dir, const Vec3F& Pos); + Ray() = default; + + public: + Vec3F dir; + Vec3F pos; + + public: + ~Ray() = default; + }; + +} \ No newline at end of file diff --git a/Math/public/Rect.hpp b/Math/public/Rect.hpp new file mode 100644 index 0000000..33fa70b --- /dev/null +++ b/Math/public/Rect.hpp @@ -0,0 +1,274 @@ + +#pragma once + +#include "Vec.hpp" + +#include "Intersections.hpp" + +namespace tp { + + template class Rect; + using RectF = Rect; + using RectI = Rect; + + template + class Rect { + public: + Rect() {} + + explicit Rect(Type val) { + this->pos = val; + this->size = val; + } + + template + explicit Rect(const Rect& rec) { + this->pos = rec.pos; + this->size = rec.size; + } + + Rect(const Vec2& pos, const Vec2& size) { + this->pos = pos; + this->size = size; + } + + Rect(Type aPosX, Type posy, Type aSizeX, Type aSizeY) { + pos.assign(aPosX, posy); + size.assign(aSizeX, aSizeY); + } + + // assign + template + Rect& assign(InType p1x, InType p1y, InType p2x, InType p2y) { + pos.assign(p1x, p1y); + size.assign(p2x, p2y); + return *this; + } + + // assign + template + Rect& assign(const Vec2& pos, const Vec2& size) { + this->pos.assign(pos.x, pos.y); + this->size.assign(size.x, size.y); + return *this; + } + + // conversion + template + Rect& operator=(const Rect& rect) { + pos = rect.pos; + size = rect.size; + return *this; + } + + bool operator==(Rect& rect) const { + return (pos == rect.pos && size == rect.size); + } + + bool isEnclosedIn(const Rect& rect, bool aParent = false) const { + if (aParent) { + return (pos.x + size.x <= rect.size.x && pos.y + size.y <= rect.size.y && + pos.x >= 0 && pos.y >= 0); + } + *(Vec2*)(&pos) -= rect.pos; + bool ret = this->isEnclosedIn(rect, true); + *(Vec2*)(&pos) += rect.pos; + return ret; + } + + void calcIntersection(Rect& in, Rect& out) const { + if (isOverlap(in)) { + out = *this; + for (char i = 0; i < 2; i++) { + clamp(out.pos[i], in.pos[i], in.pos[i] + in.size[i]); + Type p2 = pos[i] + size[i]; + clamp(p2, in.pos[i], in.pos[i] + in.size[i]); + out.size[i] = p2 - out.pos[i]; + } + } + else { + out.size.assign(0, 0); + out.pos.assign(0, 0); + } + } + + // argument isInside + bool isInside(const Vec2& p) const { + return (p.each_compre(pos) && (pos + size).each_compre(p)); + } + + bool isInside(Type x, Type y) const { + return (pos.x < x&& pos.y < y&& pos.x + size.x > x&& pos.y + size.y > y); + } + + inline Vec2 sizeVec() const { + return Vec2(size.x, size.y); + } + + inline Vec2 sizeVecW() const { + return Vec2(size.x + pos.x, size.y + pos.y); + } + + void invertY(Type scr_y) { + pos.y = scr_y - pos.y - size.y; + } + + void move(Type dx, Type dy) { + pos.x += dx; + pos.y += dy; + } + + Rect& scaleFromCenter(tp::halnf fac, bool add = false) { + if (add) { + pos += fac; + size -= fac * 2; + } + else { + auto new_size = size * fac; + pos = pos - (new_size - size) / 2; + size = new_size; + } + return *this; + } + + Vec2 p1() { + return pos; + } + + Vec2 p3() { + return pos + size; + } + + Vec2 p2() { + return { pos.x, pos.y + size.y }; + } + + Vec2 p4() { + return { pos.x + size.x, pos.y }; + } + + inline bool isAbove(const Rect& rect) const { + return (pos.y + size.y < rect.pos.y); + } + + inline bool isBellow(const Rect& rect) const { + return (rect.pos.y + rect.size.y < pos.y); + } + + inline bool isRight(const Rect& rect) const { + return (pos.x + size.x < rect.pos.x); + } + + inline bool isLeft(const Rect& rect) const { + return (rect.pos.x + rect.size.x < pos.x); + } + + inline bool isIntersectsY(const Rect& in) const { + if (INRANGE(in.pos.x, pos.x, pos.x + size.x)) return true; + if (INRANGE(pos.x, in.pos.x, in.pos.x + in.size.x)) return true; + return false; + } + + inline bool isIntersectX(const Rect& rect) const { + if (INRANGE(rect.pos.y, pos.y, pos.y + size.y)) return true; + if (INRANGE(pos.y, rect.pos.y, rect.pos.y + rect.size.y)) return true; + return false; + } + + bool isOverlap(const Rect& rect) const { + return (isIntersectX(rect) && isIntersectsY(rect)); + } + + void clamp(const Rect& bounds) { + Vec2 p3(pos + size); + Vec2 max = bounds.pos + bounds.size; + + pos.clamp(bounds.pos, max); + p3.clamp(bounds.pos, max); + + size = p3 - pos; + } + + // if only one point isInside + bool clampOutside(Vec2& v1, Vec2& v2) { + bool const in1 = isInside(v1); + bool const in2 = isInside(v2); + if (!in1 && !in2) return false; + + if (in1) { + if (!intersectLines2D(p2(), p3(), v1, v2, &v1)) { + if (!intersectLines2D(p1(), p4(), v1, v2, &v1)) { + if (!intersectLines2D(p1(), p2(), v1, v2, &v1)) { + intersectLines2D(p3(), p4(), v1, v2, &v1); + } + } + } + return true; + } + + if (!intersectLines2D(p2(), p3(), v1, v2, &v2)) { + if (!intersectLines2D(p1(), p4(), v1, v2, &v2)) { + if (!intersectLines2D(p1(), p2(), v1, v2, &v2)) { + intersectLines2D(p3(), p4(), v1, v2, &v2); + } + } + } + return true; + } + + uhalni clampInside(Vec2& v1, Vec2& v2) { + bool const in1 = isInside(v1); + bool const in2 = isInside(v2); + + if (in1 && in2) return 2; + + Vec2 v1copy = v1; + Vec2 v2copy = v2; + + if (in1 || in2) { + if (!intersectLines2D(p2(), p3(), v1copy, v2copy, &v2)) { + if (!intersectLines2D(p1(), p4(), v1copy, v2copy, &v2)) { + if (!intersectLines2D(p1(), p2(), v1copy, v2copy, &v2)) { + intersectLines2D(p3(), p4(), v1copy, v2copy, &v2); + } + } + } + return 1; + } + + DEBUG_ASSERT(0) + return 0; + } + + template + Rect operator*(ConversionType val) { + Rect out; + out.pos = pos * val; + out.size = size * val; + return out; + } + + Vec2 center() { + return pos + size / 2.f; + } + + public: + union { + Vec2 v1; + Vec2 pos; + struct { + Type x; + Type y; + }; + }; + + union { + Vec2 v2; + Vec2 size; + struct { + Type z; + Type w; + }; + }; + }; +} \ No newline at end of file diff --git a/Math/public/Topology.hpp b/Math/public/Topology.hpp new file mode 100644 index 0000000..1296637 --- /dev/null +++ b/Math/public/Topology.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "Camera.hpp" +#include "Buffer.hpp" + +namespace tp { + + class TrigCache { + static Vec3F gHitPos; + + public: + ualni mP1, mP2, mP3; + Vec3F mEdgeP1P2, mEdgeP1P3; + Vec3F mNormal; + Vec3F mOrigin; + + public: + TrigCache(); + TrigCache(const TrigCache& in); + TrigCache(ualni v1, ualni v2, ualni v3); + + public: + static const Vec3F& getHitPos() ; + [[nodiscard]] const Vec3F& getNormal() const; + + public: + void updateCache(const Buffer& points); + [[nodiscard]] bool castRay(const Ray& ray) const; + }; + + class Topology { + mat3f mBasis; + Vec3F mOrigin; + + Buffer mPoints; + Buffer mPointsTransformed; + Buffer mTrigCaches; + + public: + Topology() = default; + ~Topology() = default; + + public: + void addTrig(const Vec3F& v1, const Vec3F& v2, const Vec3F& v3); + void transformPoint(Vec3F& vert); + void updateTransformed(); + }; +} \ No newline at end of file diff --git a/Math/public/Trig.hpp b/Math/public/Trig.hpp new file mode 100644 index 0000000..956e234 --- /dev/null +++ b/Math/public/Trig.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "Vec.hpp" + +namespace tp { + + class Trig { + public: + Trig(); + Trig(const Vec3F& v0, const Vec3F& v1, const Vec3F& v2); + + public: + Vec3F p1; + Vec3F p2; + Vec3F p3; + + public: + void assign(const Vec3F& v0, const Vec3F& v1, const Vec3F& v2); + void normal(Vec3F& dir) const; + bool rayHit(class Ray& ray, Vec3F& HitPos) const; + + public: + ~Trig(); + }; +} \ No newline at end of file diff --git a/Math/public/Vec.hpp b/Math/public/Vec.hpp new file mode 100644 index 0000000..a2301f1 --- /dev/null +++ b/Math/public/Vec.hpp @@ -0,0 +1,649 @@ +#pragma once + +#include "MathCommon.hpp" + +namespace tp { + + template + class Vec { + typedef SelCopyArg TypeArg; + + private: + Type mBuff[tSize]; + + inline const Type& get(ualni i) const { return mBuff[i]; } + inline Type& get(ualni i) { return mBuff[i]; } + inline Type& set(ualni i, TypeArg arg) { return mBuff[i] = arg; } + + public: + + Vec() { + MODULE_SANITY_CHECK(gModuleMath) + } + + Vec(TypeArg val) { + assign(val); + } + + Vec(TypeArg val1, TypeArg val2, TypeArg val3, TypeArg val4) { + static_assert(tSize == 4); + mBuff[0] = val1; + mBuff[1] = val2; + mBuff[2] = val3; + mBuff[3] = val4; + } + + Vec(TypeArg val1, TypeArg val2, TypeArg val3, TypeArg val4, TypeArg val5) { + static_assert(tSize == 5); + mBuff[0] = val1; + mBuff[1] = val2; + mBuff[2] = val3; + mBuff[3] = val4; + mBuff[4] = val5; + } + + Vec(TypeArg val1, TypeArg val2, TypeArg val3, TypeArg val4, TypeArg val5, TypeArg val6) { + static_assert(tSize == 6); + mBuff[0] = val1; + mBuff[1] = val2; + mBuff[2] = val3; + mBuff[3] = val4; + mBuff[4] = val5; + mBuff[5] = val6; + } + + Vec(const Vec& in) { + memcp(mBuff, in.mBuff, sizeof(Type) * tSize); + } + + Type& operator[](ualni i) { + DEBUG_ASSERT(i < tSize && i >= 0) + return mBuff[i]; + } + + const Type& operator[](ualni i) const { + DEBUG_ASSERT(i < tSize && i >= 0) + return mBuff[i]; + } + + Vec operator-() { + Vec out; + for (halni i = 0; i < tSize; i++) { + out.get(i) = -get(i); + } + return out; + } + + // write + Vec& operator+=(const Vec& val) { + for (ualni i = 0; i < tSize; i++) { + get(i) += val[i]; + } + return *this; + } + + Vec& operator-=(const Vec& val) { + for (ualni i = 0; i < tSize; i++) { + get(i) -= val[i]; + } + return *this; + } + + Vec& operator+=(Type val) { + for (ualni i = 0; i < tSize; i++) { + get(i) += val; + } + return *this; + } + + Vec& operator-=(Type val) { + for (ualni i = 0; i < tSize; i++) { + get(i) -= val; + } + return *this; + } + + Vec& operator*=(Type val) { + for (ualni i = 0; i < tSize; i++) { + get(i) *= val; + } + return *this; + } + + Vec& operator/=(Type val) { + for (ualni i = 0; i < tSize; i++) { + get(i) /= val; + } + return *this; + } + + void assign(Type val) { + for (ualni i = 0; i < tSize; i++) { + get(i) = val; + } + } + + // create on stack + Vec operator+(const Vec& in) const { + Vec out; + for (ualni i = 0; i < tSize; i++) { + out[i] = in[i] + get(i); + } + return out; + } + + Vec operator-(const Vec& in) const { + Vec out; + for (ualni i = 0; i < tSize; i++) { + out[i] = in[i] - get(i); + } + return out; + } + + Vec operator+(Type val) const { + Vec out; + for (ualni i = 0; i < tSize; i++) { + out[i] = get(i) + val; + } + return out; + } + + Vec operator-(Type val) const { + Vec out; + for (ualni i = 0; i < tSize; i++) { + out[i] = get(i) - val; + } + return out; + } + + Vec operator*(Type val) const { + Vec out; + for (ualni i = 0; i < tSize; i++) { + out[i] = get(i) * val; + } + return out; + } + + Vec operator/(Type val) const { + Vec out; + for (ualni i = 0; i < tSize; i++) { + out[i] = get(i) / val; + } + return out; + } + + // Vector Properties + Type dot(const Vec& in) const { + Type out = 0; + for (ualni i = 0; i < tSize; i++) { + out += get(i) * in.get(i); + } + return out; + } + + Type operator*(const Vec& in) const { + return dot(in); + } + + alnf length2() const { + alnf sum = 0; + for (ualni i = 0; i < tSize; i++) { + Type val = get(i); + sum += val * val; + } + return sum; + } + + alnf length() const { + return sqrt(length2()); + } + + Vec& normalize() { + operator/=((Type) length()); + return *this; + } + + Vec unitV() { + return Vec(*this).normalize(); + } + + // Comparisons + bool operator>(const Vec& in) const { return this->length2() > in.length2(); } + bool operator<(const Vec& in) const { return this->length2() < in.length2(); } + bool operator>=(const Vec& in) const { return this->length2() >= in.length2(); } + bool operator<=(const Vec& in) const { return this->length2() <= in.length2(); } + + bool operator==(const Vec& in) const { + for (ualni i = 0; i < tSize; i++) { + if (get(i) != in.get(i)) { + return false; + } + } + return true; + } + + bool operator!=(const Vec& in) const { + return !operator==(in); + } + }; + + template + using Vec2 = Vec; + + using Vec2F = Vec; + using Vec2I = Vec; + + template + class Vec { + + public: + Type x; + Type y; + + Vec() : x(0), y(0) {} + + // Initialization + template + Vec(TypeIn aX, TypeIn aY) { + x = (Type) aX; + y = (Type) aY; + } + + template + Vec(TypeIn Vec[2]) { + x = (Type) Vec[0]; + y = (Type) Vec[1]; + } + + template + Vec(TypeIn val) { + x = y = (Type) val; + } + + template + Vec(Vec& Vec) { + x = (Type) Vec.x; + y = (Type) Vec.y; + } + + template + void assign(TypeIn aX, TypeIn aY) { + x = (Type) aX; + y = (Type) aY; + } + + template + void assign(const Vec& in) { + x = (Type) in.x; + y = (Type) in.y; + } + + template + void assign(TypeIn in[2]) { + x = (Type) in[0]; + y = (Type) in[1]; + } + + template + Vec& operator=(TypeIn val) { + x = y = (Type) val; + return *this; + } + + // Conversion + template + Vec& operator=(const Vec& in) { + x = (Type) in.x; + y = (Type) in.y; + return *this; + } + + // create on stack + Vec2 operator+(const Vec2& in) const { return Vec2(x + in.x, y + in.y); } + Vec2 operator-(const Vec2& in) const { return Vec2(x - in.x, y - in.y); } + Vec2 operator+(Type val) const { return Vec2(x + val, y + val); } + Vec2 operator-(Type val) const { return Vec2(x - val, y - val); } + Vec2 operator*(Type val) const { return Vec2(x * val, y * val); } + Vec2 operator/(Type val) const { return Vec2(x / val, y / val); } + + // write + void operator-=(Type val) { + x -= val; + y -= val; + } + + void operator+=(Type val) { + x += val; + y += val; + } + + void operator-=(const Vec2& Vec) { + x -= Vec.x; + y -= Vec.y; + } + + void operator+=(const Vec2& Vec) { + x += Vec.x; + y += Vec.y; + } + + void operator*=(Type val) { + x *= val; + y *= val; + } + + void operator/=(Type val) { + x /= val; + y /= val; + } + + void operator/=(const Vec2& in) { + x /= in.x; + y /= in.y; + } + + void operator*=(const Vec2& in) { + x *= in.x; + y *= in.y; + } + + bool compareEach(const Vec& in) const { return (x > in.x && y > in.y); } + + bool operator>(const Vec& in) const { return length2() > in.length2(); } + bool operator<(const Vec& in) const { return length2() < in.length2(); } + bool operator>=(const Vec& in) const { return length2() >= in.length2(); } + bool operator<=(const Vec& in) const { return length2() <= in.length2(); } + bool operator==(const Vec& in) const { return (x == in.x && y == in.y); } + bool operator!=(const Vec& in) const { return (x != in.x || y != in.y); } + + Type& operator[](bool axes) { return (&x)[axes]; } + + const Type& operator[](bool axes) const { return (&x)[axes]; } + + // Vector Properties + alnf operator*(const Vec& in) const { + return dot(in); + } + + alnf dot(const Vec& in) const { + return (x * in.x + y * in.y); + } + + Vec unitV() const { + return Vec(*this / (Type) this->length()); + } + + void normalize() { + *this /= (Type) length(); + } + + Vec normal() { + return { -y, x }; + } + + alnf length2() const { + return (x * x + y * y); + } + + alnf length() const { + Type const tmp = (Type) (x * x + y * y); + return sqrt(tmp); + } + + // Vector Transformation + void Rot(float cosA, float sinA) { + Type tmp = x; + x = x * cosA - y * sinA; + y = tmp * sinA + y * cosA; + } + + void clamp(const Vec& min, const Vec& max) { + tp::clamp(x, min.x, max.x); + tp::clamp(y, min.y, max.y); + } + }; + + template + using Vec3 = Vec; + + using Vec3F = Vec3; + using Vec3I = Vec3; + + template + class Vec { + + public: + + Type x; + Type y; + Type z; + + // Initialization + Vec() {} + + Vec(const Vec& in) { + x = in[0]; + y = in[1]; + z = in[2]; + } + + Vec(Type aX, Type aY, Type aZ) { + x = aX; + y = aY; + z = aZ; + } + + Vec(Type Vec[3]) { + x = Vec[0]; + y = Vec[1]; + z = Vec[2]; + } + + Vec(Type x) { + assign(x); + } + + Vec(const Vec& Vec) { + x = Vec.x; + y = Vec.y; + z = Vec.z; + } + + void assign(Type aX, Type aY, Type aZ) { + x = aX; + y = aY; + z = aZ; + } + + void assign(const Vec& in) { + x = in.x; + y = in.y; + z = in.z; + } + + void assign(Type val) { + x = val; + y = val; + z = val; + } + + Vec& operator=(const Vec& in) { + x = in.x; + y = in.y; + z = in.z; + return *this; + } + + Vec& operator=(const Type* in) { + x = in[0]; + y = in[1]; + z = in[2]; + return *this; + } + + Type& operator[](alni i) { + DEBUG_ASSERT(i < 3 && i >= 0) + return (&x)[i]; + } + + const Type& operator[](alni i) const { + DEBUG_ASSERT(i < 3 && i >= 0) + return (&x)[i]; + } + + // create on stack + Vec operator+(const Vec& in) const { + return Vec(x + in.x, y + in.y, z + in.z); + } + + Vec operator-(const Vec& in) const { + return Vec(x - in.x, y - in.y, z - in.z); + } + + Vec operator+(Type val) const { + return Vec(x + val, y + val, z + val); + } + + Vec operator-(Type val) const { + return Vec(x - val, y - val, z - val); + } + + Vec operator*(Type val) const { + return Vec(x * val, y * val, z * val); + } + + Vec operator/(Type val) const { + return Vec(x / val, y / val, z / val); + } + + + // compare + bool operator>(const Vec& Vec) const { + return length2() > Vec.length2(); + } + + bool operator<(const Vec& Vec) const { + return length2() < Vec.length2(); + } + + bool operator>=(const Vec& Vec) const { + return length2() >= Vec.length2(); + } + + bool operator<=(const Vec& Vec) const { + return length2() <= Vec.length2(); + } + + bool operator==(const Vec& Vec) const { + return (x == Vec.x && y == Vec.y && z == Vec.z); + } + + // write + Vec operator-() { + return Vec(-x, -y, -z); + } + + void operator -= (Type val) { + x -= val; + y -= val; + z -= val; + } + + void operator += (Type val) { + x += val; + y += val; + z += val; + } + + void operator -= (const Vec& in) { + x -= in.x; + y -= in.y; + z -= in.z; + } + + void operator += (const Vec& in) { + x += in.x; + y += in.y; + z += in.z; + } + + void operator *= (Type val) { + x *= val; + y *= val; + z *= val; + } + + void operator /= (Type val) { + x /= val; + y /= val; + z /= val; + } + + // Vector Properties + Type dot(const Vec& in) const { + return (x * in.x + y * in.y + z * in.z); + } + + alnf length() const { + return sqrt((halnf) (x * x + y * y + z * z)); + } + + Type length2() const { + return (x * x + y * y + z * z); + } + + Vec unitV() const { + return *this / (Type) this->length(); + } + + Vec& normalize() { + *this /= (Type) this->length(); + return *this; + } + + Vec cross(const Vec& in) const { + return Vec( + y * in.z - z * in.y, + z * in.x - x * in.z, + x * in.y - y * in.x + ); + } + + Vec operator*(const Vec& in) const { + return cross(in); + } + + // Vector Transformation + void rotZ(alnf cosA, alnf sinA) { + Type tmp = x; + x = (Type) (x * cosA - y * sinA); + y = (Type) (tmp * sinA + y * cosA); + } + + void rotY(float cosA, float sinA) { + Type tmp = x; + x = (Type) (x * cosA - z * sinA); + z = (Type) (tmp * sinA + z * cosA); + } + + void rotX(float cosA, float sinA) { + Type tmp = y; + y = (Type) (y * cosA - z * sinA); + z = (Type) (tmp * sinA + z * cosA); + } + + halnf angelX() const { + return (halnf) atan2(y, z); + } + + halnf angelY() const { + return (halnf) atan2(x, z); + } + }; + + template + using Vec4 = Vec; + + using Vec4F = Vec4; + using Vec4I = Vec4; +} \ No newline at end of file diff --git a/Math/tests/TestCommon.cpp b/Math/tests/TestCommon.cpp new file mode 100644 index 0000000..b76e9b5 --- /dev/null +++ b/Math/tests/TestCommon.cpp @@ -0,0 +1,14 @@ + +#include "Testing.hpp" + +#include + +using namespace tp; + +TEST_DEF_STATIC(Simple) { + +} + +TEST_DEF(Math) { + testSimple(); +} \ No newline at end of file diff --git a/Math/tests/Tests.cpp b/Math/tests/Tests.cpp new file mode 100644 index 0000000..360c400 --- /dev/null +++ b/Math/tests/Tests.cpp @@ -0,0 +1,26 @@ + +#include "Testing.hpp" +#include "Utils.hpp" + +#include + +static bool init(const tp::ModuleManifest* self) { + tp::gTesting.setRootName(self->getName()); + return true; +} + +void testMath(); + +int main() { + + tp::ModuleManifest* deps[] = { &tp::gModuleUtils, nullptr }; + tp::ModuleManifest testModule("ContainersTest", init, nullptr, deps); + + if (!testModule.initialize()) { + return 1; + } + + testMath(); + + testModule.deinitialize(); +} diff --git a/Modules/public/TypeInfo.hpp b/Modules/public/TypeInfo.hpp index ecf6118..dbbb893 100644 --- a/Modules/public/TypeInfo.hpp +++ b/Modules/public/TypeInfo.hpp @@ -16,7 +16,7 @@ // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // -// The above copyright notice and this permission notice shall be included in +// The isAbove copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR diff --git a/TODO b/TODO index 1573574..7405988 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,8 @@ +All: + Serialization + Containers: - Read write + Add mem leakage tests Buffer 2d ! Add check copy times AVL dont copy data just swap @@ -9,7 +12,7 @@ Strings: Implement Math: - Implement + Testing Utils: Stack trace fixes