From fd8613ac7abe1282167e761e92e54a4af501b5bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 22 Dec 2016 14:24:46 +0100 Subject: [PATCH] [apps/statistics] Redesign data API with Romain Change-Id: I666125ae85e04311806379a4ec2255c256f253f2 --- apps/curve_view.cpp | 21 +- apps/curve_view.h | 2 +- apps/curve_view_window.cpp | 5 +- apps/curve_view_window.h | 2 +- apps/graph/graph/graph_window.cpp | 46 +-- apps/probability/law/law.cpp | 2 +- apps/statistics/banner_view.cpp | 6 +- apps/statistics/data.cpp | 261 +++++++++--------- apps/statistics/data.h | 50 ++-- apps/statistics/histogram_controller.cpp | 8 +- .../histogram_parameter_controller.cpp | 8 +- apps/statistics/histogram_view.cpp | 16 +- 12 files changed, 208 insertions(+), 219 deletions(-) diff --git a/apps/curve_view.cpp b/apps/curve_view.cpp index 13bc89d1f..954875d73 100644 --- a/apps/curve_view.cpp +++ b/apps/curve_view.cpp @@ -209,26 +209,25 @@ void CurveView::drawDiscreteHistogram(KDColor color, KDContext * ctx, KDRect rec } } -void CurveView::drawHistogram(float binWidth, KDColor color, KDContext * ctx, KDRect rect, KDColor highlightColor, float coloredBin) const { - KDCoordinate pixelBinWidth = floatToPixel(Axis::Horizontal, binWidth) - floatToPixel(Axis::Horizontal, 0.0f); - float min = m_curveViewWindow->xMin(); +void CurveView::drawHistogram(float barStart, float barWidth, KDColor color, KDContext * ctx, KDRect rect, KDColor highlightColor, float coloredBin) const { + KDCoordinate pixelBarWidth = floatToPixel(Axis::Horizontal, barWidth) - floatToPixel(Axis::Horizontal, 0.0f); float rectMin = pixelToFloat(Axis::Horizontal, rect.left()); - int rectMinBinNumber = (rectMin - min)/binWidth; - float rectMinLowerBound = min + rectMinBinNumber*binWidth; + int rectMinBinNumber = floorf((rectMin - barStart)/barWidth); + float rectMinLowerBound = barStart + rectMinBinNumber*barWidth + barWidth/2; float rectMax = pixelToFloat(Axis::Horizontal, rect.right()); - int rectMaxBinNumber = (rectMax - min)/binWidth; - float rectMaxUpperBound = min + (rectMaxBinNumber+1)*binWidth; - for (float x = rectMinLowerBound; x < rectMaxUpperBound; x += binWidth) { + int rectMaxBinNumber = floorf((rectMax - barStart)/barWidth); + float rectMaxUpperBound = barStart + (rectMaxBinNumber+1)*barWidth + barWidth; + for (float x = rectMinLowerBound; x < rectMaxUpperBound; x += barWidth) { float y = evaluateCurveAtAbscissa(nullptr, x); if (!isnan(y)) { float pxf = floatToPixel(Axis::Horizontal, x); float pyf = floatToPixel(Axis::Vertical, y); - KDRect binRect(pxf, roundf(pyf), pixelBinWidth+1 , floatToPixel(Axis::Vertical, 0.0f) - roundf(pyf)); + KDRect binRect(pxf-pixelBarWidth/2, roundf(pyf), pixelBarWidth+1 , floatToPixel(Axis::Vertical, 0.0f) - roundf(pyf)); if (floatToPixel(Axis::Vertical, 0.0f) < roundf(pyf)) { - binRect = KDRect(pxf, floatToPixel(Axis::Vertical, 0.0f), pixelBinWidth+1, roundf(pyf) - floatToPixel(Axis::Vertical, 0.0f)); + binRect = KDRect(pxf-pixelBarWidth/2, floatToPixel(Axis::Vertical, 0.0f), pixelBarWidth+1, roundf(pyf) - floatToPixel(Axis::Vertical, 0.0f)); } KDColor binColor = color; - if (x <= coloredBin && coloredBin < x+binWidth) { + if (x - barWidth/2 <= coloredBin && coloredBin < x+barWidth/2) { binColor = highlightColor; } ctx->fillRect(binRect, binColor); diff --git a/apps/curve_view.h b/apps/curve_view.h index 10472539a..52782885c 100644 --- a/apps/curve_view.h +++ b/apps/curve_view.h @@ -33,7 +33,7 @@ protected: void drawAxes(Axis axis, KDContext * ctx, KDRect rect) const; void drawCurve(void * curve, KDColor color, KDContext * ctx, KDRect rect, bool colorUnderCurve = false, float colorLowerBound = 0.0f, float colorUpperBound = 0.0f, bool continuously = false) const; void drawDiscreteHistogram(KDColor color, KDContext * ctx, KDRect rect, bool colorHighlightBin = false, KDColor highlightColor = KDColorBlack, float colorLowerBound = 0.0f, float colorUpperBound = 0.0f) const; - void drawHistogram(float binWidth, KDColor color, KDContext * ctx, KDRect rect, KDColor highlightColor, float coloredBin) const; + void drawHistogram(float barStart, float barWidth, KDColor color, KDContext * ctx, KDRect rect, KDColor highlightColor, float coloredBin) const; void computeLabels(Axis axis); void drawLabels(Axis axis, bool shiftOrigin, KDContext * ctx, KDRect rect) const; private: diff --git a/apps/curve_view_window.cpp b/apps/curve_view_window.cpp index 682b8918e..7ad21fe36 100644 --- a/apps/curve_view_window.cpp +++ b/apps/curve_view_window.cpp @@ -6,14 +6,13 @@ float CurveViewWindow::yGridUnit() { return 0.0f; } -float CurveViewWindow::computeGridUnit(Axis axis) { +float CurveViewWindow::computeGridUnit(Axis axis, float min, float max) { int a = 0; int b = 0; - float d = xMax() - xMin(); + float d = max - min; float maxNumberOfUnits = k_maxNumberOfXGridUnits; float minNumberOfUnits = k_minNumberOfXGridUnits; if (axis == Axis::Y) { - d = yMax() - yMin(); maxNumberOfUnits = k_maxNumberOfYGridUnits; minNumberOfUnits = k_minNumberOfYGridUnits; } diff --git a/apps/curve_view_window.h b/apps/curve_view_window.h index 48412cf71..62ca096fe 100644 --- a/apps/curve_view_window.h +++ b/apps/curve_view_window.h @@ -21,7 +21,7 @@ protected: constexpr static float k_oneUnit = 1.0f; constexpr static float k_twoUnit = 2.0f; constexpr static float k_fiveUnit = 5.0f; - float computeGridUnit(Axis axis); + float computeGridUnit(Axis axis, float min, float max); }; #endif diff --git a/apps/graph/graph/graph_window.cpp b/apps/graph/graph/graph_window.cpp index 555f6db7c..e6409cc30 100644 --- a/apps/graph/graph/graph_window.cpp +++ b/apps/graph/graph/graph_window.cpp @@ -51,23 +51,23 @@ float GraphWindow::yGridUnit() { void GraphWindow::setXMin(float xMin) { m_xMin = xMin; computeYaxes(); - m_xGridUnit = computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); } void GraphWindow::setXMax(float xMax) { m_xMax = xMax; computeYaxes(); - m_xGridUnit = computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); } void GraphWindow::setYMin(float yMin) { m_yMin = yMin; - m_yGridUnit = computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); } void GraphWindow::setYMax(float yMax) { m_yMax = yMax; - m_yGridUnit = computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); } void GraphWindow::setYAuto(bool yAuto) { @@ -104,7 +104,7 @@ bool GraphWindow::computeYaxes() { m_yMax = max + 1; } } - m_yGridUnit = computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); return true; } @@ -123,11 +123,11 @@ void GraphWindow::zoom(float ratio) { float yMax = m_yMax; m_xMin = (xMax+xMin)/2.0f - ratio*fabsf(xMax-xMin); m_xMax = (xMax+xMin)/2.0f + ratio*fabsf(xMax-xMin); - m_xGridUnit = computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); m_yAuto = false; m_yMin = (yMax+yMin)/2.0f - ratio*fabsf(yMax-yMin); m_yMax = (yMax+yMin)/2.0f + ratio*fabsf(yMax-yMin); - m_yGridUnit = computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); } void GraphWindow::centerAxisAround(Axis axis, float position) { @@ -136,13 +136,13 @@ void GraphWindow::centerAxisAround(Axis axis, float position) { m_xMin = position - range/2.0f; m_xMax = position + range/2.0f; computeYaxes(); - m_xGridUnit = computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); } else { m_yAuto = false; float range = m_yMax - m_yMin; m_yMin = position - range/2.0f; m_yMax = position + range/2.0f; - m_yGridUnit = computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); } } @@ -151,23 +151,23 @@ void GraphWindow::translateWindow(Direction direction) { if (direction == Direction::Up) { m_yMin = m_yMin + m_yGridUnit; m_yMax = m_yMax + m_yGridUnit; - m_yGridUnit = computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); } if (direction == Direction::Down) { m_yMin = m_yMin - m_yGridUnit; m_yMax = m_yMax - m_yGridUnit; - m_yGridUnit = computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); } if (direction == Direction::Left) { m_xMin = m_xMin - m_xGridUnit; m_xMax = m_xMax - m_xGridUnit; - m_xGridUnit = computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); computeYaxes(); } if (direction == Direction::Right) { m_xMin = m_xMin + m_xGridUnit; m_xMax = m_xMax + m_xGridUnit; - m_xGridUnit = computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); computeYaxes(); } } @@ -175,11 +175,11 @@ void GraphWindow::translateWindow(Direction direction) { void GraphWindow::setTrigonometric() { m_xMin = -10.5f; m_xMax = 10.5f; - m_xGridUnit = computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); m_yAuto = false; m_yMin = -1.6f; m_yMax = 1.6f; - m_yGridUnit = computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); } void GraphWindow::roundAbscissa() { @@ -187,7 +187,7 @@ void GraphWindow::roundAbscissa() { float xMax = m_xMax; m_xMin = roundf((xMin+xMax)/2) - 160.0f; m_xMax = roundf((xMin+xMax)/2) + 159.0f; - m_xGridUnit = computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); computeYaxes(); } @@ -198,17 +198,17 @@ void GraphWindow::normalize() { float yMax = m_yMax; m_xMin = (xMin+xMax)/2 - 5.3f; m_xMax = (xMin+xMax)/2 + 5.3f; - m_xGridUnit = computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); m_yAuto = false; m_yMin = (yMin+yMax)/2 - 3.1f; m_yMax = (yMin+yMax)/2 + 3.1f; - m_yGridUnit = computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); } void GraphWindow::setDefault() { m_xMin = -10.0f; m_xMax = 10.0f; - m_xGridUnit = computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); setYAuto(true); } @@ -219,27 +219,27 @@ bool GraphWindow::panToMakePointVisible(float x, float y, float xMargin, float y if (x < m_xMin + xMargin) { m_xMin = x - xMargin; m_xMax = m_xMin + xRange; - m_xGridUnit = computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); computeYaxes(); windowMoved = true; } if (x > m_xMax - xMargin) { m_xMax = x + xMargin; m_xMin = m_xMax - xRange; - m_xGridUnit = computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); computeYaxes(); windowMoved = true; } if (y < m_yMin + yMargin) { m_yMin = y - yMargin; m_yMax = m_yMin + yRange; - m_yGridUnit = computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); windowMoved = true; } if (y > m_yMax - yMargin) { m_yMax = y + yMargin; m_yMin = m_yMax - yRange; - m_yGridUnit = computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); windowMoved = true; } return windowMoved; diff --git a/apps/probability/law/law.cpp b/apps/probability/law/law.cpp index b05cdb0db..2f503a49a 100644 --- a/apps/probability/law/law.cpp +++ b/apps/probability/law/law.cpp @@ -5,7 +5,7 @@ namespace Probability { float Law::xGridUnit() { - return computeGridUnit(Axis::X); + return computeGridUnit(Axis::X, xMin(), xMax()); } float Law::cumulativeDistributiveFunctionAtAbscissa(float x) const { diff --git a/apps/statistics/banner_view.cpp b/apps/statistics/banner_view.cpp index c04036c81..85620b63f 100644 --- a/apps/statistics/banner_view.cpp +++ b/apps/statistics/banner_view.cpp @@ -19,10 +19,10 @@ void BannerView::reload() { const char * legend = "Interval ["; int legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); - float lowerBound = m_data->selectedBin() - m_data->binWidth()/2; + float lowerBound = m_data->selectedBar() - m_data->barWidth()/2; int lowerBoundNumberOfChar = Float(lowerBound).convertFloatToText(buffer+legendLength, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode); buffer[legendLength+lowerBoundNumberOfChar] = ';'; - float upperBound = m_data->selectedBin() + m_data->binWidth()/2; + float upperBound = m_data->selectedBar() + m_data->barWidth()/2; int upperBoundNumberOfChar = Float(upperBound).convertFloatToText(buffer+legendLength+lowerBoundNumberOfChar+1, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode); buffer[legendLength+lowerBoundNumberOfChar+upperBoundNumberOfChar+1] = '['; buffer[legendLength+lowerBoundNumberOfChar+upperBoundNumberOfChar+2] = 0; @@ -31,7 +31,7 @@ void BannerView::reload() { legend = "Effectif: "; legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); - float size = m_data->sizeAtValue(m_data->selectedBin()); + float size = m_data->heightForBarAtValue(m_data->selectedBar()); Float(size).convertFloatToText(buffer+legendLength, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode); m_sizeView.setText(buffer); diff --git a/apps/statistics/data.cpp b/apps/statistics/data.cpp index 3f6b5694b..23fe92419 100644 --- a/apps/statistics/data.cpp +++ b/apps/statistics/data.cpp @@ -1,16 +1,15 @@ #include "data.h" #include #include +#include namespace Statistics { Data::Data() : m_numberOfPairs(0), - m_binWidth(1.0f), - m_totalSize(0), - m_selectedBin(0.0f), - m_minValue(0.0f), - m_maxValue(0.0f), + m_barWidth(1.0f), + m_selectedBar(0.0f), + m_barStart(0.0f), m_xMin(0.0f), m_xMax(10.0f), m_yMax(1.0f), @@ -18,23 +17,11 @@ Data::Data() : { } +/* Raw numeric data */ + int Data::numberOfPairs() const { return m_numberOfPairs; } - -float Data::binWidth() { - return m_binWidth; -} - -void Data::setBinWidth(float binWidth) { - if (binWidth <= 0.0f) { - return; - } - m_binWidth = binWidth; - computeYMax(); - initSelectedBin(); -} - float Data::valueAtIndex(int index) { assert(index < m_numberOfPairs); return m_values[index]; @@ -49,115 +36,105 @@ void Data::setValueAtIndex(float value, int index) { if (index >= k_maxNumberOfPairs) { return; } - float formerValue = index >= m_numberOfPairs ? value : m_values[index]; m_values[index] = value; if (index >= m_numberOfPairs) { m_sizes[index] = 1; - m_totalSize += 1; m_numberOfPairs++; } - updateAbsoluteBoundsAfterDeleting(formerValue); - updateAbsoluteBoundsAfterAdding(value); - initSelectedBin(); + initBarParameters(); + initWindowParameters(); } void Data::setSizeAtIndex(int size, int index) { if (index >= k_maxNumberOfPairs) { return; } - float formerValue = index >= m_numberOfPairs ? 0.0f : m_values[index]; - int formerSize = index >= m_numberOfPairs ? 0 : m_sizes[index]; m_sizes[index] = size; - m_totalSize += size - formerSize; if (index >= m_numberOfPairs) { m_values[index] = 0.0f; m_numberOfPairs++; } - updateAbsoluteBoundsAfterDeleting(formerValue); - updateAbsoluteBoundsAfterAdding(m_values[index]); - initSelectedBin(); + initBarParameters(); + initWindowParameters(); } void Data::deletePairAtIndex(int index) { m_numberOfPairs--; - float formerValue = m_values[index]; - m_totalSize -= m_sizes[index]; for (int k = index; k < m_numberOfPairs; k++) { m_values[k] = m_values[k+1]; m_sizes[k] = m_sizes[k+1]; } m_values[m_numberOfPairs] = 0.0f; m_sizes[m_numberOfPairs] = 0; - if (m_minValue == formerValue) { - computeAbsoluteBoundValue(); - } - updateAbsoluteBoundsAfterDeleting(formerValue); - initSelectedBin(); -} - -int Data::sizeAtValue(float value) { - if (value < m_minValue) { - return 0; - } - float bin = binWidth(); - int binNumber = (value - m_minValue)/bin; - float lowerBound = m_minValue + binNumber*bin; - float upperBound = m_minValue + (binNumber+1)*bin; - int result = 0; - for (int k = 0; k < m_numberOfPairs; k++) { - if (m_values[k] < upperBound && lowerBound <= m_values[k]) { - result += m_sizes[k]; - } - } - return result; + initBarParameters(); + initWindowParameters(); } int Data::totalSize() { - return m_totalSize; -} - -float Data::minValue() { - return m_minValue; -} - -void Data::setMinValue(float minValue) { - m_minValue = minValue; - computeTotalSize(); - computeYMax(); - initSelectedBin(); -} - -float Data::selectedBin() { - return m_selectedBin; -} - -void Data::initSelectedBin() { - m_selectedBin = m_minValue + m_binWidth/2; - while (sizeAtValue(m_selectedBin) == 0) { - m_selectedBin += m_binWidth; + int totalSize = 0; + for (int k = 0; k < m_numberOfPairs; k++) { + totalSize += m_sizes[k]; } - initWindowBounds(); + return totalSize; +} +/* Histogram bars */ + +float Data::barWidth() { + return m_barWidth; } -bool Data::selectNextBinToward(int direction) { - float newSelectedBin = m_selectedBin; +void Data::setBarWidth(float barWidth) { + if (barWidth <= 0.0f) { + return; + } + m_barWidth = barWidth; + initWindowParameters(); +} + +float Data::barStart() { + return m_barStart; +} + +void Data::setBarStart(float barStart) { + m_barStart = barStart; + initWindowParameters(); +} + +int Data::heightForBarAtValue(float value) { + float width = barWidth(); + int barNumber = floorf((value - m_barStart)/width); + float lowerBound = m_barStart + barNumber*width; + float upperBound = m_barStart + (barNumber+1)*width; + return sumOfValuesBetween(lowerBound, upperBound); +} + +float Data::selectedBar() { + return m_selectedBar; +} + +bool Data::selectNextBarToward(int direction) { + float newSelectedBar = m_selectedBar; + float max = maxValue(); + float min = minValue(); if (direction > 0.0f) { do { - newSelectedBin += m_binWidth; - } while (sizeAtValue(newSelectedBin) == 0 && newSelectedBin < m_maxValue); + newSelectedBar += m_barWidth; + } while (heightForBarAtValue(newSelectedBar) == 0 && newSelectedBar < max + m_barWidth); } if (direction < 0.0f) { do { - newSelectedBin -= m_binWidth; - } while (sizeAtValue(newSelectedBin) == 0 && newSelectedBin > m_minValue); + newSelectedBar -= m_barWidth; + } while (heightForBarAtValue(newSelectedBar) == 0 && newSelectedBar > min - m_barWidth); } - if (newSelectedBin > m_minValue && newSelectedBin < m_maxValue + m_binWidth) { - m_selectedBin = newSelectedBin; - return scrollToSelectedBin(); + if (newSelectedBar > min - m_barWidth && newSelectedBar < max + m_barWidth) { + m_selectedBar = newSelectedBar; + return scrollToSelectedBar(); } return false; } +/* CurveViewWindow */ + float Data::xMin() { return m_xMin; } @@ -178,83 +155,93 @@ float Data::xGridUnit() { return m_xGridUnit; } -void Data::computeTotalSize() { - m_totalSize = 0; +float Data::sumOfValuesBetween(float x1, float x2) { + int result = 0; for (int k = 0; k < m_numberOfPairs; k++) { - if (m_values[k] >= m_minValue) { - m_totalSize += m_sizes[k]; + if (m_values[k] < x2 && x1 <= m_values[k]) { + result += m_sizes[k]; } } + return result; } -bool Data::scrollToSelectedBin() { +float Data::maxValue() { + float max = -FLT_MAX; + for (int k = 0; k < m_numberOfPairs; k++) { + if (m_values[k] > max && m_sizes[k] > 0) { + max = m_values[k]; + } + } + return max; +} + +float Data::minValue() { + float min = FLT_MAX; + for (int k = 0; k < m_numberOfPairs; k++) { + if (m_values[k] < min && m_sizes[k] > 0) { + min = m_values[k]; + } + } + return min; +} + +bool Data::scrollToSelectedBar() { float range = m_xMax - m_xMin; - if (m_xMin > m_selectedBin) { - m_xMin = m_selectedBin - m_binWidth/2; + if (m_xMin > m_selectedBar) { + m_xMin = m_selectedBar - m_barWidth/2; m_xMax = m_xMin + range; return true; } - if (m_selectedBin > m_xMax) { - m_xMax = m_selectedBin + m_binWidth/2; + if (m_selectedBar > m_xMax) { + m_xMax = m_selectedBar + m_barWidth/2; m_xMin = m_xMax - range; return true; } return false; } -void Data::initWindowBounds() { - m_xMin = m_minValue; - if (m_maxValue - m_xMin > k_maxHistogramRangeValue) { - m_xMax = m_xMin + 10.0f; - } else { - m_xMax = m_maxValue + m_binWidth; +void Data::initBarParameters() { + float min = minValue(); + float max = maxValue(); + m_barStart = min; + m_barWidth = computeGridUnit(Axis::X, min, max); + if (m_barWidth <= 0.0f) { + m_barWidth = 1.0f; } - m_xGridUnit = computeGridUnit(Axis::X); } -void Data::computeYMax() { +void Data::initWindowParameters() { + float min = minValue(); + float max = maxValue(); + m_xMin = m_barStart; + m_xMax = max + m_barWidth; + if ((m_xMax - m_xMin)/m_barWidth > k_maxNumberOfBarsPerWindow) { + m_xMax = m_xMin + k_maxNumberOfBarsPerWindow*m_barWidth; + } + if (m_xMin >= m_xMax) { + m_xMax = m_xMin + 10.0f*m_barWidth; + } m_yMax = -FLT_MAX; - for (float x = m_minValue; x <= m_maxValue; x += m_binWidth) { - float size = sizeAtValue(x); + for (float x = min; x <= max; x += m_barWidth) { + float size = heightForBarAtValue(x); if (size > m_yMax) { m_yMax = size; } } - m_yMax = m_yMax/m_totalSize; -} + m_yMax = m_yMax/(float)totalSize(); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); -void Data::computeAbsoluteBoundValue() { - m_minValue = FLT_MAX; - m_maxValue = -FLT_MAX; - for (int k = 0; k < m_numberOfPairs; k++) { - if (m_values[k] < m_minValue) { - m_minValue = m_values[k]; - } - if (m_values[k] > m_maxValue) { - m_maxValue = m_values[k]; + m_selectedBar = m_barStart + m_barWidth/2; + while (heightForBarAtValue(m_selectedBar) == 0 && m_selectedBar < max + m_barWidth) { + m_selectedBar += m_barWidth; + } + if (m_selectedBar > max + m_barWidth) { + /* No bar is after m_barStart */ + m_selectedBar = m_barStart + m_barWidth/2; + while (heightForBarAtValue(m_selectedBar) == 0 && m_selectedBar > min - m_barWidth) { + m_selectedBar -= m_barWidth; } } - m_binWidth = 1.0f; - initWindowBounds(); - computeYMax(); -} - -void Data::updateAbsoluteBoundsAfterAdding(float value) { - if (m_minValue > value) { - m_minValue = value; - } - if (m_maxValue < value) { - m_maxValue = value; - } - m_binWidth = 1.0f; - initWindowBounds(); - computeYMax(); -} - -void Data::updateAbsoluteBoundsAfterDeleting(float value) { - if (value == m_minValue || value == m_maxValue) { - computeAbsoluteBoundValue(); - } } } diff --git a/apps/statistics/data.h b/apps/statistics/data.h index 24297fc48..afd5a63d8 100644 --- a/apps/statistics/data.h +++ b/apps/statistics/data.h @@ -10,48 +10,52 @@ public: Data(); // Delete the implicit copy constructor: the object is heavy Data(const Data&) = delete; + + // Raw numeric data int numberOfPairs() const; - float binWidth(); - void setBinWidth(float binWidth); float valueAtIndex(int index); int sizeAtIndex(int index); void setValueAtIndex(float value, int index); void setSizeAtIndex(int size, int index); void deletePairAtIndex(int index); - int sizeAtValue(float value); int totalSize(); - float minValue(); - void setMinValue(float minValue); - float selectedBin(); - void initSelectedBin(); - bool selectNextBinToward(int direction); + + // Histogram bars + float barWidth(); + void setBarWidth(float barWidth); + float barStart(); + void setBarStart(float barStart); + int heightForBarAtValue(float value); + float selectedBar(); + bool selectNextBarToward(int direction); + + //CurveViewWindow float xMin() override; - // if the range of value is to wide, value max returns valueMin + 10 + // if the range of value is to wide compared to the bar width, value max is capped float xMax() override; float yMin() override; float yMax() override; float xGridUnit() override; + // TODO: decide the max number of elements after optimization constexpr static int k_maxNumberOfPairs = 500; private: - constexpr static int k_maxHistogramRangeValue = 300; + constexpr static int k_maxNumberOfBarsPerWindow = 300; constexpr static float k_marginFactor = 0.2f; - void computeTotalSize(); - bool scrollToSelectedBin(); - void initWindowBounds(); - void computeYMax(); - void computeAbsoluteBoundValue(); - void updateAbsoluteBoundsAfterAdding(float value); - void updateAbsoluteBoundsAfterDeleting(float value); + float sumOfValuesBetween(float x1, float x2); + float maxValue(); + float minValue(); + bool scrollToSelectedBar(); + void initBarParameters(); + void initWindowParameters(); + // Raw numeric data int m_sizes[k_maxNumberOfPairs]; float m_values[k_maxNumberOfPairs]; int m_numberOfPairs; - float m_binWidth; - int m_totalSize; - float m_selectedBin; - // Absolute bounds of the data - float m_minValue; - float m_maxValue; + // Histogram bars + float m_barWidth; + float m_selectedBar; + float m_barStart; // Window bounds of the data float m_xMin; float m_xMax; diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 8634b05e1..efaab00ca 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -40,7 +40,7 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { if (!m_view.selectedBins()) { headerViewController()->setSelectedButton(-1); m_view.selectBins(true); - m_view.reload(m_data->selectedBin()); + m_view.reload(m_data->selectedBar()); return true; } return false; @@ -57,11 +57,11 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { } if (event == Ion::Events::Left || event == Ion::Events::Right) { int direction = event == Ion::Events::Left ? -1 : 1; - m_view.reload(m_data->selectedBin()); - if (m_data->selectNextBinToward(direction)) { + m_view.reload(m_data->selectedBar()); + if (m_data->selectNextBarToward(direction)) { m_view.reload(NAN); } else { - m_view.reload(m_data->selectedBin()); + m_view.reload(m_data->selectedBar()); } return true; } diff --git a/apps/statistics/histogram_parameter_controller.cpp b/apps/statistics/histogram_parameter_controller.cpp index 8f93f12e7..691167f86 100644 --- a/apps/statistics/histogram_parameter_controller.cpp +++ b/apps/statistics/histogram_parameter_controller.cpp @@ -24,9 +24,9 @@ const char * HistogramParameterController::title() const { float HistogramParameterController::parameterAtIndex(int index) { assert(index >= 0 && index < 2); if (index == 0) { - return m_data->binWidth(); + return m_data->barWidth(); } - return m_data->minValue(); + return m_data->barStart(); } void HistogramParameterController::setParameterAtIndex(int parameterIndex, float f) { @@ -36,9 +36,9 @@ void HistogramParameterController::setParameterAtIndex(int parameterIndex, float app()->displayWarning("Value interdite"); return; } - m_data->setBinWidth(f); + m_data->setBarWidth(f); } else { - m_data->setMinValue(f); + m_data->setBarStart(f); } } diff --git a/apps/statistics/histogram_view.cpp b/apps/statistics/histogram_view.cpp index 60d4ee725..87c15ab1a 100644 --- a/apps/statistics/histogram_view.cpp +++ b/apps/statistics/histogram_view.cpp @@ -17,9 +17,9 @@ void HistogramView::reload(float dirtyZoneCenter) { markRectAsDirty(bounds()); computeLabels(Axis::Horizontal); } else { - float pixelLowerBound = floatToPixel(Axis::Horizontal, dirtyZoneCenter - m_data->binWidth())-1; - float pixelUpperBound = floatToPixel(Axis::Horizontal, dirtyZoneCenter + m_data->binWidth())+1; - float selectedValueInPixels = floatToPixel(Axis::Vertical, (float)m_data->sizeAtValue(dirtyZoneCenter)/(float)m_data->totalSize())-1; + float pixelLowerBound = floatToPixel(Axis::Horizontal, dirtyZoneCenter - m_data->barWidth())-1; + float pixelUpperBound = floatToPixel(Axis::Horizontal, dirtyZoneCenter + m_data->barWidth())+1; + float selectedValueInPixels = floatToPixel(Axis::Vertical, (float)m_data->heightForBarAtValue(dirtyZoneCenter)/(float)m_data->totalSize())-1; float horizontalAxisInPixels = floatToPixel(Axis::Vertical, 0.0f)+1; KDRect dirtyZone(KDRect(pixelLowerBound, selectedValueInPixels, pixelUpperBound-pixelLowerBound, horizontalAxisInPixels - selectedValueInPixels)); @@ -34,9 +34,9 @@ bool HistogramView::selectedBins() { void HistogramView::selectBins(bool selectedBins) { if (m_selectedBins != selectedBins) { - reload(m_data->selectedBin()); + reload(m_data->selectedBar()); m_selectedBins = selectedBins; - reload(m_data->selectedBin()); + reload(m_data->selectedBar()); } } @@ -45,9 +45,9 @@ void HistogramView::drawRect(KDContext * ctx, KDRect rect) const { drawAxes(Axis::Horizontal, ctx, rect); drawLabels(Axis::Horizontal, true, ctx, rect); if (m_selectedBins) { - drawHistogram(m_data->binWidth(), KDColorBlack, ctx, rect, KDColorRed, m_data->selectedBin()); + drawHistogram(m_data->barStart(), m_data->barWidth(), KDColorBlack, ctx, rect, KDColorRed, m_data->selectedBar()); } else { - drawHistogram(m_data->binWidth(), KDColorBlack, ctx, rect, KDColorRed, NAN); + drawHistogram(m_data->barStart(), m_data->barWidth(), KDColorBlack, ctx, rect, KDColorRed, NAN); } } @@ -76,7 +76,7 @@ char * HistogramView::label(Axis axis, int index) const { } float HistogramView::evaluateCurveAtAbscissa(void * curve, float t) const { - return (float)m_data->sizeAtValue(t)/(float)m_data->totalSize(); + return (float)m_data->heightForBarAtValue(t)/(float)m_data->totalSize(); } }