def test_arc2_intersect_line_ray(): """Test the Arc2D intersect_line_ray method.""" pt = Point2D(2, 0) arc = Arc2D(pt, 1, 0, math.pi) circle = Arc2D(pt, 1) seg1 = LineSegment2D(pt, Vector2D(2, 2)) seg2 = LineSegment2D(Point2D(0, -2), Vector2D(6, 6)) seg3 = LineSegment2D(pt, Vector2D(0.5, 0.5)) int1 = arc.intersect_line_ray(seg1) assert len(int1) == 1 assert int1[0].x == pytest.approx(2.71, rel=1e-2) assert int1[0].y == pytest.approx(0.71, rel=1e-2) int2 = circle.intersect_line_ray(seg1) assert len(int2) == 1 assert int2[0].x == pytest.approx(2.71, rel=1e-2) assert int2[0].y == pytest.approx(0.71, rel=1e-2) int3 = arc.intersect_line_ray(seg2) assert len(int3) == 1 assert int3[0].x == pytest.approx(2.71, rel=1e-2) assert int3[0].y == pytest.approx(0.71, rel=1e-2) int4 = circle.intersect_line_ray(seg2) assert len(int4) == 2 assert int4[0].x == pytest.approx(2.71, rel=1e-2) assert int4[0].y == pytest.approx(0.71, rel=1e-2) assert int4[1].x == pytest.approx(1.29, rel=1e-2) assert int4[1].y == pytest.approx(-0.71, rel=1e-2) assert arc.intersect_line_ray(seg3) is None
def test_intersect_line_ray_colinear(): """Test the LineSegment2D intersect_line_ray method with colinear segments.""" pt_1 = Point2D(3.5362137509358353, -2.3574758339572237) vec_1 = Vector2D(2.6625609418810905, -1.7750406279207271) seg_1 = LineSegment2D(pt_1, vec_1) pt_2 = Point2D(-1.7889081328263625, 1.1926054218842419) vec_2 = Vector2D(1.7889081328263625, -1.1926054218842419) seg_2 = LineSegment2D(pt_2, vec_2) assert seg_1.intersect_line_ray(seg_2) is None seg_1 = seg_1.flip() assert seg_1.intersect_line_ray(seg_2) is None seg_2 = seg_2.flip() assert seg_1.intersect_line_ray(seg_2) is None pt_1 = Point2D(0, 0) vec_1 = Vector2D(1, 0) seg_1 = LineSegment2D(pt_1, vec_1) pt_2 = Point2D(0, 0) vec_2 = Vector2D(0, 1) seg_2 = LineSegment2D(pt_2, vec_2) assert seg_1.intersect_line_ray(seg_2) == Point2D(0, 0) pt_1 = Point2D(1, 0) vec_1 = Vector2D(-1, 0) seg_1 = LineSegment2D(pt_1, vec_1) assert seg_1.intersect_line_ray(seg_2) == Point2D(0, 0) pt_2 = Point2D(0, 1) vec_2 = Vector2D(0, -1) seg_2 = LineSegment2D(pt_2, vec_2) assert seg_1.intersect_line_ray(seg_2) == Point2D(0, 0) pt_1 = Point2D(0, -1) vec_1 = Vector2D(0, 1) seg_1 = LineSegment2D(pt_1, vec_1) assert seg_1.intersect_line_ray(seg_2) is None pt_2 = Point2D(0, 0) vec_2 = Vector2D(-1, 0) seg_2 = LineSegment2D(pt_2, vec_2) assert seg_1.intersect_line_ray(seg_2) == Point2D(0, 0) pt_2 = Point2D(-1, 0) vec_2 = Vector2D(1, 0) seg_2 = LineSegment2D(pt_2, vec_2) assert seg_1.intersect_line_ray(seg_2) == Point2D(0, 0)
def test_equality(): """Test the equality of LineSegment2D objects.""" pt = Point2D(2, 0) vec = Vector2D(0, 2) seg = LineSegment2D(pt, vec) seg_dup = seg.duplicate() seg_alt = LineSegment2D(Point2D(2, 0.1), vec) assert seg is seg assert seg is not seg_dup assert seg == seg_dup assert hash(seg) == hash(seg_dup) assert seg != seg_alt assert hash(seg) != hash(seg_alt)
def test_subdivide(): """Test the LineSegment2D subdivide methods.""" pt = Point2D(2, 2) vec = Vector2D(0, 2) seg = LineSegment2D(pt, vec) divisions = seg.subdivide(0.5) assert len(divisions) == 5 assert divisions[0] == pt assert divisions[1] == Point2D(2, 2.5) assert divisions[2] == Point2D(2, 3) assert divisions[3] == Point2D(2, 3.5) assert divisions[4] == Point2D(2, 4) divisions = seg.subdivide([1, 0.5, 0.25]) assert len(divisions) == 5 assert divisions[0] == pt assert divisions[1] == Point2D(2, 3) assert divisions[2] == Point2D(2, 3.5) assert divisions[3] == Point2D(2, 3.75) assert divisions[4] == Point2D(2, 4) divisions = seg.subdivide_evenly(4) assert len(divisions) == 5 assert divisions[0] == pt assert divisions[1] == Point2D(2, 2.5) assert divisions[2] == Point2D(2, 3) assert divisions[3] == Point2D(2, 3.5) assert divisions[4] == Point2D(2, 4)
def test_intersect_line_ray(): """Test the LineSegment2D intersect_line_ray method.""" pt_1 = Point2D(2, 2) vec_1 = Vector2D(0, 2) seg_1 = LineSegment2D(pt_1, vec_1) pt_2 = Point2D(0, 3) vec_2 = Vector2D(4, 0) seg_2 = LineSegment2D(pt_2, vec_2) pt_3 = Point2D(0, 0) vec_3 = Vector2D(1, 1) seg_3 = LineSegment2D(pt_3, vec_3) assert seg_1.intersect_line_ray(seg_2) == Point2D(2, 3) assert seg_1.intersect_line_ray(seg_3) is None
def test_reflect(): """Test the LineSegment2D reflect method.""" pt = Point2D(2, 2) vec = Vector2D(0, 2) seg = LineSegment2D(pt, vec) origin_1 = Point2D(0, 1) origin_2 = Point2D(1, 1) normal_1 = Vector2D(0, 1) normal_2 = Vector2D(-1, 1).normalize() assert seg.reflect(normal_1, origin_1).p == Point2D(2, 0) assert seg.reflect(normal_1, origin_1).v == Vector2D(0, -2) assert seg.reflect(normal_1, origin_2).p == Point2D(2, 0) assert seg.reflect(normal_1, origin_2).v == Vector2D(0, -2) test_1 = seg.reflect(normal_2, origin_2) assert test_1.p == Point2D(2, 2) assert test_1.v.x == pytest.approx(2, rel=1e-3) assert test_1.v.y == pytest.approx(0, rel=1e-3) test_2 = seg.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_1.v.x == pytest.approx(2, rel=1e-3) assert test_1.v.y == pytest.approx(0, rel=1e-3)
def test_linesegment2_to_from_dict(): """Test the to/from dict of LineSegment2D objects.""" pt = Point2D(2, 0) vec = Vector2D(0, 2) seg = LineSegment2D(pt, vec) seg_dict = seg.to_dict() new_seg = LineSegment2D.from_dict(seg_dict) assert isinstance(new_seg, LineSegment2D) assert new_seg.to_dict() == seg_dict
def custom_hour_lines2d(self, hour_labels): """Get a list of LineSegment2D for a list of numbers representing hour labels. Args: hour_labels: An array of numbers from 0 to 24 representing the hours to display. (eg. [0, 3, 6, 9, 12, 15, 18, 21, 24]) """ _hour_points, _hour_text = self._compute_hour_line_pts(hour_labels) vec = Vector2D(self._num_x * self._x_dim) return [LineSegment2D(pt, vec) for pt in _hour_points]
def test_scale_world_origin(): """Test the LineSegment2D scale method with None origin.""" pt = Point2D(2, 2) vec = Vector2D(0, 2) seg = LineSegment2D(pt, vec) new_seg = seg.scale(2) assert new_seg.p == Point2D(4, 4) assert new_seg.v == Point2D(0, 4) assert new_seg.length == 4
def test_closest_points_between_line(): """Test the LineSegement2D distance_to_point method.""" pt_1 = Point2D(2, 2) vec_1 = Vector2D(0, 2) seg_1 = LineSegment2D(pt_1, vec_1) pt_2 = Point2D(0, 3) vec_2 = Vector2D(1, 0) seg_2 = LineSegment2D(pt_2, vec_2) pt_3 = Point2D(0, 0) vec_3 = Vector2D(1, 1) seg_3 = LineSegment2D(pt_3, vec_3) assert seg_1.closest_points_between_line(seg_2) == (Point2D(2, 3), Point2D(1, 3)) assert seg_1.closest_points_between_line(seg_3) == (Point2D(2, 2), Point2D(1, 1)) assert seg_1.distance_to_line(seg_2) == 1 assert seg_1.distance_to_line(seg_3) == pytest.approx(1.41421, rel=1e-3)
def test_equivalent(): """Testing the equivalence of LineSegment2D objects.""" seg1 = LineSegment2D(Point2D(0.5, 0.5), Vector2D(1.5, 2.5)) # LineSegment2D (<0.50, 0.50> to <2.00, 3.00>) # Test equal seg1, same order seg2 = LineSegment2D(Point2D(0.5, 0.5), Vector2D(1.5, 2.5)) assert seg1.is_equivalent(seg2, 1e-10) # Test equal, diff order seg2 = LineSegment2D(Point2D(2.00, 3.00), Vector2D(-1.5, -2.5)) assert seg1.is_equivalent(seg2, 1e-10) # Test not equal first point seg2 = LineSegment2D(Point2D(2.001, 3.00), Vector2D(-1.5, -2.5)) assert not seg1.is_equivalent(seg2, 1e-10) # Test not equal second point seg2 = LineSegment2D(Point2D(0.5001, 0.5), Vector2D(1.5, 2.5)) assert not seg1.is_equivalent(seg2, 1e-10)
def test_distance_to_point(): """Test the LineSegment2D distance_to_point method.""" pt = Point2D(2, 2) vec = Vector2D(0, 2) seg = LineSegment2D(pt, vec) near_pt = Point2D(3, 3) assert seg.distance_to_point(near_pt) == 1 near_pt = Point2D(2, 0) assert seg.distance_to_point(near_pt) == 2 near_pt = Point2D(2, 5) assert seg.distance_to_point(near_pt) == 1
def test_closest_point(): """Test the LineSegment2D closest_point method.""" pt = Point2D(2, 2) vec = Vector2D(0, 2) seg = LineSegment2D(pt, vec) near_pt = Point2D(3, 3) assert seg.closest_point(near_pt) == Point2D(2, 3) near_pt = Point2D(2, 0) assert seg.closest_point(near_pt) == Point2D(2, 2) near_pt = Point2D(2, 5) assert seg.closest_point(near_pt) == Point2D(2, 4)
def test_move(): """Test the LineSegment2D move method.""" pt = Point2D(2, 0) vec = Vector2D(0, 2) seg = LineSegment2D(pt, vec) vec_1 = Vector2D(2, 2) new_seg = seg.move(vec_1) assert new_seg.p == Point2D(4, 2) assert new_seg.v == vec assert new_seg.p1 == Point2D(4, 2) assert new_seg.p2 == Point2D(4, 4)
def test_intersect_line_ray(): """Test the Polygon2D intersect_line_ray method.""" pts = (Point2D(0, 0), Point2D(2, 0), Point2D(2, 2), Point2D(0, 2)) polygon = Polygon2D(pts) ray_1 = Ray2D(Point2D(-1, 1), Vector2D(1, 0)) ray_2 = Ray2D(Point2D(1, 1), Vector2D(1, 0)) ray_3 = Ray2D(Point2D(1, 1), Vector2D(11, 0)) ray_4 = Ray2D(Point2D(-1, 1), Vector2D(-1, 0)) assert len(polygon.intersect_line_ray(ray_1)) == 2 assert len(polygon.intersect_line_ray(ray_2)) == 1 assert len(polygon.intersect_line_ray(ray_3)) == 1 assert len(polygon.intersect_line_ray(ray_4)) == 0 line_1 = LineSegment2D(Point2D(-1, 1), Vector2D(0.5, 0)) line_2 = LineSegment2D(Point2D(-1, 1), Vector2D(2, 0)) line_3 = LineSegment2D(Point2D(-1, 1), Vector2D(3, 0)) assert len(polygon.intersect_line_ray(line_1)) == 0 assert len(polygon.intersect_line_ray(line_2)) == 1 assert len(polygon.intersect_line_ray(line_3)) == 2
def test_scale(): """Test the LineSegment2D scale method.""" pt = Point2D(2, 2) vec = Vector2D(0, 2) seg = LineSegment2D(pt, vec) origin_1 = Point2D(0, 2) origin_2 = Point2D(1, 1) new_seg = seg.scale(2, origin_1) assert new_seg.p == Point2D(4, 2) assert new_seg.v == Point2D(0, 4) assert new_seg.length == 4 new_seg = seg.scale(2, origin_2) assert new_seg.p == Point2D(3, 3) assert new_seg.v == Point2D(0, 4)
def test_rotate(): """Test the LineSegment2D rotate method.""" pt = Point2D(2, 2) vec = Vector2D(0, 2) seg = LineSegment2D(pt, vec) origin_1 = Point2D(0, 2) test_1 = seg.rotate(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 = seg.rotate(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_linesegment2_immutability(): """Test the immutability of LineSegment2D objects.""" pt = Point2D(2, 0) vec = Vector2D(0, 2) seg = LineSegment2D(pt, vec) assert isinstance(seg, LineSegment2D) with pytest.raises(AttributeError): seg.p.x = 3 with pytest.raises(AttributeError): seg.v.x = 3 with pytest.raises(AttributeError): seg.p = Point2D(0, 0) with pytest.raises(AttributeError): seg.v = Vector2D(2, 2) seg_copy = seg.duplicate() assert seg.p == seg_copy.p assert seg.v == seg_copy.v
def test_linesegment2_init(): """Test the initalization of LineSegment2D objects and basic properties.""" pt = Point2D(2, 0) vec = Vector2D(0, 2) seg = LineSegment2D(pt, vec) str(seg) # test the string representation of the line segment assert seg.p == Point2D(2, 0) assert seg.v == Vector2D(0, 2) assert seg.p1 == Point2D(2, 0) assert seg.p2 == Point2D(2, 2) assert seg.midpoint == Point2D(2, 1) assert seg.point_at(0.25) == Point2D(2, 0.5) assert seg.point_at_length(1) == Point2D(2, 1) assert seg.length == 2 flip_seg = seg.flip() assert flip_seg.p == Point2D(2, 2) assert flip_seg.v == Vector2D(0, -2)
def month_lines2d(self): """Get a list of LineSegment2D for the month intervals of the chart.""" if not self._month_points: self._compute_month_line_pts() vec = Vector2D(0, self._num_y * self._y_dim) return [LineSegment2D(pt, vec) for pt in self._month_points]
def hour_lines2d(self): """Get a list of LineSegment2D for the 6-hour intervals of the chart.""" if not self._hour_points: self._compute_static_hour_line_pts() vec = Vector2D(self._num_x * self._x_dim) return [LineSegment2D(pt, vec) for pt in self._hour_points]
def next_event(self): """ Computes the event (change to edge) associated with each polygon vertex. Reflex vertices can result in split or edge events, while non-reflex vertices will result in only edge events. Returns: EdgeEvent associated with self. """ events = [] if self.is_reflex: # a reflex vertex may generate a split event # split events happen when a vertex hits an opposite edge, # splitting the polygon in two. log.debug("looking for split candidates for vertex %s", self) for edge in self.original_edges: if (edge.edge.is_equivalent(self.edge_left, self.tol) or edge.edge.is_equivalent(self.edge_right, self.tol)): continue log.debug('\tconsidering EDGE %s', edge) # A potential b is at the intersection of between our own bisector and # the bisector of the angle between the tested edge and any one of our # own edges. # We choose the 'less parallel' edge (in order to exclude a # potentially parallel edge) # Make normalized copies of vectors norm_edge_left_v = self.edge_left.v.normalize() norm_edge_right_v = self.edge_right.v.normalize() norm_edge_v = edge.edge.v.normalize() # Compute dot leftdot = abs(norm_edge_left_v.dot(norm_edge_v)) rightdot = abs(norm_edge_right_v.dot(norm_edge_v)) selfedge = self.edge_left if leftdot < rightdot else self.edge_right # otheredge = self.edge_left if leftdot > rightdot else self.edge_right # Make copies of edges and compute intersection self_edge_copy = LineSegment2D(selfedge.p, selfedge.v) edge_edge_copy = LineSegment2D(edge.edge.p, edge.edge.v) # Ray line intersection i = intersection2d.intersect_line2d_infinite( edge_edge_copy, self_edge_copy) if (i is not None) and (not i.is_equivalent( self.point, self.tol)): # locate candidate b linvec = (self.point - i).normalize() edvec = edge.edge.v.normalize() if linvec.dot(edvec) < 0: edvec = -edvec bisecvec = edvec + linvec if abs(bisecvec.magnitude) < self.tol: continue bisector = LineSegment2D(i, bisecvec) b = intersection2d.intersect_line2d( self.bisector, bisector) if b is None: continue # check eligibility of b # a valid b should lie within the area limited by the edge and the # bisectors of its two vertices: _left_bisector_norm = edge.bisector_left.v.normalize() _left_to_b_norm = (b - edge.bisector_left.p).normalize() xleft = _left_bisector_norm.determinant( _left_to_b_norm) > 0 _right_bisector_norm = edge.bisector_right.v.normalize() _right_to_b_norm = (b - edge.bisector_right.p).normalize() xright = _right_bisector_norm.determinant( _right_to_b_norm) < 0 _edge_edge_norm = edge.edge.v.normalize() _b_to_edge_norm = (b - edge.edge.p).normalize() xedge = _edge_edge_norm.determinant(_b_to_edge_norm) < 0 if not (xleft and xright and xedge): log.debug('\t\tDiscarded candidate %s (%s-%s-%s)', b, xleft, xright, xedge) continue log.debug('\t\tFound valid candidate %s', b) _dist_line_to_b = LineSegment2D( edge.edge.p, edge.edge.v).distance_to_point(b) if _dist_line_to_b < self.tol: _dist_line_to_b = 0.0 _new_split_event = _SplitEvent(_dist_line_to_b, b, self, edge.edge) events.append(_new_split_event) # Intersect line2d with line2d (does not assume lines are infinite) i_prev = intersection2d.intersect_line2d_infinite( self.prev.bisector, self.bisector) i_next = intersection2d.intersect_line2d_infinite( self.next.bisector, self.bisector) # Make EdgeEvent and append to events if i_prev is not None: dist_to_i_prev = LineSegment2D( self.edge_left.p.duplicate(), self.edge_left.v.duplicate()).distance_to_point(i_prev) if dist_to_i_prev < self.tol: dist_to_i_prev = 0.0 events.append(_EdgeEvent(dist_to_i_prev, i_prev, self.prev, self)) if i_next is not None: dist_to_i_next = LineSegment2D( self.edge_right.p.duplicate(), self.edge_right.v.duplicate()).distance_to_point(i_next) if dist_to_i_next < self.tol: dist_to_i_next = 0.0 events.append(_EdgeEvent(dist_to_i_next, i_next, self, self.next)) if not events: return None ev = min(events, key=lambda event: self.point.distance_to_point( event.intersection_point)) log.info('Generated new event for %s: %s', self, ev) return ev
def y_axis_lines(self): """Get a list of LineSegment2D for the Y-axis values of the chart.""" if not self._y_axis_points: self._compute_y_axis_points() vec = Vector2D(len(self._months_int) * self._x_dim) return [LineSegment2D(pt, vec) for pt in self._y_axis_points]