예제 #1
0
def test_translate_inertia(translation):
    shape = PlatonicFamily.get_shape("Cube")
    # Choose a volume > 1 to test the volume scaling, but don't choose one
    # that's too large because the uncentered polyhedral calculation has
    # massive error without fixing that.
    shape.volume = 2
    shape.center = (0, 0, 0)

    translated_shape = ConvexPolyhedron(shape.vertices + translation)

    translated_inertia = translate_inertia_tensor(translation,
                                                  shape.inertia_tensor,
                                                  shape.volume)
    mc_tensor = compute_inertia_mc(translated_shape.vertices, 1e4)

    assert np.allclose(
        translated_inertia,
        translated_shape._compute_inertia_tensor(False),
        atol=2e-1,
        rtol=2e-1,
    )
    assert np.allclose(mc_tensor,
                       translated_shape._compute_inertia_tensor(False),
                       atol=2e-1,
                       rtol=2e-1)

    assert np.allclose(mc_tensor, translated_inertia, atol=1e-2, rtol=1e-2)
    assert np.allclose(mc_tensor,
                       translated_shape.inertia_tensor,
                       atol=1e-2,
                       rtol=1e-2)
예제 #2
0
def test_insphere_from_center_convex_hulls(points, test_points):
    hull = ConvexHull(points)
    poly = ConvexPolyhedron(points[hull.vertices])
    insphere = poly.insphere_from_center
    assert poly.is_inside(insphere.center)

    test_points *= insphere.radius * 3
    points_in_sphere = insphere.is_inside(test_points)
    points_in_poly = poly.is_inside(test_points)
    assert np.all(points_in_sphere <= points_in_poly)
    assert insphere.volume < poly.volume
예제 #3
0
def test_rotate_inertia(points):
    # Use the input as noise rather than the base points to avoid precision and
    # degenerate cases provided by hypothesis.
    tet = PlatonicFamily.get_shape("Tetrahedron")
    vertices = tet.vertices + points

    rotation = rowan.random.rand()
    shape = ConvexPolyhedron(vertices)
    rotated_shape = ConvexPolyhedron(rowan.rotate(rotation, vertices))

    mat = rowan.to_matrix(rotation)
    rotated_inertia = rotate_order2_tensor(mat, shape.inertia_tensor)

    assert np.allclose(rotated_inertia, rotated_shape.inertia_tensor)
예제 #4
0
def test_diagonalize_inertia(points):
    """Test that we can orient a polyhedron along its principal axes."""
    hull = ConvexHull(points)
    poly = ConvexPolyhedron(points[hull.vertices])

    try:
        it = poly.inertia_tensor
    except ValueError:
        # Triangulation can fail, this is a limitation of polytri and not something we
        # can address without implementing a more robust algorithm.
        return
    if not np.allclose(np.diag(np.diag(it)), it):
        poly.diagonalize_inertia()
        it = poly.inertia_tensor
        assert np.allclose(np.diag(np.diag(it)), it)
예제 #5
0
def test_volume_damasceno_shapes(shape):
    if shape["name"] in ("RESERVED", "Sphere"):
        return
    vertices = shape["vertices"]
    poly = ConvexPolyhedron(vertices)
    hull = ConvexHull(vertices)
    assert np.isclose(poly.volume, hull.volume)
예제 #6
0
def test_circumsphere_from_center():
    """Validate circumsphere by testing the polyhedron.

    This checks that all points outside this circumsphere are also outside the
    polyhedron. Note that this is a necessary but not sufficient condition for
    correctness.
    """
    # Building convex polyhedra is the slowest part of this test, so rather
    # than testing all the shapes from this particular dataset every time we
    # instead test a random subset each time the test runs. To further speed
    # the tests, we build all convex polyhedra ahead of time. Each set of
    # random points is tested against a different random polyhedron.
    import random

    family = DOI_SHAPE_REPOSITORIES["10.1126/science.1220869"][0]
    shapes = [
        ConvexPolyhedron(s["vertices"]) for s in random.sample(
            [s for s in family.data.values() if len(s["vertices"])],
            len(family.data) // 5,
        )
    ]

    # Use a nested function to avoid warnings from hypothesis. While the shape
    # does get modified inside the testfun, it's simply being recentered each
    # time, which is not destructive since it can be overwritten in subsequent
    # calls.
    # See https://github.com/HypothesisWorks/hypothesis/issues/377
    @given(
        center=arrays(np.float64, (3, ),
                      elements=floats(-10, 10, width=64),
                      unique=True),
        points=arrays(np.float64, (50, 3),
                      elements=floats(-1, 1, width=64),
                      unique=True),
        shape_index=integers(0,
                             len(shapes) - 1),
    )
    def testfun(center, points, shape_index):
        poly = shapes[shape_index]
        poly.center = center

        sphere = poly.circumsphere_from_center
        scaled_points = points * sphere.radius + sphere.center
        points_outside = np.logical_not(sphere.is_inside(scaled_points))

        # Verify that all points outside the circumsphere are also outside the
        # polyhedron.
        assert not np.any(
            np.logical_and(points_outside, poly.is_inside(scaled_points)))

    testfun()
예제 #7
0
def test_moment_inertia_damasceno_shapes(shape):
    # These shapes pass the test for a sufficiently high number of samples, but
    # the number is too high to be worth running them regularly.
    bad_shapes = [
        "Augmented Truncated Dodecahedron",
        "Deltoidal Hexecontahedron",
        "Disdyakis Triacontahedron",
        "Truncated Dodecahedron",
        "Truncated Icosidodecahedron",
        "Metabiaugmented Truncated Dodecahedron",
        "Pentagonal Hexecontahedron",
        "Paragyrate Diminished Rhombicosidodecahedron",
        "Square Cupola",
        "Triaugmented Truncated Dodecahedron",
        "Parabiaugmented Truncated Dodecahedron",
    ]
    if shape["name"] in ["RESERVED", "Sphere"] + bad_shapes:
        return

    np.random.seed(0)
    poly = ConvexPolyhedron(shape["vertices"])
    num_samples = 1000
    accept = False
    # Loop over different sampling rates to minimize the test runtime.
    while num_samples < 1e8:
        try:
            coxeter_result = poly.inertia_tensor
            mc_result = compute_inertia_mc(shape["vertices"], num_samples)
            assert np.allclose(coxeter_result, mc_result, atol=1e-1)
            accept = True
            break
        except AssertionError:
            num_samples *= 10
            continue
    if not accept:
        raise AssertionError("The test failed for shape {}.\nMC Result: "
                             "\n{}\ncoxeter result: \n{}".format(
                                 shape["name"], mc_result, coxeter_result))
예제 #8
0
def test_convex_surface_area(points):
    """Check the surface areas of various convex sets."""
    hull = ConvexHull(points)
    poly = ConvexPolyhedron(points[hull.vertices])
    assert np.isclose(hull.area, poly.surface_area)
예제 #9
0
def test_convex_volume(points):
    """Check the volumes of various convex sets."""
    hull = ConvexHull(points)
    poly = ConvexPolyhedron(hull.points[hull.vertices])
    assert np.isclose(hull.volume, poly.volume)