Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
3D rotations in a UImage
-
@madivad I run this code without syntax error but it crashes
from objc_util import * from ctypes import * import math '''we need to wrap the CATransform structure ''' class CATransform (Structure): _fields_ = [('m11', c_float),('m12', c_float),('m13', c_float),('m14', c_float),('m21', c_float),('m22', c_float),('m23', c_float),('m24', c_float), ('m31', c_float),('m32', c_float),('m33', c_float),('m34', c_float), ('m41', c_float),('m42', c_float),('m43', c_float),('m44', c_float)] CATransform3DMakeTranslation = c.CATransform3DMakeTranslation CATransform3DMakeTranslation.argtypes = [c_float, c_float, c_float] CATransform3DMakeTranslation.restype = CATransform CATransform3DRotate = c.CATransform3DRotate CATransform3DRotate.restype = CATransform CATransform3DRotate.argtypes = [CATransform, c_float, c_float, c_float, c_float] identity= CATransform3DMakeTranslation(0,0,0) identity.m34 = -1/500 rot = CATransform3DRotate(identity, math.radians(45), 0,1,0) v=ui.View(frame=(0,0,800,800)) sv=ui.View(frame = (50,5,300,300)) sv.bgcolor = 'blue' v.add_subview(sv) v.present() SV = ObjCInstance(sv) layer = SV.layer() layer.transform = rot
Error
Fatal Python error: Segmentation fault Current thread 0x000000016be37000 (most recent call first): File "/private/var/mobile/Containers/Shared/AppGroup/1B829014-77B3-4446-9B65-034BDDC46F49/Pythonista3/Documents/a8.py", line 20 in <module> Extension modules: pykit_io, _ui, _appex, _frameworksimporter, console, _debugger_ui (total: 6)
-
TLDR; I have been researching this for hours and in checking out the image.transform options I can see that I think the transforms I need are already within pythonista, but I had chosen the wrong one initially and had never thought to look at the others. I feel quite stupid about now and am going back to the drawing board. I'm going to leave this post here because I've thought about and learned a lot and this has kind of formed my notes and I want to take the time to thank @JonB and @cvp for their time. I might revisit this code as an ObjC wrapper if I'm wrong, because I feel like I'm close to nailing it, but there is no NEED to reinvent the wheel.
I have to revisit the sample code I pasted at the origin because that was just plucked from SE and now I want to work out how I want to apply it. What I pasted isn't actually the code, I'm working on that part now. This is more about the learning for me at the moment and I am trying to get my head around the terminology for the transforms and matrixes. The maths is really beyond me and I'm starting to confuse a lot of these terms.
Looking at what @jonb has put together is based on some mistakes I think I have misstated.
This is where I'm thinking atm:
The code I pasted in the OT was from a SE site and not exactly right for my application. In learning the terms (mostly tonight) I need to MAKE the translation. At least, that's what I think I want.
I want the 3D equiv to CGAffineTransform, which doesn't take in the original instance (hence why I think I need the create/make version).
CATransform3DMakeRotation vs
CATransform3DRotate vs
CGAffineTransform <-- the 2D version
I think I need to use first one over the second.I'm still confused by the
c.CATrans....
where does thec
come from in JonB's code?Swapping out
ctypes.Structure
for justStructure
workedCATransform3DMakeTranslation declaration:
CATransform3D CATransform3DMakeTranslation(CGFloat tx, CGFloat ty, CGFloat tz);
CATranform3DMakeRotation declaration
CATransform3D CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z);
They are both of type: CATransform3D, declared here:
typedef struct CATransform3D { ... } CATransform3D;
Based on the code you guys have posted, I believe this is going to translate to:
class CATransform3D (Structure): ### NOTE 3D _fields_ = [('m11', c_float),('m12', c_float),('m13', c_float),('m14', c_float),('m21', c_float),('m22', c_float),('m23', c_float),('m24', c_float), ('m31', c_float),('m32', c_float),('m33', c_float),('m34', c_float), ('m41', c_float),('m42', c_float),('m43', c_float),('m44', c_float)]
Now I have got it crashing too, and I think it's to do with the returned 3DTransform, I need to convert it back to Affine... which means I think I would need to take the original image and trying to 3DRotate it. It's the image from a
ui.ImageView
which is..... it was at this point he knew he'd stuffed upI'll post more when I recover.
I'll be back. :)
-
@madivad said
where does the c come from in JonB's code?
Part of Pythonista...(try "print(c) in a script which does not import anything)
-
Now add
from objc_util.import *
and tryprint(c)
again.This is exactly why PEP8 advises against wildcard imports.
-
@madivad Could you try this script https://github.com/cvpe/Pythonista-scripts/blob/master/Gists/a4.py.
It will ask you first to select a photo from your camera roll, for example an iPhone face, but any photo would be ok.
Then, the script displays an empty full screen ui.View with a SceneKit subview.
This SceneKit shows a box representing an iPhone with your selected photo as a face.
You can use two fingers to rotate or scale the box.
And if you move or rotate your device, you will see the box doing the same.
There is still a limitation (which I can't actually remove), the box comes back at its initial orientation.
But anyway, you can see what Pythonista offers to you.Feedback would be appreciated
-
@cvp
Looks like @cvp came to the rescue with my untested code. But here is the original, marked up with the intent. You can do google search for iOS headers CATransform3D, and get the c header files describing the various structure and functions, then just have to manually translate. The way CFUNCTION wrappers work, the doll contains the symbol, but you have to manually set return type (restype) and argument types argtypes.The low level stuff is slightly trickier to work it’s compared to actual ObjC. When converting objc code, you usually don’t need to mess with any of that, because the method encodings define everything, and are built into the runtime. But when passing structures instead of objects, you often have to override restypes. Rubicon handles this cleaner than the current objc_util…
from objc_util import * from ctypes import * import ui import math, copy '''we need to wrap the CATransform structure struct CATransform3D { CGFloat m11, m12, m13, m14; CGFloat m21, m22, m23, m24; CGFloat m31, m32, m33, m34; CGFloat m41, m42, m43, m44; }; this could also have been done with a pack ''' class CATransform3D(Structure): _fields_ = [('m11', CGFloat),('m12', CGFloat),('m13', CGFloat),('m14', CGFloat), ('m21', CGFloat),('m22', CGFloat),('m23', CGFloat),('m24', CGFloat), ('m31', CGFloat),('m32', CGFloat),('m33', CGFloat),('m34', CGFloat), ('m41', CGFloat),('m42', CGFloat),('m43', CGFloat),('m44', CGFloat), ] ''' note, c is a variable imported in objc_util, which is the cDLL for the static library. ''' ''' /* Returns a transform that translates by '(tx, ty, tz)': * t' = [1 0 0 0; 0 1 0 0; 0 0 1 0; tx ty tz 1]. */ CA_EXTERN CATransform3D CATransform3DMakeTranslation (CGFloat tx, CGFloat ty, CGFloat tz) ''' CATransform3DMakeTranslation = c.CATransform3DMakeTranslation CATransform3DMakeTranslation.argtypes = [CGFloat, CGFloat, CGFloat] CATransform3DMakeTranslation.restype = CATransform3D ''' /* Rotate 't' by 'angle' radians about the vector '(x, y, z)' and return * the result. If the vector has zero length the behavior is undefined: * t' = rotation(angle, x, y, z) * t. */ CA_EXTERN CATransform3D CATransform3DRotate (CATransform3D t, CGFloat angle, CGFloat x, CGFloat y, CGFloat z) ''' CATransform3DRotate = c.CATransform3DRotate CATransform3DRotate.restype = CATransform3D CATransform3DRotate.argtypes = [CATransform3D, CGFloat, CGFloat, CGFloat, CGFloat] ''' named const can often be retrieved using in_dll. however, we need a mutable copy, so we dont wat to use this as identity''' CATransform3DIdentity=CATransform3D.in_dll(c,'CATransform3DIdentity') identity=copy.deepcopy(CATransform3DIdentity) '''other ways to get identity: identity = CATransform3D() identity.m11=1 identity.m22=1 identity.m33=1 identity.m44=1 or identity=CATransform3DMakeTranslation(0,0,0) note, we cant use CATransformMake because that is a c macro, not an exported function ''' v=ui.View(frame=(0,0,500,500)) v.bg_color='white' sv=ui.ImageView() sv.image=ui.Image.named('card:Clubs3') sv.size_to_fit() v.add_subview(sv) v.present('sheet') layer = sv.objc_instance.layer() #another way to get current transrorm #identity=layer.transform(argtypes=[],restype=CATransform3D) identity.m34 = -1/500 #sets perspective #rotate rot = CATransform3DRotate(identity, math.radians(55), 0,1,0) layer.setTransform_(rot,argtypes=[CATransform3D],restype=None)
-
@JonB Marvelous, as usual... Sometimes, I ask me why I try to do something here.
Thanks for him and for me for your clear explanations. -
@cvp by the way, the crash was because I was using c_float instead of CGFloat. objc_util defines CGFloat, along with some other useful iOS c types. CGFloat is double on 64 bit (all modern version of iOS) and float on 32 bit machines (ipad 3 or maybe 4 and earlier). Also, as you guys found, I mistakenly forgot the last row of the transform.
There are ways to animate the rotations.
But I suspect SceneKit is ultimately easier to work with. -
-
Have a look at this wrapper for sceneKit. There are plenty of examples, too. Though I made no regression tests lately it should still work, or at least help with understanding the bridging.