The official µGFX library repository.

gwin_graph.c 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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.org/license.html
  6. */
  7. /**
  8. * @file src/gwin/gwin_graph.c
  9. * @brief GWIN sub-system button code
  10. */
  11. #include "../../gfx.h"
  12. #if GFX_USE_GWIN && GWIN_NEED_GRAPH
  13. #include "gwin_class.h"
  14. #define GGRAPH_FLG_CONNECTPOINTS (GWIN_FIRST_CONTROL_FLAG<<0)
  15. #define GGRAPH_ARROW_SIZE 5
  16. static const GGraphStyle GGraphDefaultStyle = {
  17. { GGRAPH_POINT_DOT, 0, White }, // point
  18. { GGRAPH_LINE_DOT, 2, Gray }, // line
  19. { GGRAPH_LINE_SOLID, 0, White }, // x axis
  20. { GGRAPH_LINE_SOLID, 0, White }, // y axis
  21. { GGRAPH_LINE_NONE, 0, White, 0 }, // x grid
  22. { GGRAPH_LINE_NONE, 0, White, 0 }, // y grid
  23. GWIN_GRAPH_STYLE_XAXIS_ARROWS|GWIN_GRAPH_STYLE_YAXIS_ARROWS // flags
  24. };
  25. static const gwinVMT graphVMT = {
  26. "Graph", // The classname
  27. sizeof(GGraphObject), // The object size
  28. 0, // The destroy routine
  29. 0, // The redraw routine
  30. 0, // The after-clear routine
  31. };
  32. static void pointto(GGraphObject *gg, coord_t x, coord_t y, const GGraphPointStyle *style) {
  33. if (style->type == GGRAPH_POINT_NONE)
  34. return;
  35. // Convert to device space. Note the y-axis is inverted.
  36. x += gg->g.x + gg->xorigin;
  37. y = gg->g.y + gg->g.height - 1 - gg->yorigin - y;
  38. if (style->size <= 1) {
  39. gdispGDrawPixel(gg->g.display, x, y, style->color);
  40. return;
  41. }
  42. switch(style->type) {
  43. case GGRAPH_POINT_SQUARE:
  44. gdispGDrawBox(gg->g.display, x-style->size, y-style->size, 2*style->size, 2*style->size, style->color);
  45. break;
  46. #if GDISP_NEED_CIRCLE
  47. case GGRAPH_POINT_CIRCLE:
  48. gdispGDrawCircle(gg->g.display, x, y, style->size, style->color);
  49. break;
  50. #endif
  51. case GGRAPH_POINT_DOT:
  52. default:
  53. gdispGDrawPixel(gg->g.display, x, y, style->color);
  54. break;
  55. }
  56. }
  57. static void lineto(GGraphObject *gg, coord_t x0, coord_t y0, coord_t x1, coord_t y1, const GGraphLineStyle *style) {
  58. coord_t dy, dx;
  59. coord_t addx, addy;
  60. coord_t P, diff, i;
  61. coord_t run_on, run_off, run;
  62. if (style->type == GGRAPH_LINE_NONE)
  63. return;
  64. // Convert to device space. Note the y-axis is inverted.
  65. x0 += gg->g.x + gg->xorigin;
  66. y0 = gg->g.y + gg->g.height - 1 - gg->yorigin - y0;
  67. x1 += gg->g.x + gg->xorigin;
  68. y1 = gg->g.y + gg->g.height - 1 - gg->yorigin - y1;
  69. if (style->size <= 0) {
  70. // Use the driver to draw a solid line
  71. gdispGDrawLine(gg->g.display, x0, y0, x1, y1, style->color);
  72. return;
  73. }
  74. switch (style->type) {
  75. case GGRAPH_LINE_DOT:
  76. run_on = 1;
  77. run_off = -style->size;
  78. break;
  79. case GGRAPH_LINE_DASH:
  80. run_on = style->size;
  81. run_off = -style->size;
  82. break;
  83. case GGRAPH_LINE_SOLID:
  84. default:
  85. // Use the driver to draw a solid line
  86. gdispGDrawLine(gg->g.display, x0, y0, x1, y1, style->color);
  87. return;
  88. }
  89. // Use Bresenham's algorithm modified to draw a stylized line
  90. run = 0;
  91. if (x1 >= x0) {
  92. dx = x1 - x0;
  93. addx = 1;
  94. } else {
  95. dx = x0 - x1;
  96. addx = -1;
  97. }
  98. if (y1 >= y0) {
  99. dy = y1 - y0;
  100. addy = 1;
  101. } else {
  102. dy = y0 - y1;
  103. addy = -1;
  104. }
  105. if (dx >= dy) {
  106. dy *= 2;
  107. P = dy - dx;
  108. diff = P - dx;
  109. for(i=0; i<=dx; ++i) {
  110. if (run++ >= 0) {
  111. if (run >= run_on)
  112. run = run_off;
  113. gdispGDrawPixel(gg->g.display, x0, y0, style->color);
  114. }
  115. if (P < 0) {
  116. P += dy;
  117. x0 += addx;
  118. } else {
  119. P += diff;
  120. x0 += addx;
  121. y0 += addy;
  122. }
  123. }
  124. } else {
  125. dx *= 2;
  126. P = dx - dy;
  127. diff = P - dy;
  128. for(i=0; i<=dy; ++i) {
  129. if (run++ >= 0) {
  130. if (run >= run_on)
  131. run = run_off;
  132. gdispGDrawPixel(gg->g.display, x0, y0, style->color);
  133. }
  134. if (P < 0) {
  135. P += dx;
  136. y0 += addy;
  137. } else {
  138. P += diff;
  139. x0 += addx;
  140. y0 += addy;
  141. }
  142. }
  143. }
  144. }
  145. GHandle gwinGGraphCreate(GDisplay *g, GGraphObject *gg, const GWindowInit *pInit) {
  146. if (!(gg = (GGraphObject *)_gwindowCreate(g, &gg->g, pInit, &graphVMT, 0)))
  147. return 0;
  148. gg->xorigin = gg->yorigin = 0;
  149. gg->lastx = gg->lasty = 0;
  150. gwinGraphSetStyle((GHandle)gg, &GGraphDefaultStyle);
  151. gwinSetVisible((GHandle)gg, pInit->show);
  152. _gwinFlushRedraws(REDRAW_WAIT);
  153. return (GHandle)gg;
  154. }
  155. void gwinGraphSetStyle(GHandle gh, const GGraphStyle *pstyle) {
  156. #define gg ((GGraphObject *)gh)
  157. if (gh->vmt != &graphVMT)
  158. return;
  159. gg->style.point = pstyle->point;
  160. gg->style.line = pstyle->line;
  161. gg->style.xaxis = pstyle->xaxis;
  162. gg->style.yaxis = pstyle->yaxis;
  163. gg->style.xgrid = pstyle->xgrid;
  164. gg->style.ygrid = pstyle->ygrid;
  165. gg->style.flags = pstyle->flags;
  166. #undef gg
  167. }
  168. void gwinGraphSetOrigin(GHandle gh, coord_t x, coord_t y) {
  169. #define gg ((GGraphObject *)gh)
  170. if (gh->vmt != &graphVMT)
  171. return;
  172. gg->xorigin = x;
  173. gg->yorigin = y;
  174. #undef gg
  175. }
  176. void gwinGraphDrawAxis(GHandle gh) {
  177. #define gg ((GGraphObject *)gh)
  178. coord_t i, xmin, ymin, xmax, ymax;
  179. if (gh->vmt != &graphVMT || !_gwinDrawStart(gh))
  180. return;
  181. xmin = -gg->xorigin;
  182. xmax = gh->width-gg->xorigin-1;
  183. ymin = -gg->yorigin;
  184. ymax = gh->height-gg->yorigin-1;
  185. // x grid - this code assumes that the GGraphGridStyle is a superset of GGraphListStyle
  186. if (gg->style.xgrid.type != GGRAPH_LINE_NONE && gg->style.xgrid.spacing >= 2) {
  187. for(i = gg->style.xgrid.spacing; i <= xmax; i += gg->style.xgrid.spacing)
  188. lineto(gg, i, ymin, i, ymax, (GGraphLineStyle *)&gg->style.xgrid);
  189. for(i = -gg->style.xgrid.spacing; i >= xmin; i -= gg->style.xgrid.spacing)
  190. lineto(gg, i, ymin, i, ymax, (GGraphLineStyle *)&gg->style.xgrid);
  191. }
  192. // y grid - this code assumes that the GGraphGridStyle is a superset of GGraphListStyle
  193. if (gg->style.ygrid.type != GGRAPH_LINE_NONE && gg->style.ygrid.spacing >= 2) {
  194. for(i = gg->style.ygrid.spacing; i <= ymax; i += gg->style.ygrid.spacing)
  195. lineto(gg, xmin, i, xmax, i, (GGraphLineStyle *)&gg->style.ygrid);
  196. for(i = -gg->style.ygrid.spacing; i >= ymin; i -= gg->style.ygrid.spacing)
  197. lineto(gg, xmin, i, xmax, i, (GGraphLineStyle *)&gg->style.ygrid);
  198. }
  199. // x axis
  200. lineto(gg, xmin, 0, xmax, 0, &gg->style.xaxis);
  201. if ((gg->style.flags & GWIN_GRAPH_STYLE_XAXIS_NEGATIVE_ARROWS)) {
  202. if (xmin > 0 || xmin < -(GGRAPH_ARROW_SIZE+1)) {
  203. lineto(gg, xmin, 0, xmin+GGRAPH_ARROW_SIZE, GGRAPH_ARROW_SIZE, &gg->style.xaxis);
  204. lineto(gg, xmin, 0, xmin+GGRAPH_ARROW_SIZE, -GGRAPH_ARROW_SIZE, &gg->style.xaxis);
  205. }
  206. }
  207. if ((gg->style.flags & GWIN_GRAPH_STYLE_XAXIS_POSITIVE_ARROWS)) {
  208. if (xmax < 0 || xmax > (GGRAPH_ARROW_SIZE+1)) {
  209. lineto(gg, xmax, 0, xmax-GGRAPH_ARROW_SIZE, GGRAPH_ARROW_SIZE, &gg->style.xaxis);
  210. lineto(gg, xmax, 0, xmax-GGRAPH_ARROW_SIZE, -GGRAPH_ARROW_SIZE, &gg->style.xaxis);
  211. }
  212. }
  213. // y axis
  214. lineto(gg, 0, ymin, 0, ymax, &gg->style.yaxis);
  215. if ((gg->style.flags & GWIN_GRAPH_STYLE_YAXIS_NEGATIVE_ARROWS)) {
  216. if (ymin > 0 || ymin < -(GGRAPH_ARROW_SIZE+1)) {
  217. lineto(gg, 0, ymin, GGRAPH_ARROW_SIZE, ymin+GGRAPH_ARROW_SIZE, &gg->style.yaxis);
  218. lineto(gg, 0, ymin, -GGRAPH_ARROW_SIZE, ymin+GGRAPH_ARROW_SIZE, &gg->style.yaxis);
  219. }
  220. }
  221. if ((gg->style.flags & GWIN_GRAPH_STYLE_YAXIS_POSITIVE_ARROWS)) {
  222. if (ymax < 0 || ymax > (GGRAPH_ARROW_SIZE+1)) {
  223. lineto(gg, 0, ymax, GGRAPH_ARROW_SIZE, ymax-GGRAPH_ARROW_SIZE, &gg->style.yaxis);
  224. lineto(gg, 0, ymax, -GGRAPH_ARROW_SIZE, ymax-GGRAPH_ARROW_SIZE, &gg->style.yaxis);
  225. }
  226. }
  227. _gwinDrawEnd(gh);
  228. #undef gg
  229. }
  230. void gwinGraphStartSet(GHandle gh) {
  231. if (gh->vmt != &graphVMT)
  232. return;
  233. gh->flags &= ~GGRAPH_FLG_CONNECTPOINTS;
  234. }
  235. void gwinGraphDrawPoint(GHandle gh, coord_t x, coord_t y) {
  236. #define gg ((GGraphObject *)gh)
  237. if (gh->vmt != &graphVMT || !_gwinDrawStart(gh))
  238. return;
  239. if ((gh->flags & GGRAPH_FLG_CONNECTPOINTS)) {
  240. // Draw the line
  241. lineto(gg, gg->lastx, gg->lasty, x, y, &gg->style.line);
  242. // Redraw the previous point because the line may have overwritten it
  243. pointto(gg, gg->lastx, gg->lasty, &gg->style.point);
  244. } else
  245. gh->flags |= GGRAPH_FLG_CONNECTPOINTS;
  246. // Save this point for next time.
  247. gg->lastx = x;
  248. gg->lasty = y;
  249. // Draw this point.
  250. pointto(gg, x, y, &gg->style.point);
  251. _gwinDrawEnd(gh);
  252. #undef gg
  253. }
  254. void gwinGraphDrawPoints(GHandle gh, const point *points, unsigned count) {
  255. #define gg ((GGraphObject *)gh)
  256. unsigned i;
  257. const point *p;
  258. if (gh->vmt != &graphVMT || !_gwinDrawStart(gh))
  259. return;
  260. // Draw the connecting lines
  261. for(p = points, i = 0; i < count; p++, i++) {
  262. if ((gh->flags & GGRAPH_FLG_CONNECTPOINTS)) {
  263. // Draw the line
  264. lineto(gg, gg->lastx, gg->lasty, p->x, p->y, &gg->style.line);
  265. // Redraw the previous point because the line may have overwritten it
  266. if (i == 0)
  267. pointto(gg, gg->lastx, gg->lasty, &gg->style.point);
  268. } else
  269. gh->flags |= GGRAPH_FLG_CONNECTPOINTS;
  270. // Save this point for next time.
  271. gg->lastx = p->x;
  272. gg->lasty = p->y;
  273. }
  274. // Draw the points.
  275. for(p = points, i = 0; i < count; p++, i++)
  276. pointto(gg, p->x, p->y, &gg->style.point);
  277. _gwinDrawEnd(gh);
  278. #undef gg
  279. }
  280. #endif /* GFX_USE_GWIN && GWIN_NEED_GRAPH */