天气与日历 切换到窄版

 找回密码
 立即注册
中国膜结构网
十大进口膜材评选 十大国产膜材评选 十大膜结构设计评选 十大膜结构公司评选
查看: 88|回复: 2

NFP碰撞检测算法实现

[复制链接]
  • TA的每日心情
    开心
    2024-8-31 15:58
  • 签到天数: 89 天

    [LV.6]常住居民II

    488

    主题

    207

    回帖

    3366

    积分

    管理员

    积分
    3366
    发表于 2024-6-22 09:46:18 | 显示全部楼层 |阅读模式
    NFP (No Fit Polygon) 临界多边形算法移动碰撞检测方法用于避免两个多边形发生重叠。

    基本步骤如你所描述:
    初始化: 获取多边形A和B的顶点集合(vector<AcGePoint2d> py1 和 py2)。
    判断状态: 根据当前两个多边形的相对位置,确定B的移动方向。这通常涉及到计算它们的边界点和接触点。
    计算碰撞距离: 在B的移动方向上,找出A和B之间的最小碰撞距离。这可以通过遍历B的边界,检查与A的边界碰撞点来实现。
    更新移动: 根据碰撞距离调整B的移动量。如果距离小于0,则说明存在碰撞,调整后移量以避免冲突。
    移动B: 将B移动到新的位置,并更新其顶点集合。
    循环检测: 重复步骤2-5,直到B完成一次完整的移动周期,即回到初始位置。这时可以检查是否有重叠或间隙,如果没有,说明碰撞已避免。  
    1. 由于直接提供一个完整的C++代码来实现NFP (No Fit Polygon) 临界多边形算法会相对复杂且超出简答的范畴,但我可以提供一个简化的框架和关键步骤的伪代码,帮助你理解如何实现这个算法。
    2. 请注意,这个伪代码不包括所有的边界情况和优化,但它应该足以指导你开始实现。
    3. #include <vector>
    4. #include <geometry_library> // 假设这里有一个处理几何运算的库
    5. // 假设 AcGePoint2d 是一个二维点类,包含 x 和 y 属性
    6. class AcGePoint2d {
    7. public:
    8.     double x, y;
    9.     // ... 其他成员和函数 ...
    10. };
    11. // 计算多边形之间的最小碰撞距离和碰撞方向
    12. double CalculateCollisionDistanceAndDirection(const std::vector<AcGePoint2d>& polyA,
    13.                                              const std::vector<AcGePoint2d>& polyB,
    14.                                              Vector2d& collisionDirection) {
    15.     // 实现计算最小碰撞距离和碰撞方向的逻辑
    16.     // 这里需要使用到多边形顶点之间的距离计算、向量运算等
    17.     // ...
    18.     return minDistance; // 返回最小碰撞距离
    19. }
    20. // 调整多边形B的移动方向和距离以避免碰撞
    21. void AdjustPolygonBMovement(double collisionDistance, Vector2d collisionDirection,
    22.                             Vector2d& movementVector) {
    23.     // 根据碰撞距离和方向调整移动向量
    24.     // ...
    25. }
    26. // 移动多边形B并更新其顶点集合
    27. void MovePolygonB(std::vector<AcGePoint2d>& polyB, const Vector2d& movementVector) {
    28.     // 遍历多边形B的每个顶点,并应用移动向量
    29.     // ...
    30. }
    31. // 主函数,实现NFP碰撞检测和处理
    32. void NFPCollisionDetection(std::vector<AcGePoint2d>& polyA, std::vector<AcGePoint2d>& polyB,
    33.                            const Vector2d& initialMovementVector) {
    34.     Vector2d movementVector = initialMovementVector;
    35.     bool collisionDetected = true;
    36.    
    37.     // 循环检测直到没有碰撞或完成一个完整的移动周期
    38.     while (collisionDetected) {
    39.         double collisionDistance;
    40.         Vector2d collisionDirection;
    41.         
    42.         // 计算碰撞距离和方向
    43.         collisionDistance = CalculateCollisionDistanceAndDirection(polyA, polyB, collisionDirection);
    44.         
    45.         // 如果碰撞距离小于0,说明存在碰撞
    46.         if (collisionDistance < 0) {
    47.             // 调整多边形B的移动方向和距离
    48.             AdjustPolygonBMovement(collisionDistance, collisionDirection, movementVector);
    49.         } else {
    50.             // 如果没有碰撞,则退出循环
    51.             collisionDetected = false;
    52.         }
    53.         
    54.         // 移动多边形B并更新其顶点集合
    55.         MovePolygonB(polyB, movementVector);
    56.         
    57.         // 检查是否完成一个完整的移动周期(回到初始位置)
    58.         // ...
    59.     }
    60.    
    61.     // 在这里可以检查多边形A和B之间是否有重叠或间隙
    62.     // ...
    63. }
    64. // 注意:上面的代码中的 Vector2d 需要你自己定义,或者使用一个现有的数学库(如Eigen)
    65. 请记住,实现NFP算法需要对计算几何有深入的理解,特别是关于多边形相交、向量运算、距离计算等。你可能需要用到一个强大的几何库(如CGAL、Boost.Geometry等)来辅助你实现这些复杂的计算。
    66. 另外,上面的代码仅作为一个概念框架,实际实现时你可能需要考虑更多的细节,比如处理移动后多边形可能产生的自相交问题,以及如何定义和检测一个完整的移动周期等。
    复制代码
    1. 计算多边形之间的最小碰撞距离和碰撞方向是一个复杂的过程,特别是当涉及多个边和角的情况时。这里提供一个简化的伪代码和注释,以帮助你理解这个过程,但请注意,完整的实现将需要详细的几何运算和可能的优化。
    2. 首先,你需要一个Vector2d类来表示二维向量。这里是一个简单的实现:
    3. #include <cmath>
    4. struct Vector2d {
    5.     double x, y;
    6.     // 向量加法
    7.     Vector2d operator+(const Vector2d& other) const {
    8.         return {x + other.x, y + other.y};
    9.     }
    10.     // 向量减法
    11.     Vector2d operator-(const Vector2d& other) const {
    12.         return {x - other.x, y - other.y};
    13.     }
    14.     // 向量长度
    15.     double length() const {
    16.         return std::sqrt(x * x + y * y);
    17.     }
    18.     // 向量单位化
    19.     Vector2d normalize() const {
    20.         double len = length();
    21.         return {x / len, y / len};
    22.     }
    23.     // 向量点积
    24.     double dot(const Vector2d& other) const {
    25.         return x * other.x + y * other.y;
    26.     }
    27.     // 向量叉积(二维空间中的垂直分量)
    28.     double cross(const Vector2d& other) const {
    29.         return x * other.y - y * other.x;
    30.     }
    31. };
    32. // 假设 AcGePoint2d 已经是一个包含 x 和 y 属性的点类
    33. struct AcGePoint2d {
    34.     double x, y;
    35.     // 从点创建向量
    36.     Vector2d toVector(const AcGePoint2d& other) const {
    37.         return {other.x - x, other.y - y};
    38.     }
    39.     // ... 其他成员和函数 ...
    40. };
    41. // 伪代码:计算多边形之间的最小碰撞距离和碰撞方向
    42. double CalculateCollisionDistanceAndDirection(const std::vector<AcGePoint2d>& polyA,
    43.                                               const std::vector<AcGePoint2d>& polyB,
    44.                                               Vector2d& collisionDirection) {
    45.     double minDistance = std::numeric_limits<double>::max();
    46.     collisionDirection = {0, 0}; // 初始化为零向量
    47.     // 遍历多边形B的所有边
    48.     for (size_t i = 0; i < polyB.size(); ++i) {
    49.         size_t j = (i + 1) % polyB.size(); // 下一个顶点的索引,考虑多边形是闭合的
    50.         // 计算多边形B的当前边的向量
    51.         Vector2d edgeB = polyB[j].toVector(polyB[i]);
    52.         // 遍历多边形A的所有边和顶点
    53.         for (size_t k = 0; k < polyA.size(); ++k) {
    54.             size_t l = (k + 1) % polyA.size(); // 下一个顶点的索引
    55.             // 计算多边形A的当前边或顶点到多边形B当前边的距离
    56.             // 这里需要用到线到线、线到点、点到线的距离公式
    57.             // 假设函数 CalculateDistance 实现了这些计算
    58.             double distance = CalculateDistance(polyA[k], polyA[l], polyB[i], polyB[j]);
    59.             // 更新最小距离和碰撞方向(这里只是示意,实际逻辑会更复杂)
    60.             if (distance < minDistance) {
    61.                 minDistance = distance;
    62.                 // 碰撞方向通常是最近点的连线方向,但具体取决于你的应用场景
    63.                 // 这里只是简单地将碰撞方向设为从B的一个顶点到A的一个顶点
    64.                 collisionDirection = polyA[k].toVector(polyB[i]).normalize();
    65.             }
    66.         }
    67.     }
    68.     // 如果需要,可以进一步处理碰撞方向,比如找到真正的最近穿透向量等
    69.     return minDistance;
    70. }
    71. // 注意:上面的 CalculateDistance 函数需要根据实际情况实现
    72. // 它需要处理线到线、线到点、点到线的距离计算
    73. // ... 其他代码 ...
    74. 请注意,上面的CalculateDistance函数需要你自己实现,因为它会根据多边形A和B的边的具体位置和类型(线段、射线、直线)来计算距离。此外,碰撞方向的计算也可能需要更复杂的逻辑,特别是当多边形A和B的边缘相互交叉时。
    75. 在实际应用中,你可能还需要考虑使用更高效的算法(如分离轴定理SAT、闵可夫斯基和(Minkowski Sum等)来检测多边形之间的碰撞,并计算最小碰撞距离和碰撞方向。这些算法通常比简单地遍历所有边和顶点对更高效。
    76. 下面是一个简化的框架,用于实现基于分离轴定理(Separating Axis Theorem, SAT)的碰撞检测。SAT的基本思想是通过找到一条能够将两个多边形完全分开的轴来判断它们是否碰撞。
    77. #include <cmath>
    78. #include <vector>
    79. #include <algorithm>
    80. // ... (之前定义的Vector2d和AcGePoint2d类) ...
    81. // 判断点是否在多边形内(简化版,不考虑边界情况)
    82. bool PointInPolygon(const AcGePoint2d& point, const std::vector<AcGePoint2d>& polygon) {
    83.     // ... 实现点在多边形内的判断逻辑 ...
    84.     return false; // 示例返回false,实际需实现具体逻辑
    85. }
    86. // 计算两个点之间的向量
    87. Vector2d VectorBetweenPoints(const AcGePoint2d& a, const AcGePoint2d& b) {
    88.     return {b.x - a.x, b.y - a.y};
    89. }
    90. // 计算点到一个轴(由单位向量定义)的投影距离
    91. double ProjectPointToAxis(const AcGePoint2d& point, const Vector2d& axis) {
    92.     return point.toVector(AcGePoint2d{0, 0}).dot(axis);
    93. }
    94. // 计算多边形到一个轴(由单位向量定义)的投影区间
    95. std::pair<double, double> ProjectPolygonToAxis(const std::vector<AcGePoint2d>& polygon, const Vector2d& axis) {
    96.     double minProj = std::numeric_limits<double>::max();
    97.     double maxProj = -std::numeric_limits<double>::max();
    98.     for (const auto& point : polygon) {
    99.         double proj = ProjectPointToAxis(point, axis);
    100.         minProj = std::min(minProj, proj);
    101.         maxProj = std::max(maxProj, proj);
    102.     }
    103.     return {minProj, maxProj};
    104. }
    105. // 使用分离轴定理检测两个多边形是否碰撞
    106. bool SATCollisionDetection(const std::vector<AcGePoint2d>& polyA, const std::vector<AcGePoint2d>& polyB) {
    107.     // 检查多边形A和多边形B的边作为潜在的分离轴
    108.     for (size_t i = 0; i < polyA.size(); ++i) {
    109.         Vector2d axisA = VectorBetweenPoints(polyA[i], polyA[(i + 1) % polyA.size()]).normalize();
    110.         if (!OverlapOnAxis(ProjectPolygonToAxis(polyA, axisA), ProjectPolygonToAxis(polyB, axisA))) {
    111.             return false; // 分离,没有碰撞
    112.         }
    113.     }
    114.     for (size_t i = 0; i < polyB.size(); ++i) {
    115.         Vector2d axisB = VectorBetweenPoints(polyB[i], polyB[(i + 1) % polyB.size()]).normalize();
    116.         if (!OverlapOnAxis(ProjectPolygonToAxis(polyA, axisB), ProjectPolygonToAxis(polyB, axisB))) {
    117.             return false; // 分离,没有碰撞
    118.         }
    119.     }
    120.     // 也可以检查多边形A和多边形B的法线作为潜在的分离轴(这里省略)
    121.     // 如果没有找到分离轴,则多边形重叠或接触
    122.     return true; // 碰撞或接触
    123. }
    124. // 检查两个投影区间是否重叠
    125. bool OverlapOnAxis(const std::pair<double, double>& projA, const std::pair<double, double>& projB) {
    126.     return !(projA.second < projB.first || projB.second < projA.first);
    127. }
    128. // ... 其他代码 ...
    129. // 注意:上面的SATCollisionDetection函数只检测碰撞,而不计算最小碰撞距离和碰撞方向。
    130. // 要计算这些值,你需要在发现碰撞时进一步分析投影区间和相关的多边形边。
    131. 使用SAT进行碰撞检测时,你通常不需要直接计算最小碰撞距离和碰撞方向,因为SAT主要用于快速判断两个多边形是否碰撞。然而,如果你确实需要这些值,你可能需要在发现碰撞后进一步分析相关的边和顶点,并使用几何运算来计算它们。这通常涉及更复杂的计算,并且可能需要使用特定的算法或数据结构来优化性能。
    复制代码

     

     

     

     

    NFP碰撞检测算法实现
    中国膜结构网打造全中国最好的膜结构综合平台 ,统一协调膜结构设计,膜结构施工,膜材采购,膜材定制,膜结构预算全方位服务。 中国空间膜结构协会合作单位。
  • TA的每日心情
    开心
    昨天 15:13
  • 签到天数: 153 天

    [LV.7]常住居民III

    3456

    主题

    553

    回帖

    214748万

    积分

    管理员

    中国膜结构网www.mjgou.com

    积分
    2147483647
    QQ
    发表于 2024-6-29 16:34:00 | 显示全部楼层
    实现一个完整的CalculateDistance函数来处理多边形边之间的所有可能情况(相交、不相交、重合、平行等)是一个相当复杂的任务,因为它需要处理多种几何情况和边界条件。不过,我可以给你一个简化的框架和示例代码,说明如何开始这样的实现。

    请注意,下面的代码仅用于演示目的,并不涵盖所有可能的边界情况和优化。对于生产环境的应用,你可能需要使用更健壮和经过优化的库或算法。

    首先,我们需要定义一些辅助函数来处理线段和点的基本几何操作。然后,我们可以实现CalculateDistance函数来处理不同的几何关系。

    #include <cmath>
    #include <vector>
    #include <limits>

    // ... (之前定义的Vector2d和AcGePoint2d类) ...

    // 判断点是否在线段上(包括端点)
    bool IsPointOnSegment(const AcGePoint2d& point, const AcGePoint2d& segmentStart, const AcGePoint2d& segmentEnd) {
        Vector2d vec1 = segmentStart.toVector(point);
        Vector2d vec2 = segmentStart.toVector(segmentEnd);
        return std::fabs(vec1.cross(vec2)) < 1e-6 && // 点到线段的垂直距离几乎为0
               vec1.dot(vec2) >= 0 && // 点在线段的投影点在线段上
               (vec1.lengthSquared() <= vec2.lengthSquared()); // 点到线段起点的距离不超过线段长度
    }

    // 计算点到线段的最近点(垂足)和距离
    std::pair<AcGePoint2d, double> ClosestPointOnSegment(const AcGePoint2d& point, const AcGePoint2d& segmentStart, const AcGePoint2d& segmentEnd) {
        Vector2d segmentVec = segmentStart.toVector(segmentEnd);
        double segmentLengthSquared = segmentVec.lengthSquared();
        if (segmentLengthSquared < 1e-6) { // 线段退化为点
            return {segmentStart, (point - segmentStart).length()};
        }

        Vector2d pointVec = segmentStart.toVector(point);
        double t = pointVec.dot(segmentVec) / segmentLengthSquared;
        if (t < 0) { // 垂足在线段起点之外
            return {segmentStart, pointVec.length()};
        } else if (t > 1) { // 垂足在线段终点之外
            return {segmentEnd, (point - segmentEnd).length()};
        }

        // 垂足在线段上
        AcGePoint2d closestPoint = segmentStart + segmentVec * t;
        return {closestPoint, (point - closestPoint).length()};
    }

    // 计算两个线段之间的最小距离(简化版,不考虑平行且不相交的情况)
    double CalculateDistance(const AcGePoint2d& a1, const AcGePoint2d& a2, const AcGePoint2d& b1, const AcGePoint2d& b2) {
        double minDistance = std::numeric_limits<double>::max();

        // 检查线段a的端点是否在线段b上
        for (const auto& point : {a1, a2}) {
            if (IsPointOnSegment(point, b1, b2)) {
                return 0.0; // 重合
            }
        }

        // 检查线段b的端点是否在线段a上
        for (const auto& point : {b1, b2}) {
            if (IsPointOnSegment(point, a1, a2)) {
                return 0.0; // 重合
            }
        }

        // 计算a的每个端点到线段b的最短距离
        for (const auto& point : {a1, a2}) {
            auto [closestPoint, distance] = ClosestPointOnSegment(point, b1, b2);
            minDistance = std::min(minDistance, distance);
        }

        // 计算b的每个端点到线段a的最短距离
        for (const auto& point : {b1, b2}) {
            auto [closestPoint, distance] = ClosestPointOnSegment(point, a1, a2);
            minDistance = std::min(minDistance, distance);
        }

        return minDistance;
    }

    // ... 其他代码 ...

    // 注意:这个CalculateDistance函数没有处理平行且不相交的情况,也没有考虑浮点数的精度问题。
    // 在实际应用中,你可能需要添加额外的逻辑来处理这些情况,并使用更健壮的方法来比较浮点数。

     

     

     

     

    NFP碰撞检测算法实现
    中国膜结构网打造全中国最好的膜结构综合平台 ,统一协调膜结构设计,膜结构施工,膜材采购,膜材定制,膜结构预算全方位服务。 中国空间膜结构协会合作单位。
  • TA的每日心情
    开心
    昨天 15:13
  • 签到天数: 153 天

    [LV.7]常住居民III

    3456

    主题

    553

    回帖

    214748万

    积分

    管理员

    中国膜结构网www.mjgou.com

    积分
    2147483647
    QQ
    发表于 2024-6-29 16:35:28 | 显示全部楼层
    1. nfp 临界多边形算法移动碰撞法的基本步骤如下: 首先根据多边形A和B当前时刻的靠接状态,得到B下一步的移动方向,在该移动方向上,计算出A和B之间的最小碰撞距离,从而得到移动距离,根据移动方向和移动距离,将B移动到新的位置,然后重复以上过程,直至绕完一圈,回到初始位置。 完整c++代码   static        vector<AcGePoint2d>  NoFitPolygon(vector<AcGePoint2d> py1, vector<AcGePoint2d> py2)
    2. 实现NFP(Non-Fitting Polygon)临界多边形算法涉及多个步骤,包括计算多边形的凸包、确定接触点、计算最小碰撞距离等。下面是一个简化版的C++伪代码示例,用于演示如何使用NFP算法来处理两个多边形A和B的碰撞检测和响应:
    3. Cpp
    4. #include <vector>
    5. #include <iostream>
    6. struct AcGePoint2d {
    7.     double x;
    8.     double y;
    9. };
    10. // 简化版本的NFP算法实现
    11. std::vector<AcGePoint2d> NoFitPolygon(std::vector<AcGePoint2d> poly1, std::vector<AcGePoint2d> poly2) {
    12.     std::vector<AcGePoint2d> result;
    13.     // 假设poly1和poly2已经经过预处理,确保它们是凸的且在原点附近
    14.     while (true) {
    15.         // 计算poly1和poly2之间的最小距离和相应的法线
    16.         AcGePoint2d normal = calculateNormal(poly1, poly2);
    17.         double minDistance = calculateMinDistance(poly1, poly2, normal);
    18.         if (minDistance > 0) {
    19.             // 如果最小距离大于0,则poly1和poly2之间存在碰撞
    20.             // 根据碰撞响应调整poly2的位置
    21.             movePolygon(poly2, -minDistance * normal);
    22.         } else {
    23.             // 如果最小距离小于等于0,则poly1和poly2没有碰撞
    24.             break;
    25.         }
    26.         // 检查是否回到了初始位置
    27.         if (isBackToStart(poly1, poly2)) {
    28.             break;
    29.         }
    30.     }
    31.     return result; // 返回结果,这里可能需要返回一些关于NFP的信息,如碰撞点或碰撞次数
    32. }
    33. AcGePoint2d calculateNormal(std::vector<AcGePoint2d> poly1, std::vector<AcGePoint2d> poly2) {
    34.     // 计算poly1和poly2之间的最小距离的法线
    35.     // 这里省略了具体的实现细节
    36.     AcGePoint2d normal;
    37.     return normal;
    38. }
    39. double calculateMinDistance(std::vector<AcGePoint2d> poly1, std::vector<AcGePoint2d> poly2, AcGePoint2d normal) {
    40.     // 计算poly1和poly2之间的最小距离
    41.     // 这里省略了具体的实现细节
    42.     double minDistance = 0;
    43.     return minDistance;
    44. }
    45. void movePolygon(std::vector<AcGePoint2d>& poly, AcGePoint2d direction) {
    46.     // 根据方向移动多边形
    47.     for (auto& point : poly) {
    48.         point.x += direction.x;
    49.         point.y += direction.y;
    50.     }
    51. }
    52. bool isBackToStart(std::vector<AcGePoint2d> poly1, std::vector<AcGePoint2d> poly2) {
    53.     // 检查是否回到了初始位置
    54.     // 这里省略了具体的实现细节
    55.     bool backToStart = false;
    56.     return backToStart;
    57. }
    58. 请注意,上述代码中的calculateNormal、calculateMinDistance、movePolygon和isBackToStart函数都需要具体实现。此外,实际应用中可能还需要考虑多边形旋转、速度等因素。
    59. 由于NFP算法的具体实现较为复杂,涉及到几何计算、线性代数等多个领域的知识,上述代码仅提供了一个框架性的示例,具体实现时需要根据实际情况进行详细设计和编程。
    60. double computeCollisionDistance(vector<AcGePoint2d>& poly1, vector<AcGePoint2d>& poly2) {
    61. // 遍历poly2边界,与poly1边界对比
    62. // ...
    63. }  完整的C++代码 给出
    64. 计算两个多边形碰撞距离的过程通常涉及找到能够分离这两个多边形的轴,这个轴可以是其中一个多边形的边的法线。下面是一个基于分离轴定理(Separating Axis Theorem, SAT)的简化版C++函数,用于计算两个多边形之间的最小碰撞距离。请注意,为了简化问题,我们假设输入的多边形是凸的。
    65. Cpp
    66. #include <vector>
    67. #include <algorithm>
    68. #include <cmath>
    69. struct AcGePoint2d {
    70.     double x;
    71.     double y;
    72. };
    73. // 向量点乘
    74. double dotProduct(const AcGePoint2d& v1, const AcGePoint2d& v2) {
    75.     return v1.x * v2.x + v1.y * v2.y;
    76. }
    77. // 向量叉乘
    78. double crossProduct(const AcGePoint2d& v1, const AcGePoint2d& v2) {
    79.     return v1.x * v2.y - v1.y * v2.x;
    80. }
    81. // 计算向量的长度
    82. double vectorLength(const AcGePoint2d& v) {
    83.     return std::sqrt(v.x * v.x + v.y * v.y);
    84. }
    85. // 计算向量的单位化
    86. AcGePoint2d normalizeVector(const AcGePoint2d& v) {
    87.     double length = vectorLength(v);
    88.     return {v.x / length, v.y / length};
    89. }
    90. // 计算两个多边形之间的最小碰撞距离
    91. double computeCollisionDistance(std::vector<AcGePoint2d>& poly1, std::vector<AcGePoint2d>& poly2) {
    92.     double minDistance = std::numeric_limits<double>::max();
    93.     AcGePoint2d normal;
    94.     // 对于poly1的每条边
    95.     for (size_t i = 0; i < poly1.size(); ++i) {
    96.         size_t nextIndex = (i + 1) % poly1.size();
    97.         AcGePoint2d edge = {poly1[nextIndex].x - poly1[i].x, poly1[nextIndex].y - poly1[i].y};
    98.         AcGePoint2d edgeNormal = normalizeVector({-edge.y, edge.x});
    99.         double minProjPoly1 = dotProduct(edgeNormal, poly1[i]);
    100.         double maxProjPoly1 = minProjPoly1;
    101.         for (size_t j = 0; j < poly1.size(); ++j) {
    102.             double proj = dotProduct(edgeNormal, poly1[j]);
    103.             minProjPoly1 = std::min(minProjPoly1, proj);
    104.             maxProjPoly1 = std::max(maxProjPoly1, proj);
    105.         }
    106.         double minProjPoly2 = dotProduct(edgeNormal, poly2[0]);
    107.         double maxProjPoly2 = minProjPoly2;
    108.         for (size_t j = 0; j < poly2.size(); ++j) {
    109.             double proj = dotProduct(edgeNormal, poly2[j]);
    110.             minProjPoly2 = std::min(minProjPoly2, proj);
    111.             maxProjPoly2 = std::max(maxProjPoly2, proj);
    112.         }
    113.         if (maxProjPoly1 < minProjPoly2 || maxProjPoly2 < minProjPoly1) {
    114.             // 如果两个多边形在这个轴上的投影不重叠,则没有碰撞
    115.             return std::numeric_limits<double>::max();
    116.         }
    117.         double overlap = std::max(0.0, std::min(maxProjPoly1, maxProjPoly2) - std::max(minProjPoly1, minProjPoly2));
    118.         if (overlap < minDistance) {
    119.             minDistance = overlap;
    120.             normal = edgeNormal;
    121.         }
    122.     }
    123.     // 对于poly2的每条边,重复上述过程
    124.     for (size_t i = 0; i < poly2.size(); ++i) {
    125.         size_t nextIndex = (i + 1) % poly2.size();
    126.         AcGePoint2d edge = {poly2[nextIndex].x - poly2[i].x, poly2[nextIndex].y - poly2[i].y};
    127.         AcGePoint2d edgeNormal = normalizeVector({-edge.y, edge.x});
    128.         double minProjPoly1 = dotProduct(edgeNormal, poly1[0]);
    129.         double maxProjPoly1 = minProjPoly1;
    130.         for (size_t j = 0; j < poly1.size(); ++j) {
    131.             double proj = dotProduct(edgeNormal, poly1[j]);
    132.             minProjPoly1 = std::min(minProjPoly1, proj);
    133.             maxProjPoly1 = std::max(maxProjPoly1, proj);
    134.         }
    135.         double minProjPoly2 = dotProduct(edgeNormal, poly2[i]);
    136.         double maxProjPoly2 = minProjPoly2;
    137.         for (size_t j = 0; j < poly2.size(); ++j) {
    138.             double proj = dotProduct(edgeNormal, poly2[j]);
    139.             minProjPoly2 = std::min(minProjPoly2, proj);
    140.             maxProjPoly2 = std::max(maxProjPoly2, proj);
    141.         }
    142.         if (maxProjPoly1 < minProjPoly2 || maxProjPoly2 < minProjPoly1) {
    143.             return std::numeric_limits<double>::max();
    144.         }
    145.         double overlap = std::max(0.0, std::min(maxProjPoly1, maxProjPoly2) - std::max(minProjPoly1, minProjPoly2));
    146.         if (overlap < minDistance) {
    147.             minDistance = overlap;
    148.             normal = edgeNormal;
    149.         }
    150.     }
    151.     return minDistance;
    152. }
    153. 这段代码实现了通过遍历每个多边形的所有边,并检查它们的法线方向是否能作为分离轴来判断两个多边形是否有碰撞,以及如果碰撞发生,计算最小碰撞距离。请注意,这个函数返回的是两个多边形之间的最小重叠距离,如果返回值为正,则表示两个多边形有碰撞;如果返回值为std::numeric_limits<double>::max(),则表示没有碰撞。
    154. // 计算多边形之间的最小碰撞距离和碰撞方向 double CalculateCollisionDistanceAndDirection(const std::vector<AcGePoint2d>& polyA,                                              const std::vector<AcGePoint2d>& polyB,                                              Vector2d& collisionDirection) {     // 实现计算最小碰撞距离和碰撞方向的逻辑     // 这里需要使用到多边形顶点之间的距离计算、向量运算等     // ...     return minDistance; // 返回最小碰撞距离 }完整的C++代码
    155. 在计算两个多边形之间的最小碰撞距离及其方向时,我们可以采用分离轴定理(Separating Axis Theorem, SAT)。以下是基于SAT的C++代码实现,它不仅计算了最小碰撞距离,还返回了碰撞方向。我们使用Vector2d作为collisionDirection的类型,这可以是一个自定义的结构体或者类来表示二维向量。
    156. 首先,我们需要定义Vector2d类型:
    157. Cpp
    158. struct Vector2d {
    159.     double x, y;
    160.     Vector2d(double x = 0.0, double y = 0.0) : x(x), y(y) {}
    161.     Vector2d operator-(const Vector2d& other) const {
    162.         return Vector2d(x - other.x, y - other.y);
    163.     }
    164.     double dot(const Vector2d& other) const {
    165.         return x * other.x + y * other.y;
    166.     }
    167.     Vector2d normalized() const {
    168.         double len = sqrt(x * x + y * y);
    169.         return Vector2d(x / len, y / len);
    170.     }
    171. };
    172. 接下来是主要的函数实现:
    173. Cpp
    174. double CalculateCollisionDistanceAndDirection(
    175.     const std::vector<AcGePoint2d>& polyA,
    176.     const std::vector<AcGePoint2d>& polyB,
    177.     Vector2d& collisionDirection)
    178. {
    179.     double minOverlap = std::numeric_limits<double>::max();
    180.     Vector2d bestAxis;
    181.     // 遍历polyA的边,计算其法线并作为分离轴
    182.     for (size_t i = 0; i < polyA.size(); ++i) {
    183.         size_t nextIndex = (i + 1) % polyA.size();
    184.         Vector2d edge = Vector2d(polyA[nextIndex].x - polyA[i].x, polyA[nextIndex].y - polyA[i].y);
    185.         Vector2d axis = edge.normalized().rotated90CCW();
    186.         double minA = std::numeric_limits<double>::max();
    187.         double maxA = std::numeric_limits<double>::lowest();
    188.         for (const auto& p : polyA) {
    189.             double projection = Vector2d(p.x, p.y).dot(axis);
    190.             minA = std::min(minA, projection);
    191.             maxA = std::max(maxA, projection);
    192.         }
    193.         double minB = std::numeric_limits<double>::max();
    194.         double maxB = std::numeric_limits<double>::lowest();
    195.         for (const auto& p : polyB) {
    196.             double projection = Vector2d(p.x, p.y).dot(axis);
    197.             minB = std::min(minB, projection);
    198.             maxB = std::max(maxB, projection);
    199.         }
    200.         if (maxA < minB || maxB < minA) {
    201.             return std::numeric_limits<double>::max(); // 不相交
    202.         }
    203.         double overlap = std::max(0.0, std::min(maxA, maxB) - std::max(minA, minB));
    204.         if (overlap < minOverlap) {
    205.             minOverlap = overlap;
    206.             bestAxis = axis;
    207.         }
    208.     }
    209.     // 遍历polyB的边,计算其法线并作为分离轴
    210.     for (size_t i = 0; i < polyB.size(); ++i) {
    211.         size_t nextIndex = (i + 1) % polyB.size();
    212.         Vector2d edge = Vector2d(polyB[nextIndex].x - polyB[i].x, polyB[nextIndex].y - polyB[i].y);
    213.         Vector2d axis = edge.normalized().rotated90CCW();
    214.         double minA = std::numeric_limits<double>::max();
    215.         double maxA = std::numeric_limits<double>::lowest();
    216.         for (const auto& p : polyA) {
    217.             double projection = Vector2d(p.x, p.y).dot(axis);
    218.             minA = std::min(minA, projection);
    219.             maxA = std::max(maxA, projection);
    220.         }
    221.         double minB = std::numeric_limits<double>::max();
    222.         double maxB = std::numeric_limits<double>::lowest();
    223.         for (const auto& p : polyB) {
    224.             double projection = Vector2d(p.x, p.y).dot(axis);
    225.             minB = std::min(minB, projection);
    226.             maxB = std::max(maxB, projection);
    227.         }
    228.         if (maxA < minB || maxB < minA) {
    229.             return std::numeric_limits<double>::max(); // 不相交
    230.         }
    231.         double overlap = std::max(0.0, std::min(maxA, maxB) - std::max(minA, minB));
    232.         if (overlap < minOverlap) {
    233.             minOverlap = overlap;
    234.             bestAxis = axis;
    235.         }
    236.     }
    237.     collisionDirection = bestAxis;
    238.     return minOverlap;
    239. }
    240. 注意,上面的代码中,axis.rotated90CCW()函数用于计算一个向量逆时针旋转90度后的向量,这可以通过简单的坐标变换实现,例如:
    241. Cpp
    242. Vector2d Vector2d::rotated90CCW() const {
    243.     return Vector2d(-y, x);
    244. }
    245. 这个函数实现了计算两个多边形之间的最小碰撞距离和碰撞方向的核心逻辑,其中collisionDirection参数用于输出碰撞方向。如果两个多边形不相交,则函数会返回一个非常大的数(通过std::numeric_limits<double>::max()表示),表示没有碰撞发生。
    复制代码

     

     

     

     

    NFP碰撞检测算法实现
    中国膜结构网打造全中国最好的膜结构综合平台 ,统一协调膜结构设计,膜结构施工,膜材采购,膜材定制,膜结构预算全方位服务。 中国空间膜结构协会合作单位。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|手机版|中国膜结构网|中国膜结构协会|进口膜材|国产膜材|ETFE|PVDF|PTFE|设计|施工|安装|车棚|看台|污水池| |网站地图

    GMT+8, 2024-9-8 09:29 , Processed in 0.064631 second(s), 25 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

    快速回复 返回顶部 返回列表