class GeometricObject(ABC):
+ """
+ GeometricObject is an abstract class to represent objects with a
+ geometric representation in space. Subclasses of GeometricObject are
+ Polyhedron, Domain and Point.
+ """
@abstractproperty
def symbols(self):
+ """
+ The tuple of symbols present in the object expression, sorted according
+ to Symbol.sortkey().
+ """
pass
@property
def dimension(self):
+ """
+ The dimension of the object, i.e. the number of symbols present in it.
+ """
return len(self.symbols)
@abstractmethod
def aspolyhedron(self):
+ """
+ Return a Polyhedron object that approximates the geometric object.
+ """
pass
def asdomain(self):
+ """
+ Return a Domain object that approximates the geometric object.
+ """
return self.aspolyhedron()
class Coordinates:
+ """
+ This class represents coordinate systems.
+ """
__slots__ = (
'_coordinates',
)
def __new__(cls, coordinates):
+ """
+ Create a coordinate system from a dictionary or a sequence that maps the
+ symbols to their coordinates. Coordinates must be rational numbers.
+ """
if isinstance(coordinates, Mapping):
coordinates = coordinates.items()
self = object().__new__(cls)
@property
def symbols(self):
+ """
+ The tuple of symbols present in the coordinate system, sorted according
+ to Symbol.sortkey().
+ """
return tuple(self._coordinates)
@property
def dimension(self):
+ """
+ The dimension of the coordinate system, i.e. the number of symbols
+ present in it.
+ """
return len(self.symbols)
- def coordinates(self):
- yield from self._coordinates.items()
-
def coordinate(self, symbol):
+ """
+ Return the coordinate value of the given symbol. Raise KeyError if the
+ symbol is not involved in the coordinate system.
+ """
if not isinstance(symbol, Symbol):
raise TypeError('symbol must be a Symbol instance')
return self._coordinates[symbol]
__getitem__ = coordinate
+ def coordinates(self):
+ """
+ Iterate over the pairs (symbol, value) of coordinates in the coordinate
+ system.
+ """
+ yield from self._coordinates.items()
+
def values(self):
+ """
+ Iterate over the coordinate values in the coordinate system.
+ """
yield from self._coordinates.values()
def __bool__(self):
+ """
+ Return True if not all coordinates are 0.
+ """
return any(self._coordinates.values())
def __hash__(self):
class Point(Coordinates, GeometricObject):
"""
This class represents points in space.
+
+ Point instances are hashable and should be treated as immutable.
"""
def isorigin(self):
"""
- Return True if a Point is the origin.
+ Return True if all coordinates are 0.
"""
return not bool(self)
def __add__(self, other):
"""
- Adds a Point to a Vector and returns the result as a Point.
+ Translate the point by a Vector object and return the resulting point.
"""
- if not isinstance(other, Vector):
- return NotImplemented
- coordinates = self._map2(other, operator.add)
- return Point(coordinates)
+ if isinstance(other, Vector):
+ coordinates = self._map2(other, operator.add)
+ return Point(coordinates)
+ return NotImplemented
def __sub__(self, other):
"""
- Returns the difference between two Points as a Vector.
+ If other is a point, substract it from self and return the resulting
+ vector. If other is a vector, translate the point by the opposite vector
+ and returns the resulting point.
"""
coordinates = []
if isinstance(other, Point):
elif isinstance(other, Vector):
coordinates = self._map2(other, operator.sub)
return Point(coordinates)
- else:
- return NotImplemented
+ return NotImplemented
def __eq__(self, other):
"""
- Compares two Points for equality.
+ Test whether two points are equal.
"""
- return isinstance(other, Point) and \
- self._coordinates == other._coordinates
+ if isinstance(other, Point):
+ return self._coordinates == other._coordinates
+ return NotImplemented
def aspolyhedron(self):
- """
- Return a Point as a polyhedron.
- """
from .polyhedra import Polyhedron
equalities = []
for symbol, coordinate in self.coordinates():
class Vector(Coordinates):
"""
- This class represents displacements in space.
+ This class represents vectors in space.
+
+ Vector instances are hashable and should be treated as immutable.
"""
def __new__(cls, initial, terminal=None):
+ """
+ Create a vector from a dictionary or a sequence that maps the symbols to
+ their coordinates, or as the displacement between two points.
+ """
if not isinstance(initial, Point):
initial = Point(initial)
if terminal is None:
def isnull(self):
"""
- Returns true if a Vector is null.
+ Return True if all coordinates are 0.
"""
return not bool(self)
def __add__(self, other):
"""
- Adds either a Point or Vector to a Vector.
+ If other is a point, translate it with the vector self and return the
+ resulting point. If other is a vector, return the vector self + other.
"""
if isinstance(other, (Point, Vector)):
coordinates = self._map2(other, operator.add)
return other.__class__(coordinates)
return NotImplemented
+ def __sub__(self, other):
+ """
+ If other is a point, substract it from the vector self and return the
+ resulting point. If other is a vector, return the vector self - other.
+ """
+ if isinstance(other, (Point, Vector)):
+ coordinates = self._map2(other, operator.sub)
+ return other.__class__(coordinates)
+ return NotImplemented
+
+ def __neg__(self):
+ """
+ Return the vector -self.
+ """
+ coordinates = self._map(operator.neg)
+ return Vector(coordinates)
+
+ def __mul__(self, other):
+ """
+ Multiplies a Vector by a scalar value.
+ """
+ if isinstance(other, numbers.Real):
+ coordinates = self._map(lambda coordinate: other * coordinate)
+ return Vector(coordinates)
+ return NotImplemented
+
+ __rmul__ = __mul__
+
+ def __truediv__(self, other):
+ """
+ Divide the vector by the specified scalar and returns the result as a
+ vector.
+ """
+ if isinstance(other, numbers.Real):
+ coordinates = self._map(lambda coordinate: coordinate / other)
+ return Vector(coordinates)
+ return NotImplemented
+
+ def __eq__(self, other):
+ """
+ Test whether two vectors are equal.
+ """
+ if isinstance(other, Vector):
+ return self._coordinates == other._coordinates
+ return NotImplemented
+
def angle(self, other):
"""
Retrieve the angle required to rotate the vector into the vector passed
def cross(self, other):
"""
- Calculate the cross product of two Vector3D structures.
+ Compute the cross product of two 3D vectors. If either one of the
+ vectors is not three-dimensional, a ValueError exception is raised.
"""
if not isinstance(other, Vector):
raise TypeError('other must be a Vector instance')
coordinates.append((z, self[x]*other[y] - self[y]*other[x]))
return Vector(coordinates)
- def __truediv__(self, other):
- """
- Divide the vector by the specified scalar and returns the result as a
- vector.
- """
- if not isinstance(other, numbers.Real):
- return NotImplemented
- coordinates = self._map(lambda coordinate: coordinate / other)
- return Vector(coordinates)
-
def dot(self, other):
"""
- Calculate the dot product of two vectors.
+ Compute the dot product of two vectors.
"""
if not isinstance(other, Vector):
raise TypeError('argument must be a Vector instance')
result += coordinate1 * coordinate2
return result
- def __eq__(self, other):
- """
- Compares two Vectors for equality.
- """
- return isinstance(other, Vector) and \
- self._coordinates == other._coordinates
-
def __hash__(self):
- return hash(tuple(self.coordinates()))
-
- def __mul__(self, other):
- """
- Multiplies a Vector by a scalar value.
- """
- if not isinstance(other, numbers.Real):
- return NotImplemented
- coordinates = self._map(lambda coordinate: other * coordinate)
- return Vector(coordinates)
-
- __rmul__ = __mul__
-
- def __neg__(self):
- """
- Returns the negated form of a Vector.
- """
- coordinates = self._map(operator.neg)
- return Vector(coordinates)
+ return super().__hash__()
def norm(self):
"""
- Normalizes a Vector.
+ Return the norm of the vector.
"""
return math.sqrt(self.norm2())
def norm2(self):
+ """
+ Return the squared norm of the vector.
+ """
result = 0
for coordinate in self._coordinates.values():
result += coordinate ** 2
return result
def asunit(self):
- return self / self.norm()
-
- def __sub__(self, other):
"""
- Subtract a Point or Vector from a Vector.
+ Return the normalized vector, i.e. the vector of same direction but with
+ norm 1.
"""
- if isinstance(other, (Point, Vector)):
- coordinates = self._map2(other, operator.sub)
- return other.__class__(coordinates)
- return NotImplemented
+ return self / self.norm()