#pragma once #include #include #include #include #include #include #include #include using namespace Eigen; template T color_mul_alpha(const T& c, float a) { return T{c[0], c[1], c[2], c[3] * a}; } template T circular_mod(T a, T b) { if(a >= 0) return a % b; else return b - (std::abs(a) % b); } template T circular_fmod(T a, T b) { if(a >= 0) return std::fmod(a, b); else return b - std::fmod(a, b); } template T round_to_multiple(T a, MultipleType multiple) { T toRet = round(a / multiple) * multiple; if((toRet == +0.0 || toRet == -0.0) && std::signbit(toRet)) toRet = 0.0; return toRet; } template T round_vec_to_multiple(T a, MultipleType multiple) { for(size_t i = 0; i < static_cast(a.size()); i++) a[i] = round_to_multiple(a[i], multiple); return a; } template Matrix perpendicular_vec2(const Matrix& vec) { return Matrix{-vec.y(), vec.x()}; } template T dist_point_line_segment(const Matrix& point, const Matrix& lineP1, const Matrix& lineP2) { T lenSqrd = (lineP1 - lineP2).squaredNorm(); if(lenSqrd == 0) return (point - lineP1).norm(); T t = std::max(0, std::min(1, (point - lineP1).dot(lineP2 - lineP1) / lenSqrd)); return (point - (lineP1 + t * (lineP2 - lineP1))).norm(); } template bool collision_circle_line_segment(const Matrix& circleCenter, T circleRadius, const Matrix& lineP1, const Matrix& lineP2) { return dist_point_line_segment(circleCenter, lineP1, lineP2) <= circleRadius; } // https://stackoverflow.com/a/53896859 template bool is_collision_ray_line_segment(const Vector& rayOrigin, const Vector& rayDirection, const Vector& point1, const Vector& point2) { Vector v1 = rayOrigin - point1; Vector v2 = point2 - point1; Vector v3 = Vector{-rayDirection.y(), rayDirection.x()}; Vector v2three{v2.x(), v2.y(), 0.0}; Vector v1three{v1.x(), v1.y(), 0.0}; T dot = v2.dot(v3); if (std::abs(dot) < 0.000001) return false; T t1 = v2three.cross(v1three) / dot; T t2 = v1.dot(v3) / dot; return (t1 >= 0.0 && (t2 >= 0.0 && t2 <= 1.0)); } // https://www.geeksforgeeks.org/program-for-point-of-intersection-of-two-lines/ template Vector line_line_intersection(const Vector& a, const Vector& b, const Vector& c, const Vector& d) { T a1 = b.y() - a.y(); T b1 = a.x() - b.x(); T c1 = a1*(a.x()) + b1*(a.y()); T a2 = d.y() - c.y(); T b2 = c.x() - d.x(); T c2 = a2*(c.x())+ b2*(c.y()); T determinant = a1*b2 - a2*b1; T x = (b2*c1 - b1*c2)/determinant; T y = (a1*c2 - a2*c1)/determinant; if(std::fabs(determinant) < 0.0000001f) return Vector{std::numeric_limits::max(), std::numeric_limits::max()}; return Vector{x, y}; } template Vector project_point_on_vec(const Vector& point, const Vector unnormalizedVec) { return ((point.dot(unnormalizedVec)) / unnormalizedVec.dot(unnormalizedVec)) * unnormalizedVec; } template Vector project_point_on_normalized_vec(const Vector& point, const Vector normalizedVec) { return point.dot(normalizedVec) * normalizedVec; } // https://stackoverflow.com/a/61342198 template Vector project_point_on_line(const Vector& p, const Vector& b, const Vector& a) { Vector ap = p - a; Vector ab = b - a; T t = ap.dot(ab) / ab.dot(ab); return a + t * ab; } // https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection template Vector line_line_intersection_inaccurate(const Matrix& a, const Matrix& b, const Matrix& c, const Matrix& d) { T x1 = a.x(); T x2 = b.x(); T x3 = c.x(); T x4 = d.x(); T y1 = a.y(); T y2 = b.y(); T y3 = c.y(); T y4 = d.y(); T p1 = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)); T p2 = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)); return Vector{p1, p2}; } // https://www.reddit.com/r/algorithms/comments/9moad4/what_is_the_simplest_to_implement_line_segment/ // Taken from https://github.com/vlecomte/cp-geo template T cross_vals_vec(const Matrix& a, const Matrix& b) { return a.x() * b.y() - a.y() * b.x(); } template T orient_vals_vec(const Matrix& a, const Matrix& b, const Matrix& c) { return cross_vals_vec((b - a).eval(), (c - a).eval()); } template bool is_collision_line_segment_line_segment(const Matrix& a, const Matrix& b, const Matrix& c, const Matrix& d) { T oa = orient_vals_vec(c, d, a); T ob = orient_vals_vec(c, d, b); T oc = orient_vals_vec(a, b, c); T od = orient_vals_vec(a, b, d); return (oa * ob < T(0) && oc * od < T(0)); } template std::optional collision_line_segment_line_segment_pos(const Matrix& a, const Matrix& b, const Matrix& c, const Matrix& d) { T oa = orient_vals_vec(c, d, a); T ob = orient_vals_vec(c, d, b); T oc = orient_vals_vec(a, b, c); T od = orient_vals_vec(a, b, d); if(oa * ob < 0 && oc * od < 0) return std::optional((a * ob - b * oa) / (ob - oa)); return std::nullopt; } template bool is_collision_aabb_line_segment(const AlignedBox& aabb, const Matrix& lineP1, const Matrix& lineP2) { return aabb.contains(lineP1) || aabb.contains(lineP2) || is_collision_line_segment_line_segment(lineP1, lineP2, aabb.corner(AlignedBox::TopLeft), aabb.corner(AlignedBox::TopRight)) || is_collision_line_segment_line_segment(lineP1, lineP2, aabb.corner(AlignedBox::TopRight), aabb.corner(AlignedBox::BottomRight)) || is_collision_line_segment_line_segment(lineP1, lineP2, aabb.corner(AlignedBox::BottomLeft), aabb.corner(AlignedBox::BottomRight)) || is_collision_line_segment_line_segment(lineP1, lineP2, aabb.corner(AlignedBox::TopLeft), aabb.corner(AlignedBox::BottomLeft)); } template T vec_distance(const Vector& v1, const Vector& v2) { return (v1 - v2).norm(); } template T vec_length(const Vector& v) { return v.norm(); } template T cwise_vec_min(const T& v1, const T& v2) { T toRet; for(unsigned i = 0; i < v1.size(); i++) toRet[i] = std::min(v1[i], v2[i]); return toRet; } template T cwise_vec_max(const T& v1, const T& v2) { T toRet; for(unsigned i = 0; i < v1.size(); i++) toRet[i] = std::max(v1[i], v2[i]); return toRet; } template T cwise_vec_clamp(const T& v1, const T& v2, const T& v3) { T toRet; for(unsigned i = 0; i < v1.size(); i++) toRet[i] = std::clamp(v1[i], v2[i], v3[i]); return toRet; } template T cwise_vec_modf(const T& v1, T& v2) { T toRet; for(unsigned i = 0; i < v1.size(); i++) toRet[i] = std::modf(v1[i], &v2[i]); return toRet; } template std::vector> arc_vec(const Vector& arcCenter, const Vector& arcDirStart, const Vector& arcDirEnd, const Vector& arcDirCenter, T arcRadius, unsigned numOfDivisions) { T a1 = std::atan2(arcDirStart.y(), arcDirStart.x()); T a2 = std::atan2(arcDirEnd.y(), arcDirEnd.x()); T a3 = std::atan2(arcDirCenter.y(), arcDirCenter.x()); T minAngle = std::min(a1, a2); T maxAngle = std::max(a1, a2); bool swapHappen = (a3 > minAngle && a3 > maxAngle) || (a3 < minAngle && a3 < maxAngle); if(swapHappen) { minAngle += 2.0 * std::numbers::pi; std::swap(minAngle, maxAngle); } bool flipped = minAngle == a2; std::vector> toRet; T tStep = 1.0 / static_cast(numOfDivisions); if(flipped) { for(unsigned i = numOfDivisions - 1; i > 0; i--) { T newAngle = std::lerp(minAngle, maxAngle, tStep * i); toRet.emplace_back(arcCenter + Vector{std::cos(newAngle), std::sin(newAngle)} * arcRadius); } } else { for(unsigned i = 1; i < numOfDivisions; i++) { T newAngle = std::lerp(minAngle, maxAngle, tStep * i); toRet.emplace_back(arcCenter + Vector{std::cos(newAngle), std::sin(newAngle)} * arcRadius); } } return toRet; } template std::vector> gen_circle_points(const Vector& center, T radius, unsigned numOfPoints) { std::vector> toRet; for(unsigned i = 0; i < numOfPoints; i++) { T angle = (static_cast(i) / static_cast(numOfPoints)) * std::numbers::pi * 2; toRet.emplace_back(std::cos(angle) * radius + center.x(), std::sin(angle) * radius + center.y()); } return toRet; } // https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline template T catmull_rom_get_t(T t, T alpha, const Vector& p0, const Vector& p1) { Vector d = p1 - p0; T a = d.dot(d); T b = std::pow(a, alpha * 0.5); return (b + t); } template Vector catmull_rom(const Vector& p0, const Vector& p1, const Vector& p2, const Vector& p3, T t, T alpha = 0.5) { T t0 = 0.0; T t1 = catmull_rom_get_t(t0, alpha, p0, p1); T t2 = catmull_rom_get_t(t1, alpha, p1, p2); T t3 = catmull_rom_get_t(t2, alpha, p2, p3); t = std::lerp(t1, t2, t); Vector A1 = ( t1-t )/( t1-t0 )*p0 + ( t-t0 )/( t1-t0 )*p1; Vector A2 = ( t2-t )/( t2-t1 )*p1 + ( t-t1 )/( t2-t1 )*p2; Vector A3 = ( t3-t )/( t3-t2 )*p2 + ( t-t2 )/( t3-t2 )*p3; Vector B1 = ( t2-t )/( t2-t0 )*A1 + ( t-t0 )/( t2-t0 )*A2; Vector B2 = ( t3-t )/( t3-t1 )*A2 + ( t-t1 )/( t3-t1 )*A3; Vector C = ( t2-t )/( t2-t1 )*B1 + ( t-t1 )/( t2-t1 )*B2; return C; } template T transform_scalar(const Transform& transform, T scalar) { return (transform.linear() * Vector{scalar, scalar}).x(); } template std::array, 4> triangle_from_rect_points(const Vector& min, const Vector& max) { return {min, {min.x(), max.y()}, max, {max.x(), min.y()}}; }