예제 #1
0
파일: meshes.py 프로젝트: ryancoe/capytaine
 def keep_immersed_part(self, free_surface=0.0, sea_bottom=-np.infty):
     """Clip the mesh with two horizontal planes corresponding
     with the free surface and the sea bottom."""
     self.clip(Plane(normal=(0, 0, 1), point=(0, 0, free_surface)))
     if sea_bottom > -np.infty:
         self.clip(Plane(normal=(0, 0, -1), point=(0, 0, sea_bottom)))
     return self
예제 #2
0
    def minced(self, nb_slices=(8, 8, 4)):
        """Experimental method decomposing the mesh as a hierarchical structure.

        Parameters
        ----------
        nb_slices: Tuple[int, int, int]
            The number of slices in each of the x, y and z directions.
            Only powers of 2 are supported at the moment.

        Returns
        -------
        FloatingBody
        """
        minced_body = self.copy()

        # Extreme points of the mesh in each directions.
        x_min, x_max, y_min, y_max, z_min, z_max = self.mesh.axis_aligned_bbox
        sizes = [(x_min, x_max), (y_min, y_max), (z_min, z_max)]

        directions = [np.array(d) for d in [(1, 0, 0), (0, 1, 0), (0, 0, 1)]]

        def _slice_positions_at_depth(i):
            """Helper function.

            Returns a list of floats as follows:
            i=1 -> [1/2]
            i=2 -> [1/4, 3/4]
            i=3 -> [1/8, 3/8, 5/8, 7/8]
                   ...
            """
            denominator = 2**i
            return [
                numerator / denominator
                for numerator in range(1, denominator, 2)
            ]

        # GENERATE ALL THE PLANES THAT WILL BE USED TO MINCE THE MESH
        planes = []
        for direction, nb_slices_in_dir, (min_coord, max_coord) in zip(
                directions, nb_slices, sizes):
            planes_in_dir = []

            depth_of_treelike_structure = int(np.log2(nb_slices_in_dir))
            for i_depth in range(1, depth_of_treelike_structure + 1):
                planes_in_dir_at_depth = []
                for relative_position in _slice_positions_at_depth(i_depth):
                    slice_position = (min_coord + relative_position *
                                      (max_coord - min_coord)) * direction
                    plane = Plane(normal=direction, point=slice_position)
                    planes_in_dir_at_depth.append(plane)
                planes_in_dir.append(planes_in_dir_at_depth)
            planes.append(planes_in_dir)

        # SLICE THE MESH
        intermingled_x_y_z = chain.from_iterable(zip_longest(*planes))
        for planes in intermingled_x_y_z:
            if planes is not None:
                for plane in planes:
                    minced_body = minced_body.sliced_by_plane(plane)
        return minced_body
예제 #3
0
def test_bodies():
    body = Sphere(name="sphere", clever=False)
    assert str(body) == "sphere"
    repr(body)
    assert np.allclose(body.geometric_center, (0, 0, 0))
    body.add_translation_dof(name="Surge")
    body.add_translation_dof(name="Heave")

    # Extract faces
    body.extract_faces(np.where(body.mesh.faces_centers[:, 2] < 0)[0])

    # Clipping
    body.keep_immersed_part(inplace=False)

    # Mirror of the dofs
    mirrored = body.mirrored(Plane(point=(1, 0, 0), normal=(1, 0, 0)))
    assert np.allclose(mirrored.geometric_center, np.array([2, 0, 0]))
    assert np.allclose(body.dofs['Surge'], -mirrored.dofs['Surge'])

    # Rotation of the dofs
    sideways = body.rotated(Axis(point=(0, 0, 0), vector=(0, 1, 0)), np.pi/2)
    assert np.allclose(sideways.dofs['Heave'][0], np.array([1, 0, 0]))

    upside_down = body.rotated(Axis(point=(0, 0, 0), vector=(0, 1, 0)), np.pi)
    assert np.allclose(body.dofs['Heave'], -upside_down.dofs['Heave'])

    # Copy of the body
    copy_of_body = body.copy(name="copy_of_sphere")
    copy_of_body.translate_x(10.0)
    copy_of_body.add_translation_dof(name="Heave")

    # Join bodies
    both = body.join_bodies(copy_of_body)
    assert set(both.dofs) == {'sphere__Surge', 'copy_of_sphere__Surge', 'sphere__Heave', 'copy_of_sphere__Heave'}
예제 #4
0
def test_clipper_corner_cases():
    mesh = sphere.translated_z(10.0)

    plane = Plane(point=(0, 0, 0), normal=(0, 0, 1))
    clipped_mesh = mesh.clip(plane, inplace=False)
    assert clipped_mesh == Mesh(None, None)  # Empty mesh

    plane = Plane(point=(0, 0, 0), normal=(0, 0, -1))
    clipped_mesh = mesh.clip(plane, inplace=False)
    assert clipped_mesh == mesh  # Unchanged mesh

    # Two distinct bodies
    two_spheres = Mesh.join_meshes(sphere.translated_z(10.0),
                                   sphere.translated_z(-10.0))
    plane = Plane(point=(0, 0, 0), normal=(0, 0, -1))
    one_sphere_remaining = two_spheres.clip(plane, inplace=False)
    assert one_sphere_remaining == sphere.translated_z(10.0)
