Skip to content

odak.learn.raytracing

odak.learn.raytracing

Provides necessary definitions for geometric optics. See "General Ray tracing procedure" from G.H. Spencerand M.V.R.K Murty for more theoratical explanation.

get_sphere_normal_torch(point, sphere)

Definition to get a normal of a point on a given sphere.

Parameters:

  • point
            Point on sphere in X,Y,Z.
    
  • sphere
            Center defined in X,Y,Z and radius.
    

Returns:

  • normal_vector ( tensor ) –

    Normal vector.

Source code in odak/learn/raytracing/boundary.py
def get_sphere_normal_torch(point, sphere):
    """
    Definition to get a normal of a point on a given sphere.

    Parameters
    ----------
    point         : torch.tensor
                    Point on sphere in X,Y,Z.
    sphere        : torch.tensor
                    Center defined in X,Y,Z and radius.

    Returns
    ----------
    normal_vector : torch.tensor
                    Normal vector.
    """
    if len(point.shape) == 1:
        point = point.reshape((1, 3))
    normal_vector = create_ray_from_two_points(point, sphere[0:3])
    return normal_vector

get_triangle_normal(triangle, triangle_center=None)

Definition to calculate surface normal of a triangle.

Parameters:

  • triangle
              Set of points in X,Y and Z to define a planar surface (3,3). It can also be list of triangles (mx3x3).
    
  • triangle_center (tensor, default: None ) –
              Center point of the given triangle. See odak.learn.raytracing.center_of_triangle for more. In many scenarios you can accelerate things by precomputing triangle centers.
    

Returns:

  • normal ( tensor ) –

    Surface normal at the point of intersection.

Source code in odak/learn/raytracing/boundary.py
def get_triangle_normal(triangle, triangle_center=None):
    """
    Definition to calculate surface normal of a triangle.

    Parameters
    ----------
    triangle        : torch.tensor
                      Set of points in X,Y and Z to define a planar surface (3,3). It can also be list of triangles (mx3x3).
    triangle_center : torch.tensor
                      Center point of the given triangle. See odak.learn.raytracing.center_of_triangle for more. In many scenarios you can accelerate things by precomputing triangle centers.

    Returns
    ----------
    normal          : torch.tensor
                      Surface normal at the point of intersection.
    """
    if len(triangle.shape) == 2:
        triangle = triangle.view((1, 3, 3))
    normal = torch.zeros((triangle.shape[0], 2, 3)).to(triangle.device)
    direction = torch.cross(
                            triangle[:, 0] - triangle[:, 1], 
                            triangle[:, 2] - triangle[:, 1]
                           )
    if type(triangle_center) == type(None):
        normal[:, 0] = center_of_triangle(triangle)
    else:
        normal[:, 0] = triangle_center
    normal[:, 1] = direction / torch.sum(direction, axis=1)[0]
    if normal.shape[0] == 1:
        normal = normal.view((2, 3))
    return normal

intersect_w_circle(ray, circle)

Definition to find intersection point of a ray with a circle. Returns distance as zero if there isn't an intersection.

Parameters:

  • ray
           A vector/ray.
    
  • circle
           A list that contains (0) Set of points in X,Y and Z to define plane of a circle, (1) circle center, and (2) circle radius.
    

Returns:

  • normal ( Tensor ) –

    Surface normal at the point of intersection.

  • distance ( Tensor ) –

    Distance in between a starting point of a ray and the intersection point with a given triangle.

Source code in odak/learn/raytracing/boundary.py
def intersect_w_circle(ray, circle):
    """
    Definition to find intersection point of a ray with a circle. 
    Returns distance as zero if there isn't an intersection.

    Parameters
    ----------
    ray          : torch.Tensor
                   A vector/ray.
    circle       : list
                   A list that contains (0) Set of points in X,Y and Z to define plane of a circle, (1) circle center, and (2) circle radius.

    Returns
    ----------
    normal       : torch.Tensor
                   Surface normal at the point of intersection.
    distance     : torch.Tensor
                   Distance in between a starting point of a ray and the intersection point with a given triangle.
    """
    normal, distance = intersect_w_surface(ray, circle[0])

    if len(normal.shape) == 2:
        normal = normal.unsqueeze(0)

    distance_to_center = distance_between_two_points(normal[:, 0], circle[1])
    mask = distance_to_center > circle[2]
    distance[mask] = 0

    if len(ray.shape) == 2:
        normal = normal.squeeze(0)

    return normal, distance

