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
)
)