|
| 1 | +import numpy as np |
| 2 | +import math |
| 3 | +import transformations as xform |
| 4 | +from quaternion import * |
| 5 | +from affine2d import * |
| 6 | + |
| 7 | +class Affine3d(object): |
| 8 | + """We represent a 3D affine transformation as a 4x4 matrix." |
| 9 | +
|
| 10 | + Lots of places to learn about affine transformations like this: |
| 11 | +
|
| 12 | + http://graphics.cs.cmu.edu/nsp/course/15-462/Spring04/slides/04-transform.pdf |
| 13 | +
|
| 14 | + Examples |
| 15 | + >>> a = Affine3d() |
| 16 | + >>> b = Affine3d.fromRotation(30, [1, 0, 0]) |
| 17 | + >>> c = Affine3d.fromTranslation(.5, 1.5, 2.5) |
| 18 | + >>> d = Affine3d.fromQuaternion(1,2,3,4) |
| 19 | + >>> e = Affine3d.fromQuaternion([1,2,3,4]) |
| 20 | + >>> e.equals(d) |
| 21 | + True |
| 22 | + >>> f = Affine3d.fromQuaternion(Quaternion()) |
| 23 | + >>> a.equals(f) |
| 24 | + True |
| 25 | + >>> f = Affine3d.concatenate(b, c, d) |
| 26 | + >>> pts = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype=np.float32) |
| 27 | + >>> npts = f.transformPoints(pts) |
| 28 | + >>> len(npts) == len(pts) |
| 29 | + True |
| 30 | + |
| 31 | + """ |
| 32 | + |
| 33 | + @staticmethod |
| 34 | + def fromIdentity(): |
| 35 | + return Affine3d(xform.identity_matrix()) |
| 36 | + |
| 37 | + @staticmethod |
| 38 | + def fromTranslation(x, y, z): |
| 39 | + "return Affine3d representing translation of x, y and z" |
| 40 | + return Affine3d(xform.translation_matrix([x, y, z])) |
| 41 | + |
| 42 | + @staticmethod |
| 43 | + def fromRotation(angle, dir): |
| 44 | + "return Affine3d representing rotation of angle (in degrees) around dir" |
| 45 | + return Affine3d(xform.rotation_matrix(math.radians(angle), dir)) |
| 46 | + |
| 47 | + @staticmethod |
| 48 | + def fromQuaternion(*q): |
| 49 | + """return Affine3d representing rotation via quaternion. |
| 50 | + See Quaternion for a variety of methods to describe rotations. |
| 51 | + """ |
| 52 | + if(len(q) == 1): |
| 53 | + q = q[0] |
| 54 | + if isinstance(q, Quaternion): |
| 55 | + q = q.q # member of Quaternion |
| 56 | + return Affine3d(xform.quaternion_matrix(q)) |
| 57 | + |
| 58 | + @staticmethod |
| 59 | + def concatenate(*objs): |
| 60 | + "return Affine3d representing concatenation of Affine3ds" |
| 61 | + mats = [x.matrix for x in objs] |
| 62 | + return Affine3d(xform.concatenate_matrices(*mats)) |
| 63 | + |
| 64 | + # XXX: add more factories (scale, reflection, shear) |
| 65 | + |
| 66 | + def __init__(self, initmat=None): |
| 67 | + if initmat is None: |
| 68 | + self.matrix = xform.identity_matrix() |
| 69 | + else: |
| 70 | + self.matrix = initmat |
| 71 | + |
| 72 | + def __repr__(self): |
| 73 | + "for interactive prompt inspection" |
| 74 | + return self.matrix.__repr__() |
| 75 | + |
| 76 | + def __str__(self): |
| 77 | + "for print" |
| 78 | + return self.matrix.__str__() |
| 79 | + |
| 80 | + def equals(self, other): |
| 81 | + return np.allclose(self.matrix, other.matrix) |
| 82 | + |
| 83 | + def transformPoints(self, pts): |
| 84 | + """return an array of points transformed by my transformation matrix. |
| 85 | + pts is assumed to be an array of triples. |
| 86 | + """ |
| 87 | + result = np.array(pts, dtype=np.float32) |
| 88 | + i = 0 |
| 89 | + for p3 in pts: |
| 90 | + # p3 to p4 back to p3 (so dot works) |
| 91 | + p4 = p3.tolist() + [1] |
| 92 | + npt = self.matrix.dot(p4)[:3] |
| 93 | + result[i] = npt # npt type s np.array, should we issue tolist()? |
| 94 | + i+=1 |
| 95 | + return result |
| 96 | + |
| 97 | + def asInverse(self): |
| 98 | + return Affine3d(xform.inverse_matrix(self.matrix)) |
| 99 | + |
| 100 | + def asAffine2d(self): |
| 101 | + """return a 2d representation of |
| 102 | + """ |
| 103 | + |
| 104 | +if __name__ == "__main__": |
| 105 | + import doctest |
| 106 | + doctest.testmod() |
0 commit comments