比较两个2D矩阵的唯一点

时间:2011-11-01 13:48:05

标签: c++ math matrix

我正在开发一款游戏,我将让玩家遍历2D矩阵。我希望能够根据他的位置修改播放器周围的环境,作为渲染过程的一部分。

基本上,玩家将在x,y点,并且在任何时候都会在他周围有2个点。玩家可以向任何方向移动x,y点,我需要知道旧的不再需要指针是什么,以及玩家接近的新点。

我已经制定了一个我的意思的快速图表:

Diagram of Matrix's

我需要一个旧点(红色)列表,以及我可以迭代执行操作的新点(绿色)。

我将用C ++编写这个方法,所以我真的在寻找实现这一目标所需的sudo逻辑步骤。我通过自己的方式大约有50%的方式,但我认为它完全没有效率,我也相信这是一种简单的数学方法。

4 个答案:

答案 0 :(得分:3)

可能最有效的方法是计算一个矩形 - 矩形减法,看起来很困难并且有很多情况,但实际上并不是那么难:

struct Rect{
    int x0, y0, x1, y1;
    Rect(int x0, int y0, int x1, int y1)
        : x0(x0), y0(y0), x1(x1), y1(y1)
    {}
};

std::vector<Rect> subtract(const Rect& a, const Rect& b) {
    std::vector<Rect> result;
    if (a.y1 <= b.y0 || a.y0 >= b.y1 || a.x1 <= b.x0 || a.x0 >= b.x1) {
        // Trivial case: rectangles are not overlapping
        result.push_back(a);
    } else {
        int ystart = a.y0, yend = a.y1;
        if (ystart < b.y0) { // Something visible above
            result.push_back(Rect(a.x0, ystart, a.x1, b.y0));
            ystart = b.y0;
        }
        if (yend > b.y1) { // Something visible below
            result.push_back(Rect(a.x0, b.y1, a.x1, yend));
            yend = b.y1;
        }
        if (a.x0 < b.x0) { // Something visible on the left
            result.push_back(Rect(a.x0, ystart, b.x0, yend));
        }
        if (a.x1 > b.x1) { // Something visible on the right
            result.push_back(Rect(b.x1, ystart, a.x1, yend));
        }
    }
    return result;
}

给定两个矩形AB的上述函数返回一个矩形向量,结果为A-B。此向量可能为空(B覆盖A)或者可能有一到四个矩形(四个是B严格包含A时,结果将是一个矩形孔的矩形)。

使用此功能,您可以轻松计算new-oldold-new区域。

请注意,上述代码中使用的坐标架构采用了基于点的坐标系(不是基于像素的坐标系):

enter image description here

在上图中注意,矩形的水平X坐标从0到W(不是W-1),垂直Y坐标从0到H(而不是H-1)。

像素只是区域1的矩形,坐标为(x, y)-(x+1, y+1);该像素的中心是(x+0.5, y+0.5)x0==x1y0==y1的矩形为空。

另请注意,代码假定(并返回)非空的定向矩形,即x0<x1 && y0<y1

这种将像素坐标概念与点坐标概念分开的方法简化了许多像素数学运算:例如矩形区域为width*height而不是(width-1)*(height-1)

使用输入案例测试的一个小程序如下

void print_result(const char *name,
                  const std::vector<Rect>& rects)
{
    printf("Result '%s' (%i rects):\n", name, int(rects.size()));
    for (int i=0,n=rects.size(); i<n; i++)
    {
        printf("  %i) (%i, %i) - (%i, %i)\n",
               i+1,
               rects[i].x0, rects[i].y0,
               rects[i].x1, rects[i].y1);
    }
}

int main()
{
    Rect A(1, 1, 6, 6);
    Rect B(3, 2, 8, 7);
    print_result("A-B", subtract(A, B));
    print_result("B-A", subtract(B, A));
    return 0;
}

并且该程序的输出是

Result 'A-B' (2 rects):
  1) (1, 1) - (6, 2)
  2) (1, 2) - (3, 6)
Result 'B-A' (2 rects):
  1) (3, 6) - (8, 7)
  2) (6, 2) - (8, 6)

答案 1 :(得分:1)

我已经重新编写了我的原始答案,因此它涵盖了所有情况,并希望更有意义:(这不是C ++,但是写出来比写出sudo逻辑更容易。希望它现在会更好地解释自己。)

矩形(在这种情况下为方形)可以用它的左下角和右上角坐标表示。对于此示例,$ Rectangle [0]是左下角坐标,$ Rectangle [0] ['x']是左下角坐标的x值,$ Rectangle [0] ['y']是y值左下角坐标。 $ Rectangle [1]是右上角的坐标。

这可以修改为只处理正方形以保存一个顶点坐标和边长的东西。

