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