components/u8g2/csrc/u8g2_polygon.c

changeset 0
88d965579617
equal deleted inserted replaced
-1:000000000000 0:88d965579617
1 /*
2
3 u8g22_polygon.c
4
5 */
6
7
8 #include "u8g2.h"
9
10
11
12
13 /*===========================================*/
14 /* local definitions */
15
16 typedef int16_t pg_word_t;
17
18
19 struct pg_point_struct
20 {
21 pg_word_t x;
22 pg_word_t y;
23 };
24
25 typedef struct _pg_struct pg_struct; /* forward declaration */
26
27 struct pg_edge_struct
28 {
29 pg_word_t x_direction; /* 1, if x2 is greater than x1, -1 otherwise */
30 pg_word_t height;
31 pg_word_t current_x_offset;
32 pg_word_t error_offset;
33
34 /* --- line loop --- */
35 pg_word_t current_y;
36 pg_word_t max_y;
37 pg_word_t current_x;
38 pg_word_t error;
39
40 /* --- outer loop --- */
41 uint8_t (*next_idx_fn)(pg_struct *pg, uint8_t i);
42 uint8_t curr_idx;
43 };
44
45 /* maximum number of points in the polygon */
46 /* can be redefined, but highest possible value is 254 */
47 #define PG_MAX_POINTS 6
48
49 /* index numbers for the pge structures below */
50 #define PG_LEFT 0
51 #define PG_RIGHT 1
52
53
54 struct _pg_struct
55 {
56 struct pg_point_struct list[PG_MAX_POINTS];
57 uint8_t cnt;
58 uint8_t is_min_y_not_flat;
59 pg_word_t total_scan_line_cnt;
60 struct pg_edge_struct pge[2]; /* left and right line draw structures */
61 };
62
63
64 /*===========================================*/
65 /* procedures, which should not be inlined (save as much flash ROM as possible */
66
67 #define PG_NOINLINE U8G2_NOINLINE
68
69 static uint8_t pge_Next(struct pg_edge_struct *pge) PG_NOINLINE;
70 static uint8_t pg_inc(pg_struct *pg, uint8_t i) PG_NOINLINE;
71 static uint8_t pg_dec(pg_struct *pg, uint8_t i) PG_NOINLINE;
72 static void pg_expand_min_y(pg_struct *pg, pg_word_t min_y, uint8_t pge_idx) PG_NOINLINE;
73 static void pg_line_init(pg_struct * const pg, uint8_t pge_index) PG_NOINLINE;
74
75 /*===========================================*/
76 /* line draw algorithm */
77
78 static uint8_t pge_Next(struct pg_edge_struct *pge)
79 {
80 if ( pge->current_y >= pge->max_y )
81 return 0;
82
83 pge->current_x += pge->current_x_offset;
84 pge->error += pge->error_offset;
85 if ( pge->error > 0 )
86 {
87 pge->current_x += pge->x_direction;
88 pge->error -= pge->height;
89 }
90
91 pge->current_y++;
92 return 1;
93 }
94
95 /* assumes y2 > y1 */
96 static void pge_Init(struct pg_edge_struct *pge, pg_word_t x1, pg_word_t y1, pg_word_t x2, pg_word_t y2)
97 {
98 pg_word_t dx = x2 - x1;
99 pg_word_t width;
100
101 pge->height = y2 - y1;
102 pge->max_y = y2;
103 pge->current_y = y1;
104 pge->current_x = x1;
105
106 if ( dx >= 0 )
107 {
108 pge->x_direction = 1;
109 width = dx;
110 pge->error = 0;
111 }
112 else
113 {
114 pge->x_direction = -1;
115 width = -dx;
116 pge->error = 1 - pge->height;
117 }
118
119 pge->current_x_offset = dx / pge->height;
120 pge->error_offset = width % pge->height;
121 }
122
123 /*===========================================*/
124 /* convex polygon algorithm */
125
126 static uint8_t pg_inc(pg_struct *pg, uint8_t i)
127 {
128 i++;
129 if ( i >= pg->cnt )
130 i = 0;
131 return i;
132 }
133
134 static uint8_t pg_dec(pg_struct *pg, uint8_t i)
135 {
136 i--;
137 if ( i >= pg->cnt )
138 i = pg->cnt-1;
139 return i;
140 }
141
142 static void pg_expand_min_y(pg_struct *pg, pg_word_t min_y, uint8_t pge_idx)
143 {
144 uint8_t i = pg->pge[pge_idx].curr_idx;
145 for(;;)
146 {
147 i = pg->pge[pge_idx].next_idx_fn(pg, i);
148 if ( pg->list[i].y != min_y )
149 break;
150 pg->pge[pge_idx].curr_idx = i;
151 }
152 }
153
154 static uint8_t pg_prepare(pg_struct *pg)
155 {
156 pg_word_t max_y;
157 pg_word_t min_y;
158 uint8_t i;
159
160 /* setup the next index procedures */
161 pg->pge[PG_RIGHT].next_idx_fn = pg_inc;
162 pg->pge[PG_LEFT].next_idx_fn = pg_dec;
163
164 /* search for highest and lowest point */
165 max_y = pg->list[0].y;
166 min_y = pg->list[0].y;
167 pg->pge[PG_LEFT].curr_idx = 0;
168 for( i = 1; i < pg->cnt; i++ )
169 {
170 if ( max_y < pg->list[i].y )
171 {
172 max_y = pg->list[i].y;
173 }
174 if ( min_y > pg->list[i].y )
175 {
176 pg->pge[PG_LEFT].curr_idx = i;
177 min_y = pg->list[i].y;
178 }
179 }
180
181 /* calculate total number of scan lines */
182 pg->total_scan_line_cnt = max_y;
183 pg->total_scan_line_cnt -= min_y;
184
185 /* exit if polygon height is zero */
186 if ( pg->total_scan_line_cnt == 0 )
187 return 0;
188
189 /* if the minimum y side is flat, try to find the lowest and highest x points */
190 pg->pge[PG_RIGHT].curr_idx = pg->pge[PG_LEFT].curr_idx;
191 pg_expand_min_y(pg, min_y, PG_RIGHT);
192 pg_expand_min_y(pg, min_y, PG_LEFT);
193
194 /* check if the min side is really flat (depends on the x values) */
195 pg->is_min_y_not_flat = 1;
196 if ( pg->list[pg->pge[PG_LEFT].curr_idx].x != pg->list[pg->pge[PG_RIGHT].curr_idx].x )
197 {
198 pg->is_min_y_not_flat = 0;
199 }
200 else
201 {
202 pg->total_scan_line_cnt--;
203 if ( pg->total_scan_line_cnt == 0 )
204 return 0;
205 }
206
207 return 1;
208 }
209
210 static void pg_hline(pg_struct *pg, u8g2_t *u8g2)
211 {
212 pg_word_t x1, x2, y;
213 x1 = pg->pge[PG_LEFT].current_x;
214 x2 = pg->pge[PG_RIGHT].current_x;
215 y = pg->pge[PG_RIGHT].current_y;
216
217 if ( y < 0 )
218 return;
219 if ( y >= u8g2_GetDisplayHeight(u8g2) ) // does not work for 256x64 display???
220 return;
221 if ( x1 < x2 )
222 {
223 if ( x2 < 0 )
224 return;
225 if ( x1 >= u8g2_GetDisplayWidth(u8g2) )
226 return;
227 if ( x1 < 0 )
228 x1 = 0;
229 if ( x2 >= u8g2_GetDisplayWidth(u8g2) )
230 x2 = u8g2_GetDisplayWidth(u8g2);
231 u8g2_DrawHLine(u8g2, x1, y, x2 - x1);
232 }
233 else
234 {
235 if ( x1 < 0 )
236 return;
237 if ( x2 >= u8g2_GetDisplayWidth(u8g2) )
238 return;
239 if ( x2 < 0 )
240 x1 = 0;
241 if ( x1 >= u8g2_GetDisplayWidth(u8g2) )
242 x1 = u8g2_GetDisplayWidth(u8g2);
243 u8g2_DrawHLine(u8g2, x2, y, x1 - x2);
244 }
245 }
246
247 static void pg_line_init(pg_struct * const pg, uint8_t pge_index)
248 {
249 struct pg_edge_struct *pge = pg->pge+pge_index;
250 uint8_t idx;
251 pg_word_t x1;
252 pg_word_t y1;
253 pg_word_t x2;
254 pg_word_t y2;
255
256 idx = pge->curr_idx;
257 y1 = pg->list[idx].y;
258 x1 = pg->list[idx].x;
259 idx = pge->next_idx_fn(pg, idx);
260 y2 = pg->list[idx].y;
261 x2 = pg->list[idx].x;
262 pge->curr_idx = idx;
263
264 pge_Init(pge, x1, y1, x2, y2);
265 }
266
267 static void pg_exec(pg_struct *pg, u8g2_t *u8g2)
268 {
269 pg_word_t i = pg->total_scan_line_cnt;
270
271 /* first line is skipped if the min y line is not flat */
272 pg_line_init(pg, PG_LEFT);
273 pg_line_init(pg, PG_RIGHT);
274
275 if ( pg->is_min_y_not_flat != 0 )
276 {
277 pge_Next(&(pg->pge[PG_LEFT]));
278 pge_Next(&(pg->pge[PG_RIGHT]));
279 }
280
281 do
282 {
283 pg_hline(pg, u8g2);
284 while ( pge_Next(&(pg->pge[PG_LEFT])) == 0 )
285 {
286 pg_line_init(pg, PG_LEFT);
287 }
288 while ( pge_Next(&(pg->pge[PG_RIGHT])) == 0 )
289 {
290 pg_line_init(pg, PG_RIGHT);
291 }
292 i--;
293 } while( i > 0 );
294 }
295
296 /*===========================================*/
297 /* API procedures */
298
299 static void pg_ClearPolygonXY(pg_struct *pg)
300 {
301 pg->cnt = 0;
302 }
303
304 static void pg_AddPolygonXY(pg_struct *pg, int16_t x, int16_t y)
305 {
306 if ( pg->cnt < PG_MAX_POINTS )
307 {
308 pg->list[pg->cnt].x = x;
309 pg->list[pg->cnt].y = y;
310 pg->cnt++;
311 }
312 }
313
314 static void pg_DrawPolygon(pg_struct *pg, u8g2_t *u8g2)
315 {
316 if ( pg_prepare(pg) == 0 )
317 return;
318 pg_exec(pg, u8g2);
319 }
320
321 pg_struct u8g2_pg;
322
323 void u8g2_ClearPolygonXY(void)
324 {
325 pg_ClearPolygonXY(&u8g2_pg);
326 }
327
328 void u8g2_AddPolygonXY(U8X8_UNUSED u8g2_t *u8g2, int16_t x, int16_t y)
329 {
330 pg_AddPolygonXY(&u8g2_pg, x, y);
331 }
332
333 void u8g2_DrawPolygon(u8g2_t *u8g2)
334 {
335 pg_DrawPolygon(&u8g2_pg, u8g2);
336 }
337
338 void u8g2_DrawTriangle(u8g2_t *u8g2, int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2)
339 {
340 u8g2_ClearPolygonXY();
341 u8g2_AddPolygonXY(u8g2, x0, y0);
342 u8g2_AddPolygonXY(u8g2, x1, y1);
343 u8g2_AddPolygonXY(u8g2, x2, y2);
344 u8g2_DrawPolygon(u8g2);
345 }
346

mercurial