def test_polyface3d_init_from_faces_tolerance():
    """Test the initialization of Polyface3D from_faces with a tolerance."""
    pts_1 = [Point3D(0, 0, 0), Point3D(0, 2, 0), Point3D(2, 2, 0), Point3D(2, 0, 0)]
    pts_2 = [Point3D(0, 0, 0), Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(0, 2, 0)]
    pts_3 = [Point3D(0, 0, 0), Point3D(2, 0, 0), Point3D(2, 0, 2), Point3D(0, 0, 2)]
    pts_4 = [Point3D(2, 2, 0), Point3D(0, 2, 0), Point3D(0, 2, 2), Point3D(2, 2, 2)]
    pts_5 = [Point3D(2, 2, 0), Point3D(2, 0, 0), Point3D(2, 0, 2), Point3D(2, 2, 2)]
    pts_6 = [Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 0, 2)]
    pts_7 = [Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 0, 2.0001)]
    face_1 = Face3D(pts_1)
    face_2 = Face3D(pts_2)
    face_3 = Face3D(pts_3)
    face_4 = Face3D(pts_4)
    face_5 = Face3D(pts_5)
    face_6 = Face3D(pts_6)
    face_7 = Face3D(pts_7)
    polyface_1 = Polyface3D.from_faces(
        [face_1, face_2, face_3, face_4, face_5, face_6], 0.001)
    polyface_2 = Polyface3D.from_faces(
        [face_1, face_2, face_3, face_4, face_5, face_7], 0.001)
    polyface_3 = Polyface3D.from_faces(
        [face_1, face_2, face_3, face_4, face_5, face_7], 0.000001)

    assert polyface_1.is_solid
    assert polyface_2.is_solid
    assert not polyface_3.is_solid
def test_polyface3d_init_from_faces_coplanar():
    """Test the initialization of Polyface3D from_faces with two coplanar faces."""
    # this is an important case that must be solved
    # can be done by iterating through naked edges and finding colinear ones
    pts_1 = [Point3D(0, 0, 0), Point3D(0, 2, 0), Point3D(2, 2, 0), Point3D(2, 0, 0)]
    pts_2 = [Point3D(0, 0, 0), Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(0, 2, 0)]
    pts_3 = [Point3D(0, 0, 0), Point3D(2, 0, 0), Point3D(2, 0, 2), Point3D(0, 0, 2)]
    pts_4 = [Point3D(2, 2, 0), Point3D(0, 2, 0), Point3D(0, 2, 2), Point3D(2, 2, 2)]
    pts_5 = [Point3D(2, 2, 0), Point3D(2, 0, 0), Point3D(2, 0, 2), Point3D(2, 2, 2)]
    pts_6 = [Point3D(0, 0, 2), Point3D(0, 1, 2), Point3D(2, 1, 2), Point3D(2, 0, 2)]
    pts_7 = [Point3D(0, 1, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 1, 2)]
    face_1 = Face3D(pts_1)
    face_2 = Face3D(pts_2)
    face_3 = Face3D(pts_3)
    face_4 = Face3D(pts_4)
    face_5 = Face3D(pts_5)
    face_6 = Face3D(pts_6)
    face_7 = Face3D(pts_7)
    polyface = Polyface3D.from_faces(
        [face_1, face_2, face_3, face_4, face_5, face_6, face_7], 0.01)
    polyface_2 = Polyface3D.from_faces(
        [face_1, face_2, face_3, face_4, face_5, face_7], 0.01)

    assert not polyface.is_solid
    assert len(polyface.naked_edges) != 0

    new_polyface = polyface.merge_overlapping_edges(0.0001, 0.0001)
    assert new_polyface.is_solid
    assert len(new_polyface.naked_edges) == 0
    assert len(new_polyface.internal_edges) == 13

    new_polyface_2 = polyface_2.merge_overlapping_edges(0.0001, 0.0001)
    assert not new_polyface_2.is_solid
    assert len(new_polyface_2.naked_edges) != 0
