1 /**
2  * @file lv_line.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_line.h"
10 
11 #if LV_USE_LINE != 0
12 #include "../misc/lv_assert.h"
13 #include "../draw/lv_draw.h"
14 #include "../misc/lv_math.h"
15 #include <stdbool.h>
16 #include <stdint.h>
17 #include <string.h>
18 
19 /*********************
20  *      DEFINES
21  *********************/
22 #define MY_CLASS &lv_line_class
23 
24 /**********************
25  *      TYPEDEFS
26  **********************/
27 
28 /**********************
29  *  STATIC PROTOTYPES
30  **********************/
31 static void lv_line_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
32 static void lv_line_event(const lv_obj_class_t * class_p, lv_event_t * e);
33 
34 /**********************
35  *  STATIC VARIABLES
36  **********************/
37 const lv_obj_class_t lv_line_class = {
38     .constructor_cb = lv_line_constructor,
39     .event_cb = lv_line_event,
40     .width_def = LV_SIZE_CONTENT,
41     .height_def = LV_SIZE_CONTENT,
42     .instance_size = sizeof(lv_line_t),
43     .base_class = &lv_obj_class
44 };
45 
46 /**********************
47  *      MACROS
48  **********************/
49 
50 /**********************
51  *   GLOBAL FUNCTIONS
52  **********************/
53 
lv_line_create(lv_obj_t * parent)54 lv_obj_t * lv_line_create(lv_obj_t * parent)
55 {
56     LV_LOG_INFO("begin");
57     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
58     lv_obj_class_init_obj(obj);
59     return obj;
60 }
61 
62 /*=====================
63  * Setter functions
64  *====================*/
65 
lv_line_set_points(lv_obj_t * obj,const lv_point_t points[],uint16_t point_num)66 void lv_line_set_points(lv_obj_t * obj, const lv_point_t points[], uint16_t point_num)
67 {
68     LV_ASSERT_OBJ(obj, MY_CLASS);
69 
70     lv_line_t * line = (lv_line_t *)obj;
71     line->point_array    = points;
72     line->point_num      = point_num;
73 
74     lv_obj_refresh_self_size(obj);
75 
76     lv_obj_invalidate(obj);
77 }
78 
lv_line_set_y_invert(lv_obj_t * obj,bool en)79 void lv_line_set_y_invert(lv_obj_t * obj, bool en)
80 {
81     LV_ASSERT_OBJ(obj, MY_CLASS);
82 
83     lv_line_t * line = (lv_line_t *)obj;
84     if(line->y_inv == en) return;
85 
86     line->y_inv = en == false ? 0 : 1;
87 
88     lv_obj_invalidate(obj);
89 }
90 
91 /*=====================
92  * Getter functions
93  *====================*/
94 
lv_line_get_y_invert(const lv_obj_t * obj)95 bool lv_line_get_y_invert(const lv_obj_t * obj)
96 {
97     LV_ASSERT_OBJ(obj, MY_CLASS);
98 
99     lv_line_t * line = (lv_line_t *)obj;
100 
101     return line->y_inv == 0 ? false : true;
102 }
103 
104 /**********************
105  *   STATIC FUNCTIONS
106  **********************/
107 
lv_line_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)108 static void lv_line_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
109 {
110     LV_UNUSED(class_p);
111     LV_TRACE_OBJ_CREATE("begin");
112 
113     lv_line_t * line = (lv_line_t *)obj;
114 
115     line->point_num   = 0;
116     line->point_array = NULL;
117     line->y_inv       = 0;
118 
119     lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE);
120 
121     LV_TRACE_OBJ_CREATE("finished");
122 }
123 
lv_line_event(const lv_obj_class_t * class_p,lv_event_t * e)124 static void lv_line_event(const lv_obj_class_t * class_p, lv_event_t * e)
125 {
126     LV_UNUSED(class_p);
127 
128     lv_res_t res;
129 
130     /*Call the ancestor's event handler*/
131     res = lv_obj_event_base(MY_CLASS, e);
132     if(res != LV_RES_OK) return;
133 
134     lv_event_code_t code = lv_event_get_code(e);
135     lv_obj_t * obj = lv_event_get_target(e);
136 
137     if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
138         /*The corner of the skew lines is out of the intended area*/
139         lv_coord_t line_width = lv_obj_get_style_line_width(obj, LV_PART_MAIN);
140         lv_coord_t * s = lv_event_get_param(e);
141         if(*s < line_width) *s = line_width;
142     }
143     else if(code == LV_EVENT_GET_SELF_SIZE) {
144         lv_line_t * line = (lv_line_t *)obj;
145 
146         lv_point_t * p = lv_event_get_param(e);
147         lv_coord_t w = 0;
148         lv_coord_t h = 0;
149         if(line->point_num > 0) {
150             uint16_t i;
151             for(i = 0; i < line->point_num; i++) {
152                 w = LV_MAX(line->point_array[i].x, w);
153                 h = LV_MAX(line->point_array[i].y, h);
154             }
155 
156             lv_coord_t line_width = lv_obj_get_style_line_width(obj, LV_PART_MAIN);
157             w += line_width;
158             h += line_width;
159             p->x = w;
160             p->y = h;
161         }
162     }
163     else if(code == LV_EVENT_DRAW_MAIN) {
164         lv_line_t * line = (lv_line_t *)obj;
165         lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
166 
167         if(line->point_num == 0 || line->point_array == NULL) return;
168 
169         lv_area_t area;
170         lv_obj_get_coords(obj, &area);
171         lv_coord_t x_ofs = area.x1 - lv_obj_get_scroll_x(obj);
172         lv_coord_t y_ofs = area.y1 - lv_obj_get_scroll_y(obj);
173         lv_point_t p1;
174         lv_point_t p2;
175         lv_coord_t h = lv_obj_get_height(obj);
176         uint16_t i;
177 
178         lv_draw_line_dsc_t line_dsc;
179         lv_draw_line_dsc_init(&line_dsc);
180         lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &line_dsc);
181 
182         /*Read all points and draw the lines*/
183         for(i = 0; i < line->point_num - 1; i++) {
184 
185             p1.x = line->point_array[i].x + x_ofs;
186             p2.x = line->point_array[i + 1].x + x_ofs;
187 
188             if(line->y_inv == 0) {
189                 p1.y = line->point_array[i].y + y_ofs;
190                 p2.y = line->point_array[i + 1].y + y_ofs;
191             }
192             else {
193                 p1.y = h - line->point_array[i].y + y_ofs;
194                 p2.y = h - line->point_array[i + 1].y + y_ofs;
195             }
196             lv_draw_line(draw_ctx, &line_dsc, &p1, &p2);
197             line_dsc.round_start = 0;   /*Draw the rounding only on the end points after the first line*/
198         }
199     }
200 }
201 #endif
202