[英]Getting all intersection points between a line segment and a 2^n grid, in integers
我有一條從(x0,y0)到(x1,y1)的線穿過由2 ^ n寬的正方形瓷磚制成的網格。 我不僅需要找到線相交的瓦片,還需要找到相應的入口和出口點。 關於這一切的所有SO問題我可以找到處理“1x1”瓷磚而不關心瓷磚內交叉點發生的位置。
積分並不總是精確地在一個整數上,在某些情況下我會使用自然地板和其他我想要整理的地方。 但是現在讓它在所有情況下都自然而然地落地。
我找到了一個例子 ,最終得到了一個非常簡單的使用整數進行光線跟蹤的情況,但是它並沒有跟蹤交點,也沒有適用於通過中心的線條(假設為0.5,0.5偏移) 1x1瓷磚。
void raytrace(int x0, int y0, int x1, int y1)
{
int dx = abs(x1 - x0);
int dy = abs(y1 - y0);
int x = x0;
int y = y0;
int n = 1 + dx + dy;
int x_inc = (x1 > x0) ? 1 : -1;
int y_inc = (y1 > y0) ? 1 : -1;
int error = dx - dy;
dx *= 2;
dy *= 2;
for (; n > 0; --n)
{
visit(x, y);
if (error > 0)
{
x += x_inc;
error -= dy;
}
else
{
y += y_inc;
error += dx;
}
}
}
如何才能找到相交的2 ^ nx 2 ^ n網格圖塊,同時抓住2個相關的交叉點? 似乎能夠在一塊瓷磚中“隨處”啟動的能力確實會破壞這個算法,而我的解決方案最終會使用除法,並且可能會在每次迭代時累積誤差。 這不好......
另外我認為對於第一個和最后一個圖塊,端點可以被假定為“其他”交叉點。
Woo,Amanatides有一篇有用的文章“ Fast Voxel Traversal Algorithm ... ”。 看一下實際的實現( 網格遍歷部分 )。 我用這種方法效果很好。
通過將整個坐標系除以2 ^ n,可以將2 ^ n X 2 ^ n平鋪大小減小到1 X 1 。
確切地說,在我們的情況下,這意味着您將線的起點和終點的坐標除以2 ^ n。 從現在開始,您可以將問題視為1X1大小的磁貼問題。 在問題的最后,我們將2 ^ n乘以我們的解決方案,以獲得2 ^ n X 2 ^ n解決方案的答案。
現在是在每個瓷磚中找到進入和退出點的部分。 假設線從(2.4,4.6)開始到(7.9,6.3)結束
算法復雜度:O(nlog n)其中n是行的起始坐標和結束坐標之間的整數范圍。 通過微小的修改,這可以進一步減少到O(n)。
在x0..x1范圍內插入x的每個整數值,並求解每個y。 這將為您提供瓷磚兩側交叉點的位置。
在y0..y1范圍內插入y的每個整數值,並求解x。 這將為您提供瓷磚頂部/底部交叉點的位置。
編輯
在處理不同的瓷磚尺寸並從瓷磚內部開始時,代碼會變得更加丑陋,但想法是一樣的。 這是C#中的解決方案(在LINQPad中按原樣運行):
List<Tuple<double,double>> intersections = new List<Tuple<double,double>>();
int tile_width = 4;
int x0 = 3;
int x1 = 15;
int y0 = 1;
int y1 = 17;
int round_up_x0_to_nearest_tile = tile_width*((x0 + tile_width -1)/tile_width);
int round_down_x1_to_nearest_tile = tile_width*x1/tile_width;
int round_up_y0_to_nearest_tile = tile_width*((y0 + tile_width -1)/tile_width);
int round_down_y1_to_nearest_tile = tile_width*y1/tile_width;
double slope = (y1-y0)*1.0/(x1-x0);
double inverse_slope = 1/slope;
for (int x = round_up_x0_to_nearest_tile; x <= round_down_x1_to_nearest_tile; x += tile_width)
{
intersections.Add(new Tuple<double,double>(x, slope*(x-x0)+y0));
}
for (int y = round_up_y0_to_nearest_tile; y <= round_down_y1_to_nearest_tile; y += tile_width)
{
intersections.Add(new Tuple<double,double>(inverse_slope*(y-y0)+x0, y));
}
intersections.Sort();
Console.WriteLine(intersections);
這種方法的缺點是,當線條恰好在一個角上與一個瓷磚相交時(即交點的x和y坐標都是整數),那么相同的交點將被添加到列表中的每一個中。循環。 在這種情況下,您需要從列表中刪除重復的交叉點。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.