1 /**
2 * @file lv_line.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_line_private.h"
10 #include "../../core/lv_obj_class_private.h"
11
12
13 #if LV_USE_LINE != 0
14 #include "../../misc/lv_assert.h"
15 #include "../../misc/lv_math.h"
16 #include "../../misc/lv_types.h"
17 #include "../../draw/lv_draw.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 line_set_points(lv_obj_t * obj, const lv_point_precise_t points[], uint32_t point_num, bool mut);
33 static void lv_line_event(const lv_obj_class_t * class_p, lv_event_t * e);
34
35 /**********************
36 * STATIC VARIABLES
37 **********************/
38 const lv_obj_class_t lv_line_class = {
39 .constructor_cb = lv_line_constructor,
40 .event_cb = lv_line_event,
41 .width_def = LV_SIZE_CONTENT,
42 .height_def = LV_SIZE_CONTENT,
43 .instance_size = sizeof(lv_line_t),
44 .base_class = &lv_obj_class,
45 .name = "line",
46 };
47
48 /**********************
49 * MACROS
50 **********************/
51
52 /**********************
53 * GLOBAL FUNCTIONS
54 **********************/
55
lv_line_create(lv_obj_t * parent)56 lv_obj_t * lv_line_create(lv_obj_t * parent)
57 {
58 LV_LOG_INFO("begin");
59 lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
60 lv_obj_class_init_obj(obj);
61 return obj;
62 }
63
64 /*=====================
65 * Setter functions
66 *====================*/
67
lv_line_set_points(lv_obj_t * obj,const lv_point_precise_t points[],uint32_t point_num)68 void lv_line_set_points(lv_obj_t * obj, const lv_point_precise_t points[], uint32_t point_num)
69 {
70 line_set_points(obj, points, point_num, false);
71 }
72
lv_line_set_points_mutable(lv_obj_t * obj,lv_point_precise_t points[],uint32_t point_num)73 void lv_line_set_points_mutable(lv_obj_t * obj, lv_point_precise_t points[], uint32_t point_num)
74 {
75 line_set_points(obj, points, point_num, true);
76 }
77
lv_line_set_y_invert(lv_obj_t * obj,bool en)78 void lv_line_set_y_invert(lv_obj_t * obj, bool en)
79 {
80 LV_ASSERT_OBJ(obj, MY_CLASS);
81
82 lv_line_t * line = (lv_line_t *)obj;
83 if(line->y_inv == en) return;
84
85 line->y_inv = en ? 1U : 0U;
86
87 lv_obj_invalidate(obj);
88 }
89
90 /*=====================
91 * Getter functions
92 *====================*/
93
lv_line_get_points(lv_obj_t * obj)94 const lv_point_precise_t * lv_line_get_points(lv_obj_t * obj)
95 {
96 LV_ASSERT_OBJ(obj, MY_CLASS);
97
98 lv_line_t * line = (lv_line_t *)obj;
99 return line->point_array.constant;
100 }
101
lv_line_get_point_count(lv_obj_t * obj)102 uint32_t lv_line_get_point_count(lv_obj_t * obj)
103 {
104 LV_ASSERT_OBJ(obj, MY_CLASS);
105
106 lv_line_t * line = (lv_line_t *)obj;
107 return line->point_num;
108 }
109
lv_line_is_point_array_mutable(lv_obj_t * obj)110 bool lv_line_is_point_array_mutable(lv_obj_t * obj)
111 {
112 LV_ASSERT_OBJ(obj, MY_CLASS);
113
114 lv_line_t * line = (lv_line_t *)obj;
115 return line->point_array_is_mutable;
116 }
117
lv_line_get_points_mutable(lv_obj_t * obj)118 lv_point_precise_t * lv_line_get_points_mutable(lv_obj_t * obj)
119 {
120 LV_ASSERT_OBJ(obj, MY_CLASS);
121
122 lv_line_t * line = (lv_line_t *)obj;
123 if(!line->point_array_is_mutable) {
124 LV_LOG_WARN("the line point array is not mutable");
125 return NULL;
126 }
127 return line->point_array.mut;
128 }
129
lv_line_get_y_invert(const lv_obj_t * obj)130 bool lv_line_get_y_invert(const lv_obj_t * obj)
131 {
132 LV_ASSERT_OBJ(obj, MY_CLASS);
133
134 lv_line_t * line = (lv_line_t *)obj;
135
136 return line->y_inv == 1U;
137 }
138
139 /**********************
140 * STATIC FUNCTIONS
141 **********************/
142
lv_line_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)143 static void lv_line_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
144 {
145 LV_UNUSED(class_p);
146 LV_TRACE_OBJ_CREATE("begin");
147
148 lv_line_t * line = (lv_line_t *)obj;
149
150 line->point_num = 0;
151 line->point_array.constant = NULL;
152 line->y_inv = 0;
153 line->point_array_is_mutable = 0;
154
155 lv_obj_remove_flag(obj, LV_OBJ_FLAG_CLICKABLE);
156
157 LV_TRACE_OBJ_CREATE("finished");
158 }
159
line_set_points(lv_obj_t * obj,const lv_point_precise_t points[],uint32_t point_num,bool mut)160 static void line_set_points(lv_obj_t * obj, const lv_point_precise_t points[], uint32_t point_num, bool mut)
161 {
162 LV_ASSERT_OBJ(obj, MY_CLASS);
163
164 lv_line_t * line = (lv_line_t *)obj;
165 line->point_array.constant = points;
166 line->point_num = point_num;
167 line->point_array_is_mutable = mut;
168
169 lv_obj_refresh_self_size(obj);
170
171 lv_obj_invalidate(obj);
172 }
173
resolve_point_coord(lv_value_precise_t coord,int32_t max)174 static inline lv_value_precise_t resolve_point_coord(lv_value_precise_t coord, int32_t max)
175 {
176 if(LV_COORD_IS_PCT((int32_t)coord)) {
177 return LV_CLAMP(0, max * LV_COORD_GET_PCT((int32_t)coord) / 100, max);
178 }
179 else {
180 return coord;
181 }
182 }
183
lv_line_event(const lv_obj_class_t * class_p,lv_event_t * e)184 static void lv_line_event(const lv_obj_class_t * class_p, lv_event_t * e)
185 {
186 LV_UNUSED(class_p);
187
188 lv_result_t res;
189
190 /*Call the ancestor's event handler*/
191 res = lv_obj_event_base(MY_CLASS, e);
192 if(res != LV_RESULT_OK) return;
193
194 lv_event_code_t code = lv_event_get_code(e);
195 lv_obj_t * obj = lv_event_get_current_target(e);
196
197 if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
198 /*The corner of the skew lines is out of the intended area*/
199 int32_t line_width = lv_obj_get_style_line_width(obj, LV_PART_MAIN);
200 int32_t * s = lv_event_get_param(e);
201 if(*s < line_width) *s = line_width;
202 }
203 else if(code == LV_EVENT_GET_SELF_SIZE) {
204 lv_line_t * line = (lv_line_t *)obj;
205
206 if(line->point_num == 0 || line->point_array.constant == NULL) return;
207
208 lv_point_t * p = lv_event_get_param(e);
209 int32_t w = 0;
210 int32_t h = 0;
211
212 uint32_t i;
213 for(i = 0; i < line->point_num; i++) {
214 if(!LV_COORD_IS_PCT((int32_t)line->point_array.constant[i].x)) {
215 w = (int32_t)LV_MAX(line->point_array.constant[i].x, w);
216 }
217
218 if(!LV_COORD_IS_PCT((int32_t)line->point_array.constant[i].y)) {
219 h = (int32_t)LV_MAX(line->point_array.constant[i].y, h);
220 }
221 }
222
223 p->x = w;
224 p->y = h;
225 }
226 else if(code == LV_EVENT_DRAW_MAIN) {
227 lv_line_t * line = (lv_line_t *)obj;
228 lv_layer_t * layer = lv_event_get_layer(e);
229
230 if(line->point_num == 0 || line->point_array.constant == NULL) return;
231
232 lv_area_t area;
233 lv_obj_get_coords(obj, &area);
234 int32_t x_ofs = area.x1 - lv_obj_get_scroll_x(obj);
235 int32_t y_ofs = area.y1 - lv_obj_get_scroll_y(obj);
236
237 lv_draw_line_dsc_t line_dsc;
238 lv_draw_line_dsc_init(&line_dsc);
239 line_dsc.base.layer = layer;
240 lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &line_dsc);
241
242 /*Read all points and draw the lines*/
243 uint32_t i;
244 for(i = 0; i < line->point_num - 1; i++) {
245 int32_t w = lv_obj_get_width(obj);
246 int32_t h = lv_obj_get_height(obj);
247
248 line_dsc.p1.x = resolve_point_coord(line->point_array.constant[i].x, w) + x_ofs;
249 line_dsc.p1.y = resolve_point_coord(line->point_array.constant[i].y, h);
250
251 line_dsc.p2.x = resolve_point_coord(line->point_array.constant[i + 1].x, w) + x_ofs;
252 line_dsc.p2.y = resolve_point_coord(line->point_array.constant[i + 1].y, h);
253
254 if(line->y_inv == 0) {
255 line_dsc.p1.y = line_dsc.p1.y + y_ofs;
256 line_dsc.p2.y = line_dsc.p2.y + y_ofs;
257 }
258 else {
259 line_dsc.p1.y = h - line_dsc.p1.y + y_ofs;
260 line_dsc.p2.y = h - line_dsc.p2.y + y_ofs;
261 }
262
263 lv_draw_line(layer, &line_dsc);
264 line_dsc.round_start = 0; /*Draw the rounding only on the end points after the first line*/
265 }
266 }
267 }
268 #endif
269