有四种运动可能性:
1:没有发生移动,方块在同一个地方。在这种情况下,绿色矩形的所有四个角点都将位于蓝色矩形内 2:发生足够的运动导致两个方格之间没有重叠。在这种情况下,绿色矩形的四个角点都不在蓝色矩形内 3:运动仅在一个方向(x或y)发生,并且不足以在整个圈内移除。在这种情况下,绿色矩形的四个角点中的2个将位于蓝色矩形内 4:在x和y方向上都发生了移动,并且距离不足以完全消除重叠。在这种情况下,绿色矩形的四个角点之一将位于蓝色矩形内。

每个案件的处理方式略有不同:
1 - 您不需要遍历任何方格 2 - 您需要迭代蓝色矩形中的所有正方形和绿色矩形中的所有正方形 3 - 您需要迭代由以下定义的方块:

function getOnlyOverlapRectangle($FirstRectangle, $SecondRectangle)
    {
        $PointOne['x'] = $FirstRectangle[0]['x'];
        $PointOne['y'] = $FirstRectangle[0]['y'];
        $PointTwo['x'] = $FirstRectangle[0]['x'];
        $PointTwo['y'] = $FirstRectangle[1]['y'];
        $PointThree['x'] = $FirstRectangle[1]['x'];
        $PointThree['y'] = $FirstRectangle[1]['y'];
        $PointFour['x'] = $FirstRectangle[1]['x'];
        $PointFour['y'] = $FirstRectangle[]['y'];

        //left edge
        if(checkVertexInside($PointOne,$SecondRectangle) && checkVertexInside($PointTwo,$SecondRectangle))
        {
            $overlapRectangle[0]['x'] = $SecondRectangle[1]['x'];
        }
        else
        {
            $overlapRectangle[0]['x'] = $FirstRectangle[0]['x'];
        }

        //bottom edge
        if(checkVertexInside($PointOne,$SecondRectangle) && checkVertexInside($PointFour,$SecondRectangle))
        {
            $overlapRectangle[0]['y'] = $SecondRectangle[1]['y'];
        }
        else
        {
            $overlapRectangle[0]['y'] = $FirstRectangle[0]['y'];
        }

        //right edge
        if(checkVertexInside($PointThree,$SecondRectangle) && checkVertexInside($PointFour,$SecondRectangle))
        {
            $overlapRectangle[1]['x'] = $SecondRectangle[0]['x'];
        }
        else
        {
            $overlapRectangle[1]['x'] = $FirstRectangle[1]['x'];
        }

        //top edge
        if(checkVertexInside($PointTwo,$SecondRectangle) && checkVertexInside($PointThree,$SecondRectangle))
        {
            $overlapRectangle[1]['y'] = $SecondRectangle[0]['y'];
        }
        else
        {
            $overlapRectangle[1]['y'] = $FirstRectangle[1]['y'];
        }


        return $overlapRectangle;
    }

4 - 您需要迭代由以下定义的方块:

//Gets subset of $FirstRectangle that is outside of $SecondRectangle
function getFirstOverlapRectangle($FirstRectangle, $SecondRectangle)
    {
        //left edge
        $Point['x'] = $FirstRectangle[0]['x'];
        $Point['y'] = $FirstRectangle[1]['y'];
        if(checkVertexInside($Point,$SecondRectangle))
        {
            $overlapRectangle[0]['x'] = $SecondRectangle[1]['x'];
        }
        else
        {
            $overlapRectangle[0]['x'] = $FirstRectangle[0]['x'];
        }

        //bottom edge
        if($FirstRectangle[0]['y'] < $SecondRectangle[0]['y'] < $FirstRectangle[1]['y'])
        {
            $overlapRectangle[0]['y'] = $SecondRectangle[0]['y'];
        }
        else
        {
            $overlapRectangle[0]['y'] = $SecondRectangle[1]['y'];
        }

        //right edge
        $overlapRectangle[1]['x'] = min($FirstRectangle[1]['x'],$SecondRectangle[0]['x']);

        //top edge
        $overlapRectangle[1]['y'] = $FirstRectangle[1]['y'];

        return $overlapRectangle;
    }

    //Gets second subset of $FirstRectangle that is outside of $SecondRectangle
    function getSecondOverlapRectangle($FirstRectangle, $SecondRectangle)
    {
        //top edge
        if($FirstRectangle[0]['y'] < $SecondRectangle[0]['y'] < $FirstRectangle[1]['y'])
        {
            $overlapRectangle[1]['y'] = $SecondRectangle[0]['y'];
        }
        else
        {
            $overlapRectangle[1]['y'] = $SecondRectangle[1]['y'];
        }

        //bottom edge
        $overlapRectangle[0]['y'] = $FirstRectangle[0]['y'];

        //left edge
        $Point['x'] = $SecondRectangle[1]['x'];
        $Point['y'] = $SecondRectangle[1]['y'];
        if(checkVertexInside($Point,$FirstRectangle))
        {
            $overlapRectangle[0]['x'] = $SecondRectangle[1]['x'];
        }
        else
        {
            $overlapRectangle[0]['x'] = $FirstRectangle[0]['x'];
        }

        //right edge
        $Point['x'] = $FirstRectangle[0]['x'];
        $Point['y'] = $FirstRectangle[1]['y'];
        if(checkVertexInside($Point,$SecondRectangle))
        {
            $overlapRectangle[1]['x'] = $FirstRectangle[0]['x'];
        }
        else
        {
            $overlapRectangle[1]['x'] = $SecondRectangle[1]['x'];
        }


        return $overlapRectangle;
    }

