Problem about lv_example_scroll_2()

When I tried to zoom out on the button and hold it down to scroll, it kept shaking from side to side.
There seems to be a problem with this position calculation down here.
indev_proc_press >lv_obj_transform_point(indev_obj_act, &proc->types.pointer.act_point, true, true);


I could reproduce the issue. What problem do you see with position calculation?

You can just delete this, it works fine.
indev_proc_press >lv_obj_transform_point(indev_obj_act, &proc->types.pointer.act_point, true, true);

The calculation of position,For example, the x coordinate: pivot.x = obj->coords.x1 + lv_obj_get_style_transform_pivot_x(obj, 0) + (act_point - last_point)
When the button is moving, the position of X1(obj->coords.x1) relative to X2(act_point ) should be consistent so that the final calculated coordinates X3(act_point ) can be accurate.

This is my point of view,what do you think?

To be sure we are on the same page this is the code I’m using:

    lv_obj_t * panel = lv_obj_create(lv_scr_act());
    lv_obj_set_size(panel, 280, 120);
    lv_obj_set_scroll_snap_x(panel, LV_SCROLL_SNAP_CENTER);
    lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_ROW);
    lv_obj_align(panel, LV_ALIGN_CENTER, 0, 20);

    uint32_t i;
    for(i = 0; i < 10; i++) {
        lv_obj_t * btn = lv_btn_create(panel);
        lv_obj_set_size(btn, 150, lv_pct(100));
        lv_obj_set_style_transform_zoom(btn, 128, 0); /*ADD THIS*/

        lv_obj_t * label = lv_label_create(btn);
        if(i == 3) {
            lv_label_set_text_fmt(label, "Panel %"LV_PRIu32"\nno snap", i);
            lv_obj_clear_flag(btn, LV_OBJ_FLAG_SNAPPABLE);
        }
        else {
            lv_label_set_text_fmt(label, "Panel %"LV_PRIu32, i);
        }

        lv_obj_center(label);
    }
    lv_obj_update_snap(panel, LV_ANIM_ON);
}

If I remove the obj->coords.x1/y1 part from transform_point nothing will be drawn.


I think obj->coords.x1/y1 should be there because when there is a transformed object (zoomed or rotated) the points are interpreted in that transformed coordinate system of the object to correctly find the also transformed children.


I see that the problem is that when to object is moving due to auto focusing and snapping LVGL incorrectly thinks that there is vector as the object to which the point was relative was moved. We could say that save the non-transformed act_point and last_point and calculate the vector from them but it wouldn’t work e.g if there is a scrollabel widget rotated by 90 degree (x and y is swapped) and you make a vertical scroll. For the vertical scroll the widget will think “okay, scroll vertically” but as it’s rotated it will scroll horizontally.

Another thing: imagine that there is a slider with a 100%->1% zoom animation and you keep pressing the same point on the screen. From the slider’s point of view (which being scaled down) you always point to a different point of the slider. So it can think that there is a vector.

So I don’t see a good solution here. Maybe you already know how to solve it and only I don’t understand it correctly. If you have a working solution please send a pull request. I’m really curious about it :slight_smile:

Sorry, maybe I didn’t express clearly. no remove the obj->coords.x1/y1.As follows:


This is not a solution to the problem, just a test.

I have tried to modify this problem, it may not be correct, but it should be able to give you some suggestions for reference.

How to send a pull request? There are a lot of features I don’t know how to use. :sweat_smile:

The main code modification parts are as follows.
#define LV_INDEV_TEST

