Object flipping/mirror

I’d like a way of flipping or mirroring objects in 180 or 90-degree rotations. This would allow the creation of interfaces for tabletop games, where each user is sitting in front of the other.
I have asked about how to do this here, but reading through the code and the documentation, it seems rotations like this are impossible.
Even just allowing flipping the label rendering would suffice.

Original default LVGL:
image

Rotated object layout:
rotated object image

Hi,

You can do it with the display rotation feature. See here.

Please, see the linked images. The display rotation doesn’t resolve the issue.

Oh, I see, you want to rotate only one object/part of the screen.

You can try this:

  lv_obj_t * cont = lv_obj_create(lv_scr_act());
  lv_label_create(cont);
  lv_obj_set_size(cont, 200, 100);

  lv_obj_set_style_transform_pivot_x(cont, 100, 0);
  lv_obj_set_style_transform_pivot_y(cont, 50, 0);
  lv_obj_set_style_transform_angle(cont, 1800, 0);

My object is using the grid layout to define its size, so manually setting the size would clash with the stretch. Is it possible to rotate without setting the size?
Im going to try a few combinations of commands here.

This is my current layout.

void create2PlayerMtgScreen(int initialLife)
{
    lv_obj_t* screen = lv_obj_create(NULL);

    static lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    static lv_coord_t row_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};

    lv_obj_center(screen);
    lv_obj_set_grid_dsc_array(screen, col_dsc, row_dsc);

    lv_obj_t* counter = createCounter(initialLife, screen);
    lv_obj_set_grid_cell(counter, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_STRETCH, 0, 1);

    counter = createCounter(initialLife, screen);
    lv_obj_set_grid_cell(counter, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_STRETCH, 1, 1);
    //lv_obj_set_style_transform_angle(counter, 1800, 0); 

    lv_scr_load(screen);
}

I have tested using an absolute layout and it seems that the rotation doesn’t rotate the mask that clips the graphic. So, when I rotate the container, it gets cut and the rotation happens on the wrong place:

In LVGL v8.3.4 setting the pivot like this:

  lv_obj_set_style_transform_pivot_x(cont, lv_pct(50), 0);
  lv_obj_set_style_transform_pivot_y(cont, lv_pct(50), 0);

should work too. So you don’t have to know the exact size.

How does it work at your end?

Using this I was able to sort of rotate in 180 degree rotations. 90 deg rotations still cause mask clipping issues.

180 degree rotations cause this issue:

Can you send the code (that I can copy paste into a simulator) for this UI?

Sure! I’m going to strip some pieces from it, other parts that shouldn’t be necessary.

Main class:

#include <Arduino.h>

#include "ZLVGL/ZLVSetup.h"
#include "ZLVGL/ZLVPanel.h"
#include "ZLVGL/ZLVTouch.h"

#include "Counter/LifeCounter.h"
#include "Counter/TwoPlayerCounterScreen.h"

void setup()
{
    ZLVSetupTouch();
    ZLVCreatePanel();
    
    create2PlayerMtgScreen(40);
}

void loop()
{
    lv_task_handler();
    //lv_timer_handler();
    delay( 5 );
}

ZLVSetup.h:

#pragma once

#include <Arduino.h>

#define TOUCH_SDA 33
#define TOUCH_SCL 32
#define TOUCH_INT 21
#define TOUCH_RST 25
#define TOUCH_WIDTH  480
#define TOUCH_HEIGHT 320

static const uint16_t width  = 320;
static const uint16_t height = 480;

ZLVPanel.h:

ZLVTouch.h:

#pragma once

#include "ZLVSetup.h"
#include "TAMC_GT911.h"
#include <lvgl.h>

TAMC_GT911 touchPanel = TAMC_GT911(TOUCH_SDA, TOUCH_SCL, TOUCH_INT, TOUCH_RST, TOUCH_WIDTH, TOUCH_HEIGHT);

static bool touchSetup;

void ZLVSetupTouch()
{
    touchSetup = true;
    touchPanel.begin();
    touchPanel.setRotation(ROTATION_RIGHT);
}

void ZLVReadTouch(lv_indev_drv_t* indev_driver, lv_indev_data_t* data)
{
    if(!touchSetup) return;

    touchPanel.read();
    bool touched = touchPanel.isTouched;

    if( !touched )
    {
        data->state = LV_INDEV_STATE_REL;
    }
    else
    {
        data->state = LV_INDEV_STATE_PR;
        data->point.x = width - touchPanel.points[0].y;
        data->point.y = touchPanel.points[0].x;
    }
}

LifeCounter.h:

TwoPlayerCounterScreen.h:


#pragma once

#include "LifeCounter.h"
#include <Arduino.h>

void create2PlayerMtgScreen(int initialLife)
{
    static lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    static lv_coord_t row_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};

    lv_obj_t* screen = lv_obj_create(NULL);
    lv_obj_center(screen);
    lv_obj_set_grid_dsc_array(screen, col_dsc, row_dsc);

    lv_obj_t* counter = createCounter(initialLife, "Player 1", screen);
    lv_obj_set_grid_cell(counter, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_STRETCH, 0, 1);

    lv_obj_set_style_transform_pivot_x(counter, lv_pct(50), 0);
    lv_obj_set_style_transform_pivot_y(counter, lv_pct(50), 0);

    lv_obj_set_style_transform_angle(counter, 1800, 0);

    counter = createCounter(initialLife, "Player 2", screen);
    lv_obj_set_grid_cell(counter, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_STRETCH, 1, 1);

    lv_obj_set_style_transform_pivot_x(counter, lv_pct(50), 0);
    lv_obj_set_style_transform_pivot_y(counter, lv_pct(50), 0);
    
    //lv_obj_set_style_transform_angle(counter, 0, 0);

    lv_scr_load(screen);
}

I’ve tested your code and got a similar artifacts when LV_COLOR_SCREEN_TRANSP was not enabled. So have is enabled for you in lv_conf.h?

If so, please enable LVGL’s logging.