def test_mesh3d_init_from_face_vertices(): """Test the initialization of Mesh3D from_face_vertices.""" face_1 = (Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 0, 2)) face_2 = (Point3D(2, 2, 2), Point3D(2, 0, 2), Point3D(4, 0, 2)) mesh_1 = Mesh3D.from_face_vertices([face_1, face_2]) mesh_2 = Mesh3D.from_face_vertices([face_1, face_2], False) assert len(mesh_1.vertices) == 5 assert len(mesh_2.vertices) == 7 assert len(mesh_1.faces) == len(mesh_2.faces) == 2 assert mesh_1.area == mesh_2.area == 6 assert mesh_1.min == mesh_2.min == Point3D(0, 0, 2) assert mesh_1.max == mesh_2.max == Point3D(4, 2, 2) assert mesh_1.center == mesh_2.center == Point3D(2, 1, 2) assert len(mesh_1.face_areas) == len(mesh_2.face_areas) == 2 assert mesh_1.face_areas[0] == mesh_2.face_areas[0] == 4 assert mesh_1.face_areas[1] == mesh_2.face_areas[1] == 2 assert len(mesh_1.face_centroids) == len(mesh_2.face_centroids) == 2 assert mesh_1.face_centroids[0] == mesh_2.face_centroids[0] == Point3D( 1, 1, 2) assert mesh_1.face_centroids[1].x == mesh_2.face_centroids[ 1].x == pytest.approx(2.67, rel=1e-2) assert mesh_1.face_centroids[1].y == mesh_2.face_centroids[ 1].y == pytest.approx(0.67, rel=1e-2) assert mesh_1.face_centroids[1].z == mesh_2.face_centroids[ 1].z == pytest.approx(2, rel=1e-2) assert mesh_1._is_color_by_face is mesh_1._is_color_by_face is False assert mesh_1.colors is mesh_1.colors is None
def test_to_from_stl_ascii(): """Test from_stl method with an ascii STL file.""" file_path = 'tests/stl/cube_ascii.stl' mesh = Mesh3D.from_stl(file_path) centroids = [ Point3D(3.33, 0.00, 1.67), Point3D(1.67, 0.00, 3.33), Point3D(5.00, 3.33, 1.67), Point3D(5.00, 1.67, 3.33), Point3D(1.67, 5.00, 1.67), Point3D(3.33, 5.00, 3.33), Point3D(0.00, 1.67, 1.67), Point3D(0.00, 3.33, 3.33), Point3D(1.67, 1.67, 0.00), Point3D(3.33, 3.33, 0.00), Point3D(1.67, 1.67, 5.00), Point3D(3.33, 3.33, 5.00), Point3D(3.33, 0.00, 1.67) ] for count, cent in enumerate(mesh.face_centroids): assert cent.distance_to_point(centroids[count]) <= 0.01 new_folder, new_name = 'tests/stl/', 'cube_ascii_new.stl' new_file = mesh.to_stl(new_folder, new_name) assert os.path.isfile(new_file) Mesh3D.from_stl(new_file) os.remove(new_file)
def test_mesh3d_to_from_dict(): """Test the to/from dict of Mesh3D objects.""" pts = (Point3D(0, 0), Point3D(0, 2), Point3D(2, 2), Point3D(2, 0)) mesh = Mesh3D(pts, [(0, 1, 2, 3)]) mesh_dict = mesh.to_dict() new_mesh = Mesh3D.from_dict(mesh_dict) assert isinstance(new_mesh, Mesh3D) assert new_mesh.to_dict() == mesh_dict
def _generate_bottom_from_top(m_top, v_top): """Get a joined mesh and vectors for top and bottom from only top vectors.""" # reverse the vectors and negate all the z values of the sky patch mesh verts = tuple(Point3D(pt.x, pt.y, -pt.z) for pt in m_top.vertices) faces = tuple(face[::-1] for face in m_top.faces) m_bottom = Mesh3D(verts, faces) v_bottom = tuple(Vector3D(v.x, v.y, -v.z) for v in v_top) # join everything together patch_mesh = Mesh3D.join_meshes([m_top, m_bottom]) patch_vectors = v_top + v_bottom return patch_mesh, patch_vectors
def test_mesh3d_incorrect(): """Test the initialization of Mesh3D objects with incorrect values.""" pts = (Point3D(0, 0), Point3D(0, 2), Point3D(2, 2), Point3D(2, 0), Point3D(4, 0)) with pytest.raises(AssertionError): Mesh3D(pts, [(0, 1, 2, 3, 5)]) # too many vertices in a face with pytest.raises(AssertionError): Mesh3D(pts, []) # we need at least one face with pytest.raises(AssertionError): Mesh3D(pts, (0, 1, 2, 3)) # incorrect input type for face with pytest.raises(IndexError): Mesh3D(pts, [(0, 1, 2, 6)]) # incorrect index used by face with pytest.raises(TypeError): Mesh3D(pts, [(0.0, 1, 2, 6)]) # incorrect use of floats for face index
def test_equality(): """Test the equality of Mesh3D objects.""" pts = (Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 0, 2)) pts_2 = (Point3D(0.1, 0, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 0, 2)) mesh = Mesh3D(pts, [(0, 1, 2, 3)]) mesh_dup = mesh.duplicate() mesh_alt = Mesh3D(pts_2, [(0, 1, 2, 3)]) assert mesh is mesh assert mesh is not mesh_dup assert mesh == mesh_dup assert hash(mesh) == hash(mesh_dup) assert mesh != mesh_alt assert hash(mesh) != hash(mesh_alt)
def test_mesh3d_to_from_json(): """Test the to/from dict with JSON serialization of Mesh3D objects.""" pts = (Point3D(0, 0), Point3D(0, 2), Point3D(2, 2), Point3D(2, 0)) mesh = Mesh3D(pts, [(0, 1, 2, 3)]) mesh_dict = mesh.to_dict() geo_file = './tests/json/json_mesh.json' with open(geo_file, 'w') as fp: json.dump(mesh_dict, fp) with open(geo_file, 'r') as fp: new_mesh_dict = json.load(fp) new_mesh = Mesh3D.from_dict(new_mesh_dict) assert isinstance(new_mesh, Mesh3D) assert new_mesh.to_dict() == mesh_dict os.remove(geo_file)
def test_offset_mesh(): """Test the offset_mesh method.""" pts = (Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 0, 2)) pts_rev = tuple(reversed(pts)) mesh = Mesh3D(pts, [(0, 1, 2, 3)]) mesh_rev = Mesh3D(pts_rev, [(0, 1, 2, 3)]) new_mesh = mesh.offset_mesh(2) for v in new_mesh.vertices: assert v.z == 0 new_mesh_rev = mesh_rev.offset_mesh(2) for v in new_mesh_rev.vertices: assert v.z == 4
def from_dict(cls, ag_dict): """Create a sensor grid from a dictionary in the following format. .. code-block:: python { "type": "SensorGrid", "identifier": str, # SensorGrid identifier "display_name": str, # SensorGrid display name "sensors": [], # list of Sensor dictionaries 'room_identifier': str, # optional room identifier 'group_identifier': str, # optional group identifier 'light_path': [] # optional list of lists for light path } """ assert ag_dict['type'] == 'SensorGrid', \ 'Expected SensorGrid dictionary. Got {}.'.format(ag_dict['type']) sensors = (Sensor.from_dict(sensor) for sensor in ag_dict['sensors']) new_obj = cls(identifier=ag_dict["identifier"], sensors=sensors) if 'display_name' in ag_dict and ag_dict['display_name'] is not None: new_obj.display_name = ag_dict['display_name'] if 'room_identifier' in ag_dict and ag_dict[ 'room_identifier'] is not None: new_obj.room_identifier = ag_dict['room_identifier'] if 'group_identifier' in ag_dict and ag_dict[ 'group_identifier'] is not None: new_obj.group_identifier = ag_dict['group_identifier'] if 'light_path' in ag_dict and ag_dict['light_path'] is not None: new_obj.light_path = ag_dict['light_path'] if 'mesh' in ag_dict and ag_dict['mesh'] is not None: new_obj.mesh = Mesh3D.from_dict(ag_dict['mesh']) if 'base_geometry' in ag_dict and ag_dict['base_geometry'] is not None: new_obj.base_geometry = \ tuple(Face3D.from_dict(face) for face in ag_dict['base_geometry']) return new_obj
def generate_grid(self, x_dim, y_dim=None, offset=1.0): """Get a list of gridded Mesh3D objects offset from the floors of this room. Note that the x_dim and y_dim refer to dimensions within the XY coordinate system of the floor faces's planes. So rotating the planes of the floor faces will result in rotated grid cells. Will be None if the Room has no floor faces. Args: x_dim: The x dimension of the grid cells as a number. y_dim: The y dimension of the grid cells as a number. Default is None, which will assume the same cell dimension for y as is set for x. offset: A number for how far to offset the grid from the base face. Default is 1.0, which will not offset the grid to be 1 unit above the floor. Usage: .. code-block:: python room = Room.from_box(3.0, 6.0, 3.2, 180) floor_mesh = room.generate_grid(0.5, 0.5, 1) test_points = floor_mesh.face_centroids """ floor_grids = [] for face in self._faces: if isinstance(face.type, Floor): floor_grids.append( face.geometry.mesh_grid(x_dim, y_dim, offset, True)) if len(floor_grids) == 1: return floor_grids[0] elif len(floor_grids) > 1: return Mesh3D.join_meshes(floor_grids) return None
def test_mesh3d_init(): """Test the initialization of Mesh3D objects and basic properties.""" pts = (Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 0, 2)) mesh = Mesh3D(pts, [(0, 1, 2, 3)]) str(mesh) # test the string representation of the object assert len(mesh.vertices) == 4 assert len(mesh.faces) == 1 assert mesh[0] == Point3D(0, 0, 2) assert mesh[1] == Point3D(0, 2, 2) assert mesh[2] == Point3D(2, 2, 2) assert mesh[3] == Point3D(2, 0, 2) assert mesh.area == 4 assert mesh.min == Point3D(0, 0, 2) assert mesh.max == Point3D(2, 2, 2) assert mesh.center == Point3D(1, 1, 2) assert len(mesh.face_areas) == 1 assert mesh.face_areas[0] == 4 assert len(mesh.face_centroids) == 1 assert mesh.face_centroids[0] == Point3D(1, 1, 2) assert mesh._is_color_by_face is False assert mesh.colors is None assert len(mesh.vertex_connected_faces) == 4 for vf in mesh.vertex_connected_faces: assert len(vf) == 1 mesh.colors = [] assert mesh.colors is None
def test_join_meshes(): """Test the join_meshes method.""" pts1 = (Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 0, 2)) pts2 = (Point3D(2, 2, 2), Point3D(2, 4, 2), Point3D(4, 4, 2), Point3D(4, 2, 2)) mesh1 = Mesh3D(pts1, [(0, 1, 2, 3)]) mesh2 = Mesh3D(pts2, [(0, 1, 2, 3)]) mesh1.face_centroids mesh2.face_centroids mesh1.face_normals mesh2.face_normals joined_mesh = Mesh3D.join_meshes([mesh1, mesh2]) assert isinstance(joined_mesh, Mesh3D) assert len(joined_mesh.faces) == 2 assert len(joined_mesh.vertices) == 8
def test_rotate(): """Test the Mesh3D rotate method.""" pts = (Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 0, 2), Point3D(4, 0, 2)) mesh = Mesh3D(pts, [(0, 1, 2, 3), (2, 3, 4)]) origin = Point3D(0, 0, 0) axis = Vector3D(1, 0, 0) test_1 = mesh.rotate(axis, math.pi, origin) assert test_1[0].x == pytest.approx(0, rel=1e-3) assert test_1[0].y == pytest.approx(0, rel=1e-3) assert test_1[0].z == pytest.approx(-2, rel=1e-3) assert test_1[2].x == pytest.approx(2, rel=1e-3) assert test_1[2].y == pytest.approx(-2, rel=1e-3) assert test_1[2].z == pytest.approx(-2, rel=1e-3) assert mesh.area == test_1.area assert len(mesh.vertices) == len(test_1.vertices) assert len(mesh.faces) == len(test_1.faces) test_2 = mesh.rotate(axis, math.pi / 2, origin) assert test_2[0].x == pytest.approx(0, rel=1e-3) assert test_2[0].y == pytest.approx(-2, rel=1e-3) assert test_2[0].z == pytest.approx(0, rel=1e-3) assert test_2[2].x == pytest.approx(2, rel=1e-3) assert test_2[2].y == pytest.approx(-2, rel=1e-3) assert test_2[2].z == pytest.approx(2, rel=1e-3) assert mesh.area == test_2.area assert len(mesh.vertices) == len(test_2.vertices) assert len(mesh.faces) == len(test_2.faces)
def test_remove_faces(): """Test the Mesh3D remove_faces method.""" mesh_2d = Mesh2D.from_grid(Point2D(1, 1), 8, 2, 0.25, 1) mesh = Mesh3D.from_mesh2d(mesh_2d) assert len(mesh.vertices) == 27 assert len(mesh.faces) == 16 assert mesh.area == 4 pattern_1 = [] for i in range(4): pattern_1.extend([True, False, False, False]) mesh_1, vert_pattern = mesh.remove_faces(pattern_1) assert len(mesh_1.vertices) == 16 assert len(mesh_1.faces) == 4 assert mesh_1.area == 1 for face in mesh_1.faces: for i in face: mesh_1[i] # make sure all face indices reference current vertices pattern_2 = [] for i in range(8): pattern_2.extend([True, False]) mesh_2, vert_pattern = mesh.remove_faces(pattern_2) assert len(mesh_2.vertices) == 18 assert len(mesh_2.faces) == 8 assert mesh_2.area == 2 for face in mesh_2.faces: for i in face: mesh_2[i] # make sure all face indices reference current vertices
def test_init_graphic_con(): """Test the initialization of GraphicContainer objects.""" mesh2d = Mesh2D.from_grid(num_x=2, num_y=2) mesh3d = Mesh3D.from_mesh2d(mesh2d) data = [0, 1, 2, 3] graphic_con = GraphicContainer(data, mesh3d.min, mesh3d.max) str(graphic_con) # Test the GraphicContainer representation assert len(graphic_con) == 4 assert graphic_con[0] == 0 assert graphic_con[-1] == 3 for item in graphic_con: assert isinstance(item, (float, int)) assert len(graphic_con.values) == 4 assert isinstance(graphic_con.legend, Legend) assert graphic_con.value_colors == graphic_con.legend.value_colors assert graphic_con.legend_parameters.is_base_plane_default is False assert graphic_con.legend_parameters.is_segment_height_default is False assert graphic_con.legend_parameters.is_segment_width_default is True assert graphic_con.legend_parameters.is_text_height_default is True assert graphic_con.legend_parameters.base_plane != Plane() assert isinstance(graphic_con.lower_title_location, Plane) assert isinstance(graphic_con.upper_title_location, Plane) assert graphic_con.lower_title_location != Plane() assert graphic_con.upper_title_location != Plane()
def test_reflect(): """Test the Mesh3D reflect method.""" pts = (Point3D(1, 1, 2), Point3D(2, 1, 2), Point3D(2, 2, 2), Point3D(1, 2, 2)) mesh = Mesh3D(pts, [(0, 1, 2, 3)]) origin_1 = Point3D(1, 0, 2) normal_1 = Vector3D(1, 0, 0) normal_2 = Vector3D(-1, -1, 0).normalize() test_1 = mesh.reflect(normal_1, origin_1) assert test_1[0].x == pytest.approx(1, rel=1e-3) assert test_1[0].y == pytest.approx(1, rel=1e-3) assert test_1[0].z == pytest.approx(2, rel=1e-3) assert test_1[2].x == pytest.approx(0, rel=1e-3) assert test_1[2].y == pytest.approx(2, rel=1e-3) assert test_1[2].z == pytest.approx(2, rel=1e-3) test_1 = mesh.reflect(normal_2, Point3D(0, 0, 0)) assert test_1[0].x == pytest.approx(-1, rel=1e-3) assert test_1[0].y == pytest.approx(-1, rel=1e-3) assert test_1[0].z == pytest.approx(2, rel=1e-3) assert test_1[2].x == pytest.approx(-2, rel=1e-3) assert test_1[2].y == pytest.approx(-2, rel=1e-3) assert test_1[2].z == pytest.approx(2, rel=1e-3) test_2 = mesh.reflect(normal_2, origin_1) assert test_2[0].x == pytest.approx(0, rel=1e-3) assert test_2[0].y == pytest.approx(0, rel=1e-3) assert test_2[0].z == pytest.approx(2, rel=1e-3) assert test_2[2].x == pytest.approx(-1, rel=1e-3) assert test_2[2].y == pytest.approx(-1, rel=1e-3) assert test_2[2].z == pytest.approx(2, rel=1e-3)
def test_mesh3d_init_two_faces(): """Test the initialization of Mesh3D objects with two faces.""" pts = (Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 0, 2), Point3D(4, 0, 2)) mesh = Mesh3D(pts, [(0, 1, 2, 3), (2, 3, 4)]) assert len(mesh.vertices) == 5 assert len(mesh.faces) == 2 assert mesh[0] == Point3D(0, 0, 2) assert mesh[1] == Point3D(0, 2, 2) assert mesh[2] == Point3D(2, 2, 2) assert mesh[3] == Point3D(2, 0, 2) assert mesh[4] == Point3D(4, 0, 2) assert mesh.area == 6 assert mesh.min == Point3D(0, 0, 2) assert mesh.max == Point3D(4, 2, 2) assert mesh.center == Point3D(2, 1, 2) assert len(mesh.face_areas) == 2 assert mesh.face_areas[0] == 4 assert mesh.face_areas[1] == 2 assert len(mesh.face_centroids) == 2 assert mesh.face_centroids[0] == Point3D(1, 1, 2) assert mesh.face_centroids[1].x == pytest.approx(2.67, rel=1e-2) assert mesh.face_centroids[1].y == pytest.approx(0.67, rel=1e-2) assert mesh.face_centroids[1].z == pytest.approx(2, rel=1e-2) assert mesh._is_color_by_face is False assert mesh.colors is None
def radial_positions_mesh(positions, dir_count=8, start_vector=Vector3D(0, -1, 0), mesh_radius=1): """Generate a Mesh3D resembling a circle around each position. Args: positions: A list of (x, y ,z) tuples for position of sensors. dir_count: A positive integer for the number of radial directions to be generated around each position. (Default: 8). start_vector: A Vector3D to set the start direction of the generated directions. (Default: (0, -1, 0)). mesh_radius: A number for the radius of the radial mesh to be generated around each sensor. (Default: 1). """ # set up the start vector and rotation angles st_vec = Vector3D(start_vector.x, start_vector.y, 0).normalize() st_vec = st_vec * mesh_radius inc_ang = (math.pi * 2) / dir_count st_vec = st_vec.rotate_xy(-inc_ang / 2) # loop through the positions and angles to create the mesh verts, faces = [], [] v_count = 0 for pt in positions: st_pt = Point3D(*pt) nxt_pt = st_pt.move(st_vec) verts.extend([st_pt, nxt_pt]) for i in range(dir_count - 1): new_pt = verts[-1].rotate_xy(inc_ang, st_pt) new_f = (v_count, v_count + i + 1, v_count + i + 2) verts.append(new_pt) faces.append(new_f) faces.append((v_count, v_count + dir_count, v_count + 1)) v_count += (dir_count + 1) return Mesh3D(verts, faces)
def _compute_colored_mesh3d(self): """Compute a colored mesh from this object's data collection.""" _colored_mesh3d = Mesh3D.from_mesh2d( self.colored_mesh2d, Plane(o=Point3D(0, 0, self._container.min_point.z))) if self.z_dim != 0: _colored_mesh3d = _colored_mesh3d.height_field_mesh( self.data_collection.values, (0, self.z_dim)) return _colored_mesh3d
def test_init_graphic_con_invalid(self): """Test the initialization of GraphicContainer objects with invalid inputs.""" mesh2d = Mesh2D.from_grid(num_x=2, num_y=2) mesh3d = Mesh3D.from_mesh2d(mesh2d) data = [0, 1, 2, 3, 4] with pytest.raises(Exception): GraphicContainer(data, mesh3d.min, mesh3d.max, data_type=Temperature(), unit='NotAUnit')
def test_from_mesh3d(): mesh_2d = Mesh2D.from_grid(Point2D(1, 1), 8, 2, 0.25, 1) mesh = Mesh3D.from_mesh2d(mesh_2d) sg = SensorGrid.from_mesh3d('sg_1', mesh) assert len(sg.sensors) == 16 assert len(sg.mesh.vertices) == 27 assert len(sg.mesh.faces) == 16 assert mesh.area == 4
def to_mesh3d(mesh, color_by_face=True): """Ladybug Mesh3D from Rhino Mesh.""" if isinstance(mesh, Mesh3D): return mesh elif isinstance(mesh, bpy.types.Object): lb_verts = tuple( to_point3d(mesh.matrix_world @ pt.co) for pt in mesh.data.vertices) lb_faces, colors = _extract_mesh_faces_colors(mesh, mesh.data, color_by_face) return Mesh3D(lb_verts, lb_faces, colors)
def test_to_from_dict(): """Test the to/from dict methods.""" mesh2d = Mesh2D.from_grid(num_x=2, num_y=2) mesh3d = Mesh3D.from_mesh2d(mesh2d) data = [0, 1, 2, 3] graphic_con = GraphicContainer(data, mesh3d.min, mesh3d.max) graphic_con_dict = graphic_con.to_dict() new_graphic_con = GraphicContainer.from_dict(graphic_con_dict) assert new_graphic_con.to_dict() == graphic_con_dict
def test_face_normals(): """Test the Mesh3D face_normals property.""" pts = (Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 0, 2)) mesh = Mesh3D(pts, [(0, 1, 2, 3)]) assert len(mesh.face_normals) == 1 assert mesh.face_normals[0] == Vector3D(0, 0, -1) assert len(mesh.vertex_normals) == 4 for vert_norm in mesh.vertex_normals: assert vert_norm == Vector3D(0, 0, -1)
def _aperture_view_factor(project_folder, apertures, size=0.2, ambient_division=1000, receiver='rflux_sky.sky', octree='scene.oct', calc_folder='dmtx_aperture_grouping'): """Calculates the view factor for each aperture by sensor points.""" # Instantiate dictionary that will store the sensor count for each aperture. We need # a OrderedDict so that we can split the rfluxmtx output file by each aperture # (sensor count) in the correct order. ap_dict = OrderedDict() meshes = [] # Create a mesh for each aperture and add the the sensor count to dict. for aperture in apertures: ap_mesh = aperture.geometry.mesh_grid(size, flip=True, generate_centroids=False) meshes.append(ap_mesh) ap_dict[aperture.display_name] = { 'sensor_count': len(ap_mesh.faces) } # Create a sensor grid from joined aperture mesh. grid_mesh = SensorGrid.from_mesh3d('aperture_grid', Mesh3D.join_meshes(meshes)) # Write sensor grid to pts file. sensors = grid_mesh.to_file(os.path.join(project_folder, calc_folder), file_name='apertures') # rfluxmtx options rfluxOpt = RfluxmtxOptions() rfluxOpt.ad = ambient_division rfluxOpt.lw = 1.0 / float(rfluxOpt.ad) rfluxOpt.I = True rfluxOpt.h = True # rfluxmtx command rflux = Rfluxmtx() rflux.options = rfluxOpt rflux.receivers = receiver rflux.sensors = sensors rflux.octree = octree rflux.output = os.path.join(calc_folder, 'apertures_vf.mtx') # Run rfluxmtx command rflux.run(cwd=project_folder) # Get the output file of the rfluxmtx command. mtx_file = os.path.join(project_folder, rflux.output) return mtx_file, ap_dict
def test_height_field_mesh(): """Test the height_field_mesh method.""" pts = (Point3D(0, 0, 0), Point3D(2, 0, 0), Point3D(2, 2, 0), Point3D(0, 2, 0)) mesh = Mesh3D(pts, [(0, 1, 2, 3)]) values = [-1, 0, 1, 2] new_mesh = mesh.height_field_mesh(values, (0, 3)) assert new_mesh[0].z == 0 assert new_mesh[1].z == 1 assert new_mesh[2].z == 2 assert new_mesh[3].z == 3
def dome_radial_patches(self, azimuth_count=72, altitude_count=18): """Get Vector3Ds and a correcponding Mesh3D for a a radial dome. Args: azimuth_count: A positive integer for the number of times that the horizontal circle will be subdivided into azimuth patches. (Default: 72). altitude_count: A positive integer for the number of times that the dome quarter-circle will be subdivided into altitude patches. (Default: 18). Returns: A tuple with two elements - patch_mesh: A ladybug_geometry Mesh3D that represents the patches at the input azimuth_count and altitude_count. - patch_vectors: A list of ladybug_geometry Vector3D with one vector per mesh face. These will align with the faces of the patch_mesh. All vectors are unit vectors. """ # set up starting vectors and points base_vec, rotate_axis = Vector3D(0, 1, 0), Vector3D(1, 0, 0) horiz_angle = -2 * math.pi / azimuth_count vertical_angle = math.pi / (2 * altitude_count) # loop through the patch values and generate points for each vertex vertices, faces = [], [] pt_i = -2 # track the number of vertices in the mesh for row_i in range(altitude_count - 1): pt_i += 2 # advance the number of vertices by two vec1 = base_vec.rotate(rotate_axis, vertical_angle * row_i) vec2 = vec1.rotate(rotate_axis, vertical_angle) vertices.extend((Point3D(v.x, v.y, v.z) for v in (vec1, vec2))) for _ in range(azimuth_count): # generate the row of patches vec3 = vec1.rotate_xy(horiz_angle) vec4 = vec2.rotate_xy(horiz_angle) vertices.extend((Point3D(v.x, v.y, v.z) for v in (vec3, vec4))) faces.append((pt_i, pt_i + 1, pt_i + 3, pt_i + 2)) pt_i += 2 # advance the number of vertices by two vec1, vec2 = vec3, vec4 # reset vec1 and vec2 for the next patch # add triangular faces to represent the last circular patch end_vert_i = len(vertices) start_vert_i = len(vertices) - azimuth_count * 2 - 1 vertices.append(Point3D(0, 0, 1)) for tr_i in range(0, azimuth_count * 2, 2): faces.append((start_vert_i + tr_i, end_vert_i, start_vert_i + tr_i + 2)) # create the Mesh3D object and derive the patch vectors from the mesh patch_mesh = Mesh3D(vertices, faces) patch_vectors = patch_mesh.face_normals return patch_mesh, patch_vectors
def test_height_field_mesh_faces(): """Test the height_field_mesh method with values for faces.""" pts = (Point3D(0, 0, 0), Point3D(2, 0, 0), Point3D(2, 2, 0), Point3D(0, 2, 0), Point3D(4, 0, 0)) mesh = Mesh3D(pts, [(0, 1, 2, 3), (2, 3, 4)]) values = [-1, 1] new_mesh = mesh.height_field_mesh(values, (1, 2)) assert new_mesh[0].z == 1 assert new_mesh[1].z == 1 assert new_mesh[2].z == 1.5 assert new_mesh[3].z == 1.5 assert new_mesh[4].z == 2
def test_scale_world_origin(): """Test the Mesh2D scale method with None origin.""" pts = (Point3D(0, 0, 2), Point3D(0, 2, 2), Point3D(2, 2, 2), Point3D(2, 0, 2), Point3D(4, 0, 2)) mesh = Mesh3D(pts, [(0, 1, 2, 3), (2, 3, 4)]) new_mesh_1 = mesh.scale(2) assert new_mesh_1[0] == Point3D(0, 0, 4) assert new_mesh_1[1] == Point3D(0, 4, 4) assert new_mesh_1[2] == Point3D(4, 4, 4) assert new_mesh_1[3] == Point3D(4, 0, 4) assert new_mesh_1[4] == Point3D(8, 0, 4) assert new_mesh_1.area == 24 assert len(mesh.vertices) == len(new_mesh_1.vertices) assert len(mesh.faces) == len(new_mesh_1.faces)
def test_init_graphic_con_vertex_based(): """Test the initialization of ResultMesh objects with vertex-based input.""" mesh2d = Mesh2D.from_grid(num_x=2, num_y=2) mesh3d = Mesh3D.from_mesh2d(mesh2d) data = [0, 1, 2, 3, 4, 5, 6, 7, 8] graphic_con = GraphicContainer(data, mesh3d.min, mesh3d.max) assert len(graphic_con) == 9 assert graphic_con[0] == 0 assert graphic_con[-1] == 8 assert len(graphic_con.values) == 9 assert isinstance(graphic_con.legend_parameters, LegendParameters) assert isinstance(graphic_con.legend, Legend) assert graphic_con.value_colors == graphic_con.legend.value_colors