Ejemplo n.º 1
0
def percentile(points, axis, percentile):
    """
    Given a cloud of points and an axis, find a point along that axis
    from the centroid at the given percentile.

    Args:
        points (np.arraylike): A `kx3` stack of points.
        axis (np.arraylike): A 3D vector specifying the direction of
            interest.
        percentile (float): The desired percentile.

    Returns:
        np.ndarray: A 3D point at the requested percentile.
    """
    k = vg.shape.check(locals(), "points", (-1, 3))
    if k < 1:
        raise ValueError("At least one point is needed")
    vg.shape.check(locals(), "axis", (3,))
    if vg.almost_zero(axis):
        raise ValueError("Axis must be non-zero")

    axis = vg.normalize(axis)
    coords_on_axis = points.dot(axis)
    selected_coord_on_axis = np.percentile(coords_on_axis, percentile)
    centroid = np.average(points, axis=0)
    return vg.reject(centroid, axis) + selected_coord_on_axis * axis
Ejemplo n.º 2
0
    def point_along_path(self, fraction_of_total):
        """
        Selects a point the given fraction of the total length of the polyline. For
        example, to find the halfway point, pass `fraction_of_total=0.5`. Also works
        with stacked values, e.g. `fraction_of_total=np.linspace(0, 1, 11)`.

        Args:
            fraction_of_total (object): Fraction of the total length, from 0 to 1

        Returns (object):
            A point on the polyline that is the given fraction of the total length
            from the starting point to the endpoint. For stacked fractions, return
            the points.
        """
        from .._common.shape import columnize

        fraction_of_total, _, transform_result = columnize(
            fraction_of_total, (-1, ), name="fraction_of_total")

        if np.any(0 > fraction_of_total) or np.any(fraction_of_total > 1):
            raise ValueError(
                "fraction_of_total must be a value between 0 and 1")

        desired_length = self.total_length * fraction_of_total
        cumulative_length = np.cumsum([0, *self.segment_lengths])
        index_of_segment = (np.argmax(
            cumulative_length.reshape(-1, 1) > desired_length, axis=0) - 1)

        return transform_result(
            self.v[index_of_segment] +
            (desired_length -
             cumulative_length[index_of_segment]).reshape(-1, 1) *
            vg.normalize(self.segment_vectors[index_of_segment]))
Ejemplo n.º 3
0
def test_project_point_to_line_stacked_both():
    p1 = np.array([5.0, 5.0, 4.0])
    p2 = np.array([10.0, 10.0, 6.0])
    along_line = p2 - p1

    common_kwargs = dict(
        reference_points_of_lines=np.array([p1, p1, p1]),
        vectors_along_lines=np.array([along_line, along_line, along_line]),
    )

    other_point_on_line = np.array([0.0, 0.0, 2.0])

    example_perpendicular_displacement = [
        k * vg.perpendicular(vg.normalize(along_line), vg.basis.x)
        for k in [0.1, 0.5, -2.0]
    ]

    example_points = np.vstack([p1, p2, other_point_on_line])
    expected_projected_points = np.vstack([p1, p2, other_point_on_line])

    np.testing.assert_array_almost_equal(
        project_point_to_line(points=example_points, **common_kwargs),
        expected_projected_points,
    )
    np.testing.assert_array_almost_equal(
        project_point_to_line(points=example_points +
                              example_perpendicular_displacement,
                              **common_kwargs),
        expected_projected_points,
    )
