When transforming a view (not a scene), what is the origin, and can I change it?
Thanks,
Mikael
When transforming a view (not a scene), what is the origin, and can I change it?
Thanks,
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.
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()
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?
You can also use ui.Transform() to get the "identity" transformation (though effectively, translation(0, 0) does the same thing).