Forum Archive

View transform origin

mikael

When transforming a view (not a scene), what is the origin, and can I change it?

Thanks,
Mikael

mikael

Replying to myself:
Origin is the center, as stated in the docs. Have not found any method to change this.

More view transform confusion: rotation seems an absolute transform (sets a specific angle, not incremental), while translate is relative to current position. Confusion about this made also the result of invert() surprising - expected invert to return the view to the original position, which it did not, as there was rotation involved.

I kindly request pointing this out in the docs.

JonB

If you want incremental transforms, you use concat. If you just set your view.transform to a new transform, it completely overrides any existing transform, and so you end up with a transform relative to the original view location. Note that rotations, and possibly scale, are relative to the original view center, so if you have a translation currently, then concat a rotation, you rotate with a lever arm in place. if you want to rotate in place, you have to apply the rotation before the current transform.

Here is a little playground that shows that setting transform always results in an absolute transform from the original view. By using concat, you can use incremental. I also show a few contrived examples where invert is useful, and an example where the rotation origin is changed - which involves a translation, rotation, then inverted original translation.

# coding: utf-8
#   ui.Transform playground
#     discover that all transforms are absolute and override existing transform, unless using concat
#.    also, discover use for invert, such as changing order of transforms
import ui
from math import pi

v=ui.View(frame=(0,0,700,700),bg_color='white')
target=ui.Label(bg_color=(.5,.5,.5))
target.text='Target'
target.center=500,200
crosshair=ui.Label()
crosshair.text='+'
crosshair.size_to_fit()
crosshair.center=500,200
v.add_subview(target)
v.add_subview(crosshair)

#define some actions which operate on target
def translate_absolute(sender):
   target.transform=ui.Transform.translation(100,0)
def translate_relative(sender):
   target.transform=target.transform.concat(ui.Transform.translation(100,0))
def rotation_relative(sender):
   target.transform=target.transform.concat(ui.Transform.rotation(pi/8.))
def rotation_absolute(sender):
   target.transform=ui.Transform.rotation(pi/8.)
def rotation_relative_in_place(sender):
   # invert current transform, rotate, then redo transform
   inverted_then_rotation_then_transform= target.transform.invert().concat(
         ui.Transform.rotation(pi/8.)).concat(
         target.transform)
   target.transform=target.transform.concat(inverted_then_rotation_then_transform)
def rotate_about_top_r_corner(sender):
   #first, we shift by -w/2,-h/2 to get tr corner at rot center
   # then rotate
   # then shift back so corner matches
   # then spply existing transform
   t=ui.Transform.translation(-target.width/2.,target.height/2.)
   r=ui.Transform.rotation(pi/8.)
   target.transform=t.concat(r).concat(t.invert()).concat(target.transform)


# build the buttons
buttons=[translate_absolute,
   translate_relative,
   rotation_absolute, 
   rotation_relative, 
   rotation_relative_in_place,
   rotate_about_top_r_corner]

y=400
for b in buttons:
   btn=ui.Button(title=b.__name__)
   btn.action=b
   btn.y=y
   btn.x=50
   btn.height=30
   btn.border_width=1
   btn.bg_color=(1,.7,.7)
   y+=45
   v.add_subview(btn)
v.present()
mikael

Thanks for a truly impressive reply. Would be excellent to have examples like these included in the documentation.

I tried to clear transforms by setting view.transform to None, but that did not work, so I set it to translation(0, 0) instead. Is there a "proper" way to clear the transforms?

omz

You can also use ui.Transform() to get the "identity" transformation (though effectively, translation(0, 0) does the same thing).