2

I have got a 2D array of cells with size 10x10, and many points that are pairs of floating point values, like: (1.6, 1.54), (4.53, 3.23). The pairs (x,y) are such that x<10 and y<10

Each cell takes points whose coordinates have the same integer part as the cell coordinates. So arr[3][7] will take points with x={3...3.99(9)} and y={7... 7.99(9)}, for example (3.5, 7.1) or (3.2, 7.6). Similarly (1.6, 1.54) is in arr[1][1] , (4.53, 3.23) is in arr[4][3], etc.

Each point has got a designated place in the array that is easy to find, because I just have to cast x and y to int to get rid of the decimal points.

But I would like to find which cells in the array are crossed by the line segment between two points A(x,y) and B(x,y).

For example: A(1.5, 2.5) and B(4.3, 3.2) crosses cells in array with indexes [1][2], [2][2], [3,3] and [3,4]

Is there any algorithm for that?

It is a similar problem to that one: Cells in grid crossed by a line ( PHP )

Community
  • 1
  • 1
Mateusz
  • 604
  • 8
  • 20
  • Can you determine whether two line segments intersect? A square grid cell will be intersected exactly if one of its four sides is intersected. – das-g Mar 04 '16 at 23:36

3 Answers3

6

Method of Amanatides and Woo A Fast Voxel Traversal Algorithm for Ray Tracing allows to enumerate all intersected cells.
Here is practical implementation.

An example of work (intersected and touched cells are colored) enter image description here

MBo
  • 77,366
  • 5
  • 53
  • 86
5

Let your points be A and B, with respective coordinates (xA,yA) and (xB,yB).

A parametric equation for the line segment between both points is given by:
A + t * (B-A) = (xA + t * (xB - xA), yA + t * (yB - yA)) where ttakes all valuers between 0 and 1.

You need to consider all the integral values of either coordinate, along the line segment. This will give you the point of intersection of your line and a cell's side, so you can mark both cells adjacent to this side as "traversed".

Here is the outline of an algorithm to do this, sorting intersection points along the line:

  • start at cell A
  • while you're not at cell B:
    • find the next intersection of your segment with an x axis
    • find the next intersection of your segment with a y axis
    • take the closest one, mark the adjacent cell, and move to it

There are some special cases, like cells that are only touched at one corner. To treat those specially in the previous algorithm, you can recognize the that both potential future intersections are the same.


Here is a quick python demo, where I scaled (multiplied) all t values of the parametric equation by dx * dy so you don't have to divide by dx or dy, except if you want the exact intersection coordinates.

from math import floor
def sign(n):
    return (n > 0) - (n < 0)

def raytrace(A, B):
    """ Return all cells of the unit grid crossed by the line segment between
        A and B.
    """

    (xA, yA) = A
    (xB, yB) = B
    (dx, dy) = (xB - xA, yB - yA)
    (sx, sy) = (sign(dx), sign(dy))

    grid_A = (floor(A[0]), floor(A[1]))
    grid_B = (floor(B[0]), floor(B[1]))
    (x, y) = grid_A
    traversed=[grid_A]

    tIx = dy * (x + sx - xA) if dx != 0 else float("+inf")
    tIy = dx * (y + sy - yA) if dy != 0 else float("+inf")

    while (x,y) != grid_B:
        # NB if tIx == tIy we increment both x and y
        (movx, movy) = (tIx <= tIy, tIy <= tIx)

        if movx:
            # intersection is at (x + sx, yA + tIx / dx^2)
            x += sx
            tIx = dy * (x + sx - xA)

        if movy:
            # intersection is at (xA + tIy / dy^2, y + sy)
            y += sy
            tIy = dx * (y + sy - yA)

        traversed.append( (x,y) )

    return traversed

If your cell width is w and the cell with coordinates 0, 0 starts at (x0, y0) (that is [x0 , x0 + w] * [y0, y0 + w]) then normalize for that when calling the function, i.e. instead of

raytrace( (1,1.5) , (5,2.5) )

use

raytrace( ((1 - x0) / w, (1.5 - y0) / w) , ((4 - x0) / w, (1.5 - y0) / w) )
Cimbali
  • 11,012
  • 1
  • 39
  • 68
  • thanks! And what is A anb B in this equation? what should be the result of this equation? – Mateusz Mar 05 '16 at 13:36
  • ok these are xA,xB and yA, yB but you used also A and B. Anyway for simple example like A(1;1,5) B(4;2,5) you take xI =2 and xI=3 right? – Mateusz Mar 05 '16 at 13:40
  • A(1 ; 1,5) B(4 ; 1.5) means you iterate for xI = 2 and for xI =3, and you don't need to iterate for yI. – Cimbali Mar 05 '16 at 14:34
  • oh sorry I ment A(1 ; 1,5) B(1 ; 4.5) when there is no xI – Mateusz Mar 05 '16 at 15:47
  • yeah I see, but I have to write in C++. And I have got next problem because I had to change my grid that each cell has got 0.2 lenght and cell[0,0] keeps values with x = -0.1 to 0.1, cell[1,0] keeps with x = 0.1 to 0.3 and so on :/ Is it hard to change this is your implementation? – Mateusz Mar 05 '16 at 17:20
  • Thanks a lot! i meant that y also is moved by 0.1 but I will sort it out. Thanks but floor will not work now because width of cell is not 1 but 0.2 – Mateusz Mar 05 '16 at 18:30
  • I have tried to write first version of your algoritm in C++: http://pastebin.com/8F9w8EBv but it doesnt work correctly :/ it find me couple of cells when should be a lot.could you take a look what am I doing wrong? – Mateusz Mar 05 '16 at 19:56
  • You need to iterate over the yI as well. And consider both sides of the intersected border, that is (xI, yI) and (xI+1, y1) – Cimbali Mar 05 '16 at 20:01
  • I thought that it will find many cells at least for X axis. Ok I will try to code fucntion for Y. Thanks for reply – Mateusz Mar 05 '16 at 20:45
  • So what is the formula for y axis? like for x is "t = (xI - xA) / (xB - xA); yI = yA + t * (yB - yA);" Will it be ? X=xA+ (xB-xA-Y+yA )/ yB-yA – Mateusz Mar 05 '16 at 21:07
  • 1
    that algorithm does not work for a.x > b.x || a.y > b.y it will be nice to write such constraints explicitly – Dominik Oct 18 '19 at 20:39
  • 1
    Anyway I have tweaked it a bit and made working sample: https://codepen.io/dominik-lach/pen/eYYBOXw – Dominik Oct 18 '19 at 21:39
1

Try Bresenham’s line drawing algorithm, which has python package. The code looks like this:

from bresenham import bresenham


cells = list(bresenham(96, 280, 95, 275))
print(cells)

I received [(96, 280), (96, 279), (96, 278), (95, 277), (95, 276), (95, 275)]