C 矩形选择算法

C 矩形选择算法,c,algorithm,geometry,selection,rectangles,C,Algorithm,Geometry,Selection,Rectangles,矩形位于双链接列表中。在此上下文中,矩形是一个图形,它使用矩形结构的数据形成,无论如何 struct rect { int l; /* left */ int t; /* top */ int r; /* right */ int b; /* bottom */ }; 它的字段具有我保证的任何矩形的严格位置 [l; t] [r; t] +------+ | | | |

矩形位于双链接列表中。在此上下文中,矩形是一个图形,它使用矩形结构的数据形成,无论如何

struct rect {
    int l; /* left */
    int t; /* top */
    int r; /* right */
    int b; /* bottom */
};
它的字段具有我保证的任何矩形的严格位置

[l; t]          [r; t]
       +------+ 
       |      |
       |      |
       +------+ 
[l; b]          [r; b]
矩形存储在对象中

struct object {
    struct rect *rect;
    bool selected;
};
和对象存储在列表的节点中

struct node {
    struct object *obj;
    struct node *next;
    struct node *prev;
}; 
矩形始终从列表的开头开始绘制-首先绘制一个靠近列表开头的矩形。因此,每个下一个图与先前的图重叠

我选择矩形,带有一个由鼠标光标形成的选择矩形。在从末尾遍历列表的过程中,完成选择检查

struct node *node = getend(list);
struct object *obj;

do {
    obj = node->obj;

    /* Check, if a selection rectangle somehow 
     * intersects with a rectangle of the current 
     * object. */
    if (rcheck(sel_rect, obj->rect))
        obj->selected = true;
}
while ((node = node->prev));
请看下面一点我的问题演示

如您所见,选择矩形选择两个矩形:黄色和绿色。在这种情况下,只应选择黄色数字;一般情况下-如果在前向矩形后面有另一个图形位于后向矩形,并且选择矩形没有覆盖动画中该图形的可见部分,它是由黄色图形重叠形成的绿色多边形,但覆盖其隐藏部分,则不应选择后向矩形

前向矩形意味着它位于列表末尾附近;一个向后的矩形-它位于更靠近其起点的位置


此算法用于游戏的二维地图编辑器,用于矩形纹理选择。

五种不同的方法:

屏幕空间选择

将矩形从后向前顺序渲染到临时像素阵列/网格上,但使用矩形ID而不是颜色。使用选择矩形选择像素,并检查它们以确定选定矩形的矩形ID。这可能是最简单的选择,也可能有最差的性能

选择矩形细分

像你一样从前到后搜索;但是,当您找到与选择矩形重叠的矩形时,请使用其边将选择矩形拆分为子矩形,并丢弃与找到的矩形重叠的子矩形。这将为您留下0到8个子矩形,这些子矩形不会与找到的矩形重叠。使用每个生成的子矩形(而不是原始选择矩形)重复此过程,以查找更多选定的矩形。请注意,在最坏的情况下,对于每个选定的矩形,子矩形的数量可以增加7个

矩形列表细分

使用前矩形的边对矩形列表进行预处理,以细分任何后多边形,并丢弃任何重叠的子矩形;这样,生成的矩形列表不再包含任何重叠的内容。此后,;查找与选择矩形重叠的所有子矩形。请注意,如果矩形列表不经常更改,并且相同的预处理矩形列表可以多次重复使用,则效果更好

选择多边形减法

假设选择区域是一个具有N个顶点和N条边的任意形状,在开始时恰好是4个顶点和4条边。像你一样从前到后搜索;但当您找到与选择多边形重叠的矩形时,将从选择多边形中减去重叠区域。这是难以置信的复杂;部分原因是选择多边形可以分割为分离/非接触的部分和/或可以变成类似甜甜圈的部分。这可以与前面的方法结合使用-例如,使用重叠矩形的边将选择多边形分割为子多边形,然后丢弃重叠的子多边形,然后合并共享公共边的子多边形以减少子多边形的数量

矩形列表减法

预处理矩形列表,并使用与“选择多边形减法”类似的方法将它们全部转换为任意形状的多边形。此后,;查找与选择矩形重叠的所有子多边形。请注意,如果矩形列表不经常更改,并且相同的预处理多边形列表可以多次重复使用,这会更好

注释

如果你能接近我所描述的方法,多边形减法比“先分割后合并”更先进,这更复杂,性能也更好。有关一篇研究论文,请参阅


