def test_ray2d_angle(self): ray = ConstructionRay((10, 10), angle=HALF_PI) assert ray._is_vertical is True ray = ConstructionRay((10, 10), angle=0) assert ray._is_horizontal is True ray = ConstructionRay((10, 10), angle=math.pi / 4) assert math.isclose(ray._slope, 1.)
def test_ray2d_intersect(self): ray1 = ConstructionRay((10, 1), (20, 10)) ray2 = ConstructionRay((17, -7), (-10, 3)) point = ray1.intersect(ray2) assert point.isclose(Vector(5.7434, -2.8309), abs_tol=1e-4) assert ray1.is_parallel(ray2) is False
def test_ray2d_intersect_with_vertical(self): ray1 = ConstructionRay((10, 1), (10, -7)) ray2 = ConstructionRay((-10, 3), (17, -7)) point = ray1.intersect(ray2) assert point.isclose(Vector(10., -4.4074), abs_tol=1e-4) with pytest.raises(ArithmeticError): _ = ray1.yof(1)
def test_ray2d_intersect_with_vertical_and_horizontal(self): ray1 = ConstructionRay((-10, 10), (10, 10)) ray2 = ConstructionRay((5, 0), (5, 20)) point = ray1.intersect(ray2) assert point.y == 10 assert point.x == 5 assert point.isclose(Vector(5.0, 10.0), abs_tol=1e-4)
def offset_vertices_2d(vertices: Iterable['Vertex'], offset: float, closed: bool = False) -> Iterable['Vec2']: """ Yields vertices of the offset line to the shape defined by `vertices`. The source shape consist of straight segments and is located in the xy-plane, the z-axis of input vertices is ignored. Takes closed shapes into account if argument `closed` is ``True``, which yields intersection of first and last offset segment as first vertex for a closed shape. For closed shapes the first and last vertex can be equal, else an implicit closing segment from last to first vertex is added. A shape with equal first and last vertex is not handled automatically as closed shape. .. warning:: Adjacent collinear segments in `opposite` directions, same as a turn by 180 degree (U-turn), leads to unexpected results. Args: vertices: source shape defined by vertices offset: line offset perpendicular to direction of shape segments defined by vertices order, offset > ``0`` is 'left' of line segment, offset < ``0`` is 'right' of line segment closed: ``True`` to handle as closed shape """ vertices = Vec2.list(vertices) if len(vertices) < 2: raise ValueError('2 or more vertices required.') if closed and not vertices[0].isclose(vertices[-1]): # append first vertex as last vertex to close shape vertices.append(vertices[0]) # create offset segments offset_segments = list() for start, end in zip(vertices[:-1], vertices[1:]): offset_vec = (end - start).orthogonal().normalize(offset) offset_segments.append((start + offset_vec, end + offset_vec)) if closed: # insert last segment also as first segment offset_segments.insert(0, offset_segments[-1]) # first offset vertex = start point of first segment for open shapes if not closed: yield offset_segments[0][0] # yield intersection points of offset_segments if len(offset_segments) > 1: for (start1, end1), (start2, end2) in zip(offset_segments[:-1], offset_segments[1:]): try: # the usual case yield ConstructionRay(start1, end1).intersect( ConstructionRay(start2, end2)) except ParallelRaysError: # collinear segments yield end1 if not end1.isclose(start2): # it's an U-turn (180 deg) # creates an additional vertex! yield start2 # last offset vertex = end point of last segment for open shapes if not closed: yield offset_segments[-1][1]
def test_init_with_angle(self): point = (10, 10) ray = ConstructionRay(point, angle=0) ray_normal = ray.orthogonal(point) assert ray_normal._is_vertical is True ray = ConstructionRay(point, angle=-HALF_PI) assert ray._is_horizontal is False assert ray._is_vertical is True
def test_intersect_ray_pass(self): circle = ConstructionCircle((10., 10.), 3) ray1_hor = ConstructionRay((10., 15.), angle=0) ray2_hor = ConstructionRay((10., 5.), angle=0) ray1_vert = ConstructionRay((5., 10.), angle=HALF_PI) ray2_vert = ConstructionRay((15., 10.), angle=-HALF_PI) ray3 = ConstructionRay((13.24, 14.95), angle=0.3992) self.assertFalse(circle.intersect_ray(ray1_hor)) self.assertFalse(circle.intersect_ray(ray2_hor)) self.assertFalse(circle.intersect_ray(ray1_vert)) self.assertFalse(circle.intersect_ray(ray2_vert)) self.assertFalse(circle.intersect_ray(ray3))
def test_two_close_horizontal_rays(self): p1 = (39340.75302672016, 32489.73349764998) p2 = (39037.75302672119, 32489.73349764978) p3 = (38490.75302672015, 32489.73349764997) ray1 = ConstructionRay(p1, p2) ray2 = ConstructionRay(p2, p3) assert ray1.is_horizontal is True assert ray2.is_horizontal is True assert ray1.is_parallel(ray2) is True assert math.isclose( ray1.slope, ray2.slope) is False, 'Only slope testing is not sufficient'
def test_ray2d_parallel(self): ray1 = ConstructionRay((17, -8), (-10, 2)) ray2 = ConstructionRay((-10, 3), (17, -7)) ray3 = ConstructionRay((-10, 4), (17, -6)) assert ray2.is_parallel(ray3) is True assert ray1.is_parallel(ray3) is True with pytest.raises(ParallelRaysError): _ = ray2.intersect(ray3)
def _setup(self) -> None: """ Calc setup values and determines the point order of the dimension line points. """ self.measure_points = [Vector(point) for point in self.measure_points] # type: List[Vector] dimlineray = ConstructionRay(self.dimlinepos, angle=radians(self.angle)) # Type: ConstructionRay self.dimline_points = [self._get_point_on_dimline(point, dimlineray) for point in self.measure_points] # type: List[Vector] self.point_order = self._indices_of_sorted_points(self.dimline_points) # type: List[int] self._build_vectors()
def test_touch(testnum, x, y, _angle, abs_tol=1e-6): result = True ray = ConstructionRay((x, y), angle=_angle) points = circle.intersect_ray(ray, abs_tol=abs_tol) if len(points) != 1: result = False else: point = points[0] # print ("{0}: x= {1:.{places}f} y= {2:.{places}f} : x'= {3:.{places}f} y' = {4:.{places}f}".format(testnum, x, y, point[0], point[1], places=places)) if not isclose(point[0], x, abs_tol=abs_tol): result = False if not isclose(point[1], y, abs_tol=abs_tol): result = False return result
def test_bisectrix(self): ray1 = ConstructionRay((10, 10), angle=math.pi / 3) ray2 = ConstructionRay((3, -5), angle=math.pi / 2) ray3 = ConstructionRay((1, 1), angle=math.pi / 3) a = ray1.bisectrix(ray2) assert math.isclose(a._angle, 1.309, abs_tol=1e-4) assert math.isclose(a.yof(7), 12.80385, abs_tol=1e-4) with pytest.raises(ParallelRaysError): _ = ray1.bisectrix(ray3)
def test_intersect_ray_intersect(self): circle = ConstructionCircle((10., 10.), 3) ray_vert = ConstructionRay((8.5, 10.), angle=HALF_PI) cross_points = circle.intersect_ray(ray_vert) self.assertEqual(len(cross_points), 2) p1, p2 = cross_points if p1[1] > p2[1]: p1, p2 = p2, p1 self.assertTrue(is_close_points(p1, (8.5, 7.4019), abs_tol=1e-4)) self.assertTrue(is_close_points(p2, (8.5, 12.5981), abs_tol=1e-4)) ray_hor = ConstructionRay((10, 8.5), angle=0.) cross_points = circle.intersect_ray(ray_hor) self.assertEqual(len(cross_points), 2) p1, p2 = cross_points if p1[0] > p2[0]: p1, p2 = p2, p1 self.assertTrue(is_close_points(p1, (7.4019, 8.5), abs_tol=1e-4)) self.assertTrue(is_close_points(p2, (12.5981, 8.5), abs_tol=1e-4)) ray_slope = ConstructionRay((5, 5), (16, 12)) cross_points = circle.intersect_ray(ray_slope) self.assertEqual(len(cross_points), 2) p1, p2 = cross_points if p1[0] > p2[0]: p1, p2 = p2, p1 self.assertTrue(is_close_points(p1, (8.64840, 7.3217), abs_tol=1e-4)) self.assertTrue(is_close_points(p2, (12.9986, 10.0900), abs_tol=1e-4)) # ray with slope through midpoint ray_slope = ConstructionRay((10, 10), angle=HALF_PI / 2) cross_points = circle.intersect_ray(ray_slope) self.assertEqual(len(cross_points), 2) p1, p2 = cross_points if p1[0] > p2[0]: p1, p2 = p2, p1 # print (p1[0], p1[1], p2[0], p2[1]) self.assertTrue(is_close_points(p1, (7.8787, 7.8787), abs_tol=1e-4)) self.assertTrue(is_close_points(p2, (12.1213, 12.1213), abs_tol=1e-4)) # horizontal ray through midpoint ray_hor = ConstructionRay((10, 10), angle=0) cross_points = circle.intersect_ray(ray_hor) self.assertEqual(len(cross_points), 2) p1, p2 = cross_points if p1[0] > p2[0]: p1, p2 = p2, p1 # print (p1[0], p1[1], p2[0], p2[1]) self.assertTrue(is_close_points(p1, (7, 10), abs_tol=1e-5)) self.assertTrue(is_close_points(p2, (13, 10), abs_tol=1e-5)) # vertical ray through midpoint ray_vert = ConstructionRay((10, 10), angle=HALF_PI) cross_points = circle.intersect_ray(ray_vert) self.assertEqual(len(cross_points), 2) p1, p2 = cross_points if p1[1] > p2[1]: p1, p2 = p2, p1 # print (p1[0], p1[1], p2[0], p2[1]) self.assertTrue(is_close_points(p1, (10, 7), abs_tol=1e-5)) self.assertTrue(is_close_points(p2, (10, 13), abs_tol=1e-5))
def center_of_3points_arc(point1: 'Vertex', point2: 'Vertex', point3: 'Vertex') -> Vector: """ Calc center point of 3 point arc. ConstructionCircle is defined by 3 points on the circle: point1, point2 and point3. """ ray1 = ConstructionRay(point1, point2) ray2 = ConstructionRay(point1, point3) midpoint1 = lerp(point1, point2) midpoint2 = lerp(point1, point3) center_ray1 = ray1.orthogonal(midpoint1) center_ray2 = ray2.orthogonal(midpoint2) return center_ray1.intersect(center_ray2)
def test_ray2d_normal(self): ray = ConstructionRay((-10, 3), (17, -7)) ortho = ray.orthogonal((3, 3)) point = ray.intersect(ortho) assert point.isclose(Vector(1.4318, -1.234), abs_tol=1e-4)
def test_ray2d_normal_vertical(self): ray = ConstructionRay((10, 1), (10, -7)) # vertical line ortho = ray.orthogonal((3, 3)) point = ray.intersect(ortho) assert point.isclose(Vector(10, 3))
def test_ray2d_parallel_vertical(self): ray1 = ConstructionRay((10, 1), (10, -7)) ray2 = ConstructionRay((11, 0), angle=HALF_PI) ray3 = ConstructionRay((12, -10), (12, 7)) ray4 = ConstructionRay((0, 0), (1, 1)) ray5 = ConstructionRay((0, 0), angle=0) with pytest.raises(ParallelRaysError): _ = ray1.intersect(ray3) assert ray1.is_parallel(ray3) is True assert ray1.is_parallel(ray2) is True assert ray2.is_parallel(ray2) is True assert ray1.is_parallel(ray4) is False assert ray2.is_parallel(ray4) is False assert ray3.is_parallel(ray4) is False assert ray1.is_parallel(ray5) is False assert ray2.is_parallel(ray5) is False assert ray3.is_parallel(ray5) is False # vertical rays can't calc a y-value with pytest.raises(ArithmeticError): _ = ray1.yof(-1.)
def test_Ray2D_get_x_y(self): ray1 = ConstructionRay((10, 1), (20, 10)) y = ray1.yof(15) assert math.isclose(y, 5.5) assert math.isclose(ray1.xof(y), 15.)
def test_ray2d_normal_horizontal(self): ray = ConstructionRay((10, 10), (20, 10)) # horizontal line ortho = ray.orthogonal((3, 3)) point = ray.intersect(ortho) assert point.isclose(Vector(3, 10))
def _get_point_on_dimline(point: 'Vertex', dimray: ConstructionRay) -> Vector: """ get the measure target point projection on the dimension line """ return dimray.intersect(dimray.orthogonal(point))