intersect_w_sphere(ray, sphere, learning_rate=0.2, number_of_steps=5000, error_threshold=0.01)

Definition to find the intersection between ray(s) and sphere(s).

Parameters:

  • ray
                  Input ray(s).
                  Expected size is [1 x 2 x 3] or [m x 2 x 3].
    
  • sphere
                  Input sphere.
                  Expected size is [1 x 4].
    
  • learning_rate
                  Learning rate used in the optimizer for finding the propagation distances of the rays.
    
  • number_of_steps
                  Number of steps used in the optimizer.
    
  • error_threshold
                  The error threshold that will help deciding intersection or no intersection.
    

Returns:

  • intersecting_ray ( tensor ) –

    Ray(s) that intersecting with the given sphere. Expected size is [n x 2 x 3], where n could be any real number.

  • intersecting_normal ( tensor ) –

    Normal(s) for the ray(s) intersecting with the given sphere Expected size is [n x 2 x 3], where n could be any real number.

Source code in odak/learn/raytracing/boundary.py
def intersect_w_sphere(ray, sphere, learning_rate = 2e-1, number_of_steps = 5000, error_threshold = 1e-2):
    """
    Definition to find the intersection between ray(s) and sphere(s).

    Parameters
    ----------
    ray                 : torch.tensor
                          Input ray(s).
                          Expected size is [1 x 2 x 3] or [m x 2 x 3].
    sphere              : torch.tensor
                          Input sphere.
                          Expected size is [1 x 4].
    learning_rate       : float
                          Learning rate used in the optimizer for finding the propagation distances of the rays.
    number_of_steps     : int
                          Number of steps used in the optimizer.
    error_threshold     : float
                          The error threshold that will help deciding intersection or no intersection.

    Returns
    -------
    intersecting_ray    : torch.tensor
                          Ray(s) that intersecting with the given sphere.
                          Expected size is [n x 2 x 3], where n could be any real number.
    intersecting_normal : torch.tensor
                          Normal(s) for the ray(s) intersecting with the given sphere
                          Expected size is [n x 2 x 3], where n could be any real number.

    """
    if len(ray.shape) == 2:
        ray = ray.unsqueeze(0)
    if len(sphere.shape) == 1:
        sphere = sphere.unsqueeze(0)
    distance = torch.zeros(ray.shape[0], device = ray.device, requires_grad = True)
    loss_l2 = torch.nn.MSELoss(reduction = 'sum')
    optimizer = torch.optim.AdamW([distance], lr = learning_rate)    
    t = tqdm(range(number_of_steps), leave = False, dynamic_ncols = True)
    for step in t:
        optimizer.zero_grad()
        propagated_ray = propagate_ray(ray, distance)
        test = torch.abs((propagated_ray[:, 0, 0] - sphere[:, 0]) ** 2 + (propagated_ray[:, 0, 1] - sphere[:, 1]) ** 2 + (propagated_ray[:, 0, 2] - sphere[:, 2]) ** 2 - sphere[:, 3] ** 2)
        loss = loss_l2(
                       test,
                       torch.zeros_like(test)
                      )
        loss.backward(retain_graph = True)
        optimizer.step()
        t.set_description('Sphere intersection loss: {}'.format(loss.item()))
    check = test < error_threshold
    intersecting_ray = propagate_ray(ray[check == True], distance[check == True])
    intersecting_normal = create_ray_from_two_points(
                                                     sphere[:, 0:3],
                                                     intersecting_ray[:, 0]
                                                    )
    return intersecting_ray, intersecting_normal, distance, check

intersect_w_surface(ray, points)

Definition to find intersection point inbetween a surface and a ray. For more see: http://geomalgorithms.com/a06-_intersect-2.html

Parameters:

  • ray
           A vector/ray.
    
  • points
           Set of points in X,Y and Z to define a planar surface.
    

Returns:

  • normal ( tensor ) –

    Surface normal at the point of intersection.

  • distance ( float ) –

    Distance in between starting point of a ray with it's intersection with a planar surface.

