/** * @file gjk.h * @brief GJK (Gilbert–Johnson–Keerthi) 碰撞检测算法 * * 检测任意凸形状之间的碰撞与最近距离。 * 通过 Minkowski 差和支撑函数迭代逼近最近点对。 * * @ingroup collision */ #pragma once #include "vde/core/point.h" #include #include namespace vde::collision { using core::Point3D; using core::Vector3D; /** * @brief 支撑函数类型 * * 给定方向向量 d,返回形状在该方向上最远点的坐标: * @code{.cpp} * Point3D support(const Vector3D& d) { * return argmax_{v ∈ shape} dot(v, d); * } * @endcode * * 支撑函数是 GJK / EPA 算法的核心抽象。 * 只要实现了支撑函数,即可用于任意凸形状的碰撞检测。 */ using SupportFunc = std::function; /** * @brief GJK 完整检测结果 */ struct GJKResult { bool intersect; /**< 两凸体是否相交 */ double distance; /**< 最近距离(不相交时 >0,相交时为 0) */ Point3D point_a; /**< 形状 A 上的最近点 */ Point3D point_b; /**< 形状 B 上的最近点 */ }; /** * @brief GJK 相交检测(仅判断是否碰撞) * * 构造 Minkowski 差 A − B,在原点附近迭代构建单纯形。 * 若原点在 Minkowski 差内 → 碰撞。 * * 算法流程: * 1. 取初始方向(如 a.center − b.center) * 2. 计算支撑点并加入单纯形 * 3. 判断单纯形是否包含原点 * 4. 若不包含,更新搜索方向并迭代 * 5. 若方向无法收敛 → 不相交 * * 时间复杂度 O(n),其中 n 为迭代次数(通常 < 20 对于紧凑形状)。 * * @param shape_a 形状 A 的支撑函数 * @param shape_b 形状 B 的支撑函数 * @return 相交返回 true * * @code{.cpp} * auto sphere_support = [&](const Vector3D& d) { * return center + radius * d.normalized(); * }; * auto box_support = [&](const Vector3D& d) { * return center + Vector3D{ * half_extent.x * (d.x > 0 ? 1 : -1), * half_extent.y * (d.y > 0 ? 1 : -1), * half_extent.z * (d.z > 0 ? 1 : -1) * }; * }; * if (gjk_intersect(sphere_support, box_support)) { ... } * @endcode * * @see gjk_distance, gjk_full, SupportFunc */ bool gjk_intersect(const SupportFunc& shape_a, const SupportFunc& shape_b); /** * @brief GJK 最近距离查询 * * 在 gjk_intersect 基础上,不相交时额外调用 EPA (Expanding Polytope Algorithm) * 计算两形状之间的精确最近距离。 * * @param shape_a 形状 A 的支撑函数 * @param shape_b 形状 B 的支撑函数 * @return 最近距离(相交时返回 0),始终 ≥ 0 * * @see gjk_intersect, gjk_full */ double gjk_distance(const SupportFunc& shape_a, const SupportFunc& shape_b); /** * @brief GJK 完整检测:碰撞状态、距离、最近点对 * * 融合 gjk_intersect 和 gjk_distance,一次调用返回 * 碰撞状态、最近距离及两形状上的最近点。 * * @param shape_a 形状 A 的支撑函数 * @param shape_b 形状 B 的支撑函数 * @return GJKResult 包含碰撞状态、距离、最近点对 * * @note 对于连续碰撞检测 (CCD) 或需要分离向量的场景, * 使用此函数可同时获取碰撞信息和穿透深度方向。 * * @code{.cpp} * auto result = gjk_full(a_support, b_support); * if (result.intersect) { * // 穿透,分离方向 = (point_b - point_a).normalized() * } else { * double gap = result.distance; // 间隙 * } * @endcode * * @see gjk_intersect, gjk_distance */ GJKResult gjk_full(const SupportFunc& shape_a, const SupportFunc& shape_b); } // namespace vde::collision