所以可能的解决方案就是这样:

计算绿色矩形的多少个角落在蓝色矩形内。 打开该号码:
 0:遍历所有绿色矩形点和所有蓝色矩形点
 1:迭代getFirstOverlapRectangle和getSecondOverlapRectangle的结果  2:迭代getOnlyOverlapRectangle的结果
 3:这不应该发生......
 4:不要遍历任何方格。

迭代必须像这样工作:

for($CurrentXCoord = $OldSquaresOne[0]['x'] + 0.5; $CurrentXCoord < $OldSquaresOne[1]['x'];  $CurrentXCoord ++)
    {
        for($CurrentYCoord = $OldSquaresOne[0]['y'] + 0.5; $CurrentYCoord < $OldSquaresOne[1]['y'];  $CurrentYCoord ++)
        {
            //do stuff.
        }
    }

+0.5确保您不会超过边界点两次,因此您可以通过中心坐标而不是角坐标来引用蓝色/绿色方块内的方块。

checkVertexInside函数将是:

function checkVertexInside($Point, $Rectangle)
    {
        if
        (
            $Point['x'] <= $Rectangle[1]['x'] && $Point['x'] >= $Rectangle[0]['x']
            && $Point['y'] <= $Rectangle[1]['y'] && $Point['y'] >= $Rectangle[0]['y']
        )
        {
            return true;
        }

        return false;
    }

    function getRectanglePoint($topFlag,$rightFlag,$Rectangle)
    {
        $Point['x'] = $Rectangle[$rightFlag]['x'];
        $Point['y'] = $Rectangle[$topFlag]['y'];
        return $Point;
    }

答案 2 :(得分:1)

这是输出红色和绿色非重叠点的一般方法。调用函数find_points来完成工作。这假设你有一个Points类来保存一组带有add(x,y)方法的点,用于向它添加一个点。

struct Box {
  int x1; // min x value
  int x2; // max x value + 1
  int y1; // min y value
  int y2; // max y value + 1
};

Box intersection(const Box &box1,const Box &box2)
{
  int wx1 = max(box1.x1,box2.x1);
  int wx2 = min(box1.x2,box2.x2);
  int wy1 = max(box1.y1,box2.y1);
  int wy2 = min(box1.y2,box2.y2);
  Box result = {wx1,wx2,wy1,wy2};
  return result;
}

void output_box(Points &p,int x1,int x2,int y1,int y2)
{
  for (int y=y1; y!=y2; ++y) {
    for (int x=x1; x!=x2; ++x) {
      p.add(x,y);
    }
  }
}

void output_difference(Points &points,const Box &box1,const Box &box2)
{
  output_box(points,box1.x1,box1.x2,box1.y1,box2.y1);
  output_box(points,box1.x1,box2.x1,box2.y1,box2.y2);
  output_box(points,box2.x2,box1.x2,box2.y1,box2.y2);
  output_box(points,box1.x1,box1.x2,box2.y2,box1.y2);
}

void find_points(Points &red,Points &green,const Box &red_box,const Box &green_box)
{
  Box white_box = intersection(red_box,green_box);
  output_difference(red,red_box,white_box);
  output_difference(green,green_box,white_box);
}

编辑:计算wy2时出现了一个错误 - 现在已经修复了。

答案 3 :(得分:0)

  1. 列出旧点(红色)。
  2. 列出新点(绿色)。
  3. 迭代一个列表。对于每个点,在另一个列表中查找它,如果它在那里,则从两个列表中删除它。

修改:

让我重新说一下:

  1. 列出旧社区中的点(蓝色边框内)。
  2. 列出新邻居中的点(绿色边框内)。
  3. 迭代一个列表。对于每个点,在另一个列表中查找它,如果它在那里,则从两个列表中删除它。

您将看到一个不再可见的点(红色)和新可见点(绿色)的列表。