Source code in odak/learn/raytracing/boundary.py
def intersect_w_surface(ray, points):
    """
    Definition to find intersection point inbetween a surface and a ray. For more see: http://geomalgorithms.com/a06-_intersect-2.html

    Parameters
    ----------
    ray          : torch.tensor
                   A vector/ray.
    points       : torch.tensor
                   Set of points in X,Y and Z to define a planar surface.

    Returns
    ----------
    normal       : torch.tensor
                   Surface normal at the point of intersection.
    distance     : float
                   Distance in between starting point of a ray with it's intersection with a planar surface.
    """
    normal = get_triangle_normal(points)
    if len(ray.shape) == 2:
        ray = ray.unsqueeze(0)
    if len(points) == 2:
        points = points.unsqueeze(0)
    if len(normal.shape) == 2:
        normal = normal.unsqueeze(0)
    f = normal[:, 0] - ray[:, 0]
    distance = (torch.mm(normal[:, 1], f.T) / torch.mm(normal[:, 1], ray[:, 1].T)).T
    new_normal = torch.zeros_like(ray)
    new_normal[:, 0] = ray[:, 0] + distance * ray[:, 1]
    new_normal[:, 1] = normal[:, 1]
    new_normal = torch.nan_to_num(
                                  new_normal,
                                  nan = float('nan'),
                                  posinf = float('nan'),
                                  neginf = float('nan')
                                 )
    distance = torch.nan_to_num(
                                distance,
                                nan = float('nan'),
                                posinf = float('nan'),
                                neginf = float('nan')
                               )
    return new_normal, distance

intersect_w_triangle(ray, triangle)

Definition to find intersection point of a ray with a triangle.

Parameters:

  • ray
                  A ray [1 x 2 x 3] or a batch of ray [m x 2 x 3].
    
  • triangle
                  Set of points in X,Y and Z to define a single triangle [1 x 3 x 3].
    

Returns:

  • normal ( tensor ) –

    Surface normal at the point of intersection with the surface of triangle. This could also involve surface normals that are not on the triangle. Expected size is [1 x 2 x 3] or [m x 2 x 3] depending on the input.

  • distance ( float ) –

    Distance in between a starting point of a ray and the intersection point with a given triangle. Expected size is [1 x 1] or [m x 1] depending on the input.

  • intersecting_ray ( tensor ) –

    Rays that intersect with the triangle plane and on the triangle. Expected size is [1 x 2 x 3] or [m x 2 x 3] depending on the input.

  • intersecting_normal ( tensor ) –

    Normals that intersect with the triangle plane and on the triangle. Expected size is [1 x 2 x 3] or [m x 2 x 3] depending on the input.

  • check ( tensor ) –

    A list that provides a bool as True or False for each ray used as input. A test to see is a ray could be on the given triangle. Expected size is [1] or [m].

Source code in odak/learn/raytracing/boundary.py
def intersect_w_triangle(ray, triangle):
    """
    Definition to find intersection point of a ray with a triangle. 

    Parameters
    ----------
    ray                 : torch.tensor
                          A ray [1 x 2 x 3] or a batch of ray [m x 2 x 3].
    triangle            : torch.tensor
                          Set of points in X,Y and Z to define a single triangle [1 x 3 x 3].

    Returns
    ----------
    normal              : torch.tensor
                          Surface normal at the point of intersection with the surface of triangle.
                          This could also involve surface normals that are not on the triangle.
                          Expected size is [1 x 2 x 3] or [m x 2 x 3] depending on the input.
    distance            : float
                          Distance in between a starting point of a ray and the intersection point with a given triangle.
                          Expected size is [1 x 1] or [m x 1] depending on the input.
    intersecting_ray    : torch.tensor
                          Rays that intersect with the triangle plane and on the triangle.
                          Expected size is [1 x 2 x 3] or [m x 2 x 3] depending on the input.
    intersecting_normal : torch.tensor
                          Normals that intersect with the triangle plane and on the triangle.
                          Expected size is [1 x 2 x 3] or [m x 2 x 3] depending on the input.
    check               : torch.tensor
                          A list that provides a bool as True or False for each ray used as input.
                          A test to see is a ray could be on the given triangle.
                          Expected size is [1] or [m].
    """
    if len(triangle.shape) == 2:
       triangle = triangle.unsqueeze(0)
    if len(ray.shape) == 2:
       ray = ray.unsqueeze(0)
    normal, distance = intersect_w_surface(ray, triangle)
    check = is_it_on_triangle(normal[:, 0], triangle)
    intersecting_ray = ray.unsqueeze(0)
    intersecting_ray = intersecting_ray.repeat(triangle.shape[0], 1, 1, 1)
    intersecting_ray = intersecting_ray[check == True]
    intersecting_normal = normal.unsqueeze(0)
    intersecting_normal = intersecting_normal.repeat(triangle.shape[0], 1, 1, 1)
    intersecting_normal = intersecting_normal[check ==  True]
    return normal, distance, intersecting_ray, intersecting_normal, check

