From 11251f03dea83e608866d328eeef6b7139fa08b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 15:09:43 +0200 Subject: [PATCH] [apps] Fix Statistics: now displays three series --- apps/regression/app.cpp | 2 +- apps/shared/float_pair_store.cpp | 28 +++++++--- apps/shared/float_pair_store.h | 5 +- apps/shared/store_controller.cpp | 17 +++--- apps/shared/store_controller.h | 7 ++- apps/shared/store_parameter_controller.cpp | 7 +-- apps/shared/store_parameter_controller.h | 2 + apps/statistics/app.cpp | 2 +- apps/statistics/box_controller.cpp | 11 ++-- apps/statistics/box_controller.h | 1 + apps/statistics/box_range.cpp | 8 +-- apps/statistics/box_view.cpp | 20 ++++--- apps/statistics/box_view.h | 2 + apps/statistics/calculation_controller.cpp | 7 +-- apps/statistics/histogram_controller.cpp | 47 ++++++++--------- .../histogram_parameter_controller.cpp | 52 +++++++++++++++++-- apps/statistics/histogram_view.cpp | 12 +++-- apps/statistics/store.cpp | 47 ++++++++++++++--- apps/statistics/store.h | 7 ++- 19 files changed, 194 insertions(+), 90 deletions(-) diff --git a/apps/regression/app.cpp b/apps/regression/app.cpp index ecd8180a3..d1b3f9ade 100644 --- a/apps/regression/app.cpp +++ b/apps/regression/app.cpp @@ -32,7 +32,7 @@ App * App::Snapshot::unpack(Container * container) { } void App::Snapshot::reset() { - m_store.deleteAllPairs(); + m_store.deleteAllPairsOfAllSeries(); m_store.setDefault(); m_modelVersion = 0; m_rangeVersion = 0; diff --git a/apps/shared/float_pair_store.cpp b/apps/shared/float_pair_store.cpp index e9a5e0938..d5328cf79 100644 --- a/apps/shared/float_pair_store.cpp +++ b/apps/shared/float_pair_store.cpp @@ -12,13 +12,21 @@ void FloatPairStore::set(double f, int series, int i, int j) { return; } m_data[series][i][j] = f; - if (j >= m_numberOfPairs) { + if (j >= m_numberOfPairs[series]) { int otherI = i == 0 ? 1 : 0; - m_data[series][otherI][j] = defaultValue(otherI, j); + m_data[series][otherI][j] = defaultValue(series, otherI, j); m_numberOfPairs[series]++; } } +int FloatPairStore::numberOfPairs() const { + int result = 0; + for (int i = 0; i < k_numberOfSeries; i++) { + result += m_numberOfPairs[i]; + } + return result; +} + void FloatPairStore::deletePairAtIndex(int series, int j) { m_numberOfPairs[series]--; for (int k = j; k < m_numberOfPairs[series]; k++) { @@ -27,8 +35,8 @@ void FloatPairStore::deletePairAtIndex(int series, int j) { } /* We reset the values of the empty row to ensure the correctness of the * checksum. */ - m_data[series][0][m_numberOfPairs] = 0; - m_data[series][1][m_numberOfPairs] = 0; + m_data[series][0][m_numberOfPairs[series]] = 0; + m_data[series][1][m_numberOfPairs[series]] = 0; } void FloatPairStore::deleteAllPairs(int series) { @@ -41,15 +49,21 @@ void FloatPairStore::deleteAllPairs(int series) { m_numberOfPairs[series] = 0; } +void FloatPairStore::deleteAllPairsOfAllSeries() { + for (int i = 0; i < k_numberOfSeries; i ++) { + deleteAllPairs(i); + } +} + void FloatPairStore::resetColumn(int series, int i) { assert(series >= 0 && series < k_numberOfSeries); assert(i == 0 || i == 1); for (int k = 0; k < m_numberOfPairs[series]; k++) { - m_data[series][i][k] = defaultValue(i, k); + m_data[series][i][k] = defaultValue(series, i, k); } } -double FloatPairStore::sumOfColumn(int series, int i) { +double FloatPairStore::sumOfColumn(int series, int i) const { assert(series >= 0 && series < k_numberOfSeries); assert(i == 0 || i == 1); double result = 0; @@ -72,7 +86,7 @@ uint32_t FloatPairStore::storeChecksum() { double FloatPairStore::defaultValue(int series, int i, int j) { assert(series >= 0 && series < k_numberOfSeries); if(i == 0 && j > 1) { - return 2*m_data[series][i][j-1]-m_data[i][j-2]; + return 2*m_data[series][i][j-1]-m_data[series][i][j-2]; } else { return 0.0; } diff --git a/apps/shared/float_pair_store.h b/apps/shared/float_pair_store.h index 2a642c3f8..8d41a4c55 100644 --- a/apps/shared/float_pair_store.h +++ b/apps/shared/float_pair_store.h @@ -21,11 +21,12 @@ public: return m_data[series][i][j]; } void set(double f, int series, int i, int j); - int numberOfPairsOfSeries(int series) const { return m_numberOfPairs[series]; } + int numberOfPairs() const; void deletePairAtIndex(int series, int j); void deleteAllPairs(int series); + void deleteAllPairsOfAllSeries(); void resetColumn(int series, int i); - double sumOfColumn(int series, int i); + double sumOfColumn(int series, int i) const; uint32_t storeChecksum(); protected: virtual double defaultValue(int series, int i, int j); diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index de34846a7..81ed0e1d5 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -21,7 +21,7 @@ const char * StoreController::title() { } int StoreController::numberOfColumns() { - return k_numberOfColumnsPerSeries * k_numberOfSeries; + return k_numberOfColumnsPerSeries * FloatPairStore::k_numberOfSeries; } KDCoordinate StoreController::columnWidth(int i) { @@ -52,10 +52,7 @@ HighlightCell * StoreController::reusableCell(int index, int type) { } int StoreController::reusableCellCount(int type) { - if (type == k_titleCellType) { - return k_numberOfTitleCells; - } - return k_maxNumberOfEditableCells; + return type == k_titleCellType ? k_numberOfTitleCells : k_maxNumberOfEditableCells; } int StoreController::typeAtLocation(int i, int j) { @@ -81,8 +78,10 @@ bool StoreController::handleEvent(Ion::Events::Event event) { return true; } assert(selectedColumn() >= 0 && selectedColumn() < numberOfColumns()); + int series = selectedColumn()/k_numberOfColumnsPerSeries; if ((event == Ion::Events::OK || event == Ion::Events::EXE) && selectedRow() == 0) { m_storeParameterController.selectXColumn(selectedColumn()%k_numberOfColumnsPerSeries == 0); + m_storeParameterController.selectSeries(series); StackViewController * stack = ((StackViewController *)parentResponder()->parentResponder()); stack->push(&m_storeParameterController); return true; @@ -91,7 +90,7 @@ bool StoreController::handleEvent(Ion::Events::Event event) { if (selectedRow() == 0 || selectedRow() == numberOfRows()-1) { return false; } - m_store->deletePairAtIndex(selectedRow()-1); + m_store->deletePairAtIndex(series, selectedRow()-1); selectableTableView()->reloadData(); return true; } @@ -110,12 +109,12 @@ bool StoreController::cellAtLocationIsEditable(int columnIndex, int rowIndex) { } bool StoreController::setDataAtLocation(double floatBody, int columnIndex, int rowIndex) { - m_store->set(floatBody, columnIndex, rowIndex-1); + m_store->set(floatBody, columnIndex/k_numberOfColumnsPerSeries, columnIndex%k_numberOfColumnsPerSeries, rowIndex-1); return true; } double StoreController::dataAtLocation(int columnIndex, int rowIndex) { - return m_store->get(columnIndex, rowIndex-1); + return m_store->get(columnIndex/k_numberOfColumnsPerSeries, columnIndex%k_numberOfColumnsPerSeries, rowIndex-1); } int StoreController::numberOfElements() { @@ -123,7 +122,7 @@ int StoreController::numberOfElements() { } int StoreController::maxNumberOfElements() const { - return FloatPairStore::k_maxNumberOfPairs; + return FloatPairStore::k_numberOfSeries * FloatPairStore::k_maxNumberOfPairs; } View * StoreController::loadView() { diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index e50375f94..0c815f0ae 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -23,11 +23,10 @@ public: bool handleEvent(Ion::Events::Event event) override; void didBecomeFirstResponder() override; protected: - static constexpr KDCoordinate k_cellWidth = Ion::Display::Width/2 - Metric::CommonRightMargin/2 - Metric::CommonLeftMargin/2; - constexpr static int k_numberOfSeries = 3; + static constexpr KDCoordinate k_cellWidth = 80; //TODO constexpr static int k_numberOfColumnsPerSeries = 2; - constexpr static int k_maxNumberOfEditableCells = 22 * k_numberOfSeries; - constexpr static int k_numberOfTitleCells = k_numberOfColumnsPerSeries * k_numberOfSeries; + constexpr static int k_maxNumberOfEditableCells = 22 * FloatPairStore::k_numberOfSeries; + constexpr static int k_numberOfTitleCells = k_numberOfColumnsPerSeries * FloatPairStore::k_numberOfSeries; // TODO Put finer number of cells static constexpr int k_titleCellType = 0; static constexpr int k_editableCellType = 1; diff --git a/apps/shared/store_parameter_controller.cpp b/apps/shared/store_parameter_controller.cpp index 354547045..34fd99c57 100644 --- a/apps/shared/store_parameter_controller.cpp +++ b/apps/shared/store_parameter_controller.cpp @@ -12,7 +12,8 @@ StoreParameterController::StoreParameterController(Responder * parentResponder, #endif m_selectableTableView(this, this, this), m_store(store), - m_xColumnSelected(true) + m_xColumnSelected(true), + m_series(0) { } @@ -31,9 +32,9 @@ bool StoreParameterController::handleEvent(Ion::Events::Event event) { case 0: { if (m_xColumnSelected) { - m_store->deleteAllPairs(); + m_store->deleteAllPairs(m_series); } else { - m_store->resetColumn(!m_xColumnSelected); + m_store->resetColumn(m_series, !m_xColumnSelected); } StackViewController * stack = ((StackViewController *)parentResponder()); stack->pop(); diff --git a/apps/shared/store_parameter_controller.h b/apps/shared/store_parameter_controller.h index 3dc5b0abf..3efdf94a5 100644 --- a/apps/shared/store_parameter_controller.h +++ b/apps/shared/store_parameter_controller.h @@ -11,6 +11,7 @@ class StoreParameterController : public ViewController, public SimpleListViewDat public: StoreParameterController(Responder * parentResponder, FloatPairStore * store); void selectXColumn(bool xColumnSelected) { m_xColumnSelected = xColumnSelected; } + void selectSeries(int series) { m_series = series; } View * view() override { return &m_selectableTableView; } const char * title() override; bool handleEvent(Ion::Events::Event event) override; @@ -31,6 +32,7 @@ private: SelectableTableView m_selectableTableView; FloatPairStore * m_store; bool m_xColumnSelected; + int m_series; }; } diff --git a/apps/statistics/app.cpp b/apps/statistics/app.cpp index e437ae87b..c25367ba3 100644 --- a/apps/statistics/app.cpp +++ b/apps/statistics/app.cpp @@ -33,7 +33,7 @@ App * App::Snapshot::unpack(Container * container) { } void App::Snapshot::reset() { - m_store.deleteAllPairs(); + m_store.deleteAllPairsOfAllSeries(); m_storeVersion = 0; m_barVersion = 0; m_rangeVersion = 0; diff --git a/apps/statistics/box_controller.cpp b/apps/statistics/box_controller.cpp index 77dd73868..2bc47470e 100644 --- a/apps/statistics/box_controller.cpp +++ b/apps/statistics/box_controller.cpp @@ -11,7 +11,8 @@ BoxController::BoxController(Responder * parentResponder, ButtonRowController * ButtonRowDelegate(header, nullptr), m_boxBannerView(), m_view(store, &m_boxBannerView, selectedQuantile), - m_store(store) + m_store(store), + m_selectedSeries(0) { } @@ -46,8 +47,10 @@ void BoxController::didBecomeFirstResponder() { } bool BoxController::isEmpty() const { - if (m_store->sumOfColumn(1) == 0) { - return true; + for (int i = 0; i < Shared::FloatPairStore::k_numberOfSeries; i++) { + if (m_store->sumOfOccurrences(i) == 0) { + return true; + } } return false; } @@ -70,7 +73,7 @@ void BoxController::reloadBannerView() { char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; CalculPointer calculationMethods[5] = {&Store::minValue, &Store::firstQuartile, &Store::median, &Store::thirdQuartile, &Store::maxValue}; - double calculation = (m_store->*calculationMethods[(int)m_view.selectedQuantile()])(); + double calculation = (m_store->*calculationMethods[(int)m_view.selectedQuantile()])(m_selectedSeries); PrintFloat::convertFloatToText(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); m_boxBannerView.setLegendAtIndex(buffer, 1); } diff --git a/apps/statistics/box_controller.h b/apps/statistics/box_controller.h index 810c8a693..bfbb9389f 100644 --- a/apps/statistics/box_controller.h +++ b/apps/statistics/box_controller.h @@ -26,6 +26,7 @@ private: BoxBannerView m_boxBannerView; BoxView m_view; Store * m_store; + int m_selectedSeries; }; } diff --git a/apps/statistics/box_range.cpp b/apps/statistics/box_range.cpp index be087e243..53d298083 100644 --- a/apps/statistics/box_range.cpp +++ b/apps/statistics/box_range.cpp @@ -8,15 +8,15 @@ BoxRange::BoxRange(Store * store) : } float BoxRange::xMin() { - float min = m_store->minValue(); - float max = m_store->maxValue(); + float min = m_store->minValueForAllSeries(); + float max = m_store->maxValueForAllSeries(); max = min >= max ? min + 1 : max; return min - k_displayLeftMarginRatio*(max-min); } float BoxRange::xMax() { - float min = m_store->minValue(); - float max = m_store->maxValue(); + float min = m_store->minValueForAllSeries(); + float max = m_store->maxValueForAllSeries(); max = min >= max ? min + 1 : max; return max + k_displayRightMarginRatio*(max - min); } diff --git a/apps/statistics/box_view.cpp b/apps/statistics/box_view.cpp index c1b3ab61c..8904dae73 100644 --- a/apps/statistics/box_view.cpp +++ b/apps/statistics/box_view.cpp @@ -11,7 +11,8 @@ BoxView::BoxView(Store * store, BannerView * bannerView, Quantile * selectedQuan m_store(store), m_boxRange(BoxRange(store)), m_labels{}, - m_selectedQuantile(selectedQuantile) + m_selectedQuantile(selectedQuantile), + m_series(0) { } @@ -19,7 +20,7 @@ void BoxView::reload() { CurveView::reload(); CalculPointer calculationMethods[5] = {&Store::minValue, &Store::firstQuartile, &Store::median, &Store::thirdQuartile, &Store::maxValue}; - float calculation = (m_store->*calculationMethods[(int)*m_selectedQuantile])(); + float calculation = (m_store->*calculationMethods[(int)*m_selectedQuantile])(m_series); float pixelUpperBound = floatToPixel(Axis::Vertical, 0.2f)+1; float pixelLowerBound = floatToPixel(Axis::Vertical, 0.8)-1; float selectedValueInPixels = floatToPixel(Axis::Horizontal, calculation)-1; @@ -49,18 +50,23 @@ void BoxView::drawRect(KDContext * ctx, KDRect rect) const { drawLabels(ctx, rect, Axis::Horizontal, false); float lowBound = 0.35f; float upBound = 0.65f; + double minVal = m_store->minValue(m_series); + double firstQuart = m_store->firstQuartile(m_series); + double thirdQuart = m_store->thirdQuartile(m_series); + double maxVal = m_store->maxValue(m_series); + // Draw the main box - KDCoordinate firstQuartilePixels = std::round(floatToPixel(Axis::Horizontal, m_store->firstQuartile())); - KDCoordinate thirdQuartilePixels = std::round(floatToPixel(Axis::Horizontal, m_store->thirdQuartile())); + KDCoordinate firstQuartilePixels = std::round(floatToPixel(Axis::Horizontal, firstQuart)); + KDCoordinate thirdQuartilePixels = std::round(floatToPixel(Axis::Horizontal, thirdQuart)); KDCoordinate lowBoundPixel = std::round(floatToPixel(Axis::Vertical, upBound)); KDCoordinate upBoundPixel = std::round(floatToPixel(Axis::Vertical, lowBound)); ctx->fillRect(KDRect(firstQuartilePixels, lowBoundPixel, thirdQuartilePixels - firstQuartilePixels+2, upBoundPixel-lowBoundPixel), Palette::GreyWhite); // Draw the horizontal lines linking the box to the extreme bounds - drawSegment(ctx, rect, Axis::Horizontal, 0.5f, m_store->minValue(), m_store->firstQuartile(), Palette::GreyDark); - drawSegment(ctx, rect, Axis::Horizontal, 0.5f, m_store->thirdQuartile(), m_store->maxValue(), Palette::GreyDark); + drawSegment(ctx, rect, Axis::Horizontal, 0.5f, minVal, firstQuart, Palette::GreyDark); + drawSegment(ctx, rect, Axis::Horizontal, 0.5f, thirdQuart, maxVal, Palette::GreyDark); - double calculations[5] = {m_store->minValue(), m_store->firstQuartile(), m_store->median(), m_store->thirdQuartile(), m_store->maxValue()}; + double calculations[5] = {minVal, firstQuart, m_store->median(m_series), thirdQuart, maxVal}; /* We then draw all the vertical lines of the box and then recolor the * the selected quantile (if there is one). As two quantiles can have the same * value, we cannot choose line colors and then color only once the vertical diff --git a/apps/statistics/box_view.h b/apps/statistics/box_view.h index a156297e8..cc4e79764 100644 --- a/apps/statistics/box_view.h +++ b/apps/statistics/box_view.h @@ -23,6 +23,7 @@ public: void reload() override; Quantile selectedQuantile(); bool selectQuantile(int selectedQuantile); + void setSeries(int series) { m_series = series; } void drawRect(KDContext * ctx, KDRect rect) const override; private: char * label(Axis axis, int index) const override; @@ -30,6 +31,7 @@ private: BoxRange m_boxRange; char m_labels[k_maxNumberOfXLabels][Poincare::PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits)]; Quantile * m_selectedQuantile; + int m_series; }; } diff --git a/apps/statistics/calculation_controller.cpp b/apps/statistics/calculation_controller.cpp index d65b771b6..fbcbcc249 100644 --- a/apps/statistics/calculation_controller.cpp +++ b/apps/statistics/calculation_controller.cpp @@ -47,10 +47,7 @@ void CalculationController::didBecomeFirstResponder() { } bool CalculationController::isEmpty() const { - if (m_store->sumOfColumn(1) == 0) { - return true; - } - return false; + return m_store->isEmpty(); } I18n::Message CalculationController::emptyMessage() { @@ -81,7 +78,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int CalculPointer calculationMethods[k_totalNumberOfRows] = {&Store::sumOfOccurrences, &Store::minValue, &Store::maxValue, &Store::range, &Store::mean, &Store::standardDeviation, &Store::variance, &Store::firstQuartile, &Store::thirdQuartile, &Store::median, &Store::quartileRange, &Store::sum, &Store::squaredValueSum, &Store::sampleStandardDeviation}; - double calculation = (m_store->*calculationMethods[j])(); + double calculation = (m_store->*calculationMethods[j])(0); //TODO EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)cell; char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; PrintFloat::convertFloatToText(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index e61893ab0..e79eb597a 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -100,7 +100,7 @@ void HistogramController::didBecomeFirstResponder() { if (!m_view.isMainViewSelected()) { header()->setSelectedButton(0); } else { - m_view.setHighlight(m_store->startOfBarAtIndex(*m_selectedBarIndex), m_store->endOfBarAtIndex(*m_selectedBarIndex)); + m_view.setHighlight(m_store->startOfBarAtIndex(0, *m_selectedBarIndex), m_store->endOfBarAtIndex(0, *m_selectedBarIndex)); //TODO } } @@ -115,10 +115,7 @@ Button * HistogramController::buttonAtIndex(int index, ButtonRowController::Posi } bool HistogramController::isEmpty() const { - if (m_store->sumOfColumn(1) == 0) { - return true; - } - return false; + return m_store->isEmpty(); } I18n::Message HistogramController::emptyMessage() { @@ -161,13 +158,13 @@ void HistogramController::reloadBannerView() { numberOfChar += legendLength; // Add lower bound - double lowerBound = m_store->startOfBarAtIndex(*m_selectedBarIndex); + double lowerBound = m_store->startOfBarAtIndex(0, *m_selectedBarIndex); //TODO numberOfChar += PrintFloat::convertFloatToText(lowerBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); buffer[numberOfChar++] = ';'; // Add upper bound - double upperBound = m_store->endOfBarAtIndex(*m_selectedBarIndex); + double upperBound = m_store->endOfBarAtIndex(0, *m_selectedBarIndex); //TODO numberOfChar += PrintFloat::convertFloatToText(upperBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); buffer[numberOfChar++] = '['; @@ -185,7 +182,7 @@ void HistogramController::reloadBannerView() { legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; - double size = m_store->heightOfBarAtIndex(*m_selectedBarIndex); + double size = m_store->heightOfBarAtIndex(0, *m_selectedBarIndex); //TODO numberOfChar += PrintFloat::convertFloatToText(size, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); // Padding for (int i = numberOfChar; i < k_maxLegendLength; i++) { @@ -200,7 +197,7 @@ void HistogramController::reloadBannerView() { legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; - double frequency = size/m_store->sumOfColumn(1); + double frequency = size/m_store->sumOfOccurrences(0); //TODO numberOfChar += PrintFloat::convertFloatToText(frequency, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); // Padding for (int i = numberOfChar; i < k_maxLegendLength; i++) { @@ -215,16 +212,16 @@ bool HistogramController::moveSelection(int deltaIndex) { if (deltaIndex > 0) { do { newSelectedBarIndex++; - } while (m_store->heightOfBarAtIndex(newSelectedBarIndex) == 0 && newSelectedBarIndex < m_store->numberOfBars()); + } while (m_store->heightOfBarAtIndex(0, newSelectedBarIndex) == 0 && newSelectedBarIndex < m_store->numberOfBars(0)); //TODO } else { do { newSelectedBarIndex--; - } while (m_store->heightOfBarAtIndex(newSelectedBarIndex) == 0 && newSelectedBarIndex >= 0); + } while (m_store->heightOfBarAtIndex(0, newSelectedBarIndex) == 0 && newSelectedBarIndex >= 0); //TODO } - if (newSelectedBarIndex >= 0 && newSelectedBarIndex < m_store->numberOfBars() && *m_selectedBarIndex != newSelectedBarIndex) { + if (newSelectedBarIndex >= 0 && newSelectedBarIndex < m_store->numberOfBars(0) && *m_selectedBarIndex != newSelectedBarIndex) {//TODO *m_selectedBarIndex = newSelectedBarIndex; - m_view.setHighlight(m_store->startOfBarAtIndex(*m_selectedBarIndex), m_store->endOfBarAtIndex(*m_selectedBarIndex)); - m_store->scrollToSelectedBarIndex(*m_selectedBarIndex); + m_view.setHighlight(m_store->startOfBarAtIndex(0, *m_selectedBarIndex), m_store->endOfBarAtIndex(0, *m_selectedBarIndex)); //TODO + m_store->scrollToSelectedBarIndex(0, *m_selectedBarIndex);//TODO return true; } return false; @@ -232,7 +229,7 @@ bool HistogramController::moveSelection(int deltaIndex) { void HistogramController::initRangeParameters() { float min = m_store->firstDrawnBarAbscissa(); - float max = m_store->maxValue(); + float max = m_store->maxValue(0); //TODO float barWidth = m_store->barWidth(); float xMin = min; float xMax = max + barWidth; @@ -247,21 +244,21 @@ void HistogramController::initRangeParameters() { m_store->setXMin(xMin - Store::k_displayLeftMarginRatio*(xMax-xMin)); m_store->setXMax(xMax + Store::k_displayRightMarginRatio*(xMax-xMin)); float yMax = -FLT_MAX; - for (int index = 0; index < m_store->numberOfBars(); index++) { - float size = m_store->heightOfBarAtIndex(index); + for (int index = 0; index < m_store->numberOfBars(0); index++) { //TODO + float size = m_store->heightOfBarAtIndex(0, index); //TODO if (size > yMax) { yMax = size; } } - yMax = yMax/m_store->sumOfColumn(1); + yMax = yMax/m_store->sumOfOccurrences(0); //TODO yMax = yMax < 0 ? 1 : yMax; m_store->setYMin(-Store::k_displayBottomMarginRatio*yMax); m_store->setYMax(yMax*(1.0f+Store::k_displayTopMarginRatio)); } void HistogramController::initBarParameters() { - float min = m_store->minValue(); - float max = m_store->maxValue(); + float min = m_store->minValue(0); //TODO + float max = m_store->maxValue(0); //TODO max = min >= max ? min + std::pow(10.0f, std::floor(std::log10(std::fabs(min)))-1.0f) : max; m_store->setFirstDrawnBarAbscissa(min); float barWidth = m_store->computeGridUnit(CurveViewRange::Axis::X, min, max); @@ -273,18 +270,18 @@ void HistogramController::initBarParameters() { void HistogramController::initBarSelection() { *m_selectedBarIndex = 0; - while ((m_store->heightOfBarAtIndex(*m_selectedBarIndex) == 0 || - m_store->startOfBarAtIndex(*m_selectedBarIndex) < m_store->firstDrawnBarAbscissa()) && *m_selectedBarIndex < m_store->numberOfBars()) { + while ((m_store->heightOfBarAtIndex(0, *m_selectedBarIndex) == 0 || //TODO + m_store->startOfBarAtIndex(0, *m_selectedBarIndex) < m_store->firstDrawnBarAbscissa()) && *m_selectedBarIndex < m_store->numberOfBars(0)) {//TODO *m_selectedBarIndex = *m_selectedBarIndex+1; } - if (*m_selectedBarIndex >= m_store->numberOfBars()) { + if (*m_selectedBarIndex >= m_store->numberOfBars(0)) { //TODO /* No bar is after m_firstDrawnBarAbscissa, so we select the first bar */ *m_selectedBarIndex = 0; - while (m_store->heightOfBarAtIndex(*m_selectedBarIndex) == 0 && *m_selectedBarIndex < m_store->numberOfBars()) { + while (m_store->heightOfBarAtIndex(0, *m_selectedBarIndex) == 0 && *m_selectedBarIndex < m_store->numberOfBars(0)) { //TODO *m_selectedBarIndex = *m_selectedBarIndex+1; } } - m_store->scrollToSelectedBarIndex(*m_selectedBarIndex); + m_store->scrollToSelectedBarIndex(0, *m_selectedBarIndex);//TODO } } diff --git a/apps/statistics/histogram_parameter_controller.cpp b/apps/statistics/histogram_parameter_controller.cpp index 387633b3a..e8c277737 100644 --- a/apps/statistics/histogram_parameter_controller.cpp +++ b/apps/statistics/histogram_parameter_controller.cpp @@ -43,18 +43,62 @@ double HistogramParameterController::parameterAtIndex(int index) { bool HistogramParameterController::setParameterAtIndex(int parameterIndex, double f) { assert(parameterIndex >= 0 && parameterIndex < k_numberOfCells); if (parameterIndex == 0) { - double newNumberOfBars = std::ceil((m_store->maxValue() - m_store->minValue())/f); - if (f <= 0.0f || newNumberOfBars > Store::k_maxNumberOfBars || m_store->firstDrawnBarAbscissa() > m_store->maxValue()+f) { + // The bar width cannot be negative + if (f <= 0.0f) { app()->displayWarning(I18n::Message::ForbiddenValue); return false; } + + // There should be at least one value in the drawn bin + for (int i = 0; i < FloatPairStore::k_numberOfSeries; i++) { + if (m_store->firstDrawnBarAbscissa() <= m_store->maxValue(i)+f) { + break; + } else if (i == FloatPairStore::k_numberOfSeries - 1) { + app()->displayWarning(I18n::Message::ForbiddenValue); + return false; + } + } + + // The number of bars cannot be above the max + assert(FloatPairStore::k_numberOfSeries > 0); + double maxNewNumberOfBars = std::ceil((m_store->maxValue(0) - m_store->minValue(0))/f); + for (int i = 1; i < FloatPairStore::k_numberOfSeries; i++) { + double numberOfBars = std::ceil((m_store->maxValue(i) - m_store->minValue(i))/f); + if (maxNewNumberOfBars < numberOfBars) { + maxNewNumberOfBars = numberOfBars; + } + } + if (maxNewNumberOfBars > Store::k_maxNumberOfBars) { + app()->displayWarning(I18n::Message::ForbiddenValue); + return false; + } + + // Set the bar width m_store->setBarWidth(f); } else { - double newNumberOfBars = ceilf((m_store->maxValue() - f)/m_store->barWidth()); - if (newNumberOfBars > Store::k_maxNumberOfBars || f > m_store->maxValue()+m_store->barWidth()) { + // The number of bars cannot be above the max + assert(FloatPairStore::k_numberOfSeries > 0); + double maxNewNumberOfBars = ceilf((m_store->maxValue(0) - f)/m_store->barWidth()); + for (int i = 1; i < FloatPairStore::k_numberOfSeries; i++) { + double numberOfBars = ceilf((m_store->maxValue(i) - f)/m_store->barWidth()); + if (maxNewNumberOfBars < numberOfBars) { + maxNewNumberOfBars = numberOfBars; + } + } + if (maxNewNumberOfBars > Store::k_maxNumberOfBars) { app()->displayWarning(I18n::Message::ForbiddenValue); return false; } + // There should be at least one value in the drawn bin + for (int i = 0; i < FloatPairStore::k_numberOfSeries; i++) { + if (f <= m_store->maxValue(i)+m_store->barWidth()) { + break; + } else if (i == FloatPairStore::k_numberOfSeries - 1) { + app()->displayWarning(I18n::Message::ForbiddenValue); + return false; + } + } + // Set the first drawn bar abscissa m_store->setFirstDrawnBarAbscissa(f); } return true; diff --git a/apps/statistics/histogram_view.cpp b/apps/statistics/histogram_view.cpp index a1af3b496..0f94927d3 100644 --- a/apps/statistics/histogram_view.cpp +++ b/apps/statistics/histogram_view.cpp @@ -32,11 +32,12 @@ void HistogramView::drawRect(KDContext * ctx, KDRect rect) const { drawLabels(ctx, rect, Axis::Horizontal, false); /* We memoize the total size to avoid recomputing it in double precision at * every call to EvaluateHistogramAtAbscissa() */ - float totalSize = m_store->sumOfColumn(1); + float totalSize = m_store->sumOfColumn(0, 1); // TODO Draw all the histograms + float context[] = {totalSize, 0}; if (isMainViewSelected()) { - drawHistogram(ctx, rect, EvaluateHistogramAtAbscissa, m_store, &totalSize, m_store->firstDrawnBarAbscissa(), m_store->barWidth(), true, Palette::Select, Palette::YellowDark, m_highlightedBarStart, m_highlightedBarEnd); + drawHistogram(ctx, rect, EvaluateHistogramAtAbscissa, m_store, context, m_store->firstDrawnBarAbscissa(), m_store->barWidth(), true, Palette::Select, Palette::YellowDark, m_highlightedBarStart, m_highlightedBarEnd); } else { - drawHistogram(ctx, rect, EvaluateHistogramAtAbscissa, m_store, &totalSize, m_store->firstDrawnBarAbscissa(), m_store->barWidth(), true, Palette::GreyMiddle, Palette::YellowDark); + drawHistogram(ctx, rect, EvaluateHistogramAtAbscissa, m_store, context, m_store->firstDrawnBarAbscissa(), m_store->barWidth(), true, Palette::GreyMiddle, Palette::YellowDark); } } @@ -58,8 +59,9 @@ char * HistogramView::label(Axis axis, int index) const { float HistogramView::EvaluateHistogramAtAbscissa(float abscissa, void * model, void * context) { Store * store = (Store *)model; - float * totalSize = (float *)context; - return store->heightOfBarAtValue(abscissa)/(*totalSize); + float totalSize = ((float *)context)[0]; + int series = ((float *)context)[1]; + return store->heightOfBarAtValue(series, abscissa)/(totalSize); } } diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 32bb60478..26ada9855 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -33,7 +33,7 @@ void Store::setBarWidth(double barWidth) { } double Store::heightOfBarAtIndex(int series, int index) { - return sumOfValuesBetween(startOfBarAtIndex(series, index), endOfBarAtIndex(series, index)); + return sumOfValuesBetween(series, startOfBarAtIndex(series, index), endOfBarAtIndex(series, index)); } double Store::heightOfBarAtValue(int series, double value) { @@ -76,15 +76,48 @@ bool Store::scrollToSelectedBarIndex(int series, int index) { return false; } +bool Store::isEmpty() { + for (int i = 0; i < k_numberOfSeries; i ++) { + if (sumOfOccurrences(i) > 0) { + return false; + } + } + return true; +} + /* Calculation */ double Store::sumOfOccurrences(int series) { return sumOfColumn(series, 1); } +double Store::maxValueForAllSeries() { + assert(FloatPairStore::k_numberOfSeries > 0); + double result = maxValue(0); + for (int i = 1; i < FloatPairStore::k_numberOfSeries; i++) { + double maxCurrentSeries = maxValue(i); + if (result < maxCurrentSeries) { + result = maxCurrentSeries; + } + } + return result; +} + +double Store::minValueForAllSeries() { + assert(FloatPairStore::k_numberOfSeries > 0); + double result = minValue(0); + for (int i = 1; i < FloatPairStore::k_numberOfSeries; i++) { + double minCurrentSeries = minValue(i); + if (result > minCurrentSeries) { + result = minCurrentSeries; + } + } + return result; +} + double Store::maxValue(int series) { double max = -DBL_MAX; - for (int k = 0; k < m_numberOfPairs; k++) { + for (int k = 0; k < m_numberOfPairs[series]; k++) { if (m_data[series][0][k] > max && m_data[series][1][k] > 0) { max = m_data[series][0][k]; } @@ -94,7 +127,7 @@ double Store::maxValue(int series) { double Store::minValue(int series) { double min = DBL_MAX; - for (int k = 0; k < m_numberOfPairs; k++) { + for (int k = 0; k < m_numberOfPairs[series]; k++) { if (m_data[series][0][k] < min && m_data[series][1][k] > 0) { min = m_data[series][0][k]; } @@ -136,7 +169,7 @@ double Store::thirdQuartile(int series) { } double Store::quartileRange(int series) { - return thirdQuartile()-firstQuartile(); + return thirdQuartile(series)-firstQuartile(series); } double Store::median(int series) { @@ -154,7 +187,7 @@ double Store::median(int series) { double Store::sum(int series) { double result = 0; - for (int k = 0; k < m_numberOfPairs; k++) { + for (int k = 0; k < m_numberOfPairs[series]; k++) { result += m_data[series][0][k]*m_data[series][1][k]; } return result; @@ -162,7 +195,7 @@ double Store::sum(int series) { double Store::squaredValueSum(int series) { double result = 0; - for (int k = 0; k < m_numberOfPairs; k++) { + for (int k = 0; k < m_numberOfPairs[series]; k++) { result += m_data[series][0][k]*m_data[series][0][k]*m_data[series][1][k]; } return result; @@ -174,7 +207,7 @@ double Store::defaultValue(int series, int i, int j) { return i == 0 ? FloatPairStore::defaultValue(series, i, j) : 1.0; } -double Store::sumOfValuesBetween(double x1, double x2) { +double Store::sumOfValuesBetween(int series, double x1, double x2) { double result = 0; for (int k = 0; k < m_numberOfPairs[series]; k++) { if (m_data[series][0][k] < x2 && x1 <= m_data[series][0][k]) { diff --git a/apps/statistics/store.h b/apps/statistics/store.h index f8406c963..1bcd40633 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -14,7 +14,7 @@ public: double barWidth() const { return m_barWidth; } void setBarWidth(double barWidth); double firstDrawnBarAbscissa() const { return m_firstDrawnBarAbscissa; } - void setFirstDrawnBarAbscissa(double firstDrawnBarAbscissa) { m_firstDrawnBarAbscissa = firstBarAbscissa;} + void setFirstDrawnBarAbscissa(double firstDrawnBarAbscissa) { m_firstDrawnBarAbscissa = firstDrawnBarAbscissa;} double heightOfBarAtIndex(int series, int index); double heightOfBarAtValue(int series, double value); double startOfBarAtIndex(int series, int index); @@ -22,9 +22,12 @@ public: double numberOfBars(int series); // return true if the window has scrolled bool scrollToSelectedBarIndex(int series, int index); + bool isEmpty(); // Calculation double sumOfOccurrences(int series); + double maxValueForAllSeries(); + double minValueForAllSeries(); double maxValue(int series); double minValue(int series); double range(int series); @@ -53,7 +56,7 @@ private: double m_firstDrawnBarAbscissa; }; -typedef double (Store::*CalculPointer)(); +typedef double (Store::*CalculPointer)(int); }