# Tools and types for affine transformation of {\RR^d} # Last edited on 2021-09-23 23:09:24 by stolfi import affine_IMP; from affine_IMP import Affine_IMP class Affine(Affine_IMP): # An object of this class represents an affine geometric transformation of # some real Cartesian space {\RR^d}. It is a subclass of {trafo.Trafo}. # # An {Affine} object {O} has a {d x d} matrix {A}, the /linear map/, and # a {d}-vector {b}, the /displacement/. The transformation # applied to a point {p} of {\RR^d} consists in multiplying {p} # (seen as a row vector) by the matrix {A} and adding {b} to the result. # # The object also stores the data for the inverse trafo, which is also # an affine transformation. The matrix {A'} is the inverse of {A}, and # the vector {b'} is {b} times {A'}. # # An affine trafo may also be a pair {(kinv,Aobj)} where {Aobj} is an {Affine} # object and {kinv} is {+1}, meaning the transformation implied by {Aobj}, or {-1}, # meaning its inverse. We call this pair an /oriented affine trafo/. # In general, an {Affine} object {Aobj} (or {None}) can be used were an oriented affine # trafo is expected; the {kinv} bit is then assumed to be {+1}. pass def make(name,d,Adir,bdir,Ainv,binv): # Creates an {Affine} object with linear map {d} by {d} matrix {Adir} and displacement # {d}-vector {bdir}. Also requires the matrix and {Ainv} and vector {binv} of the # inverse transformation. # # The name will be saved as the the trafo name (see {trafo.get_name}). # However, if {name} is {None}, "??" will be saved instead. # # If any of {Adir} or {Ainv} is {None}, assumes the {d} by {d} identity maxtrix. # If any of {bdir} or {binv} is {None}, assumes the zero vector. return affine_IMP.make(name,d,Adir,bdir,Ainv,binv) def dimension(A): # Returns the dimenson {d} of the space for wich the oriented affine trafo {A} # applies to. It is the number of rows and columns of the linear matrix # and the number of elements of the displacement vector. return affine_IMP.dimension(A) def apply(p,A): # Applies the oriented affine trafo {A} to the point {p}, which must be a list or tuple with # least {d} elements, were {d} is {dimension(A)}. return affine_IMP.apply(p,A) def unpack(A): # Given an oriented affine trafo {A}, returns two results: # the inversion bit ({+1} or {-1}), and the underlying {Affine} object {Aobj}. # If {A} is already an {Affine} object or {None}, returns {+1} and {A}. # # Also does some type checking. return affine_IMP.unpack(A) def lin_matrix(A): # Returns the linear matrix of the oriented affine trafo {A}, # taking its direction bit into account. If the result is {None}, # it should be assumed to be the {d} by {d} identity matrix, where {d=dimension(A)}. return affine_IMP.lin_matrix(A) def disp_vector(A): # Returns the displacement vector of the oriented affine trafo {A}, # taking its direction bit into account. # # If the result is {None}, it should be assumed to be the zero {d}-vector, # where {d=dimension(A)}. return affine_IMP.disp_vector(A) def inv(A): # Returns the inverse of the oriented affine trafo {A}. This basically # negates the inversion bit, without copying or affecting the underlying # object. return affine_IMP.inv(A) def compose(A1,A2): # Composes the two oriented affine trafos {A1,A2}, applied in that order, # taking their inversion bits into account, into a single {Affine} object {A}. # The name will be set to "??"; it can be changed to {trafo.set_name}. return affine_IMP.compose(A1,A2)