reflect(input_ray, normal)

Definition to reflect an incoming ray from a surface defined by a surface normal. Used method described in G.H. Spencer and M.V.R.K. Murty, "General Ray-Tracing Procedure", 1961.

Parameters:

  • input_ray
           A ray or rays.
           Expected size is [2 x 3], [1 x 2 x 3] or [m x 2 x 3].
    
  • normal
           A surface normal(s).
           Expected size is [2 x 3], [1 x 2 x 3] or [m x 2 x 3].
    

Returns:

  • output_ray ( tensor ) –

    Array that contains starting points and cosines of a reflected ray. Expected size is [1 x 2 x 3] or [m x 2 x 3].

Source code in odak/learn/raytracing/boundary.py
def reflect(input_ray, normal):
    """ 
    Definition to reflect an incoming ray from a surface defined by a surface normal. 
    Used method described in G.H. Spencer and M.V.R.K. Murty, "General Ray-Tracing Procedure", 1961.


    Parameters
    ----------
    input_ray    : torch.tensor
                   A ray or rays.
                   Expected size is [2 x 3], [1 x 2 x 3] or [m x 2 x 3].
    normal       : torch.tensor
                   A surface normal(s).
                   Expected size is [2 x 3], [1 x 2 x 3] or [m x 2 x 3].

    Returns
    ----------
    output_ray   : torch.tensor
                   Array that contains starting points and cosines of a reflected ray.
                   Expected size is [1 x 2 x 3] or [m x 2 x 3].
    """
    if len(input_ray.shape) == 2:
        input_ray = input_ray.unsqueeze(0)
    if len(normal.shape) == 2:
        normal = normal.unsqueeze(0)
    mu = 1
    div = normal[:, 1, 0]**2 + normal[:, 1, 1]**2 + normal[:, 1, 2]**2 + 1e-8
    a = mu * (input_ray[:, 1, 0] * normal[:, 1, 0] + input_ray[:, 1, 1] * normal[:, 1, 1] + input_ray[:, 1, 2] * normal[:, 1, 2]) / div
    a = a.unsqueeze(1)
    n = int(torch.amax(torch.tensor([normal.shape[0], input_ray.shape[0]])))
    output_ray = torch.zeros((n, 2, 3)).to(input_ray.device)
    output_ray[:, 0] = normal[:, 0]
    output_ray[:, 1] = input_ray[:, 1] - 2 * a * normal[:, 1]
    return output_ray

refract(vector, normvector, n1, n2, error=0.01)

Definition to refract an incoming ray. Used method described in G.H. Spencer and M.V.R.K. Murty, "General Ray-Tracing Procedure", 1961.

Parameters:

  • vector
             Incoming ray.
             Expected size is [2, 3], [1, 2, 3] or [m, 2, 3].
    
  • normvector
             Normal vector.
             Expected size is [2, 3], [1, 2, 3] or [m, 2, 3]].
    
  • n1
             Refractive index of the incoming medium.
    
  • n2
             Refractive index of the outgoing medium.
    
  • error
             Desired error.
    

Returns:

  • output ( tensor ) –

    Refracted ray. Expected size is [1, 2, 3]