Ejemplo n.º 4
0
def test_project_point_to_line():
    p1 = np.array([5.0, 5.0, 4.0])
    p2 = np.array([10.0, 10.0, 6.0])
    along_line = p2 - p1

    common_kwargs = dict(reference_points_of_lines=p1,
                         vectors_along_lines=along_line)

    np.testing.assert_array_almost_equal(
        project_point_to_line(points=p1, **common_kwargs), p1)
    np.testing.assert_array_almost_equal(
        project_point_to_line(points=p2, **common_kwargs), p2)

    other_point_on_line = np.array([0.0, 0.0, 2.0])
    np.testing.assert_array_almost_equal(
        project_point_to_line(points=other_point_on_line, **common_kwargs),
        other_point_on_line,
    )

    example_perpendicular_displacement = [
        k * vg.perpendicular(vg.normalize(along_line), vg.basis.x)
        for k in [0.1, 0.5, -2.0]
    ]
    for point_on_line in [p1, p2, other_point_on_line]:
        for displacement in example_perpendicular_displacement:
            np.testing.assert_array_almost_equal(
                project_point_to_line(points=point_on_line + displacement,
                                      **common_kwargs),
                point_on_line,
            )
Ejemplo n.º 5
0
    def __init__(self, point_on_plane, unit_normal):
        vg.shape.check(locals(), "point_on_plane", (3, ))
        vg.shape.check(locals(), "unit_normal", (3, ))

        if vg.almost_zero(unit_normal):
            raise ValueError("unit_normal should not be the zero vector")

        unit_normal = vg.normalize(unit_normal)

        self._r0 = np.asarray(point_on_plane)
        self._n = np.asarray(unit_normal)
Ejemplo n.º 6
0
def test_signed_distances_for_diagonal_plane():
    np.testing.assert_array_almost_equal(
        signed_distance_to_plane(
            points=np.array([[425.0, 425.0, 25.0], [-500.0, -500.0, 25.0]]),
            # Diagonal plane @ origin - draw a picture!
            plane_equations=Plane(
                point_on_plane=np.array([1.0, 1.0, 0.0]),
                unit_normal=vg.normalize(np.array([1.0, 1.0, 0.0])),
            ).equation,
        ),
        np.array([
            math.sqrt(2 * (425.0 - 1.0)**2), -math.sqrt(2 * (500.0 + 1.0)**2)
        ]),
    )
Ejemplo n.º 7
0
def world_to_view(position, target, up=vg.basis.y, inverse=False):
    """
    Create a transform matrix which sends world-space coordinates to
    view-space coordinates.

    Args:
        position (np.ndarray): The camera's position in world coordinates.
        target (np.ndarray): The camera's target in world coordinates.
            `target - position` is the "look at" vector.
        up (np.ndarray): The approximate up direction, in world coordinates.
        inverse (bool): When `True`, return the inverse transform instead.

    Returns:
        np.ndarray: The `4x4` transformation matrix, which can be used with
        `polliwog.transform.apply_transform()`.

    See also:
        https://cseweb.ucsd.edu/classes/wi18/cse167-a/lec4.pdf
        http://www.songho.ca/opengl/gl_camera.html
    """
    vg.shape.check(locals(), "position", (3, ))
    vg.shape.check(locals(), "target", (3, ))

    look = vg.normalize(target - position)
    left = vg.normalize(vg.cross(look, up))
    recomputed_up = vg.cross(left, look)

    rotation = transform_matrix_for_rotation(
        np.array([left, recomputed_up, look]))
    if inverse:
        inverse_rotation = rotation.T
        inverse_translation = transform_matrix_for_translation(position)
        return compose_transforms(inverse_rotation, inverse_translation)
    else:
        translation = transform_matrix_for_translation(-position)
        return compose_transforms(translation, rotation)
