Numworks Epsilon  1.4.1
Graphing Calculator Operating System
curve_view.cpp
Go to the documentation of this file.
1 #include "curve_view.h"
2 #include "../constant.h"
3 #include <assert.h>
4 #include <string.h>
5 #include <cmath>
6 #include <float.h>
7 
8 using namespace Poincare;
9 
10 namespace Shared {
11 
12 CurveView::CurveView(CurveViewRange * curveViewRange, CurveViewCursor * curveViewCursor, BannerView * bannerView,
13  View * cursorView, View * okView) :
14  View(),
15  m_bannerView(bannerView),
16  m_curveViewCursor(curveViewCursor),
17  m_curveViewRange(curveViewRange),
18  m_cursorView(cursorView),
19  m_okView(okView),
20  m_mainViewSelected(false),
21  m_drawnRangeVersion(0)
22 {
23 }
24 
26  uint32_t rangeVersion = m_curveViewRange->rangeChecksum();
27  if (m_drawnRangeVersion != rangeVersion) {
28  // FIXME: This should also be called if the *curve* changed
29  m_drawnRangeVersion = rangeVersion;
30  KDCoordinate bannerHeight = m_bannerView != nullptr ? m_bannerView->bounds().height() : 0;
31  markRectAsDirty(KDRect(0, 0, bounds().width(), bounds().height() - bannerHeight));
32  if (label(Axis::Horizontal, 0) != nullptr) {
34  }
35  if (label(Axis::Vertical, 0) != nullptr) {
37  }
38  }
39  layoutSubviews();
40 }
41 
43  return m_mainViewSelected;
44 }
45 
46 void CurveView::selectMainView(bool mainViewSelected) {
47  if (m_mainViewSelected != mainViewSelected) {
48  m_mainViewSelected = mainViewSelected;
49  reload();
50  }
51 }
52 
54  m_curveViewRange = curveViewRange;
55 }
56 
57 /* When setting cursor, banner or ok view we first dirty the former element
58  * frame (in case we set the new element to be nullptr or the new element frame
59  * does not recover the former element frame) and then we dirty the new element
60  * frame (most of the time it is automatically done by the layout but the frame
61  * might be identical to the previous one and in that case layoutSubviews will
62  * do nothing). */
63 
64 void CurveView::setCursorView(View * cursorView) {
65  markRectAsDirty(cursorFrame());
66  m_cursorView = cursorView;
67  markRectAsDirty(cursorFrame());
68  layoutSubviews();
69 }
70 
71 void CurveView::setBannerView(View * bannerView) {
72  markRectAsDirty(bannerFrame());
73  m_bannerView = bannerView;
74  layoutSubviews();
75 }
76 
77 void CurveView::setOkView(View * okView) {
78  markRectAsDirty(okFrame());
79  m_okView = okView;
80  layoutSubviews();
81 }
82 
83 float CurveView::resolution() const {
84  return bounds().width()*samplingRatio();
85 }
86 
87 float CurveView::samplingRatio() const {
88  return 1.1f;
89 }
90 
91 float CurveView::min(Axis axis) const {
92  assert(axis == Axis::Horizontal || axis == Axis::Vertical);
93  return (axis == Axis::Horizontal ? m_curveViewRange->xMin(): m_curveViewRange->yMin());
94 }
95 
96 float CurveView::max(Axis axis) const {
97  assert(axis == Axis::Horizontal || axis == Axis::Vertical);
98  return (axis == Axis::Horizontal ? m_curveViewRange->xMax() : m_curveViewRange->yMax());
99 }
100 
101 float CurveView::gridUnit(Axis axis) const {
102  return (axis == Axis::Horizontal ? m_curveViewRange->xGridUnit() : m_curveViewRange->yGridUnit());
103 }
104 
105 KDCoordinate CurveView::pixelLength(Axis axis) const {
106  assert(axis == Axis::Horizontal || axis == Axis::Vertical);
107  return (axis == Axis::Horizontal ? m_frame.width() : m_frame.height());
108 }
109 
111  KDCoordinate pixels = axis == Axis::Horizontal ? p : pixelLength(axis)-p;
112  return min(axis) + pixels*((max(axis)-min(axis))/pixelLength(axis));
113 }
114 
115 float CurveView::floatToPixel(Axis axis, float f) const {
116  float fraction = (f-min(axis))/(max(axis)-min(axis));
117  fraction = axis == Axis::Horizontal ? fraction : 1.0f - fraction;
118  /* Fraction is a float that translates the relative position of f on the axis.
119  * When fraction is between 0 and 1, f is visible. Otherwise, f is out of the
120  * visible window. We need to clip fraction to avoid big float issue (often
121  * due to float to int transformation). However, we cannot clip fraction
122  * between 0 and 1 because drawing a sized stamp on the extern boarder of the
123  * window should still be visible. We thus arbitrarily clip fraction between
124  * -10 and 10. */
125  fraction = fraction < -10.0f ? -10.0f : fraction;
126  fraction = fraction > 10.0f ? 10.0f : fraction;
127  return pixelLength(axis)*fraction;
128 }
129 
131  char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits)];
132  float step = gridUnit(axis);
133  for (int index = 0; index < numberOfLabels(axis); index++) {
134  float labelValue = 2.0f*step*(std::ceil(min(axis)/(2.0f*step)))+index*2.0f*step;
135  if (labelValue < step && labelValue > -step) {
136  labelValue = 0.0f;
137  }
138  PrintFloat::convertFloatToText<float>(labelValue, buffer,
139  PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits),
140  Constant::ShortNumberOfSignificantDigits, PrintFloat::Mode::Decimal);
141  //TODO: check for size of label?
142  strlcpy(label(axis, index), buffer, strlen(buffer)+1);
143  }
144 }
145 
146 void CurveView::drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin) const {
147  float step = gridUnit(axis);
148  float start = 2.0f*step*(std::ceil(min(axis)/(2.0f*step)));
149  float end = max(axis);
150  int i = 0;
151  for (float x = start; x < end; x += 2.0f*step) {
152  /* When |start| >> step, start + step = start. In that case, quit the
153  * infinite loop. */
154  if (x == x-step || x == x+step) {
155  return;
156  }
157  KDSize textSize = KDText::stringSize(label(axis, i), KDText::FontSize::Small);
160  if (axis == Axis::Vertical) {
163  }
164  if (-step < x && x < step && shiftOrigin) {
166  }
167  if (rect.intersects(KDRect(origin, KDText::stringSize(label(axis, i), KDText::FontSize::Small)))) {
168  ctx->blendString(label(axis, i), origin, KDText::FontSize::Small, KDColorBlack);
169  }
170  ctx->fillRect(graduation, KDColorBlack);
171  i++;
172  }
173 }
174 
175 void CurveView::drawLine(KDContext * ctx, KDRect rect, Axis axis, float coordinate, KDColor color, KDCoordinate thickness) const {
176  KDRect lineRect = KDRectZero;
177  switch(axis) {
178  case Axis::Horizontal:
179  lineRect = KDRect(
180  rect.x(), std::round(floatToPixel(Axis::Vertical, coordinate)),
181  rect.width(), thickness
182  );
183  break;
184  case Axis::Vertical:
185  lineRect = KDRect(
186  std::round(floatToPixel(Axis::Horizontal, coordinate)), rect.y(),
187  thickness, rect.height()
188  );
189  break;
190  }
191  if (rect.intersects(lineRect)) {
192  ctx->fillRect(lineRect, color);
193  }
194 }
195 
196 void CurveView::drawSegment(KDContext * ctx, KDRect rect, Axis axis, float coordinate, float lowerBound, float upperBound, KDColor color, KDCoordinate thickness) const {
197  KDRect lineRect = KDRectZero;
198  switch(axis) {
199  case Axis::Horizontal:
200  lineRect = KDRect(
202  std::round(floatToPixel(Axis::Horizontal, upperBound) - floatToPixel(Axis::Horizontal, lowerBound)), thickness
203  );
204  break;
205  case Axis::Vertical:
206  lineRect = KDRect(
208  thickness, std::round(floatToPixel(Axis::Vertical, lowerBound) - floatToPixel(Axis::Vertical, upperBound))
209  );
210  break;
211  }
212  if (rect.intersects(lineRect)) {
213  ctx->fillRect(lineRect, color);
214  }
215 }
216 
217 constexpr KDCoordinate dotDiameter = 5;
219  {0xE1, 0x45, 0x0C, 0x45, 0xE1},
220  {0x45, 0x00, 0x00, 0x00, 0x45},
221  {0x00, 0x00, 0x00, 0x00, 0x00},
222  {0x45, 0x00, 0x00, 0x00, 0x45},
223  {0xE1, 0x45, 0x0C, 0x45, 0xE1},
224 };
225 
228  {0xE1, 0x45, 0x0C, 0x00, 0x0C, 0x45, 0xE1},
229  {0x45, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x45},
230  {0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C},
231  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
232  {0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C},
233  {0x45, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x45},
234  {0xE1, 0x45, 0x0C, 0x00, 0x0C, 0x45, 0xE1},
235 
236 };
237 
240 
241 void CurveView::drawDot(KDContext * ctx, KDRect rect, float x, float y, KDColor color, bool oversize) const {
244  if ((px + dotDiameter < rect.left() - k_externRectMargin || px - dotDiameter > rect.right() + k_externRectMargin) ||
245  (py + dotDiameter < rect.top() - k_externRectMargin || py - dotDiameter > rect.bottom() + k_externRectMargin)) {
246  return;
247  }
248  KDRect dotRect = KDRect(px - dotDiameter/2, py-dotDiameter/2, dotDiameter, dotDiameter);
249  ctx->blendRectWithMask(dotRect, color, (const uint8_t *)dotMask, s_dotWorkingBuffer);
250  if (oversize) {
252  ctx->blendRectWithMask(oversizeDotRect, color, (const uint8_t *)oversizeDotMask, s_oversizeDotWorkingBuffer);
253  }
254 }
255 
256 void CurveView::drawGridLines(KDContext * ctx, KDRect rect, Axis axis, float step, KDColor color) const {
257  float rectMin = pixelToFloat(Axis::Horizontal, rect.left());
258  float rectMax = pixelToFloat(Axis::Horizontal, rect.right());
259  if (axis == Axis::Vertical) {
260  rectMax = pixelToFloat(Axis::Vertical, rect.top());
261  rectMin = pixelToFloat(Axis::Vertical, rect.bottom());
262  }
263  float start = step*((int)(min(axis)/step));
264  Axis otherAxis = (axis == Axis::Horizontal) ? Axis::Vertical : Axis::Horizontal;
265  for (float x =start; x < max(axis); x += step) {
266  /* When |start| >> step, start + step = start. In that case, quit the
267  * infinite loop. */
268  if (x == x-step || x == x+step) {
269  return;
270  }
271  if (rectMin <= x && x <= rectMax) {
272  drawLine(ctx, rect, otherAxis, x, color);
273  }
274  }
275 }
276 
277 void CurveView::drawGrid(KDContext * ctx, KDRect rect) const {
278  drawGridLines(ctx, rect, Axis::Horizontal, m_curveViewRange->xGridUnit(), Palette::GreyWhite);
279  drawGridLines(ctx, rect, Axis::Vertical, m_curveViewRange->yGridUnit(), Palette::GreyWhite);
280 }
281 
282 void CurveView::drawAxes(KDContext * ctx, KDRect rect, Axis axis) const {
283  drawLine(ctx, rect, axis, 0.0f, KDColorBlack, 1);
284 }
285 
286 #define LINE_THICKNESS 2
287 
288 #if LINE_THICKNESS == 1
289 
290 constexpr KDCoordinate circleDiameter = 1;
292 const uint8_t stampMask[stampSize+1][stampSize+1] = {
293  {0xFF, 0xE1, 0xFF},
294  {0xE1, 0x00, 0xE1},
295  {0xFF, 0xE1, 0xFF},
296 };
297 
298 #elif LINE_THICKNESS == 2
299 
303  {0xFF, 0xE6, 0xE6, 0xFF},
304  {0xE6, 0x33, 0x33, 0xE6},
305  {0xE6, 0x33, 0x33, 0xE6},
306  {0xFF, 0xE6, 0xE6, 0xFF},
307 };
308 
309 #elif LINE_THICKNESS == 3
310 
311 constexpr KDCoordinate circleDiameter = 3;
313 const uint8_t stampMask[stampSize+1][stampSize+1] = {
314  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
315  {0xFF, 0x7A, 0x0C, 0x7A, 0xFF},
316  {0xFF, 0x0C, 0x00, 0x0C, 0xFF},
317  {0xFF, 0x7A, 0x0C, 0x7A, 0xFF},
318  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
319 };
320 
321 #elif LINE_THICKNESS == 5
322 
323 constexpr KDCoordinate circleDiameter = 5;
325 const uint8_t stampMask[stampSize+1][stampSize+1] = {
326  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
327  {0xFF, 0xE1, 0x45, 0x0C, 0x45, 0xE1, 0xFF},
328  {0xFF, 0x45, 0x00, 0x00, 0x00, 0x45, 0xFF},
329  {0xFF, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0xFF},
330  {0xFF, 0x45, 0x00, 0x00, 0x00, 0x45, 0xFF},
331  {0xFF, 0xE1, 0x45, 0x0C, 0x45, 0xE1, 0xFF},
332  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
333 };
334 
335 #endif
336 
337 constexpr static int k_maxNumberOfIterations = 10;
338 
339 void CurveView::drawCurve(KDContext * ctx, KDRect rect, EvaluateModelWithParameter evaluation, void * model, void * context, KDColor color, bool colorUnderCurve, float colorLowerBound, float colorUpperBound, bool continuously) const {
340  float xMin = min(Axis::Horizontal);
341  float xMax = max(Axis::Horizontal);
342  float xStep = (xMax-xMin)/resolution();
343  float rectMin = pixelToFloat(Axis::Horizontal, rect.left() - k_externRectMargin);
344  float rectMax = pixelToFloat(Axis::Horizontal, rect.right() + k_externRectMargin);
345 
346  float pixelColorLowerBound = std::round(floatToPixel(Axis::Horizontal, colorLowerBound));
347  float pixelColorUpperBound = std::round(floatToPixel(Axis::Horizontal, colorUpperBound));
348 
349  for (float x = rectMin; x < rectMax; x += xStep) {
350  /* When |rectMin| >> xStep, rectMin + xStep = rectMin. In that case, quit
351  * the infinite loop. */
352  if (x == x-xStep || x == x+xStep) {
353  return;
354  }
355  float y = evaluation(x, model, context);
356  if (std::isnan(y)|| std::isinf(y)) {
357  continue;
358  }
359  float pxf = floatToPixel(Axis::Horizontal, x);
360  float pyf = floatToPixel(Axis::Vertical, y);
361  if (colorUnderCurve && pxf > pixelColorLowerBound && pxf < pixelColorUpperBound) {
362  KDRect colorRect((int)pxf, std::round(pyf), 1, std::round(floatToPixel(Axis::Vertical, 0.0f)) - std::round(pyf));
363  if (floatToPixel(Axis::Vertical, 0.0f) < std::round(pyf)) {
364  colorRect = KDRect((int)pxf, std::round(floatToPixel(Axis::Vertical, 0.0f)), 1, std::round(pyf) - std::round(floatToPixel(Axis::Vertical, 0.0f)));
365  }
366  ctx->fillRect(colorRect, color);
367  }
368  stampAtLocation(ctx, rect, pxf, pyf, color);
369  if (x <= rectMin || std::isnan(evaluation(x-xStep, model, context))) {
370  continue;
371  }
372  if (continuously) {
373  float puf = floatToPixel(Axis::Horizontal, x - xStep);
374  float pvf = floatToPixel(Axis::Vertical, evaluation(x-xStep, model, context));
375  straightJoinDots(ctx, rect, puf, pvf, pxf, pyf, color);
376  } else {
377  jointDots(ctx, rect, evaluation, model, context, x - xStep, evaluation(x-xStep, model, context), x, y, color, k_maxNumberOfIterations);
378  }
379  }
380 }
381 
382 void CurveView::drawHistogram(KDContext * ctx, KDRect rect, EvaluateModelWithParameter evaluation, void * model, void * context, float firstBarAbscissa, float barWidth,
383  bool fillBar, KDColor defaultColor, KDColor highlightColor, float highlightLowerBound, float highlightUpperBound) const {
384  float rectMin = pixelToFloat(Axis::Horizontal, rect.left());
385  float rectMinBinNumber = std::floor((rectMin - firstBarAbscissa)/barWidth);
386  float rectMinLowerBound = firstBarAbscissa + rectMinBinNumber*barWidth;
387  float rectMax = pixelToFloat(Axis::Horizontal, rect.right());
388  float rectMaxBinNumber = std::floor((rectMax - firstBarAbscissa)/barWidth);
389  float rectMaxUpperBound = firstBarAbscissa + (rectMaxBinNumber+1)*barWidth + barWidth;
390  float pHighlightLowerBound = floatToPixel(Axis::Horizontal, highlightLowerBound);
391  float pHighlightUpperBound = floatToPixel(Axis::Horizontal, highlightUpperBound);
392  for (float x = rectMinLowerBound; x < rectMaxUpperBound; x += barWidth) {
393  /* When |rectMinLowerBound| >> barWidth, rectMinLowerBound + barWidth = rectMinLowerBound.
394  * In that case, quit the infinite loop. */
395  if (x == x-barWidth || x == x+barWidth) {
396  return;
397  }
398  float centerX = fillBar ? x+barWidth/2.0f : x;
399  float y = evaluation(centerX, model, context);
400  if (std::isnan(y)) {
401  continue;
402  }
405  KDCoordinate pixelBarWidth = fillBar ? std::round(floatToPixel(Axis::Horizontal, x+barWidth)) - std::round(floatToPixel(Axis::Horizontal, x))-1 : 2;
406  KDRect binRect(pxf, pyf, pixelBarWidth, std::round(floatToPixel(Axis::Vertical, 0.0f)) - pyf);
407  if (floatToPixel(Axis::Vertical, 0.0f) < pyf) {
408  binRect = KDRect(pxf, std::round(floatToPixel(Axis::Vertical, 0.0f)), pixelBarWidth+1, pyf - std::round(floatToPixel(Axis::Vertical, 0.0f)));
409  }
410  KDColor binColor = defaultColor;
411  bool shouldColorBin = fillBar ? centerX >= highlightLowerBound && centerX <= highlightUpperBound : pxf >= floorf(pHighlightLowerBound) && pxf <= floorf(pHighlightUpperBound);
412  if (shouldColorBin) {
413  binColor = highlightColor;
414  }
415  ctx->fillRect(binRect, binColor);
416  }
417 }
418 
419 int CurveView::numberOfLabels(Axis axis) const {
420  Axis otherAxis = axis == Axis::Horizontal ? Axis::Vertical : Axis::Horizontal;
421  if (min(otherAxis) > 0.0f || max(otherAxis) < 0.0f) {
422  return 0;
423  }
424  return std::ceil((max(axis) - min(axis))/(2*gridUnit(axis)));
425 }
426 
427 void CurveView::jointDots(KDContext * ctx, KDRect rect, EvaluateModelWithParameter evaluation, void * model, void * context, float x, float y, float u, float v, KDColor color, int maxNumberOfRecursion) const {
428  float pyf = floatToPixel(Axis::Vertical, y);
429  float pvf = floatToPixel(Axis::Vertical, v);
430  if (std::isnan(pyf) || std::isnan(pvf)) {
431  return;
432  }
433  // No need to draw if both dots are outside visible area
434  if ((pyf < -stampSize && pvf < -stampSize) || (pyf > pixelLength(Axis::Vertical)+stampSize && pvf > pixelLength(Axis::Vertical)+stampSize)) {
435  return;
436  }
437  // If one of the dot is infinite, we cap it with a dot outside area
438  if (std::isinf(pyf)) {
439  pyf = pyf > 0 ? pixelLength(Axis::Vertical)+stampSize : -stampSize;
440  }
441  if (std::isinf(pvf)) {
442  pvf = pvf > 0 ? pixelLength(Axis::Vertical)+stampSize : -stampSize;
443  }
444  if (pyf - (float)circleDiameter/2.0f < pvf && pvf < pyf + (float)circleDiameter/2.0f) {
445  // the dots are already joined
446  return;
447  }
448  // C is the dot whose abscissa is between x and u
449  float cx = (x + u)/2.0f;
450  float cy = evaluation(cx, model, context);
451  if ((y <= cy && cy <= v) || (v <= cy && cy <= y)) {
452  /* As the middle dot is vertically between the two dots, we assume that we
453  * can draw a 'straight' line between the two */
454  float pxf = floatToPixel(Axis::Horizontal, x);
455  float puf = floatToPixel(Axis::Horizontal, u);
456  if (std::isnan(pxf) || std::isnan(puf)) {
457  return;
458  }
459  straightJoinDots(ctx, rect, pxf, pyf, puf, pvf, color);
460  return;
461  }
462  float pcxf = floatToPixel(Axis::Horizontal, cx);
463  float pcyf = floatToPixel(Axis::Vertical, cy);
464  if (maxNumberOfRecursion > 0) {
465  stampAtLocation(ctx, rect, pcxf, pcyf, color);
466  jointDots(ctx, rect, evaluation, model, context, x, y, cx, cy, color, maxNumberOfRecursion-1);
467  jointDots(ctx, rect, evaluation, model, context, cx, cy, u, v, color, maxNumberOfRecursion-1);
468  }
469 }
470 
471 void CurveView::straightJoinDots(KDContext * ctx, KDRect rect, float pxf, float pyf, float puf, float pvf, KDColor color) const {
472  if (pyf <= pvf) {
473  for (float pnf = pyf; pnf<pvf; pnf+= 1.0f) {
474  float pmf = pxf + (pnf - pyf)*(puf - pxf)/(pvf - pyf);
475  stampAtLocation(ctx, rect, pmf, pnf, color);
476  }
477  return;
478  }
479  straightJoinDots(ctx, rect, puf, pvf, pxf, pyf, color);
480 }
481 
482 void CurveView::stampAtLocation(KDContext * ctx, KDRect rect, float pxf, float pyf, KDColor color) const {
483  // We avoid drawing when no part of the stamp is visible
484  if (pyf < -stampSize || pyf > pixelLength(Axis::Vertical)+stampSize) {
485  return;
486  }
487  KDCoordinate px = pxf;
488  KDCoordinate py = pyf;
489  KDRect stampRect(px-(circleDiameter-2)/2, py-(circleDiameter-2)/2, stampSize, stampSize);
490  if (!rect.intersects(stampRect)) {
491  return;
492  }
493  uint8_t shiftedMask[stampSize][stampSize];
494  KDColor workingBuffer[stampSize*stampSize];
495  float dx = pxf - std::floor(pxf);
496  float dy = pyf - std::floor(pyf);
497  /* TODO: this could be optimized by precomputing 10 or 100 shifted masks. The
498  * dx and dy would be rounded to one tenth or one hundredth to choose the
499  * right shifted mask. */
500  for (int i=0; i<stampSize; i++) {
501  for (int j=0; j<stampSize; j++) {
502  shiftedMask[i][j] = dx * (stampMask[i][j]*dy+stampMask[i+1][j]*(1.0f-dy))
503  + (1.0f-dx) * (stampMask[i][j+1]*dy + stampMask[i+1][j+1]*(1.0f-dy));
504  }
505  }
506  ctx->blendRectWithMask(stampRect, color, (const uint8_t *)shiftedMask, workingBuffer);
507 }
508 
509 void CurveView::layoutSubviews() {
510  if (m_curveViewCursor != nullptr && m_cursorView != nullptr) {
511  m_cursorView->setFrame(cursorFrame());
512  }
513  if (m_bannerView != nullptr) {
514  m_bannerView->setFrame(bannerFrame());
515  }
516  if (m_okView != nullptr) {
517  m_okView->setFrame(okFrame());
518  }
519 }
520 
521 KDRect CurveView::cursorFrame() {
522  KDRect cursorFrame = KDRectZero;
523  if (m_cursorView && m_mainViewSelected && !std::isnan(m_curveViewCursor->x()) && !std::isnan(m_curveViewCursor->y())) {
524  KDSize cursorSize = m_cursorView->minimalSizeForOptimalDisplay();
527  cursorFrame = KDRect(xCursorPixelPosition - (cursorSize.width()-1)/2, yCursorPixelPosition - (cursorSize.height()-1)/2, cursorSize.width(), cursorSize.height());
528  if (cursorSize.height() == 0) {
529  KDCoordinate bannerHeight = m_bannerView != nullptr ? m_bannerView->minimalSizeForOptimalDisplay().height() : 0;
530  cursorFrame = KDRect(xCursorPixelPosition - (cursorSize.width()-1)/2, 0, cursorSize.width(),bounds().height()-bannerHeight);
531  }
532  }
533  return cursorFrame;
534 }
535 
536 KDRect CurveView::bannerFrame() {
537  KDRect bannerFrame = KDRectZero;
538  if (m_bannerView && m_mainViewSelected) {
540  bannerFrame = KDRect(0, bounds().height()- bannerHeight, bounds().width(), bannerHeight);
541  }
542  return bannerFrame;
543 }
544 
545 KDRect CurveView::okFrame() {
546  KDRect okFrame = KDRectZero;
547  if (m_okView && m_mainViewSelected) {
548  KDCoordinate bannerHeight = 0;
549  if (m_bannerView != nullptr) {
551  }
552  KDSize okSize = m_okView->minimalSizeForOptimalDisplay();
553  okFrame = KDRect(bounds().width()- okSize.width()-k_okMargin, bounds().height()- bannerHeight-okSize.height()-k_okMargin, okSize);
554  }
555  return okFrame;
556 }
557 
558 int CurveView::numberOfSubviews() const {
559  return (m_bannerView != nullptr) + (m_cursorView != nullptr) + (m_okView != nullptr);
560 };
561 
562 View * CurveView::subviewAtIndex(int index) {
563  assert(index >= 0 && index < 3);
564  /* If all subviews exist, we want Ok view to be the first child to avoid
565  * redrawing it because it falls in the union of dirty rectangles linked to
566  * the banner view and curve view */
567  if (index == 0) {
568  if (m_okView != nullptr) {
569  return m_okView;
570  } else {
571  if (m_bannerView != nullptr) {
572  return m_bannerView;
573  }
574  }
575  }
576  if (index == 1 && m_bannerView != nullptr && m_okView != nullptr) {
577  return m_bannerView;
578  }
579  return m_cursorView;
580 }
581 
582 }
constexpr KDCoordinate circleDiameter
Definition: curve_view.cpp:300
bool isMainViewSelected() const
Definition: curve_view.cpp:42
#define floorf(x)
Definition: math.h:142
#define assert(e)
Definition: assert.h:9
#define isinf(x)
Definition: math.h:44
KDCoordinate x() const
Definition: rect.h:36
View * m_bannerView
Definition: curve_view.h:56
void setFrame(KDRect frame)
Definition: view.cpp:125
void drawGridLines(KDContext *ctx, KDRect rect, Axis axis, float step, KDColor color) const
Definition: curve_view.cpp:256
const SettingsMessageTree model
KDColor s_dotWorkingBuffer[dotDiameter *dotDiameter]
Definition: curve_view.cpp:238
int16_t KDCoordinate
Definition: coordinate.h:6
constexpr KDCoordinate width() const
Definition: size.h:10
static KDSize stringSize(const char *text, FontSize size=FontSize::Large)
Definition: text.cpp:6
void setBannerView(View *bannerView)
Definition: curve_view.cpp:71
void markRectAsDirty(KDRect rect)
Definition: view.cpp:39
void drawSegment(KDContext *ctx, KDRect rect, Axis axis, float coordinate, float lowerBound, float upperBound, KDColor color, KDCoordinate thickness=1) const
Definition: curve_view.cpp:196
void blendRectWithMask(KDRect rect, KDColor color, const uint8_t *mask, KDColor *workingBuffer)
void drawGrid(KDContext *ctx, KDRect rect) const
Definition: curve_view.cpp:277
void drawAxes(KDContext *ctx, KDRect rect, Axis axis) const
Definition: curve_view.cpp:282
void setOkView(View *okView)
Definition: curve_view.cpp:77
size_t strlcpy(char *dst, const char *src, size_t len)
Definition: strlcpy.c:3
KDCoordinate top() const
Definition: rect.h:42
void drawCurve(KDContext *ctx, KDRect rect, EvaluateModelWithParameter evaluation, void *model, void *context, KDColor color, bool colorUnderCurve=false, float colorLowerBound=0.0f, float colorUpperBound=0.0f, bool continuously=false) const
Definition: curve_view.cpp:339
float resolution() const
Definition: curve_view.cpp:83
static constexpr KDCoordinate k_labelMargin
Definition: curve_view.h:34
void drawLine(KDContext *ctx, KDRect rect, Axis axis, float coordinate, KDColor color, KDCoordinate thickness=1) const
Definition: curve_view.cpp:175
unsigned char uint8_t
Definition: stdint.h:4
virtual float xMax()=0
virtual float xGridUnit()=0
static constexpr int ShortNumberOfSignificantDigits
Definition: constant.h:8
KDPoint blendString(const char *text, KDPoint p, KDText::FontSize size, KDColor textColor=KDColorBlack)
CurveViewCursor * m_curveViewCursor
Definition: curve_view.h:57
Definition: point.h:6
const uint8_t stampMask[stampSize+1][stampSize+1]
Definition: curve_view.cpp:302
Definition: size.h:6
virtual float samplingRatio() const
Definition: curve_view.cpp:87
KDCoordinate right() const
Definition: rect.h:43
constexpr KDCoordinate stampSize
Definition: curve_view.cpp:301
KDRect m_frame
Definition: view.h:66
size_t strlen(const char *s)
Definition: strlen.c:3
void drawLabels(KDContext *ctx, KDRect rect, Axis axis, bool shiftOrigin) const
Definition: curve_view.cpp:146
unsigned int uint32_t
Definition: stdint.h:6
#define round(x)
Definition: math.h:192
KDColor s_oversizeDotWorkingBuffer[oversizeDotDiameter *oversizeDotDiameter]
Definition: curve_view.cpp:239
static constexpr KDCoordinate k_okMargin
Definition: curve_view.h:35
static constexpr int k_externRectMargin
Definition: curve_view.h:39
KDCoordinate left() const
Definition: rect.h:45
constexpr KDColor KDColorBlack
Definition: color.h:41
#define false
Definition: stdbool.h:9
#define ceil(x)
Definition: math.h:170
#define isnan(x)
Definition: math.h:43
virtual float yMin()=0
void setCurveViewRange(CurveViewRange *curveViewRange)
Definition: curve_view.cpp:53
virtual uint32_t rangeChecksum()
constexpr KDCoordinate dotDiameter
Definition: curve_view.cpp:217
void start()
Definition: rt0.cpp:31
KDCoordinate y() const
Definition: rect.h:37
void drawHistogram(KDContext *ctx, KDRect rect, EvaluateModelWithParameter evaluation, void *model, void *context, float firstBarAbscissa, float barWidth, bool fillBar, KDColor defaultColor, KDColor highlightColor, float highlightLowerBound=INFINITY, float highlightUpperBound=-INFINITY) const
Definition: curve_view.cpp:382
virtual float yMax()=0
virtual KDSize minimalSizeForOptimalDisplay() const
Definition: view.cpp:183
constexpr KDRect KDRectZero
Definition: rect.h:70
Definition: rect.h:26
void fillRect(KDRect rect, KDColor color)
Definition: context_rect.cpp:8
Definition: color.h:6
void setCursorView(View *cursorView)
Definition: curve_view.cpp:64
float floatToPixel(Axis axis, float f) const
Definition: curve_view.cpp:115
Definition: view.h:23
virtual void reload()
Definition: curve_view.cpp:25
void computeLabels(Axis axis)
Definition: curve_view.cpp:130
constexpr KDCoordinate oversizeDotDiameter
Definition: curve_view.cpp:226
KDCoordinate width() const
Definition: rect.h:39
void selectMainView(bool mainViewSelected)
Definition: curve_view.cpp:46
const uint8_t oversizeDotMask[oversizeDotDiameter][oversizeDotDiameter]
Definition: curve_view.cpp:227
void drawDot(KDContext *ctx, KDRect rect, float x, float y, KDColor color, bool oversize=false) const
Definition: curve_view.cpp:241
#define floor(x)
Definition: math.h:179
const uint8_t dotMask[dotDiameter][dotDiameter]
Definition: curve_view.cpp:218
KDCoordinate height() const
Definition: rect.h:40
KDRect bounds() const
Definition: view.cpp:157
float pixelToFloat(Axis axis, KDCoordinate p) const
Definition: curve_view.cpp:110
bool intersects(const KDRect &other) const
Definition: rect.cpp:24
static constexpr KDColor GreyWhite
Definition: palette.h:12
virtual float xMin()=0
static constexpr KDCoordinate k_labelGraduationLength
Definition: curve_view.h:36
KDCoordinate bottom() const
Definition: rect.h:44
constexpr KDCoordinate height() const
Definition: size.h:11