我自己,如果不需要尽可能高的性能,并且如果矩形列表比选择矩形变化更频繁,我可能会使用选择矩形细分方法。

五种不同的方法:

[l; t]          [r; t]
       +------+ 
       |      |
       |      |
       +------+ 
[l; b]          [r; b]
屏幕空间选择

将矩形从后向前顺序渲染到临时像素阵列/网格上,但使用矩形ID而不是颜色。使用选择矩形选择像素,并检查它们以确定选定矩形的矩形ID。这可能是最简单的选择,也可能是t o表现最差

选择矩形细分

像你一样从前到后搜索;但是,当您找到与选择矩形重叠的矩形时,请使用其边将选择矩形拆分为子矩形,并丢弃与找到的矩形重叠的子矩形。这将为您留下0到8个子矩形,这些子矩形不会与找到的矩形重叠。使用每个生成的子矩形(而不是原始选择矩形)重复此过程,以查找更多选定的矩形。请注意,在最坏的情况下,对于每个选定的矩形,子矩形的数量可以增加7个

矩形列表细分

使用前矩形的边对矩形列表进行预处理,以细分任何后多边形,并丢弃任何重叠的子矩形;这样,生成的矩形列表不再包含任何重叠的内容。此后,;查找与选择矩形重叠的所有子矩形。请注意,如果矩形列表不经常更改,并且相同的预处理矩形列表可以多次重复使用,则效果更好

选择多边形减法

假设选择区域是一个具有N个顶点和N条边的任意形状,在开始时恰好是4个顶点和4条边。像你一样从前到后搜索;但当您找到与选择多边形重叠的矩形时,将从选择多边形中减去重叠区域。这是难以置信的复杂;部分原因是选择多边形可以分割为分离/非接触的部分和/或可以变成类似甜甜圈的部分。这可以与前面的方法结合使用-例如,使用重叠矩形的边将选择多边形分割为子多边形,然后丢弃重叠的子多边形,然后合并共享公共边的子多边形以减少子多边形的数量

矩形列表减法

预处理矩形列表,并使用与“选择多边形减法”类似的方法将它们全部转换为任意形状的多边形。此后,;查找与选择矩形重叠的所有子多边形。请注意,如果矩形列表不经常更改,并且相同的预处理多边形列表可以多次重复使用,这会更好

注释

如果你能接近我所描述的方法,多边形减法比“先分割后合并”更先进,这更复杂,性能也更好。有关一篇研究论文,请参阅


就我自己而言,如果没有必要获得尽可能高的性能,并且如果矩形列表的变化比选择矩形更频繁,我可能会使用选择矩形细分方法。

我自己想到了这个解决方案,结果比我预期的要难一点。我花了一段时间

[l; t]          [r; t]
       +------+ 
       |      |
       |      |
       +------+ 
[l; b]          [r; b]
列表遍历的方法没有改变——我从前面到后面对每个节点进行选择检查

首先,我选择根矩形第一个矩形,与选择相交。接下来,如果其他矩形相交,则在与根相交时检查它们,以确保它们不只是在旁边

我记住了当前矩形和根(包括根)之间的矩形,需要进一步检查。矩形存储在另一个列表的矩形列表中

开始遍历此列表。在这一刻,我首先检查r和sra节点的矩形是否相交,并将它们的相交存储在ir中。此外,选择矩形应与ir相交

现在,我用坐标扫描矩形每边ir点周围的区域:

struct rect scan_area {
    ir.l - 1,
    ir.t - 1,
    ir.r + 1,
    ir.b + 1
};
要在当前矩形和选定区域中检查点,但不是在所有记忆矩形列表中检查点,请执行此操作

bool l = false;
bool t = false;
bool r = false;
bool b = false;
struct point p;
int i;

