Here is the formal class interface.

homcoord.py

# - - - - - c l a s s X f o r m class Xform(object): '''Represents an arbitrary homogeneous coordinate transform. Exports: Xform(m): [ m is a 3x3 transform matrix as a num.array, or a sequence that num.array() will accept as a 3x3 array -> return a new Xform instance representing that transform ] .apply(p): [ p is a Pt instance -> return a new Pt instance representing p transformed by self ] .invert(p): [ p is a Pt instance -> return a new Pt instance pp such that self.apply(pp) == p ] .inverse(): [ return the inverse of self as an Xform instance ] .compose(t): [ t is an Xform instance -> return a new Xform representing the composition of self followed by t ] .offset(): [ return the net offset that self will shift the origin, as a Pt instance ] .angle(): [ return the net angle that self will rotate the unit vector from (0,0) to (1,1) ] .mag(): [ return the net magnification that self will apply to the unit vector ] .__str__(self): [ return a string representation of self ]

Internally, the `.__m`

attribute is the
actual transform matrix.

We use memoization to avoid computing the inverse of the
transform matrix every time the `.inverse()`

method is called. We set an internal attribute `.__mInverse`

initially to `None`

.
Then, when the `.inverse()`

method is called,
we store the inverted transform in this attribute for reuse
on the next call.

Also memoized are the results of the `.__analyze()`

method, which computes the net
translation, rotation, and uniform scaling components of
the transform.

homcoord.py

State/Invariants: self.__m: [ a 3x3 num.array representing the argument passed to the constructor ] self.__mInverse: [ the inverse of self.__m or None ] self.__offset: [ the net translation of self or None ] self.__angle: [ the net rotation of self or None ] self.__mag: [ the net uniform scaling of self or None ] ORIGIN: [ the origin as a Pt instance ] UNIT: [ a point 1.0 along the line x=y ] ''' ORIGIN = Pt(0,0) UNIT = ORIGIN.radial(1.0, RAD_45)