Source code for ddg.abc

from collections import deque
from functools import reduce

import numpy as np

from ddg.math._functools import compose


[docs]class Transformable: """Makes a class transformable by keeping a stack of transformations. It is assumed that the transformations are functions. If a matrix is passed to push_transformation, it will be converted to a function. """ def __init__(self): self._trafos = deque()
[docs] def push_transformation(self, f): """Add a transformation to the trafo-stack. Parameters ---------- f : numpy.ndarray or function """ if isinstance(f, np.ndarray): self._trafos.appendleft(lambda x: np.dot(f, x)) else: self._trafos.appendleft(f)
[docs] def pop_transformation(self): """Pop transformation from the trafo-stack. Returns ------- function """ return self._trafos.popleft()
[docs] def transform(self, f): """Wrapper for `push_transformation`. See Also -------- push_transformation """ self.push_transformation(f)
@property def transformation(self): """Composition of all transformations on the stack. Returns ------- function """ return compose(*self._trafos)
[docs]class LinearTransformable(Transformable): """Makes a class transformable by keeping a stack of transformations. It is assumed that the transformations are matrices. Attributes ---------- trafo_dimension : int An empty transformation stack returns np.eye(_trafo_dimension). """ def __init__(self, trafo_dimension): self.trafo_dimension = trafo_dimension super().__init__()
[docs] def push_transformation(self, f): """Add a transformation to the trafo-stack. Parameters ---------- f : numpy.ndarray """ if not isinstance(f, np.ndarray): raise TypeError("Transformation should be a matrix.") self._trafos.appendleft(f)
@property def transformation(self): """Product of all matrices on the stack. Returns ------- numpy.ndarray """ return reduce(np.dot, self._trafos, np.eye(self.trafo_dimension))
[docs] def change_affine_picture(self, before, after=-1): """Transform the object to a different affine view. Dehomogenizing the object post-transform with affine component `after` will produce the same affine picture as dehomogenizing the object pre-transform with affine component `before`. The actual transformation that achieves this just permutes the homogeneous coordinates as follows: It deletes the entry at `before` and inserts it again at position `after`. Here are two examples of how you might use this function: 1. You defined a projective object `X` with a certain affine picture in mind and you followed our convention of using affine component -1. You now want to see what it would look like when dehomogenized using a different affine component `i`. To do this, you would just do ``X.change_affine_picture(i)`` and then visualize the object normally. 2. You don't like our convention of dehomogenizing by the last component and want to define your object `X` with the affine picture with respect to affine component `i` in mind. To visualize your object as you imagine it, you would also do ``X.change_affine_picture(i)``. To change back, you can do ``X.change_affine_picture(-1, i)``. Parameters ---------- before : int after : int (default=-1) """ from ddg.math.projective import affine_component_transformation self.transform( affine_component_transformation( self.trafo_dimension, before=before, after=after ) )