Beispiel #1
0
    def line_segment_intersection(self, line_start, line_end):
        """Find the intersection point with a line segment. Inspired by
        https://math.stackexchange.com/q/47594/672781.

        Parameters
        ----------
        line_start : list or ndarray of length 3
            Starting point of the line segment.
        line_end : list or ndarray of length 3
            Terminating point of the line segment.

        Returns
        -------
        intersection : bool or ndarray of size 3
            If True, the line segment lies in the plane. If False, the line
            segment does not intersect the plane. Otherwise, the point of
            intersection is returned as an array.

        """

        ls = validate_3d_vector(line_start)
        le = validate_3d_vector(line_end)

        # Get perpendicular distances of line points from plane:
        ds = np.dot(self.normal_vector, ls - self.point)
        de = np.dot(self.normal_vector, le - self.point)

        if np.isclose(abs(ds) + abs(de), 0):
            # Line segment lies in plane
            intersection = True

        elif ds * de > 0:
            # Line segment does not intersect plane
            intersection = False

        elif ds * de <= 0:
            # Line segment intersects plane (or starts/ends in plane)

            # Get unit vector along line segment:
            line = le - ls
            lu = line / np.linalg.norm(line)
            cos_theta = np.dot(self.normal_vector, lu)

            # If cos_theta == 0, the line segment is parallel to the plane,
            # in which case, it either lies in the plane (returned above),
            # or it does not intersect the plane (returned above).

            intersection = le - (lu * (de / cos_theta))

        return intersection
Beispiel #2
0
def camera_transform(look_at, up, look_from=None):
    """Find the rotation matrix to rotate the model objects into camera space.

    Parameters
    ----------
    look_at : 
        Direction the camera is pointing in.
    up : 
        Orientation of the camera about the `look_at` vector.
    look_from : 
        Position of the camera.

    Returns
    -------
    mat : ndarray of shape (4, 4)

    """

    if look_from is None:
        look_from = [0, 0, 0]

    look_at = validate_3d_vector(look_at)
    up = validate_3d_vector(up)
    look_from = validate_3d_vector(look_from)

    # construct an orthonormal basis:
    u_x = np.cross(look_at, up)
    # (`look_at` and `up` may not be perpendicular)
    u_y = np.cross(u_x, look_at)

    # Normalise:
    u_x = u_x / np.linalg.norm(u_x)
    u_y = u_y / np.linalg.norm(u_y)
    u_z = -look_at / np.linalg.norm(look_at)

    mat = np.vstack([u_x, u_y, u_z])

    trans = np.vstack([
        np.dot(u_x, -look_from),
        np.dot(u_y, -look_from),
        np.dot(u_z, -look_from),
    ])

    mat = np.vstack([
        np.hstack([mat, trans]),
        [0, 0, 0, 1]
    ])

    return mat
Beispiel #3
0
    def from_bounding_box(cls, geometry_group, look_at, up, width=None, height=None,
                          depth=None, camera_translate=None):
        """Create the camera from the bounding box of a geometry group.

        Parameters
        ----------
        geometry_group : GeometryGroup
            The geometry group whose bounding box is used to generate the camera.

        """

        # Find the correct `look_from` and frustum dimensions. Since the geometry group
        # centroid is axis aligned, it will change as the geometry group is rotated,
        # so we need the centroid of the rotation geometry group to be the `look_from`
        # vector.
        cam1 = camera_transform(look_at, up)
        cam1_inv = np.linalg.inv(cam1)
        geometry_group = copy.deepcopy(geometry_group)
        geometry_group.transform(cam1[:3, :3])
        look_from = (cam1_inv @ np.append(geometry_group.centroid, [0])[:, None])[:3, 0]
        geometries = {
            'points': geometry_group.points,
            'boxes': geometry_group.boxes,
            'lines': geometry_group.lines,
        }
        extrema = get_overall_geometry_extrema(geometries)
        dims = extrema[:, 1] - extrema[:, 0]

        if not width:
            width = dims[0]
        if not height:
            height = dims[1]
        if not depth:
            depth = dims[2]

        cam2 = camera_transform(look_at, up, look_from)
        cam2_inv = np.linalg.inv(cam2)

        # Translate `look_from` away from the centroid, towards the near plane of the
        # bounding box:
        depth_translate = (cam2_inv @ np.array([[0, 0, depth / 2, 1]]).T)[:3, 0]
        depth_translate = depth_translate - look_from
        new_look_from = np.copy(look_from) + depth_translate

        if camera_translate is not None:
            camera_translate = validate_3d_vector(camera_translate).astype(float)
            translate = (cam2_inv @ np.append(camera_translate, [0])[:, None])[:3, 0]
            new_look_from += translate

        camera = cls(
            look_at,
            up,
            width=width,
            height=height,
            depth=depth,
            look_from=new_look_from,
        )

        return camera
Beispiel #4
0
 def _validate_point(self, point):
     return validate_3d_vector(point)
Beispiel #5
0
 def _validate_normal(self, normal):
     normal = validate_3d_vector(normal)
     normal = normal / np.linalg.norm(normal)
     return normal
Beispiel #6
0
    def __init__(self, look_at, up, look_from=None):

        self.look_at = validate_3d_vector(look_at).astype(float)
        self.look_from = validate_3d_vector(look_from).astype(float)
        self.up = validate_3d_vector(up).astype(float)