Esempio n. 3
0
    def __init__(self, identifier, faces, tolerance=0, angle_tolerance=0):
        """Initialize Room."""
        _BaseWithShade.__init__(self, identifier)  # process the identifier

        # process the zone volume geometry
        if not isinstance(faces, tuple):
            faces = tuple(faces)
        for face in faces:
            assert isinstance(face, Face), \
                'Expected honeybee Face. Got {}'.format(type(face))
            face._parent = self

        if tolerance == 0:
            self._faces = faces
            self._geometry = None  # calculated later from faces or added by classmethods
        else:
            # try to get a closed volume between the faces
            room_polyface = Polyface3D.from_faces(
                tuple(face.geometry for face in faces), tolerance)
            if not room_polyface.is_solid and angle_tolerance != 0:
                ang_tol = math.radians(angle_tolerance)
                room_polyface = room_polyface.merge_overlapping_edges(
                    tolerance, ang_tol)
            # replace honeybee face geometry with versions that are facing outwards
            if room_polyface.is_solid:
                for i, correct_face3d in enumerate(room_polyface.faces):
                    faces[i]._geometry = correct_face3d
            self._faces = faces
            self._geometry = room_polyface

        self._multiplier = 1  # default value that can be overridden later
        self._story = None  # default value that can be overridden later
        self._properties = RoomProperties(self)  # properties for extensions
Esempio n. 4
0
    def check_solid(self,
                    tolerance=0.01,
                    angle_tolerance=1,
                    raise_exception=True):
        """Check whether the Room is a closed solid to within the input tolerances.

        Args:
            tolerance: tolerance: The maximum difference between x, y, and z values
                at which face vertices are considered equivalent. This is used in
                determining whether the faces form a closed volume. Default: 0.01,
                suitable for objects in meters.
            angle_tolerance: The max angle difference in degrees that vertices are
                allowed to differ from one another in order to consider them colinear.
                Default: 1 degree.
            raise_exception: Boolean to note whether a ValueError should be raised
                if the room geometry does not form a closed solid.
        """
        if self._geometry is not None and self.geometry.is_solid:
            return True
        face_geometries = tuple(face.geometry for face in self._faces)
        self._geometry = Polyface3D.from_faces(face_geometries, tolerance)
        if self.geometry.is_solid:
            return True
        ang_tol = math.radians(angle_tolerance)
        self._geometry = self.geometry.merge_overlapping_edges(
            tolerance, ang_tol)
        if self.geometry.is_solid:
            return True
        if raise_exception:
            raise ValueError(
                'Room "{}" is not closed to within {} tolerance and {} angle '
                'tolerance.'.format(self.display_name, tolerance,
                                    angle_tolerance))
        return False
Esempio n. 5
0
 def geometry(self):
     """Get a ladybug_geometry Polyface3D object representing the room."""
     if self._geometry is None:
         self._geometry = Polyface3D.from_faces(
             tuple(face.geometry for face in self._faces),
             0)  # use 0 tolerance
     return self._geometry
Esempio n. 6
0
def to_polyface3d(geo, meshing_parameters=None):
    """A Ladybug Polyface3D object from a Rhino Brep.

    Args:
        geo: A Rhino Brep, Surface or Mesh that will be converted into a single
            Ladybug Polyface3D.
        meshing_parameters: Optional Rhino Meshing Parameters to describe how
            curved faces should be converted into planar elements. If None,
            Rhino's Default Meshing Parameters will be used.
    """
    mesh_par = meshing_parameters or rg.MeshingParameters.Default  # default
    if not isinstance(
            geo, rg.Mesh) and _planar.has_curved_face(geo):  # keep solidity
        return Polyface3D.from_faces(_planar.curved_solid_faces(geo, mesh_par),
                                     tolerance)
    return Polyface3D.from_faces(to_face3d(geo, mesh_par), tolerance)
