1

I have a simple Composable Canvas that draws a shape multiple times in different positions, and I wish to apply rotation to each iteration of the shape as per a particular algorithm. However, I am unable to fully control the positioning of the shapes in the canvas since the rotate transformation seems to apply a translation trasformation of its own. Here's what I have

    @Preview
    @Composable
    fun CanvasCollosum() {
        val painter = rememberVectorPainter(image = ImageVector.vectorResource(id = R.drawable.tick))
        Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center){
            Canvas(modifier = Modifier.height(200.dp).aspectRatio(1f)) {
                with(painter) {
                    repeat(12) {
                        (it * 30f).let { angle ->
                            translate(
                                top = 0f, // Some translation is still applied by 'rotate'
                                left = 0f
                            ) {
                                rotate(angle) {
                                    draw(
                                        Size(100f, 100f),
                                        colorFilter = ColorFilter.tint(Color.White)
                                    )
                                }
                            }
                        }
                    }
                }
            }
        }
    }

Hence, for some reason, the positionings of all of the shapes here (12 in total) assume the shape of a circle, which is not at all what I expected. I can assume it is something related to the 'pivot' of the rotation. However, it is set, by default, as the center of the Canvas, which seems fairly appropriate. The shape is being rendered far away from the pivot which seems to be causing the translation effect to occur as a side effect, so the question is -- How do I render all my shapes with a fixed rotation, and position them with specific (x, y) cords, given that I have an algorithm to position them relative to the center of the Canvas?

For reference, here's some outputs, displaying a single shape, with the only varying property being the rotation angle

0f Degrees

0 Degrees

90f Degrees

enter image description here

180f Degrees

enter image description here

270f Degrees

enter image description here

360f is the same as 0f

This does not seem like something that should be happening here, but it is.

The Square is the Box Composable and the little white thing is the shape in concern.

Richard Onslow Roper
  • 5,477
  • 2
  • 11
  • 42

1 Answers1

2

It is not applying translation, but is rotating around a pivot, which is set to the center of the Box.

rotate has a parameter named pivot that will solve your problem:

@Composable
fun Rotation() {
    val painter = rememberVectorPainter(image = Icons.Default.ThumbUp)
    Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        Canvas(
            modifier = Modifier
                .height(200.dp)
                .background(color = Color.Red)
                .aspectRatio(1f)
        ) {
            with(painter) {
                repeat(4) {
                    rotate(
                        degrees = (it * 90f),
                        pivot = Offset(
                            painter.intrinsicSize.width * 2,
                            painter.intrinsicSize.height * 2
                        )
                    ) {
                        draw(
                            Size(100f, 100f),
                            colorFilter = ColorFilter.tint(Color.White)
                        )
                    }
                }
            }
        }
    }
}

The code above produces this result:

In order to change the point, around which the rotation is done, simple change the coordinates in pivot parameter.

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
Primož Ivančič
  • 1,984
  • 1
  • 17
  • 29
  • For my specific use-case, it'll be good to make it the center of the image by dividing your pivot offset by 2, but yeah, that does it. – Richard Onslow Roper Apr 26 '22 at 08:09
  • Didn't work even after dividing by 2. Is there a way to make it so that the shapes are drawn directly at the center of the canvas instead of a the top left corner? I have the dimensions of the shape and I'd like all the them to be rotated by the degrees I specified, but all positioned at 0,0 -- overlapping. This way I can translate them to the center and then let my algorithm decide the positioning of each shape. Basically, my algorithm needs shapes originally at the center, which it can position. So, all the shapes must overlap for the algorithm to work. – Richard Onslow Roper Apr 26 '22 at 08:15
  • Got it finally. You were using the Intrinsic Size of the vector, but I had specifically specified the size in the Canvas, so I needed it to be set to half of that instead. Also, your example working in this case seems much of a coincidence since the pivot should be calculated based on the `Size(100f, 100f)` parameter, instead of using the instrinsic size. I think the intrinsic size of the painter actually turned out to be 100x100, which is why it worked. Anyway, good solution sir, thank you. I'll try to implement it real quick and see if everything checks out. – Richard Onslow Roper Apr 26 '22 at 08:19
  • 1
    Well, I didn't try to solve your particular case in detail. I just told you why it behaves the way it does and shoved you example of how to fix it. The details are up to you - as it should be, I believe. :) – Primož Ivančič Apr 26 '22 at 08:33