Curved text on Circular watch display

Hi team

I am working with lvgl 7.11 and I am trying to create curved text to display on watch face can you please send me a sample code snippet as soon as possible. Find the following attachment for reference.

Thanks in advance.

You would probably have to use an image; there is no native support for rotating label text in this manner.

(Technically, you could do it by using a canvas and drawing each letter one at a time with rotation, but I think this would be too slow and complex.)

(cc @kisvegabor)

I agree with @embeddedt that using a canvas or multiple smaller canvases would be a good direction.

Thanks for the reply i will check and will inform. :slightly_smiling_face:

1 Like

Is this still the recommended way in LVGL 8.3?

This is not that hard to do. You need to use circle math to do it.

Here is an example written in Python that can be ported to C.

def point_on_circle(degree, center_x, center_y, radius):
    radians = math.radians(degree)
    x = center_x + (radius * math.cos(radians))
    y = center_y + (radius * math.sin(radians))
    return int(x), int(y)

def remap(value, old_min, old_max, new_min, new_max):
    return (
        ((value - old_min) * (new_max - new_min)) / (old_max - old_min)
    ) + new_min



class ArcLabel(object):

    def __init__(
        self,
        parent,
        start_angle,
        stop_angle,
        text,
        font,
        radius,
        center_x,
        center_y
    ):
        angle_range = stop_angle - start_angle
        self.text_len = len(text)
        self.start_angle = start_angle
        self.stop_angle = stop_angle
        self.angle_range = angle_range

        self.labels = []
        self.shadows = []

        angle_step = float(angle_range) / (len(text) + 1)
        angle = start_angle + angle_step

        text = list(text)

        if 90 < start_angle < 180 or 270 < start_angle < 360:
            text.reverse()

        for char in text:

            t_angle = 2700 - (3600 - int(round(angle, 1) * 10))

            if 180 < start_angle and 360 > stop_angle:
                t_angle += 1800

            shadow = lv.label(parent)
            shadow.set_style_text_font(font, lv.PART.MAIN)
            shadow.set_text(char)
            shadow.set_style_text_color(lv.color_hex(0x4B4B4B), lv.PART.MAIN)
            shadow.set_size(lv.SIZE_CONTENT, lv.SIZE_CONTENT)

            if 180 < start_angle and 360 > stop_angle:
                l_height = shadow.get_self_height()
                x, y = point_on_circle(
                    angle, center_x, center_y, radius + l_height
                )
            else:
                x, y = point_on_circle(angle, center_x, center_y, radius)

            shaadow_x = x
            shadow_y = y

            ang = int(round(angle))

            if ang > 360:
                ang -= 360

            if ang in (0, 360):
                shaadow_x += 2
            elif ang == 90:
                shadow_y += 1
            elif ang == 180:
                shaadow_x -= 2
            elif ang == 270:
                shadow_y -= 1
            elif ang < 90:
                shaadow_x += 2
                shadow_y += 1
            elif ang < 180:
                shaadow_x -= 2
                shadow_y += 1
            elif ang < 270:
                shaadow_x -= 2
                shadow_y -= 1
            else:
                shaadow_x += 2
                shadow_y -= 1

            shadow.set_x(shaadow_x)
            shadow.set_y(shadow_y)
            shadow.set_style_transform_angle(t_angle, 0)

            label = lv.label(parent)
            label.set_style_text_font(font, lv.PART.MAIN)
            label.set_text(char)
            label.set_style_text_color(lv.color_hex(0xC8C8C8), lv.PART.MAIN)
            label.set_size(lv.SIZE_CONTENT, lv.SIZE_CONTENT)
            label.set_style_transform_angle(t_angle, 0)
            label.set_x(x)
            label.set_y(y)

            self.shadows.append(shadow)
            self.labels.append(label)

            angle += angle_step

    def set_color_range(self, color1, color2, value, start_angle, stop_angle):
        if self.stop_angle < 360:
            angle = remap(value, 0, 100, start_angle, stop_angle)
        else:
            angle = remap(value, 0, 100, stop_angle, start_angle)
            color1, color2 = color2, color1

        steps = self.angle_range / (self.text_len + 1)
        for i, label in enumerate(self.labels):

            if self.start_angle + (steps * (i + 1)) < angle:
                label.set_style_text_color(color1, 0)
            else:
                label.set_style_text_color(color2, 0)

    def set_style_text_color(self, color, part):
        for label in self.labels:
            label.set_style_text_color(color, part)

    def set_style_shadow_color(self, color, part):
        for shadow in self.shadows:
            shadow.set_style_text_color(color, part)

    def set_style_shadow_opa(self, opa, part):
        for shadow in self.shadows:
            shadow.set_style_shadow_opa(opa, part)

    def clear_flag(self, flag):
        for i in range(self.text_len):
            self.labels[i].clear_flag(flag)
            self.shadows[i].clear_flag(flag)

    def get_text(self):
        text = ''

        for label in self.labels:
            text += label.get_text()

        if 90 < self.start_angle < 180 or 270 < self.start_angle < 360:
            text = list(text)
            text.reverse()
            text = ''.join(text)

        return text.strip()

    def set_text(self, value):
        if len(value) > self.text_len:
            raise RuntimeError

        remainder = self.text_len - len(value)

        value = (' ' * int(remainder / 2)) + value

        remainder += remainder % 2
        value += ' ' * int(remainder / 2)

        value = list(value)
        if 90 < self.start_angle < 180 or 270 < self.start_angle < 360:
            value.reverse()

        for i, char in enumerate(value):
            self.labels[i].set_text(char)
            self.shadows[i].set_text(char)

This allows for creating a series of arced letters from a string of text. The text is able to be reduced in length but not increased so make sure to initially create it with the longest string first thing. This also creates a shadow. You will have to adjust the rotation angle of the text for it to appear the way you want, not that hard to do.

This is also written for version 9.0 so there are as few changes that would need to be made to work with older versions of LVGL.

This code produces text like this.

Thanks! I’ll take a look and try to get it working on 8.3 which I’m on.