Source code in odak/learn/raytracing/boundary.py
def refract(vector, normvector, n1, n2, error = 0.01):
    """
    Definition to refract an incoming ray.
    Used method described in G.H. Spencer and M.V.R.K. Murty, "General Ray-Tracing Procedure", 1961.


    Parameters
    ----------
    vector         : torch.tensor
                     Incoming ray.
                     Expected size is [2, 3], [1, 2, 3] or [m, 2, 3].
    normvector     : torch.tensor
                     Normal vector.
                     Expected size is [2, 3], [1, 2, 3] or [m, 2, 3]].
    n1             : float
                     Refractive index of the incoming medium.
    n2             : float
                     Refractive index of the outgoing medium.
    error          : float 
                     Desired error.

    Returns
    -------
    output         : torch.tensor
                     Refracted ray.
                     Expected size is [1, 2, 3]
    """
    if len(vector.shape) == 2:
        vector = vector.unsqueeze(0)
    if len(normvector.shape) == 2:
        normvector = normvector.unsqueeze(0)
    mu    = n1 / n2
    div   = normvector[:, 1, 0] ** 2  + normvector[:, 1, 1] ** 2 + normvector[:, 1, 2] ** 2
    a     = mu * (vector[:, 1, 0] * normvector[:, 1, 0] + vector[:, 1, 1] * normvector[:, 1, 1] + vector[:, 1, 2] * normvector[:, 1, 2]) / div
    b     = (mu ** 2 - 1) / div
    to    = - b * 0.5 / a
    num   = 0
    eps   = torch.ones(vector.shape[0], device = vector.device) * error * 2
    while len(eps[eps > error]) > 0:
       num   += 1
       oldto  = to
       v      = to ** 2 + 2 * a * to + b
       deltav = 2 * (to + a)
       to     = to - v / deltav
       eps    = abs(oldto - to)
    output = torch.zeros_like(vector)
    output[:, 0, 0] = normvector[:, 0, 0]
    output[:, 0, 1] = normvector[:, 0, 1]
    output[:, 0, 2] = normvector[:, 0, 2]
    output[:, 1, 0] = mu * vector[:, 1, 0] + to * normvector[:, 1, 0]
    output[:, 1, 1] = mu * vector[:, 1, 1] + to * normvector[:, 1, 1]
    output[:, 1, 2] = mu * vector[:, 1, 2] + to * normvector[:, 1, 2]
    return output

center_of_triangle(triangle)

Definition to calculate center of a triangle.

Parameters:

  • triangle
            An array that contains three points defining a triangle (Mx3). 
            It can also parallel process many triangles (NxMx3).
    

Returns:

  • centers ( tensor ) –

    Triangle centers.

Source code in odak/learn/raytracing/primitives.py
def center_of_triangle(triangle):
    """
    Definition to calculate center of a triangle.

    Parameters
    ----------
    triangle      : torch.tensor
                    An array that contains three points defining a triangle (Mx3). 
                    It can also parallel process many triangles (NxMx3).

    Returns
    -------
    centers       : torch.tensor
                    Triangle centers.
    """
    if len(triangle.shape) == 2:
        triangle = triangle.view((1, 3, 3))
    center = torch.mean(triangle, axis=1)
    return center

define_circle(center, radius, angles)

Definition to describe a circle in a single variable packed form.

Parameters:

  • center
      Center of a circle to be defined in 3D space.
    
  • radius
      Radius of a circle to be defined.
    
  • angles
      Angular tilt of a circle represented by rotations about x, y, and z axes.
    

Returns:

  • circle ( list ) –

    Single variable packed form.

Source code in odak/learn/raytracing/primitives.py
def define_circle(center, radius, angles):
    """
    Definition to describe a circle in a single variable packed form.

    Parameters
    ----------
    center  : torch.Tensor
              Center of a circle to be defined in 3D space.
    radius  : float
              Radius of a circle to be defined.
    angles  : torch.Tensor
              Angular tilt of a circle represented by rotations about x, y, and z axes.

    Returns
    ----------
    circle  : list
              Single variable packed form.
    """
    points = define_plane(center, angles=angles)
    circle = [
        points,
        center,
        torch.tensor([radius])
    ]
    return circle

define_plane(point, angles=torch.tensor([0.0, 0.0, 0.0]))

Definition to generate a rotation matrix along X axis.

Parameters:

  • point
           A point that is at the center of a plane.
    
  • angles
           Rotation angles in degrees.
    

Returns:

  • plane ( tensor ) –

    Points defining plane.

Source code in odak/learn/raytracing/primitives.py
def define_plane(point, angles = torch.tensor([0., 0., 0.])):
    """ 
    Definition to generate a rotation matrix along X axis.

    Parameters
    ----------
    point        : torch.tensor
                   A point that is at the center of a plane.
    angles       : torch.tensor
                   Rotation angles in degrees.

    Returns
    ----------
    plane        : torch.tensor
                   Points defining plane.
    """
    plane = torch.tensor([
                          [10., 10., 0.],
                          [0., 10., 0.],
                          [0.,  0., 0.]
                         ], device = point.device)
    for i in range(0, plane.shape[0]):
        plane[i], _, _, _ = rotate_points(plane[i], angles = angles.to(point.device))
        plane[i] = plane[i] + point
    return plane

