【问题标题】:Boolean operations on 2d rectangles二维矩形的布尔运算
【发布时间】:2014-09-26 18:34:37
【问题描述】:

我已经编写了自己的矩形类,它包含一个从另一个矩形中减去一个矩形的方法。该算法简单地确定源矩形与目标矩形重叠的边缘,然后遍历所有可能的情况,包括完全在内部、仅在边缘上、完全封闭等等。事实上,有很多案例我正在查看代码,想知道是否有可用的矩形布尔运算的算法或示例。

我知道有 generalised clipping algorithms for 2d polytopes,但我一直在寻找特定于 2d 矩形的东西,以及适当的伴随优化和简化。

谁能指出我正确的方向,或者 Weiler-Atherton 是这个矩形只是一个案例的一般问题的最后一句话吗?

【问题讨论】:

  • 你想要什么样的输出?一组矩形?多边形?我建议从返回共同矩形区域的“重叠”操作开始,减去的区域可以在相对于减去的矩形的 9 个位置中的任何一个,但相同的代码可以处理 4 个角位置,其他代码4个边缘位置,所以你只有3个有意义的不同情况......(我写了代码来做这个 - 处理每个具有设置/重置/切换行为的矩形堆栈,找到最简单的非重叠等 - 今年早些时候,但封闭源代码 - 不是我的选择)。
  • 抱歉忘了补充:我所做的算法返回一组矩形而不是多边形,即它只处理矩形。它实际上是一个窗口系统(窗口总是矩形)。

标签: c++ algorithm graphics boolean


【解决方案1】:

如果将两个方向分开,则只有几个基本情况,然后可以将它们组合在一个嵌套循环中。

基本情况如下:

               |              |
       XXXXX   |..............|              1 section
               |              |
            XXXXXXX...........|              2 sections
               |              |
               |...XXXXXXX....|              3 sections
               |              |
               |..........XXXXXXXX           2 secions
               |              |
               |..............|   XXXX       1 section
               |              |
             XXXXXXXXXXXXXXXXXXXX            nothing
               |              |

竖线是原始矩形的边缘,X 是要减去的矩形,点标记部分。垂直条之间的X 也是保留的部分,除非与另一个方向的X 部分结合使用。 (如果这听起来太复杂:留下的洞由X 部分在两个方向上指定。

我们可以通过将矩形属性 left、top、right 和 bottom 重新设计为最小/最大值数组来分离方向:

typedef struct Rect Rect;

struct Rect {
    int min[2];
    int max[2];
};

(恐怕代码是C,不是C++。)

然后我们可以找到每个方向的部分:

int rect_sub_dir(int sec[], int *skip, Rect a, Rect b, int dir)
{
    int n = 0;

    sec[n++] = a.min[dir];
    if (b.min[dir] > a.min[dir] && b.min[dir] < a.max[dir]) {
        sec[n++] = b.min[dir];
    }
    *skip = n - 1;
    if (b.max[dir] < a.max[dir] && b.max[dir] > a.min[dir]) {
        sec[n++] = b.max[dir];
    }
    sec[n] = a.max[dir];

    // Backpatch if rectangles don't overlap        
    if (b.max[dir] < a.min[dir]) *skip = -1;
    if (b.min[dir] > a.max[dir]) *skip = -1;

    return n;
}

这会在n 部分之间创建一个n + 1 边界数组。 skip 值表示在竖线之间标记为 X 的部分。

然后您可以将两个方向的部分组合起来:

int rect_sub(Rect res[], Rect a, Rect b)
{
    int hor[4];
    int ver[4];

    int hskip, nhor;
    int vskip, nver;
    int h, v;
    int n = 0;

    nhor = rect_sub_dir(hor, &hskip, a, b, 0);
    nver = rect_sub_dir(ver, &vskip, a, b, 1);

    printf("%d, %d\n", hskip, vskip);

    for (h = 0; h < nhor; h++) {
        for (v = 0; v < nver; v++) {
            if (h == hskip && v == vskip) continue;

            res[n++] = rect(hor[h], ver[v], hor[h + 1], ver[v + 1]);
        }
    }

    return n;
}

这个解决方案不是最优的。当第二个矩形包含在第一个矩形中时,它将创建八个矩形,这可能不是您想要的。之后您总是可以尝试合并相邻的矩形。或者您可以重写代码以更智能地分割矩形。

我已经用一些案例测试了代码,但是因为有很多可能的安排,所以代码没有完全测试。

【讨论】:

  • 看起来不错。会试一试。 +1 努力,虽然我还没有研究过它是否会起作用:p。如果我成功了,会回复你。
猜你喜欢
  • 1970-01-01
  • 2012-01-09
  • 2014-08-05
  • 2013-01-08
  • 1970-01-01
  • 2012-07-04
  • 2014-08-13
  • 2012-11-20
  • 2020-08-22
相关资源
最近更新 更多