我遇到了问题:我的磁贴引擎需要一个算法。
我有一个二维数组存储我的不可走路的瓷砖。
现在我想实现一个光引擎,但这个引擎需要影子船体。
所以我需要一种能够创建这些影子外壳的算法。
我需要一组矩形来绑定阵列的不可走路部分(具有1
s的单元格)
例如:
黑色瓷砖是1
s;我需要找到一组完全包围它们的红色矩形。
答案 0 :(得分:2)
尝试这样的事情:
创建包含每个所需点的列表。 (在您的情况下,每个1
)的坐标
对于此列表中的每个点:
0
)0
。这可能不是最快的方法,但它应该有效。
答案 1 :(得分:2)
经过深思熟虑后,我想出了一个更快的解决方案:
使用Range
,StartX
和StartY
属性创建不可变EndY
结构。
维护一个稀疏的当前Range
数组,其长度等于图像的高度,以及一个可为空的currentRange变量。此数组将包含从每个Y坐标开始的范围(如果有)
对于每列,
currentRange
对于列中的每个单元格:
如果没有currentRange,或者如果有,但它在此单元格之前结束(如果currentRange.EndY <= y
),则将currentRange设置为范围数组中的y
'元素。 />
因此,currentRange
将始终引用包含当前单元格的范围,如果当前单元格不在范围内,则null
。
如果当前单元格为1
:
如果我们在一个范围内,什么也不做 - 范围将继续进入下一栏。
如果我们不在一个范围内,请循环播放列,直到我们点击0
或列的结尾,然后创建一个新范围,从1
延伸到我们刚刚发现直到循环结束。
然后,继续执行下一个if(因为当前单元格现在是0
或列的末尾,而我们不在范围内)
如果在前进时创建新范围时达到现有范围,则可以停止新范围并让现有范围继续低于它(最适合模糊边缘),或关闭现有范围(见下文)并让新范围向下延伸以接替现有范围。
0
:
此算法在计算中为O(x * y)
,在空间中为O(y)
。我相信这是解决问题的最快方法。
您还可以旋转此算法并扫描行而不是列(以便范围向下而不是向右拉伸),这将在存储中O(x)
。
这是一个C#实现:
class BoxFinder {
class Range {
public Range(int startX, int startY, int endY) {
StartX = startX;
StartY = startY;
EndY = endY;
}
public int StartX { get; private set; }
public int StartY { get; private set; }
public int EndY { get; private set; }
}
public BoxFinder(int[,] data) {
Data = data;
Width = data.GetLength(0);
Height = data.GetLength(1);
ranges = new Range[Height];
}
public int[,] Data { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
private Range[] ranges;
public IEnumerable<Rectangle> GetBoxes() {
for (int x = 0; x < Width; x++) {
Range currentRange = null;
for (int y = 0; y < Height; y++) {
Func<Range, Rectangle> CloseRange = r => {
currentRange = null;
ranges[r.StartY] = null;
return new Rectangle(
r.StartY,
r.StartX,
x - r.StartX,
r.EndY - r.StartY
);
};
if (currentRange == null || currentRange.EndY <= y)
currentRange = ranges[y];
if (Data[x, y] == 1) {
if (currentRange == null) {
int startY = y;
for (; y < Height && Data[x, y] == 1; y++) {
if (ranges[y] != null)
yield return CloseRange(ranges[y]);
//If there are fuzzy edges, break; instead
}
ranges[startY] = new Range(x, startY, y);
if (y == Height)
continue;
//Otherwise, continue to the next if in case a previous range just ended
}
}
//No else; we can get here after creating a range
if(Data[x, y] == 0) {
if (currentRange != null)
yield return CloseRange(currentRange);
}
}
}
}
}