define_plane_mesh(number_of_meshes=[10, 10], size=[1.0, 1.0], angles=torch.tensor([0.0, 0.0, 0.0]), offset=torch.tensor([[0.0, 0.0, 0.0]]))

Definition to generate a plane with meshes.

Parameters:

  • number_of_meshes
                Number of squares over plane.
                There are two triangles at each square.
    
  • size
                Size of the plane.
    
  • angles
                Rotation angles in degrees.
    
  • offset
                Offset along XYZ axes.
                Expected dimension is [1 x 3] or offset for each triangle [m x 3].
                m here refers to `2 * number_of_meshes[0]` times  `number_of_meshes[1]`.
    

Returns:

  • triangles ( tensor ) –

    Triangles [m x 3 x 3], where m is 2 * number_of_meshes[0] times number_of_meshes[1].

Source code in odak/learn/raytracing/primitives.py
def define_plane_mesh(
                      number_of_meshes = [10, 10], 
                      size = [1., 1.], 
                      angles = torch.tensor([0., 0., 0.]), 
                      offset = torch.tensor([[0., 0., 0.]])
                     ):
    """
    Definition to generate a plane with meshes.


    Parameters
    -----------
    number_of_meshes  : torch.tensor
                        Number of squares over plane.
                        There are two triangles at each square.
    size              : list
                        Size of the plane.
    angles            : torch.tensor
                        Rotation angles in degrees.
    offset            : torch.tensor
                        Offset along XYZ axes.
                        Expected dimension is [1 x 3] or offset for each triangle [m x 3].
                        m here refers to `2 * number_of_meshes[0]` times  `number_of_meshes[1]`. 

    Returns
    -------
    triangles         : torch.tensor
                        Triangles [m x 3 x 3], where m is `2 * number_of_meshes[0]` times  `number_of_meshes[1]`.
    """
    triangles = torch.zeros(2, number_of_meshes[0], number_of_meshes[1], 3, 3)
    step = [size[0] / number_of_meshes[0], size[1] / number_of_meshes[1]]
    for i in range(0, number_of_meshes[0] - 1):
        for j in range(0, number_of_meshes[1] - 1):
            first_triangle = torch.tensor([
                                           [       -size[0] / 2. + step[0] * i,       -size[1] / 2. + step[0] * j, 0.],
                                           [ -size[0] / 2. + step[0] * (i + 1),       -size[1] / 2. + step[0] * j, 0.],
                                           [       -size[0] / 2. + step[0] * i, -size[1] / 2. + step[0] * (j + 1), 0.]
                                          ])
            second_triangle = torch.tensor([
                                            [ -size[0] / 2. + step[0] * (i + 1), -size[1] / 2. + step[0] * (j + 1), 0.],
                                            [ -size[0] / 2. + step[0] * (i + 1),       -size[1] / 2. + step[0] * j, 0.],
                                            [       -size[0] / 2. + step[0] * i, -size[1] / 2. + step[0] * (j + 1), 0.]
                                           ])
            triangles[0, i, j], _, _, _ = rotate_points(first_triangle, angles = angles)
            triangles[1, i, j], _, _, _ = rotate_points(second_triangle, angles = angles)
    triangles = triangles.view(-1, 3, 3) + offset
    return triangles

define_sphere(center=torch.tensor([[0.0, 0.0, 0.0]]), radius=torch.tensor([1.0]))

Definition to define a sphere.

Parameters:

  • center
          Center of the sphere(s) along XYZ axes.
          Expected size is [3], [1, 3] or [m, 3].
    
  • radius
          Radius of that sphere(s).
          Expected size is [1], [1, 1], [m] or [m, 1].
    

Returns:

  • parameters ( tensor ) –

    Parameters of defined sphere(s). Expected size is [1, 3] or [m x 3].