def test_polyface3d_to_from_dict_with_overlap():
    """Test the to/from dict of Polyface3D objects with overlapping edges."""
    pts_1 = [Point3D(0, 0, 0), Point3D(0, 2, 0), Point3D(2, 2, 0), Point3D(2, 0, 0)]
    pts_2 = [Point3D(0, 0, 0), Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(0, 2, 0)]
    pts_3 = [Point3D(0, 0, 0), Point3D(2, 0, 0), Point3D(2, 0, 2), Point3D(0, 0, 2)]
    pts_4 = [Point3D(2, 2, 0), Point3D(0, 2, 0), Point3D(0, 2, 2), Point3D(2, 2, 2)]
    pts_5 = [Point3D(2, 2, 0), Point3D(2, 0, 0), Point3D(2, 0, 2), Point3D(2, 2, 2)]
    pts_6 = [Point3D(0, 0, 2), Point3D(0, 1, 2), Point3D(2, 1, 2), Point3D(2, 0, 2)]
    pts_7 = [Point3D(0, 1, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 1, 2)]
    face_1 = Face3D(pts_1)
    face_2 = Face3D(pts_2)
    face_3 = Face3D(pts_3)
    face_4 = Face3D(pts_4)
    face_5 = Face3D(pts_5)
    face_6 = Face3D(pts_6)
    face_7 = Face3D(pts_7)
    polyface = Polyface3D.from_faces(
        [face_1, face_2, face_3, face_4, face_5, face_6, face_7], 0.01)
    new_polyface = polyface.merge_overlapping_edges(0.0001, 0.0001)
    assert new_polyface.is_solid
    assert len(new_polyface.naked_edges) == 0
    assert len(new_polyface.internal_edges) == 13

    polyface_dict = new_polyface.to_dict()
    dict_polyface = Polyface3D.from_dict(polyface_dict)
    assert isinstance(dict_polyface, Polyface3D)
    assert dict_polyface.to_dict() == polyface_dict
    assert dict_polyface.is_solid
    assert len(dict_polyface.naked_edges) == 0
    assert len(dict_polyface.internal_edges) == 13
def test_polyface3d_init_from_faces_open():
    """Test the initialization of Polyface3D from_faces with an open object."""
    pts_1 = [Point3D(0, 0, 0), Point3D(0, 2, 0), Point3D(2, 2, 0), Point3D(2, 0, 0)]
    pts_2 = [Point3D(0, 0, 0), Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(0, 2, 0)]
    pts_3 = [Point3D(0, 0, 0), Point3D(2, 0, 0), Point3D(2, 0, 2), Point3D(0, 0, 2)]
    pts_4 = [Point3D(2, 2, 0), Point3D(0, 2, 0), Point3D(0, 2, 2), Point3D(2, 2, 2)]
    pts_5 = [Point3D(2, 2, 0), Point3D(2, 0, 0), Point3D(2, 0, 2), Point3D(2, 2, 2)]
    face_1 = Face3D(pts_1)
    face_2 = Face3D(pts_2)
    face_3 = Face3D(pts_3)
    face_4 = Face3D(pts_4)
    face_5 = Face3D(pts_5)
    polyface = Polyface3D.from_faces([face_1, face_2, face_3, face_4, face_5], 0.01)

    assert len(polyface.vertices) == 8
    assert len(polyface.face_indices) == 5
    assert len(polyface.faces) == 5
    assert len(polyface.edge_indices) == 12
    assert len(polyface.edges) == 12
    assert len(polyface.naked_edges) == 4
    assert len(polyface.non_manifold_edges) == 0
    assert len(polyface.internal_edges) == 8
    assert polyface.area == 20
    assert polyface.volume == 0
    assert polyface.is_solid is False

    for face in polyface.faces:
        assert face.area == 4
        assert face.is_clockwise is False
