µGFX  2.9
version 2.9
gwin_graph.c
Go to the documentation of this file.
1 /*
2  * This file is subject to the terms of the GFX License. If a copy of
3  * the license was not distributed with this file, you can obtain one at:
4  *
5  * http://ugfx.io/license.html
6  */
7 
8 /**
9  * @file src/gwin/gwin_graph.c
10  * @brief GWIN sub-system button code
11  */
12 
13 #include "../../gfx.h"
14 
15 #if GFX_USE_GWIN && GWIN_NEED_GRAPH
16 
17 #include "gwin_class.h"
18 
19 #define GGRAPH_FLG_CONNECTPOINTS (GWIN_FIRST_CONTROL_FLAG<<0)
20 #define GGRAPH_ARROW_SIZE 5
21 
22 static const GGraphStyle GGraphDefaultStyle = {
23  { GGRAPH_POINT_DOT, 0, GFX_WHITE }, // point
24  { GGRAPH_LINE_DOT, 2, GFX_GRAY }, // line
25  { GGRAPH_LINE_SOLID, 0, GFX_WHITE }, // x axis
26  { GGRAPH_LINE_SOLID, 0, GFX_WHITE }, // y axis
27  { GGRAPH_LINE_NONE, 0, GFX_WHITE, 0 }, // x grid
28  { GGRAPH_LINE_NONE, 0, GFX_WHITE, 0 }, // y grid
29  GWIN_GRAPH_STYLE_XAXIS_ARROWS|GWIN_GRAPH_STYLE_YAXIS_ARROWS // flags
30 };
31 
32 static const gwinVMT graphVMT = {
33  "Graph", // The classname
34  sizeof(GGraphObject), // The object size
35  0, // The destroy routine
36  0, // The redraw routine
37  0, // The after-clear routine
38 };
39 
40 static void pointto(GGraphObject *gg, gCoord x, gCoord y, const GGraphPointStyle *style) {
41  if (style->type == GGRAPH_POINT_NONE)
42  return;
43 
44  // Convert to device space. Note the y-axis is inverted.
45  x += gg->g.x + gg->xorigin;
46  y = gg->g.y + gg->g.height - 1 - gg->yorigin - y;
47 
48  if (style->size <= 1) {
49  gdispGDrawPixel(gg->g.display, x, y, style->color);
50  return;
51  }
52 
53  switch(style->type) {
54  case GGRAPH_POINT_SQUARE:
55  gdispGDrawBox(gg->g.display, x-style->size, y-style->size, 2*style->size, 2*style->size, style->color);
56  break;
57 #if GDISP_NEED_CIRCLE
58  case GGRAPH_POINT_CIRCLE:
59  gdispGDrawCircle(gg->g.display, x, y, style->size, style->color);
60  break;
61 #endif
62  case GGRAPH_POINT_DOT:
63  default:
64  gdispGDrawPixel(gg->g.display, x, y, style->color);
65  break;
66  }
67 }
68 
69 static void lineto(GGraphObject *gg, gCoord x0, gCoord y0, gCoord x1, gCoord y1, const GGraphLineStyle *style) {
70  gCoord dy, dx;
71  gCoord addx, addy;
72  gCoord P, diff, i;
73  gCoord run_on, run_off, run;
74 
75  if (style->type == GGRAPH_LINE_NONE)
76  return;
77 
78  // Convert to device space. Note the y-axis is inverted.
79  x0 += gg->g.x + gg->xorigin;
80  y0 = gg->g.y + gg->g.height - 1 - gg->yorigin - y0;
81  x1 += gg->g.x + gg->xorigin;
82  y1 = gg->g.y + gg->g.height - 1 - gg->yorigin - y1;
83 
84  if (style->size <= 0) {
85  // Use the driver to draw a solid line
86  gdispGDrawLine(gg->g.display, x0, y0, x1, y1, style->color);
87  return;
88  }
89 
90  switch (style->type) {
91  case GGRAPH_LINE_DOT:
92  run_on = 1;
93  run_off = -style->size;
94  break;
95 
96  case GGRAPH_LINE_DASH:
97  run_on = style->size;
98  run_off = -style->size;
99  break;
100 
101  case GGRAPH_LINE_SOLID:
102  default:
103  // Use the driver to draw a solid line
104  gdispGDrawLine(gg->g.display, x0, y0, x1, y1, style->color);
105  return;
106  }
107 
108  // Use Bresenham's algorithm modified to draw a stylized line
109  run = 0;
110  if (x1 >= x0) {
111  dx = x1 - x0;
112  addx = 1;
113  } else {
114  dx = x0 - x1;
115  addx = -1;
116  }
117  if (y1 >= y0) {
118  dy = y1 - y0;
119  addy = 1;
120  } else {
121  dy = y0 - y1;
122  addy = -1;
123  }
124 
125  if (dx >= dy) {
126  dy *= 2;
127  P = dy - dx;
128  diff = P - dx;
129 
130  for(i=0; i<=dx; ++i) {
131  if (run++ >= 0) {
132  if (run >= run_on)
133  run = run_off;
134  gdispGDrawPixel(gg->g.display, x0, y0, style->color);
135  }
136  if (P < 0) {
137  P += dy;
138  x0 += addx;
139  } else {
140  P += diff;
141  x0 += addx;
142  y0 += addy;
143  }
144  }
145  } else {
146  dx *= 2;
147  P = dx - dy;
148  diff = P - dy;
149 
150  for(i=0; i<=dy; ++i) {
151  if (run++ >= 0) {
152  if (run >= run_on)
153  run = run_off;
154  gdispGDrawPixel(gg->g.display, x0, y0, style->color);
155  }
156  if (P < 0) {
157  P += dx;
158  y0 += addy;
159  } else {
160  P += diff;
161  x0 += addx;
162  y0 += addy;
163  }
164  }
165  }
166 }
167 
168 GHandle gwinGGraphCreate(GDisplay *g, GGraphObject *gg, const GWindowInit *pInit) {
169  if (!(gg = (GGraphObject *)_gwindowCreate(g, &gg->g, pInit, &graphVMT, 0)))
170  return 0;
171  gg->xorigin = gg->yorigin = 0;
172  gg->lastx = gg->lasty = 0;
173  gwinGraphSetStyle((GHandle)gg, &GGraphDefaultStyle);
174  gwinSetVisible((GHandle)gg, pInit->show);
175  _gwinFlushRedraws(REDRAW_WAIT);
176  return (GHandle)gg;
177 }
178 
179 void gwinGraphSetStyle(GHandle gh, const GGraphStyle *pstyle) {
180  #define gg ((GGraphObject *)gh)
181 
182  if (gh->vmt != &graphVMT)
183  return;
184 
185  gg->style.point = pstyle->point;
186  gg->style.line = pstyle->line;
187  gg->style.xaxis = pstyle->xaxis;
188  gg->style.yaxis = pstyle->yaxis;
189  gg->style.xgrid = pstyle->xgrid;
190  gg->style.ygrid = pstyle->ygrid;
191  gg->style.flags = pstyle->flags;
192 
193  #undef gg
194 }
195 
196 void gwinGraphSetOrigin(GHandle gh, gCoord x, gCoord y) {
197  #define gg ((GGraphObject *)gh)
198 
199  if (gh->vmt != &graphVMT)
200  return;
201 
202  gg->xorigin = x;
203  gg->yorigin = y;
204 
205  #undef gg
206 }
207 
208 void gwinGraphDrawAxis(GHandle gh) {
209  #define gg ((GGraphObject *)gh)
210  gCoord i, xmin, ymin, xmax, ymax;
211 
212  if (gh->vmt != &graphVMT || !_gwinDrawStart(gh))
213  return;
214 
215  xmin = -gg->xorigin;
216  xmax = gh->width-gg->xorigin-1;
217  ymin = -gg->yorigin;
218  ymax = gh->height-gg->yorigin-1;
219 
220  // x grid - this code assumes that the GGraphGridStyle is a superset of GGraphListStyle
221  if (gg->style.xgrid.type != GGRAPH_LINE_NONE && gg->style.xgrid.spacing >= 2) {
222  for(i = gg->style.xgrid.spacing; i <= xmax; i += gg->style.xgrid.spacing)
223  lineto(gg, i, ymin, i, ymax, (GGraphLineStyle *)&gg->style.xgrid);
224  for(i = -gg->style.xgrid.spacing; i >= xmin; i -= gg->style.xgrid.spacing)
225  lineto(gg, i, ymin, i, ymax, (GGraphLineStyle *)&gg->style.xgrid);
226  }
227 
228  // y grid - this code assumes that the GGraphGridStyle is a superset of GGraphListStyle
229  if (gg->style.ygrid.type != GGRAPH_LINE_NONE && gg->style.ygrid.spacing >= 2) {
230  for(i = gg->style.ygrid.spacing; i <= ymax; i += gg->style.ygrid.spacing)
231  lineto(gg, xmin, i, xmax, i, (GGraphLineStyle *)&gg->style.ygrid);
232  for(i = -gg->style.ygrid.spacing; i >= ymin; i -= gg->style.ygrid.spacing)
233  lineto(gg, xmin, i, xmax, i, (GGraphLineStyle *)&gg->style.ygrid);
234  }
235 
236  // x axis
237  lineto(gg, xmin, 0, xmax, 0, &gg->style.xaxis);
238  if ((gg->style.flags & GWIN_GRAPH_STYLE_XAXIS_NEGATIVE_ARROWS)) {
239  if (xmin > 0 || xmin < -(GGRAPH_ARROW_SIZE+1)) {
240  lineto(gg, xmin, 0, xmin+GGRAPH_ARROW_SIZE, GGRAPH_ARROW_SIZE, &gg->style.xaxis);
241  lineto(gg, xmin, 0, xmin+GGRAPH_ARROW_SIZE, -GGRAPH_ARROW_SIZE, &gg->style.xaxis);
242  }
243  }
244  if ((gg->style.flags & GWIN_GRAPH_STYLE_XAXIS_POSITIVE_ARROWS)) {
245  if (xmax < 0 || xmax > (GGRAPH_ARROW_SIZE+1)) {
246  lineto(gg, xmax, 0, xmax-GGRAPH_ARROW_SIZE, GGRAPH_ARROW_SIZE, &gg->style.xaxis);
247  lineto(gg, xmax, 0, xmax-GGRAPH_ARROW_SIZE, -GGRAPH_ARROW_SIZE, &gg->style.xaxis);
248  }
249  }
250 
251  // y axis
252  lineto(gg, 0, ymin, 0, ymax, &gg->style.yaxis);
253  if ((gg->style.flags & GWIN_GRAPH_STYLE_YAXIS_NEGATIVE_ARROWS)) {
254  if (ymin > 0 || ymin < -(GGRAPH_ARROW_SIZE+1)) {
255  lineto(gg, 0, ymin, GGRAPH_ARROW_SIZE, ymin+GGRAPH_ARROW_SIZE, &gg->style.yaxis);
256  lineto(gg, 0, ymin, -GGRAPH_ARROW_SIZE, ymin+GGRAPH_ARROW_SIZE, &gg->style.yaxis);
257  }
258  }
259  if ((gg->style.flags & GWIN_GRAPH_STYLE_YAXIS_POSITIVE_ARROWS)) {
260  if (ymax < 0 || ymax > (GGRAPH_ARROW_SIZE+1)) {
261  lineto(gg, 0, ymax, GGRAPH_ARROW_SIZE, ymax-GGRAPH_ARROW_SIZE, &gg->style.yaxis);
262  lineto(gg, 0, ymax, -GGRAPH_ARROW_SIZE, ymax-GGRAPH_ARROW_SIZE, &gg->style.yaxis);
263  }
264  }
265 
266  _gwinDrawEnd(gh);
267  #undef gg
268 }
269 
270 void gwinGraphStartSet(GHandle gh) {
271  if (gh->vmt != &graphVMT)
272  return;
273 
274  gh->flags &= ~GGRAPH_FLG_CONNECTPOINTS;
275 }
276 
277 void gwinGraphDrawPoint(GHandle gh, gCoord x, gCoord y) {
278  #define gg ((GGraphObject *)gh)
279 
280  if (gh->vmt != &graphVMT || !_gwinDrawStart(gh))
281  return;
282 
283  if ((gh->flags & GGRAPH_FLG_CONNECTPOINTS)) {
284  // Draw the line
285  lineto(gg, gg->lastx, gg->lasty, x, y, &gg->style.line);
286 
287  // Redraw the previous point because the line may have overwritten it
288  pointto(gg, gg->lastx, gg->lasty, &gg->style.point);
289 
290  } else
291  gh->flags |= GGRAPH_FLG_CONNECTPOINTS;
292 
293  // Save this point for next time.
294  gg->lastx = x;
295  gg->lasty = y;
296 
297  // Draw this point.
298  pointto(gg, x, y, &gg->style.point);
299 
300  _gwinDrawEnd(gh);
301  #undef gg
302 }
303 
304 void gwinGraphDrawPoints(GHandle gh, const gPoint *points, unsigned count) {
305  #define gg ((GGraphObject *)gh)
306  unsigned i;
307  const gPoint *p;
308 
309  if (gh->vmt != &graphVMT || !_gwinDrawStart(gh))
310  return;
311 
312  // Draw the connecting lines
313  for(p = points, i = 0; i < count; p++, i++) {
314  if ((gh->flags & GGRAPH_FLG_CONNECTPOINTS)) {
315  // Draw the line
316  lineto(gg, gg->lastx, gg->lasty, p->x, p->y, &gg->style.line);
317 
318  // Redraw the previous point because the line may have overwritten it
319  if (i == 0)
320  pointto(gg, gg->lastx, gg->lasty, &gg->style.point);
321 
322  } else
323  gh->flags |= GGRAPH_FLG_CONNECTPOINTS;
324 
325  // Save this point for next time.
326  gg->lastx = p->x;
327  gg->lasty = p->y;
328  }
329 
330 
331  // Draw the points.
332  for(p = points, i = 0; i < count; p++, i++)
333  pointto(gg, p->x, p->y, &gg->style.point);
334 
335  _gwinDrawEnd(gh);
336  #undef gg
337 }
338 
339 #endif /* GFX_USE_GWIN && GWIN_NEED_GRAPH */
void gdispGDrawPixel(GDisplay *g, gCoord x, gCoord y, gColor color)
Set a pixel in the specified color.
void gdispGDrawCircle(GDisplay *g, gCoord x, gCoord y, gCoord radius, gColor color)
Draw a circle.
void gdispGDrawLine(GDisplay *g, gCoord x0, gCoord y0, gCoord x1, gCoord y1, gColor color)
Draw a line.
gI16 gCoord
The type for a coordinate or length on the screen.
Definition: gdisp.h:39
void gdispGDrawBox(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, gColor color)
Draw a rectangular box.
void gwinGraphDrawPoint(GHandle gh, gCoord x, gCoord y)
Draw a graph point.
void gwinGraphStartSet(GHandle gh)
Start a new set of graphing data.
void gwinGraphDrawPoints(GHandle gh, const gPoint *points, unsigned count)
Draw multiple graph points.
void gwinGraphDrawAxis(GHandle gh)
Draw the axis and the background grid.
GHandle gwinGGraphCreate(GDisplay *g, GGraphObject *gg, const GWindowInit *pInit)
Create a graph window.
void gwinGraphSetStyle(GHandle gh, const GGraphStyle *pstyle)
Set the style of the graphing operations.
void gwinGraphSetOrigin(GHandle gh, gCoord x, gCoord y)
Set the origin for graphing operations.
void gwinSetVisible(GHandle gh, gBool visible)
Sets whether a window is visible or not.
The structure to initialise a GWIN.
Definition: gwin.h:75
gBool show
Definition: gwin.h:80
A window object structure.
Definition: gwin.h:40
gCoord width
Definition: gwin.h:49
const struct gwinVMT * vmt
Definition: gwin.h:45
gU32 flags
Definition: gwin.h:53
gCoord height
Definition: gwin.h:50
Type for a 2D point on the screen.
Definition: gdisp.h:51
gCoord y
Definition: gdisp.h:53
gCoord x
Definition: gdisp.h:52
The Virtual Method Table for a GWIN window.
Definition: gwin_class.h:55