Source code in odak/learn/raytracing/primitives.py
def define_sphere(center = torch.tensor([[0., 0., 0.]]), radius = torch.tensor([1.])):
    """
    Definition to define a sphere.

    Parameters
    ----------
    center      : torch.tensor
                  Center of the sphere(s) along XYZ axes.
                  Expected size is [3], [1, 3] or [m, 3].
    radius      : torch.tensor
                  Radius of that sphere(s).
                  Expected size is [1], [1, 1], [m] or [m, 1].

    Returns
    -------
    parameters  : torch.tensor
                  Parameters of defined sphere(s).
                  Expected size is [1, 3] or [m x 3].
    """
    if len(radius.shape) == 1:
        radius = radius.unsqueeze(0)
    if len(center.shape) == 1:
        center = center.unsqueeze(1)
    parameters = torch.cat((center, radius), dim = 1)
    return parameters

is_it_on_triangle(point_to_check, triangle)

Definition to check if a given point is inside a triangle. If the given point is inside a defined triangle, this definition returns True. For more details, visit: https://blackpawn.com/texts/pointinpoly/.

Parameters:

  • point_to_check
              Point(s) to check.
              Expected size is [3], [1 x 3] or [m x 3].
    
  • triangle
              Triangle described with three points.
              Expected size is [3 x 3], [1 x 3 x 3] or [m x 3 x3].
    

Returns:

  • result ( tensor ) –

    Is it on a triangle? Returns NaN if condition not satisfied. Expected size is [1] or [m] depending on the input.

Source code in odak/learn/raytracing/primitives.py
def is_it_on_triangle(point_to_check, triangle):
    """
    Definition to check if a given point is inside a triangle. 
    If the given point is inside a defined triangle, this definition returns True.
    For more details, visit: [https://blackpawn.com/texts/pointinpoly/](https://blackpawn.com/texts/pointinpoly/).

    Parameters
    ----------
    point_to_check  : torch.tensor
                      Point(s) to check.
                      Expected size is [3], [1 x 3] or [m x 3].
    triangle        : torch.tensor
                      Triangle described with three points.
                      Expected size is [3 x 3], [1 x 3 x 3] or [m x 3 x3].

    Returns
    -------
    result          : torch.tensor
                      Is it on a triangle? Returns NaN if condition not satisfied.
                      Expected size is [1] or [m] depending on the input.
    """
    if len(point_to_check.shape) == 1:
        point_to_check = point_to_check.unsqueeze(0)
    if len(triangle.shape) == 2:
        triangle = triangle.unsqueeze(0)
    v0 = triangle[:, 2] - triangle[:, 0]
    v1 = triangle[:, 1] - triangle[:, 0]
    v2 = point_to_check - triangle[:, 0]
    if len(v0.shape) == 1:
        v0 = v0.unsqueeze(0)
    if len(v1.shape) == 1:
        v1 = v1.unsqueeze(0)
    if len(v2.shape) == 1:
        v2 = v2.unsqueeze(0)
    dot00 = torch.mm(v0, v0.T)
    dot01 = torch.mm(v0, v1.T)
    dot02 = torch.mm(v0, v2.T) 
    dot11 = torch.mm(v1, v1.T)
    dot12 = torch.mm(v1, v2.T)
    invDenom = 1. / (dot00 * dot11 - dot01 * dot01)
    u = (dot11 * dot02 - dot01 * dot12) * invDenom
    v = (dot00 * dot12 - dot01 * dot02) * invDenom
    result = (u >= 0.) & (v >= 0.) & ((u + v) < 1)
    return result

create_ray(xyz, abg)

Definition to create a ray.

Parameters:

  • xyz
           List that contains X,Y and Z start locations of a ray.
           Size could be [1 x 3], [3], [m x 3].
    
  • abg
           List that contains angles in degrees with respect to the X,Y and Z axes.
           Size could be [1 x 3], [3], [m x 3].
    

Returns:

  • ray ( tensor ) –

    Array that contains starting points and cosines of a created ray. Size will be either [1 x 3] or [m x 3].

Source code in odak/learn/raytracing/ray.py
def create_ray(xyz, abg):
    """
    Definition to create a ray.

    Parameters
    ----------
    xyz          : torch.tensor
                   List that contains X,Y and Z start locations of a ray.
                   Size could be [1 x 3], [3], [m x 3].
    abg          : torch.tensor
                   List that contains angles in degrees with respect to the X,Y and Z axes.
                   Size could be [1 x 3], [3], [m x 3].

    Returns
    ----------
    ray          : torch.tensor
                   Array that contains starting points and cosines of a created ray.
                   Size will be either [1 x 3] or [m x 3].
    """
    points = xyz
    angles = abg
    if len(xyz) == 1:
        points = xyz.unsqueeze(0)
    if len(abg) == 1:
        angles = abg.unsqueeze(0)
    ray = torch.zeros(points.shape[0], 2, 3, device = points.device)
    ray[:, 0] = points
    ray[:, 1] = torch.cos(torch.deg2rad(abg))
    return ray