for (i = ir.l; i <= ir.r; i++) {
    p.x = i;

    p.y = ir.t - 1;
    if ((t = list_check(rect_list, p))
        break;

    p.y = ir.b + 1;
    if ((b = list_check(rect_list, p))
        break;
}

for (i = ir.t; i <= ir.b; i++) {
    if (t || b)
        break;

    p.y = i;

    p.x = ir.l - 1;
    if ((l = list_check(rect_list, crd))
        break;

    p.x = ir.r + 1;
    if ((r = list_check(rect_list, crd))
        break;
}

if ((obj->selected = l || t || r || b))
    break;
我不会说这个程序效率很低,与第一个提出的方法相比:屏幕空间选择,因为我首先需要检查所有的矩形;其次,检查这些矩形的所有像素,包括选择的每个像素


客观地说,这个程序在^3上并不快。然而,这在我的任务中并不明显。

我自己想到了这个解决方案,结果比我预期的要困难一些。我花了一段时间

列表遍历的方法没有改变——我从前面到后面对每个节点进行选择检查

首先,我选择根矩形第一个矩形,与选择相交。接下来,如果其他矩形相交,则在与根相交时检查它们,以确保它们不只是在旁边

我记住了当前矩形和根(包括根)之间的矩形,需要进一步检查。矩形存储在另一个列表的矩形列表中

开始遍历此列表。在这一刻,我首先检查r和sra节点的矩形是否相交,并将它们的相交存储在ir中。此外, 选择矩形应与ir相交

现在,我用坐标扫描矩形每边ir点周围的区域:

struct rect scan_area {
    ir.l - 1,
    ir.t - 1,
    ir.r + 1,
    ir.b + 1
};
要在当前矩形和选定区域中检查点,但不是在所有记忆矩形列表中检查点,请执行此操作

bool l = false;
bool t = false;
bool r = false;
bool b = false;
struct point p;
int i;

for (i = ir.l; i <= ir.r; i++) {
    p.x = i;

    p.y = ir.t - 1;
    if ((t = list_check(rect_list, p))
        break;

    p.y = ir.b + 1;
    if ((b = list_check(rect_list, p))
        break;
}

for (i = ir.t; i <= ir.b; i++) {
    if (t || b)
        break;

    p.y = i;

    p.x = ir.l - 1;
    if ((l = list_check(rect_list, crd))
        break;

    p.x = ir.r + 1;
    if ((r = list_check(rect_list, crd))
        break;
}

if ((obj->selected = l || t || r || b))
    break;
我不会说这个程序效率很低,与第一个提出的方法相比:屏幕空间选择,因为我首先需要检查所有的矩形;其次,检查这些矩形的所有像素,包括选择的每个像素


客观地说,这个程序在^3上并不快。但是,它在我的任务中并不明显。

选择循环应该用于struct node*node=getendlist;节点;node=node->prev{…}。使用do/while循环进行编码时,无法处理空列表。根据经验,将矩形描述为“上”、“左”、“下”、“右”会生成比“上”、“左”、“宽”、“高”更简单的代码。选择循环应用于结构节点*节点=getendlist;节点;node=node->prev{…}。使用do/while循环进行编码时,您无法处理空列表。根据经验,将矩形描述为“上”、“左”、“下”、“右”比“上”、“左”、“宽”、“高”生成的代码更简单。我还喜欢选择矩形细分,因为使用递归函数进行编码很简单。请注意,对于列表的其余部分,要处理的子矩形的最大数量是4,而不是8:例如,可以首先将选择矩形拆分为对象上方、对象下方的3个水平带,中间带可以进一步水平拆分为3个矩形,其中只有2个需要进一步处理。如果前景中有许多小矩形,这种方法效率很低。我也喜欢选择矩形细分,因为使用递归函数编写代码很简单。请注意,对于列表的其余部分,要处理的子矩形的最大数量是4,而不是8:例如,可以首先将选择矩形拆分为对象上方、对象下方的3个水平带,中间带可以进一步水平拆分为3个矩形,其中只有2个需要进一步处理。如果前景中有许多小矩形,则此方法效率非常低。您的两个矩形相交公式不正确,因为w和h应该是矩形的尺寸,而不是右下角的坐标+1,-1调整也非常可疑。恐怕你的解决办法只是巧合。在选择矩形中的所有点上迭代的蛮力方法可以工作,但效率非常低。但是您的迭代并没有实现这一点,因为循环不是嵌套的。@chqrlie,是的,您提出的符号使问题更清楚。我更新了问题和自己的答案。你关于两个矩形相交的公式不正确,因为w和h应该是矩形的尺寸,而不是右下角的坐标+1,-1调整也非常可疑。恐怕你的解决办法只是巧合。在选择矩形中的所有点上迭代的蛮力方法可以工作,但效率非常低。但是您的迭代并没有实现这一点,因为循环不是嵌套的。@chqrlie,是的,您提出的符号使问题更清楚。我更新了问题和自己的答案。