android/GlueIOIOPort: fix spurious errors after IOIO baud rate change
[xcsoar.git] / src / Renderer / ChartRenderer.cpp
blob22ae6421b39460d9ab1cce0e9826e323169296e6
1 /*
2 Copyright_License {
4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "ChartRenderer.hpp"
25 #include "Screen/Canvas.hpp"
26 #include "Screen/Layout.hpp"
27 #include "Language/Language.hpp"
28 #include "Asset.hpp"
29 #include "Math/LeastSquares.hpp"
30 #include "Util/StaticString.hpp"
32 #include <assert.h>
33 #include <stdio.h>
34 #include <windef.h> /* for MAX_PATH */
36 void
37 ChartRenderer::Axis::Reset()
39 unscaled = true;
40 scale = fixed(0);
41 min = fixed(0);
42 max = fixed(0);
45 PixelScalar
46 ChartRenderer::Axis::ToScreen(fixed value) const
48 return (long)((value - min) * scale);
51 void
52 ChartRenderer::ResetScale()
54 x.Reset();
55 y.Reset();
58 ChartRenderer::ChartRenderer(const ChartLook &_look, Canvas &the_canvas,
59 const PixelRect the_rc)
60 :look(_look), canvas(the_canvas), rc(the_rc),
61 padding_left(24), padding_bottom(19)
63 ResetScale();
66 void
67 ChartRenderer::ScaleYFromData(const LeastSquares &lsdata)
69 if (lsdata.IsEmpty())
70 return;
72 if (y.unscaled) {
73 y.min = lsdata.y_min;
74 y.max = lsdata.y_max;
75 y.unscaled = false;
76 } else {
77 y.min = std::min(y.min, lsdata.y_min);
78 y.max = std::max(y.max, lsdata.y_max);
81 if (lsdata.sum_n > 1) {
82 fixed y0, y1;
83 y0 = lsdata.x_min * lsdata.m + lsdata.b;
84 y1 = lsdata.x_max * lsdata.m + lsdata.b;
85 y.min = std::min({y.min, y0, y1});
86 y.max = std::max({y.max, y0, y1});
89 if (fabs(y.max - y.min) > fixed(50)) {
90 y.scale = (y.max - y.min);
91 if (positive(y.scale))
92 y.scale = fixed(rc.bottom - rc.top - padding_bottom) / y.scale;
93 } else {
94 y.scale = fixed(2000);
98 void
99 ChartRenderer::ScaleXFromData(const LeastSquares &lsdata)
101 if (lsdata.IsEmpty())
102 return;
104 if (x.unscaled) {
105 x.min = lsdata.x_min;
106 x.max = lsdata.x_max;
107 x.unscaled = false;
108 } else {
109 x.min = std::min(x.min, lsdata.x_min);
110 x.max = std::max(x.max, lsdata.x_max);
113 x.scale = (x.max - x.min);
114 if (positive(x.scale))
115 x.scale = fixed(rc.right - rc.left - padding_left) / x.scale;
118 void
119 ChartRenderer::ScaleYFromValue(const fixed value)
121 if (y.unscaled) {
122 y.min = value;
123 y.max = value;
124 y.unscaled = false;
125 } else {
126 y.min = std::min(value, y.min);
127 y.max = std::max(value, y.max);
130 y.scale = (y.max - y.min);
131 if (positive(y.scale))
132 y.scale = fixed(rc.bottom - rc.top - padding_bottom) / y.scale;
135 void
136 ChartRenderer::ScaleXFromValue(const fixed value)
138 if (x.unscaled) {
139 x.min = value;
140 x.max = value;
141 x.unscaled = false;
142 } else {
143 x.min = std::min(value, x.min);
144 x.max = std::max(value, x.max);
147 x.scale = (x.max - x.min);
148 if (positive(x.scale))
149 x.scale = fixed(rc.right - rc.left - padding_left) / x.scale;
152 void
153 ChartRenderer::DrawLabel(const TCHAR *text, const fixed xv, const fixed yv)
155 canvas.Select(look.label_font);
156 canvas.SetBackgroundTransparent();
158 PixelSize tsize = canvas.CalcTextSize(text);
159 RasterPoint pt = ToScreen(xv, yv);
160 canvas.DrawText(pt.x - tsize.cx / 2, pt.y - tsize.cy / 2, text);
163 void
164 ChartRenderer::DrawNoData()
166 canvas.Select(look.label_font);
167 canvas.SetBackgroundTransparent();
169 const TCHAR *text = _("No data");
170 PixelSize tsize = canvas.CalcTextSize(text);
172 PixelScalar x = (rc.left + rc.right - tsize.cx) / 2;
173 PixelScalar y = (rc.top + rc.bottom - tsize.cy) / 2;
175 canvas.DrawText(x, y, text);
178 void
179 ChartRenderer::DrawXLabel(const TCHAR *text)
181 canvas.Select(look.axis_label_font);
182 canvas.SetBackgroundTransparent();
184 PixelSize tsize = canvas.CalcTextSize(text);
185 PixelScalar x = rc.right - tsize.cx - Layout::Scale(3);
186 PixelScalar y = rc.bottom - tsize.cy;
188 canvas.DrawText(x, y, text);
191 void
192 ChartRenderer::DrawXLabel(const TCHAR *text, const TCHAR *unit)
194 assert(text != NULL);
195 assert(unit != NULL);
197 StaticString<64> buffer;
198 buffer.UnsafeFormat(_T("%s [%s]"), text, unit);
199 DrawXLabel(buffer);
202 void
203 ChartRenderer::DrawYLabel(const TCHAR *text)
205 canvas.Select(look.axis_label_font);
206 canvas.SetBackgroundTransparent();
208 PixelSize tsize = canvas.CalcTextSize(text);
209 PixelScalar x = std::max(PixelScalar(2), PixelScalar(rc.left - tsize.cx));
210 PixelScalar y = rc.top;
212 canvas.DrawText(x, y, text);
215 void
216 ChartRenderer::DrawYLabel(const TCHAR *text, const TCHAR *unit)
218 assert(text != NULL);
219 assert(unit != NULL);
221 StaticString<64> buffer;
222 buffer.UnsafeFormat(_T("%s [%s]"), text, unit);
223 DrawYLabel(buffer);
226 void
227 ChartRenderer::DrawTrend(const LeastSquares &lsdata, ChartLook::Style style)
229 if (lsdata.sum_n < 2)
230 return;
232 if (x.unscaled || y.unscaled)
233 return;
235 fixed xmin, xmax, ymin, ymax;
236 xmin = lsdata.x_min;
237 xmax = lsdata.x_max;
238 ymin = lsdata.x_min * lsdata.m + lsdata.b;
239 ymax = lsdata.x_max * lsdata.m + lsdata.b;
241 DrawLine(xmin, ymin, xmax, ymax, look.GetPen(style));
244 void
245 ChartRenderer::DrawTrendN(const LeastSquares &lsdata, ChartLook::Style style)
247 if (lsdata.sum_n < 2)
248 return;
250 if (x.unscaled || y.unscaled)
251 return;
253 fixed xmin, xmax, ymin, ymax;
254 xmin = fixed(0.5);
255 xmax = fixed(lsdata.sum_n) + fixed(0.5);
256 ymin = lsdata.x_min * lsdata.m + lsdata.b;
257 ymax = lsdata.x_max * lsdata.m + lsdata.b;
259 DrawLine(xmin, ymin, xmax, ymax, look.GetPen(style));
262 void
263 ChartRenderer::DrawLine(const fixed xmin, const fixed ymin,
264 const fixed xmax, const fixed ymax, const Pen &pen)
266 if (x.unscaled || y.unscaled)
267 return;
269 assert(pen.IsDefined());
270 canvas.Select(pen);
271 canvas.DrawLine(ToScreen(xmin, ymin), ToScreen(xmax, ymax));
274 void
275 ChartRenderer::DrawFilledLine(const fixed xmin, const fixed ymin,
276 const fixed xmax, const fixed ymax,
277 const Brush &brush)
279 RasterPoint line[4];
281 line[0] = ToScreen(xmin, ymin);
282 line[1] = ToScreen(xmax, ymax);
284 line[2].x = line[1].x;
285 line[2].y = ScreenY(fixed(0));
286 line[3].x = line[0].x;
287 line[3].y = line[2].y;
289 canvas.Select(brush);
290 canvas.SelectNullPen();
291 canvas.DrawTriangleFan(line, 4);
294 void
295 ChartRenderer::DrawLine(const fixed xmin, const fixed ymin,
296 const fixed xmax, const fixed ymax,
297 ChartLook::Style style)
299 DrawLine(xmin, ymin, xmax, ymax, look.GetPen(style));
302 void
303 ChartRenderer::DrawBarChart(const LeastSquares &lsdata)
305 if (x.unscaled || y.unscaled)
306 return;
308 canvas.Select(look.bar_brush);
309 canvas.SelectNullPen();
311 for (unsigned i = 0, n = lsdata.slots.size(); i != n; i++) {
312 PixelScalar xmin((fixed(i) + fixed(1.2)) * x.scale
313 + fixed(rc.left + padding_left));
314 PixelScalar ymin = ScreenY(y.min);
315 PixelScalar xmax((fixed(i) + fixed(1.8)) * x.scale
316 + fixed(rc.left + padding_left));
317 PixelScalar ymax = ScreenY(lsdata.slots[i].y);
318 canvas.Rectangle(xmin, ymin, xmax, ymax);
322 void
323 ChartRenderer::DrawFilledLineGraph(const LeastSquares &lsdata)
325 assert(lsdata.slots.size() >= 2);
327 const unsigned n = lsdata.slots.size() + 2;
328 RasterPoint *points = point_buffer.get(n);
330 RasterPoint *p = points;
331 for (auto i = lsdata.slots.begin(), end = lsdata.slots.end();
332 i != end; ++i)
333 *p++ = ToScreen(i->x, i->y);
334 const RasterPoint &last = p[-1];
335 *p++ = RasterPoint{ last.x, rc.bottom - padding_bottom };
336 *p++ = RasterPoint{ points[0].x, rc.bottom - padding_bottom };
338 assert(p == points + n);
340 canvas.DrawPolygon(points, n);
343 void
344 ChartRenderer::DrawLineGraph(const LeastSquares &lsdata, const Pen &pen)
346 assert(lsdata.slots.size() >= 2);
348 const unsigned n = lsdata.slots.size();
349 RasterPoint *points = point_buffer.get(n);
351 RasterPoint *p = points;
352 for (auto i = lsdata.slots.begin(), end = lsdata.slots.end();
353 i != end; ++i)
354 *p++ = ToScreen(i->x, i->y);
355 assert(p == points + n);
357 canvas.Select(pen);
358 canvas.DrawPolyline(points, n);
361 void
362 ChartRenderer::DrawLineGraph(const LeastSquares &lsdata,
363 ChartLook::Style style)
365 DrawLineGraph(lsdata, look.GetPen(style));
368 void
369 ChartRenderer::FormatTicText(TCHAR *text, const fixed val, const fixed step)
371 if (step < fixed(1)) {
372 _stprintf(text, _T("%.1f"), (double)val);
373 } else {
374 _stprintf(text, _T("%.0f"), (double)val);
378 void
379 ChartRenderer::DrawXGrid(const fixed tic_step, ChartLook::Style style,
380 const fixed unit_step, bool draw_units)
382 DrawXGrid(tic_step, look.GetPen(style), unit_step, draw_units);
385 void
386 ChartRenderer::DrawXGrid(fixed tic_step, const Pen &pen,
387 fixed unit_step, bool draw_units)
389 assert(positive(tic_step));
391 canvas.Select(pen);
392 canvas.Select(look.axis_value_font);
393 canvas.SetBackgroundTransparent();
395 RasterPoint line[2];
397 /** the minimum next position of the text, to avoid overlapping */
398 PixelScalar next_text = rc.left;
400 /* increase tic step so graph not too crowded */
401 while ((x.max-x.min)/tic_step > fixed(10)) {
402 tic_step *= fixed(2);
403 unit_step *= fixed(2);
405 // bool do_units = ((x.max-zero)/tic_step)<10;
407 line[0].y = rc.top;
408 line[1].y = rc.bottom - padding_bottom;
410 fixed start = (int)(x.min / tic_step) * tic_step;
412 for (fixed xval = start; xval <= x.max; xval += tic_step) {
413 const PixelScalar xmin = ScreenX(xval);
414 line[0].x = line[1].x = xmin;
416 // STYLE_THINDASHPAPER
417 if (xmin >= rc.left + padding_left && xmin <= rc.right) {
418 canvas.DrawLine(line[0], line[1]);
420 if (draw_units && xmin >= next_text) {
421 TCHAR unit_text[MAX_PATH];
422 FormatTicText(unit_text, xval * unit_step / tic_step, unit_step);
424 canvas.DrawText(xmin, rc.bottom - Layout::Scale(17), unit_text);
426 next_text = xmin + canvas.CalcTextSize(unit_text).cx + Layout::FastScale(2);
432 void
433 ChartRenderer::DrawYGrid(const fixed tic_step, ChartLook::Style style,
434 const fixed unit_step, bool draw_units)
436 DrawYGrid(tic_step, look.GetPen(style), unit_step, draw_units);
439 void
440 ChartRenderer::DrawYGrid(fixed tic_step, const Pen &pen,
441 fixed unit_step, bool draw_units)
443 assert(positive(tic_step));
445 canvas.Select(pen);
446 canvas.Select(look.axis_value_font);
447 canvas.SetBackgroundTransparent();
449 RasterPoint line[2];
451 /* increase tic step so graph not too crowded */
452 while ((y.max-y.min)/tic_step > fixed(10)) {
453 tic_step *= fixed(2);
454 unit_step *= fixed(2);
457 line[0].x = rc.left + padding_left;
458 line[1].x = rc.right;
460 fixed start = (int)(y.min / tic_step) * tic_step;
462 for (fixed yval = start; yval <= y.max; yval += tic_step) {
463 const PixelScalar ymin = ScreenY(yval);
464 line[0].y = line[1].y = ymin;
466 // STYLE_THINDASHPAPER
467 if (ymin >= rc.top && ymin <= rc.bottom - padding_bottom) {
468 canvas.DrawLine(line[0], line[1]);
470 if (draw_units) {
471 TCHAR unit_text[MAX_PATH];
472 FormatTicText(unit_text, yval * unit_step / tic_step, unit_step);
474 canvas.DrawText(rc.left + Layout::Scale(8), ymin, unit_text);
480 PixelScalar
481 ChartRenderer::ScreenX(fixed _x) const
483 return rc.left + padding_left + x.ToScreen(_x);
486 PixelScalar
487 ChartRenderer::ScreenY(fixed _y) const
489 return rc.bottom - padding_bottom - y.ToScreen(_y);
492 void
493 ChartRenderer::DrawFilledY(const std::vector<std::pair<fixed, fixed>> &vals,
494 const Brush &brush, const Pen* pen)
496 if (vals.size()<2)
497 return;
498 const unsigned fsize = vals.size()+2;
499 RasterPoint *line = point_buffer.get(fsize);
501 for (unsigned i = 0; i < vals.size(); ++i)
502 line[i + 2] = ToScreen(vals[i].first, vals[i].second);
504 line[0].x = rc.left + padding_left;
505 line[0].y = line[fsize-1].y;
506 line[1].x = rc.left + padding_left;
507 line[1].y = line[2].y;
509 canvas.Select(brush);
510 if (pen == NULL) {
511 canvas.SelectNullPen();
512 } else {
513 canvas.Select(*pen);
515 canvas.DrawPolygon(line, fsize);
518 void
519 ChartRenderer::DrawDot(const fixed x, const fixed y, const PixelScalar width)
521 RasterPoint p = ToScreen(x, y);
522 RasterPoint line[4] = { { p.x, p.y - width },
523 { p.x - width, p.y },
524 { p.x, p.y + width },
525 { p.x + width, p.y } };
526 canvas.SelectNullPen();
527 canvas.DrawTriangleFan(line, 4);