create_ray_from_two_points(x0y0z0, x1y1z1)

Definition to create a ray from two given points. Note that both inputs must match in shape.

Parameters:

  • x0y0z0
           List that contains X,Y and Z start locations of a ray.
           Size could be [1 x 3], [3], [m x 3].
    
  • x1y1z1
           List that contains X,Y and Z ending locations of a ray or batch of rays.
           Size could be [1 x 3], [3], [m x 3].
    

Returns:

  • ray ( tensor ) –

    Array that contains starting points and cosines of a created ray(s).

Source code in odak/learn/raytracing/ray.py
def create_ray_from_two_points(x0y0z0, x1y1z1):
    """
    Definition to create a ray from two given points. Note that both inputs must match in shape.

    Parameters
    ----------
    x0y0z0       : torch.tensor
                   List that contains X,Y and Z start locations of a ray.
                   Size could be [1 x 3], [3], [m x 3].
    x1y1z1       : torch.tensor
                   List that contains X,Y and Z ending locations of a ray or batch of rays.
                   Size could be [1 x 3], [3], [m x 3].

    Returns
    ----------
    ray          : torch.tensor
                   Array that contains starting points and cosines of a created ray(s).
    """
    if len(x0y0z0.shape) == 1:
        x0y0z0 = x0y0z0.unsqueeze(0)
    if len(x1y1z1.shape) == 1:
        x1y1z1 = x1y1z1.unsqueeze(0)
    xdiff = x1y1z1[:, 0] - x0y0z0[:, 0]
    ydiff = x1y1z1[:, 1] - x0y0z0[:, 1]
    zdiff = x1y1z1[:, 2] - x0y0z0[:, 2]
    s = (xdiff ** 2 + ydiff ** 2 + zdiff ** 2) ** 0.5
    s[s == 0] = float('nan')
    cosines = torch.zeros_like(x0y0z0 * x1y1z1)
    cosines[:, 0] = xdiff / s
    cosines[:, 1] = ydiff / s
    cosines[:, 2] = zdiff / s
    ray = torch.zeros(xdiff.shape[0], 2, 3, device = x0y0z0.device)
    ray[:, 0] = x0y0z0
    ray[:, 1] = cosines
    return ray

propagate_ray(ray, distance)

Definition to propagate a ray at a certain given distance.

Parameters:

  • ray
         A ray with a size of [2 x 3], [1 x 2 x 3] or a batch of rays with [m x 2 x 3].
    
  • distance
         Distance with a size of [1], [1, m] or distances with a size of [m], [1, m].
    

Returns:

  • new_ray ( tensor ) –

    Propagated ray with a size of [1 x 2 x 3] or batch of rays with [m x 2 x 3].

Source code in odak/learn/raytracing/ray.py
def propagate_ray(ray, distance):
    """
    Definition to propagate a ray at a certain given distance.

    Parameters
    ----------
    ray        : torch.tensor
                 A ray with a size of [2 x 3], [1 x 2 x 3] or a batch of rays with [m x 2 x 3].
    distance   : torch.tensor
                 Distance with a size of [1], [1, m] or distances with a size of [m], [1, m].

    Returns
    ----------
    new_ray    : torch.tensor
                 Propagated ray with a size of [1 x 2 x 3] or batch of rays with [m x 2 x 3].
    """
    if len(ray.shape) == 2:
        ray = ray.unsqueeze(0)
    if len(distance.shape) == 2:
        distance = distance.squeeze(-1)
    new_ray = torch.zeros_like(ray)
    new_ray[:, 0, 0] = distance * ray[:, 1, 0] + ray[:, 0, 0]
    new_ray[:, 0, 1] = distance * ray[:, 1, 1] + ray[:, 0, 1]
    new_ray[:, 0, 2] = distance * ray[:, 1, 2] + ray[:, 0, 2]
    return new_ray