Hello! How can I create perspective effects when r...
# compose-desktop
k
Hello! How can I create perspective effects when rotating objects away from the screen in Compose? https://developer.android.com/reference/kotlin/androidx/compose/ui/graphics/GraphicsLayerScope#cameraDistance() This doc says "... A short distance may cause much more perspective distortion upon rotation, ..." but I haven't been able to achieve any perspective effect at all by manipulating this value, on an Image() item. Thanks!
There are examples online where mathematical transformations are calculated, especially with vector data. I would've also thought some sort of matrix setup would be needed as well. I guess I was hoping there was a simple Field Of View setting that could give an impression of perspective distortion!
i
That is not yet supported in desktop target. You can track the feature here
👍🏻 1
k
Thank you! So is there any way I can squash/stretch the top and bottom edges of an object to give the impression of perspective? Or a way to remap the corners of objects to new 2D locations on screen to simulate this? Thanks @Igor Demin
i
You can achieve this, using matrix transformations
Copy code
Box(Modifier.drawWithContent {
    drawIntoCanvas {
        it.concat(matrix)
        drawContent()
    }
}) {

}
Don't know yet how exactly, but you can look at these tutorials: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/transforms/3d-rotation https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/transforms/matrix
k
Thank you so much! I will have a look right now. 😎
Mate, thanks so much for knowing and sharing where info can be found! That first article you linked, the 3D rotations one was what I needed. I really wanted a library or code function that does it for me, but that article explained very clearly why the matrix system works, as well as how to apply it! I've really learnt something! It helps me understand what other people have skimmed over, so now I will attempt to create my own 3D rotation functions for drawn objects, such as lines and curves. With any luck, I might be able to get it to work with images as well! Thank you!!
🎉 2
Hello @Igor Demin, I'm just working on trying the tips you gave me, but I'm having trouble finding a
Camera
object to use with the
Matrix
. It seems others are using
android.graphics.Camera
but I'm not sure that's the thing to do with a Compose Project?? Thanks!
i
android.graphics.Camera is the class from Android, which doesn't exist on Desktop. Probably it corresponds to the Skia class https://github.com/google/skia/blob/main/include/utils/SkCamera.h, but Skia bindings for desktop don't contain it, because it is deprecated
k
Hmm. OK, so I need to look further then. It seems a Camera object is needed to compute the transformations with the matrix. I did see however, another way of doing it using OpenGL 2.0 Would this be a possibility with Desktop Compose?
OpenGL ES 2.0 I mean.
i
Would this be a possibility with Desktop Compose?
Compose has
canvas.setMatrix
function, which accepts 4x4 matrix. 4x4 matrix can manipulate how the image projected to the screen.
k
I found this in the docs for SKCanvas, which was referenced in that SKCamera page you linked: Transformations The canvas supports a number of 2D transformations. Unlike other 2D graphic systems like CoreGraphics or Cairo, SKCanvas extends the transformations to include perspectives. You can use the ScaleSkewTranslateRotateDegreesRotateRadians to perform some of the most common 2D transformations. For more control you can use the SetMatrix to set an arbitrary transformation using the SKMatrix and the Concat to concatenate an SKMatrix transformation to the current matrix in use. The ResetMatrix can be used to reset the state of the matrix.
OK! That looks promising! I'll look up
canvas.setMatrix
. Thanks!
I'm actually wondering..... If maybe I don't need any Camera object in my use case, because I'm not trying to compute the transformations if I use the maths formulae to calculate them myself, and apply them to the image??? Hmm. I might try that...
Then I should be able to just concatenate the matrices and BINGO! I wish....
@Igor Demin!!!! MATE!!! I've got it WORKING!!! Just have to pop my mouse movement code in to get live updates!!! THANK YOU SO MUCH FOR THE HINTS!!
🎉 1
K 1
😁
Now I just need to find how to refresh the object when the rotate value changes on mouse movement!
I was using TransformableState to do Zoom/Pan/Rotate before, so there may be a similar way of redrawing the object with new matrix values. I think I've seen an OnDirty function somewhere, so will look that up.
i
find how to refresh the object when the rotate value changes on mouse movement!
if your matrix is wrapped to
mutableStateOf
, Compose will automatically redraw the Canvas. alternative is to call `currentRecomposeScope.invalidate`:
Copy code
@Composable
fun App() {
    val scope = currentRecomposeScope
    
    Button(onClick = {
        scope.invalidate()
    }) {
        Text("Refresh")
    }
    
    Canvas(Modifier) {
        ...
    }
}
k
Thanks Igor! I'll try those things. So you mean to make the matrix like
Copy code
val matrix by remember { mutableStateOf( Matrix( ...etc...
or just like
Copy code
val matrix = mutableStateOf( etc...
Also, should it best be created within the Canvas modifier, or further back up the hierarchy? My main issue at the moment is tweaking the rotation and scale origins to suit my needs, as I have a couple of nested Surfaces to handle the panning (translation), zoom (scale), and rotation separately. Cheers!
i
val matrix by remember
this one, if you define
matrix
in Composable function.
remember
just remembers the state instance across recompositions (
mutableStateOf
itself).
Also, should it best be created within the Canvas modifier
It should be created outside of Canvas, but you can access it inside Canvas
k
Thanks so much! I will try that. I've had a play with the matrix properties today, and got things "improved!"
@Igor Demin @darkmoon_uk 😁🥳😁🥳😁
🛤️ 2
🎉 2
Sincere apologies to Model Railroader for borrowing one of their fantastic plans to try out the effects....
Thank you so much for your guidance and hints to get me this far!
i
Awesome! 😲🎉
k
I think so too! My plan is to start placing railway layout components on that "work plane" now, and make them interactive thru our electronic wizardry that controls the trains and layout items!
d
Whoah, awesome work @Kebbin! 🤩
🤓 1
...what are we looking at here, technically, @Kebbin? Is this a perspective matrix transformation applied to a
@Composable
, in a way that you could also sit 'untransformed' components around it as your UI/tool palette?
I think you might just be the first and only personal to have created a 3D application in Compose.
🖥️ 1
🤠 1
k
I have applied a non-affine transform to the
@Composable Surface
. Anything placed on it is affected, and anything else around it is not. I will build a component palette and menus, etc. I now can move on to creating my data model, know that I have my 3D view basics in place!
d
Well this is very cool... Have you proven hit detection on the Surface too? As in, can you get back the coordinates on the transformed
@Composable
to identify e.g. the switch point or light clicked? Since I tinker with a bit of hardware too @Kebbin I'm curious on the hardware side - what does an interface to the railway kit look like; RS232, or is it all Wifi and API's as well these days?
Wait, I'm over-thinking this - I guess if you made an element
.clickable
then transformed it... it just remains clickable 🙂
k
www.dcc-ex.com I'll catch up with you after I've got the kids off to school!
Hey mate! You got me curious thinking about the Clickable-ness... I have to keep reminding myself this effect is only a mash of the display, not a rotation of the surface! Sadly, the coordinates of the rotated components remain as they would be untransformed. I just got to trying it this evening.... So for the full effect, I will need to pursue a proper 3D library. The only other option is to do the same maths on the mouse position, and try guessing what's been clicked that way. Will try that first!!