class TestPolygon(unittest.TestCase): vertices = [ Point(0, 0), Point(30, 0), Point(0, 30), ] polygon = Polygon(vertices) def test_sides(self): expected = [ Segment(self.vertices[0], self.vertices[1]), Segment(self.vertices[1], self.vertices[2]), Segment(self.vertices[2], self.vertices[0]) ] actual = self.polygon.sides() self.assertEqual(expected, actual) def test_centroid(self): expected = Point(10, 10) actual = self.polygon.centroid self.assertEqual(expected, actual) def test_doesnt_contain_point(self): point = Point(15, 20) self.assertFalse(self.polygon.contains_point(point)) def test_contains_point(self): point = Point(15, 10) self.assertTrue(self.polygon.contains_point(point)) def test_contains_vertex(self): self.assertTrue(self.polygon.contains_point(self.vertices[0]))
def test_to_polygon(self): actual = self.rect.to_polygon() expected = Polygon( [self.origin, Point(10, 0), Point(10, 5), Point(0, 5)]) self.assertEqual(expected, actual)
class TestAffineTransformations(unittest.TestCase): point = Point(7, 2) center = Point(2, 2) def test_make_positive_rotation(self): actual = make_rotation(math.pi / 2, self.center) expected = AffineTransform(0, 0, 4, 0, -1, 1) self.assertEqual(expected, actual)
class TestSegment(unittest.TestCase): start = Point(400, 0) end = Point(0, 400) segment = Segment(start, end) def test_length(self): expected = 400 * math.sqrt(2) actual = self.segment.length self.assertAlmostEqual(expected, actual) # --- POINT AT --- # def test_point_at_wrong_t(self): self.assertRaises(tparam.TParamError, self.segment.point_at, 56.7) def test_point_at(self): t = tparam.make(0.25) expected = Point(300, 100) actual = self.segment.point_at(t) self.assertEqual(expected, actual) def test_middle_point(self): expected = Point(200, 200) actual = self.segment.middle self.assertEqual(expected, actual) # --- CLOSEST POINT --- # def test_closest_point_is_start(self): p = Point(500, 20) expected = self.start actual = self.segment.closest_point_to(p) self.assertEqual(expected, actual) def test_closest_point_is_end(self): p = Point(20, 500) expected = self.end actual = self.segment.closest_point_to(p) self.assertEqual(expected, actual) def test_closest_point_is_middle(self): p = Point(250, 250) expected = Point(200, 200) actual = self.segment.closest_point_to(p) self.assertEqual(expected, actual) # --- INTERSECTIONS --- # def test_parallel_segments_no_intersection(self): other = Segment(Point(200, 0), Point(0, 200)) actual = self.segment.intersection_with(other) self.assertIsNone(actual) def test_segments_intersection(self): other = Segment(Point(0, 0), Point(400, 400)) expected = Point(200, 200) actual = self.segment.intersection_with(other) self.assertEqual(expected, actual)
def test_to_polygon_4_divisions(self): circle = Circle(Point(2, 5), 10) actual = circle.to_polygon(4) expected = Polygon([ Point(12, 5), Point(2, 15), Point(-8, 5), Point(2, -5) ]) self.assertEqual(expected, actual)
def to_polygon(self): """ Creates a `Polygon` equivalent to this rectangle. The polygon is made of the rectangle vertices in the following order: - (left, bottom) ≡ origin - (right, bottom) - (right, top) - (left, top) :return: `Polygon` """ return Polygon([ self.origin, Point(self.right, self.bottom), Point(self.right, self.top), Point(self.left, self.top) ])
def centroid(self): """ The centroid or geometric center of a plane figure is the arithmetic mean position of all the points in the figure. :return: `Point` """ vtx_count = len(self.vertices) vtx_sum = reduce(operator.add, self.vertices) return Point(vtx_sum.x / vtx_count, vtx_sum.y / vtx_count)
class TestCircle(unittest.TestCase): circle = Circle(Point(10, 10), 10) # --- PROPERTIES --- # def test_area(self): expected = math.pi * 100 self.assertAlmostEqual(expected, self.circle.area) def test_circumference(self): expected = math.pi * 20 self.assertAlmostEqual(expected, self.circle.circumference) # --- CONTAINS --- # def test_contains_point(self): point = Point(11, 12) self.assertTrue(self.circle.contains_point(point)) def test_doesnt_contain_point(self): point = Point(100, 50) self.assertFalse(self.circle.contains_point(point)) # --- OVERLAPS --- # def test_overlaps(self): other = Circle(Point(30, 30), 20) self.assertTrue(self.circle.overlaps(other)) def test_dont_overlap(self): other = Circle(Point(30, 30), 5) self.assertFalse(self.circle.overlaps(other)) # --- PENETRATION --- # def test_no_penetration(self): other = Circle(Point(30, 30), 5) self.assertIsNone(self.circle.penetration_vector(other)) def test_penetration(self): other = Circle(Point(30, 30), 20) actual = self.circle.penetration_vector(other) pen_proj = -(math.sqrt(2) * 15 - 20) expected = Vector(pen_proj, pen_proj) self.assertEqual(expected, actual) # --- TO POLYGON --- # def test_to_polygon_4_divisions(self): circle = Circle(Point(2, 5), 10) actual = circle.to_polygon(4) expected = Polygon([ Point(12, 5), Point(2, 15), Point(-8, 5), Point(2, -5) ]) self.assertEqual(expected, actual)
class TestPoint(unittest.TestCase): p = Point(1, 2) q = Point(4, 6) # <----- Distance -----> # def test_distance_to(self): expected = 5 actual = self.p.distance_to(self.q) self.assertAlmostEqual(expected, actual) # <----- Operations -----> # def test_plus(self): expected = Point(5, 8) actual = self.p + self.q self.assertEqual(expected, actual) def test_minus(self): expected = Vector(-3, -4) actual = self.p - self.q self.assertEqual(expected, actual)
def apply_to_point(self, point: Point): """ Computes a new `Point` result of applying this affine transformation to the given `point`. :param point: source `Point` :return: transformed `Point` """ return Point( (self.sx * point.x) + (self.shx * point.y) + self.tx, (self.shy * point.x) + (self.sy * point.y) + self.ty )
def make_rect_centered(center: Point, width: float, height: float): """ Computes a rectangle which center point is `center` and with a size of `width` and `height`. :param center: `Point` :param width: `float` :param height: `float` :return: """ origin = Point(center.x - width / 2, center.y - height / 2) return Rect(origin, Size(width, height))
def make_rect_containing_with_margin(points: List[Point], margin: float): """ Computes the smallest rectangle containing all the passed points, and adds a margin to all four sides. :param points: `[Point]` :param margin: `float` :return: `Rect` """ rect = make_rect_containing(points) return Rect( Point(rect.origin.x - margin, rect.origin.y - margin), Size(2 * margin + rect.size.width, 2 * margin + rect.size.height))
def make_scale(sx: float, sy: float, center=Point(0, 0)): """ Creates a scaling affine transformation about the `center` point and using the `sx` and `sy` scale factors. :param sx: scale in the x-axis :param sy: scale in the y-axis :param center: center point :return: `AffineTransform` """ return AffineTransform(sx=sx, sy=sy, tx=center.x * (1.0 - sx), ty=center.y * (1.0 - sy))
def make_rotation(radians: float, center=Point(0, 0)): """ Creates a rotation affine transformation of the given angle in radians about the `center` point. :param radians: angle in radians :param center: center point :return: `AffineTransform` """ cos = math.cos(radians) sin = math.sin(radians) one_minus_cos = 1.0 - cos return AffineTransform(sx=cos, sy=cos, tx=center.x * one_minus_cos + center.y * sin, ty=center.y * one_minus_cos - center.x * sin, shx=-sin, shy=sin)
def intersection_with(self, other): """ Computes the intersection between this rectangle and another rectangle `other`. The intersection result may be a `Rect` or `None`. :param other: `Rect` :return: `bool` """ h_overlap = self.__horizontal_overlap_with(other) if h_overlap is None: return None v_overlap = self.__vertical_overlap_with(other) if v_overlap is None: return None return Rect(Point(h_overlap.start, v_overlap.start), Size(h_overlap.length, v_overlap.length))
def make_rect_containing(points: List[Point]): """ Computes the smallest rectangle containing all the passed points. :param points: `[Point]` :return: `Rect` """ if not points: raise ValueError('Expected at least one point') first_point = points[0] min_x, max_x = first_point.x, first_point.x min_y, max_y = first_point.y, first_point.y for point in points[1:]: min_x, max_x = min(min_x, point.x), max(max_x, point.x) min_y, max_y = min(min_y, point.y), max(max_y, point.y) return Rect(Point(min_x, min_y), Size(max_x - min_x, max_y - min_x))
class TestAffineTransform(unittest.TestCase): point = Point(2, 3) scale = AffineTransform(2, 5) trans = AffineTransform(1, 1, 10, 15) shear = AffineTransform(1, 1, 0, 0, 3, 4) # APPLICATION # def test_scale_point(self): expected = Point(4, 15) actual = self.scale.apply_to_point(self.point) self.assertEqual(expected, actual) def test_translate_point(self): expected = Point(12, 18) actual = self.trans.apply_to_point(self.point) self.assertEqual(expected, actual) def test_shear_point(self): expected = Point(11, 11) actual = self.shear.apply_to_point(self.point) self.assertEqual(expected, actual) # CONCATENATION # def test_concatenate_scale_then_translate(self): expected = AffineTransform(2, 5, 10, 15) actual = self.scale.then(self.trans) self.assertEqual(expected, actual) def test_concatenate_translate_then_scale(self): expected = AffineTransform(2, 5, 20, 75) actual = self.trans.then(self.scale) self.assertEqual(expected, actual) # INVERSE # def test_inverse(self): transf = AffineTransform(1, 2, 3, 4, 5, 6) expected = AffineTransform() actual = transf.then(transf.inverse()) self.assertEqual(expected, actual)
class TestRect(unittest.TestCase): origin = Point(0, 0) size = Size(10, 5) rect = Rect(origin, size) # --- CONTAINS --- # def test_contains_point(self): point = Point(5, 3) self.assertTrue(self.rect.contains_point(point)) def test_doesnt_contain_point(self): point = Point(50, 7) self.assertFalse(self.rect.contains_point(point)) # --- INTERSECTION --- # def test_no_intersection_horizontal_overlap(self): other = Rect(Point(50, 0), self.size) self.assertIsNone(self.rect.intersection_with(other)) def test_no_intersection_vertical_overlap(self): other = Rect(Point(0, 50), self.size) self.assertIsNone(self.rect.intersection_with(other)) def test_intersection(self): other = Rect(Point(5, 2), self.size) actual = self.rect.intersection_with(other) expected = Rect(other.origin, Size(5, 3)) self.assertEqual(expected, actual) # --- POLYGON --- # def test_to_polygon(self): actual = self.rect.to_polygon() expected = Polygon( [self.origin, Point(10, 0), Point(10, 5), Point(0, 5)]) self.assertEqual(expected, actual)
def test_lines_intersection(self): l1 = Line(Point(50, 0), Vector(0, 1)) l2 = Line(Point(0, 30), Vector(1, 0)) actual = l1.intersection_with(l2) expected = Point(50, 30) self.assertEqual(expected, actual)
def test_intersection(self): other = Rect(Point(5, 2), self.size) actual = self.rect.intersection_with(other) expected = Rect(other.origin, Size(5, 3)) self.assertEqual(expected, actual)
def test_no_intersection_horizontal_overlap(self): other = Rect(Point(50, 0), self.size) self.assertIsNone(self.rect.intersection_with(other))
def test_translate_point(self): expected = Point(12, 18) actual = self.trans.apply_to_point(self.point) self.assertEqual(expected, actual)
def test_penetration(self): other = Circle(Point(30, 30), 20) actual = self.circle.penetration_vector(other) pen_proj = -(math.sqrt(2) * 15 - 20) expected = Vector(pen_proj, pen_proj) self.assertEqual(expected, actual)
def test_parallel_lines_no_intersection(self): l1 = Line(Point(0, 0), Vector(1, 1)) l2 = Line(Point(10, 10), Vector(1, 1)) self.assertIsNone(l1.intersection_with(l2))
def test_scale_point(self): expected = Point(4, 15) actual = self.scale.apply_to_point(self.point) self.assertEqual(expected, actual)
def test_no_intersection_vertical_overlap(self): other = Rect(Point(0, 50), self.size) self.assertIsNone(self.rect.intersection_with(other))
def test_shear_point(self): expected = Point(11, 11) actual = self.shear.apply_to_point(self.point) self.assertEqual(expected, actual)
def test_contains_point(self): point = Point(5, 3) self.assertTrue(self.rect.contains_point(point))
from geom2d.point import Point a = Point(0, 0) b = Point(3, 4) print(a.distance(b)) print (a == b) print (a == Point(0, 0))
def test_doesnt_contain_point(self): point = Point(50, 7) self.assertFalse(self.rect.contains_point(point))