def __sub__(self, other): """ Creates a `Vector` going from `other` to `self`. :param other: `Point` :return: `Vector`: `other` -> `self` """ return Vector(self.x - other.x, self.y - other.y)
def make_versor(u: float, v: float): """ Creates a `Vector` with `u` and `v` projections, then normalizes it. :param u: `float` horizontal projection :param v: `float` vertical projection :return: `Vector` with unitary norm """ return Vector(u, v).normalized()
def displaced(self, vector: Vector, times=1): """ Creates a new `Point` result of displacing this one the given `vector` and number of times `times`. :param vector: displacement `Vector` :param times: times the displacement vector is applied :return: `Point` """ scaled_vec = vector.scaled_by(times) return Point(self.x + scaled_vec.u, self.y + scaled_vec.v)
class Circle(Shape): def __init__(self, center=(0, 0), radius=1.0): self.center = Vector(*center) self.radius = radius def __info__(self): return "c=%s, r=%s" % (self.center, self.radius) # -------------------------------------------------------------------------- # Geometry interface def move(self, dx=0.0, dy=0.0): if dx == 0.0 and dy == 0.0: return self.center.move(dx, dy) def scale(self, factor, pivot=None): if factor == 1.0: return if pivot is not None: self.center.scale(factor, pivot) self.radius *= factor def rotate(self, alpha, pivot=None): if alpha == 0.0: return if pivot is not None: self.center.rotate(alpha, pivot) # -------------------------------------------------------------------------- # Shape interface def contains_point(self, point): radius = self.radius delta = point - self.center return delta.squared_length <= radius * radius def segment_intersections(self, segment): points = [] cx, cy = self.center r = self.radius m = segment.slope if m is None: # vertical segment if cx - r <= segment.a.x <= cx + r: pass else: b = segment.y_at(0.0) A = 1 + m*m B = -2*cx + 2*m*b -2*m*cy C = cx*cx + cy*cy + b*b - r*r - 2*b*cy x0, x1 = solve_quadratic(A, B, C) xinterval = segment.xrange if x0 in xinterval: points.append(Vector(x0, segment.y_at(x0))) if x1 in xinterval: points.append(Vector(x1, segment.y_at(x1))) return points
def __init__(self, center=(0, 0), radius=1.0): self.center = Vector(*center) self.radius = radius
def test_vector(): for _ in xrange(1000): v = random_vector() x, y = v assert v + (2, 0) == Vector(x+2, y) assert v - (2, 0) == Vector(x-2, y) assert +v == Vector(x, y) assert -v == Vector(-x, -y) assert v * 2 == Vector(2*x, 2*y) assert v / 2 == Vector(x/2, y/2) v = Vector(1, 0) assert v.angle == 0 v.angle = pi/2 v.x, v.y = map(Approx, v) assert v == Vector(0, 1) v.length = 2 v.x, v.y = map(Approx, v) assert v == Vector(0, 2) v.move(2, 2) assert v == Vector(2, 4) v.scale(2, 2, pivot=(1, 1)) assert v == Vector(3, 7) v.rotate(pi/2, pivot=(3, 3)) v.x, v.y = [Approx(n) for n in v] assert v == Vector(-1, 3)
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_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_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_rotate_right_angle(self): actual = self.east.rotated_radians(math.pi / 2) expected = Vector(0, 1) self.assertEqual(expected, actual)
def test_rotate_positive_angle(self): sqrt2 = math.sqrt(2) actual = self.east.rotated_radians(math.pi / 4) expected = Vector(1 / sqrt2, 1 / sqrt2) self.assertEqual(expected, actual)
class TestVector(unittest.TestCase): u = Vector(1, 2) v = Vector(4, 6) east = Vector(1, 0) west = Vector(-1, 0) north_east = Vector(1, 1) south_east = Vector(1, -1) # <----- Operations -----> # def test_plus(self): expected = Vector(5, 8) actual = self.u + self.v self.assertEqual(expected, actual) def test_minus(self): expected = Vector(-3, -4) actual = self.u - self.v self.assertEqual(expected, actual) # <----- Products -----> # def test_dot_product(self): expected = 16 actual = self.u.dot(self.v) self.assertAlmostEqual(expected, actual) def test_cross_product(self): expected = -2 actual = self.u.cross(self.v) self.assertAlmostEqual(expected, actual) # <----- Parallel & Perpendicular -----> # def test_are_parallel(self): self.assertTrue(self.u.is_parallel_to(self.u)) def test_are_not_parallel(self): self.assertFalse(self.u.is_parallel_to(self.v)) def test_are_perpendicular(self): perp = Vector(-2, 1) self.assertTrue(self.u.is_perpendicular_to(perp)) def test_are_not_perpendicular(self): self.assertFalse(self.u.is_perpendicular_to(self.v)) # <----- Angle Value -----> # def test_angle_value_of_zero(self): actual = self.east.angle_value_to(self.east) expected = 0 self.assertAlmostEqual(expected, actual) def test_angle_value_of_pi(self): actual = self.east.angle_value_to(self.west) expected = math.pi self.assertAlmostEqual(expected, actual) def test_angle_value_when_angle_positive(self): actual = self.east.angle_value_to(self.north_east) expected = math.pi / 4 self.assertAlmostEqual(expected, actual) def test_angle_value_when_angle_negative(self): actual = self.east.angle_value_to(self.north_east) expected = math.pi / 4 self.assertAlmostEqual(expected, actual) # <----- Angle Value -----> # def test_angle_when_angle_positive(self): actual = self.east.angle_to(self.north_east) expected = math.pi / 4 self.assertAlmostEqual(expected, actual) def test_angle_when_angle_negative(self): actual = self.east.angle_to(self.south_east) expected = -math.pi / 4 self.assertAlmostEqual(expected, actual) # <----- Rotate Angle -----> # def test_rotate_zero_radians(self): actual = self.east.rotated_radians(0) expected = self.east self.assertEqual(expected, actual) def test_rotate_positive_angle(self): sqrt2 = math.sqrt(2) actual = self.east.rotated_radians(math.pi / 4) expected = Vector(1 / sqrt2, 1 / sqrt2) self.assertEqual(expected, actual) def test_rotate_right_angle(self): actual = self.east.rotated_radians(math.pi / 2) expected = Vector(0, 1) self.assertEqual(expected, actual) def test_rotate_straight_angle(self): actual = self.north_east.rotated_radians(math.pi) expected = Vector(-1, -1) self.assertEqual(expected, actual) def test_rotate_negative_angle(self): sqrt2 = math.sqrt(2) actual = self.east.rotated_radians(-math.pi / 4) expected = Vector(1 / sqrt2, -1 / sqrt2) self.assertEqual(expected, actual)
def test_are_perpendicular(self): perp = Vector(-2, 1) self.assertTrue(self.u.is_perpendicular_to(perp))
def test_minus(self): expected = Vector(-3, -4) actual = self.u - self.v self.assertEqual(expected, actual)
def test_plus(self): expected = Vector(5, 8) actual = self.u + self.v self.assertEqual(expected, actual)
def test_rotate_straight_angle(self): actual = self.north_east.rotated_radians(math.pi) expected = Vector(-1, -1) self.assertEqual(expected, actual)