Esempio n. 9
0
    def __init__(self, name, faces, tolerance=None, angle_tolerance=None):
        """A volume enclosed by faces, representing a single room or space.

        Note that, if None is input for tolerance and angle_tolerance, no checks will
        be performed to determine whether the room is a closed volume and no attempt
        will be made to flip faces in the event that they are not facing outward from
        the room volume.  As such, an input tolerance of None is intended for
        workflows where the solidity of the room volume has been evaluated elsewhere.

        Args:
            name: Room name. Must be < 100 characters.
            faces: A list or tuple of honeybee Face objects that together form the
                closed volume of a room.
            tolerance: The maximum difference between x, y, and z values
                at which vertices of adjacent faces are considered equivalent. This is
                used in determining whether the faces form a closed volume. Default
                is None, which makes no attempt to evaluate whether the Room volume
                is closed.
            angle_tolerance: The max angle difference in degrees that vertices are
                allowed to differ from one another in order to consider them colinear.
                Default is None, which makes no attempt to evaluate whether the Room
                volume is closed.
        """
        _BaseWithShade.__init__(self, name)  # process the name

        # process the zone volume geometry
        if not isinstance(faces, tuple):
            faces = tuple(faces)
        for face in faces:
            assert isinstance(face, Face), \
                'Expected honeybee Face. Got {}'.format(type(face))
            face._parent = self

        if tolerance is None:
            self._faces = faces
            self._geometry = None  # calculated later from faces or added by classmethods
        else:
            # try to get a closed volume between the faces
            room_polyface = Polyface3D.from_faces(
                tuple(face.geometry for face in faces), tolerance)
            if not room_polyface.is_solid and angle_tolerance is not None:
                ang_tol = math.radians(angle_tolerance)
                room_polyface = room_polyface.merge_overlapping_edges(
                    tolerance, ang_tol)
            # replace honeybee face geometry with versions that are facing outwards
            if room_polyface.is_solid:
                for i, correct_face3d in enumerate(room_polyface.faces):
                    faces[i]._geometry = correct_face3d
            self._faces = faces
            self._geometry = room_polyface

        self._multiplier = 1  # default value that can be overridden later
        self._properties = RoomProperties(self)  # properties for extensions
Esempio n. 10
0
    def floor_geometry(self, tolerance=0.01):
        """Get a ladybug_geometry Polyface3D object representing the floor plate.

        Args:
            tolerance: The minimum distance between points at which they are
                not considered touching. Default: 0.01, suitable for objects
                in meters.
        """
        story_height = self.floor_height
        room_floors = []
        for room in self.room_2ds:
            diff = story_height - room.floor_height
            if abs(diff) <= tolerance:
                room_floors.append(room.floor_geometry)
            else:
                room_floors.append(room.floor_geometry.move(Vector3D(0, 0, diff)))
        # TODO: consider returning a list of polyfaces if input rooms are disjointed
        return Polyface3D.from_faces(room_floors, tolerance)
Esempio n. 11
0
    def remove_colinear_vertices_envelope(self, tolerance=0.01):
        """Remove colinear and duplicate vertices from this object's Faces and Sub-faces.

        Note that this does not affect any assigned Shades.

        Args:
            tolerance: The minimum distance between a vertex and the boundary segments
                at which point the vertex is considered colinear. Default: 0.01,
                suitable for objects in meters.
        """
        for face in self._faces:
            face.remove_colinear_vertices(tolerance)
            for ap in face._apertures:
                ap.remove_colinear_vertices(tolerance)
            for dr in face._doors:
                dr.remove_colinear_vertices(tolerance)
        if self._geometry is not None:
            self._geometry = Polyface3D.from_faces(
                tuple(face.geometry for face in self._faces), tolerance)
def test_polyface3d_init_from_faces_solid():
    """Test the initialization of Polyface3D from_faces with a solid."""
    pts_1 = [Point3D(0, 0, 0), Point3D(0, 2, 0), Point3D(2, 2, 0), Point3D(2, 0, 0)]
    pts_2 = [Point3D(0, 0, 0), Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(0, 2, 0)]
    pts_3 = [Point3D(0, 0, 0), Point3D(2, 0, 0), Point3D(2, 0, 2), Point3D(0, 0, 2)]
    pts_4 = [Point3D(2, 2, 0), Point3D(0, 2, 0), Point3D(0, 2, 2), Point3D(2, 2, 2)]
    pts_5 = [Point3D(2, 2, 0), Point3D(2, 0, 0), Point3D(2, 0, 2), Point3D(2, 2, 2)]
    pts_6 = [Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 0, 2)]
    face_1 = Face3D(pts_1)
    face_2 = Face3D(pts_2)
    face_3 = Face3D(pts_3)
    face_4 = Face3D(pts_4)
    face_5 = Face3D(pts_5)
    face_6 = Face3D(pts_6)
    polyface = Polyface3D.from_faces(
        [face_1, face_2, face_3, face_4, face_5, face_6], 0.01)

    assert len(polyface.vertices) == 8
    assert len(polyface.face_indices) == 6
    assert len(polyface.faces) == 6
    assert len(polyface.edge_indices) == 12
    assert len(polyface.edges) == 12
    assert len(polyface.naked_edges) == 0
    assert len(polyface.non_manifold_edges) == 0
    assert len(polyface.internal_edges) == 12
    assert polyface.area == 24
    assert polyface.volume == 8
    assert polyface.is_solid

    for face in polyface.faces:
        assert face.area == 4
        assert face.is_clockwise is False
    assert polyface.faces[0].normal == Vector3D(0, 0, -1)
    assert polyface.faces[1].normal == Vector3D(-1, 0, 0)
    assert polyface.faces[2].normal == Vector3D(0, -1, 0)
    assert polyface.faces[3].normal == Vector3D(0, 1, 0)
    assert polyface.faces[4].normal == Vector3D(1, 0, 0)
    assert polyface.faces[5].normal == Vector3D(0, 0, 1)
