Source code for Musica.Geometry.Transformation
####################################################################################################
#
# Musica - A Music Theory Package for Python
# Copyright (C) 2017 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
####################################################################################################
####################################################################################################
from math import sin, cos, radians # degrees
import numpy as np
####################################################################################################
[docs]class Transformation:
__dimension__ = None
__size__ = None
##############################################
##############################################
[docs] def __init__(self, obj):
if isinstance(obj, Transformation):
if self.same_dimension(obj):
array = obj.array # *._m
else:
raise ValueError
elif isinstance(obj, np.ndarray):
if obj.shape == (self.__size__, self.__size__):
array = obj
else:
raise ValueError
else:
array = np.array((self.__size__, self.__size__))
array[...] = obj
self._m = np.array(array)
##############################################
@property
def dimension(self):
return self.__dimension__
@property
def size(self):
return self.__size__
@property
def array(self):
return self._m
##############################################
##############################################
##############################################
[docs] def _compose_transformation(self, transformation):
return np.matmul(self._m, transformation.array)
##############################################
[docs] def transform(self, transformation):
array = self._compose_transformation(transformation)
return self.__class__(array)
#######################################
[docs] def __mul__(self, obj):
try:
return obj.transform(self)
except AttributeError:
try:
return [item.transform(self) for item in obj]
except TypeError:
raise ValueError
#######################################
[docs] def __imul__(self, obj):
if isinstance(obj, Transformation):
self._m = self._compose_transformation(obj)
else:
raise ValueError
return self
####################################################################################################
[docs]class Transformation2D(Transformation):
__dimension__ = 2
__size__ = 2
##############################################
[docs] @classmethod
def Rotation(cls, angle):
angle = radians(angle)
c = cos(angle)
s = sin(angle)
return cls(np.array(((c, -s), (s, c))))
##############################################
[docs] @classmethod
def Scale(cls, x_scale, y_scale):
return cls(np.array((x_scale, 0), (0, y_scale)))
##############################################
##############################################
##############################################
####################################################################################################
[docs]class AffineTransformation(Transformation):
##############################################
[docs] @classmethod
def Translation(cls, vector):
transformation = cls.Identity()
transformation.translation_part[...] = vector.v[...]
return transformation
##############################################
[docs] @classmethod
def Rotation(cls, angle):
transformation = cls.Identity()
transformation.matrix_part[...] = Transformation2D.Rotation(angle).array
return transformation
##############################################
[docs] @classmethod
def RotationAt(cls, center, angle):
transformation = cls.Translation(center)
transformation *= cls.Rotation(angle)
transformation *= cls.Translation(-center)
return transformation
##############################################
@property
def matrix_part(self):
return self._m[:self.__dimension__,:self.__dimension__]
@property
def translation_part(self):
return self._m[:self.__dimension__,-1]
####################################################################################################
####################################################################################################
# The matrix to rotate an angle θ about the axis defined by unit vector (l, m, n) is
# l*l*(1-c) + c , m*l*(1-c) - n*s , n*l*(1-c) + m*s
# l*m*(1-c) + n*s , m*m*(1-c) + c , n*m*(1-c) - l*s
# l*n*(1-c) - m*s , m*n*(1-c) + l*s , n*n*(1-c) + c