def test_intersect_line_ray(): """Test the Polyface3D intersect_line_ray method.""" pts = (Point3D(0, 0, 2), Point3D(2, 0, 2), Point3D(2, 1, 2), Point3D(1, 1, 2), Point3D(1, 2, 2), Point3D(0, 2, 2)) plane = Plane(Vector3D(0, 0, 1), Point3D(0, 0, 2)) face = Face3D(pts, plane) polyface = Polyface3D.from_offset_face(face, 1) ray_1 = Ray3D(Point3D(0.5, 0.5, 0), Vector3D(0, 0, 1)) ray_2 = Ray3D(Point3D(0.5, 0.5, 0), Vector3D(0, 0, -1)) ray_3 = Ray3D(Point3D(1.5, 1.5, 0), Vector3D(0, 0, 1)) ray_4 = Ray3D(Point3D(-1, -1, 0), Vector3D(0, 0, 1)) line_1 = LineSegment3D(Point3D(0.5, 0.5, 0), Vector3D(0, 0, 3)) line_2 = LineSegment3D(Point3D(0.5, 0.5, 0), Vector3D(0, 0, 1)) assert polyface.does_intersect_line_ray_exist(ray_1) is True assert polyface.does_intersect_line_ray_exist(ray_2) is False assert polyface.does_intersect_line_ray_exist(ray_3) is False assert polyface.does_intersect_line_ray_exist(ray_4) is False assert polyface.does_intersect_line_ray_exist(line_1) is True assert polyface.does_intersect_line_ray_exist(line_2) is False assert polyface.intersect_line_ray(ray_1) == [ Point3D(0.5, 0.5, 2), Point3D(0.5, 0.5, 3) ] assert polyface.intersect_line_ray(ray_2) == [] assert polyface.intersect_line_ray(ray_3) == [] assert polyface.intersect_line_ray(ray_4) == [] assert polyface.intersect_line_ray(line_1) == [ Point3D(0.5, 0.5, 2), Point3D(0.5, 0.5, 3) ] assert polyface.intersect_line_ray(line_2) == []
def test_ray3_to_from_dict(): """Test the to/from dict of Ray3D objects.""" pt = Point3D(2, 0, 2) vec = Vector3D(0, 2, 0) ray = Ray3D(pt, vec) ray_dict = ray.to_dict() new_ray = Ray3D.from_dict(ray_dict) assert isinstance(new_ray, Ray3D) assert new_ray.to_dict() == ray_dict
def test_reflect(): """Test the Ray3D reflect method.""" pt = Point3D(2, 2, 2) vec = Vector3D(0, 2, 0) ray = Ray3D(pt, vec) origin_1 = Point3D(0, 1, 2) origin_2 = Point3D(1, 1, 2) normal_1 = Vector3D(0, 1, 0) normal_2 = Vector3D(-1, 1, 0).normalize() assert ray.reflect(normal_1, origin_1).p == Point3D(2, 0, 2) assert ray.reflect(normal_1, origin_1).v == Vector3D(0, -2, 0) assert ray.reflect(normal_1, origin_2).p == Point3D(2, 0, 2) assert ray.reflect(normal_1, origin_2).v == Vector3D(0, -2, 0) test_1 = ray.reflect(normal_2, origin_2) assert test_1.p == Point3D(2, 2, 2) assert test_1.v.x == pytest.approx(2, rel=1e-3) assert test_1.v.y == pytest.approx(0, rel=1e-3) assert test_1.v.z == pytest.approx(0, rel=1e-3) test_2 = ray.reflect(normal_2, origin_1) assert test_2.p.x == pytest.approx(1, rel=1e-3) assert test_2.p.y == pytest.approx(3, rel=1e-3) assert test_2.p.z == pytest.approx(2, rel=1e-3) assert test_2.v.x == pytest.approx(2, rel=1e-3) assert test_2.v.y == pytest.approx(0, rel=1e-3) assert test_2.v.z == pytest.approx(0, rel=1e-3)
def test_intersect_line_ray(): """Test the Plane intersect_line_ray method.""" pt = Point3D(2, 2, 2) vec = Vector3D(0, 2, 0) plane = Plane(vec, pt) test_seg = LineSegment3D(Point3D(0, 4, 0), Vector3D(1, 1, 1)) assert plane.intersect_line_ray(test_seg) is None test_seg = LineSegment3D(Point3D(0, 0, 0), Vector3D(3, 3, 3)) assert plane.intersect_line_ray(test_seg) == Point3D(2, 2, 2) test_ray = Ray3D(Point3D(0, 4, 0), Vector3D(1, 1, 1)) assert plane.intersect_line_ray(test_ray) is None test_ray = Ray3D(Point3D(0, 4, 0), Vector3D(-1, -1, -1)) assert plane.intersect_line_ray(test_ray) == Point3D(-2, 2, -2)
def test_intersect_plane(): """Test the Plane intersect_plane method.""" pt_1 = Point3D(2, 2, 2) vec_1 = Vector3D(0, 2, 0) plane_1 = Plane(vec_1, pt_1) pt_2 = Point3D(0, 0, 0) vec_2 = Vector3D(2, 0, 0) plane_2 = Plane(vec_2, pt_2) pt_3 = Point3D(0, 0, 0) vec_3 = Vector3D(0, 2, 0) plane_3 = Plane(vec_3, pt_3) assert plane_1.intersect_plane(plane_2) == Ray3D( Point3D(0, 2, 0), Vector3D(0, 0, -1)) assert plane_1.intersect_plane(plane_3) is None assert plane_2.intersect_plane(plane_3) == Ray3D( Point3D(0, 0, 0), Vector3D(0, 0, 1))
def test_scale_world_origin(): """Test the Ray3D scale method with None origin.""" pt = Point3D(2, 2, 2) vec = Vector3D(0, 2, 0) ray = Ray3D(pt, vec) new_ray = ray.scale(2) assert new_ray.p == Point3D(4, 4, 4) assert new_ray.v == Point3D(0, 4) assert new_ray.v.magnitude == 4
def test_intersect_line_ray(): """Test the Face3D intersect_line_ray method.""" pts = (Point3D(0, 0, 2), Point3D(2, 0, 2), Point3D(2, 1, 2), Point3D(1, 1, 2), Point3D(1, 2, 2), Point3D(0, 2, 2)) plane = Plane(Vector3D(0, 0, 1), Point3D(0, 0, 2)) face = Face3D(pts, plane) ray_1 = Ray3D(Point3D(0.5, 0.5, 0), Vector3D(0, 0, 1)) ray_2 = Ray3D(Point3D(0.5, 0.5, 0), Vector3D(0, 0, -1)) ray_3 = Ray3D(Point3D(1.5, 1.5, 0), Vector3D(0, 0, 1)) ray_4 = Ray3D(Point3D(-1, -1, 0), Vector3D(0, 0, 1)) line_1 = LineSegment3D(Point3D(0.5, 0.5, 0), Vector3D(0, 0, 3)) line_2 = LineSegment3D(Point3D(0.5, 0.5, 0), Vector3D(0, 0, 1)) assert face.intersect_line_ray(ray_1) == Point3D(0.5, 0.5, 2) assert face.intersect_line_ray(ray_2) is None assert face.intersect_line_ray(ray_3) is None assert face.intersect_line_ray(ray_4) is None assert face.intersect_line_ray(line_1) == Point3D(0.5, 0.5, 2) assert face.intersect_line_ray(line_2) is None
def test_move(): """Test the Ray3D move method.""" pt = Point3D(2, 0, 2) vec = Vector3D(0, 2, 0) ray = Ray3D(pt, vec) vec_1 = Vector3D(2, 2, 2) new_ray = ray.move(vec_1) assert new_ray.p == Point3D(4, 2, 4) assert new_ray.v == vec assert new_ray.v.magnitude == 2
def test_distance_to_point(): """Test the Ray3D distance_to_point method.""" pt = Point3D(2, 2, 2) vec = Vector3D(0, 2, 0) ray = Ray3D(pt, vec) near_pt = Point3D(3, 3, 2) assert ray.distance_to_point(near_pt) == 1 near_pt = Point3D(2, 0, 2) assert ray.distance_to_point(near_pt) == 2 near_pt = Point3D(1, 5, 2) assert ray.distance_to_point(near_pt) == 1
def test_closest_point(): """Test the Ray3D closest_point method.""" pt = Point3D(2, 2, 2) vec = Vector3D(0, 2, 0) ray = Ray3D(pt, vec) near_pt = Point3D(3, 3, 0) assert ray.closest_point(near_pt) == Point3D(2, 3, 2) near_pt = Point3D(2, 0, 0) assert ray.closest_point(near_pt) == Point3D(2, 2, 2) near_pt = Point3D(1, 5, 0) assert ray.closest_point(near_pt) == Point3D(2, 5, 2)
def test_ray3d_init(): """Test the initalization of Ray3D objects and basic properties.""" pt = Point3D(2, 0, 2) vec = Vector3D(0, 2, 0) ray = Ray3D(pt, vec) str(ray) # test the string representation of the ray assert ray.p == Point3D(2, 0, 2) assert ray.v == Vector3D(0, 2, 0) flip_ray = ray.reverse() assert flip_ray.p == Point3D(2, 0, 2) assert flip_ray.v == Vector3D(0, -2, 0)
def test_scale(): """Test the Ray3D scale method.""" pt = Point3D(2, 2, 2) vec = Vector3D(0, 2, 0) ray = Ray3D(pt, vec) origin_1 = Point3D(0, 2, 2) origin_2 = Point3D(1, 1, 2) new_ray = ray.scale(2, origin_1) assert new_ray.p == Point3D(4, 2, 2) assert new_ray.v == Point3D(0, 4, 0) assert new_ray.v.magnitude == 4 new_ray = ray.scale(2, origin_2) assert new_ray.p == Point3D(3, 3, 2) assert new_ray.v == Point3D(0, 4, 0)
def test_rotate_xy(): """Test the Ray3D rotate_xy method.""" pt = Point3D(2, 2, 2) vec = Vector3D(0, 2, 0) ray = Ray3D(pt, vec) origin_1 = Point3D(0, 2, 2) test_1 = ray.rotate_xy(math.pi, origin_1) assert test_1.p.x == pytest.approx(-2, rel=1e-3) assert test_1.p.y == pytest.approx(2, rel=1e-3) assert test_1.v.x == pytest.approx(0, rel=1e-3) assert test_1.v.y == pytest.approx(-2, rel=1e-3) test_2 = ray.rotate_xy(math.pi / 2, origin_1) assert test_2.p.x == pytest.approx(0, rel=1e-3) assert test_2.p.y == pytest.approx(4, rel=1e-3) assert test_2.v.x == pytest.approx(-2, rel=1e-3) assert test_2.v.y == pytest.approx(0, rel=1e-3)
def test_linerayment2_mutability(): """Test the immutability of Ray3D objects.""" pt = Point3D(2, 0, 0) vec = Vector3D(0, 2, 0) ray = Ray3D(pt, vec) assert isinstance(ray, Ray3D) with pytest.raises(AttributeError): ray.p.x = 3 with pytest.raises(AttributeError): ray.v.x = 3 with pytest.raises(AttributeError): ray.p = Point3D(0, 0, 0) with pytest.raises(AttributeError): ray.v = Vector3D(2, 2, 0) ray_copy = ray.duplicate() assert ray.p == ray_copy.p assert ray.v == ray_copy.v
def ground_by_custom_surface(self, ground_geometry, tolerance=0.01, angle_tolerance=1.0): """Set ground boundary conditions using an array of Face3D for the ground. Room faces that are coplanar with the ground surface or lie completely below it will get a Ground boundary condition while those above will get an Outdoors boundary condition. Existing Faces with an indoor boundary condition will be unaffected. Args: ground_geometry: An array of ladybug_geometry Face3D that together represent the ground surface. tolerance: The minimum difference between the coordinate values of two vertices at which they can be considered equivalent. Default: 0.01, suitable for objects in meters. angle_tolerance: The max angle in degrees that the plane normals can differ from one another in order for them to be considered coplanar. (Default: 1) """ # default values used throughout the check ang_tol = math.degrees(angle_tolerance) up_vec = Vector3D(0, 0, 1) # loop through the faces and check their relation to the ground geometry for face in self.faces: if isinstance(face.boundary_condition, (Outdoors, Ground)): ray = Ray3D(face.center, up_vec) for grnd_geo in ground_geometry: # first check if the surface is below the ground surface if grnd_geo.intersect_line_ray(ray): face.boundary_condition = boundary_conditions.ground break # then check if the Face is coplanar with the ground face pl1, pl2 = face.geometry.plane, grnd_geo.plane if pl1.is_coplanar_tolerance(pl2, tolerance, ang_tol): if grnd_geo.is_point_on_face(face.center, tolerance): face.boundary_condition = boundary_conditions.ground break else: face.boundary_condition = boundary_conditions.outdoors
def set_top_exposed_by_story_above(self, story_above, tolerance=0.01): """Set the child Room2Ds of this object to have ceilings exposed to the outdoors. Args: story_above: A Story object that sits above this Story. Each Room2D of this Story will be checked to see if it intersects the Story above and the top exposure will be set based on this. tolerance: The minimum difference between the coordinate values of two faces at which they can be considered adjacent. Default: 0.01, suitable for objects in meters. """ up_vec = Vector3D(0, 0, 1) for room in self._room_2ds: face_ray = Ray3D(room._floor_geometry._point_on_face(tolerance), up_vec) for other_room in story_above._room_2ds: if other_room._floor_geometry.intersect_line_ray(face_ray) is not None: room.is_top_exposed = False break else: room.is_top_exposed = True
# convert all of the _source_geo and contex into a single Brep for ray tracing rtrace_brep = join_geometry_to_brep(_source_geo + context_) # autocompute the first and last bounce length if it's unspecified if _first_length_ is None or _last_length_ is None: max_pt, min_pt = (to_point3d(p) for p in bounding_box_extents(rtrace_brep)) diag_dist = max_pt.distance_to_point(min_pt) _first_length_ = diag_dist if _first_length_ is None else _first_length_ _last_length_ = diag_dist if _last_length_ is None else _last_length_ # create the gridded mesh from the _source_geo and set up the starting rays study_mesh = to_joined_gridded_mesh3d(_source_geo, _grid_size) move_vec = neg_lb_vec * _first_length_ source_points = [pt + move_vec for pt in study_mesh.face_centroids] lb_rays = [Ray3D(pt, lb_vec) for pt in source_points] start_rays = [from_ray3d(ray) for ray in lb_rays] # trace each ray through the geometry cutoff_ang = math.pi / 2 rtrace_geo = [rtrace_brep] rays, int_pts = [], [] for ray, pt, norm in zip(start_rays, source_points, study_mesh.face_normals): if norm.angle(neg_lb_vec) < cutoff_ang: pl_pts = trace_ray(ray, rtrace_geo, _bounce_count_ + 1) # if the intersection was successful, create a polyline represeting the ray if pl_pts: # gather all of the intersection points all_pts = [pt] for i_pt in pl_pts:
def to_ray3d(ray): """Ladybug Ray3D from Rhino Ray3d.""" return Ray3D(to_point3d(ray.Position), to_vector3d(ray.Direction))