Krolyxon
Commit 3120783

Branches

Tags

Refs →

orion-rf.piolibdepsesp32-s3-n16r8U8g2srcclibu8g2_polygon.c

346 lines · 7.2 KB · C

1/*
2
3 u8g22_polygon.c
4
5*/
6
7
8#include "u8g2.h"
9
10
11
12
13/*===========================================*/
14/* local definitions */
15
16typedef int16_t pg_word_t;
17
18
19struct pg_point_struct
20{
21 pg_word_t x;
22 pg_word_t y;
23};
24
25typedef struct _pg_struct pg_struct; /* forward declaration */
26
27struct 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
54struct _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
69static uint8_t pge_Next(struct pg_edge_struct *pge) PG_NOINLINE;
70static uint8_t pg_inc(pg_struct *pg, uint8_t i) PG_NOINLINE;
71static uint8_t pg_dec(pg_struct *pg, uint8_t i) PG_NOINLINE;
72static void pg_expand_min_y(pg_struct *pg, pg_word_t min_y, uint8_t pge_idx) PG_NOINLINE;
73static void pg_line_init(pg_struct * const pg, uint8_t pge_index) PG_NOINLINE;
74
75/*===========================================*/
76/* line draw algorithm */
77
78static 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 */
96static 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
126static 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
134static 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
142static 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
154static 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
210static 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 >= (pg_word_t)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 >= (pg_word_t)u8g2_GetDisplayWidth(u8g2) )
226 return;
227 if ( x1 < 0 )
228 x1 = 0;
229 if ( x2 >= (pg_word_t)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 >= (pg_word_t)u8g2_GetDisplayWidth(u8g2) )
238 return;
239 if ( x2 < 0 )
240 x1 = 0;
241 if ( x1 >= (pg_word_t)u8g2_GetDisplayWidth(u8g2) )
242 x1 = u8g2_GetDisplayWidth(u8g2);
243 u8g2_DrawHLine(u8g2, x2, y, x1 - x2);
244 }
245}
246
247static 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
267static 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
299static void pg_ClearPolygonXY(pg_struct *pg)
300{
301 pg->cnt = 0;
302}
303
304static 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
314static 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
321pg_struct u8g2_pg;
322
323void u8g2_ClearPolygonXY(void)
324{
325 pg_ClearPolygonXY(&u8g2_pg);
326}
327
328void u8g2_AddPolygonXY(U8X8_UNUSED u8g2_t *u8g2, int16_t x, int16_t y)
329{
330 pg_AddPolygonXY(&u8g2_pg, x, y);
331}
332
333void u8g2_DrawPolygon(u8g2_t *u8g2)
334{
335 pg_DrawPolygon(&u8g2_pg, u8g2);
336}
337
338void 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