typedef struct _lv_indev_proc_t {
    lv_indev_state_t state; /**< Current state of the input device.*/
    /*Flags*/
    uint8_t long_pr_sent : 1;
    uint8_t reset_query : 1;
    uint8_t disabled : 1;
    uint8_t wait_until_release : 1;

    union {
        struct {
            /*Pointer and button data*/
            lv_point_t act_point; /**< Current point of input device.*/
            #ifdef LV_INDEV_TEST
            lv_point_t indev_point; /**< Current point of input device.*/
            #endif
            lv_point_t last_point; /**< Last point of input device.*/
            lv_point_t last_raw_point; /**< Last point read from read_cb. */
            lv_point_t vect; /**< Difference between `act_point` and `last_point`.*/
            lv_point_t scroll_sum; /*Count the dragged pixels to check LV_INDEV_DEF_SCROLL_LIMIT*/
            lv_point_t scroll_throw_vect;
            lv_point_t scroll_throw_vect_ori;
            struct _lv_obj_t * act_obj;      /*The object being pressed*/
            struct _lv_obj_t * last_obj;     /*The last object which was pressed*/
            struct _lv_obj_t * scroll_obj;   /*The object being scrolled*/
            struct _lv_obj_t * last_pressed; /*The lastly pressed object*/
            lv_area_t scroll_area;

            lv_point_t gesture_sum; /*Count the gesture pixels to check LV_INDEV_DEF_GESTURE_LIMIT*/
            /*Flags*/
            lv_dir_t scroll_dir : 4;
            lv_dir_t gesture_dir : 4;
            uint8_t gesture_sent : 1;
        } pointer;
        struct {
            /*Keypad data*/
            lv_indev_state_t last_state;
            uint32_t last_key;
        } keypad;
    } types;

    uint32_t pr_timestamp;         /**< Pressed time stamp*/
    uint32_t longpr_rep_timestamp; /**< Long press repeat time stamp*/
} _lv_indev_proc_t;
static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data)
{
    lv_disp_t * disp = i->driver->disp;
    /*Save the raw points so they can be used again in _lv_indev_read*/
    i->proc.types.pointer.last_raw_point.x = data->point.x;
    i->proc.types.pointer.last_raw_point.y = data->point.y;

    if(disp->driver->rotated == LV_DISP_ROT_180 || disp->driver->rotated == LV_DISP_ROT_270) {
        data->point.x = disp->driver->hor_res - data->point.x - 1;
        data->point.y = disp->driver->ver_res - data->point.y - 1;
    }
    if(disp->driver->rotated == LV_DISP_ROT_90 || disp->driver->rotated == LV_DISP_ROT_270) {
        lv_coord_t tmp = data->point.y;
        data->point.y = data->point.x;
        data->point.x = disp->driver->ver_res - tmp - 1;
    }

    /*Simple sanity check*/
    if(data->point.x < 0) {
        LV_LOG_WARN("X is %d which is smaller than zero", data->point.x);
    }
    if(data->point.x >= lv_disp_get_hor_res(i->driver->disp)) {
        LV_LOG_WARN("X is %d which is greater than hor. res", data->point.x);
    }
    if(data->point.y < 0) {
        LV_LOG_WARN("Y is %d which is smaller than zero", data->point.y);
    }
    if(data->point.y >= lv_disp_get_ver_res(i->driver->disp)) {
        LV_LOG_WARN("Y is %d which is greater than ver. res", data->point.y);
    }

    /*Move the cursor if set and moved*/
    if(i->cursor != NULL &&
       (i->proc.types.pointer.last_point.x != data->point.x || i->proc.types.pointer.last_point.y != data->point.y)) {
        lv_obj_set_pos(i->cursor, data->point.x, data->point.y);
    }

    i->proc.types.pointer.act_point.x = data->point.x;
    i->proc.types.pointer.act_point.y = data->point.y;

    if(i->proc.state == LV_INDEV_STATE_PRESSED) {
        indev_proc_press(&i->proc);
    }
    else {
        indev_proc_release(&i->proc);
    }
#ifdef LV_INDEV_TEST
	i->proc.types.pointer.indev_point.x = data->point.x;
	i->proc.types.pointer.indev_point.y = data->point.y;
#endif
    i->proc.types.pointer.last_point.x = i->proc.types.pointer.act_point.x;
    i->proc.types.pointer.last_point.y = i->proc.types.pointer.act_point.y;
}
static void indev_proc_press(_lv_indev_proc_t * proc)
{
	#ifdef LV_INDEV_TEST
	uint8_t indev_flag = false;
	#endif
	
    LV_LOG_INFO("pressed at x:%d y:%d", proc->types.pointer.act_point.x, proc->types.pointer.act_point.y);
    indev_obj_act = proc->types.pointer.act_obj;

    if(proc->wait_until_release != 0) return;

    lv_disp_t * disp = indev_act->driver->disp;
    bool new_obj_searched = false;

    /*If there is no last object then search*/
    if(indev_obj_act == NULL) {
        indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_sys(disp), &proc->types.pointer.act_point);
        if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_top(disp),
                                                                          &proc->types.pointer.act_point);
        if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_scr_act(disp),
                                                                          &proc->types.pointer.act_point);
        new_obj_searched = true;
    }
    /*If there is last object but it is not scrolled and not protected also search*/
    else if(proc->types.pointer.scroll_obj == NULL &&
            lv_obj_has_flag(indev_obj_act, LV_OBJ_FLAG_PRESS_LOCK) == false) {
        indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_sys(disp), &proc->types.pointer.act_point);
        if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_top(disp),
                                                                          &proc->types.pointer.act_point);
        if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_scr_act(disp),
                                                                          &proc->types.pointer.act_point);
        new_obj_searched = true;
    }

    /*The last object might have scroll throw. Stop it manually*/
    if(new_obj_searched && proc->types.pointer.last_obj) {
        proc->types.pointer.scroll_throw_vect.x = 0;
        proc->types.pointer.scroll_throw_vect.y = 0;
        _lv_indev_scroll_throw_handler(proc);
        if(indev_reset_check(proc)) return;
    }

    #ifdef LV_INDEV_TEST
    if((proc->types.pointer.act_point.x == proc->types.pointer.indev_point.x) && (proc->types.pointer.act_point.y == proc->types.pointer.indev_point.y))
    	indev_flag = true;
    	
    lv_obj_transform_point(indev_obj_act, &proc->types.pointer.act_point, &proc->types.pointer.indev_point, true, true);
    
	if(indev_flag)
    {
		proc->types.pointer.last_point.x = proc->types.pointer.act_point.x;
		proc->types.pointer.last_point.y = proc->types.pointer.act_point.y;
    }
    
	#else
    lv_obj_transform_point(indev_obj_act, &proc->types.pointer.act_point, true, true);
	#endif
    /*If a new object was found reset some variables and send a pressed event handler*/
    if(indev_obj_act != proc->types.pointer.act_obj) {
        proc->types.pointer.last_point.x = proc->types.pointer.act_point.x;
        proc->types.pointer.last_point.y = proc->types.pointer.act_point.y;

        /*If a new object found the previous was lost, so send a Call the ancestor's event handler*/
        if(proc->types.pointer.act_obj != NULL) {
            /*Save the obj because in special cases `act_obj` can change in the Call the ancestor's event handler function*/
            lv_obj_t * last_obj = proc->types.pointer.act_obj;

            lv_event_send(last_obj, LV_EVENT_PRESS_LOST, indev_act);
            if(indev_reset_check(proc)) return;
        }
#ifdef LV_INDEV_TEST
static void transform_point(const lv_obj_t * obj, lv_point_t * p, lv_point_t * p1, bool inv)
{
    int16_t angle = lv_obj_get_style_transform_angle(obj, 0);
    int16_t zoom = lv_obj_get_style_transform_zoom(obj, 0);

    if(angle == 0 && zoom == LV_IMG_ZOOM_NONE) return;

    lv_point_t pivot;

    if(p1)
    {
		pivot.x = obj->coords.x1 + (p->x - p1->x) + lv_obj_get_style_transform_pivot_x(obj, 0);
		pivot.y = obj->coords.y1 + (p->y - p1->y) + lv_obj_get_style_transform_pivot_y(obj, 0);
    }
    else
    {
	    pivot.x = obj->coords.x1 + lv_obj_get_style_transform_pivot_x(obj, 0);
	    pivot.y = obj->coords.y1 + lv_obj_get_style_transform_pivot_y(obj, 0);
    }
    if(inv) {
        angle = -angle;
        zoom = (256 * 256) / zoom;
    }

    lv_point_transform(p, angle, zoom, &pivot);
}
#else
static void transform_point(const lv_obj_t * obj, lv_point_t * p, bool inv)
{
    int16_t angle = lv_obj_get_style_transform_angle(obj, 0);
    int16_t zoom = lv_obj_get_style_transform_zoom(obj, 0);

    if(angle == 0 && zoom == LV_IMG_ZOOM_NONE) return;

    lv_point_t pivot;

    pivot.x = obj->coords.x1 + lv_obj_get_style_transform_pivot_x(obj, 0);
    pivot.y = obj->coords.y1 + lv_obj_get_style_transform_pivot_y(obj, 0);
    
    if(inv) {
        angle = -angle;
        zoom = (256 * 256) / zoom;
    }

    lv_point_transform(p, angle, zoom, &pivot);
}
#endif

I have sent a pull request. you can use it as a reference.
modify lv_obj_transform_point #3819

I’m checking the PR, thank you!