Ejemplo n.º 8
0
def test_sliced_by_plane_open():
    original = Polyline(
        np.array([
            [0.0, 0.0, 0.0],
            [1.0, 0.0, 0.0],
            [1.0, 1.0, 0.0],
            [1.0, 7.0, 0.0],
            [1.0, 8.0, 0.0],
        ]),
        is_closed=False,
    )

    expected_vs = np.array([[1.0, 7.5, 0.0], [1.0, 8.0, 0.0]])
    actual = original.sliced_by_plane(
        Plane(point_on_plane=np.array([0.0, 7.5, 0.0]),
              unit_normal=vg.basis.y))

    np.testing.assert_array_almost_equal(actual.v, expected_vs)
    assert actual.is_closed is False

    expected_vs = np.array([
        [0.0, 0.0, 0.0],
        [1.0, 0.0, 0.0],
        [1.0, 1.0, 0.0],
        [1.0, 7.0, 0.0],
        [1.0, 7.5, 0.0],
    ])
    actual = original.sliced_by_plane(
        Plane(point_on_plane=np.array([0.0, 7.5, 0.0]),
              unit_normal=vg.basis.neg_y))

    np.testing.assert_array_almost_equal(actual.v, expected_vs)
    assert actual.is_closed is False

    with pytest.raises(ValueError):
        original.sliced_by_plane(
            Plane(point_on_plane=np.array([0.0, 15.0, 0.0]),
                  unit_normal=vg.basis.neg_y))

    actual = original.sliced_by_plane(
        Plane(
            point_on_plane=np.array([0.5, 0.0, 0.0]),
            unit_normal=vg.normalize(np.array([1.0, -1.0, 0.0])),
        ))
    expected_vs = np.array([[0.5, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 0.5, 0.0]])
    np.testing.assert_array_almost_equal(actual.v, expected_vs)
    assert actual.is_closed is False
Ejemplo n.º 9
0
def surface_normals(points, normalize=True):
    """
    Compute the surface normal of a triangle. The direction of the normal
    follows conventional counter-clockwise winding and the right-hand
    rule.

    Also works on stacked inputs (i.e. many sets of three points).
    """
    points, _, transform_result = columnize(points, (-1, 3, 3), name="points")

    p1s = points[:, 0]
    p2s = points[:, 1]
    p3s = points[:, 2]
    v1s = p2s - p1s
    v2s = p3s - p1s
    normals = vg.cross(v1s, v2s)

    if normalize:
        normals = vg.normalize(normals)

    return transform_result(normals)
Ejemplo n.º 10
0
def test_surface_normals_from_points_vectorized():
    from polliwog.shapes import triangular_prism

    p1 = np.array([3.0, 0.0, 0.0])
    p2 = np.array([0.0, 3.0, 0.0])
    p3 = np.array([0.0, 0.0, 3.0])
    vertices = triangular_prism(p1, p2, p3, 1.0)

    expected_normals = vg.normalize(
        np.array(
            [
                [1.0, 1.0, 1.0],
                [1.0, 1.0, -2.0],
                [1.0, 1.0, -2.0],
                [-2.0, 1.0, 1.0],
                [-2.0, 1.0, 1.0],
                [1.0, -2.0, 1.0],
                [1.0, -2.0, 1.0],
                [-1.0, -1.0, -1.0],
            ]
        )
    )

    np.testing.assert_allclose(surface_normals(vertices), expected_normals)
Ejemplo n.º 11
0
import numpy as np
from polliwog import Plane
import pytest
from vg.compat import v2 as vg
from ._slice_by_plane import slice_open_polyline_by_plane

point_on_plane = np.array([1.0, 2.0, 3.0])
plane_normal = vg.normalize(np.array([3.0, 4.0, 5.0]))
plane = Plane(point_on_plane=point_on_plane, unit_normal=plane_normal)


def rand_nonzero(*shape):
    return 128 * np.random.rand(*shape) + 1e-6


def vertices_with_signs(signs):
    num_verts = len(signs)
    random_points_on_plane = plane.project_point(rand_nonzero(num_verts, 3))
    random_displacement_along_normal = (
        rand_nonzero(num_verts).reshape(-1, 1) * plane_normal)
    vertices = (random_points_on_plane +
                signs.reshape(-1, 1) * random_displacement_along_normal)
    # Because of rounding, the random points don't necessarily return 0 for
    # sign, so pick one that does.
    vertices[signs == 0] = plane.reference_point
    np.testing.assert_array_equal(plane.sign(vertices), signs)
    return vertices


def intersect_segment_with_plane(p1, p2):
    from ..plane import intersect_segment_with_plane as _intersect_segment_with_plane
Ejemplo n.º 12
0
def test_v2_has_functions():
    np.testing.assert_array_equal(vg.normalize(np.array([5, 0, 0])),
                                  np.array([1, 0, 0]))