利用旋转卡尺在XNA中寻找凸壳的定向边界框

时间:2012-06-15 14:26:10

标签: xna rotation computational-geometry bounding-box convex

也许这更像是一个数学问题,而不是编程问题,但我一直在尝试在XNA中实现旋转卡尺算法。

我已经使用维基百科上详述的单调链从我的点集推导出一个凸包。

现在,我正在尝试对我的算法进行建模,以便在找到OBB之后找到OBB: http://www.cs.purdue.edu/research/technical_reports/1983/TR%2083-463.pdf

但是,我不明白它在最后一页上提到的DOTPR和CROSSPR方法应该返回。

我理解如何获得两点的Dot Product和两点的Cross Product,但似乎这些函数应该返回两个边/线段的Dot和Cross Products。我的数学知识无疑是有限的,但这是我对算法寻找的最佳猜测

    public static float PolygonCross(List<Vector2> polygon, int indexA, int indexB)
    {
        var segmentA1 = NextVertice(indexA, polygon) - polygon[indexA];
        var segmentB1 = NextVertice(indexB, polygon) - polygon[indexB];

        float crossProduct1 = CrossProduct(segmentA1, segmentB1);
        return crossProduct1;
    }

    public static float CrossProduct(Vector2 v1, Vector2 v2)
    {
        return (v1.X * v2.Y - v1.Y * v2.X);
    }

    public static float PolygonDot(List<Vector2> polygon, int indexA, int indexB)
    {
        var segmentA1 = NextVertice(indexA, polygon) - polygon[indexA];
        var segmentB1 = NextVertice(indexB, polygon) - polygon[indexB];

        float dotProduct = Vector2.Dot(segmentA1, segmentB1);
        return dotProduct;
    }

然而,当我在我的代码的这一部分中使用这些方法时......

            while (PolygonDot(polygon, i, j) > 0)
            {
                j = NextIndex(j, polygon);
            }

            if (i == 0)
            {
                k = j;
            }
            while (PolygonCross(polygon, i, k) > 0)
            {
                k = NextIndex(k, polygon);
            }

            if (i == 0)
            {
                m = k;
            }
            while (PolygonDot(polygon, i, m) < 0)
            {
                m = NextIndex(m, polygon);
            }

..当我给它一组测试点时,它返回j,k的相同索引:

    List<Vector2> polygon = new List<Vector2>() 
        { 
            new Vector2(0, 138),
            new Vector2(1, 138), 
            new Vector2(150, 110), 
            new Vector2(199, 68), 
            new Vector2(204, 63), 
            new Vector2(131, 0), 
            new Vector2(129, 0), 
            new Vector2(115, 14), 
            new Vector2(0, 138), 
        };

注意,我调用polygon.Reverse将这些点按照逆时针顺序放置,如perdue.edu的技术文档中所示。我的用于寻找点集的凸包的算法以逆时针顺序生成点列表,但是假设y <0。 0高于y> 0因为当绘制到屏幕时0,0是左上角。反转列表似乎就足够了。我也删除了最后的重复点。

在此过程之后,数据变为:

  • Vector2(115,14)
  • Vector2(129,0)
  • Vector2(131,0)
  • Vector2(204,63)
  • Vector2(199,68)
  • Vector2(150,110)
  • Vector2(1,138)
  • Vector2(0,138)

当i等于0且j等于3时,此测试在第一个循环上失败。它发现行(115,14)到(204,63)和行(204,63)到(的交叉积) 199,68)为0.然后发现相同行的点积也为0,因此j和k共享相同的索引。

相反,当给出这个测试集时: http://www.wolframalpha.com/input/?i=polygon+%282%2C1%29%2C%281%2C2%29%2C%281%2C3%29%2C%282%2C4%29%2C%284%2C4%29%2C%285%2C3%29%2C%283%2C1%29

我的代码成功返回此OBB: http://www.wolframalpha.com/input/?i=polygon+%282.5%2C0.5%29%2C%280.5%2C2.5%29%2C%283%2C5%29%2C%285%2C3%29

我已经阅读了http://www.geometrictools.com/LibMathematics/Containment/Wm5ContMinBox2.cpp上找到的C ++算法,但我太过密集而无法完全遵循它。它似乎与上文中详述的另一个非常不同。

有谁知道我跳过的步骤或在我的代码中发现了一些错误,以找到两个线段的点积和交叉积?有没有人在C#之前成功实现过这段代码并有一个例子?

2 个答案:

答案 0 :(得分:1)

作为数据结构的点和向量基本上是相同的;两者都包括两个浮点数(如果你在三个维度上工作,则为三个浮点数)。所以,当被要求取边缘的点积时,我想这意味着取边缘定义的矢量的点积。您提供的代码正是如此。

CrossProduct的实施似乎是正确的(请参阅Wolfram MathWorld)。但是,在PolygonCrossPolygonDot中,我认为您不应该规范化细分。它会影响PolygonDotPolygonCross的返回值的大小。通过删除对Vector2.Normalize的多余调用,您可以加快代码速度并减少浮点值中的噪音量。但是,规范化与您粘贴的代码的正确性无关,因为它只将结果与零进行比较。

请注意,您引用的纸张假定多边形顶点按逆时针顺序列出(第5页,“注释开始”之后的第一段),但您的示例polygon按顺时针顺序定义。这就是为什么PolygonCross(polygon, 0, 1)为负数,并为jk获得相同的值。

答案 1 :(得分:0)

我认为DOTPR是一个正常的矢量点积,crosspr是一个交叉积。 dotproduct将返回一个正常数字,crossproduct将返回一个垂直于给定的两个向量的向量。 (基本矢量数学,检查维基百科)

它们实际上是在论文中定义的,因为DOTPR(i,j)返回从顶点i到i + 1和j到j + 1的向量的点积。同样适用于CROSSPR但具有交叉产品。