lab_geometry/geometry/vector.py

142 lines
4.9 KiB
Python

from geometry.point import Point
from geometry.validation import is_coordinates, expect_type
from geometry.drawing import line_end_arrow
from math import sqrt, atan
from superturtle.movement import fly
from turtle import setheading, forward
class Vector:
"""A Vector represents movement in 2D space. Like a Point, a
Vector is defined by x and y coordinates. However, Points and Vectors
are different. Here are a few things you can do with Vectors which it doesn't
make sense to do with Points:
- Multiply them by a number (multiplying a vector by 2 means twice the movement)
- Negate them (-v means moving in the opposite direction of v)
- Find their length
A Vector can be initialized in multiple ways:
- With no value: Represents no motion at all: (0, 0).
- With an ordered pair of numbers: The Vector will have those coordinates.
- With a Point: The Vector will have the Point's x and y coordinates.
"""
def __init__(self, value=None):
if value is None:
self.x, self.y = (0, 0)
elif is_coordinates(value):
self.x, self.y = value
elif isinstance(value, Point):
self.x, self.y = value.x, value.y
else:
raise TypeError(f"Can't create a Vector from {value}.")
def draw(self, origin=None):
"Draws the vector."
origin = origin or Point()
expect_type(origin, Point)
fly(origin.x, origin.y)
θ = atan(self.y / self.x)
setheading(θ)
forward(self.mag())
line_end_arrow()
def __str__(self):
return f"{self.x}, {self.y}"
def __repr__(self):
return f"Vector(({self.x}, {self.y}))"
def __eq__(self, other):
"""Implements equality checking with a == b.
Two Vectors are equal if their coordinates are the same.
A Vector can only be equal to another Vector.
"""
try:
expect_type(other, Vector)
return (self.x, self.y) == (other.x, other.y)
except TypeError:
return False
def __neg__(self):
"""Implements negation with -a.
"""
return Vector((-self.x, -self.y))
def __add__(self, other):
"""Implements addition with a + b. Other can be:
- a Vector: interpreted as combining the movement of self and other,
returning a new Vector.
- a Point: interpreted as moving the point in the direction of the Vector,
returning a new Point.
"""
expect_type(other, (Point, Vector))
if isinstance(other, Vector):
return Vector((self.x + other.x, self.y + other.y))
elif isinstance(other, Point):
return Point((self.x + other.x, self.y + other.y))
def __sub__(self, other):
"""Implements subtraction with a - b. Other must be a Vector,
interpreted as subtraction the movement of other from self and
returning a new Vector.
"""
expect_type(other, Vector)
return Vector((self.x - other.x, self.y - other.y))
def __mul__(self, other):
"""Implements multiplication with a * 2, interpreted as scaling the
vector by other. Other must be numeric.
"""
expect_type(other, (int, float))
return Vector((self.x * other, self.y * other))
def __rmul__(self, other):
"""Implements multiplication with 2 * a.
"""
return self * other
def __truediv__(self, other):
"""Implements division with a / b, interpreted as dividing the
vector by other. Other must be numeric.
"""
expect_type(other, (int, float))
if other == 0:
raise ZeroDivisionError("Can't divide a Vector by zero.")
return Vector((self.x / other, self.y / other))
def __bool__(self):
"""Implements truthiness--whether an instance of Vector is treated as
True or False. Any Vector other than the zero vector is True.
"""
return self.mag() != 0
def mag(self):
"""Returns the magnitude (length) of the vector, using the Pythagorean formula.
"""
return sqrt(self.x * self.x + self.y * self.y)
def scale(self, mag):
"""Returns a Vector pointing in the same direction with the given magnitude.
"""
expect_type(mag, (int, float))
if self.mag() > 0:
return self * (mag / self.mag())
else:
raise ValueError("Can't scale the zero vector.")
def normalize(self):
"""Returns a Vector pointing in the same direction with a magnitude of 1.
"""
return self.scale(1)
def dot(self, other):
"""Computes the dot product of this vector with another.
"""
expect_type(other, Vector)
return self.x * other.x + self.y * other.y
def project(self, other):
"""Projects this vector onto another.
"""
return other.scale(self.dot(other.normalize()))