需要算法来绘制重叠的矩形

时间:2012-10-24 20:50:42

标签: algorithm rectangles

我需要帮助有效地绘制/剔除一系列不透明的矩形,换句话说,这是一堆索引卡在桌面上。具体是:

  • 没有旋转,所以一切都是简单的整数坐标,轴对齐
  • 卡片完全不透明
  • 卡可以有任何整数X,Y位置
  • 所有卡片大小相同
  • 我有z-order
  • 的卡片列表

我认为我(基本上)有两种选择:

1)蛮力画家的方法,其中桌面视口中的所有卡片都以反向z顺序完全绘制。优点:简单。缺点:a)需要屏幕外缓冲以避免闪烁,b)当该区域可能最终被遮挡时,可能会浪费大量时间来绘制每张卡的昂贵区域,最坏情况是整张卡被覆盖。

2)为每张卡生成可见(或模糊)矩形列表的算法,以便只绘制可见部分。

选择2是我需要建议的地方,特别是在算法方面,以及“聪明”绘制周期的专业和概念。

感谢任何语言/平台不可知的建议。如果重要,这将在MS Windows上实现。

对任何建议持开放态度,包括混合方法。我意识到一个精确的答案很可能非常依赖于代码的细节,但是在这一点上我甚至会对广义概念感到高兴!

附加说明:可能会有数千张卡片堆叠在一起,所以我非常积极地避免使用纯粹的蛮力画家的方法 - 至少没有进行某种预处理以完全剔除模糊的卡片。许多卡片都是紧密平铺的,情况也是如此,更糟糕的情况只是它们的边框显示 - 如果可能的话,我想在这些情况下跳过绘制复杂的内部结构。

2 个答案:

答案 0 :(得分:0)

如何仅从最底部到最顶部绘制每张卡的轮廓线?然后你可以进行填充填充以在轮廓内部进行绘制。这样,您只会重新绘制与有交叉点的边界对应的几个像素。

编辑:上传的图片,以帮助我解释这个想法。

Part 1 of the method Part 2

第一步是标记卡片的边框,指定其Z顺序(左上图像)。这样,就会有覆盖,但只有在像素数量很少的边界上。

之后,您可以按照两个规则绘制卡片的纹理(最低Z顺序):

  • 您从边框开始绘制空白直到到达边框;
  • 如果边框的Z顺序是当前的顺序,则绘制它;
  • 如果找到的边框的Z顺序小于当前的Z顺序,则继续绘画,因为它是空白的;
  • 否则,您找到了一个Z序列更大的边框,因此您跳过该块;
  • 下一张卡片。

希望有所帮助:)

答案 1 :(得分:0)

好的,这里有一些松散的伪代码,我认为这个问题可以解决。

从卡的z顺序排序列表开始。每张卡都有一个可见的列表(稍后解释),需要从一个矩形开始,设置为卡的完整边界框。循环首先使用最低的z顺序卡开始。

Cards.SortZOrder();
foreach Card in Cards do
  Card.ResetVisibleRects; // VisibleRects.DeleteAll; VisibleRects.Add(BoundingBox);

CurrentCard = Cards.Last;
TestCard = CurrentCard;

这里的想法是,我们将从我们的“当前”卡向上工作,看看每张高卡对它有什么影响。我们测试每张高卡时有3种可能性。它要么完全错过,要么完全模糊,要么部分模糊。如果完全错过,我们会忽略测试卡,因为它不会影响我们当前的卡。对于一个完全模糊的,我们当前的卡被剔除。部分重叠是可见矩形列表的来源,因为部分重叠可以(可能)将下部矩形分成两部分。 (如果你只抓两张扑克牌或索引牌,很容易看出它是如何发挥作用的。顶部的一张让底部的一张要么调整其中一条边,如果它们共用任何边缘,要么导致底部分裂如果它们没有共享边,则分为两个部分。)

警告:这是非常未优化的,展开的代码......仅仅是为了讨论这些原则。是的,我即将使用“goto”......如果你必须,请嘲笑我。

[GetNextCard]
TestCard = Cards.NextHighest(TestCard);

[OverlapTest]
// Test the overlap of TestCard against all our VisibleRects.
// The first time through this test, CurrentCard will have only one
// rect in the VisibleRect list, but that rect may get split up later.
// OverlapTests() checks each rect in the VisibleRects list, and
// creates an Overlap record for any of the rects that do overlap,
// like: Overlap.RectIndex, Overlap.Type. It also summarizes the
// results into the .Summary field. 

Result = CurrentCard.OverlapTests(TestCard);

case Result.Summary
  none: 
    goto [GetNextCard];

  complete:
    CurrentCard.Culled = true;

    // we're now done with this CurrentCard, so we move upwards
    CurrentCard = TestCard;
    goto [GetNextCard]

  partial:
    // since there was some overlap, we need to adjust,
    // split, or delete some or all of our visible rectangles.
    // (we won't delete them all, that would have been caught above)

    foreach Overlap in Result.Overlaps
      R = CurrentCard.VisibleRects[Overlap.RectIndex];
      case Overlap.Type
        partial: CurrentCard.SplitOrAdjust(R, TestCard);
        complete: CurrentCard.Delete(R);
      end case

    // so we've either added new rects, or deleted some, but either 
    // way, we're done with this test card. We leave CurrentCard
    // where it is and loop to look at the next higher card.
    goto [GetNextCard]

测试在CurrentCard = Cards.First完成,因为最顶层的卡片始终完全可见。

这里还有几个想法...

我认为这在实际代码中相当简单。关于它的最复杂的事情是将一个矩形分成两个,并且考虑到它都是整数数学,即使这很简单。

此外,不必每个喷漆周期都执行此操作。只有在内容,位置或z顺序发生任何变化时才需要完成。

在列表中放弃之后,您将获得一张可用于绘制颜色的卡片列表,每张非剔除卡片至少有一个矩形可能会落入显示器的剪切/脏区域内。当您绘制卡片时,您可以检查其可见矩形列表,并且可能跳过绘制可能需要渲染的卡片部分。