TableOfContents

What is a Transform?

We use the [http://blake.bcm.edu/eman2/doxygen_html/classEMAN_1_1Transform.html Transform] class for storing/managing Euler angles,translations, scales and x mirroring. At any time a Transform object $$Tr$$ defines a group of 4 transformations of a rigid body that are applied in a specific order, namely

$$ Tr \equiv M T S R $$

Where $$M$$ is a mirroring operation about the x-axis, $$T$$ is a translation, $$S$$ is a uniform, positive, non zero scaling operation and $$R$$ is a rotation. The Transform object stores these transformations internally in a 4x4 matrix, as is commonly the case in computer graphics applications that use homogeneous coordinate systems (i.e. OpenGL). In our approach the internal 4x4 transformation matrix is constructed in this way

$$ Tr = [[sMR,M\mathbf{t}],[\mathbf{0}^T,1]]$$

Where $$s$$ is the constant scaling factor, $$M$$ is the identity or the x-mirroring matrix, $$R$$ is a $$3x3$$ rotation matrix and $$\mathbf{t}=(tx,ty,tz)^T$$ is the translation.

The Transform object in Python

Constructing a Transform

There a three ways to construct a Transform object in Python

   1 t = Transform() # default constructor, t is the identity
   2 t = Transform({"type":"eman","az":10,"alt":150,"scale":2.0,"mirror":True,"tx":3.4}) # construction using a dictionary
   3 #  PAP  What happens when some are not specified?  What are they set to?  As in below:
   4 p = Transform({"type":"eman","az":10,"mirror":True,"tx":3.4}) # construction using a dictionary
   5 s = Transform(t) # copy construction - s is precisely the same as t

Setting/getting rotations

   1 t = Transform()
   2 t.set_rotation({"type":"spider","phi":32,"theta":12,"psi":-100})
   3 spider_rot = t.get_rotation("spider")
   4 eman_rot = t.get_rotation("eman")
   5 s = Transform(eman_rot) # works fine

Setting/getting scale

   1 t = Transform()
   2 t.set_scale(2.0)
   3 scale = t.get_scale()
   4 s = Transform({"scale":scale}) # set scale as part of construction

Setting/getting mirror

   1 t = Transform()
   2 #  PAP:  I changed it to 1, by which I think you mean True.
   3 #  However, it seems to be inconsistent, as in set mirror you put 1, but get mirror returns False.
   4 # In short, you cannot do t.set_mirror(u.get_mirror()).
   5 # DSAW - That is incorrect. It works on my machine.
   6 t.set_mirror(2.0) # DSAW - this results in an error, it is illegal
   7 t.set_mirror(1.0) # DSAW - this results in an error, it is illegal
   8 t.set_mirror(1) # fine
   9 t.set_mirror(0) # fine
  10 t.set_mirror(False) # fine
  11 t.set_mirror(True) # fine
  12 mirror = t.get_mirror()
  13 s = Transform({"mirror":mirror}) # set mirror as part of construction

Setting/getting translation

   1 t = Transform()
   2 t.set_trans(1,2,3) # method 1
   3 t.set_trans(Vec3f(1,2,3)) # method 2
   4 t.set_trans([1,2,3]) # method 3 - the tuple is converted to a Vec3f automatically
   5 v = t.get_trans()
   6 s = Transform("tx":v[0],"ty":v[1],"tz":v[2]) # set translation as part of construction

Setting/getting parameters

You can tell a Transform deduce any of its parameters from a dictionary. Similarly you can get the parameters of a Transform as a dictionary

   1 t = Transform()
   2 t.set_params({"type":"eman","az":10,"alt":150,"scale":2.0,"mirror":True,"tx":3.4})
   3 d = t.get_params("eman") # must specify the euler convention
   4 s = Transform(d) # s is the same as t
   5 d = t.get_params("spider")
   6 s = Transform(d) # s is the same as t
   7 d = t.get_params("matrix")
   8 s = Transform(d) # s is the same as t

A Transform multiplied by a Vec3f

   1 t = Transform()
   2 t.set_params({"type":"eman","az":10,"alt":150,"scale":2.0,"mirror":True,"tx":3.4})
   3 v = Vec3f(3,4,10)
   4 v_transformed = t*v
   5 v_transformed = t.transform(v) # can also do it this way if you prefer

2D degenerate use of the Transform object in Python

The Transform object can be used as though it were a 2D transformation matrix. In this case the interface for setting/getting scale and mirror is unchanged. However setting the rotation should be done using the euler type "2d", and there are some accommodations for setting and getting 2d translations and parameters.

Setting/getting 2D rotations

Use the "2d" euler type, and the angle is specified using "alpha"

   1 t = Transform()
   2 t.set_rotation({"type":"2d","alpha":32})
   3 a = t.get_rotation("2d")
   4 a = t.get_rotation_2d() # same as line above it
   5 s = Transform(a) # works fine

Setting/getting 2D translation

The interface is more or less identical to the 3D case

   1 t = Transform()
   2 t.set_trans(1,2) # method 1
   3 t.set_trans(Vec2f(1,2)) # method 2
   4 v = t.get_trans_2d()
   5 s = Transform("tx":v[0],"ty") # set translation as part of construction

Setting/getting 2D parameters

For getting the parameters in 2D form there is the get_params_2d function

   1 t = Transform()
   2 t.set_params({"type":"2d","alpha":10,"scale":2.0,"mirror":True,"dx":3.4,"dy":0.0}) # no special interface required for 2D, use the same as 3D
   3 d = t.get_params_2d() # no euler type required because there is only one ("2d")
   4 s = Transform(d) # s is the same as t

A Transform multiplied by a Vec2f

The interface is precisely the same as the interface for

   1 t = Transform()
   2 t.set_params({"type":"eman","az":10,"alt":150,"scale":2.0,"mirror":True,"tx":3.4})
   3 v = Vec2f(3,1)
   4 v_transformed = t*v
   5 v_transformed = t.transform(v) # can also do it this way if you prefer

A more in depth look at the dictionary constructor

The dictionary constructor, which is equivalent to calling the default constructor followed by the set_params function, takes up to 9 parameters as exemplified in the following

   1 t = Transform({"type":"eman","az":10,"alt":150,"phi":20,"scale":2.0,"mirror":True,"tx":3.4,"ty":3,"tz":2})
   2 t = Transform({"type":"spider","phi":10,"theta":150,"psi":20,"scale":2.0,"mirror":True,"tx":3.4,"ty":3,"tz":2})

If any of the angles or not specified they are implicitly set to 0

   1 t = Transform({"type":"spider","phi":10,"scale":2.0,"mirror":True,"tx":3.4,"ty":3,"tz":2}) # spider theta and psi are both 0
   2 t = Transform({"type":"spider","scale":2.0,"mirror":True,"tx":3.4,"ty":3,"tz":2}) # spider phi, theta and psi are all 0

If no euler type is specified then the rotation matrix is the identity

   1 t = Transform({"scale":2.0,"mirror":True,"tx":3.4,"ty":3,"tz":2}) # Rotation matrix is the identity

Similarly if any of the translation parameters are not specified they are implicitly set to 0

   1 t = Transform({"scale":2.0,"mirror":True,"tx":3.4,"ty":3}) # tz is 0
   2 t = Transform({"scale":2.0,"mirror":True}) # tx,ty and tz are all 0

If scale is not specified it is by default 1.0, similarly if mirror is not specified it is be default False

   1 t = Transform({"tx":3.4,"ty":3,"tz":3}) # scale is 1.0, mirror is False

set_params considerations

The transform function The dictionary constructor is the equivalent of called the default constructor and following it with a call to set_params

   1 d = {"type":"eman","az":10,"alt":150,"phi":20,"scale":2.0,"mirror":True,"tx":3.4,"ty":3,"tz":2}
   2 t = Transform({"type":"eman","az":10,"alt":150,"phi":20,"scale":2.0,"mirror":True,"tx":3.4,"ty":3,"tz":2})
   3 s = Transform()
   4 s.set_params(d) # s is identical to t

If calling the set_params function on a Transform that has already been initialized and had many things done than you should be aware of the following:

If new parameters are not set than the old ones are retained

   1 t = Transform({"type":"eman","az":10,"alt":150,"phi":20,"scale":2.0,"mirror":True,"tx":3.4,"ty":3,"tz":2})
   2 d = {"tx":23,"ty":23,"tz":12}
   3 t.set_params(d) # t still has the same rotation, scale and mirror but it has an updated translation

For rotation and translation, setting one is equivalent to setting them all

   1 t = Transform({"tx":3.4,"ty":3,"tz":2})
   2 d = {"tx":23}
   3 t.set_params(d) # ty and tz are now 0
   4 #
   5 t = Transform({"type":"eman","az":10,"alt":150,"phi":20})
   6 d = {"type":"eman","phi":10}
   7 t.set_params(d) # alt and az are 0
   8 t.set_params({"type":eman}) # all angles are 0