(伪)EPA(Expanding Polytope Algorithm) 求重叠的凸图形的嵌入方向与深度

参考: http://www.dyn4j.org/2010/05/epa-expanding-polytope-algorithm/

前篇, 用GJK可以判断两个凸图形是否重叠, 而EPA可以求重叠其嵌入的深度的与方向.

两个凸图形的闵可夫斯基差如果包含原点, 那么两个凸图形重叠. 而闵可夫斯基差上的一条边到原点的距离就是两个图形的最小嵌入深度, 这条边的垂线方向就是两个图形的最小嵌入方向.

用GJK得到包含原点的单纯形后, EPA对这个单纯形作扩展, 使得扩展后的单纯形包含闵可夫斯基差上距原点距离最小的边.

不过要求嵌入深度和方向只需要求得那一条边就足够了, 并不需要保留整个扩展单纯形. 所以我改一下变成这样:

如图, 假设椭圆形是闵可夫斯基差的形状, ABC是GJK结束时所得到的单纯形, 点O是原点.

首先求得AC和BC上距离原点最近的点da和db, 用da和db的模长判断AC和BC到原点的距离.

如果AC离原点比BC更近, 那么抛弃点B, 用点A和C当作新的单纯形顶点A‘和B‘, 用向量da的方向当作新的搜索方向.

否则, 抛弃点A(如下图所示), 用点B和点C当作新的单纯形顶点A‘和B‘, 用向量db当作新的搜索方向.

之后, 用和GJK里相同的getSupportPoint函数找到新的C‘.

现在, 把点A(或点B也可)以及点C投影到搜索方向上, 求其差值det(如图中蓝色虚线所示).

如果det足够小, 那么说明已经找到一条闵可夫斯基差上尽可能接近原点的边了, 此时返回AB上距原点最近的点向量vec, vec的方向就是嵌入方向, vec的模长就是最小嵌入距离.

如果det太大, 就重复以上过程.

proc getPenetrationVector*(s1, s2: Sprite2D, sA, sB, sC: Vector2D, tolerance: float32 = 0.2): Vector2D =
    var
        a = sA
        b = sB
        c = sC
        dir: Vector2D
    while true:
        let
            da = getClosestPointToOrigin(a, c)
            db = getClosestPointToOrigin(b, c)
        if da.sqLen > db.sqLen:
            a = b
            b = c
            dir = db.norm()
        else:
            b = c
            dir = da.norm()
        c = getSupportPoint(s1, s2, dir)
        if (c - a) * dir <= tolerance:
            return getClosestPointToOrigin(a, b)

求线段上到原点距离最近的点的方法是把原点投影到该线段上.

proc getClosestPointToOrigin(a, b: Vector2D): Vector2D =
    let
        ab = norm(b - a)
        ao = a.negate()
    return a + ab * (ab * ao)

 

最后效果如图, 白色的线表示把多边形推离椭圆形需要的长度最小的向量

时间: 2024-10-31 17:06:40

(伪)EPA(Expanding Polytope Algorithm) 求重叠的凸图形的嵌入方向与深度的相关文章

hdu 1255 覆盖的面积 线段树扫描线求重叠面积

稀里糊涂打完了没想到1A了,心情还是很舒畅的,c和c++的四舍五入还是四舍六入遇积进位遇偶舍,我感觉很混乱啊,这道题我输出的答案是7.62,也就是遇偶舍了,可是我就手动处理一下进位问题,发现0.005 系统自动进位0.01了,尼玛啊,我一下子就混乱了,不是遇偶舍么,0也是偶数啊,怎么就进位了呢.最后我就不手动处理进位问题了,直接0.2lf%,虽然我输出的结果是7.62,但是提交也过了 这道题和poj1151,hdu1542差不多,扫描线详细讲解http://blog.csdn.net/young

求重叠区间个数,某书某题错例分析

大意就是我淘钱买了一本题集,觉得书中有些地方作者太随意,例子错得不严谨,一度阻碍阅读.作为消费者不得不拿出来说一说.本文本着不迷信,实事求是精神.本文编排如下:1.引用书中原例2.主观分析例子有错3.代码运行验证其错4.修正例子代码5.另一个求值代码现在开始. 下面是书中原例引用: 求重叠区间个数 给定多个可能重叠的区间,找出重叠区间的个数.区间定义如下: class Interval { int start; // 起点 int end; // 止点 Interval(int a, intb)

EPA, 求嵌入深度和嵌入方向 (fix)

参考: http://www.dyn4j.org/2010/05/epa-expanding-polytope-algorithm/ 一个在线演示: http://sandbox.runjs.cn/show/xseojpfa GJK可以判断两个凸图形是否重叠, EPA可以基于GJK的工作找出分离两个图形的最小向量. 如果原点在闵可夫斯基差内部, 那么两个图形重叠. 而闵可夫斯基差边界上离原点最近的点的向量就是这个最小分离向量. EPA把GJK结束时的simplex进行扩展, 从闵可夫斯基差的边界

GJK(Gilbert–Johnson–Keerthi) 判断任意凸图形是否重叠

参考:http://www.dyn4j.org/2010/04/gjk-gilbert-johnson-keerthi/ 和SAT(分离轴)法一样, GJK可以判断两个凸图形是否重叠. 比起SAT, GJK优在用同一套办法可以处理所有的图形, 而SAT判断某两种不同图形(多边形-多边形/多边形-圆形/圆形-圆形等)都要区别处理. GJK原理: 如果两个凸图形的闵可夫斯基差包含原点, 那么这两个图形重叠. 所以问题转变成判断一个闵可夫斯基差图形是否包含原点. 闵可夫斯基和就是把两个图形里的点都当作

[algorithm]求最长公共子序列问题

最直白方法:时间复杂度是O(n3), 空间复杂度是常数 reference:http://blog.csdn.net/monkeyandy/article/details/7957263 /** ** [email protected] ** http://blog.csdn.net/MonkeyAndy **/ 首先介绍动态规划方法的相关知识 动态规划方法的基本思想: 分成若干个子问题,先求解子问题,然后根据子问题的解求得原问题的解.经分解得到的子问题往往不是互相独立的.可重复利用! 其核心思

Algorithm --&gt; 求N以内的真分数个数

求N以内的真分数个数 For example, if N = 5, the number of possible irreducible fractions are 11 as below. 0 1/5 1/4 1/3 2/5 1/2 3/5 2/3 3/4 4/5 1 Input 1    --> case 个数5 Output 11 代码: #include <iostream> #include <cstdio> using namespace std; #define

Algorithm --&gt; 求出A到B的最小步数

求出A到B的最小步数 给定象棋盘,以及位置A和B, 求出从A到B的最小步数 Input 2             -->case 个数9 9          -->棋盘大小3 5 2 8    --> 起始位置20 202 3 7 9 Output2 5 代码: #include <cstdio> #include <iostream> #include <string.h> using namespace std; #define MAX 100

Oulipo POJ - 3461(kmp,求重叠匹配个数)

Problem Description The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter'e'. He was a member of the Oulipo group. A quote from the book: Tout avait Pair normal, mais tout s’affirmait faux. Tout avait Fair

裁剪出碰撞点(manifold) (for GJK的通用改)

用EPA得到图形的嵌入方向之后, 就可以裁出碰撞点(边)了. 首先需要找到图形在嵌入方向上最远的一条边. 多边形: method getFarthestEdgeInDirection*(self: Polygon, direction: Vector2D): Line2D = var bestIndex: int = 0 bestProjection: float32 = self[0] * direction projection: float32 #先找出在direction上最远的一个顶点