Welcome, guest | Sign In | My Account | Store | Cart
"""
N-D Bresenham line algo
"""
import numpy as np
def bresenhamline_nslope(slope):
    """
    Normalize slope for Bresenham's line algorithm.

    >>> s = np.array([[-2, -2, -2, 0]])
    >>> bresenhamline_nslope(s)
    array([[-1., -1., -1.,  0.]])

    >>> s = np.array([[0, 0, 0, 0]])
    >>> bresenhamline_nslope(s)
    array([[ 0.,  0.,  0.,  0.]])

    >>> s = np.array([[0, 0, 9, 0]])
    >>> bresenhamline_nslope(s)
    array([[ 0.,  0.,  1.,  0.]])
    """
    scale = np.amax(np.abs(slope), axis=1).reshape(-1, 1)
    zeroslope = (scale == 0).all(1)
    scale[zeroslope] = np.ones(1)
    normalizedslope = np.array(slope, dtype=np.double) / scale
    normalizedslope[zeroslope] = np.zeros(slope[0].shape)
    return normalizedslope

def bresenhamline_step(start, normalizedslope, err):
    """
    Performs a single incremental step according to Bresenham's algorithm.

    >>> s = np.array([[4, 4, 4, 0]])
    >>> ns = bresenhamline_nslope(np.array([[-2, -2, -2, 0]]))
    >>> e = np.zeros((1, 4))
    >>> bresenhamline_step(s, ns, e)
    (array([[ 3.,  3.,  3.,  0.]]), array([[ 0.,  0.,  0.,  0.]]))

    >>> s = np.array([[0, 0, 9, 0]])
    >>> ns = bresenhamline_nslope(-1 * s)
    >>> e = np.zeros((1, 4))
    """
    nextpoint = start + normalizedslope + err
    nextvox = np.rint(nextpoint)
    err = nextpoint - nextvox
    return nextvox, err

def bresenhamline_gen(start, end):
    """
    Returns a generator of points from (start, end] by ray tracing a line b/w
    the points.
    Parameters:
        start: An array of start points (number of points x dimension)
        end:   An end points (1 x dimension)
            or An array of end point corresponding to each start point
                (number of points x dimension)

    Returns:
        A generator that returns a point being traversed at each step
        corresponding to each start point.

    >>> [p for p in bresenhamline_gen(np.array([[3, 1, 3, 0]]), np.zeros(4))]
    [array([[2, 1, 2, 0]]), array([[1, 0, 1, 0]]), array([[0, 0, 0, 0]])]
    """
    dim = start.shape[1]
    err = np.zeros(start.shape)
    slope = np.array(end - start, dtype=np.float32)
    nslope = bresenhamline_nslope(slope)
    cur_vox = start
    while (np.sum(nslope != 0)):
        cur_vox, err = bresenhamline_step(cur_vox, nslope, err)
        # reached end ?
        nslope[(np.abs(end - cur_vox) < 1).all(1)] = np.zeros(dim) 
        yield np.array(cur_vox, dtype=start.dtype)

def bresenhamline(start, end, max_iter=5):
    """
    Returns a list of points from (start, end] by ray tracing a line b/w the
    points.
    Parameters:
        start: An array of start points (number of points x dimension)
        end:   An end points (1 x dimension)
            or An array of end point corresponding to each start point
                (number of points x dimension)
        max_iter: Max points to traverse

    Returns:
        linevox (n x dimension) A cumulative array of all points traversed by
        all the lines so far.

    >>> s = np.array([[0, 0, 3, 0]])
    >>> bresenhamline(s, np.zeros(s.shape[1]), max_iter=9)
    array([[0, 0, 2, 0],
           [0, 0, 1, 0],
           [0, 0, 0, 0]])

    >>> s = np.array([[3, 1, 9, 0]])
    >>> bresenhamline(s, np.zeros(s.shape[1]), max_iter=9)
    array([[3, 1, 8, 0],
           [2, 1, 7, 0],
           [2, 1, 6, 0],
           [2, 1, 5, 0],
           [1, 0, 4, 0],
           [1, 0, 3, 0],
           [1, 0, 2, 0],
           [0, 0, 1, 0],
           [0, 0, 0, 0]])
    """
    dim = start.shape[1]
    linevox = np.zeros((0, dim), dtype=start.dtype)
    linegen = bresenhamline_gen(start, end)
    try:
        for i in range(max_iter):
            cur_vox = linegen.next()
            linevox = np.vstack((linevox, cur_vox))
    except StopIteration:
        pass
    return np.array(linevox, dtype=start.dtype)

if __name__ == "__main__":
    import doctest
    doctest.testmod()

History