Reputation: 1840
I have a line going from (x0, y0) to (x1, y1) through a grid made of square tiles 2^n wide. I not only need to find the tiles the line intersects, but also the corresponding entry and exit points. All the SO questions on this I can find deal with "1x1" tiles without caring where the intersections occur within the tile.
The points won't always be precisely on an integer, and in some cases I'll use the natural floor and others I'll want to round up. But letting it naturally floor in all cases for this is fine for now.
I found an example that eventually gets to a very simple case of raytracing with integers, but it doesn't keep track of the intersection points and also isn't adapted for anything but lines going through the center (assumed 0.5, 0.5 offset) of 1x1 tiles.
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;
}
}
}
How can it be adapted to find the intersected 2^n x 2^n grid tiles while also grabbing the 2 relevant intersection points? It seems the ability to start "anywhere" in a tile really mucks up this algorithm, and my solutions end up using division and probably setting things up to accumulate error over each iteration. And that's no good...
Also I think for the first and last tile, the endpoint can be assumed to be the "other" intersection point.
Upvotes: 2
Views: 3941
Reputation: 85
You can reduce your 2^n X 2^n tile size to 1 X 1 by dividing the entire coordinate system by 2^n.
Precisely, in our case that would mean that you divide the coordinates of the start point and end point of the line by 2^n. From now on, you can treat the problem as a 1X1 sized tile problem. At the end of the problem, we'll multiply 2^n back into our solution so get the answer for 2^n X 2^n solution.
Now the part of finding the entry and exit points in each tile. Suppose the line starts at (2.4, 4.6 ) and ends at (7.9, 6.3)
Algorithm Complexity : O(nlog n ) where n is the range of integers between the start and end coordinates of the line. Through minor modifications, this can further be reduced to O(n).
Upvotes: 2
Reputation: 10579
Plug in each integer value of x in the range x0..x1, and solve for each y. That will give you the locations of the intersections on the sides of the tiles.
Plug in each integer value of y in the range y0..y1, and solve for x. That will give you the locations of the intersections on the top/bottom of the tiles.
EDIT
The code gets a little uglier when dealing with different tile sizes and starting inside of a tile, but the idea is the same. Here is a solution in C# (runs as-is in 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);
The downside to this method is that, when the line intersects a tile exactly on a corner (i.e. the x and y coordinates of the intersection are both integers), then the same intersection point will be added to the list from each of the 2 for loops. In that case, you would want to remove the duplicate intersection points from your list.
Upvotes: 1
Reputation: 80327
There is useful article "Fast Voxel Traversal Algorithm..." by Woo, Amanatides. Look at the practical implementation (grid traversal section) too. I've used this method with good results.
Upvotes: 2