def test_is_solid_with_hole():
    """Test the is_solid property for a polyface with a hole.

    This ensures that the is_solid property still works where the Euler
    characteristic fails.
    """
    pts_1 = [Point3D(0, 0, 2), Point3D(0, 0, 0), Point3D(4, 0, 0), Point3D(4, 0, 2)]
    pts_2 = [Point3D(4, 0, 2), Point3D(4, 0, 0), Point3D(4, 4, 0), Point3D(4, 4, 2)]
    pts_3 = [Point3D(4, 4, 2), Point3D(4, 4, 0), Point3D(0, 4, 0), Point3D(0, 4, 2)]
    pts_4 = [Point3D(0, 4, 2), Point3D(0, 4, 0), Point3D(0, 0, 0), Point3D(0, 0, 2)]
    pts_5 = [Point3D(0, 0, 0), Point3D(0, 4, 0), Point3D(1, 3, 1), Point3D(1, 1, 1)]
    pts_6 = [Point3D(4, 0, 0), Point3D(0, 0, 0), Point3D(1, 1, 1), Point3D(3, 1, 1)]
    pts_7 = [Point3D(4, 4, 0), Point3D(4, 0, 0), Point3D(3, 1, 1), Point3D(3, 3, 1)]
    pts_8 = [Point3D(0, 4, 0), Point3D(4, 4, 0), Point3D(3, 3, 1), Point3D(1, 3, 1)]
    pts_9 = [Point3D(1, 1, 1), Point3D(1, 3, 1), Point3D(0, 4, 2), Point3D(0, 0, 2)]
    pts_10 = [Point3D(3, 1, 1), Point3D(1, 1, 1), Point3D(0, 0, 2), Point3D(4, 0, 2)]
    pts_11 = [Point3D(3, 3, 1), Point3D(3, 1, 1), Point3D(4, 0, 2), Point3D(4, 4, 2)]
    pts_12 = [Point3D(1, 3, 1), Point3D(3, 3, 1), Point3D(4, 4, 2), Point3D(0, 4, 2)]

    face_1 = Face3D(pts_1)
    face_2 = Face3D(pts_2)
    face_3 = Face3D(pts_3)
    face_4 = Face3D(pts_4)
    face_5 = Face3D(pts_5)
    face_6 = Face3D(pts_6)
    face_7 = Face3D(pts_7)
    face_8 = Face3D(pts_8)
    face_9 = Face3D(pts_9)
    face_10 = Face3D(pts_10)
    face_11 = Face3D(pts_11)
    face_12 = Face3D(pts_12)
    polyface = Polyface3D.from_faces([face_1, face_2, face_3, face_4, face_5,
                                      face_6, face_7, face_8, face_9, face_10,
                                      face_11, face_12], 0.01)
    assert len(polyface.faces) + len(polyface.vertices) - len(polyface.edges) != 2
    assert polyface.area == pytest.approx(65.941125, rel=1e-3)
    assert polyface.volume == pytest.approx(13.333333, rel=1e-3)
    assert polyface.is_solid
Esempio n. 14
0
 def geometry(self):
     """A ladybug_geometry Polyface3D object representing the room."""
     if self._geometry is None:
         self._geometry = Polyface3D.from_faces(
             tuple(face.geometry for face in self._faces))
     return self._geometry