def test_plane():
    assert (0, 1, 1) in yOz_Plane
    assert Oy_axis in yOz_Plane

    assert np.allclose(Plane(normal=(1, 1, 0)).normal, (np.sqrt(2)/2, np.sqrt(2)/2, 0))
    assert yOz_Plane == Plane(point=(0, 1, 1), normal=(2, 0, 0))

    assert xOy_Plane.is_orthogonal_to(Oz_axis)

    points_in_xplus = np.random.rand(10, 3) + np.array([1.0, -0.5, -0.5])
    assert np.all(yOz_Plane.distance_to_point(points_in_xplus) > 0)
    assert np.all(yOz_Plane.translated_x(-5.0).distance_to_point(points_in_xplus) > 0)
    assert not np.any(yOz_Plane.translated_x(5.0).distance_to_point(points_in_xplus) > 0)

    points_in_xminus = np.random.rand(10, 3) + np.array([-2.0, -0.5, -0.5])
    assert np.all(yOz_Plane.distance_to_point(points_in_xminus) < 0)
    assert not np.any(yOz_Plane.translated_x(-5.0).distance_to_point(points_in_xminus) < 0)
    assert np.all(yOz_Plane.translated_x(5.0).distance_to_point(points_in_xminus) < 0)
예제 #6
0
 def sliced_by_plane(self, plane: Plane):
     from capytaine.meshes.collections import CollectionOfMeshes
     faces_ids_on_one_side = np.where(plane.distance_to_point(self.faces_centers) < 0)[0]
     if len(faces_ids_on_one_side) == 0 or len(faces_ids_on_one_side) == self.nb_faces:
         return self.copy()
     else:
         mesh_part_1 = self.extract_faces(faces_ids_on_one_side)
         mesh_part_2 = self.extract_faces(list(set(range(self.nb_faces)) - set(faces_ids_on_one_side)))
         return CollectionOfMeshes([mesh_part_1, mesh_part_2],
                                   name=f"{self.name}_splitted_by_{plane}")
예제 #7
0
def test_clipper_indices(size):
    """Test clipped_mesh_faces_ids."""
    mesh = Rectangle(size=(size, size),
                     resolution=(size, size),
                     center=(0, 0, 0)).mesh.merged()
    clipped_mesh = clip(mesh, plane=Plane(point=(0, 0, 0), normal=(0, 0, 1)))
    faces_ids = clipped_mesh._clipping_data['faces_ids']

    assert clipped_mesh.nb_faces == len(faces_ids)
    assert all(
        norm(clipped_mesh.faces_centers[i] - mesh.faces_centers[face_id]) < 0.3
        for i, face_id in enumerate(faces_ids))
예제 #8
0
def test_plane_transformations():
    # TRANSLATIONS
    translated_plane = xOz_Plane.translate(vector=(1, 0, 0), inplace=False)
    assert xOz_Plane is not translated_plane
    assert xOz_Plane == translated_plane

    assert yOz_Plane.translated_x(10).rotated_y(np.pi / 8).c == 10

    translated_plane = xOz_Plane.translate(vector=(0, 1, 0), inplace=False)
    assert translated_plane.c == 1
    assert np.all(translated_plane.normal == xOz_Plane.normal)

    # ROTATIONS
    rotated_plane = xOz_Plane.rotate(Oy_axis, angle=np.pi / 12, inplace=False)
    assert rotated_plane == xOz_Plane.rotated(Oy_axis, angle=np.pi / 12)
    assert xOz_Plane is not rotated_plane
    assert xOz_Plane == rotated_plane

    rotated_plane = xOz_Plane.rotate(Ox_axis, angle=np.pi / 2, inplace=False)
    assert rotated_plane == xOy_Plane

    # MIRRORED BY ITSELF
    plane = Plane(normal=(1, 0, 0), point=(0.3, 0.2, 0.6))
    assert plane.mirrored(plane) != plane
    assert plane.mirrored(plane) == Plane(normal=(-1, 0, 0),
                                          point=(0.3, 0.2, 0.6))

    flipped_plane = plane.rotate(Axis(point=plane.point, vector=(0, 1, 0)),
                                 np.pi)
    assert flipped_plane == plane.mirror(plane)
예제 #9
0
    def __init__(self, half: Union[Mesh, CollectionOfMeshes], plane: Plane, name=None):
        assert isinstance(half, Mesh) or isinstance(half, CollectionOfMeshes)
        assert isinstance(plane, Plane)
        assert plane.normal[2] == 0, "Only vertical reflection planes are supported in ReflectionSymmetry classes."

        other_half = half.mirrored(plane, name=f"mirrored_of_{str(half)}")

        super().__init__((half, other_half), name=name)

        self.plane = plane.copy()

        if self.name is not None:
            LOG.debug(f"New mirror symmetric mesh: {self.name}.")
        else:
            LOG.debug(f"New mirror symmetric mesh.")
예제 #10
0
 def keep_immersed_part(self, free_surface=0.0, sea_bottom=-np.infty):
     """Remove the parts of the mesh above the sea bottom and below the free surface."""
     self.clip(Plane(normal=(0, 0, 1), point=(0, 0, free_surface)))
     if sea_bottom > -np.infty:
         self.clip(Plane(normal=(0, 0, -1), point=(0, 0, sea_bottom)))
     return self
예제 #11
0
def test_mirror():
    new_cylinder = cylinder.mirrored(Plane())
    cylinder.mirror(Plane())
    assert new_cylinder == cylinder