From dfca5d84386cae2fb69436322d4a78e7bc72ed54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 10:06:28 +0200 Subject: [PATCH 001/156] [apps/statistics] Allow non integer value sizes --- apps/statistics/store.cpp | 2 +- apps/statistics/store_controller.cpp | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 122ed2c25..140ded211 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -192,7 +192,7 @@ double Store::defaultValue(int i, int j) { } double Store::sumOfValuesBetween(double x1, double x2) { - int result = 0; + double result = 0; for (int k = 0; k < m_numberOfPairs; k++) { if (m_data[0][k] < x2 && x1 <= m_data[0][k]) { result += m_data[1][k]; diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index 0b9a8714a..05ccab7a3 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -42,8 +42,6 @@ bool StoreController::setDataAtLocation(double floatBody, int columnIndex, int r if (floatBody < 0) { return false; } - m_store->set(std::round(floatBody), columnIndex, rowIndex-1); - return true; } return Shared::StoreController::setDataAtLocation(floatBody, columnIndex, rowIndex); } From cb67aa535de061191b792f733ec035e2bf1d32e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 10:33:04 +0200 Subject: [PATCH 002/156] [apps/shared] Create static ints for cell types --- apps/shared/store_controller.cpp | 8 ++++---- apps/shared/store_controller.h | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index e3aeb7633..e332a0385 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -39,10 +39,10 @@ int StoreController::indexFromCumulatedWidth(KDCoordinate offsetX) { HighlightCell * StoreController::reusableCell(int index, int type) { assert(index >= 0); switch (type) { - case 0: + case k_titleCellType: assert(index < k_numberOfTitleCells); return titleCells(index); - case 1: + case k_editableCellType: assert(index < k_maxNumberOfEditableCells); return m_editableCells[index]; default: @@ -52,14 +52,14 @@ HighlightCell * StoreController::reusableCell(int index, int type) { } int StoreController::reusableCellCount(int type) { - if (type == 0) { + if (type == k_titleCellType) { return k_numberOfTitleCells; } return k_maxNumberOfEditableCells; } int StoreController::typeAtLocation(int i, int j) { - return j!=0; + return j == 0 ? k_titleCellType : k_editableCellType; } void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 915dad585..0bdf2b44d 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -26,6 +26,8 @@ protected: static constexpr KDCoordinate k_cellWidth = Ion::Display::Width/2 - Metric::CommonRightMargin/2 - Metric::CommonLeftMargin/2; constexpr static int k_maxNumberOfEditableCells = 22; constexpr static int k_numberOfTitleCells = 2; + static constexpr int k_titleCellType = 0; + static constexpr int k_editableCellType = 1; Responder * tabController() const override; View * loadView() override; void unloadView(View * view) override; From 40d541bc1c87da67ce232f9c2a9cca61e0e7e215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 11:17:52 +0200 Subject: [PATCH 003/156] [apps] Display 3 series in Regression and Stats --- apps/regression/store_controller.cpp | 11 ++++++++--- apps/regression/store_controller.h | 2 +- apps/shared/store_controller.cpp | 9 +++++---- apps/shared/store_controller.h | 7 +++++-- apps/statistics/base.de.i18n | 8 ++++++-- apps/statistics/base.en.i18n | 8 ++++++-- apps/statistics/base.es.i18n | 8 ++++++-- apps/statistics/base.fr.i18n | 8 ++++++-- apps/statistics/base.pt.i18n | 8 ++++++-- apps/statistics/store_controller.cpp | 13 +++++++++---- 10 files changed, 58 insertions(+), 24 deletions(-) diff --git a/apps/regression/store_controller.cpp b/apps/regression/store_controller.cpp index e08bb358d..280689011 100644 --- a/apps/regression/store_controller.cpp +++ b/apps/regression/store_controller.cpp @@ -16,12 +16,17 @@ StoreController::StoreController(Responder * parentResponder, Store * store, But Shared::StoreController(parentResponder, store, header), m_titleCells{} { - m_titleLayout[0] = new HorizontalLayout(new CharLayout('X', KDText::FontSize::Small), new VerticalOffsetLayout(new CharLayout('i', KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false), false); - m_titleLayout[1] = new HorizontalLayout(new CharLayout('Y', KDText::FontSize::Small), new VerticalOffsetLayout(new CharLayout('i', KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false), false); + for (int i = 0; i < k_numberOfSeries; i++) { + /* If the index is too big, the layout creation should take into account the + * possibility of a two-digits index. */ + assert(k_numberOfSeries < 10); + m_titleLayout[k_numberOfColumnsPerSeries*i] = new HorizontalLayout(new CharLayout('X', KDText::FontSize::Small), new VerticalOffsetLayout(new CharLayout('0'+ i, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false), false); + m_titleLayout[k_numberOfColumnsPerSeries*i+1] = new HorizontalLayout(new CharLayout('Y', KDText::FontSize::Small), new VerticalOffsetLayout(new CharLayout('0' + i, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false), false); + } } StoreController::~StoreController() { - for (int i = 0; i < 2; i++) { + for (int i = 0; i < k_numberOfTitleCells; i++) { if (m_titleLayout[i]) { delete m_titleLayout[i]; m_titleLayout[i] = nullptr; diff --git a/apps/regression/store_controller.h b/apps/regression/store_controller.h index ecbb76244..822f3a4ca 100644 --- a/apps/regression/store_controller.h +++ b/apps/regression/store_controller.h @@ -21,7 +21,7 @@ private: View * loadView() override; void unloadView(View * view) override; EvenOddExpressionCell * m_titleCells[k_numberOfTitleCells]; - Poincare::ExpressionLayout * m_titleLayout[2]; + Poincare::ExpressionLayout * m_titleLayout[k_numberOfTitleCells]; }; } diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index e332a0385..de34846a7 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -21,8 +21,8 @@ const char * StoreController::title() { } int StoreController::numberOfColumns() { - return 2; -}; + return k_numberOfColumnsPerSeries * k_numberOfSeries; +} KDCoordinate StoreController::columnWidth(int i) { return k_cellWidth; @@ -67,7 +67,7 @@ void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int } void StoreController::didBecomeFirstResponder() { - if (selectedRow() < 0) { + if (selectedRow() < 0 || selectedColumn() < 0) { selectCellAtLocation(0, 0); } EditableCellTableViewController::didBecomeFirstResponder(); @@ -80,8 +80,9 @@ bool StoreController::handleEvent(Ion::Events::Event event) { app()->setFirstResponder(tabController()); return true; } + assert(selectedColumn() >= 0 && selectedColumn() < numberOfColumns()); if ((event == Ion::Events::OK || event == Ion::Events::EXE) && selectedRow() == 0) { - m_storeParameterController.selectXColumn(selectedColumn() == 0); + m_storeParameterController.selectXColumn(selectedColumn()%k_numberOfColumnsPerSeries == 0); StackViewController * stack = ((StackViewController *)parentResponder()->parentResponder()); stack->push(&m_storeParameterController); return true; diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 0bdf2b44d..e50375f94 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -24,8 +24,11 @@ public: void didBecomeFirstResponder() override; protected: static constexpr KDCoordinate k_cellWidth = Ion::Display::Width/2 - Metric::CommonRightMargin/2 - Metric::CommonLeftMargin/2; - constexpr static int k_maxNumberOfEditableCells = 22; - constexpr static int k_numberOfTitleCells = 2; + constexpr static int k_numberOfSeries = 3; + constexpr static int k_numberOfColumnsPerSeries = 2; + constexpr static int k_maxNumberOfEditableCells = 22 * k_numberOfSeries; + constexpr static int k_numberOfTitleCells = k_numberOfColumnsPerSeries * k_numberOfSeries; + // TODO Put finer number of cells static constexpr int k_titleCellType = 0; static constexpr int k_editableCellType = 1; Responder * tabController() const override; diff --git a/apps/statistics/base.de.i18n b/apps/statistics/base.de.i18n index a91532061..aebe0c9f1 100644 --- a/apps/statistics/base.de.i18n +++ b/apps/statistics/base.de.i18n @@ -3,8 +3,12 @@ StatsAppCapital = "STATISTIKEN" HistogramTab = "Histogramm" BoxTab = "Boxplot" StatTab = "Stats" -Values = "Werte" -Sizes = "Haufigkeiten" +Values1 = "Werte V1" +Values2 = "Werte V2" +Values3 = "Werte V3" +Sizes1 = "Haufigkeiten N1" +Sizes2 = "Haufigkeiten N2" +Sizes3 = "Haufigkeiten N3" ImportList = "Laden eine Liste" NoDataToPlot = "Keine Daten zu zeichnen" Interval = " Intervall" diff --git a/apps/statistics/base.en.i18n b/apps/statistics/base.en.i18n index 63e7f7496..107fcc7d2 100644 --- a/apps/statistics/base.en.i18n +++ b/apps/statistics/base.en.i18n @@ -3,8 +3,12 @@ StatsAppCapital = "STATISTICS" HistogramTab = "Histogram" BoxTab = "Box" StatTab = "Stats" -Values = "Values" -Sizes = "Sizes" +Values1 = "Values V1" +Values2 = "Values V2" +Values3 = "Values V3" +Sizes1 = "Sizes N1" +Sizes2 = "Sizes N2" +Sizes3 = "Sizes N3" ImportList = "Import from a list" NoDataToPlot = "No data to draw" Interval = " Interval " diff --git a/apps/statistics/base.es.i18n b/apps/statistics/base.es.i18n index 62d33c1e7..08faa7bc1 100644 --- a/apps/statistics/base.es.i18n +++ b/apps/statistics/base.es.i18n @@ -3,8 +3,12 @@ StatsAppCapital = "ESTADISTICA" HistogramTab = "Histograma" BoxTab = "Caja" StatTab = "Medidas" -Values = "Valores" -Sizes = "Frecuencias" +Values1 = "Valores V1" +Values2 = "Valores V2" +Values3 = "Valores V3" +Sizes1 = "Frecuencias N1" +Sizes2 = "Frecuencias N2" +Sizes3 = "Frecuencias N3" ImportList = "Importar una lista" NoDataToPlot = "Ningunos datos que dibujar" Interval = " Intervalo" diff --git a/apps/statistics/base.fr.i18n b/apps/statistics/base.fr.i18n index 3a03235ea..ad4e9d212 100644 --- a/apps/statistics/base.fr.i18n +++ b/apps/statistics/base.fr.i18n @@ -3,8 +3,12 @@ StatsAppCapital = "STATISTIQUES" HistogramTab = "Histogramme" BoxTab = "Boite" StatTab = "Stats" -Values = "Valeurs" -Sizes = "Effectifs" +Values1 = "Valeurs V1" +Values2 = "Valeurs V2" +Values3 = "Valeurs V3" +Sizes1 = "Effectifs N1" +Sizes2 = "Effectifs N2" +Sizes3 = "Effectifs N3" ImportList = "Importer une liste" NoDataToPlot = "Aucune donnee a tracer" Interval = " Intervalle " diff --git a/apps/statistics/base.pt.i18n b/apps/statistics/base.pt.i18n index 7e3dd4ea1..bf634f523 100644 --- a/apps/statistics/base.pt.i18n +++ b/apps/statistics/base.pt.i18n @@ -3,8 +3,12 @@ StatsAppCapital = "ESTATISTICA" HistogramTab = "Histograma" BoxTab = "Caixa" StatTab = "Estat" -Values = "Valores" -Sizes = "Frequencias" +Values1 = "Valores V1" +Values2 = "Valores V2" +Values3 = "Valores V3" +Sizes1 = "Frequencias N1" +Sizes2 = "Frequencias N2" +Sizes3 = "Frequencias N3" ImportList = "Importar de uma lista" NoDataToPlot = "Nao ha dados para desenhar" Interval = " Intervalo" diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index 05ccab7a3..8e1ccfb21 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -22,11 +22,16 @@ void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int return; } EvenOddMessageTextCell * mytitleCell = (EvenOddMessageTextCell *)cell; - if (i == 0) { - mytitleCell->setMessage(I18n::Message::Values); - return; + bool valuesColumn = i%k_numberOfColumnsPerSeries == 0; + int seriesIndex = i/k_numberOfColumnsPerSeries; + assert(seriesIndex >= 0 && seriesIndex < FloatPairStore::k_numberOfSeries); + if (valuesColumn) { + I18n::Message valuesMessages[] = {I18n::Message::Values1, I18n::Message::Values2, I18n::Message::Values3}; + mytitleCell->setMessage(valuesMessages[seriesIndex]); + } else { + I18n::Message sizesMessages[] = {I18n::Message::Sizes1, I18n::Message::Sizes2, I18n::Message::Sizes3}; + mytitleCell->setMessage(sizesMessages[seriesIndex]); } - mytitleCell->setMessage(I18n::Message::Sizes); } HighlightCell * StoreController::titleCells(int index) { From 9e47b62775cf1ac9f458086b9b89bca79864889d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 11:58:45 +0200 Subject: [PATCH 004/156] [apps] Inline statistics/store.h --- apps/statistics/store.cpp | 23 +++-------------------- apps/statistics/store.h | 6 +++--- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 140ded211..80de1e5cc 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -26,23 +26,10 @@ uint32_t Store::barChecksum() { /* Histogram bars */ -double Store::barWidth() { - return m_barWidth; -} - void Store::setBarWidth(double barWidth) { - if (barWidth <= 0.0) { - return; + if (barWidth > 0.0) { + m_barWidth = barWidth; } - m_barWidth = barWidth; -} - -double Store::firstDrawnBarAbscissa() { - return m_firstDrawnBarAbscissa; -} - -void Store::setFirstDrawnBarAbscissa(double firstBarAbscissa) { - m_firstDrawnBarAbscissa = firstBarAbscissa; } double Store::heightOfBarAtIndex(int index) { @@ -184,11 +171,7 @@ double Store::squaredValueSum() { /* private methods */ double Store::defaultValue(int i, int j) { - if (i == 0) { - return FloatPairStore::defaultValue(i, j); - } else { - return 1.0; - } + return i == 0 ? FloatPairStore::defaultValue(i, j) : 1.0; } double Store::sumOfValuesBetween(double x1, double x2) { diff --git a/apps/statistics/store.h b/apps/statistics/store.h index c627b4c50..634d3116d 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -11,10 +11,10 @@ public: Store(); uint32_t barChecksum(); // Histogram bars - double barWidth(); + double barWidth() const { return m_barWidth; } void setBarWidth(double barWidth); - double firstDrawnBarAbscissa(); - void setFirstDrawnBarAbscissa(double firstDrawnBarAbscissa); + double firstDrawnBarAbscissa() const { return m_firstDrawnBarAbscissa; } + void setFirstDrawnBarAbscissa(double firstDrawnBarAbscissa) { m_firstDrawnBarAbscissa = firstBarAbscissa;} double heightOfBarAtIndex(int index); double heightOfBarAtValue(double value); double startOfBarAtIndex(int index); From 600f5307c430191274470bc1a55d3bdfb33c2b60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 12:10:34 +0200 Subject: [PATCH 005/156] [apps] Use sumOfOccurrences() in statistics/store.cpp --- apps/statistics/store.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 80de1e5cc..d70136d53 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -107,12 +107,12 @@ double Store::range() { } double Store::mean() { - return sum()/sumOfColumn(1); + return sum()/sumOfOccurrences(); } double Store::variance() { double m = mean(); - return squaredValueSum()/sumOfColumn(1) - m*m; + return squaredValueSum()/sumOfOccurrences() - m*m; } double Store::standardDeviation() { @@ -120,18 +120,18 @@ double Store::standardDeviation() { } double Store::sampleStandardDeviation() { - double n = sumOfColumn(1); + double n = sumOfOccurrences(); double s = std::sqrt(n/(n-1.0)); return s*standardDeviation(); } double Store::firstQuartile() { - int firstQuartileIndex = std::ceil(sumOfColumn(1)/4); + int firstQuartileIndex = std::ceil(sumOfOccurrences()/4); return sortedElementNumber(firstQuartileIndex); } double Store::thirdQuartile() { - int thirdQuartileIndex = std::ceil(3*sumOfColumn(1)/4); + int thirdQuartileIndex = std::ceil(3*sumOfOccurrences()/4); return sortedElementNumber(thirdQuartileIndex); } @@ -140,7 +140,7 @@ double Store::quartileRange() { } double Store::median() { - int total = sumOfColumn(1); + int total = sumOfOccurrences(); int halfTotal = total/2; int totalMod2 = total - 2*halfTotal; if (totalMod2 == 0) { From 33fa32d55fd1606fa326cbe9fa5b2116ac9e5332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 12:21:57 +0200 Subject: [PATCH 006/156] [apps] Multiple data series in FloatPairStore --- apps/shared/float_pair_store.cpp | 72 ++++++++---------- apps/shared/float_pair_store.h | 32 +++++--- apps/statistics/store.cpp | 124 +++++++++++++++---------------- apps/statistics/store.h | 46 ++++++------ 4 files changed, 137 insertions(+), 137 deletions(-) diff --git a/apps/shared/float_pair_store.cpp b/apps/shared/float_pair_store.cpp index c03e64e67..e9a5e0938 100644 --- a/apps/shared/float_pair_store.cpp +++ b/apps/shared/float_pair_store.cpp @@ -6,64 +6,55 @@ namespace Shared { -FloatPairStore::FloatPairStore() : - m_numberOfPairs(0), - m_data{} -{ -} - -double FloatPairStore::get(int i, int j) { - assert(j < m_numberOfPairs); - return m_data[i][j]; -} - -void FloatPairStore::set(double f, int i, int j) { +void FloatPairStore::set(double f, int series, int i, int j) { + assert(series >= 0 && series < k_numberOfSeries); if (j >= k_maxNumberOfPairs) { return; } - m_data[i][j] = f; + m_data[series][i][j] = f; if (j >= m_numberOfPairs) { int otherI = i == 0 ? 1 : 0; - m_data[otherI][j] = defaultValue(otherI, j); - m_numberOfPairs++; + m_data[series][otherI][j] = defaultValue(otherI, j); + m_numberOfPairs[series]++; } } -int FloatPairStore::numberOfPairs() { - return m_numberOfPairs; -} - -void FloatPairStore::deletePairAtIndex(int i) { - m_numberOfPairs--; - for (int k = i; k < m_numberOfPairs; k++) { - m_data[0][k] = m_data[0][k+1]; - m_data[1][k] = m_data[1][k+1]; +void FloatPairStore::deletePairAtIndex(int series, int j) { + m_numberOfPairs[series]--; + for (int k = j; k < m_numberOfPairs[series]; k++) { + m_data[series][0][k] = m_data[series][0][k+1]; + m_data[series][1][k] = m_data[series][1][k+1]; } /* We reset the values of the empty row to ensure the correctness of the * checksum. */ - m_data[0][m_numberOfPairs] = 0; - m_data[1][m_numberOfPairs] = 0; + m_data[series][0][m_numberOfPairs] = 0; + m_data[series][1][m_numberOfPairs] = 0; } -void FloatPairStore::deleteAllPairs() { +void FloatPairStore::deleteAllPairs(int series) { + assert(series >= 0 && series < k_numberOfSeries); /* We reset all values to 0 to ensure the correctness of the checksum.*/ - for (int k = 0; k < m_numberOfPairs; k++) { - m_data[0][k] = 0; - m_data[1][k] = 0; + for (int k = 0; k < m_numberOfPairs[series]; k++) { + m_data[series][0][k] = 0; + m_data[series][1][k] = 0; } - m_numberOfPairs = 0; + m_numberOfPairs[series] = 0; } -void FloatPairStore::resetColumn(int i) { - for (int k = 0; k < m_numberOfPairs; k++) { - m_data[i][k] = defaultValue(i, k); +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); } } -double FloatPairStore::sumOfColumn(int i) { +double FloatPairStore::sumOfColumn(int series, int i) { + assert(series >= 0 && series < k_numberOfSeries); + assert(i == 0 || i == 1); double result = 0; - for (int k = 0; k < m_numberOfPairs; k++) { - result += m_data[i][k]; + for (int k = 0; k < m_numberOfPairs[series]; k++) { + result += m_data[series][i][k]; } return result; } @@ -73,14 +64,15 @@ uint32_t FloatPairStore::storeChecksum() { * pairs. However, the two values of a pair are not stored consecutively. We * thus compute the checksum on all pairs and ensure to set the pair at 0 * when removing them. */ - size_t dataLengthInBytes = k_maxNumberOfPairs*2*sizeof(double); + size_t dataLengthInBytes = k_numberOfSeries*k_maxNumberOfPairs*2*sizeof(double); assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4 return Ion::crc32((uint32_t *)m_data, dataLengthInBytes/sizeof(uint32_t)); } -double FloatPairStore::defaultValue(int i, int j) { +double FloatPairStore::defaultValue(int series, int i, int j) { + assert(series >= 0 && series < k_numberOfSeries); if(i == 0 && j > 1) { - return 2*m_data[i][j-1]-m_data[i][j-2]; + return 2*m_data[series][i][j-1]-m_data[i][j-2]; } else { return 0.0; } diff --git a/apps/shared/float_pair_store.h b/apps/shared/float_pair_store.h index 6efdfe840..2a642c3f8 100644 --- a/apps/shared/float_pair_store.h +++ b/apps/shared/float_pair_store.h @@ -2,27 +2,35 @@ #define SHARED_FLOAT_PAIR_STORE_H #include +#include namespace Shared { class FloatPairStore { public: - FloatPairStore(); + constexpr static int k_numberOfSeries = 3; + constexpr static int k_maxNumberOfPairs = 100; + FloatPairStore() : + m_numberOfPairs{}, + m_data{} + {} // Delete the implicit copy constructor: the object is heavy FloatPairStore(const FloatPairStore&) = delete; - double get(int i, int j); - void set(double f, int i, int j); - int numberOfPairs(); - void deletePairAtIndex(int j); - void deleteAllPairs(); - void resetColumn(int i); - double sumOfColumn(int i); + double get(int series, int i, int j) const { + assert(j < m_numberOfPairs[series]); + 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]; } + void deletePairAtIndex(int series, int j); + void deleteAllPairs(int series); + void resetColumn(int series, int i); + double sumOfColumn(int series, int i); uint32_t storeChecksum(); - constexpr static int k_maxNumberOfPairs = 100; protected: - virtual double defaultValue(int i, int j); - int m_numberOfPairs; - double m_data[2][k_maxNumberOfPairs]; + virtual double defaultValue(int series, int i, int j); + int m_numberOfPairs[k_numberOfSeries]; + double m_data[k_numberOfSeries][2][k_maxNumberOfPairs]; }; } diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index d70136d53..32bb60478 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -32,34 +32,34 @@ void Store::setBarWidth(double barWidth) { } } -double Store::heightOfBarAtIndex(int index) { - return sumOfValuesBetween(startOfBarAtIndex(index), endOfBarAtIndex(index)); +double Store::heightOfBarAtIndex(int series, int index) { + return sumOfValuesBetween(startOfBarAtIndex(series, index), endOfBarAtIndex(series, index)); } -double Store::heightOfBarAtValue(double value) { +double Store::heightOfBarAtValue(int series, double value) { double width = barWidth(); int barNumber = std::floor((value - m_firstDrawnBarAbscissa)/width); double lowerBound = m_firstDrawnBarAbscissa + barNumber*width; double upperBound = m_firstDrawnBarAbscissa + (barNumber+1)*width; - return sumOfValuesBetween(lowerBound, upperBound); + return sumOfValuesBetween(series, lowerBound, upperBound); } -double Store::startOfBarAtIndex(int index) { - double firstBarAbscissa = m_firstDrawnBarAbscissa + m_barWidth*std::floor((minValue()- m_firstDrawnBarAbscissa)/m_barWidth); +double Store::startOfBarAtIndex(int series, int index) { + double firstBarAbscissa = m_firstDrawnBarAbscissa + m_barWidth*std::floor((minValue(series)- m_firstDrawnBarAbscissa)/m_barWidth); return firstBarAbscissa + index * m_barWidth; } -double Store::endOfBarAtIndex(int index) { - return startOfBarAtIndex(index+1); +double Store::endOfBarAtIndex(int series, int index) { + return startOfBarAtIndex(series, index+1); } -double Store::numberOfBars() { - double firstBarAbscissa = m_firstDrawnBarAbscissa + m_barWidth*std::floor((minValue()- m_firstDrawnBarAbscissa)/m_barWidth); - return std::ceil((maxValue() - firstBarAbscissa)/m_barWidth)+1; +double Store::numberOfBars(int series) { + double firstBarAbscissa = m_firstDrawnBarAbscissa + m_barWidth*std::floor((minValue(series)- m_firstDrawnBarAbscissa)/m_barWidth); + return std::ceil((maxValue(series) - firstBarAbscissa)/m_barWidth)+1; } -bool Store::scrollToSelectedBarIndex(int index) { - float startSelectedBar = startOfBarAtIndex(index); +bool Store::scrollToSelectedBarIndex(int series, int index) { + float startSelectedBar = startOfBarAtIndex(series, index); float windowRange = m_xMax - m_xMin; float range = windowRange/(1+k_displayLeftMarginRatio+k_displayRightMarginRatio); if (m_xMin + k_displayLeftMarginRatio*range > startSelectedBar) { @@ -67,7 +67,7 @@ bool Store::scrollToSelectedBarIndex(int index) { m_xMax = m_xMin + windowRange; return true; } - float endSelectedBar = endOfBarAtIndex(index); + float endSelectedBar = endOfBarAtIndex(series, index); if (endSelectedBar > m_xMax - k_displayRightMarginRatio*range) { m_xMax = endSelectedBar + k_displayRightMarginRatio*range; m_xMin = m_xMax - windowRange; @@ -78,124 +78,124 @@ bool Store::scrollToSelectedBarIndex(int index) { /* Calculation */ -double Store::sumOfOccurrences() { - return sumOfColumn(1); +double Store::sumOfOccurrences(int series) { + return sumOfColumn(series, 1); } -double Store::maxValue() { +double Store::maxValue(int series) { double max = -DBL_MAX; for (int k = 0; k < m_numberOfPairs; k++) { - if (m_data[0][k] > max && m_data[1][k] > 0) { - max = m_data[0][k]; + if (m_data[series][0][k] > max && m_data[series][1][k] > 0) { + max = m_data[series][0][k]; } } return max; } -double Store::minValue() { +double Store::minValue(int series) { double min = DBL_MAX; for (int k = 0; k < m_numberOfPairs; k++) { - if (m_data[0][k] < min && m_data[1][k] > 0) { - min = m_data[0][k]; + if (m_data[series][0][k] < min && m_data[series][1][k] > 0) { + min = m_data[series][0][k]; } } return min; } -double Store::range() { - return maxValue()-minValue(); +double Store::range(int series) { + return maxValue(series)-minValue(series); } -double Store::mean() { - return sum()/sumOfOccurrences(); +double Store::mean(int series) { + return sum(series)/sumOfOccurrences(series); } -double Store::variance() { - double m = mean(); - return squaredValueSum()/sumOfOccurrences() - m*m; +double Store::variance(int series) { + double m = mean(series); + return squaredValueSum(series)/sumOfOccurrences(series) - m*m; } -double Store::standardDeviation() { - return std::sqrt(variance()); +double Store::standardDeviation(int series) { + return std::sqrt(variance(series)); } -double Store::sampleStandardDeviation() { - double n = sumOfOccurrences(); +double Store::sampleStandardDeviation(int series) { + double n = sumOfOccurrences(series); double s = std::sqrt(n/(n-1.0)); - return s*standardDeviation(); + return s*standardDeviation(series); } -double Store::firstQuartile() { - int firstQuartileIndex = std::ceil(sumOfOccurrences()/4); - return sortedElementNumber(firstQuartileIndex); +double Store::firstQuartile(int series) { + int firstQuartileIndex = std::ceil(sumOfOccurrences(series)/4); + return sortedElementNumber(series, firstQuartileIndex); } -double Store::thirdQuartile() { - int thirdQuartileIndex = std::ceil(3*sumOfOccurrences()/4); - return sortedElementNumber(thirdQuartileIndex); +double Store::thirdQuartile(int series) { + int thirdQuartileIndex = std::ceil(3*sumOfOccurrences(series)/4); + return sortedElementNumber(series, thirdQuartileIndex); } -double Store::quartileRange() { +double Store::quartileRange(int series) { return thirdQuartile()-firstQuartile(); } -double Store::median() { - int total = sumOfOccurrences(); +double Store::median(int series) { + int total = sumOfOccurrences(series); int halfTotal = total/2; int totalMod2 = total - 2*halfTotal; if (totalMod2 == 0) { - double minusMedian = sortedElementNumber(halfTotal); - double maxMedian = sortedElementNumber(halfTotal+1); + double minusMedian = sortedElementNumber(series, halfTotal); + double maxMedian = sortedElementNumber(series, halfTotal+1); return (minusMedian+maxMedian)/2.0; } else { - return sortedElementNumber(halfTotal+1); + return sortedElementNumber(series, halfTotal+1); } } -double Store::sum() { +double Store::sum(int series) { double result = 0; for (int k = 0; k < m_numberOfPairs; k++) { - result += m_data[0][k]*m_data[1][k]; + result += m_data[series][0][k]*m_data[series][1][k]; } return result; } -double Store::squaredValueSum() { +double Store::squaredValueSum(int series) { double result = 0; for (int k = 0; k < m_numberOfPairs; k++) { - result += m_data[0][k]*m_data[0][k]*m_data[1][k]; + result += m_data[series][0][k]*m_data[series][0][k]*m_data[series][1][k]; } return result; } -/* private methods */ +/* Private methods */ -double Store::defaultValue(int i, int j) { - return i == 0 ? FloatPairStore::defaultValue(i, j) : 1.0; +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 result = 0; - for (int k = 0; k < m_numberOfPairs; k++) { - if (m_data[0][k] < x2 && x1 <= m_data[0][k]) { - result += m_data[1][k]; + for (int k = 0; k < m_numberOfPairs[series]; k++) { + if (m_data[series][0][k] < x2 && x1 <= m_data[series][0][k]) { + result += m_data[series][1][k]; } } return result; } -double Store::sortedElementNumber(int k) { +double Store::sortedElementNumber(int series, int k) { // TODO: use an other algorithm (ex quickselect) to avoid quadratic complexity - double bufferValues[m_numberOfPairs]; - memcpy(bufferValues, m_data[0], m_numberOfPairs*sizeof(double)); + double bufferValues[m_numberOfPairs[series]]; + memcpy(bufferValues, m_data[series][0], m_numberOfPairs[series]*sizeof(double)); int sortedElementIndex = 0; double cumulatedSize = 0.0; while (cumulatedSize < k) { - sortedElementIndex = minIndex(bufferValues, m_numberOfPairs); + sortedElementIndex = minIndex(bufferValues, m_numberOfPairs[series]); bufferValues[sortedElementIndex] = DBL_MAX; - cumulatedSize += m_data[1][sortedElementIndex]; + cumulatedSize += m_data[series][1][sortedElementIndex]; } - return m_data[0][sortedElementIndex]; + return m_data[series][0][sortedElementIndex]; } int Store::minIndex(double * bufferValues, int bufferLength) { diff --git a/apps/statistics/store.h b/apps/statistics/store.h index 634d3116d..f8406c963 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -15,38 +15,38 @@ public: void setBarWidth(double barWidth); double firstDrawnBarAbscissa() const { return m_firstDrawnBarAbscissa; } void setFirstDrawnBarAbscissa(double firstDrawnBarAbscissa) { m_firstDrawnBarAbscissa = firstBarAbscissa;} - double heightOfBarAtIndex(int index); - double heightOfBarAtValue(double value); - double startOfBarAtIndex(int index); - double endOfBarAtIndex(int index); - double numberOfBars(); + double heightOfBarAtIndex(int series, int index); + double heightOfBarAtValue(int series, double value); + double startOfBarAtIndex(int series, int index); + double endOfBarAtIndex(int series, int index); + double numberOfBars(int series); // return true if the window has scrolled - bool scrollToSelectedBarIndex(int index); + bool scrollToSelectedBarIndex(int series, int index); // Calculation - double sumOfOccurrences(); - double maxValue(); - double minValue(); - double range(); - double mean(); - double variance(); - double standardDeviation(); - double sampleStandardDeviation(); - double firstQuartile(); - double thirdQuartile(); - double quartileRange(); - double median(); - double sum(); - double squaredValueSum(); + double sumOfOccurrences(int series); + double maxValue(int series); + double minValue(int series); + double range(int series); + double mean(int series); + double variance(int series); + double standardDeviation(int series); + double sampleStandardDeviation(int series); + double firstQuartile(int series); + double thirdQuartile(int series); + double quartileRange(int series); + double median(int series); + double sum(int series); + double squaredValueSum(int series); constexpr static double k_maxNumberOfBars = 10000.0; constexpr static float k_displayTopMarginRatio = 0.1f; constexpr static float k_displayRightMarginRatio = 0.04f; constexpr static float k_displayBottomMarginRatio = 0.4f; constexpr static float k_displayLeftMarginRatio = 0.04f; private: - double defaultValue(int i, int j) override; - double sumOfValuesBetween(double x1, double x2); - double sortedElementNumber(int k); + double defaultValue(int series, int i, int j) override; + double sumOfValuesBetween(int series, double x1, double x2); + double sortedElementNumber(int series, int k); int minIndex(double * bufferValues, int bufferLength); // Histogram bars double m_barWidth; From 25713dc95093c58563642c60124e794b31190de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 12:30:07 +0200 Subject: [PATCH 007/156] [apps] Clean and inline regression/store --- apps/regression/store.cpp | 51 +++++++++++++++------------------------ apps/regression/store.h | 2 +- 2 files changed, 20 insertions(+), 33 deletions(-) diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index 93937fb25..af5ff04af 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -38,10 +38,12 @@ int Store::closestVerticalDot(int direction, float x) { } } // Compare with the mean dot - if (m_xMin <= meanOfColumn(0) && meanOfColumn(0) <= m_xMax && - (std::fabs(meanOfColumn(0) - x) < std::fabs(nextX - x)) && - ((meanOfColumn(1) - yValueForXValue(meanOfColumn(0)) >= 0) == (direction > 0))) { - if (nextX != meanOfColumn(0) || ((nextY - meanOfColumn(1) >= 0) == (direction > 0))) { + double meanX = meanOfColumn(0); + double meanY = meanOfColumn(1); + if (m_xMin <= meanX && meanX <= m_xMax && + (std::fabs(meanX - x) < std::fabs(nextX - x)) && + ((meanY - yValueForXValue(meanX) >= 0) == (direction > 0))) { + if (nextX != meanX || ((nextY - meanY >= 0) == (direction > 0))) { selectedDot = m_numberOfPairs; } } @@ -51,7 +53,8 @@ int Store::closestVerticalDot(int direction, float x) { int Store::nextDot(int direction, int dot) { float nextX = INFINITY; int selectedDot = -1; - float x = meanOfColumn(0); + double meanX = meanOfColumn(0); + float x = meanX; if (dot >= 0 && dot < m_numberOfPairs) { x = get(0, dot); } @@ -74,20 +77,20 @@ int Store::nextDot(int direction, int dot) { } } // Compare with the mean dot - if (std::fabs(meanOfColumn(0) - x) < std::fabs(nextX - x) && + if (std::fabs(meanX - x) < std::fabs(nextX - x) && (m_numberOfPairs != dot) && - (meanOfColumn(0) >= x)) { - if (meanOfColumn(0) != x || (x > dot)) { + (meanX >= x)) { + if (meanX != x || (x > dot)) { selectedDot = m_numberOfPairs; } } } else { // Compare with the mean dot - if (std::fabs(meanOfColumn(0) - x) < std::fabs(nextX - x) && + if (std::fabs(meanX - x) < std::fabs(nextX - x) && (m_numberOfPairs != dot) && - (meanOfColumn(0) <= x)) { - if (meanOfColumn(0) != x || (m_numberOfPairs < dot)) { - nextX = meanOfColumn(0); + (meanX <= x)) { + if (meanX != x || (m_numberOfPairs < dot)) { + nextX = meanX; selectedDot = m_numberOfPairs; } } @@ -119,10 +122,6 @@ void Store::setDefault() { /* Calculations */ -double Store::numberOfPairs() { - return m_numberOfPairs; -} - float Store::maxValueOfColumn(int i) { float max = -FLT_MAX; for (int k = 0; k < m_numberOfPairs; k++) { @@ -160,10 +159,7 @@ double Store::columnProductSum() { } double Store::meanOfColumn(int i) { - if (m_numberOfPairs == 0) { - return 0; - } - return sumOfColumn(i)/m_numberOfPairs; + return m_numberOfPairs == 0 ? 0 : sumOfColumn(i)/m_numberOfPairs; } double Store::varianceOfColumn(int i) { @@ -192,29 +188,20 @@ double Store::yValueForXValue(double x) { } double Store::xValueForYValue(double y) { - if (std::fabs(slope()) < DBL_EPSILON) { - return NAN; - } - return (y - yIntercept())/slope(); + return std::fabs(slope()) < DBL_EPSILON ? NAN : (y - yIntercept())/slope(); } double Store::correlationCoefficient() { double sd0 = standardDeviationOfColumn(0); double sd1 = standardDeviationOfColumn(1); - if (sd0 == 0.0 || sd1 == 0.0) { - return 1.0; - } - return covariance()/(sd0*sd1); + return (sd0 == 0.0 || sd1 == 0.0) ? 1.0 : covariance()/(sd0*sd1); } double Store::squaredCorrelationCoefficient() { double cov = covariance(); double v0 = varianceOfColumn(0); double v1 = varianceOfColumn(1); - if (v0 == 0.0 || v1 == 0.0) { - return 1.0; - } - return cov*cov/(v0*v1); + return (v0 == 0.0 || v1 == 0.0) ? 1.0 : cov*cov/(v0*v1); } InteractiveCurveViewRangeDelegate::Range Store::computeYRange(InteractiveCurveViewRange * interactiveCurveViewRange) { diff --git a/apps/regression/store.h b/apps/regression/store.h index c9ed07c73..9c295cc81 100644 --- a/apps/regression/store.h +++ b/apps/regression/store.h @@ -21,7 +21,7 @@ public: void setDefault() override; // Calculation - double numberOfPairs(); + double numberOfPairs() const { return m_numberOfPairs; } double squaredValueSumOfColumn(int i); double columnProductSum(); double meanOfColumn(int i); From 76371830559528ea4ab86367df4a678e7b049fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 13:19:01 +0200 Subject: [PATCH 008/156] [apps] Multiple series in regression/store --- apps/regression/store.cpp | 154 +++++++++++++++++++------------------- apps/regression/store.h | 36 ++++----- 2 files changed, 95 insertions(+), 95 deletions(-) diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index af5ff04af..8e9de77d3 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -16,7 +16,7 @@ Store::Store() : /* Dots */ -int Store::closestVerticalDot(int direction, float x) { +int Store::closestVerticalDot(int series, int direction, float x) { float nextX = INFINITY; float nextY = INFINITY; int selectedDot = -1; @@ -25,26 +25,26 @@ int Store::closestVerticalDot(int direction, float x) { * - the next dot is the closest one in abscissa to x * - the next dot is above the regression curve if direction == 1 and below * otherwise */ - for (int index = 0; index < m_numberOfPairs; index++) { - if ((m_xMin <= m_data[0][index] && m_data[0][index] <= m_xMax) && - (std::fabs(m_data[0][index] - x) < std::fabs(nextX - x)) && - ((m_data[1][index] - yValueForXValue(m_data[0][index]) >= 0) == (direction > 0))) { + for (int index = 0; index < m_numberOfPairs[series]; index++) { + if ((m_xMin <= m_data[series][0][index] && m_data[series][0][index] <= m_xMax) && + (std::fabs(m_data[series][0][index] - x) < std::fabs(nextX - x)) && + ((m_data[series][1][index] - yValueForXValue(m_data[series][0][index]) >= 0) == (direction > 0))) { // Handle edge case: if 2 dots have the same abscissa but different ordinates - if (nextX != m_data[0][index] || ((nextY - m_data[1][index] >= 0) == (direction > 0))) { - nextX = m_data[0][index]; - nextY = m_data[1][index]; + if (nextX != m_data[series][0][index] || ((nextY - m_data[series][1][index] >= 0) == (direction > 0))) { + nextX = m_data[series][0][index]; + nextY = m_data[series][1][index]; selectedDot = index; } } } // Compare with the mean dot - double meanX = meanOfColumn(0); - double meanY = meanOfColumn(1); + double meanX = meanOfColumn(series, 0); + double meanY = meanOfColumn(series, 1); if (m_xMin <= meanX && meanX <= m_xMax && (std::fabs(meanX - x) < std::fabs(nextX - x)) && ((meanY - yValueForXValue(meanX) >= 0) == (direction > 0))) { if (nextX != meanX || ((nextY - meanY >= 0) == (direction > 0))) { - selectedDot = m_numberOfPairs; + selectedDot = m_numberOfPairs[series]; } } return selectedDot; @@ -53,54 +53,54 @@ int Store::closestVerticalDot(int direction, float x) { int Store::nextDot(int direction, int dot) { float nextX = INFINITY; int selectedDot = -1; - double meanX = meanOfColumn(0); + double meanX = meanOfColumn(series, 0); float x = meanX; - if (dot >= 0 && dot < m_numberOfPairs) { + if (dot >= 0 && dot < m_numberOfPairs[series]) { x = get(0, dot); } /* We have to scan the Store in opposite ways for the 2 directions to ensure to * select all dots (even with equal abscissa) */ if (direction > 0) { - for (int index = 0; index < m_numberOfPairs; index++) { + for (int index = 0; index < m_numberOfPairs[series]; index++) { /* The conditions to test are in this order: * - the next dot is the closest one in abscissa to x * - the next dot is not the same as the selected one * - the next dot is at the right of the selected one */ - if (std::fabs(m_data[0][index] - x) < std::fabs(nextX - x) && + if (std::fabs(m_date[series][0][index] - x) < std::fabs(nextX - x) && (index != dot) && - (m_data[0][index] >= x)) { + (m_date[series][0][index] >= x)) { // Handle edge case: 2 dots have same abscissa - if (m_data[0][index] != x || (index > dot)) { - nextX = m_data[0][index]; + if (m_date[series][0][index] != x || (index > dot)) { + nextX = m_date[series][0][index]; selectedDot = index; } } } // Compare with the mean dot if (std::fabs(meanX - x) < std::fabs(nextX - x) && - (m_numberOfPairs != dot) && + (m_numberOfPairs[series] != dot) && (meanX >= x)) { if (meanX != x || (x > dot)) { - selectedDot = m_numberOfPairs; + selectedDot = m_numberOfPairs[series]; } } } else { // Compare with the mean dot if (std::fabs(meanX - x) < std::fabs(nextX - x) && - (m_numberOfPairs != dot) && + (m_numberOfPairs[series] != dot) && (meanX <= x)) { - if (meanX != x || (m_numberOfPairs < dot)) { + if (meanX != x || (m_numberOfPairs[series] < dot)) { nextX = meanX; - selectedDot = m_numberOfPairs; + selectedDot = m_numberOfPairs[series]; } } - for (int index = m_numberOfPairs-1; index >= 0; index--) { - if (std::fabs(m_data[0][index] - x) < std::fabs(nextX - x) && + for (int index = m_numberOfPairs[series]-1; index >= 0; index--) { + if (std::fabs(m_date[series][0][index] - x) < std::fabs(nextX - x) && (index != dot) && - (m_data[0][index] <= x)) { + (m_date[series][0][index] <= x)) { // Handle edge case: 2 dots have same abscissa - if (m_data[0][index] != x || (index < dot)) { - nextX = m_data[0][index]; + if (m_date[series][0][index] != x || (index < dot)) { + nextX = m_date[series][0][index]; selectedDot = index; } } @@ -111,9 +111,9 @@ int Store::nextDot(int direction, int dot) { /* Window */ -void Store::setDefault() { - float min = minValueOfColumn(0); - float max = maxValueOfColumn(0); +void Store::setDefault(int series) { + float min = minValueOfColumn(series, 0); + float max = maxValueOfColumn(series, 0); float range = max - min; setXMin(min - k_displayLeftMarginRatio*range); setXMax(max + k_displayRightMarginRatio*range); @@ -122,98 +122,98 @@ void Store::setDefault() { /* Calculations */ -float Store::maxValueOfColumn(int i) { +float Store::maxValueOfColumn(int series, int i) { float max = -FLT_MAX; - for (int k = 0; k < m_numberOfPairs; k++) { - if (m_data[i][k] > max) { - max = m_data[i][k]; + for (int k = 0; k < m_numberOfPairs[series]; k++) { + if (m_data[series][i][k] > max) { + max = m_data[series][i][k]; } } return max; } -float Store::minValueOfColumn(int i) { +float Store::minValueOfColumn(int series, int i) { float min = FLT_MAX; - for (int k = 0; k < m_numberOfPairs; k++) { - if (m_data[i][k] < min) { - min = m_data[i][k]; + for (int k = 0; k < m_numberOfPairs[series]; k++) { + if (m_data[series][i][k] < min) { + min = m_data[series][i][k]; } } return min; } -double Store::squaredValueSumOfColumn(int i) { +double Store::squaredValueSumOfColumn(int series, int i) { double result = 0; - for (int k = 0; k < m_numberOfPairs; k++) { - result += m_data[i][k]*m_data[i][k]; + for (int k = 0; k < m_numberOfPairs[series]; k++) { + result += m_data[series][i][k]*m_data[series][i][k]; } return result; } -double Store::columnProductSum() { +double Store::columnProductSum(int series) { double result = 0; - for (int k = 0; k < m_numberOfPairs; k++) { - result += m_data[0][k]*m_data[1][k]; + for (int k = 0; k < m_numberOfPairs[series]; k++) { + result += m_date[series][0][k]*m_date[series][1][k]; } return result; } -double Store::meanOfColumn(int i) { - return m_numberOfPairs == 0 ? 0 : sumOfColumn(i)/m_numberOfPairs; +double Store::meanOfColumn(int series, int i) { + return m_numberOfPairs[series] == 0 ? 0 : sumOfColumn(series, i)/m_numberOfPairs[series]; } -double Store::varianceOfColumn(int i) { - double mean = meanOfColumn(i); - return squaredValueSumOfColumn(i)/m_numberOfPairs - mean*mean; +double Store::varianceOfColumn(int series, int i) { + double mean = meanOfColumn(series, i); + return squaredValueSumOfColumn(series, i)/m_numberOfPairs[series] - mean*mean; } -double Store::standardDeviationOfColumn(int i) { - return std::sqrt(varianceOfColumn(i)); +double Store::standardDeviationOfColumn(int series, int i) { + return std::sqrt(varianceOfColumn(series, i)); } -double Store::covariance() { - return columnProductSum()/m_numberOfPairs - meanOfColumn(0)*meanOfColumn(1); +double Store::covariance(int series) { + return columnProductSum(series)/m_numberOfPairs[series] - meanOfColumn(series, 0)*meanOfColumn(series, 1); } -double Store::slope() { - return covariance()/varianceOfColumn(0); +double Store::slope(int series) { + return covariance(series)/varianceOfColumn(series, 0); } -double Store::yIntercept() { - return meanOfColumn(1) - slope()*meanOfColumn(0); +double Store::yIntercept(int series) { + return meanOfColumn(series, 1) - slope(series)*meanOfColumn(series, 0); } -double Store::yValueForXValue(double x) { - return slope()*x+yIntercept(); +double Store::yValueForXValue(int series, double x) { + return slope(series)*x+yIntercept(series); } -double Store::xValueForYValue(double y) { - return std::fabs(slope()) < DBL_EPSILON ? NAN : (y - yIntercept())/slope(); +double Store::xValueForYValue(int series, double y) { + return std::fabs(slope(series)) < DBL_EPSILON ? NAN : (y - yIntercept(series))/slope(series); } -double Store::correlationCoefficient() { - double sd0 = standardDeviationOfColumn(0); - double sd1 = standardDeviationOfColumn(1); - return (sd0 == 0.0 || sd1 == 0.0) ? 1.0 : covariance()/(sd0*sd1); +double Store::correlationCoefficient(int series) { + double sd0 = standardDeviationOfColumn(series, 0); + double sd1 = standardDeviationOfColumn(series, 1); + return (sd0 == 0.0 || sd1 == 0.0) ? 1.0 : covariance(series)/(sd0*sd1); } -double Store::squaredCorrelationCoefficient() { - double cov = covariance(); - double v0 = varianceOfColumn(0); - double v1 = varianceOfColumn(1); +double Store::squaredCorrelationCoefficient(int series) { + double cov = covariance(series); + double v0 = varianceOfColumn(series, 0); + double v1 = varianceOfColumn(series, 1); return (v0 == 0.0 || v1 == 0.0) ? 1.0 : cov*cov/(v0*v1); } -InteractiveCurveViewRangeDelegate::Range Store::computeYRange(InteractiveCurveViewRange * interactiveCurveViewRange) { +InteractiveCurveViewRangeDelegate::Range Store::computeYRange(int series, InteractiveCurveViewRange * interactiveCurveViewRange) { float min = FLT_MAX; float max = -FLT_MAX; - for (int k = 0; k < m_numberOfPairs; k++) { - if (m_xMin <= m_data[0][k] && m_data[0][k] <= m_xMax) { - if (m_data[1][k] < min) { - min = m_data[1][k]; + for (int k = 0; k < m_numberOfPairs[series]; k++) { + if (m_xMin <= m_date[series][0][k] && m_date[series][0][k] <= m_xMax) { + if (m_date[series][1][k] < min) { + min = m_date[series][1][k]; } - if (m_data[1][k] > max) { - max = m_data[1][k]; + if (m_date[series][1][k] > max) { + max = m_date[series][1][k]; } } } diff --git a/apps/regression/store.h b/apps/regression/store.h index 9c295cc81..59dd10fdc 100644 --- a/apps/regression/store.h +++ b/apps/regression/store.h @@ -12,37 +12,37 @@ public: // Dots /* Return the closest dot to x above the regression curve if direction > 0, * below otherwise*/ - int closestVerticalDot(int direction, float x); + int closestVerticalDot(int series, int direction, float x); /* Return the closest dot to dot given on the right if direction > 0, * on the left otherwise*/ - int nextDot(int direction, int dot); + int nextDot(int series, int direction, int dot); // Window void setDefault() override; // Calculation - double numberOfPairs() const { return m_numberOfPairs; } - double squaredValueSumOfColumn(int i); - double columnProductSum(); - double meanOfColumn(int i); - double varianceOfColumn(int i); - double standardDeviationOfColumn(int i); - double covariance(); - double slope(); - double yIntercept(); - double yValueForXValue(double x); - double xValueForYValue(double y); - double correlationCoefficient(); - double squaredCorrelationCoefficient(); + double numberOfPairs(int series) const { return m_numberOfPairs; } + double squaredValueSumOfColumn(int series, int i); + double columnProductSum(int series); + double meanOfColumn(int series, int i); + double varianceOfColumn(int series, int i); + double standardDeviationOfColumn(int series, int i); + double covariance(int series); + double slope(int series); + double yIntercept(int series); + double yValueForXValue(int series, double x); + double xValueForYValue(int series, double y); + double correlationCoefficient(int series); + double squaredCorrelationCoefficient(int series); private: constexpr static float k_displayTopMarginRatio = 0.12f; constexpr static float k_displayRightMarginRatio = 0.05f; constexpr static float k_displayBottomMarginRatio = 0.5f; constexpr static float k_displayLeftMarginRatio = 0.05f; - InteractiveCurveViewRangeDelegate::Range computeYRange(InteractiveCurveViewRange * interactiveCurveViewRange) override; + InteractiveCurveViewRangeDelegate::Range computeYRange(int series, InteractiveCurveViewRange * interactiveCurveViewRange) override; float addMargin(float x, float range, bool isMin) override; - float maxValueOfColumn(int i); - float minValueOfColumn(int i); + float maxValueOfColumn(int series, int i); + float minValueOfColumn(int series, int i); }; typedef double (Store::*ArgCalculPointer)(int); From a184ff95b4110ac1c562ad5b1188ca508d3f251d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 13:29:22 +0200 Subject: [PATCH 009/156] [apps] Inline shared/store_parameter_controller.h --- apps/shared/store_parameter_controller.cpp | 20 -------------------- apps/shared/store_parameter_controller.h | 10 +++++----- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/apps/shared/store_parameter_controller.cpp b/apps/shared/store_parameter_controller.cpp index dec09aa1b..354547045 100644 --- a/apps/shared/store_parameter_controller.cpp +++ b/apps/shared/store_parameter_controller.cpp @@ -16,18 +16,10 @@ StoreParameterController::StoreParameterController(Responder * parentResponder, { } -void StoreParameterController::selectXColumn(bool xColumnSelected) { - m_xColumnSelected = xColumnSelected; -} - const char * StoreParameterController::title() { return I18n::translate(I18n::Message::ColumnOptions); } -View * StoreParameterController::view() { - return &m_selectableTableView; -} - void StoreParameterController::didBecomeFirstResponder() { selectCellAtLocation(0, 0); app()->setFirstResponder(&m_selectableTableView); @@ -62,10 +54,6 @@ bool StoreParameterController::handleEvent(Ion::Events::Event event) { return false; } -int StoreParameterController::numberOfRows() { - return k_totalNumberOfCell; -}; - HighlightCell * StoreParameterController::reusableCell(int index) { assert(index >= 0); assert(index < k_totalNumberOfCell); @@ -73,12 +61,4 @@ HighlightCell * StoreParameterController::reusableCell(int index) { return cells[index]; } -int StoreParameterController::reusableCellCount() { - return k_totalNumberOfCell; -} - -KDCoordinate StoreParameterController::cellHeight() { - return Metric::ParameterCellHeight; -} - } diff --git a/apps/shared/store_parameter_controller.h b/apps/shared/store_parameter_controller.h index e8e620b63..3dc5b0abf 100644 --- a/apps/shared/store_parameter_controller.h +++ b/apps/shared/store_parameter_controller.h @@ -10,15 +10,15 @@ namespace Shared { class StoreParameterController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource { public: StoreParameterController(Responder * parentResponder, FloatPairStore * store); - void selectXColumn(bool xColumnSelected); - View * view() override; + void selectXColumn(bool xColumnSelected) { m_xColumnSelected = xColumnSelected; } + View * view() override { return &m_selectableTableView; } const char * title() override; bool handleEvent(Ion::Events::Event event) override; void didBecomeFirstResponder() override; - int numberOfRows() override; - KDCoordinate cellHeight() override; + int numberOfRows() override { return k_totalNumberOfCell; } + KDCoordinate cellHeight() override { return Metric::ParameterCellHeight; } HighlightCell * reusableCell(int index) override; - int reusableCellCount() override; + int reusableCellCount() override { return k_totalNumberOfCell; } private: #if COPY_IMPORT_LIST constexpr static int k_totalNumberOfCell = 3; 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 010/156] [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); } From 551606037d11a7f6fc26be6fb1098cb44f7620a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 15:46:46 +0200 Subject: [PATCH 011/156] [apps] Do not display empty cells in store_controller --- apps/shared/float_pair_store.cpp | 2 +- apps/shared/float_pair_store.h | 6 +++++- apps/shared/store_controller.cpp | 15 +++++++++++---- apps/shared/store_controller.h | 1 + 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/apps/shared/float_pair_store.cpp b/apps/shared/float_pair_store.cpp index d5328cf79..4f5d9c4bc 100644 --- a/apps/shared/float_pair_store.cpp +++ b/apps/shared/float_pair_store.cpp @@ -19,7 +19,7 @@ void FloatPairStore::set(double f, int series, int i, int j) { } } -int FloatPairStore::numberOfPairs() const { +int FloatPairStore::numberOfPairsOfAllSeries() const { int result = 0; for (int i = 0; i < k_numberOfSeries; i++) { result += m_numberOfPairs[i]; diff --git a/apps/shared/float_pair_store.h b/apps/shared/float_pair_store.h index 8d41a4c55..94a61032b 100644 --- a/apps/shared/float_pair_store.h +++ b/apps/shared/float_pair_store.h @@ -21,7 +21,11 @@ public: return m_data[series][i][j]; } void set(double f, int series, int i, int j); - int numberOfPairs() const; + int numberOfPairsOfAllSeries() const; + int numberOfPairs(int series) const { + assert(series >= 0 && series < k_numberOfSeries); + return m_numberOfPairs[series]; + } void deletePairAtIndex(int series, int j); void deleteAllPairs(int series); void deleteAllPairsOfAllSeries(); diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 81ed0e1d5..0716340fe 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -60,6 +60,13 @@ int StoreController::typeAtLocation(int i, int j) { } void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { + // Handle empty cells + if (j > m_store->numberOfPairs(seriesAtColumn(i)) && j < numberOfRows() - 1) { + ((EvenOddCell *)cell)->setEven(j%2 == 0); + assert(cellAtLocationIsEditable(i, j)); + ((EvenOddEditableTextCell *)cell)->editableTextCell()->textField()->setText(""); + return; + } willDisplayCellAtLocationWithDisplayMode(cell, i, j, PrintFloat::Mode::Decimal); } @@ -78,7 +85,7 @@ bool StoreController::handleEvent(Ion::Events::Event event) { return true; } assert(selectedColumn() >= 0 && selectedColumn() < numberOfColumns()); - int series = selectedColumn()/k_numberOfColumnsPerSeries; + int series = seriesAtColumn(selectedColumn()); if ((event == Ion::Events::OK || event == Ion::Events::EXE) && selectedRow() == 0) { m_storeParameterController.selectXColumn(selectedColumn()%k_numberOfColumnsPerSeries == 0); m_storeParameterController.selectSeries(series); @@ -109,16 +116,16 @@ bool StoreController::cellAtLocationIsEditable(int columnIndex, int rowIndex) { } bool StoreController::setDataAtLocation(double floatBody, int columnIndex, int rowIndex) { - m_store->set(floatBody, columnIndex/k_numberOfColumnsPerSeries, columnIndex%k_numberOfColumnsPerSeries, rowIndex-1); + m_store->set(floatBody, seriesAtColumn(columnIndex), columnIndex%k_numberOfColumnsPerSeries, rowIndex-1); return true; } double StoreController::dataAtLocation(int columnIndex, int rowIndex) { - return m_store->get(columnIndex/k_numberOfColumnsPerSeries, columnIndex%k_numberOfColumnsPerSeries, rowIndex-1); + return m_store->get(seriesAtColumn(columnIndex), columnIndex%k_numberOfColumnsPerSeries, rowIndex-1); } int StoreController::numberOfElements() { - return m_store->numberOfPairs(); + return m_store->numberOfPairsOfAllSeries(); } int StoreController::maxNumberOfElements() const { diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 0c815f0ae..0bb1ea150 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -40,6 +40,7 @@ protected: int maxNumberOfElements() const override; virtual HighlightCell * titleCells(int index) = 0; char m_draftTextBuffer[TextField::maxBufferSize()]; + int seriesAtColumn(int column) const { return column / k_numberOfColumnsPerSeries; } EvenOddEditableTextCell * m_editableCells[k_maxNumberOfEditableCells]; FloatPairStore * m_store; StoreParameterController m_storeParameterController; From 9fe7cd2a46c38016b71181c0722c9d32885b6b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 15:52:22 +0200 Subject: [PATCH 012/156] [apps] Rename FloatPairStore methods --- apps/shared/float_pair_store.cpp | 10 +++++----- apps/shared/float_pair_store.h | 10 +++++----- apps/shared/store_controller.cpp | 6 +++--- apps/shared/store_parameter_controller.cpp | 2 +- apps/statistics/app.cpp | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/shared/float_pair_store.cpp b/apps/shared/float_pair_store.cpp index 4f5d9c4bc..7e17649ca 100644 --- a/apps/shared/float_pair_store.cpp +++ b/apps/shared/float_pair_store.cpp @@ -19,7 +19,7 @@ void FloatPairStore::set(double f, int series, int i, int j) { } } -int FloatPairStore::numberOfPairsOfAllSeries() const { +int FloatPairStore::numberOfPairs() const { int result = 0; for (int i = 0; i < k_numberOfSeries; i++) { result += m_numberOfPairs[i]; @@ -27,7 +27,7 @@ int FloatPairStore::numberOfPairsOfAllSeries() const { return result; } -void FloatPairStore::deletePairAtIndex(int series, int j) { +void FloatPairStore::deletePairOfSeriesAtIndex(int series, int j) { m_numberOfPairs[series]--; for (int k = j; k < m_numberOfPairs[series]; k++) { m_data[series][0][k] = m_data[series][0][k+1]; @@ -39,7 +39,7 @@ void FloatPairStore::deletePairAtIndex(int series, int j) { m_data[series][1][m_numberOfPairs[series]] = 0; } -void FloatPairStore::deleteAllPairs(int series) { +void FloatPairStore::deleteAllPairsOfSeries(int series) { assert(series >= 0 && series < k_numberOfSeries); /* We reset all values to 0 to ensure the correctness of the checksum.*/ for (int k = 0; k < m_numberOfPairs[series]; k++) { @@ -49,9 +49,9 @@ void FloatPairStore::deleteAllPairs(int series) { m_numberOfPairs[series] = 0; } -void FloatPairStore::deleteAllPairsOfAllSeries() { +void FloatPairStore::deleteAllPairs() { for (int i = 0; i < k_numberOfSeries; i ++) { - deleteAllPairs(i); + deleteAllPairsOfSeries(i); } } diff --git a/apps/shared/float_pair_store.h b/apps/shared/float_pair_store.h index 94a61032b..4ef42604f 100644 --- a/apps/shared/float_pair_store.h +++ b/apps/shared/float_pair_store.h @@ -21,14 +21,14 @@ public: return m_data[series][i][j]; } void set(double f, int series, int i, int j); - int numberOfPairsOfAllSeries() const; - int numberOfPairs(int series) const { + int numberOfPairs() const; + int numberOfPairsOfSeries(int series) const { assert(series >= 0 && series < k_numberOfSeries); return m_numberOfPairs[series]; } - void deletePairAtIndex(int series, int j); - void deleteAllPairs(int series); - void deleteAllPairsOfAllSeries(); + void deletePairOfSeriesAtIndex(int series, int j); + void deleteAllPairsOfSeries(int series); + void deleteAllPairs(); void resetColumn(int series, int i); double sumOfColumn(int series, int i) const; uint32_t storeChecksum(); diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 0716340fe..8fd02dc23 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -61,7 +61,7 @@ int StoreController::typeAtLocation(int i, int j) { void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { // Handle empty cells - if (j > m_store->numberOfPairs(seriesAtColumn(i)) && j < numberOfRows() - 1) { + if (j > m_store->numberOfPairsOfSeries(seriesAtColumn(i)) && j < numberOfRows() - 1) { ((EvenOddCell *)cell)->setEven(j%2 == 0); assert(cellAtLocationIsEditable(i, j)); ((EvenOddEditableTextCell *)cell)->editableTextCell()->textField()->setText(""); @@ -97,7 +97,7 @@ bool StoreController::handleEvent(Ion::Events::Event event) { if (selectedRow() == 0 || selectedRow() == numberOfRows()-1) { return false; } - m_store->deletePairAtIndex(series, selectedRow()-1); + m_store->deletePairOfSeriesAtIndex(series, selectedRow()-1); selectableTableView()->reloadData(); return true; } @@ -125,7 +125,7 @@ double StoreController::dataAtLocation(int columnIndex, int rowIndex) { } int StoreController::numberOfElements() { - return m_store->numberOfPairsOfAllSeries(); + return m_store->numberOfPairs(); } int StoreController::maxNumberOfElements() const { diff --git a/apps/shared/store_parameter_controller.cpp b/apps/shared/store_parameter_controller.cpp index 34fd99c57..2bceea42e 100644 --- a/apps/shared/store_parameter_controller.cpp +++ b/apps/shared/store_parameter_controller.cpp @@ -32,7 +32,7 @@ bool StoreParameterController::handleEvent(Ion::Events::Event event) { case 0: { if (m_xColumnSelected) { - m_store->deleteAllPairs(m_series); + m_store->deleteAllPairsOfSeries(m_series); } else { m_store->resetColumn(m_series, !m_xColumnSelected); } diff --git a/apps/statistics/app.cpp b/apps/statistics/app.cpp index c25367ba3..e437ae87b 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.deleteAllPairsOfAllSeries(); + m_store.deleteAllPairs(); m_storeVersion = 0; m_barVersion = 0; m_rangeVersion = 0; From f775af59de19dd0741ee23075c1409fa184e64ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 16:40:57 +0200 Subject: [PATCH 013/156] [apps] Draw empty cells if no data in StoreController --- apps/shared/store_controller.cpp | 30 ++++++++++++++++++++++++++++-- apps/shared/store_controller.h | 1 + 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 8fd02dc23..9c437c0d5 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -16,6 +16,28 @@ StoreController::StoreController(Responder * parentResponder, FloatPairStore * s { } +bool StoreController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { + AppsContainer * appsContainer = ((TextFieldDelegateApp *)app())->container(); + Context * globalContext = appsContainer->globalContext(); + double floatBody = Expression::approximateToScalar(text, *globalContext); + if (std::isnan(floatBody) || std::isinf(floatBody)) { + app()->displayWarning(I18n::Message::UndefinedValue); + return false; + } + if (!setDataAtLocation(floatBody, selectedColumn(), selectedRow())) { + app()->displayWarning(I18n::Message::ForbiddenValue); + return false; + } + // FIXME Find out if redrawing errors can be suppressed without always reloading all the data + selectableTableView()->reloadData(); + if (event == Ion::Events::EXE || event == Ion::Events::OK) { + selectableTableView()->selectCellAtLocation(selectedColumn(), selectedRow()+1); + } else { + selectableTableView()->handleEvent(event); + } + return true; +} + const char * StoreController::title() { return I18n::translate(I18n::Message::DataTab); } @@ -61,7 +83,7 @@ int StoreController::typeAtLocation(int i, int j) { void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { // Handle empty cells - if (j > m_store->numberOfPairsOfSeries(seriesAtColumn(i)) && j < numberOfRows() - 1) { + if (j > 0 && j > m_store->numberOfPairsOfSeries(seriesAtColumn(i)) && j < numberOfRows() - 1) { ((EvenOddCell *)cell)->setEven(j%2 == 0); assert(cellAtLocationIsEditable(i, j)); ((EvenOddEditableTextCell *)cell)->editableTextCell()->textField()->setText(""); @@ -125,7 +147,11 @@ double StoreController::dataAtLocation(int columnIndex, int rowIndex) { } int StoreController::numberOfElements() { - return m_store->numberOfPairs(); + int result = 0; + for (int i = 0; i < FloatPairStore::k_numberOfSeries; i++) { + result = max(result, m_store->numberOfPairsOfSeries(i)); + } + return result; } int StoreController::maxNumberOfElements() const { diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 0bb1ea150..52329f3f4 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -11,6 +11,7 @@ namespace Shared { class StoreController : public EditableCellTableViewController, public ButtonRowDelegate { public: StoreController(Responder * parentResponder, FloatPairStore * store, ButtonRowController * header); + bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override; const char * title() override; int numberOfColumns() override; KDCoordinate columnWidth(int i) override; From 9376b7c62d839b63c73d5d34e7988eef0bd33eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 17:25:04 +0200 Subject: [PATCH 014/156] [apps/statistics] Reorganize BoxController --- apps/statistics/box_controller.cpp | 54 +++++++++++++++--------------- apps/statistics/box_controller.h | 15 ++++++--- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/apps/statistics/box_controller.cpp b/apps/statistics/box_controller.cpp index 2bc47470e..6e093e808 100644 --- a/apps/statistics/box_controller.cpp +++ b/apps/statistics/box_controller.cpp @@ -16,6 +16,23 @@ BoxController::BoxController(Responder * parentResponder, ButtonRowController * { } +bool BoxController::isEmpty() const { + for (int i = 0; i < Shared::FloatPairStore::k_numberOfSeries; i++) { + if (m_store->sumOfOccurrences(i) == 0) { + return true; + } + } + return false; +} + +I18n::Message BoxController::emptyMessage() { + return I18n::Message::NoDataToPlot; +} + +Responder * BoxController::defaultController() { + return tabController(); +} + const char * BoxController::title() { return I18n::translate(I18n::Message::BoxTab); } @@ -24,6 +41,12 @@ View * BoxController::view() { return &m_view; } +void BoxController::viewWillAppear() { + m_view.selectMainView(true); + reloadBannerView(); + m_view.reload(); +} + bool BoxController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Up) { m_view.selectMainView(false); @@ -46,21 +69,11 @@ void BoxController::didBecomeFirstResponder() { m_view.reload(); } -bool BoxController::isEmpty() const { - for (int i = 0; i < Shared::FloatPairStore::k_numberOfSeries; i++) { - if (m_store->sumOfOccurrences(i) == 0) { - return true; - } +void BoxController::willExitResponderChain(Responder * nextFirstResponder) { + if (nextFirstResponder == tabController()) { + m_view.selectMainView(false); + m_view.reload(); } - return false; -} - -I18n::Message BoxController::emptyMessage() { - return I18n::Message::NoDataToPlot; -} - -Responder * BoxController::defaultController() { - return tabController(); } Responder * BoxController::tabController() const { @@ -78,17 +91,4 @@ void BoxController::reloadBannerView() { m_boxBannerView.setLegendAtIndex(buffer, 1); } -void BoxController::viewWillAppear() { - m_view.selectMainView(true); - reloadBannerView(); - m_view.reload(); -} - -void BoxController::willExitResponderChain(Responder * nextFirstResponder) { - if (nextFirstResponder == tabController()) { - m_view.selectMainView(false); - m_view.reload(); - } -} - } diff --git a/apps/statistics/box_controller.h b/apps/statistics/box_controller.h index bfbb9389f..a9d1466fe 100644 --- a/apps/statistics/box_controller.h +++ b/apps/statistics/box_controller.h @@ -11,14 +11,21 @@ namespace Statistics { class BoxController : public ViewController, public ButtonRowDelegate, public AlternateEmptyViewDelegate { public: BoxController(Responder * parentResponder, ButtonRowController * header, Store * store, BoxView::Quantile * selectedQuantile); - const char * title() override; - View * view() override; - bool handleEvent(Ion::Events::Event event) override; - void didBecomeFirstResponder() override; + + // AlternateEmptyViewDelegate bool isEmpty() const override; I18n::Message emptyMessage() override; Responder * defaultController() override; + + // ViewController + const char * title() override; + View * view() override; void viewWillAppear() override; + ViewController::DisplayParameter displayParameter() override { return ViewController::DisplayParameter::DoNotShowOwnTitle; } + + // Responder + bool handleEvent(Ion::Events::Event event) override; + void didBecomeFirstResponder() override; void willExitResponderChain(Responder * nextFirstResponder) override; private: Responder * tabController() const; From f6ceeeb7939d4af3c4736d4b3116559a9e6316ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 17:26:12 +0200 Subject: [PATCH 015/156] [apps/statistics] Inline method in BoxController --- apps/statistics/box_controller.cpp | 4 ---- apps/statistics/box_controller.h | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/statistics/box_controller.cpp b/apps/statistics/box_controller.cpp index 6e093e808..edc3c581a 100644 --- a/apps/statistics/box_controller.cpp +++ b/apps/statistics/box_controller.cpp @@ -37,10 +37,6 @@ const char * BoxController::title() { return I18n::translate(I18n::Message::BoxTab); } -View * BoxController::view() { - return &m_view; -} - void BoxController::viewWillAppear() { m_view.selectMainView(true); reloadBannerView(); diff --git a/apps/statistics/box_controller.h b/apps/statistics/box_controller.h index a9d1466fe..215f11f63 100644 --- a/apps/statistics/box_controller.h +++ b/apps/statistics/box_controller.h @@ -19,7 +19,7 @@ public: // ViewController const char * title() override; - View * view() override; + View * view() override { return &m_view; } void viewWillAppear() override; ViewController::DisplayParameter displayParameter() override { return ViewController::DisplayParameter::DoNotShowOwnTitle; } From c806ae65f8cf5930f17b5b926791933e0c62a52a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 17:28:59 +0200 Subject: [PATCH 016/156] [apps/statistics] Inline methods in app.cpp --- apps/statistics/app.cpp | 24 ------------------------ apps/statistics/app.h | 12 ++++++------ 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/apps/statistics/app.cpp b/apps/statistics/app.cpp index e437ae87b..eea3cb2dc 100644 --- a/apps/statistics/app.cpp +++ b/apps/statistics/app.cpp @@ -47,30 +47,6 @@ App::Descriptor * App::Snapshot::descriptor() { return &descriptor; } -Store * App::Snapshot::store() { - return &m_store; -} - -uint32_t * App::Snapshot::storeVersion() { - return &m_storeVersion; -} - -uint32_t * App::Snapshot::barVersion() { - return &m_barVersion; -} - -uint32_t * App::Snapshot::rangeVersion() { - return &m_rangeVersion; -} - -int * App::Snapshot::selectedHistogramBarIndex() { - return &m_selectedHistogramBarIndex; -} - -BoxView::Quantile * App::Snapshot::selectedBoxQuantile() { - return &m_selectedBoxQuantile; -} - App::App(Container * container, Snapshot * snapshot) : TextFieldDelegateApp(container, snapshot, &m_tabViewController), m_calculationController(&m_calculationAlternateEmptyViewController, &m_calculationHeader, snapshot->store()), diff --git a/apps/statistics/app.h b/apps/statistics/app.h index ee6e46e02..791f809ed 100644 --- a/apps/statistics/app.h +++ b/apps/statistics/app.h @@ -25,12 +25,12 @@ public: App * unpack(Container * container) override; void reset() override; Descriptor * descriptor() override; - Store * store(); - uint32_t * storeVersion(); - uint32_t * barVersion(); - uint32_t * rangeVersion(); - int * selectedHistogramBarIndex(); - BoxView::Quantile * selectedBoxQuantile(); + Store * store() { return &m_store; } + uint32_t * storeVersion() { return &m_storeVersion; } + uint32_t * barVersion() { return &m_barVersion; } + uint32_t * rangeVersion() { return &m_rangeVersion; } + int * selectedHistogramBarIndex() { return &m_selectedHistogramBarIndex; } + BoxView::Quantile * selectedBoxQuantile() { return &m_selectedBoxQuantile; } private: Store m_store; uint32_t m_storeVersion; From 80ee817c45c679e2c02a6a75d31374d2a0f251c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 17:30:52 +0200 Subject: [PATCH 017/156] [apps/statistics] Clean BoxBannerView --- apps/statistics/box_banner_view.cpp | 9 +-------- apps/statistics/box_banner_view.h | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/apps/statistics/box_banner_view.cpp b/apps/statistics/box_banner_view.cpp index 67d426f40..22e84c683 100644 --- a/apps/statistics/box_banner_view.cpp +++ b/apps/statistics/box_banner_view.cpp @@ -8,20 +8,13 @@ BoxBannerView::BoxBannerView() : { } -int BoxBannerView::numberOfSubviews() const { - return 2; -} - TextView * BoxBannerView::textViewAtIndex(int index) const { const TextView * textViews[2] = {&m_calculationName, &m_calculationValue}; return (TextView *)textViews[index]; } MessageTextView * BoxBannerView::messageTextViewAtIndex(int index) const { - if (index == 0) { - return (MessageTextView *)&m_calculationName; - } - return nullptr; + return index == 0 ? (MessageTextView *)&m_calculationName : nullptr; } } diff --git a/apps/statistics/box_banner_view.h b/apps/statistics/box_banner_view.h index c73dbddcf..f69238901 100644 --- a/apps/statistics/box_banner_view.h +++ b/apps/statistics/box_banner_view.h @@ -11,7 +11,7 @@ class BoxBannerView : public Shared::BannerView { public: BoxBannerView(); private: - int numberOfSubviews() const override; + int numberOfSubviews() const override { return 2; } TextView * textViewAtIndex(int i) const override; MessageTextView * messageTextViewAtIndex(int i) const override; MessageTextView m_calculationName; From c7aab7b69ba94e9f8348bee611f5eae84d5f929d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 17:33:08 +0200 Subject: [PATCH 018/156] [apps/statistics] Clean BoxRange --- apps/statistics/box_range.cpp | 8 -------- apps/statistics/box_range.h | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/apps/statistics/box_range.cpp b/apps/statistics/box_range.cpp index 53d298083..f8a9c674d 100644 --- a/apps/statistics/box_range.cpp +++ b/apps/statistics/box_range.cpp @@ -21,14 +21,6 @@ float BoxRange::xMax() { return max + k_displayRightMarginRatio*(max - min); } -float BoxRange::yMin() { - return -k_displayBottomMarginRatio; -} - -float BoxRange::yMax() { - return 1.0f+k_displayTopMarginRatio; -} - float BoxRange::xGridUnit() { return computeGridUnit(Axis::X, xMin(), xMax()); } diff --git a/apps/statistics/box_range.h b/apps/statistics/box_range.h index 28116e66e..22ddcc636 100644 --- a/apps/statistics/box_range.h +++ b/apps/statistics/box_range.h @@ -11,8 +11,8 @@ public: BoxRange(Store * store); float xMin() override; float xMax() override; - float yMin() override; - float yMax() override; + float yMin() override { return -k_displayBottomMarginRatio; } + float yMax() override { return 1.0f+k_displayTopMarginRatio; } float xGridUnit() override; private: constexpr static float k_displayTopMarginRatio = 0.05f; From 4149c99ded946f1c11dc8546b29e95dc81756f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 17:37:48 +0200 Subject: [PATCH 019/156] [apps/statistics] Clean BoxView --- apps/statistics/box_view.cpp | 33 +++++++++++++-------------------- apps/statistics/box_view.h | 8 ++++++-- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/apps/statistics/box_view.cpp b/apps/statistics/box_view.cpp index 8904dae73..5dda1c82b 100644 --- a/apps/statistics/box_view.cpp +++ b/apps/statistics/box_view.cpp @@ -16,22 +16,6 @@ BoxView::BoxView(Store * store, BannerView * bannerView, Quantile * selectedQuan { } -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])(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; - KDRect dirtyZone(KDRect(selectedValueInPixels, pixelLowerBound, 4, pixelUpperBound - pixelLowerBound)); - markRectAsDirty(dirtyZone); -} - -BoxView::Quantile BoxView::selectedQuantile() { - return *m_selectedQuantile; -} - bool BoxView::selectQuantile(int selectedQuantile) { if (selectedQuantile < 0 || selectedQuantile > 4) { return false; @@ -44,6 +28,18 @@ bool BoxView::selectQuantile(int selectedQuantile) { return true; } +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])(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; + KDRect dirtyZone(KDRect(selectedValueInPixels, pixelLowerBound, 4, pixelUpperBound - pixelLowerBound)); + markRectAsDirty(dirtyZone); +} + void BoxView::drawRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(rect, KDColorWhite); drawAxes(ctx, rect, Axis::Horizontal); @@ -81,10 +77,7 @@ void BoxView::drawRect(KDContext * ctx, KDRect rect) const { } char * BoxView::label(Axis axis, int index) const { - if (axis == Axis::Vertical) { - return nullptr; - } - return (char *)m_labels[index]; + return axis == Axis::Vertical ? nullptr : (char *)m_labels[index]; } } diff --git a/apps/statistics/box_view.h b/apps/statistics/box_view.h index cc4e79764..a9dd4d2a7 100644 --- a/apps/statistics/box_view.h +++ b/apps/statistics/box_view.h @@ -20,10 +20,14 @@ public: Max = 4 }; BoxView(Store * store, Shared::BannerView * bannerView, Quantile * selectedQuantile); - void reload() override; - Quantile selectedQuantile(); + Quantile selectedQuantile() const { return *m_selectedQuantile; } bool selectQuantile(int selectedQuantile); void setSeries(int series) { m_series = series; } + + // CurveView + void reload() override; + + // View void drawRect(KDContext * ctx, KDRect rect) const override; private: char * label(Axis axis, int index) const override; From 0e9897ab928406f6806afe3e33bd7168cd805849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 17:45:27 +0200 Subject: [PATCH 020/156] [apps/statistics] Clean CalculationController --- apps/statistics/calculation_controller.cpp | 84 +++++++++------------- apps/statistics/calculation_controller.h | 21 ++++-- 2 files changed, 49 insertions(+), 56 deletions(-) diff --git a/apps/statistics/calculation_controller.cpp b/apps/statistics/calculation_controller.cpp index fbcbcc249..f21897058 100644 --- a/apps/statistics/calculation_controller.cpp +++ b/apps/statistics/calculation_controller.cpp @@ -19,32 +19,7 @@ CalculationController::CalculationController(Responder * parentResponder, Button { } -const char * CalculationController::title() { - return I18n::translate(I18n::Message::StatTab); -} - -bool CalculationController::handleEvent(Ion::Events::Event event) { - if (event == Ion::Events::Up) { - selectableTableView()->deselectTable(); - app()->setFirstResponder(tabController()); - return true; - } - if (event == Ion::Events::Copy && selectedColumn() == 1) { - EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)selectableTableView()->selectedCell(); - Clipboard::sharedClipboard()->store(myCell->text()); - return true; - } - return false; -} - -void CalculationController::didBecomeFirstResponder() { - if (selectedRow() == -1) { - selectCellAtLocation(0, 0); - } else { - selectCellAtLocation(selectedColumn(), selectedRow()); - } - TabTableController::didBecomeFirstResponder(); -} +// AlternateEmptyViewDelegate bool CalculationController::isEmpty() const { return m_store->isEmpty(); @@ -58,13 +33,7 @@ Responder * CalculationController::defaultController() { return tabController(); } -int CalculationController::numberOfRows() { - return k_totalNumberOfRows; -} - -int CalculationController::numberOfColumns() { - return 2; -} +// TableViewDataSource void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { EvenOddCell * myCell = (EvenOddCell *)cell; @@ -87,14 +56,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int } KDCoordinate CalculationController::columnWidth(int i) { - if (i == 0) { - return k_titleCellWidth; - } - return Ion::Display::Width - Metric::CommonRightMargin - Metric::CommonLeftMargin-k_titleCellWidth; -} - -KDCoordinate CalculationController::rowHeight(int j) { - return k_cellHeight; + return i == 0 ? k_titleCellWidth : Ion::Display::Width - Metric::CommonRightMargin - Metric::CommonLeftMargin-k_titleCellWidth; } KDCoordinate CalculationController::cumulatedWidthFromIndex(int i) { @@ -124,20 +86,44 @@ int CalculationController::indexFromCumulatedHeight(KDCoordinate offsetY) { HighlightCell * CalculationController::reusableCell(int index, int type) { assert(index < k_totalNumberOfRows); - if (type == 0) { - return m_titleCells[index]; - } - return m_calculationCells[index]; -} - -int CalculationController::reusableCellCount(int type) { - return k_maxNumberOfDisplayableRows; + return type == 0 ? static_cast(m_titleCells[index]) : static_cast(m_calculationCells[index]); } int CalculationController::typeAtLocation(int i, int j) { return i; } +// ViewController +const char * CalculationController::title() { + return I18n::translate(I18n::Message::StatTab); +} + +// Responder +bool CalculationController::handleEvent(Ion::Events::Event event) { + if (event == Ion::Events::Up) { + selectableTableView()->deselectTable(); + app()->setFirstResponder(tabController()); + return true; + } + if (event == Ion::Events::Copy && selectedColumn() == 1) { + EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)selectableTableView()->selectedCell(); + Clipboard::sharedClipboard()->store(myCell->text()); + return true; + } + return false; +} + +void CalculationController::didBecomeFirstResponder() { + if (selectedRow() == -1) { + selectCellAtLocation(0, 0); + } else { + selectCellAtLocation(selectedColumn(), selectedRow()); + } + TabTableController::didBecomeFirstResponder(); +} + +// Private + Responder * CalculationController::tabController() const { return (parentResponder()->parentResponder()->parentResponder()); } diff --git a/apps/statistics/calculation_controller.h b/apps/statistics/calculation_controller.h index d0bfafaca..d682720cf 100644 --- a/apps/statistics/calculation_controller.h +++ b/apps/statistics/calculation_controller.h @@ -11,26 +11,33 @@ class CalculationController : public Shared::TabTableController, public ButtonRo public: CalculationController(Responder * parentResponder, ButtonRowController * header, Store * store); - const char * title() override; - bool handleEvent(Ion::Events::Event event) override; - void didBecomeFirstResponder() override; + // AlternateEmptyViewDelegate bool isEmpty() const override; I18n::Message emptyMessage() override; Responder * defaultController() override; - int numberOfRows() override; - int numberOfColumns() override; + // TableViewDataSource + int numberOfRows() override { return k_totalNumberOfRows; } + int numberOfColumns() override { return 2; } void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; KDCoordinate columnWidth(int i) override; - KDCoordinate rowHeight(int j) override; + KDCoordinate rowHeight(int j) override { return k_cellHeight; } KDCoordinate cumulatedHeightFromIndex(int j) override; int indexFromCumulatedHeight(KDCoordinate offsetY) override; KDCoordinate cumulatedWidthFromIndex(int i) override; int indexFromCumulatedWidth(KDCoordinate offsetX) override; HighlightCell * reusableCell(int index, int type) override; - int reusableCellCount(int type) override; + int reusableCellCount(int type) override { return k_maxNumberOfDisplayableRows; } int typeAtLocation(int i, int j) override; + + // ViewController + const char * title() override; + ViewController::DisplayParameter displayParameter() override { return ViewController::DisplayParameter::DoNotShowOwnTitle; } + + // Responder + bool handleEvent(Ion::Events::Event event) override; + void didBecomeFirstResponder() override; private: Responder * tabController() const override; View * loadView() override; From 94cee8981f29b17d7ad55e63706987a35423b918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 17:49:57 +0200 Subject: [PATCH 021/156] [apps/statistics] Clean HistogramBannerView --- apps/statistics/histogram_banner_view.cpp | 8 ++------ apps/statistics/histogram_banner_view.h | 3 ++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/statistics/histogram_banner_view.cpp b/apps/statistics/histogram_banner_view.cpp index 8e8d83879..95c36180b 100644 --- a/apps/statistics/histogram_banner_view.cpp +++ b/apps/statistics/histogram_banner_view.cpp @@ -15,17 +15,13 @@ HistogramBannerView::HistogramBannerView() : { } -int HistogramBannerView::numberOfSubviews() const { - return 6; -} - TextView * HistogramBannerView::textViewAtIndex(int i) const { - const TextView * textViews[6] = {&m_intervalLegendView, &m_intervalView, &m_sizeLegendView, &m_sizeView, &m_frequencyLegendView, &m_frequencyView}; + const TextView * textViews[k_numberOfSubviews] = {&m_intervalLegendView, &m_intervalView, &m_sizeLegendView, &m_sizeView, &m_frequencyLegendView, &m_frequencyView}; return (TextView *)textViews[i]; } MessageTextView * HistogramBannerView::messageTextViewAtIndex(int index) const { - const MessageTextView * textViews[6] = {&m_intervalLegendView, nullptr, &m_sizeLegendView, nullptr, &m_frequencyLegendView, nullptr}; + const MessageTextView * textViews[k_numberOfSubviews] = {&m_intervalLegendView, nullptr, &m_sizeLegendView, nullptr, &m_frequencyLegendView, nullptr}; return (MessageTextView *)textViews[index]; } diff --git a/apps/statistics/histogram_banner_view.h b/apps/statistics/histogram_banner_view.h index 6386141f8..dcb3df8f3 100644 --- a/apps/statistics/histogram_banner_view.h +++ b/apps/statistics/histogram_banner_view.h @@ -11,7 +11,8 @@ class HistogramBannerView : public Shared::BannerView { public: HistogramBannerView(); private: - int numberOfSubviews() const override; + static constexpr int k_numberOfSubviews = 6; + int numberOfSubviews() const override { return k_numberOfSubviews; } TextView * textViewAtIndex(int i) const override; MessageTextView * messageTextViewAtIndex(int index) const override; MessageTextView m_intervalLegendView; From 2cf34c4cef55d16a2d68e1bb0c99c2c033ed28ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 17:54:04 +0200 Subject: [PATCH 022/156] [apps/statistics] Clean HistogramController --- apps/statistics/histogram_controller.cpp | 71 ++++++++++-------------- apps/statistics/histogram_controller.h | 16 ++++-- 2 files changed, 41 insertions(+), 46 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index e79eb597a..7e9adb367 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -29,21 +29,41 @@ HistogramController::HistogramController(Responder * parentResponder, ButtonRowC { } -const char * HistogramController::title() { - return I18n::translate(I18n::Message::HistogramTab); -} - -View * HistogramController::view() { - return &m_view; -} - StackViewController * HistogramController::stackController() { StackViewController * stack = (StackViewController *)(parentResponder()->parentResponder()->parentResponder()); return stack; } -HistogramParameterController * HistogramController::histogramParameterController() { - return &m_histogramParameterController; +int HistogramController::numberOfButtons(ButtonRowController::Position) const { + return isEmpty() ? 0 : 1; +} +Button * HistogramController::buttonAtIndex(int index, ButtonRowController::Position) const { + return (Button *)&m_settingButton; +} + +bool HistogramController::isEmpty() const { + return m_store->isEmpty(); +} + +I18n::Message HistogramController::emptyMessage() { + return I18n::Message::NoDataToPlot; +} + +Responder * HistogramController::defaultController() { + return tabController(); +} + +const char * HistogramController::title() { + return I18n::translate(I18n::Message::HistogramTab); +} + +void HistogramController::viewWillAppear() { + if (!m_view.isMainViewSelected()) { + m_view.selectMainView(true); + header()->setSelectedButton(-1); + } + reloadBannerView(); + m_view.reload(); } bool HistogramController::handleEvent(Ion::Events::Event event) { @@ -104,37 +124,6 @@ void HistogramController::didBecomeFirstResponder() { } } -int HistogramController::numberOfButtons(ButtonRowController::Position) const { - if (isEmpty()) { - return 0; - } - return 1; -} -Button * HistogramController::buttonAtIndex(int index, ButtonRowController::Position) const { - return (Button *)&m_settingButton; -} - -bool HistogramController::isEmpty() const { - return m_store->isEmpty(); -} - -I18n::Message HistogramController::emptyMessage() { - return I18n::Message::NoDataToPlot; -} - -Responder * HistogramController::defaultController() { - return tabController(); -} - -void HistogramController::viewWillAppear() { - if (!m_view.isMainViewSelected()) { - m_view.selectMainView(true); - header()->setSelectedButton(-1); - } - reloadBannerView(); - m_view.reload(); -} - void HistogramController::willExitResponderChain(Responder * nextFirstResponder) { if (nextFirstResponder == nullptr || nextFirstResponder == tabController()) { m_view.selectMainView(false); diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index 84d13a447..afa38d07d 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -14,20 +14,26 @@ class HistogramController : public ViewController, public ButtonRowDelegate, pub public: HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, uint32_t * m_storeVersion, uint32_t * m_barVersion, uint32_t * m_rangeVersion, int * m_selectedBarIndex); - const char * title() override; - View * view() override; StackViewController * stackController(); - HistogramParameterController * histogramParameterController(); - bool handleEvent(Ion::Events::Event event) override; - void didBecomeFirstResponder() override; + HistogramParameterController * histogramParameterController() { return &m_histogramParameterController; } + // ButtonRowDelegate int numberOfButtons(ButtonRowController::Position) const override; Button * buttonAtIndex(int index, ButtonRowController::Position position) const override; + // AlternateEmptyViewDelegate bool isEmpty() const override; I18n::Message emptyMessage() override; Responder * defaultController() override; + + // ViewController + const char * title() override; + View * view() override { return &m_view; } void viewWillAppear() override; + + // Responder + bool handleEvent(Ion::Events::Event event) override; + void didBecomeFirstResponder() override; void willExitResponderChain(Responder * nextFirstResponder) override; private: constexpr static int k_maxNumberOfBarsPerWindow = 100; From bb2921054e7b09ee09271bb51e602ba2e82e1379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 17:56:29 +0200 Subject: [PATCH 023/156] [apps/statistics] Clean HistogramParameterController --- apps/statistics/histogram_parameter_controller.cpp | 13 +------------ apps/statistics/histogram_parameter_controller.h | 6 +++--- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/apps/statistics/histogram_parameter_controller.cpp b/apps/statistics/histogram_parameter_controller.cpp index e8c277737..1ffc16f13 100644 --- a/apps/statistics/histogram_parameter_controller.cpp +++ b/apps/statistics/histogram_parameter_controller.cpp @@ -18,10 +18,6 @@ const char * HistogramParameterController::title() { return I18n::translate(I18n::Message::HistogramSet); } -int HistogramParameterController::numberOfRows() { - return 1+k_numberOfCells; -} - void HistogramParameterController::willDisplayCellForIndex(HighlightCell * cell, int index) { if (index == numberOfRows()-1) { return; @@ -34,10 +30,7 @@ void HistogramParameterController::willDisplayCellForIndex(HighlightCell * cell, double HistogramParameterController::parameterAtIndex(int index) { assert(index >= 0 && index < k_numberOfCells); - if (index == 0) { - return m_store->barWidth(); - } - return m_store->firstDrawnBarAbscissa(); + return index == 0 ? m_store->barWidth() : m_store->firstDrawnBarAbscissa(); } bool HistogramParameterController::setParameterAtIndex(int parameterIndex, double f) { @@ -109,10 +102,6 @@ HighlightCell * HistogramParameterController::reusableParameterCell(int index, i return m_cells[index]; } -int HistogramParameterController::reusableParameterCellCount(int type) { - return k_numberOfCells; -} - View * HistogramParameterController::loadView() { SelectableTableView * tableView = (SelectableTableView *)FloatParameterController::loadView(); for (int i = 0; i < k_numberOfCells; i++) { diff --git a/apps/statistics/histogram_parameter_controller.h b/apps/statistics/histogram_parameter_controller.h index 3a9a35de2..ab20dd019 100644 --- a/apps/statistics/histogram_parameter_controller.h +++ b/apps/statistics/histogram_parameter_controller.h @@ -11,17 +11,17 @@ class HistogramParameterController : public Shared::FloatParameterController { public: HistogramParameterController(Responder * parentResponder, Store * store); const char * title() override; - int numberOfRows() override; + int numberOfRows() override { return 1+k_numberOfCells; } void willDisplayCellForIndex(HighlightCell * cell, int index) override; private: + constexpr static int k_numberOfCells = 2; HighlightCell * reusableParameterCell(int index, int type) override; - int reusableParameterCellCount(int type) override; + int reusableParameterCellCount(int type) override { return k_numberOfCells; } double parameterAtIndex(int index) override; bool setParameterAtIndex(int parameterIndex, double f) override; View * loadView() override; void unloadView(View * view) override; char m_draftTextBuffer[MessageTableCellWithEditableText::k_bufferLength]; - constexpr static int k_numberOfCells = 2; MessageTableCellWithEditableText * m_cells[k_numberOfCells]; Store * m_store; }; From 614b74eff9f91d32f1ca279d4a62236ec65ae655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 21 May 2018 17:57:25 +0200 Subject: [PATCH 024/156] [apps/statistics] Clean HistogramView --- apps/statistics/histogram_view.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/statistics/histogram_view.cpp b/apps/statistics/histogram_view.cpp index 0f94927d3..12d2c825b 100644 --- a/apps/statistics/histogram_view.cpp +++ b/apps/statistics/histogram_view.cpp @@ -51,10 +51,7 @@ void HistogramView::setHighlight(float start, float end) { } char * HistogramView::label(Axis axis, int index) const { - if (axis == Axis::Vertical) { - return nullptr; - } - return (char *)m_labels[index]; + return axis == Axis::Vertical ? nullptr : (char *)m_labels[index]; } float HistogramView::EvaluateHistogramAtAbscissa(float abscissa, void * model, void * context) { From 4a2963381dade5bc15db1244aecca29667b82606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 22 May 2018 10:17:25 +0200 Subject: [PATCH 025/156] [apps] Reorganize Shared::StoreController --- apps/shared/store_controller.cpp | 18 +++++++++--------- apps/shared/store_controller.h | 11 ++++++++++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 9c437c0d5..80e2163e0 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -38,10 +38,6 @@ bool StoreController::textFieldDidFinishEditing(TextField * textField, const cha return true; } -const char * StoreController::title() { - return I18n::translate(I18n::Message::DataTab); -} - int StoreController::numberOfColumns() { return k_numberOfColumnsPerSeries * FloatPairStore::k_numberOfSeries; } @@ -92,11 +88,8 @@ void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int willDisplayCellAtLocationWithDisplayMode(cell, i, j, PrintFloat::Mode::Decimal); } -void StoreController::didBecomeFirstResponder() { - if (selectedRow() < 0 || selectedColumn() < 0) { - selectCellAtLocation(0, 0); - } - EditableCellTableViewController::didBecomeFirstResponder(); +const char * StoreController::title() { + return I18n::translate(I18n::Message::DataTab); } bool StoreController::handleEvent(Ion::Events::Event event) { @@ -126,6 +119,13 @@ bool StoreController::handleEvent(Ion::Events::Event event) { return false; } +void StoreController::didBecomeFirstResponder() { + if (selectedRow() < 0 || selectedColumn() < 0) { + selectCellAtLocation(0, 0); + } + EditableCellTableViewController::didBecomeFirstResponder(); +} + Responder * StoreController::tabController() const { return (parentResponder()->parentResponder()->parentResponder()); } diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 52329f3f4..597cbcf47 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -11,8 +11,11 @@ namespace Shared { class StoreController : public EditableCellTableViewController, public ButtonRowDelegate { public: StoreController(Responder * parentResponder, FloatPairStore * store, ButtonRowController * header); + + // TextFieldDelegate bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override; - const char * title() override; + + // TableViewDataSource int numberOfColumns() override; KDCoordinate columnWidth(int i) override; KDCoordinate cumulatedWidthFromIndex(int i) override; @@ -21,8 +24,14 @@ public: int reusableCellCount(int type) override; int typeAtLocation(int i, int j) override; void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; + + // ViewController + const char * title() override; + + // Responder bool handleEvent(Ion::Events::Event event) override; void didBecomeFirstResponder() override; + protected: static constexpr KDCoordinate k_cellWidth = 80; //TODO constexpr static int k_numberOfColumnsPerSeries = 2; From 54ca272eb1a735f6f8c39cc941a0ea404a78b764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 22 May 2018 10:21:49 +0200 Subject: [PATCH 026/156] [apps] Set margins in Shared::StoreController --- apps/shared/store_controller.cpp | 1 + apps/shared/store_controller.h | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 80e2163e0..c57e01137 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -163,6 +163,7 @@ View * StoreController::loadView() { for (int i = 0; i < k_maxNumberOfEditableCells; i++) { m_editableCells[i] = new EvenOddEditableTextCell(tableView, this, m_draftTextBuffer); } + tableView->setMargins(k_margin); return tableView; } diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 597cbcf47..a5ae3d19b 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -34,6 +34,7 @@ public: protected: static constexpr KDCoordinate k_cellWidth = 80; //TODO + static constexpr KDCoordinate k_margin = 8; constexpr static int k_numberOfColumnsPerSeries = 2; constexpr static int k_maxNumberOfEditableCells = 22 * FloatPairStore::k_numberOfSeries; constexpr static int k_numberOfTitleCells = k_numberOfColumnsPerSeries * FloatPairStore::k_numberOfSeries; From b1e732e135eaf3aae8b4b88168073430f265b378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 22 May 2018 11:42:08 +0200 Subject: [PATCH 027/156] [apps] Color Series names if not empty in Statistics --- apps/graph/Makefile | 1 - apps/graph/list/list_controller.cpp | 4 +-- apps/graph/list/list_controller.h | 6 ++--- apps/graph/values/values_controller.cpp | 6 ++--- apps/graph/values/values_controller.h | 6 ++--- apps/shared/Makefile | 1 + .../buffer_function_title_cell.cpp} | 26 +++++++++---------- .../buffer_function_title_cell.h} | 12 ++++----- apps/statistics/store_controller.cpp | 10 ++++--- apps/statistics/store_controller.h | 3 ++- 10 files changed, 38 insertions(+), 37 deletions(-) rename apps/{graph/function_title_cell.cpp => shared/buffer_function_title_cell.cpp} (53%) rename apps/{graph/function_title_cell.h => shared/buffer_function_title_cell.h} (54%) diff --git a/apps/graph/Makefile b/apps/graph/Makefile index 04edd7eb8..342bcfecd 100644 --- a/apps/graph/Makefile +++ b/apps/graph/Makefile @@ -5,7 +5,6 @@ app_objs += $(addprefix apps/graph/,\ app.o\ cartesian_function.o\ cartesian_function_store.o\ - function_title_cell.o\ graph/banner_view.o\ graph/calculation_graph_controller.o\ graph/calculation_parameter_controller.o\ diff --git a/apps/graph/list/list_controller.cpp b/apps/graph/list/list_controller.cpp index 9c84175d6..d1fc7db22 100644 --- a/apps/graph/list/list_controller.cpp +++ b/apps/graph/list/list_controller.cpp @@ -79,7 +79,7 @@ HighlightCell * ListController::expressionCells(int index) { void ListController::willDisplayTitleCellAtIndex(HighlightCell * cell, int j) { - FunctionTitleCell * myFunctionCell = (FunctionTitleCell *)cell; + Shared::BufferFunctionTitleCell * myFunctionCell = (Shared::BufferFunctionTitleCell *)cell; CartesianFunction * function = ((CartesianFunctionStore *)m_functionStore)->functionAtIndex(j); char bufferName[5] = {*function->name(),'(', m_functionStore->symbol(),')', 0}; myFunctionCell->setText(bufferName); @@ -106,7 +106,7 @@ bool ListController::removeFunctionRow(Function * function) { View * ListController::loadView() { for (int i = 0; i < k_maxNumberOfRows; i++) { - m_functionTitleCells[i] = new FunctionTitleCell(FunctionTitleCell::Orientation::VerticalIndicator); + m_functionTitleCells[i] = new Shared::BufferFunctionTitleCell(FunctionTitleCell::Orientation::VerticalIndicator); m_expressionCells[i] = new FunctionExpressionCell(); } return Shared::ListController::loadView(); diff --git a/apps/graph/list/list_controller.h b/apps/graph/list/list_controller.h index 2eb9df238..ba3fed54b 100644 --- a/apps/graph/list/list_controller.h +++ b/apps/graph/list/list_controller.h @@ -2,9 +2,9 @@ #define GRAPH_LIST_CONTROLLER_H #include -#include "../function_title_cell.h" -#include "../../shared/function_expression_cell.h" #include "../cartesian_function_store.h" +#include "../../shared/buffer_function_title_cell.h" +#include "../../shared/function_expression_cell.h" #include "../../shared/new_function_cell.h" #include "../../shared/list_controller.h" #include "../../shared/list_parameter_controller.h" @@ -29,7 +29,7 @@ private: View * loadView() override; void unloadView(View * view) override; constexpr static int k_maxNumberOfRows = 5; - FunctionTitleCell * m_functionTitleCells[k_maxNumberOfRows]; + Shared::BufferFunctionTitleCell * m_functionTitleCells[k_maxNumberOfRows]; Shared::FunctionExpressionCell * m_expressionCells[k_maxNumberOfRows]; Shared::ListParameterController m_parameterController; }; diff --git a/apps/graph/values/values_controller.cpp b/apps/graph/values/values_controller.cpp index 278eb5d40..e55617ed4 100644 --- a/apps/graph/values/values_controller.cpp +++ b/apps/graph/values/values_controller.cpp @@ -37,7 +37,7 @@ void ValuesController::willDisplayCellAtLocation(HighlightCell * cell, int i, in } // The cell is a function title cell: if (j == 0 && i > 0) { - FunctionTitleCell * myFunctionCell = (FunctionTitleCell *)cell; + Shared::BufferFunctionTitleCell * myFunctionCell = (Shared::BufferFunctionTitleCell *)cell; CartesianFunction * function = functionAtColumn(i); char bufferName[6] = {0, 0, '(', 'x', ')', 0}; const char * name = nullptr; @@ -122,7 +122,7 @@ int ValuesController::maxNumberOfFunctions() { return k_maxNumberOfFunctions; } -FunctionTitleCell * ValuesController::functionTitleCells(int j) { +Shared::BufferFunctionTitleCell * ValuesController::functionTitleCells(int j) { assert(j >= 0 && j < k_maxNumberOfFunctions); return m_functionTitleCells[j]; } @@ -151,7 +151,7 @@ double ValuesController::evaluationOfAbscissaAtColumn(double abscissa, int colum View * ValuesController::loadView() { for (int i = 0; i < k_maxNumberOfFunctions; i++) { - m_functionTitleCells[i] = new FunctionTitleCell(FunctionTitleCell::Orientation::HorizontalIndicator, KDText::FontSize::Small); + m_functionTitleCells[i] = new Shared::BufferFunctionTitleCell(FunctionTitleCell::Orientation::HorizontalIndicator, KDText::FontSize::Small); } for (int i = 0; i < k_maxNumberOfCells; i++) { m_floatCells[i] = new EvenOddBufferTextCell(); diff --git a/apps/graph/values/values_controller.h b/apps/graph/values/values_controller.h index 2297e5afc..b12507ec6 100644 --- a/apps/graph/values/values_controller.h +++ b/apps/graph/values/values_controller.h @@ -2,7 +2,7 @@ #define GRAPH_VALUES_CONTROLLER_H #include "../cartesian_function_store.h" -#include "../function_title_cell.h" +#include "../../shared/buffer_function_title_cell.h" #include "../../shared/values_controller.h" #include "../../shared/interval_parameter_controller.h" #include "derivative_parameter_controller.h" @@ -27,8 +27,8 @@ private: double evaluationOfAbscissaAtColumn(double abscissa, int columnIndex) override; constexpr static int k_maxNumberOfCells = 50; constexpr static int k_maxNumberOfFunctions = 5; - FunctionTitleCell * m_functionTitleCells[k_maxNumberOfFunctions]; - FunctionTitleCell * functionTitleCells(int j) override; + Shared::BufferFunctionTitleCell * m_functionTitleCells[k_maxNumberOfFunctions]; + Shared::BufferFunctionTitleCell * functionTitleCells(int j) override; EvenOddBufferTextCell * m_floatCells[k_maxNumberOfCells]; EvenOddBufferTextCell * floatCells(int j) override; CartesianFunctionStore * m_functionStore; diff --git a/apps/shared/Makefile b/apps/shared/Makefile index 01c64faa4..5034a0580 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -1,5 +1,6 @@ app_objs += $(addprefix apps/shared/,\ banner_view.o\ + buffer_function_title_cell.o\ button_with_separator.o\ cursor_view.o\ curve_view.o\ diff --git a/apps/graph/function_title_cell.cpp b/apps/shared/buffer_function_title_cell.cpp similarity index 53% rename from apps/graph/function_title_cell.cpp rename to apps/shared/buffer_function_title_cell.cpp index e4036acb5..f6e43d529 100644 --- a/apps/graph/function_title_cell.cpp +++ b/apps/shared/buffer_function_title_cell.cpp @@ -1,45 +1,43 @@ -#include "function_title_cell.h" +#include "buffer_function_title_cell.h" #include -using namespace Shared; +namespace Shared { -namespace Graph { - -FunctionTitleCell::FunctionTitleCell(Orientation orientation, KDText::FontSize size) : - Shared::FunctionTitleCell(orientation), +BufferFunctionTitleCell::BufferFunctionTitleCell(Orientation orientation, KDText::FontSize size) : + FunctionTitleCell(orientation), m_bufferTextView(size, 0.5f, 0.5f) { } -void FunctionTitleCell::setHighlighted(bool highlight) { +void BufferFunctionTitleCell::setHighlighted(bool highlight) { EvenOddCell::setHighlighted(highlight); m_bufferTextView.setHighlighted(highlight); } -void FunctionTitleCell::setEven(bool even) { +void BufferFunctionTitleCell::setEven(bool even) { EvenOddCell::setEven(even); m_bufferTextView.setEven(even); } -void FunctionTitleCell::setColor(KDColor color) { - Shared::FunctionTitleCell::setColor(color); +void BufferFunctionTitleCell::setColor(KDColor color) { + FunctionTitleCell::setColor(color); m_bufferTextView.setTextColor(color); } -void FunctionTitleCell::setText(const char * title) { +void BufferFunctionTitleCell::setText(const char * title) { m_bufferTextView.setText(title); } -int FunctionTitleCell::numberOfSubviews() const { +int BufferFunctionTitleCell::numberOfSubviews() const { return 1; } -View * FunctionTitleCell::subviewAtIndex(int index) { +View * BufferFunctionTitleCell::subviewAtIndex(int index) { assert(index == 0); return &m_bufferTextView; } -void FunctionTitleCell::layoutSubviews() { +void BufferFunctionTitleCell::layoutSubviews() { KDRect textFrame(0, k_colorIndicatorThickness, bounds().width(), bounds().height() - k_colorIndicatorThickness); if (m_orientation == Orientation::VerticalIndicator){ textFrame = KDRect(k_colorIndicatorThickness, 0, bounds().width() - k_colorIndicatorThickness, bounds().height()-k_separatorThickness); diff --git a/apps/graph/function_title_cell.h b/apps/shared/buffer_function_title_cell.h similarity index 54% rename from apps/graph/function_title_cell.h rename to apps/shared/buffer_function_title_cell.h index 6d10b179a..dab68686c 100644 --- a/apps/graph/function_title_cell.h +++ b/apps/shared/buffer_function_title_cell.h @@ -1,13 +1,13 @@ -#ifndef GRAPH_FUNCTION_TITLE_CELL_H -#define GRAPH_FUNCTION_TITLE_CELL_H +#ifndef SHARED_BUFFER_FUNCTION_TITLE_CELL_H +#define SHARED_BUFFER_FUNCTION_TITLE_CELL_H -#include "../shared/function_title_cell.h" +#include "function_title_cell.h" -namespace Graph { +namespace Shared { -class FunctionTitleCell : public Shared::FunctionTitleCell { +class BufferFunctionTitleCell : public FunctionTitleCell { public: - FunctionTitleCell(Orientation orientation, KDText::FontSize size = KDText::FontSize::Large); + BufferFunctionTitleCell(Orientation orientation, KDText::FontSize size = KDText::FontSize::Large); void setEven(bool even) override; void setHighlighted(bool highlight) override; void setColor(KDColor color) override; diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index 8e1ccfb21..ec0410e92 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -21,17 +21,19 @@ void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int if (cellAtLocationIsEditable(i, j)) { return; } - EvenOddMessageTextCell * mytitleCell = (EvenOddMessageTextCell *)cell; + Shared::BufferFunctionTitleCell * mytitleCell = (Shared::BufferFunctionTitleCell *)cell; bool valuesColumn = i%k_numberOfColumnsPerSeries == 0; int seriesIndex = i/k_numberOfColumnsPerSeries; assert(seriesIndex >= 0 && seriesIndex < FloatPairStore::k_numberOfSeries); if (valuesColumn) { I18n::Message valuesMessages[] = {I18n::Message::Values1, I18n::Message::Values2, I18n::Message::Values3}; - mytitleCell->setMessage(valuesMessages[seriesIndex]); + mytitleCell->setText(I18n::translate(valuesMessages[seriesIndex])); } else { I18n::Message sizesMessages[] = {I18n::Message::Sizes1, I18n::Message::Sizes2, I18n::Message::Sizes3}; - mytitleCell->setMessage(sizesMessages[seriesIndex]); + mytitleCell->setText(I18n::translate(sizesMessages[seriesIndex])); } + KDColor colors[] = {Palette::Red, Palette::Blue, Palette::Green}; + mytitleCell->setColor(m_store->numberOfPairsOfSeries(seriesIndex) == 0 ? Palette::GreyDark : colors[seriesIndex]); // TODO Share GreyDark and other colors with graph/list_controller } HighlightCell * StoreController::titleCells(int index) { @@ -53,7 +55,7 @@ bool StoreController::setDataAtLocation(double floatBody, int columnIndex, int r View * StoreController::loadView() { for (int i = 0; i < k_numberOfTitleCells; i++) { - m_titleCells[i] = new EvenOddMessageTextCell(KDText::FontSize::Small); + m_titleCells[i] = new Shared::BufferFunctionTitleCell(FunctionTitleCell::Orientation::HorizontalIndicator, KDText::FontSize::Small); } return Shared::StoreController::loadView(); } diff --git a/apps/statistics/store_controller.h b/apps/statistics/store_controller.h index e3ded8a4f..34bb91cd0 100644 --- a/apps/statistics/store_controller.h +++ b/apps/statistics/store_controller.h @@ -4,6 +4,7 @@ #include #include "store.h" #include "../shared/store_controller.h" +#include "../shared/buffer_function_title_cell.h" namespace Statistics { @@ -16,7 +17,7 @@ private: HighlightCell * titleCells(int index) override; View * loadView() override; void unloadView(View * view) override; - EvenOddMessageTextCell * m_titleCells[k_numberOfTitleCells]; + Shared::BufferFunctionTitleCell * m_titleCells[k_numberOfTitleCells]; }; } From c84fd45b974a40f7e5b2adae835a1a304b5826ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 22 May 2018 16:30:54 +0200 Subject: [PATCH 028/156] [apps/statistics] Show several histograms --- apps/statistics/app.cpp | 2 +- apps/statistics/histogram_controller.cpp | 140 +++++++++++++++++------ apps/statistics/histogram_controller.h | 25 +++- apps/statistics/histogram_view.cpp | 12 +- apps/statistics/histogram_view.h | 7 +- apps/statistics/store.cpp | 17 ++- apps/statistics/store.h | 3 + 7 files changed, 159 insertions(+), 47 deletions(-) diff --git a/apps/statistics/app.cpp b/apps/statistics/app.cpp index eea3cb2dc..301589027 100644 --- a/apps/statistics/app.cpp +++ b/apps/statistics/app.cpp @@ -55,7 +55,7 @@ App::App(Container * container, Snapshot * snapshot) : m_boxController(&m_boxAlternateEmptyViewController, &m_boxHeader, snapshot->store(), snapshot->selectedBoxQuantile()), m_boxAlternateEmptyViewController(&m_boxHeader, &m_boxController, &m_boxController), m_boxHeader(&m_tabViewController, &m_boxAlternateEmptyViewController, &m_boxController), - m_histogramController(&m_histogramAlternateEmptyViewController, &m_histogramHeader, snapshot->store(), snapshot->storeVersion(), snapshot->barVersion(), snapshot->rangeVersion(), snapshot->selectedHistogramBarIndex()), + m_histogramController(&m_histogramAlternateEmptyViewController, &m_histogramHeader, snapshot->store(), 0, snapshot->storeVersion(), snapshot->barVersion(), snapshot->rangeVersion(), snapshot->selectedHistogramBarIndex()), m_histogramAlternateEmptyViewController(&m_histogramHeader, &m_histogramController, &m_histogramController), m_histogramHeader(&m_histogramStackViewController, &m_histogramAlternateEmptyViewController, &m_histogramController), m_histogramStackViewController(&m_tabViewController, &m_histogramHeader), diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 7e9adb367..a1ae0a6f2 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -10,20 +10,77 @@ using namespace Shared; namespace Statistics { -HistogramController::HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, uint32_t * storeVersion, uint32_t * barVersion, uint32_t * rangeVersion, int * selectedBarIndex) : +HistogramController::ContentView::ContentView(HistogramController * controller, Store * store) : + m_histogramView1(controller, store, 0, &m_bannerView), + m_histogramView2(controller, store, 1, &m_bannerView), + m_histogramView3(controller, store, 2, &m_bannerView), + m_bannerView(), + m_store(store) +{ +} + +HistogramView * HistogramController::ContentView::histogramViewAtIndex(int index) { + assert(index >= 0 && index < 3); + HistogramView * views[] = {&m_histogramView1, &m_histogramView2, &m_histogramView3}; + return views[index]; +} + +int HistogramController::ContentView::seriesOfSubviewAtIndex(int index) { + assert(index >= 0 && index < numberOfSubviews()); + return static_cast(subviewAtIndex(index))->series(); +} + +void HistogramController::ContentView::layoutSubviews() { + int numberSubviews = numberOfSubviews(); + KDCoordinate subviewHeight = bounds().height()/numberSubviews; + int displayedSubviewIndex = 0; + for (int i = 0; i < 3; i++) { + if (!m_store->seriesIsEmpty(i)) { + KDRect frame = KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), subviewHeight); + subviewAtIndex(displayedSubviewIndex)->setFrame(frame); + displayedSubviewIndex++; + } + } +} + +int HistogramController::ContentView::numberOfSubviews() const { + int result = m_store->numberOfNonEmptySeries(); + assert(result <= Store::k_numberOfSeries); + return result; +} + +View * HistogramController::ContentView::subviewAtIndex(int index) { + int seriesIndex = 0; + int nonEmptySeriesIndex = 0; + while (nonEmptySeriesIndex < index && seriesIndex < Store::k_numberOfSeries) { + if (!m_store->seriesIsEmpty(seriesIndex)) { + nonEmptySeriesIndex++; + } + seriesIndex++; + } + if (nonEmptySeriesIndex == index) { + assert(seriesIndex >=0 && seriesIndex < 3); + View * views[] = {&m_histogramView1, &m_histogramView2, &m_histogramView3}; + return views[seriesIndex]; + } + assert(false); + return nullptr; +} + +HistogramController::HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, int series, uint32_t * storeVersion, uint32_t * barVersion, uint32_t * rangeVersion, int * selectedBarIndex) : ViewController(parentResponder), ButtonRowDelegate(header, nullptr), - m_bannerView(), - m_view(store, &m_bannerView), m_settingButton(this, I18n::Message::HistogramSet, Invocation([](void * context, void * sender) { HistogramController * histogramController = (HistogramController *) context; StackViewController * stack = ((StackViewController *)histogramController->stackController()); stack->push(histogramController->histogramParameterController()); }, this)), m_store(store), + m_view(this, store), m_storeVersion(storeVersion), m_barVersion(barVersion), m_rangeVersion(rangeVersion), + m_selectedSeries(-1), m_selectedBarIndex(selectedBarIndex), m_histogramParameterController(nullptr, store) { @@ -34,6 +91,10 @@ StackViewController * HistogramController::stackController() { return stack; } +void HistogramController::setCurrentDrawnSeries(int series) { + initYRangeParameters(series); +} + int HistogramController::numberOfButtons(ButtonRowController::Position) const { return isEmpty() ? 0 : 1; } @@ -58,8 +119,9 @@ const char * HistogramController::title() { } void HistogramController::viewWillAppear() { - if (!m_view.isMainViewSelected()) { - m_view.selectMainView(true); + if (m_selectedSeries < 0) { + m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); + m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); header()->setSelectedButton(-1); } reloadBannerView(); @@ -68,9 +130,9 @@ void HistogramController::viewWillAppear() { bool HistogramController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Down) { - if (!m_view.isMainViewSelected()) { + if (!m_view.histogramViewAtIndex(m_selectedSeries)->isMainViewSelected()) { header()->setSelectedButton(-1); - m_view.selectMainView(true); + m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); reloadBannerView(); m_view.reload(); app()->setFirstResponder(this); @@ -79,16 +141,16 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { return false; } if (event == Ion::Events::Up) { - if (!m_view.isMainViewSelected()) { + if (!m_view.histogramViewAtIndex(m_selectedSeries)->isMainViewSelected()) { header()->setSelectedButton(-1); app()->setFirstResponder(tabController()); return true; } - m_view.selectMainView(false); + m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); header()->setSelectedButton(0); return true; } - if (m_view.isMainViewSelected() && (event == Ion::Events::Left || event == Ion::Events::Right)) { + if (m_view.histogramViewAtIndex(m_selectedSeries)->isMainViewSelected() && (event == Ion::Events::Left || event == Ion::Events::Right)) { int direction = event == Ion::Events::Left ? -1 : 1; if (moveSelection(direction)) { reloadBannerView(); @@ -117,16 +179,16 @@ void HistogramController::didBecomeFirstResponder() { initBarSelection(); reloadBannerView(); } - if (!m_view.isMainViewSelected()) { + if (!m_view.histogramViewAtIndex(m_selectedSeries)->isMainViewSelected()) { header()->setSelectedButton(0); } else { - m_view.setHighlight(m_store->startOfBarAtIndex(0, *m_selectedBarIndex), m_store->endOfBarAtIndex(0, *m_selectedBarIndex)); //TODO + m_view.histogramViewAtIndex(m_selectedSeries)->setHighlight(m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex), m_store->endOfBarAtIndex(0, *m_selectedBarIndex)); } } void HistogramController::willExitResponderChain(Responder * nextFirstResponder) { if (nextFirstResponder == nullptr || nextFirstResponder == tabController()) { - m_view.selectMainView(false); + m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); header()->setSelectedButton(-1); m_view.reload(); } @@ -147,13 +209,13 @@ void HistogramController::reloadBannerView() { numberOfChar += legendLength; // Add lower bound - double lowerBound = m_store->startOfBarAtIndex(0, *m_selectedBarIndex); //TODO + double lowerBound = m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex); numberOfChar += PrintFloat::convertFloatToText(lowerBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); buffer[numberOfChar++] = ';'; // Add upper bound - double upperBound = m_store->endOfBarAtIndex(0, *m_selectedBarIndex); //TODO + double upperBound = m_store->endOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex); numberOfChar += PrintFloat::convertFloatToText(upperBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); buffer[numberOfChar++] = '['; @@ -163,7 +225,7 @@ void HistogramController::reloadBannerView() { buffer[numberOfChar++] = ' '; } buffer[k_maxIntervalLegendLength] = 0; - m_bannerView.setLegendAtIndex(buffer, 1); + m_view.bannerView()->setLegendAtIndex(buffer, 1); // Add Size Data numberOfChar = 0; @@ -171,14 +233,14 @@ void HistogramController::reloadBannerView() { legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; - double size = m_store->heightOfBarAtIndex(0, *m_selectedBarIndex); //TODO + double size = m_store->heightOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex); numberOfChar += PrintFloat::convertFloatToText(size, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); // Padding for (int i = numberOfChar; i < k_maxLegendLength; i++) { buffer[numberOfChar++] = ' '; } buffer[k_maxLegendLength] = 0; - m_bannerView.setLegendAtIndex(buffer, 3); + m_view.bannerView()->setLegendAtIndex(buffer, 3); // Add Frequency Data numberOfChar = 0; @@ -186,14 +248,14 @@ void HistogramController::reloadBannerView() { legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; - double frequency = size/m_store->sumOfOccurrences(0); //TODO + double frequency = size/m_store->sumOfOccurrences(m_selectedSeries); numberOfChar += PrintFloat::convertFloatToText(frequency, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); // Padding for (int i = numberOfChar; i < k_maxLegendLength; i++) { buffer[numberOfChar++] = ' '; } buffer[k_maxLegendLength] = 0; - m_bannerView.setLegendAtIndex(buffer, 5); + m_view.bannerView()->setLegendAtIndex(buffer, 5); } bool HistogramController::moveSelection(int deltaIndex) { @@ -201,16 +263,15 @@ bool HistogramController::moveSelection(int deltaIndex) { if (deltaIndex > 0) { do { newSelectedBarIndex++; - } while (m_store->heightOfBarAtIndex(0, newSelectedBarIndex) == 0 && newSelectedBarIndex < m_store->numberOfBars(0)); //TODO - } else { + } while (m_store->heightOfBarAtIndex(m_selectedSeries, newSelectedBarIndex) == 0 && newSelectedBarIndex < m_store->numberOfBars(m_selectedSeries)); } else { do { newSelectedBarIndex--; - } while (m_store->heightOfBarAtIndex(0, newSelectedBarIndex) == 0 && newSelectedBarIndex >= 0); //TODO + } while (m_store->heightOfBarAtIndex(m_selectedSeries, newSelectedBarIndex) == 0 && newSelectedBarIndex >= 0); } - if (newSelectedBarIndex >= 0 && newSelectedBarIndex < m_store->numberOfBars(0) && *m_selectedBarIndex != newSelectedBarIndex) {//TODO + if (newSelectedBarIndex >= 0 && newSelectedBarIndex < m_store->numberOfBars(m_selectedSeries) && *m_selectedBarIndex != newSelectedBarIndex) { *m_selectedBarIndex = newSelectedBarIndex; - m_view.setHighlight(m_store->startOfBarAtIndex(0, *m_selectedBarIndex), m_store->endOfBarAtIndex(0, *m_selectedBarIndex)); //TODO - m_store->scrollToSelectedBarIndex(0, *m_selectedBarIndex);//TODO + m_view.histogramViewAtIndex(m_selectedSeries)->setHighlight(m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex), m_store->endOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex)); + m_store->scrollToSelectedBarIndex(m_selectedSeries, *m_selectedBarIndex); return true; } return false; @@ -218,7 +279,7 @@ bool HistogramController::moveSelection(int deltaIndex) { void HistogramController::initRangeParameters() { float min = m_store->firstDrawnBarAbscissa(); - float max = m_store->maxValue(0); //TODO + float max = m_store->maxValue(m_selectedSeries); float barWidth = m_store->barWidth(); float xMin = min; float xMax = max + barWidth; @@ -232,22 +293,27 @@ void HistogramController::initRangeParameters() { } m_store->setXMin(xMin - Store::k_displayLeftMarginRatio*(xMax-xMin)); m_store->setXMax(xMax + Store::k_displayRightMarginRatio*(xMax-xMin)); + + initYRangeParameters(m_selectedSeries); +} + +void HistogramController::initYRangeParameters(int series) { float yMax = -FLT_MAX; - for (int index = 0; index < m_store->numberOfBars(0); index++) { //TODO - float size = m_store->heightOfBarAtIndex(0, index); //TODO + for (int index = 0; index < m_store->numberOfBars(series); index++) { + float size = m_store->heightOfBarAtIndex(series, index); if (size > yMax) { yMax = size; } } - yMax = yMax/m_store->sumOfOccurrences(0); //TODO + yMax = yMax/m_store->sumOfOccurrences(series); 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(0); //TODO - float max = m_store->maxValue(0); //TODO + float min = m_store->minValue(m_selectedSeries); + float max = m_store->maxValue(m_selectedSeries); 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); @@ -259,18 +325,18 @@ void HistogramController::initBarParameters() { void HistogramController::initBarSelection() { *m_selectedBarIndex = 0; - 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 + while ((m_store->heightOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex) == 0 || + m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex) < m_store->firstDrawnBarAbscissa()) && *m_selectedBarIndex < m_store->numberOfBars(m_selectedSeries)) { *m_selectedBarIndex = *m_selectedBarIndex+1; } - if (*m_selectedBarIndex >= m_store->numberOfBars(0)) { //TODO + if (*m_selectedBarIndex >= m_store->numberOfBars(m_selectedSeries)) { /* No bar is after m_firstDrawnBarAbscissa, so we select the first bar */ *m_selectedBarIndex = 0; - while (m_store->heightOfBarAtIndex(0, *m_selectedBarIndex) == 0 && *m_selectedBarIndex < m_store->numberOfBars(0)) { //TODO + while (m_store->heightOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex) == 0 && *m_selectedBarIndex < m_store->numberOfBars(m_selectedSeries)) { *m_selectedBarIndex = *m_selectedBarIndex+1; } } - m_store->scrollToSelectedBarIndex(0, *m_selectedBarIndex);//TODO + m_store->scrollToSelectedBarIndex(m_selectedSeries, *m_selectedBarIndex); } } diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index afa38d07d..df4ba04aa 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -13,9 +13,10 @@ namespace Statistics { class HistogramController : public ViewController, public ButtonRowDelegate, public AlternateEmptyViewDelegate { public: - HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, uint32_t * m_storeVersion, uint32_t * m_barVersion, uint32_t * m_rangeVersion, int * m_selectedBarIndex); + HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, int series, uint32_t * m_storeVersion, uint32_t * m_barVersion, uint32_t * m_rangeVersion, int * m_selectedBarIndex); StackViewController * stackController(); HistogramParameterController * histogramParameterController() { return &m_histogramParameterController; } + void setCurrentDrawnSeries(int series); // ButtonRowDelegate int numberOfButtons(ButtonRowController::Position) const override; @@ -40,20 +41,38 @@ private: constexpr static int k_maxIntervalLegendLength = 33; constexpr static int k_maxLegendLength = 13; constexpr static int k_maxNumberOfCharacters = 30; + class ContentView : public View { + public: + ContentView(HistogramController * controller, Store * store); + void reload() {} + HistogramView * histogramViewAtIndex(int index); + int seriesOfSubviewAtIndex(int index); + HistogramBannerView * bannerView() { return &m_bannerView; } + void layoutSubviews() override; + private: + int numberOfSubviews() const override; + View * subviewAtIndex(int index) override; + HistogramView m_histogramView1; + HistogramView m_histogramView2; + HistogramView m_histogramView3; + HistogramBannerView m_bannerView; + Store * m_store; // TODO Do not duplicate this pointer + }; Responder * tabController() const; void reloadBannerView(); void initRangeParameters(); + void initYRangeParameters(int series); void initBarParameters(); void initBarSelection(); // return true if the window has scrolled bool moveSelection(int deltaIndex); - HistogramBannerView m_bannerView; - HistogramView m_view; Button m_settingButton; Store * m_store; + ContentView m_view; uint32_t * m_storeVersion; uint32_t * m_barVersion; uint32_t * m_rangeVersion; + int m_selectedSeries; int * m_selectedBarIndex; HistogramParameterController m_histogramParameterController; }; diff --git a/apps/statistics/histogram_view.cpp b/apps/statistics/histogram_view.cpp index 12d2c825b..aaca0be3e 100644 --- a/apps/statistics/histogram_view.cpp +++ b/apps/statistics/histogram_view.cpp @@ -1,4 +1,5 @@ #include "histogram_view.h" +#include "histogram_controller.h" #include #include @@ -6,12 +7,14 @@ using namespace Shared; namespace Statistics { -HistogramView::HistogramView(Store * store, BannerView * bannerView) : +HistogramView::HistogramView(HistogramController * controller, Store * store, int series, Shared::BannerView * bannerView) : CurveView(store, nullptr, bannerView, nullptr), + m_controller(controller), m_store(store), m_labels{}, m_highlightedBarStart(NAN), - m_highlightedBarEnd(NAN) + m_highlightedBarEnd(NAN), + m_series(series) { } @@ -27,13 +30,14 @@ void HistogramView::reload() { } void HistogramView::drawRect(KDContext * ctx, KDRect rect) const { + m_controller->setCurrentDrawnSeries(m_series); ctx->fillRect(rect, KDColorWhite); drawAxes(ctx, rect, Axis::Horizontal); 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(0, 1); // TODO Draw all the histograms - float context[] = {totalSize, 0}; + float totalSize = m_store->sumOfOccurrences(m_series); + float context[] = {totalSize, static_cast(m_series)}; if (isMainViewSelected()) { drawHistogram(ctx, rect, EvaluateHistogramAtAbscissa, m_store, context, m_store->firstDrawnBarAbscissa(), m_store->barWidth(), true, Palette::Select, Palette::YellowDark, m_highlightedBarStart, m_highlightedBarEnd); } else { diff --git a/apps/statistics/histogram_view.h b/apps/statistics/histogram_view.h index fc97095e3..3d7fd3a4d 100644 --- a/apps/statistics/histogram_view.h +++ b/apps/statistics/histogram_view.h @@ -8,19 +8,24 @@ namespace Statistics { +class HistogramController; + class HistogramView : public Shared::CurveView { public: - HistogramView(Store * store, Shared::BannerView * bannerView); + HistogramView(HistogramController * controller, Store * store, int series, Shared::BannerView * bannerView); + int series() const { return m_series; } void reload() override; void drawRect(KDContext * ctx, KDRect rect) const override; void setHighlight(float start, float end); private: char * label(Axis axis, int index) const override; + HistogramController * m_controller; Store * m_store; char m_labels[k_maxNumberOfXLabels][Poincare::PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits)]; static float EvaluateHistogramAtAbscissa(float abscissa, void * model, void * context); float m_highlightedBarStart; float m_highlightedBarEnd; + int m_series; }; } diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 26ada9855..75b096e6e 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -78,13 +78,28 @@ bool Store::scrollToSelectedBarIndex(int series, int index) { bool Store::isEmpty() { for (int i = 0; i < k_numberOfSeries; i ++) { - if (sumOfOccurrences(i) > 0) { + if (!seriesIsEmpty(i)) { return false; } } return true; } +int Store::numberOfNonEmptySeries() { + int result = 0; + for (int i = 0; i < k_numberOfSeries; i ++) { + if (!seriesIsEmpty(i)) { + result++; + } + } + return result; +} + +bool Store::seriesIsEmpty(int i) { + return sumOfOccurrences(i) == 0; +} + + /* Calculation */ double Store::sumOfOccurrences(int series) { diff --git a/apps/statistics/store.h b/apps/statistics/store.h index 1bcd40633..0934eb26e 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -23,6 +23,8 @@ public: // return true if the window has scrolled bool scrollToSelectedBarIndex(int series, int index); bool isEmpty(); + int numberOfNonEmptySeries(); + bool seriesIsEmpty(int i); // Calculation double sumOfOccurrences(int series); @@ -46,6 +48,7 @@ public: constexpr static float k_displayRightMarginRatio = 0.04f; constexpr static float k_displayBottomMarginRatio = 0.4f; constexpr static float k_displayLeftMarginRatio = 0.04f; + private: double defaultValue(int series, int i, int j) override; double sumOfValuesBetween(int series, double x1, double x2); From 4a89ff4ba593603ffde083d9ba4e70ee2b678fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 22 May 2018 17:48:07 +0200 Subject: [PATCH 029/156] [apps/statistics] Remove duplicate banner views --- apps/shared/curve_view.cpp | 21 +++++++++++---------- apps/shared/curve_view.h | 10 ++++++++-- apps/statistics/histogram_controller.cpp | 3 +++ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/apps/shared/curve_view.cpp b/apps/shared/curve_view.cpp index c1d2945b4..c263b65ed 100644 --- a/apps/shared/curve_view.cpp +++ b/apps/shared/curve_view.cpp @@ -10,7 +10,7 @@ using namespace Poincare; namespace Shared { CurveView::CurveView(CurveViewRange * curveViewRange, CurveViewCursor * curveViewCursor, BannerView * bannerView, - View * cursorView, View * okView) : + View * cursorView, View * okView, bool displayBanner) : View(), m_bannerView(bannerView), m_curveViewCursor(curveViewCursor), @@ -18,7 +18,8 @@ CurveView::CurveView(CurveViewRange * curveViewRange, CurveViewCursor * curveVie m_cursorView(cursorView), m_okView(okView), m_mainViewSelected(false), - m_drawnRangeVersion(0) + m_drawnRangeVersion(0), + m_displayBanner(displayBanner) { } @@ -27,7 +28,7 @@ void CurveView::reload() { if (m_drawnRangeVersion != rangeVersion) { // FIXME: This should also be called if the *curve* changed m_drawnRangeVersion = rangeVersion; - KDCoordinate bannerHeight = m_bannerView != nullptr ? m_bannerView->bounds().height() : 0; + KDCoordinate bannerHeight = (m_bannerView != nullptr && m_displayBanner) ? m_bannerView->bounds().height() : 0; markRectAsDirty(KDRect(0, 0, bounds().width(), bounds().height() - bannerHeight)); if (label(Axis::Horizontal, 0) != nullptr) { computeLabels(Axis::Horizontal); @@ -514,7 +515,7 @@ void CurveView::layoutSubviews() { if (m_curveViewCursor != nullptr && m_cursorView != nullptr) { m_cursorView->setFrame(cursorFrame()); } - if (m_bannerView != nullptr) { + if (m_bannerView != nullptr && m_displayBanner) { m_bannerView->setFrame(bannerFrame()); } if (m_okView != nullptr) { @@ -530,7 +531,7 @@ KDRect CurveView::cursorFrame() { KDCoordinate yCursorPixelPosition = std::round(floatToPixel(Axis::Vertical, m_curveViewCursor->y())); cursorFrame = KDRect(xCursorPixelPosition - (cursorSize.width()-1)/2, yCursorPixelPosition - (cursorSize.height()-1)/2, cursorSize.width(), cursorSize.height()); if (cursorSize.height() == 0) { - KDCoordinate bannerHeight = m_bannerView != nullptr ? m_bannerView->minimalSizeForOptimalDisplay().height() : 0; + KDCoordinate bannerHeight = (m_bannerView != nullptr && m_displayBanner) ? m_bannerView->minimalSizeForOptimalDisplay().height() : 0; cursorFrame = KDRect(xCursorPixelPosition - (cursorSize.width()-1)/2, 0, cursorSize.width(),bounds().height()-bannerHeight); } } @@ -539,7 +540,7 @@ KDRect CurveView::cursorFrame() { KDRect CurveView::bannerFrame() { KDRect bannerFrame = KDRectZero; - if (m_bannerView && m_mainViewSelected) { + if (m_bannerView && m_displayBanner && m_mainViewSelected) { KDCoordinate bannerHeight = m_bannerView->minimalSizeForOptimalDisplay().height(); bannerFrame = KDRect(0, bounds().height()- bannerHeight, bounds().width(), bannerHeight); } @@ -550,7 +551,7 @@ KDRect CurveView::okFrame() { KDRect okFrame = KDRectZero; if (m_okView && m_mainViewSelected) { KDCoordinate bannerHeight = 0; - if (m_bannerView != nullptr) { + if (m_bannerView != nullptr && m_displayBanner) { bannerHeight = m_bannerView->minimalSizeForOptimalDisplay().height(); } KDSize okSize = m_okView->minimalSizeForOptimalDisplay(); @@ -560,7 +561,7 @@ KDRect CurveView::okFrame() { } int CurveView::numberOfSubviews() const { - return (m_bannerView != nullptr) + (m_cursorView != nullptr) + (m_okView != nullptr); + return (m_bannerView != nullptr && m_displayBanner) + (m_cursorView != nullptr) + (m_okView != nullptr); }; View * CurveView::subviewAtIndex(int index) { @@ -572,12 +573,12 @@ View * CurveView::subviewAtIndex(int index) { if (m_okView != nullptr) { return m_okView; } else { - if (m_bannerView != nullptr) { + if (m_bannerView != nullptr && m_displayBanner) { return m_bannerView; } } } - if (index == 1 && m_bannerView != nullptr && m_okView != nullptr) { + if (index == 1 && m_bannerView != nullptr && m_displayBanner && m_okView != nullptr) { return m_bannerView; } return m_cursorView; diff --git a/apps/shared/curve_view.h b/apps/shared/curve_view.h index 7552b2e85..eb1b24c5a 100644 --- a/apps/shared/curve_view.h +++ b/apps/shared/curve_view.h @@ -17,14 +17,19 @@ public: Horizontal = 0, Vertical = 1 }; - CurveView(CurveViewRange * curveViewRange = nullptr, CurveViewCursor * curveViewCursor = nullptr, - BannerView * bannerView = nullptr, View * cursorView = nullptr, View * okView = nullptr); + CurveView(CurveViewRange * curveViewRange = nullptr, + CurveViewCursor * curveViewCursor = nullptr, + BannerView * bannerView = nullptr, + View * cursorView = nullptr, + View * okView = nullptr, + bool displayBanner = true); virtual void reload(); // When the main view is selected, the banner view is visible bool isMainViewSelected() const; void selectMainView(bool mainViewSelected); void setCursorView(View * cursorView); void setBannerView(View * bannerView); + void setDisplayBannerView(bool display) { m_displayBanner = display; } void setOkView(View * okView); float resolution() const; protected: @@ -86,6 +91,7 @@ private: View * m_okView; bool m_mainViewSelected; uint32_t m_drawnRangeVersion; + bool m_displayBanner; }; } diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index a1ae0a6f2..25bf9e09a 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -17,6 +17,9 @@ HistogramController::ContentView::ContentView(HistogramController * controller, m_bannerView(), m_store(store) { + m_histogramView1.setDisplayBannerView(false); + m_histogramView2.setDisplayBannerView(false); + m_histogramView3.setDisplayBannerView(false); } HistogramView * HistogramController::ContentView::histogramViewAtIndex(int index) { From f2a30405656ce8732648fe7b86df40b13a968ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 22 May 2018 18:00:25 +0200 Subject: [PATCH 030/156] [apps/statistics] Histogram navigation --- apps/statistics/histogram_controller.cpp | 58 +++++++++++++++++++++--- apps/statistics/histogram_controller.h | 3 +- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 25bf9e09a..74d206e6d 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -33,6 +33,22 @@ int HistogramController::ContentView::seriesOfSubviewAtIndex(int index) { return static_cast(subviewAtIndex(index))->series(); } +int HistogramController::ContentView::indexOfSubviewAtSeries(int series) { + int displayedSubviewIndex = 0; + for (int i = 0; i < 3; i++) { + if (!m_store->seriesIsEmpty(i)) { + if (i == series) { + return displayedSubviewIndex; + } + displayedSubviewIndex++; + } else if (i == series) { + return -1; + } + } + assert(false); + return -1; +} + void HistogramController::ContentView::layoutSubviews() { int numberSubviews = numberOfSubviews(); KDCoordinate subviewHeight = bounds().height()/numberSubviews; @@ -133,8 +149,21 @@ void HistogramController::viewWillAppear() { bool HistogramController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Down) { - if (!m_view.histogramViewAtIndex(m_selectedSeries)->isMainViewSelected()) { + bool newSelectedSeries = false; + if (m_selectedSeries < 0) { header()->setSelectedButton(-1); + m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); + newSelectedSeries = true; + } else { + int currentSelectedSubview = m_view.indexOfSubviewAtSeries(m_selectedSeries); + if (currentSelectedSubview < m_view.numberOfSubviews() - 1) { + m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); + m_selectedSeries = m_view.seriesOfSubviewAtIndex(currentSelectedSubview+1); + *m_selectedBarIndex = 0; + newSelectedSeries = true; + } + } + if (newSelectedSeries) { m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); reloadBannerView(); m_view.reload(); @@ -144,16 +173,28 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { return false; } if (event == Ion::Events::Up) { - if (!m_view.histogramViewAtIndex(m_selectedSeries)->isMainViewSelected()) { + if (m_selectedSeries < 0) { header()->setSelectedButton(-1); app()->setFirstResponder(tabController()); return true; } m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); - header()->setSelectedButton(0); + int currentSelectedSubview = m_view.indexOfSubviewAtSeries(m_selectedSeries); + if (currentSelectedSubview > 0) { + assert(currentSelectedSubview > 0); + m_selectedSeries = m_view.seriesOfSubviewAtIndex(currentSelectedSubview-1); + m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); + *m_selectedBarIndex = 0; + reloadBannerView(); + m_view.reload(); + app()->setFirstResponder(this); + } else { + m_selectedSeries = -1; + header()->setSelectedButton(0); + } return true; } - if (m_view.histogramViewAtIndex(m_selectedSeries)->isMainViewSelected() && (event == Ion::Events::Left || event == Ion::Events::Right)) { + if (m_selectedSeries >= 0 && (event == Ion::Events::Left || event == Ion::Events::Right)) { int direction = event == Ion::Events::Left ? -1 : 1; if (moveSelection(direction)) { reloadBannerView(); @@ -182,16 +223,19 @@ void HistogramController::didBecomeFirstResponder() { initBarSelection(); reloadBannerView(); } - if (!m_view.histogramViewAtIndex(m_selectedSeries)->isMainViewSelected()) { + if (m_selectedSeries < 0) { header()->setSelectedButton(0); } else { - m_view.histogramViewAtIndex(m_selectedSeries)->setHighlight(m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex), m_store->endOfBarAtIndex(0, *m_selectedBarIndex)); + m_view.histogramViewAtIndex(m_selectedSeries)->setHighlight(m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex), m_store->endOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex)); } } void HistogramController::willExitResponderChain(Responder * nextFirstResponder) { if (nextFirstResponder == nullptr || nextFirstResponder == tabController()) { - m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); + if (m_selectedSeries >= 0) { + m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); + m_selectedSeries = -1; + } header()->setSelectedButton(-1); m_view.reload(); } diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index df4ba04aa..21658f19f 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -47,10 +47,11 @@ private: void reload() {} HistogramView * histogramViewAtIndex(int index); int seriesOfSubviewAtIndex(int index); + int indexOfSubviewAtSeries(int series); HistogramBannerView * bannerView() { return &m_bannerView; } void layoutSubviews() override; - private: int numberOfSubviews() const override; + private: View * subviewAtIndex(int index) override; HistogramView m_histogramView1; HistogramView m_histogramView2; From 86c2b7a365b1e133bb0278af0cfc3cc96d0b10ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 22 May 2018 18:00:57 +0200 Subject: [PATCH 031/156] [apps/statistics] Fix HistogramController::reloadBannerView and initBarParameters --- apps/statistics/histogram_controller.cpp | 28 +++++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 74d206e6d..2e927b2e6 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -256,14 +256,18 @@ void HistogramController::reloadBannerView() { numberOfChar += legendLength; // Add lower bound - double lowerBound = m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex); - numberOfChar += PrintFloat::convertFloatToText(lowerBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + if (m_selectedSeries >= 0) { + double lowerBound = m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex); + numberOfChar += PrintFloat::convertFloatToText(lowerBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + } buffer[numberOfChar++] = ';'; // Add upper bound - double upperBound = m_store->endOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex); - numberOfChar += PrintFloat::convertFloatToText(upperBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + if (m_selectedSeries >= 0) { + double upperBound = m_store->endOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex); + numberOfChar += PrintFloat::convertFloatToText(upperBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + } buffer[numberOfChar++] = '['; @@ -280,8 +284,11 @@ void HistogramController::reloadBannerView() { legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; - double size = m_store->heightOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex); - numberOfChar += PrintFloat::convertFloatToText(size, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + double size = 0; + if (m_selectedSeries >= 0) { + size = m_store->heightOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex); + numberOfChar += PrintFloat::convertFloatToText(size, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + } // Padding for (int i = numberOfChar; i < k_maxLegendLength; i++) { buffer[numberOfChar++] = ' '; @@ -295,8 +302,10 @@ void HistogramController::reloadBannerView() { legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; - double frequency = size/m_store->sumOfOccurrences(m_selectedSeries); - numberOfChar += PrintFloat::convertFloatToText(frequency, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + if (m_selectedSeries >= 0) { + double frequency = size/m_store->sumOfOccurrences(m_selectedSeries); + numberOfChar += PrintFloat::convertFloatToText(frequency, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + } // Padding for (int i = numberOfChar; i < k_maxLegendLength; i++) { buffer[numberOfChar++] = ' '; @@ -372,6 +381,9 @@ void HistogramController::initBarParameters() { void HistogramController::initBarSelection() { *m_selectedBarIndex = 0; + if (m_selectedSeries < 0) { + return; + } while ((m_store->heightOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex) == 0 || m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex) < m_store->firstDrawnBarAbscissa()) && *m_selectedBarIndex < m_store->numberOfBars(m_selectedSeries)) { *m_selectedBarIndex = *m_selectedBarIndex+1; From 620b432dd1dae4f171eb2e41e0b87d05f54a1e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 10:26:07 +0200 Subject: [PATCH 032/156] [apps/stats] Remove Histogram settings button --- apps/statistics/histogram_controller.cpp | 51 +++++------------------- apps/statistics/histogram_controller.h | 5 --- 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 2e927b2e6..d37510221 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -89,11 +89,6 @@ View * HistogramController::ContentView::subviewAtIndex(int index) { HistogramController::HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, int series, uint32_t * storeVersion, uint32_t * barVersion, uint32_t * rangeVersion, int * selectedBarIndex) : ViewController(parentResponder), ButtonRowDelegate(header, nullptr), - m_settingButton(this, I18n::Message::HistogramSet, Invocation([](void * context, void * sender) { - HistogramController * histogramController = (HistogramController *) context; - StackViewController * stack = ((StackViewController *)histogramController->stackController()); - stack->push(histogramController->histogramParameterController()); - }, this)), m_store(store), m_view(this, store), m_storeVersion(storeVersion), @@ -114,13 +109,6 @@ void HistogramController::setCurrentDrawnSeries(int series) { initYRangeParameters(series); } -int HistogramController::numberOfButtons(ButtonRowController::Position) const { - return isEmpty() ? 0 : 1; -} -Button * HistogramController::buttonAtIndex(int index, ButtonRowController::Position) const { - return (Button *)&m_settingButton; -} - bool HistogramController::isEmpty() const { return m_store->isEmpty(); } @@ -141,7 +129,6 @@ void HistogramController::viewWillAppear() { if (m_selectedSeries < 0) { m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); - header()->setSelectedButton(-1); } reloadBannerView(); m_view.reload(); @@ -149,21 +136,11 @@ void HistogramController::viewWillAppear() { bool HistogramController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Down) { - bool newSelectedSeries = false; - if (m_selectedSeries < 0) { - header()->setSelectedButton(-1); - m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); - newSelectedSeries = true; - } else { - int currentSelectedSubview = m_view.indexOfSubviewAtSeries(m_selectedSeries); - if (currentSelectedSubview < m_view.numberOfSubviews() - 1) { - m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); - m_selectedSeries = m_view.seriesOfSubviewAtIndex(currentSelectedSubview+1); - *m_selectedBarIndex = 0; - newSelectedSeries = true; - } - } - if (newSelectedSeries) { + int currentSelectedSubview = m_view.indexOfSubviewAtSeries(m_selectedSeries); + if (currentSelectedSubview < m_view.numberOfSubviews() - 1) { + m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); + m_selectedSeries = m_view.seriesOfSubviewAtIndex(currentSelectedSubview+1); + *m_selectedBarIndex = 0; m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); reloadBannerView(); m_view.reload(); @@ -173,11 +150,6 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { return false; } if (event == Ion::Events::Up) { - if (m_selectedSeries < 0) { - header()->setSelectedButton(-1); - app()->setFirstResponder(tabController()); - return true; - } m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); int currentSelectedSubview = m_view.indexOfSubviewAtSeries(m_selectedSeries); if (currentSelectedSubview > 0) { @@ -185,13 +157,12 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { m_selectedSeries = m_view.seriesOfSubviewAtIndex(currentSelectedSubview-1); m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); *m_selectedBarIndex = 0; - reloadBannerView(); - m_view.reload(); app()->setFirstResponder(this); } else { - m_selectedSeries = -1; - header()->setSelectedButton(0); + app()->setFirstResponder(tabController()); } + reloadBannerView(); + m_view.reload(); return true; } if (m_selectedSeries >= 0 && (event == Ion::Events::Left || event == Ion::Events::Right)) { @@ -224,10 +195,9 @@ void HistogramController::didBecomeFirstResponder() { reloadBannerView(); } if (m_selectedSeries < 0) { - header()->setSelectedButton(0); - } else { - m_view.histogramViewAtIndex(m_selectedSeries)->setHighlight(m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex), m_store->endOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex)); + m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); } + m_view.histogramViewAtIndex(m_selectedSeries)->setHighlight(m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex), m_store->endOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex)); } void HistogramController::willExitResponderChain(Responder * nextFirstResponder) { @@ -236,7 +206,6 @@ void HistogramController::willExitResponderChain(Responder * nextFirstResponder) m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); m_selectedSeries = -1; } - header()->setSelectedButton(-1); m_view.reload(); } } diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index 21658f19f..bd7a6257f 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -18,10 +18,6 @@ public: HistogramParameterController * histogramParameterController() { return &m_histogramParameterController; } void setCurrentDrawnSeries(int series); - // ButtonRowDelegate - int numberOfButtons(ButtonRowController::Position) const override; - Button * buttonAtIndex(int index, ButtonRowController::Position position) const override; - // AlternateEmptyViewDelegate bool isEmpty() const override; I18n::Message emptyMessage() override; @@ -67,7 +63,6 @@ private: void initBarSelection(); // return true if the window has scrolled bool moveSelection(int deltaIndex); - Button m_settingButton; Store * m_store; ContentView m_view; uint32_t * m_storeVersion; From 645fcdfcd8e8c42ebc514ebc0055114e9d843b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 11:09:09 +0200 Subject: [PATCH 033/156] [apps/stats] Display the banner view --- apps/statistics/histogram_controller.cpp | 62 +++++++++++++++--------- apps/statistics/histogram_controller.h | 2 +- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index d37510221..ed8b9556a 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -22,6 +22,13 @@ HistogramController::ContentView::ContentView(HistogramController * controller, m_histogramView3.setDisplayBannerView(false); } +void HistogramController::ContentView::reload() { + layoutSubviews(); + m_histogramView1.reload(); + m_histogramView2.reload(); + m_histogramView3.reload(); +} + HistogramView * HistogramController::ContentView::histogramViewAtIndex(int index) { assert(index >= 0 && index < 3); HistogramView * views[] = {&m_histogramView1, &m_histogramView2, &m_histogramView3}; @@ -29,7 +36,7 @@ HistogramView * HistogramController::ContentView::histogramViewAtIndex(int inde } int HistogramController::ContentView::seriesOfSubviewAtIndex(int index) { - assert(index >= 0 && index < numberOfSubviews()); + assert(index >= 0 && index < numberOfSubviews() - 1); return static_cast(subviewAtIndex(index))->series(); } @@ -50,8 +57,10 @@ int HistogramController::ContentView::indexOfSubviewAtSeries(int series) { } void HistogramController::ContentView::layoutSubviews() { - int numberSubviews = numberOfSubviews(); - KDCoordinate subviewHeight = bounds().height()/numberSubviews; + int numberHistogramSubviews = m_store->numberOfNonEmptySeries(); + assert(numberHistogramSubviews > 0); + KDCoordinate bannerHeight = m_bannerView.minimalSizeForOptimalDisplay().height(); + KDCoordinate subviewHeight = (bounds().height() - bannerHeight)/numberHistogramSubviews; int displayedSubviewIndex = 0; for (int i = 0; i < 3; i++) { if (!m_store->seriesIsEmpty(i)) { @@ -60,15 +69,20 @@ void HistogramController::ContentView::layoutSubviews() { displayedSubviewIndex++; } } + KDRect frame = KDRect(0, bounds().height()- bannerHeight, bounds().width(), bannerHeight); + m_bannerView.setFrame(frame); } int HistogramController::ContentView::numberOfSubviews() const { int result = m_store->numberOfNonEmptySeries(); assert(result <= Store::k_numberOfSeries); - return result; + return result + 1; // +1 for the banner view } View * HistogramController::ContentView::subviewAtIndex(int index) { + if (index == numberOfSubviews() -1) { + return &m_bannerView; + } int seriesIndex = 0; int nonEmptySeriesIndex = 0; while (nonEmptySeriesIndex < index && seriesIndex < Store::k_numberOfSeries) { @@ -78,9 +92,7 @@ View * HistogramController::ContentView::subviewAtIndex(int index) { seriesIndex++; } if (nonEmptySeriesIndex == index) { - assert(seriesIndex >=0 && seriesIndex < 3); - View * views[] = {&m_histogramView1, &m_histogramView2, &m_histogramView3}; - return views[seriesIndex]; + return histogramViewAtIndex(seriesIndex); } assert(false); return nullptr; @@ -135,9 +147,10 @@ void HistogramController::viewWillAppear() { } bool HistogramController::handleEvent(Ion::Events::Event event) { + assert(m_selectedSeries >= 0); if (event == Ion::Events::Down) { int currentSelectedSubview = m_view.indexOfSubviewAtSeries(m_selectedSeries); - if (currentSelectedSubview < m_view.numberOfSubviews() - 1) { + if (currentSelectedSubview < m_view.numberOfSubviews() - 2) { m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); m_selectedSeries = m_view.seriesOfSubviewAtIndex(currentSelectedSubview+1); *m_selectedBarIndex = 0; @@ -177,6 +190,10 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { } void HistogramController::didBecomeFirstResponder() { + if (m_selectedSeries < 0) { + m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); + m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); + } uint32_t storeChecksum = m_store->storeChecksum(); if (*m_storeVersion != storeChecksum) { *m_storeVersion = storeChecksum; @@ -194,9 +211,6 @@ void HistogramController::didBecomeFirstResponder() { initBarSelection(); reloadBannerView(); } - if (m_selectedSeries < 0) { - m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); - } m_view.histogramViewAtIndex(m_selectedSeries)->setHighlight(m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex), m_store->endOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex)); } @@ -285,15 +299,16 @@ void HistogramController::reloadBannerView() { bool HistogramController::moveSelection(int deltaIndex) { int newSelectedBarIndex = *m_selectedBarIndex; - if (deltaIndex > 0) { - do { - newSelectedBarIndex++; - } while (m_store->heightOfBarAtIndex(m_selectedSeries, newSelectedBarIndex) == 0 && newSelectedBarIndex < m_store->numberOfBars(m_selectedSeries)); } else { - do { - newSelectedBarIndex--; - } while (m_store->heightOfBarAtIndex(m_selectedSeries, newSelectedBarIndex) == 0 && newSelectedBarIndex >= 0); - } - if (newSelectedBarIndex >= 0 && newSelectedBarIndex < m_store->numberOfBars(m_selectedSeries) && *m_selectedBarIndex != newSelectedBarIndex) { + do { + newSelectedBarIndex+=deltaIndex; + } while (m_store->heightOfBarAtIndex(m_selectedSeries, newSelectedBarIndex) == 0 + && newSelectedBarIndex >= 0 + && newSelectedBarIndex < m_store->numberOfBars(m_selectedSeries)); + + if (newSelectedBarIndex >= 0 + && newSelectedBarIndex < m_store->numberOfBars(m_selectedSeries) + && *m_selectedBarIndex != newSelectedBarIndex) + { *m_selectedBarIndex = newSelectedBarIndex; m_view.histogramViewAtIndex(m_selectedSeries)->setHighlight(m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex), m_store->endOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex)); m_store->scrollToSelectedBarIndex(m_selectedSeries, *m_selectedBarIndex); @@ -303,6 +318,7 @@ bool HistogramController::moveSelection(int deltaIndex) { } void HistogramController::initRangeParameters() { + assert(m_selectedSeries >= 0 && m_store->sumOfOccurrences(m_selectedSeries) > 0); float min = m_store->firstDrawnBarAbscissa(); float max = m_store->maxValue(m_selectedSeries); float barWidth = m_store->barWidth(); @@ -323,6 +339,7 @@ void HistogramController::initRangeParameters() { } void HistogramController::initYRangeParameters(int series) { + assert(series >= 0 && m_store->sumOfOccurrences(series) > 0); float yMax = -FLT_MAX; for (int index = 0; index < m_store->numberOfBars(series); index++) { float size = m_store->heightOfBarAtIndex(series, index); @@ -337,6 +354,7 @@ void HistogramController::initYRangeParameters(int series) { } void HistogramController::initBarParameters() { + assert(m_selectedSeries >= 0 && m_store->sumOfOccurrences(m_selectedSeries) > 0); float min = m_store->minValue(m_selectedSeries); float max = m_store->maxValue(m_selectedSeries); max = min >= max ? min + std::pow(10.0f, std::floor(std::log10(std::fabs(min)))-1.0f) : max; @@ -349,10 +367,8 @@ void HistogramController::initBarParameters() { } void HistogramController::initBarSelection() { + assert(m_selectedSeries >= 0 && m_store->sumOfOccurrences(m_selectedSeries) > 0); *m_selectedBarIndex = 0; - if (m_selectedSeries < 0) { - return; - } while ((m_store->heightOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex) == 0 || m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex) < m_store->firstDrawnBarAbscissa()) && *m_selectedBarIndex < m_store->numberOfBars(m_selectedSeries)) { *m_selectedBarIndex = *m_selectedBarIndex+1; diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index bd7a6257f..d599b6c7c 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -40,7 +40,7 @@ private: class ContentView : public View { public: ContentView(HistogramController * controller, Store * store); - void reload() {} + void reload(); HistogramView * histogramViewAtIndex(int index); int seriesOfSubviewAtIndex(int index); int indexOfSubviewAtSeries(int series); From 8980a7d90ad0608d7798a3ddec6d6d7bd3546a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 11:26:18 +0200 Subject: [PATCH 034/156] [apps/stats] Fix histogram display if series 0 is empty --- apps/statistics/histogram_controller.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index ed8b9556a..5e76144fd 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -84,16 +84,16 @@ View * HistogramController::ContentView::subviewAtIndex(int index) { return &m_bannerView; } int seriesIndex = 0; - int nonEmptySeriesIndex = 0; - while (nonEmptySeriesIndex < index && seriesIndex < Store::k_numberOfSeries) { + int nonEmptySeriesIndex = -1; + while (seriesIndex < Store::k_numberOfSeries) { if (!m_store->seriesIsEmpty(seriesIndex)) { nonEmptySeriesIndex++; + if (nonEmptySeriesIndex == index) { + return histogramViewAtIndex(seriesIndex); + } } seriesIndex++; } - if (nonEmptySeriesIndex == index) { - return histogramViewAtIndex(seriesIndex); - } assert(false); return nullptr; } @@ -190,7 +190,10 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { } void HistogramController::didBecomeFirstResponder() { - if (m_selectedSeries < 0) { + if (m_selectedSeries < 0 || m_store->sumOfOccurrences(m_selectedSeries) == 0) { + if (m_selectedSeries >= 0) { + m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); + } m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); } From a9ec0a38057750dd174b717a26055af5e84e99a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 11:30:59 +0200 Subject: [PATCH 035/156] [apps/stats] Fix histogram dirty tracking --- apps/shared/curve_view.h | 1 + apps/statistics/histogram_view.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/shared/curve_view.h b/apps/shared/curve_view.h index eb1b24c5a..b9d5e4822 100644 --- a/apps/shared/curve_view.h +++ b/apps/shared/curve_view.h @@ -30,6 +30,7 @@ public: void setCursorView(View * cursorView); void setBannerView(View * bannerView); void setDisplayBannerView(bool display) { m_displayBanner = display; } + bool displayBannerView() const { return m_displayBanner; } void setOkView(View * okView); float resolution() const; protected: diff --git a/apps/statistics/histogram_view.cpp b/apps/statistics/histogram_view.cpp index aaca0be3e..2f729f761 100644 --- a/apps/statistics/histogram_view.cpp +++ b/apps/statistics/histogram_view.cpp @@ -25,7 +25,7 @@ void HistogramView::reload() { /* We deliberately do not mark as dirty the frame of the banner view to avoid *unpleasant blinking of the drawing of the banner view. */ KDRect dirtyZone(KDRect(pixelLowerBound, 0, pixelUpperBound-pixelLowerBound, - bounds().height()-m_bannerView->bounds().height())); + bounds().height() - (displayBannerView() ? m_bannerView->bounds().height() : 0))); markRectAsDirty(dirtyZone); } From 9c1c16c8ddb185c105f230ae602b05a8f3330c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 11:54:58 +0200 Subject: [PATCH 036/156] [apps/stats] Color the histogram --- apps/statistics/histogram_controller.cpp | 7 ++++--- apps/statistics/histogram_view.cpp | 16 ++++++++-------- apps/statistics/histogram_view.h | 5 ++++- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 5e76144fd..a91490116 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -11,9 +11,10 @@ using namespace Shared; namespace Statistics { HistogramController::ContentView::ContentView(HistogramController * controller, Store * store) : - m_histogramView1(controller, store, 0, &m_bannerView), - m_histogramView2(controller, store, 1, &m_bannerView), - m_histogramView3(controller, store, 2, &m_bannerView), + m_histogramView1(controller, store, 0, &m_bannerView, Palette::Red), + m_histogramView2(controller, store, 1, &m_bannerView, Palette::Blue), + m_histogramView3(controller, store, 2, &m_bannerView, Palette::Green), + // TODO Share colors with stats/store_controller m_bannerView(), m_store(store) { diff --git a/apps/statistics/histogram_view.cpp b/apps/statistics/histogram_view.cpp index 2f729f761..28103299f 100644 --- a/apps/statistics/histogram_view.cpp +++ b/apps/statistics/histogram_view.cpp @@ -7,25 +7,25 @@ using namespace Shared; namespace Statistics { -HistogramView::HistogramView(HistogramController * controller, Store * store, int series, Shared::BannerView * bannerView) : +HistogramView::HistogramView(HistogramController * controller, Store * store, int series, Shared::BannerView * bannerView, KDColor selectedHistogramColor, KDColor notSelectedHistogramColor, KDColor selectedBarColor) : CurveView(store, nullptr, bannerView, nullptr), m_controller(controller), m_store(store), m_labels{}, m_highlightedBarStart(NAN), m_highlightedBarEnd(NAN), - m_series(series) + m_series(series), + m_selectedHistogramColor(selectedHistogramColor), + m_notSelectedHistogramColor(notSelectedHistogramColor), + m_selectedBarColor(selectedBarColor) { } void HistogramView::reload() { CurveView::reload(); - float pixelLowerBound = floatToPixel(Axis::Horizontal, m_highlightedBarStart)-2; - float pixelUpperBound = floatToPixel(Axis::Horizontal, m_highlightedBarEnd)+2; /* We deliberately do not mark as dirty the frame of the banner view to avoid *unpleasant blinking of the drawing of the banner view. */ - KDRect dirtyZone(KDRect(pixelLowerBound, 0, pixelUpperBound-pixelLowerBound, - bounds().height() - (displayBannerView() ? m_bannerView->bounds().height() : 0))); + KDRect dirtyZone(KDRect(0, 0, bounds().width(), bounds().height() - (displayBannerView() ? m_bannerView->bounds().height() : 0))); markRectAsDirty(dirtyZone); } @@ -39,9 +39,9 @@ void HistogramView::drawRect(KDContext * ctx, KDRect rect) const { float totalSize = m_store->sumOfOccurrences(m_series); float context[] = {totalSize, static_cast(m_series)}; if (isMainViewSelected()) { - drawHistogram(ctx, rect, EvaluateHistogramAtAbscissa, m_store, context, 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, m_selectedHistogramColor, m_selectedBarColor, m_highlightedBarStart, m_highlightedBarEnd); } else { - drawHistogram(ctx, rect, EvaluateHistogramAtAbscissa, m_store, context, 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, m_notSelectedHistogramColor, m_selectedBarColor); } } diff --git a/apps/statistics/histogram_view.h b/apps/statistics/histogram_view.h index 3d7fd3a4d..d67ad3576 100644 --- a/apps/statistics/histogram_view.h +++ b/apps/statistics/histogram_view.h @@ -12,7 +12,7 @@ class HistogramController; class HistogramView : public Shared::CurveView { public: - HistogramView(HistogramController * controller, Store * store, int series, Shared::BannerView * bannerView); + HistogramView(HistogramController * controller, Store * store, int series, Shared::BannerView * bannerView, KDColor selectedHistogramColor = Palette::Select, KDColor notSelectedHistogramColor = Palette::GreyMiddle, KDColor selectedBarColor = Palette::YellowDark); int series() const { return m_series; } void reload() override; void drawRect(KDContext * ctx, KDRect rect) const override; @@ -26,6 +26,9 @@ private: float m_highlightedBarStart; float m_highlightedBarEnd; int m_series; + KDColor m_selectedHistogramColor; + KDColor m_notSelectedHistogramColor; + KDColor m_selectedBarColor; }; } From db94ba351f17e6d6bdc1cb2217afbf273f6f76f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 13:52:38 +0200 Subject: [PATCH 037/156] [apps/stats] Hide the banner view if no histogram selected --- apps/statistics/histogram_controller.cpp | 53 +++++++++++++++++------- apps/statistics/histogram_controller.h | 7 +++- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index a91490116..b3e4b7696 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -16,6 +16,7 @@ HistogramController::ContentView::ContentView(HistogramController * controller, m_histogramView3(controller, store, 2, &m_bannerView, Palette::Green), // TODO Share colors with stats/store_controller m_bannerView(), + m_displayBanner(false), m_store(store) { m_histogramView1.setDisplayBannerView(false); @@ -57,21 +58,10 @@ int HistogramController::ContentView::indexOfSubviewAtSeries(int series) { return -1; } -void HistogramController::ContentView::layoutSubviews() { - int numberHistogramSubviews = m_store->numberOfNonEmptySeries(); - assert(numberHistogramSubviews > 0); - KDCoordinate bannerHeight = m_bannerView.minimalSizeForOptimalDisplay().height(); - KDCoordinate subviewHeight = (bounds().height() - bannerHeight)/numberHistogramSubviews; - int displayedSubviewIndex = 0; - for (int i = 0; i < 3; i++) { - if (!m_store->seriesIsEmpty(i)) { - KDRect frame = KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), subviewHeight); - subviewAtIndex(displayedSubviewIndex)->setFrame(frame); - displayedSubviewIndex++; - } +void HistogramController::ContentView::drawRect(KDContext * ctx, KDRect rect) const { + if (!m_displayBanner) { + ctx->fillRect(bannerFrame(), KDColorWhite); } - KDRect frame = KDRect(0, bounds().height()- bannerHeight, bounds().width(), bannerHeight); - m_bannerView.setFrame(frame); } int HistogramController::ContentView::numberOfSubviews() const { @@ -80,6 +70,12 @@ int HistogramController::ContentView::numberOfSubviews() const { return result + 1; // +1 for the banner view } +KDRect HistogramController::ContentView::bannerFrame() const { + KDCoordinate bannerHeight = m_bannerView.minimalSizeForOptimalDisplay().height(); + KDRect frame = KDRect(0, bounds().height() - bannerHeight, bounds().width(), bannerHeight); + return frame; +} + View * HistogramController::ContentView::subviewAtIndex(int index) { if (index == numberOfSubviews() -1) { return &m_bannerView; @@ -99,6 +95,27 @@ View * HistogramController::ContentView::subviewAtIndex(int index) { return nullptr; } +void HistogramController::ContentView::layoutSubviews() { + int numberHistogramSubviews = m_store->numberOfNonEmptySeries(); + assert(numberHistogramSubviews > 0); + KDCoordinate bannerHeight = bannerFrame().height(); + KDCoordinate subviewHeight = (bounds().height() - bannerHeight)/numberHistogramSubviews; + int displayedSubviewIndex = 0; + for (int i = 0; i < 3; i++) { + if (!m_store->seriesIsEmpty(i)) { + KDRect frame = KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), subviewHeight); + subviewAtIndex(displayedSubviewIndex)->setFrame(frame); + displayedSubviewIndex++; + } + } + if (m_displayBanner) { + m_bannerView.setFrame(bannerFrame()); + } else { + KDRect frame = KDRect(0, bounds().height() - bannerHeight, bounds().width(), 0); + m_bannerView.setFrame(frame); + } +} + HistogramController::HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, int series, uint32_t * storeVersion, uint32_t * barVersion, uint32_t * rangeVersion, int * selectedBarIndex) : ViewController(parentResponder), ButtonRowDelegate(header, nullptr), @@ -139,6 +156,7 @@ const char * HistogramController::title() { } void HistogramController::viewWillAppear() { + m_view.setDisplayBanner(true); if (m_selectedSeries < 0) { m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); @@ -173,6 +191,7 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { *m_selectedBarIndex = 0; app()->setFirstResponder(this); } else { + m_view.setDisplayBanner(false); app()->setFirstResponder(tabController()); } reloadBannerView(); @@ -191,12 +210,14 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { } void HistogramController::didBecomeFirstResponder() { + m_view.setDisplayBanner(true); if (m_selectedSeries < 0 || m_store->sumOfOccurrences(m_selectedSeries) == 0) { if (m_selectedSeries >= 0) { m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); } m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); + m_view.reload(); } uint32_t storeChecksum = m_store->storeChecksum(); if (*m_storeVersion != storeChecksum) { @@ -223,6 +244,7 @@ void HistogramController::willExitResponderChain(Responder * nextFirstResponder) if (m_selectedSeries >= 0) { m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); m_selectedSeries = -1; + m_view.setDisplayBanner(false); } m_view.reload(); } @@ -233,6 +255,9 @@ Responder * HistogramController::tabController() const { } void HistogramController::reloadBannerView() { + if (m_selectedSeries < 0) { + return; + } char buffer[k_maxNumberOfCharacters+ PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)*2]; int numberOfChar = 0; diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index d599b6c7c..828c2782e 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -45,14 +45,19 @@ private: int seriesOfSubviewAtIndex(int index); int indexOfSubviewAtSeries(int series); HistogramBannerView * bannerView() { return &m_bannerView; } - void layoutSubviews() override; + void setDisplayBanner(bool display) { m_displayBanner = display; } + // View + void drawRect(KDContext * ctx, KDRect rect) const override; int numberOfSubviews() const override; private: + KDRect bannerFrame() const; View * subviewAtIndex(int index) override; + void layoutSubviews() override; HistogramView m_histogramView1; HistogramView m_histogramView2; HistogramView m_histogramView3; HistogramBannerView m_bannerView; + bool m_displayBanner; Store * m_store; // TODO Do not duplicate this pointer }; Responder * tabController() const; From ac59105a46578fcf959798a8ff3b142414fcfd73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 14:44:38 +0200 Subject: [PATCH 038/156] [apps/stats] Display the labels of the bottom histogram only --- apps/shared/curve_view.cpp | 22 +++++++++++++--------- apps/shared/curve_view.h | 2 +- apps/statistics/histogram_controller.cpp | 5 +++++ apps/statistics/histogram_view.cpp | 5 +++-- apps/statistics/histogram_view.h | 2 ++ 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/apps/shared/curve_view.cpp b/apps/shared/curve_view.cpp index c263b65ed..7e44df5a1 100644 --- a/apps/shared/curve_view.cpp +++ b/apps/shared/curve_view.cpp @@ -144,7 +144,7 @@ void CurveView::computeLabels(Axis axis) { } } -void CurveView::drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin) const { +void CurveView::drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin, bool graduationOnly) const { float step = gridUnit(axis); float start = 2.0f*step*(std::ceil(min(axis)/(2.0f*step))); float end = max(axis); @@ -155,18 +155,22 @@ void CurveView::drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOr if (x == x-step || x == x+step) { return; } - KDSize textSize = KDText::stringSize(label(axis, i), KDText::FontSize::Small); - KDPoint origin(std::round(floatToPixel(Axis::Horizontal, x)) - textSize.width()/2, std::round(floatToPixel(Axis::Vertical, 0.0f)) + k_labelMargin); KDRect graduation(std::round(floatToPixel(Axis::Horizontal, x)), std::round(floatToPixel(Axis::Vertical, 0.0f)) -(k_labelGraduationLength-2)/2, 1, k_labelGraduationLength); if (axis == Axis::Vertical) { - origin = KDPoint(std::round(floatToPixel(Axis::Horizontal, 0.0f)) + k_labelMargin, std::round(floatToPixel(Axis::Vertical, x)) - textSize.height()/2); graduation = KDRect(std::round(floatToPixel(Axis::Horizontal, 0.0f))-(k_labelGraduationLength-2)/2, std::round(floatToPixel(Axis::Vertical, x)), k_labelGraduationLength, 1); } - if (-step < x && x < step && shiftOrigin) { - origin = KDPoint(std::round(floatToPixel(Axis::Horizontal, 0.0f)) + k_labelMargin, std::round(floatToPixel(Axis::Vertical, 0.0f)) + k_labelMargin); - } - if (rect.intersects(KDRect(origin, KDText::stringSize(label(axis, i), KDText::FontSize::Small)))) { - ctx->blendString(label(axis, i), origin, KDText::FontSize::Small, KDColorBlack); + if (!graduationOnly) { + KDSize textSize = KDText::stringSize(label(axis, i), KDText::FontSize::Small); + KDPoint origin(std::round(floatToPixel(Axis::Horizontal, x)) - textSize.width()/2, std::round(floatToPixel(Axis::Vertical, 0.0f)) + k_labelMargin); + if (axis == Axis::Vertical) { + origin = KDPoint(std::round(floatToPixel(Axis::Horizontal, 0.0f)) + k_labelMargin, std::round(floatToPixel(Axis::Vertical, x)) - textSize.height()/2); + } + if (-step < x && x < step && shiftOrigin) { + origin = KDPoint(std::round(floatToPixel(Axis::Horizontal, 0.0f)) + k_labelMargin, std::round(floatToPixel(Axis::Vertical, 0.0f)) + k_labelMargin); + } + if (rect.intersects(KDRect(origin, KDText::stringSize(label(axis, i), KDText::FontSize::Small)))) { + ctx->blendString(label(axis, i), origin, KDText::FontSize::Small, KDColorBlack); + } } ctx->fillRect(graduation, KDColorBlack); i++; diff --git a/apps/shared/curve_view.h b/apps/shared/curve_view.h index b9d5e4822..e79f39def 100644 --- a/apps/shared/curve_view.h +++ b/apps/shared/curve_view.h @@ -58,7 +58,7 @@ protected: 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; void computeLabels(Axis axis); - void drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin) const; + void drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin, bool graduationOnly = false) const; View * m_bannerView; CurveViewCursor * m_curveViewCursor; private: diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index b3e4b7696..2a546fbae 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -101,13 +101,18 @@ void HistogramController::ContentView::layoutSubviews() { KDCoordinate bannerHeight = bannerFrame().height(); KDCoordinate subviewHeight = (bounds().height() - bannerHeight)/numberHistogramSubviews; int displayedSubviewIndex = 0; + int bottomSeriesDisplayed = -1; for (int i = 0; i < 3; i++) { if (!m_store->seriesIsEmpty(i)) { + histogramViewAtIndex(i)->setDisplayLabels(false); KDRect frame = KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), subviewHeight); subviewAtIndex(displayedSubviewIndex)->setFrame(frame); displayedSubviewIndex++; + bottomSeriesDisplayed = i; } } + assert(bottomSeriesDisplayed >= 0); + histogramViewAtIndex(bottomSeriesDisplayed)->setDisplayLabels(true); if (m_displayBanner) { m_bannerView.setFrame(bannerFrame()); } else { diff --git a/apps/statistics/histogram_view.cpp b/apps/statistics/histogram_view.cpp index 28103299f..1bc1792fc 100644 --- a/apps/statistics/histogram_view.cpp +++ b/apps/statistics/histogram_view.cpp @@ -17,7 +17,8 @@ HistogramView::HistogramView(HistogramController * controller, Store * store, in m_series(series), m_selectedHistogramColor(selectedHistogramColor), m_notSelectedHistogramColor(notSelectedHistogramColor), - m_selectedBarColor(selectedBarColor) + m_selectedBarColor(selectedBarColor), + m_displayLabels(true) { } @@ -33,7 +34,7 @@ void HistogramView::drawRect(KDContext * ctx, KDRect rect) const { m_controller->setCurrentDrawnSeries(m_series); ctx->fillRect(rect, KDColorWhite); drawAxes(ctx, rect, Axis::Horizontal); - drawLabels(ctx, rect, Axis::Horizontal, false); + drawLabels(ctx, rect, Axis::Horizontal, false, !m_displayLabels); /* We memoize the total size to avoid recomputing it in double precision at * every call to EvaluateHistogramAtAbscissa() */ float totalSize = m_store->sumOfOccurrences(m_series); diff --git a/apps/statistics/histogram_view.h b/apps/statistics/histogram_view.h index d67ad3576..91834219f 100644 --- a/apps/statistics/histogram_view.h +++ b/apps/statistics/histogram_view.h @@ -17,6 +17,7 @@ public: void reload() override; void drawRect(KDContext * ctx, KDRect rect) const override; void setHighlight(float start, float end); + void setDisplayLabels(bool display) { m_displayLabels = display; } private: char * label(Axis axis, int index) const override; HistogramController * m_controller; @@ -29,6 +30,7 @@ private: KDColor m_selectedHistogramColor; KDColor m_notSelectedHistogramColor; KDColor m_selectedBarColor; + bool m_displayLabels; }; } From d928fbed78482c6afa9c157a7a63705720a6945b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 15:21:29 +0200 Subject: [PATCH 039/156] [apps/stats] Histograms axis labels follow the selection --- apps/statistics/histogram_controller.cpp | 37 +++++++++++++++--------- apps/statistics/histogram_controller.h | 3 ++ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 2a546fbae..669d4aa6e 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -20,8 +20,11 @@ HistogramController::ContentView::ContentView(HistogramController * controller, m_store(store) { m_histogramView1.setDisplayBannerView(false); + m_histogramView1.setDisplayLabels(false); m_histogramView2.setDisplayBannerView(false); + m_histogramView2.setDisplayLabels(false); m_histogramView3.setDisplayBannerView(false); + m_histogramView3.setDisplayLabels(false); } void HistogramController::ContentView::reload() { @@ -58,6 +61,14 @@ int HistogramController::ContentView::indexOfSubviewAtSeries(int series) { return -1; } +void HistogramController::ContentView::selectHistogram(int index) { + changeHistogramSelection(index, true); +} + +void HistogramController::ContentView::deselectHistogram(int index) { + changeHistogramSelection(index, false); +} + void HistogramController::ContentView::drawRect(KDContext * ctx, KDRect rect) const { if (!m_displayBanner) { ctx->fillRect(bannerFrame(), KDColorWhite); @@ -101,18 +112,13 @@ void HistogramController::ContentView::layoutSubviews() { KDCoordinate bannerHeight = bannerFrame().height(); KDCoordinate subviewHeight = (bounds().height() - bannerHeight)/numberHistogramSubviews; int displayedSubviewIndex = 0; - int bottomSeriesDisplayed = -1; for (int i = 0; i < 3; i++) { if (!m_store->seriesIsEmpty(i)) { - histogramViewAtIndex(i)->setDisplayLabels(false); KDRect frame = KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), subviewHeight); subviewAtIndex(displayedSubviewIndex)->setFrame(frame); displayedSubviewIndex++; - bottomSeriesDisplayed = i; } } - assert(bottomSeriesDisplayed >= 0); - histogramViewAtIndex(bottomSeriesDisplayed)->setDisplayLabels(true); if (m_displayBanner) { m_bannerView.setFrame(bannerFrame()); } else { @@ -121,6 +127,11 @@ void HistogramController::ContentView::layoutSubviews() { } } +void HistogramController::ContentView::changeHistogramSelection(int index, bool select) { + histogramViewAtIndex(index)->selectMainView(select); + histogramViewAtIndex(index)->setDisplayLabels(select); +} + HistogramController::HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, int series, uint32_t * storeVersion, uint32_t * barVersion, uint32_t * rangeVersion, int * selectedBarIndex) : ViewController(parentResponder), ButtonRowDelegate(header, nullptr), @@ -164,7 +175,7 @@ void HistogramController::viewWillAppear() { m_view.setDisplayBanner(true); if (m_selectedSeries < 0) { m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); - m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); + m_view.selectHistogram(m_selectedSeries); } reloadBannerView(); m_view.reload(); @@ -175,10 +186,10 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Down) { int currentSelectedSubview = m_view.indexOfSubviewAtSeries(m_selectedSeries); if (currentSelectedSubview < m_view.numberOfSubviews() - 2) { - m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); + m_view.deselectHistogram(m_selectedSeries); m_selectedSeries = m_view.seriesOfSubviewAtIndex(currentSelectedSubview+1); *m_selectedBarIndex = 0; - m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); + m_view.selectHistogram(m_selectedSeries); reloadBannerView(); m_view.reload(); app()->setFirstResponder(this); @@ -187,12 +198,12 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { return false; } if (event == Ion::Events::Up) { - m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); + m_view.deselectHistogram(m_selectedSeries); int currentSelectedSubview = m_view.indexOfSubviewAtSeries(m_selectedSeries); if (currentSelectedSubview > 0) { assert(currentSelectedSubview > 0); m_selectedSeries = m_view.seriesOfSubviewAtIndex(currentSelectedSubview-1); - m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); + m_view.selectHistogram(m_selectedSeries); *m_selectedBarIndex = 0; app()->setFirstResponder(this); } else { @@ -218,10 +229,10 @@ void HistogramController::didBecomeFirstResponder() { m_view.setDisplayBanner(true); if (m_selectedSeries < 0 || m_store->sumOfOccurrences(m_selectedSeries) == 0) { if (m_selectedSeries >= 0) { - m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); + m_view.deselectHistogram(m_selectedSeries); } m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); - m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(true); + m_view.selectHistogram(m_selectedSeries); m_view.reload(); } uint32_t storeChecksum = m_store->storeChecksum(); @@ -247,7 +258,7 @@ void HistogramController::didBecomeFirstResponder() { void HistogramController::willExitResponderChain(Responder * nextFirstResponder) { if (nextFirstResponder == nullptr || nextFirstResponder == tabController()) { if (m_selectedSeries >= 0) { - m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); + m_view.deselectHistogram(m_selectedSeries); m_selectedSeries = -1; m_view.setDisplayBanner(false); } diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index 828c2782e..8e0210b81 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -46,6 +46,8 @@ private: int indexOfSubviewAtSeries(int series); HistogramBannerView * bannerView() { return &m_bannerView; } void setDisplayBanner(bool display) { m_displayBanner = display; } + void selectHistogram(int index); + void deselectHistogram(int index); // View void drawRect(KDContext * ctx, KDRect rect) const override; int numberOfSubviews() const override; @@ -53,6 +55,7 @@ private: KDRect bannerFrame() const; View * subviewAtIndex(int index) override; void layoutSubviews() override; + void changeHistogramSelection(int index, bool select); HistogramView m_histogramView1; HistogramView m_histogramView2; HistogramView m_histogramView3; From b0076e05cff858294e117f93f4816d45f683bd37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 15:29:50 +0200 Subject: [PATCH 040/156] [apps/stats] Keep displaying histogram axes label when selecting the tab --- apps/statistics/histogram_controller.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 669d4aa6e..545e0e634 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -198,16 +198,15 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { return false; } if (event == Ion::Events::Up) { - m_view.deselectHistogram(m_selectedSeries); int currentSelectedSubview = m_view.indexOfSubviewAtSeries(m_selectedSeries); if (currentSelectedSubview > 0) { + m_view.deselectHistogram(m_selectedSeries); assert(currentSelectedSubview > 0); m_selectedSeries = m_view.seriesOfSubviewAtIndex(currentSelectedSubview-1); m_view.selectHistogram(m_selectedSeries); *m_selectedBarIndex = 0; app()->setFirstResponder(this); } else { - m_view.setDisplayBanner(false); app()->setFirstResponder(tabController()); } reloadBannerView(); @@ -258,7 +257,7 @@ void HistogramController::didBecomeFirstResponder() { void HistogramController::willExitResponderChain(Responder * nextFirstResponder) { if (nextFirstResponder == nullptr || nextFirstResponder == tabController()) { if (m_selectedSeries >= 0) { - m_view.deselectHistogram(m_selectedSeries); + m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); m_selectedSeries = -1; m_view.setDisplayBanner(false); } From c3b343a10813f0dfca0081a20e065564746b6cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 15:45:40 +0200 Subject: [PATCH 041/156] [apps/stats] Finer auto range and bar parameters The values are computed so that all bars of all histograms are shown --- apps/statistics/histogram_controller.cpp | 29 ++++++++++++++++-------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 545e0e634..9227fd739 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -363,11 +363,16 @@ bool HistogramController::moveSelection(int deltaIndex) { void HistogramController::initRangeParameters() { assert(m_selectedSeries >= 0 && m_store->sumOfOccurrences(m_selectedSeries) > 0); - float min = m_store->firstDrawnBarAbscissa(); - float max = m_store->maxValue(m_selectedSeries); + float minValue = m_store->firstDrawnBarAbscissa(); + float maxValue = -FLT_MAX; + for (int i = 0; i < Store::k_numberOfSeries; i ++) { + if (!m_store->seriesIsEmpty(i)) { + maxValue = max(maxValue, m_store->maxValue(i)); + } + } float barWidth = m_store->barWidth(); - float xMin = min; - float xMax = max + barWidth; + float xMin = minValue; + float xMax = maxValue + barWidth; /* if a bar is represented by less than one pixel, we cap xMax */ if ((xMax - xMin)/barWidth > k_maxNumberOfBarsPerWindow) { xMax = xMin + k_maxNumberOfBarsPerWindow*barWidth; @@ -399,11 +404,17 @@ void HistogramController::initYRangeParameters(int series) { void HistogramController::initBarParameters() { assert(m_selectedSeries >= 0 && m_store->sumOfOccurrences(m_selectedSeries) > 0); - float min = m_store->minValue(m_selectedSeries); - float max = m_store->maxValue(m_selectedSeries); - 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); + float minValue = -FLT_MAX; + float maxValue = FLT_MAX; + for (int i = 0; i < Store::k_numberOfSeries; i ++) { + if (!m_store->seriesIsEmpty(i)) { + minValue = min(minValue, m_store->minValue(i)); + maxValue = max(maxValue, m_store->maxValue(i)); + } + } + maxValue = minValue >= maxValue ? minValue + std::pow(10.0f, std::floor(std::log10(std::fabs(minValue)))-1.0f) : maxValue; + m_store->setFirstDrawnBarAbscissa(minValue); + float barWidth = m_store->computeGridUnit(CurveViewRange::Axis::X, minValue, maxValue); if (barWidth <= 0.0f) { barWidth = 1.0f; } From 64fddab9282dbcd1a3242d2b82fe4bab9e4fe2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 15:50:58 +0200 Subject: [PATCH 042/156] [apps/stats] Remove unneeded dependency --- apps/statistics/histogram_controller.h | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index 8e0210b81..3f6bda90f 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -6,7 +6,6 @@ #include "histogram_view.h" #include "histogram_banner_view.h" #include "histogram_parameter_controller.h" -#include "../shared/curve_view.h" namespace Statistics { From 4f0672ba67dbfb23b696d27f9ee28cf26bc1b9d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 16:25:53 +0200 Subject: [PATCH 043/156] [apps/stats] Better margins for histogram label displaying --- apps/statistics/histogram_controller.cpp | 14 +++++++++++++- apps/statistics/store.h | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 9227fd739..45da45e58 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -398,8 +398,20 @@ void HistogramController::initYRangeParameters(int series) { } yMax = yMax/m_store->sumOfOccurrences(series); yMax = yMax < 0 ? 1 : yMax; - m_store->setYMin(-Store::k_displayBottomMarginRatio*yMax); m_store->setYMax(yMax*(1.0f+Store::k_displayTopMarginRatio)); + + /* Compute YMin: + * ratioFloatPixel*(0-yMin) = k_bottomMargin + * ratioFloatPixel*(yMax-yMin) = viewHeight + * + * -ratioFloatPixel*yMin = k_bottomMargin + * ratioFloatPixel*yMax-ratioFloatPixel*yMin = viewHeight + * + * ratioFloatPixel = (viewHeight - k_bottomMargin)/yMax + * yMin = -k_bottomMargin/ratioFloatPixel = yMax*k_bottomMargin/(k_bottomMargin - viewHeight) + * */ + + m_store->setYMin(m_store->yMax()*(float)Store::k_bottomMargin/((float)Store::k_bottomMargin - m_view.histogramViewAtIndex(series)->bounds().height())); } void HistogramController::initBarParameters() { diff --git a/apps/statistics/store.h b/apps/statistics/store.h index 0934eb26e..db1aecbe9 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -46,7 +46,7 @@ public: constexpr static double k_maxNumberOfBars = 10000.0; constexpr static float k_displayTopMarginRatio = 0.1f; constexpr static float k_displayRightMarginRatio = 0.04f; - constexpr static float k_displayBottomMarginRatio = 0.4f; + constexpr static int k_bottomMargin = 20; constexpr static float k_displayLeftMarginRatio = 0.04f; private: From 82a5b9f982035c7f7376276fcf3314d3043ad3da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 16:36:48 +0200 Subject: [PATCH 044/156] [apps/stats] Fix histogram bar parameters initialization --- apps/statistics/histogram_controller.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 45da45e58..6f16132b0 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -10,6 +10,9 @@ using namespace Shared; namespace Statistics { +static inline float min(float x, float y) { return (xy ? x : y); } + HistogramController::ContentView::ContentView(HistogramController * controller, Store * store) : m_histogramView1(controller, store, 0, &m_bannerView, Palette::Red), m_histogramView2(controller, store, 1, &m_bannerView, Palette::Blue), @@ -416,8 +419,8 @@ void HistogramController::initYRangeParameters(int series) { void HistogramController::initBarParameters() { assert(m_selectedSeries >= 0 && m_store->sumOfOccurrences(m_selectedSeries) > 0); - float minValue = -FLT_MAX; - float maxValue = FLT_MAX; + float minValue = FLT_MAX; + float maxValue = -FLT_MAX; for (int i = 0; i < Store::k_numberOfSeries; i ++) { if (!m_store->seriesIsEmpty(i)) { minValue = min(minValue, m_store->minValue(i)); From ac99767a584684013cce66541ae4fd3469f263cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 16:45:59 +0200 Subject: [PATCH 045/156] [apps/stats] Remove unneeded parameter --- apps/statistics/histogram_controller.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 6f16132b0..9ce21690a 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -14,9 +14,9 @@ static inline float min(float x, float y) { return (xy ? x : y); } HistogramController::ContentView::ContentView(HistogramController * controller, Store * store) : - m_histogramView1(controller, store, 0, &m_bannerView, Palette::Red), - m_histogramView2(controller, store, 1, &m_bannerView, Palette::Blue), - m_histogramView3(controller, store, 2, &m_bannerView, Palette::Green), + m_histogramView1(controller, store, 0, nullptr, Palette::Red), + m_histogramView2(controller, store, 1, nullptr, Palette::Blue), + m_histogramView3(controller, store, 2, nullptr, Palette::Green), // TODO Share colors with stats/store_controller m_bannerView(), m_displayBanner(false), From cbe9a95472d8e176c6653549359302810e032115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 17:28:02 +0200 Subject: [PATCH 046/156] [apps/stats] Display OK in histogram --- apps/shared/curve_view.cpp | 5 +++-- apps/shared/curve_view.h | 11 +++++++---- apps/statistics/histogram_controller.cpp | 18 +++++++++++------- apps/statistics/histogram_controller.h | 2 ++ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/apps/shared/curve_view.cpp b/apps/shared/curve_view.cpp index 7e44df5a1..5d7696834 100644 --- a/apps/shared/curve_view.cpp +++ b/apps/shared/curve_view.cpp @@ -17,6 +17,7 @@ CurveView::CurveView(CurveViewRange * curveViewRange, CurveViewCursor * curveVie m_curveViewRange(curveViewRange), m_cursorView(cursorView), m_okView(okView), + m_forceOkDisplay(false), m_mainViewSelected(false), m_drawnRangeVersion(0), m_displayBanner(displayBanner) @@ -553,13 +554,13 @@ KDRect CurveView::bannerFrame() { KDRect CurveView::okFrame() { KDRect okFrame = KDRectZero; - if (m_okView && m_mainViewSelected) { + if (m_okView && (m_mainViewSelected || m_forceOkDisplay)) { KDCoordinate bannerHeight = 0; if (m_bannerView != nullptr && m_displayBanner) { bannerHeight = m_bannerView->minimalSizeForOptimalDisplay().height(); } KDSize okSize = m_okView->minimalSizeForOptimalDisplay(); - okFrame = KDRect(bounds().width()- okSize.width()-k_okMargin, bounds().height()- bannerHeight-okSize.height()-k_okMargin, okSize); + okFrame = KDRect(bounds().width()- okSize.width()-k_okHorizontalMargin, bounds().height()- bannerHeight-okSize.height()-k_okVerticalMargin, okSize); } return okFrame; } diff --git a/apps/shared/curve_view.h b/apps/shared/curve_view.h index e79f39def..9dea2c23f 100644 --- a/apps/shared/curve_view.h +++ b/apps/shared/curve_view.h @@ -32,16 +32,18 @@ public: void setDisplayBannerView(bool display) { m_displayBanner = display; } bool displayBannerView() const { return m_displayBanner; } void setOkView(View * okView); + void setForceOkDisplay(bool force) { m_forceOkDisplay = force; } float resolution() const; protected: void setCurveViewRange(CurveViewRange * curveViewRange); // Drawing methods virtual float samplingRatio() const; - constexpr static KDCoordinate k_labelMargin = 4; - constexpr static KDCoordinate k_okMargin = 10; - constexpr static KDCoordinate k_labelGraduationLength = 6; + constexpr static KDCoordinate k_labelMargin = 4; + constexpr static KDCoordinate k_okVerticalMargin = 23; + constexpr static KDCoordinate k_okHorizontalMargin = 10; + constexpr static KDCoordinate k_labelGraduationLength = 6; constexpr static int k_maxNumberOfXLabels = CurveViewRange::k_maxNumberOfXGridUnits; - constexpr static int k_maxNumberOfYLabels = CurveViewRange::k_maxNumberOfYGridUnits; + constexpr static int k_maxNumberOfYLabels = CurveViewRange::k_maxNumberOfYGridUnits; constexpr static int k_externRectMargin = 2; float pixelToFloat(Axis axis, KDCoordinate p) const; float floatToPixel(Axis axis, float f) const; @@ -90,6 +92,7 @@ private: CurveViewRange * m_curveViewRange; View * m_cursorView; View * m_okView; + bool m_forceOkDisplay; bool m_mainViewSelected; uint32_t m_drawnRangeVersion; bool m_displayBanner; diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 9ce21690a..2364c4c38 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -19,15 +19,16 @@ HistogramController::ContentView::ContentView(HistogramController * controller, m_histogramView3(controller, store, 2, nullptr, Palette::Green), // TODO Share colors with stats/store_controller m_bannerView(), + m_okView(), m_displayBanner(false), m_store(store) { - m_histogramView1.setDisplayBannerView(false); - m_histogramView1.setDisplayLabels(false); - m_histogramView2.setDisplayBannerView(false); - m_histogramView2.setDisplayLabels(false); - m_histogramView3.setDisplayBannerView(false); - m_histogramView3.setDisplayLabels(false); + for (int i = 0; i < Store::k_numberOfSeries; i++) { + HistogramView * histView = histogramViewAtIndex(i); + histView->setDisplayBannerView(false); + histView->setDisplayLabels(false); + histView->setForceOkDisplay(true); + } } void HistogramController::ContentView::reload() { @@ -117,9 +118,11 @@ void HistogramController::ContentView::layoutSubviews() { int displayedSubviewIndex = 0; for (int i = 0; i < 3; i++) { if (!m_store->seriesIsEmpty(i)) { + HistogramView * histView = histogramViewAtIndex(i); KDRect frame = KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), subviewHeight); - subviewAtIndex(displayedSubviewIndex)->setFrame(frame); + histView->setFrame(frame); displayedSubviewIndex++; + histView->setOkView(displayedSubviewIndex == numberHistogramSubviews ? &m_okView : nullptr); } } if (m_displayBanner) { @@ -261,6 +264,7 @@ void HistogramController::willExitResponderChain(Responder * nextFirstResponder) if (nextFirstResponder == nullptr || nextFirstResponder == tabController()) { if (m_selectedSeries >= 0) { m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); + m_view.histogramViewAtIndex(m_selectedSeries)->setForceOkDisplay(false); m_selectedSeries = -1; m_view.setDisplayBanner(false); } diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index 3f6bda90f..7e753e692 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -6,6 +6,7 @@ #include "histogram_view.h" #include "histogram_banner_view.h" #include "histogram_parameter_controller.h" +#include "../shared/ok_view.h" namespace Statistics { @@ -59,6 +60,7 @@ private: HistogramView m_histogramView2; HistogramView m_histogramView3; HistogramBannerView m_bannerView; + Shared::OkView m_okView; bool m_displayBanner; Store * m_store; // TODO Do not duplicate this pointer }; From 19cc3fd60bf543adbb80768b470d3e38ddff369f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 23 May 2018 17:30:51 +0200 Subject: [PATCH 047/156] [apps/stats] Connect the OK button in histogram --- apps/statistics/histogram_controller.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 2364c4c38..cccb75116 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -227,6 +227,10 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { } return true; } + if (event == Ion::Events::OK) { + stackController()->push(histogramParameterController()); + return true; + } return false; } From 8997a7671e46088069cdeed49f0c8267f5e45f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 24 May 2018 09:45:33 +0200 Subject: [PATCH 048/156] [apps/stats] Fix quartiles computation for non integers value sizes --- apps/statistics/store.cpp | 14 +++++++------- apps/statistics/store.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 75b096e6e..49e976c20 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -174,12 +174,12 @@ double Store::sampleStandardDeviation(int series) { } double Store::firstQuartile(int series) { - int firstQuartileIndex = std::ceil(sumOfOccurrences(series)/4); + double firstQuartileIndex = sumOfOccurrences(series)/4.0f; return sortedElementNumber(series, firstQuartileIndex); } double Store::thirdQuartile(int series) { - int thirdQuartileIndex = std::ceil(3*sumOfOccurrences(series)/4); + double thirdQuartileIndex = 3*sumOfOccurrences(series)/4.0f; return sortedElementNumber(series, thirdQuartileIndex); } @@ -188,15 +188,15 @@ double Store::quartileRange(int series) { } double Store::median(int series) { - int total = sumOfOccurrences(series); - int halfTotal = total/2; + double total = sumOfOccurrences(series); + double halfTotal = total/2; int totalMod2 = total - 2*halfTotal; if (totalMod2 == 0) { double minusMedian = sortedElementNumber(series, halfTotal); - double maxMedian = sortedElementNumber(series, halfTotal+1); + double maxMedian = sortedElementNumber(series, halfTotal+1.0); return (minusMedian+maxMedian)/2.0; } else { - return sortedElementNumber(series, halfTotal+1); + return sortedElementNumber(series, halfTotal+1.0); } } @@ -232,7 +232,7 @@ double Store::sumOfValuesBetween(int series, double x1, double x2) { return result; } -double Store::sortedElementNumber(int series, int k) { +double Store::sortedElementNumber(int series, double k) { // TODO: use an other algorithm (ex quickselect) to avoid quadratic complexity double bufferValues[m_numberOfPairs[series]]; memcpy(bufferValues, m_data[series][0], m_numberOfPairs[series]*sizeof(double)); diff --git a/apps/statistics/store.h b/apps/statistics/store.h index db1aecbe9..fb72b74b6 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -52,7 +52,7 @@ public: private: double defaultValue(int series, int i, int j) override; double sumOfValuesBetween(int series, double x1, double x2); - double sortedElementNumber(int series, int k); + double sortedElementNumber(int series, double k); int minIndex(double * bufferValues, int bufferLength); // Histogram bars double m_barWidth; From 2b185261b7f4323979fe00f5936f5b7f4b312849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 24 May 2018 11:32:11 +0200 Subject: [PATCH 049/156] [apps/stats] Hide empty histogram cells --- apps/shared/Makefile | 1 + .../hideable_even_odd_editable_text_cell.cpp | 20 ++++++++++++++++ .../hideable_even_odd_editable_text_cell.h | 22 ++++++++++++++++++ apps/shared/store_controller.cpp | 23 +++++++++++++++---- apps/shared/store_controller.h | 5 +++- apps/statistics/store_controller.cpp | 4 ++-- escher/src/even_odd_cell.cpp | 4 ++-- 7 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 apps/shared/hideable_even_odd_editable_text_cell.cpp create mode 100644 apps/shared/hideable_even_odd_editable_text_cell.h diff --git a/apps/shared/Makefile b/apps/shared/Makefile index 5034a0580..bcd225770 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -21,6 +21,7 @@ app_objs += $(addprefix apps/shared/,\ function_expression_cell.o\ function_title_cell.o\ go_to_parameter_controller.o\ + hideable_even_odd_editable_text_cell.o\ initialisation_parameter_controller.o\ interactive_curve_view_controller.o\ interactive_curve_view_range.o\ diff --git a/apps/shared/hideable_even_odd_editable_text_cell.cpp b/apps/shared/hideable_even_odd_editable_text_cell.cpp new file mode 100644 index 000000000..c9f5b1ed1 --- /dev/null +++ b/apps/shared/hideable_even_odd_editable_text_cell.cpp @@ -0,0 +1,20 @@ +#include "hideable_even_odd_editable_text_cell.h" + +namespace Shared { + +KDColor HideableEvenOddEditableTextCell::backgroundColor() const { + if (m_hide) { + return Palette::WallScreenDark; + } + return EvenOddEditableTextCell::backgroundColor(); +} + +void HideableEvenOddEditableTextCell::setHide(bool hide) { + if (m_hide != hide) { + m_hide = hide; + editableTextCell()->textField()->setBackgroundColor(backgroundColor()); + reloadCell(); + } +} + +} diff --git a/apps/shared/hideable_even_odd_editable_text_cell.h b/apps/shared/hideable_even_odd_editable_text_cell.h new file mode 100644 index 000000000..0103a9e85 --- /dev/null +++ b/apps/shared/hideable_even_odd_editable_text_cell.h @@ -0,0 +1,22 @@ +#ifndef APPS_SHARED_HIDEABLE_EVEN_ODD_EDITABLE_TEXT_CELL_H +#define APPS_SHARED_HIDEABLE_EVEN_ODD_EDITABLE_TEXT_CELL_H + +#include + +namespace Shared { + +class HideableEvenOddEditableTextCell : public EvenOddEditableTextCell { +public: + HideableEvenOddEditableTextCell(Responder * parentResponder = nullptr, TextFieldDelegate * delegate = nullptr, char * draftTextBuffer = nullptr) : + EvenOddEditableTextCell(parentResponder, delegate, draftTextBuffer), + m_hide(false) + {} + KDColor backgroundColor() const override; + void setHide(bool hide); +private: + bool m_hide; +}; + +} + +#endif diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index c57e01137..58f8cc9eb 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -79,12 +79,20 @@ int StoreController::typeAtLocation(int i, int j) { void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { // Handle empty cells - if (j > 0 && j > m_store->numberOfPairsOfSeries(seriesAtColumn(i)) && j < numberOfRows() - 1) { - ((EvenOddCell *)cell)->setEven(j%2 == 0); - assert(cellAtLocationIsEditable(i, j)); - ((EvenOddEditableTextCell *)cell)->editableTextCell()->textField()->setText(""); + if (j > 0 && j > m_store->numberOfPairsOfSeries(seriesAtColumn(i)) && j < numberOfRows()) { + HideableEvenOddEditableTextCell * myCell = static_cast(cell); + myCell->editableTextCell()->textField()->setText(""); + if (cellShouldBeTransparent(i,j)) { + myCell->setHide(true); + } else { + myCell->setEven(j%2 == 0); + myCell->setHide(false); + } return; } + if (cellAtLocationIsEditable(i, j)) { + static_cast(cell)->setHide(false); + } willDisplayCellAtLocationWithDisplayMode(cell, i, j, PrintFloat::Mode::Decimal); } @@ -161,7 +169,7 @@ int StoreController::maxNumberOfElements() const { View * StoreController::loadView() { SelectableTableView * tableView = (SelectableTableView*)EditableCellTableViewController::loadView(); for (int i = 0; i < k_maxNumberOfEditableCells; i++) { - m_editableCells[i] = new EvenOddEditableTextCell(tableView, this, m_draftTextBuffer); + m_editableCells[i] = new HideableEvenOddEditableTextCell(tableView, this, m_draftTextBuffer); } tableView->setMargins(k_margin); return tableView; @@ -175,4 +183,9 @@ void StoreController::unloadView(View * view) { EditableCellTableViewController::unloadView(view); } +bool StoreController::cellShouldBeTransparent(int i, int j) { + int seriesIndex = i/k_numberOfColumnsPerSeries; + return j > 1 + m_store->numberOfPairsOfSeries(seriesIndex); +} + } diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index a5ae3d19b..5354ba614 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -3,6 +3,7 @@ #include #include "float_pair_store.h" +#include "hideable_even_odd_editable_text_cell.h" #include "store_parameter_controller.h" #include "editable_cell_table_view_controller.h" @@ -52,9 +53,11 @@ protected: virtual HighlightCell * titleCells(int index) = 0; char m_draftTextBuffer[TextField::maxBufferSize()]; int seriesAtColumn(int column) const { return column / k_numberOfColumnsPerSeries; } - EvenOddEditableTextCell * m_editableCells[k_maxNumberOfEditableCells]; + HideableEvenOddEditableTextCell * m_editableCells[k_maxNumberOfEditableCells]; FloatPairStore * m_store; StoreParameterController m_storeParameterController; +private: + bool cellShouldBeTransparent(int i, int j); }; } diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index ec0410e92..934ae72ee 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -17,13 +17,13 @@ StoreController::StoreController(Responder * parentResponder, Store * store, But } void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { - ::StoreController::willDisplayCellAtLocation(cell, i, j); + Shared::StoreController::willDisplayCellAtLocation(cell, i, j); if (cellAtLocationIsEditable(i, j)) { return; } Shared::BufferFunctionTitleCell * mytitleCell = (Shared::BufferFunctionTitleCell *)cell; - bool valuesColumn = i%k_numberOfColumnsPerSeries == 0; int seriesIndex = i/k_numberOfColumnsPerSeries; + bool valuesColumn = i%k_numberOfColumnsPerSeries == 0; assert(seriesIndex >= 0 && seriesIndex < FloatPairStore::k_numberOfSeries); if (valuesColumn) { I18n::Message valuesMessages[] = {I18n::Message::Values1, I18n::Message::Values2, I18n::Message::Values3}; diff --git a/escher/src/even_odd_cell.cpp b/escher/src/even_odd_cell.cpp index 3b6671f8a..4e6b0bb9f 100644 --- a/escher/src/even_odd_cell.cpp +++ b/escher/src/even_odd_cell.cpp @@ -9,8 +9,8 @@ EvenOddCell::EvenOddCell() : void EvenOddCell::setEven(bool even) { if (even != m_even) { - m_even = even; - reloadCell(); + m_even = even; + reloadCell(); } } From 24d7593276ddf46015be0596c5763210958e7538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 24 May 2018 13:51:11 +0200 Subject: [PATCH 050/156] [apps/shared] k_numberOfColumnsPerSeries is a FloatPairStore attribute --- apps/shared/float_pair_store.cpp | 2 +- apps/shared/float_pair_store.h | 3 ++- apps/shared/store_controller.cpp | 10 +++++----- apps/shared/store_controller.h | 6 ++---- apps/statistics/store_controller.cpp | 4 ++-- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/apps/shared/float_pair_store.cpp b/apps/shared/float_pair_store.cpp index 7e17649ca..4ca63c8a3 100644 --- a/apps/shared/float_pair_store.cpp +++ b/apps/shared/float_pair_store.cpp @@ -78,7 +78,7 @@ uint32_t FloatPairStore::storeChecksum() { * pairs. However, the two values of a pair are not stored consecutively. We * thus compute the checksum on all pairs and ensure to set the pair at 0 * when removing them. */ - size_t dataLengthInBytes = k_numberOfSeries*k_maxNumberOfPairs*2*sizeof(double); + size_t dataLengthInBytes = k_numberOfSeries*k_maxNumberOfPairs*k_numberOfColumnsPerSeries*sizeof(double); assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4 return Ion::crc32((uint32_t *)m_data, dataLengthInBytes/sizeof(uint32_t)); } diff --git a/apps/shared/float_pair_store.h b/apps/shared/float_pair_store.h index 4ef42604f..7b9c05238 100644 --- a/apps/shared/float_pair_store.h +++ b/apps/shared/float_pair_store.h @@ -9,6 +9,7 @@ namespace Shared { class FloatPairStore { public: constexpr static int k_numberOfSeries = 3; + constexpr static int k_numberOfColumnsPerSeries = 2; constexpr static int k_maxNumberOfPairs = 100; FloatPairStore() : m_numberOfPairs{}, @@ -35,7 +36,7 @@ public: protected: virtual double defaultValue(int series, int i, int j); int m_numberOfPairs[k_numberOfSeries]; - double m_data[k_numberOfSeries][2][k_maxNumberOfPairs]; + double m_data[k_numberOfSeries][k_numberOfColumnsPerSeries][k_maxNumberOfPairs]; }; } diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 58f8cc9eb..65b252602 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -39,7 +39,7 @@ bool StoreController::textFieldDidFinishEditing(TextField * textField, const cha } int StoreController::numberOfColumns() { - return k_numberOfColumnsPerSeries * FloatPairStore::k_numberOfSeries; + return FloatPairStore::k_numberOfColumnsPerSeries * FloatPairStore::k_numberOfSeries; } KDCoordinate StoreController::columnWidth(int i) { @@ -110,7 +110,7 @@ bool StoreController::handleEvent(Ion::Events::Event event) { assert(selectedColumn() >= 0 && selectedColumn() < numberOfColumns()); int series = seriesAtColumn(selectedColumn()); if ((event == Ion::Events::OK || event == Ion::Events::EXE) && selectedRow() == 0) { - m_storeParameterController.selectXColumn(selectedColumn()%k_numberOfColumnsPerSeries == 0); + m_storeParameterController.selectXColumn(selectedColumn()%FloatPairStore::k_numberOfColumnsPerSeries == 0); m_storeParameterController.selectSeries(series); StackViewController * stack = ((StackViewController *)parentResponder()->parentResponder()); stack->push(&m_storeParameterController); @@ -146,12 +146,12 @@ bool StoreController::cellAtLocationIsEditable(int columnIndex, int rowIndex) { } bool StoreController::setDataAtLocation(double floatBody, int columnIndex, int rowIndex) { - m_store->set(floatBody, seriesAtColumn(columnIndex), columnIndex%k_numberOfColumnsPerSeries, rowIndex-1); + m_store->set(floatBody, seriesAtColumn(columnIndex), columnIndex%FloatPairStore::k_numberOfColumnsPerSeries, rowIndex-1); return true; } double StoreController::dataAtLocation(int columnIndex, int rowIndex) { - return m_store->get(seriesAtColumn(columnIndex), columnIndex%k_numberOfColumnsPerSeries, rowIndex-1); + return m_store->get(seriesAtColumn(columnIndex), columnIndex%FloatPairStore::k_numberOfColumnsPerSeries, rowIndex-1); } int StoreController::numberOfElements() { @@ -184,7 +184,7 @@ void StoreController::unloadView(View * view) { } bool StoreController::cellShouldBeTransparent(int i, int j) { - int seriesIndex = i/k_numberOfColumnsPerSeries; + int seriesIndex = i/FloatPairStore::k_numberOfColumnsPerSeries; return j > 1 + m_store->numberOfPairsOfSeries(seriesIndex); } diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 5354ba614..2e7187449 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -36,10 +36,8 @@ public: protected: static constexpr KDCoordinate k_cellWidth = 80; //TODO static constexpr KDCoordinate k_margin = 8; - constexpr static int k_numberOfColumnsPerSeries = 2; 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 + constexpr static int k_numberOfTitleCells = FloatPairStore::k_numberOfColumnsPerSeries * FloatPairStore::k_numberOfSeries; // TODO Put finer number of cells static constexpr int k_titleCellType = 0; static constexpr int k_editableCellType = 1; Responder * tabController() const override; @@ -52,7 +50,7 @@ protected: int maxNumberOfElements() const override; virtual HighlightCell * titleCells(int index) = 0; char m_draftTextBuffer[TextField::maxBufferSize()]; - int seriesAtColumn(int column) const { return column / k_numberOfColumnsPerSeries; } + int seriesAtColumn(int column) const { return column / FloatPairStore::k_numberOfColumnsPerSeries; } HideableEvenOddEditableTextCell * m_editableCells[k_maxNumberOfEditableCells]; FloatPairStore * m_store; StoreParameterController m_storeParameterController; diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index 934ae72ee..95ef251b3 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -22,8 +22,8 @@ void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int return; } Shared::BufferFunctionTitleCell * mytitleCell = (Shared::BufferFunctionTitleCell *)cell; - int seriesIndex = i/k_numberOfColumnsPerSeries; - bool valuesColumn = i%k_numberOfColumnsPerSeries == 0; + int seriesIndex = i/Store::k_numberOfColumnsPerSeries; + bool valuesColumn = i%Store::k_numberOfColumnsPerSeries == 0; assert(seriesIndex >= 0 && seriesIndex < FloatPairStore::k_numberOfSeries); if (valuesColumn) { I18n::Message valuesMessages[] = {I18n::Message::Values1, I18n::Message::Values2, I18n::Message::Values3}; From 70fa8d658e178557f96a266f495eb2626845c3b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 24 May 2018 13:57:28 +0200 Subject: [PATCH 051/156] [apps/shared] Handle empty cells in store data table navigation --- apps/shared/Makefile | 1 + apps/shared/store_controller.cpp | 6 ++- apps/shared/store_controller.h | 2 +- apps/shared/store_selectable_table_view.cpp | 42 +++++++++++++++++++++ apps/shared/store_selectable_table_view.h | 20 ++++++++++ 5 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 apps/shared/store_selectable_table_view.cpp create mode 100644 apps/shared/store_selectable_table_view.h diff --git a/apps/shared/Makefile b/apps/shared/Makefile index bcd225770..52777d509 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -43,6 +43,7 @@ app_objs += $(addprefix apps/shared/,\ expression_layout_field_delegate.o\ store_controller.o\ store_parameter_controller.o\ + store_selectable_table_view.o\ sum_graph_controller.o\ tab_table_controller.o\ text_field_delegate.o\ diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 65b252602..a24454b63 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -1,4 +1,5 @@ #include "store_controller.h" +#include "store_selectable_table_view.h" #include "../apps_container.h" #include "../constant.h" #include @@ -167,7 +168,10 @@ int StoreController::maxNumberOfElements() const { } View * StoreController::loadView() { - SelectableTableView * tableView = (SelectableTableView*)EditableCellTableViewController::loadView(); + StoreSelectableTableView * tableView = new StoreSelectableTableView(m_store, this, this, this); + tableView->setBackgroundColor(Palette::WallScreenDark); + tableView->setVerticalCellOverlap(0); + for (int i = 0; i < k_maxNumberOfEditableCells; i++) { m_editableCells[i] = new HideableEvenOddEditableTextCell(tableView, this, m_draftTextBuffer); } diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 2e7187449..19cab4e36 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -2,10 +2,10 @@ #define SHARED_STORE_CONTROLLER_H #include +#include "editable_cell_table_view_controller.h" #include "float_pair_store.h" #include "hideable_even_odd_editable_text_cell.h" #include "store_parameter_controller.h" -#include "editable_cell_table_view_controller.h" namespace Shared { diff --git a/apps/shared/store_selectable_table_view.cpp b/apps/shared/store_selectable_table_view.cpp new file mode 100644 index 000000000..ec6adcf3d --- /dev/null +++ b/apps/shared/store_selectable_table_view.cpp @@ -0,0 +1,42 @@ +#include "store_selectable_table_view.h" + +namespace Shared { + +StoreSelectableTableView::StoreSelectableTableView(FloatPairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, SelectableTableViewDelegate * delegate) : + SelectableTableView(parentResponder, dataSource, selectionDataSource, delegate), + m_store(store) +{ +} + +bool StoreSelectableTableView::handleEvent(Ion::Events::Event event) { + if (event == Ion::Events::Down) { + return selecNonHiddenCellAtLocation(selectedColumn(), selectedRow()+1); + } + if (event == Ion::Events::Up) { + return selecNonHiddenCellAtLocation(selectedColumn(), selectedRow()-1); + } + if (event == Ion::Events::Left) { + return selecNonHiddenCellAtLocation(selectedColumn()-1, selectedRow()); + } + if (event == Ion::Events::Right) { + return selecNonHiddenCellAtLocation(selectedColumn()+1, selectedRow()); + } + return false; +} + +bool StoreSelectableTableView::selecNonHiddenCellAtLocation(int i, int j) { + if (i < 0 || i >= dataSource()->numberOfColumns()) { + return false; + } + if (j < 0 || j >= dataSource()->numberOfRows()) { + return false; + } + int seriesIndex = i/FloatPairStore::k_numberOfColumnsPerSeries; + int numberOfPairsOfCurrentSeries = m_store->numberOfPairsOfSeries(seriesIndex); + if (j > 1 + numberOfPairsOfCurrentSeries) { + return selectCellAtLocation(i, 1 + numberOfPairsOfCurrentSeries); + } + return selectCellAtLocation(i, j); +} + +} diff --git a/apps/shared/store_selectable_table_view.h b/apps/shared/store_selectable_table_view.h new file mode 100644 index 000000000..ca370b3ef --- /dev/null +++ b/apps/shared/store_selectable_table_view.h @@ -0,0 +1,20 @@ +#ifndef APPS_SHARED_STORE_SELECTABLE_TABLE_VIEW_H +#define APPS_SHARED_STORE_SELECTABLE_TABLE_VIEW_H + +#include +#include "float_pair_store.h" + +namespace Shared { + +class StoreSelectableTableView : public SelectableTableView { +public: + StoreSelectableTableView(FloatPairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource = nullptr, SelectableTableViewDelegate * delegate = nullptr); + bool handleEvent(Ion::Events::Event event) override; +private: + bool selecNonHiddenCellAtLocation(int i, int j); + FloatPairStore * m_store; +}; + +} + +#endif From 2717e8ebefdd32a46a25704129d05d1667d8acb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 24 May 2018 15:12:20 +0200 Subject: [PATCH 052/156] [apps/shared] Draw separators between series in Store data --- apps/shared/Makefile | 2 ++ apps/shared/buffer_function_title_cell.cpp | 6 +++- apps/shared/buffer_function_title_cell.h | 3 ++ .../hideable_even_odd_editable_text_cell.cpp | 2 +- .../hideable_even_odd_editable_text_cell.h | 2 ++ apps/shared/store_cell.cpp | 26 +++++++++++++++++ apps/shared/store_cell.h | 24 +++++++++++++++ apps/shared/store_controller.cpp | 11 +++++-- apps/shared/store_controller.h | 4 +-- apps/shared/store_title_cell.cpp | 29 +++++++++++++++++++ apps/shared/store_title_cell.h | 24 +++++++++++++++ apps/statistics/store_controller.cpp | 5 ++-- apps/statistics/store_controller.h | 4 +-- 13 files changed, 131 insertions(+), 11 deletions(-) create mode 100644 apps/shared/store_cell.cpp create mode 100644 apps/shared/store_cell.h create mode 100644 apps/shared/store_title_cell.cpp create mode 100644 apps/shared/store_title_cell.h diff --git a/apps/shared/Makefile b/apps/shared/Makefile index 52777d509..1187839ce 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -41,9 +41,11 @@ app_objs += $(addprefix apps/shared/,\ round_cursor_view.o\ simple_interactive_curve_view_controller.o\ expression_layout_field_delegate.o\ + store_cell.o\ store_controller.o\ store_parameter_controller.o\ store_selectable_table_view.o\ + store_title_cell.o\ sum_graph_controller.o\ tab_table_controller.o\ text_field_delegate.o\ diff --git a/apps/shared/buffer_function_title_cell.cpp b/apps/shared/buffer_function_title_cell.cpp index f6e43d529..28050f2b2 100644 --- a/apps/shared/buffer_function_title_cell.cpp +++ b/apps/shared/buffer_function_title_cell.cpp @@ -38,11 +38,15 @@ View * BufferFunctionTitleCell::subviewAtIndex(int index) { } void BufferFunctionTitleCell::layoutSubviews() { + m_bufferTextView.setFrame(bufferTextViewFrame()); +} + +KDRect BufferFunctionTitleCell::bufferTextViewFrame() const { KDRect textFrame(0, k_colorIndicatorThickness, bounds().width(), bounds().height() - k_colorIndicatorThickness); if (m_orientation == Orientation::VerticalIndicator){ textFrame = KDRect(k_colorIndicatorThickness, 0, bounds().width() - k_colorIndicatorThickness, bounds().height()-k_separatorThickness); } - m_bufferTextView.setFrame(textFrame); + return textFrame; } } diff --git a/apps/shared/buffer_function_title_cell.h b/apps/shared/buffer_function_title_cell.h index dab68686c..e99aee2ec 100644 --- a/apps/shared/buffer_function_title_cell.h +++ b/apps/shared/buffer_function_title_cell.h @@ -15,6 +15,9 @@ public: int numberOfSubviews() const override; View * subviewAtIndex(int index) override; void layoutSubviews() override; +protected: + KDRect bufferTextViewFrame() const; + EvenOddBufferTextCell * bufferTextView() { return &m_bufferTextView; } private: EvenOddBufferTextCell m_bufferTextView; }; diff --git a/apps/shared/hideable_even_odd_editable_text_cell.cpp b/apps/shared/hideable_even_odd_editable_text_cell.cpp index c9f5b1ed1..e060e08a1 100644 --- a/apps/shared/hideable_even_odd_editable_text_cell.cpp +++ b/apps/shared/hideable_even_odd_editable_text_cell.cpp @@ -4,7 +4,7 @@ namespace Shared { KDColor HideableEvenOddEditableTextCell::backgroundColor() const { if (m_hide) { - return Palette::WallScreenDark; + return hideColor(); } return EvenOddEditableTextCell::backgroundColor(); } diff --git a/apps/shared/hideable_even_odd_editable_text_cell.h b/apps/shared/hideable_even_odd_editable_text_cell.h index 0103a9e85..ce36bf478 100644 --- a/apps/shared/hideable_even_odd_editable_text_cell.h +++ b/apps/shared/hideable_even_odd_editable_text_cell.h @@ -2,6 +2,7 @@ #define APPS_SHARED_HIDEABLE_EVEN_ODD_EDITABLE_TEXT_CELL_H #include +#include namespace Shared { @@ -12,6 +13,7 @@ public: m_hide(false) {} KDColor backgroundColor() const override; + static KDColor hideColor() { return Palette::WallScreenDark; } void setHide(bool hide); private: bool m_hide; diff --git a/apps/shared/store_cell.cpp b/apps/shared/store_cell.cpp new file mode 100644 index 000000000..a2329b753 --- /dev/null +++ b/apps/shared/store_cell.cpp @@ -0,0 +1,26 @@ +#include "store_cell.h" + +namespace Shared { + +void StoreCell::setSeparatorRight(bool separator) { + if (m_separatorRight != separator) { + m_separatorRight = separator; + reloadCell(); + } +} + +void StoreCell::drawRect(KDContext * ctx, KDRect rect) const { + HideableEvenOddEditableTextCell::drawRect(ctx, rect); + // Draw the separator + if (m_separatorRight) { + ctx->fillRect(KDRect(bounds().width() - k_separatorThickness, 0, k_separatorThickness, bounds().height()), HideableEvenOddEditableTextCell::hideColor()); + } +} + +void StoreCell::layoutSubviews() { + KDRect boundsThis = bounds(); + editableTextCell()->setFrame(KDRect(boundsThis.topLeft(), boundsThis.width() - k_separatorThickness, boundsThis.height())); +} + +} + diff --git a/apps/shared/store_cell.h b/apps/shared/store_cell.h new file mode 100644 index 000000000..ea0acfa05 --- /dev/null +++ b/apps/shared/store_cell.h @@ -0,0 +1,24 @@ +#ifndef APPS_SHARED_STORE_CELL_H +#define APPS_SHARED_STORE_CELL_H + +#include "hideable_even_odd_editable_text_cell.h" + +namespace Shared { + +class StoreCell : public HideableEvenOddEditableTextCell { +public: + StoreCell(Responder * parentResponder = nullptr, TextFieldDelegate * delegate = nullptr, char * draftTextBuffer = nullptr) : + HideableEvenOddEditableTextCell(parentResponder, delegate, draftTextBuffer), + m_separatorRight(false) + {} + void setSeparatorRight(bool separator); + void drawRect(KDContext * ctx, KDRect rect) const override; + void layoutSubviews() override; +private: + static constexpr KDCoordinate k_separatorThickness = 2; + bool m_separatorRight; +}; + +} + +#endif diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index a24454b63..c243a3813 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -79,9 +79,14 @@ int StoreController::typeAtLocation(int i, int j) { } void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { + // Handle the separator + if (cellAtLocationIsEditable(i, j)) { + bool shoudHaveRightSeparator = i % FloatPairStore::k_numberOfColumnsPerSeries == 1; + static_cast(cell)->setSeparatorRight(shoudHaveRightSeparator); + } // Handle empty cells if (j > 0 && j > m_store->numberOfPairsOfSeries(seriesAtColumn(i)) && j < numberOfRows()) { - HideableEvenOddEditableTextCell * myCell = static_cast(cell); + StoreCell * myCell = static_cast(cell); myCell->editableTextCell()->textField()->setText(""); if (cellShouldBeTransparent(i,j)) { myCell->setHide(true); @@ -92,7 +97,7 @@ void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int return; } if (cellAtLocationIsEditable(i, j)) { - static_cast(cell)->setHide(false); + static_cast(cell)->setHide(false); } willDisplayCellAtLocationWithDisplayMode(cell, i, j, PrintFloat::Mode::Decimal); } @@ -173,7 +178,7 @@ View * StoreController::loadView() { tableView->setVerticalCellOverlap(0); for (int i = 0; i < k_maxNumberOfEditableCells; i++) { - m_editableCells[i] = new HideableEvenOddEditableTextCell(tableView, this, m_draftTextBuffer); + m_editableCells[i] = new StoreCell(tableView, this, m_draftTextBuffer); } tableView->setMargins(k_margin); return tableView; diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 19cab4e36..7ae903b9a 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -4,7 +4,7 @@ #include #include "editable_cell_table_view_controller.h" #include "float_pair_store.h" -#include "hideable_even_odd_editable_text_cell.h" +#include "store_cell.h" #include "store_parameter_controller.h" namespace Shared { @@ -51,7 +51,7 @@ protected: virtual HighlightCell * titleCells(int index) = 0; char m_draftTextBuffer[TextField::maxBufferSize()]; int seriesAtColumn(int column) const { return column / FloatPairStore::k_numberOfColumnsPerSeries; } - HideableEvenOddEditableTextCell * m_editableCells[k_maxNumberOfEditableCells]; + StoreCell * m_editableCells[k_maxNumberOfEditableCells]; FloatPairStore * m_store; StoreParameterController m_storeParameterController; private: diff --git a/apps/shared/store_title_cell.cpp b/apps/shared/store_title_cell.cpp new file mode 100644 index 000000000..e0246bdb6 --- /dev/null +++ b/apps/shared/store_title_cell.cpp @@ -0,0 +1,29 @@ +#include "store_title_cell.h" +#include "hideable_even_odd_editable_text_cell.h" + +namespace Shared { + +void StoreTitleCell::setSeparatorRight(bool separator) { + if (m_separatorRight != separator) { + m_separatorRight = separator; + reloadCell(); + } +} + +void StoreTitleCell::drawRect(KDContext * ctx, KDRect rect) const { + BufferFunctionTitleCell::drawRect(ctx, rect); + // Draw the separator + KDRect separatorRect(bounds().width() - k_separatorThickness, m_separatorRight ? 0 : k_colorIndicatorThickness, k_separatorThickness, bounds().height()); + if (m_separatorRight) { + ctx->fillRect(separatorRect, HideableEvenOddEditableTextCell::hideColor()); + } else { + ctx->fillRect(separatorRect, backgroundColor()); + } +} + +void StoreTitleCell::layoutSubviews() { + KDRect textFrame = bufferTextViewFrame(); + bufferTextView()->setFrame(KDRect(textFrame.topLeft(), textFrame.width() - k_separatorThickness, textFrame.height() )); +} + +} diff --git a/apps/shared/store_title_cell.h b/apps/shared/store_title_cell.h new file mode 100644 index 000000000..884346705 --- /dev/null +++ b/apps/shared/store_title_cell.h @@ -0,0 +1,24 @@ +#ifndef SHARED_STORE_TITLE_CELL_H +#define SHARED_STORE_TITLE_CELL_H + +#include "buffer_function_title_cell.h" + +namespace Shared { + +class StoreTitleCell : public BufferFunctionTitleCell { +public: + StoreTitleCell(Orientation orientation, KDText::FontSize size = KDText::FontSize::Large) : + BufferFunctionTitleCell(orientation, size), + m_separatorRight(false) + {} + void setSeparatorRight(bool separator); + void drawRect(KDContext * ctx, KDRect rect) const override; + void layoutSubviews() override; +private: + static constexpr KDCoordinate k_separatorThickness = 2; + bool m_separatorRight; +}; + +} + +#endif diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index 95ef251b3..f917ea312 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -21,7 +21,8 @@ void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int if (cellAtLocationIsEditable(i, j)) { return; } - Shared::BufferFunctionTitleCell * mytitleCell = (Shared::BufferFunctionTitleCell *)cell; + Shared::StoreTitleCell * mytitleCell = static_cast(cell); + mytitleCell->setSeparatorRight(i % Store::k_numberOfColumnsPerSeries == 1); int seriesIndex = i/Store::k_numberOfColumnsPerSeries; bool valuesColumn = i%Store::k_numberOfColumnsPerSeries == 0; assert(seriesIndex >= 0 && seriesIndex < FloatPairStore::k_numberOfSeries); @@ -55,7 +56,7 @@ bool StoreController::setDataAtLocation(double floatBody, int columnIndex, int r View * StoreController::loadView() { for (int i = 0; i < k_numberOfTitleCells; i++) { - m_titleCells[i] = new Shared::BufferFunctionTitleCell(FunctionTitleCell::Orientation::HorizontalIndicator, KDText::FontSize::Small); + m_titleCells[i] = new Shared::StoreTitleCell(FunctionTitleCell::Orientation::HorizontalIndicator, KDText::FontSize::Small); } return Shared::StoreController::loadView(); } diff --git a/apps/statistics/store_controller.h b/apps/statistics/store_controller.h index 34bb91cd0..8427ab5af 100644 --- a/apps/statistics/store_controller.h +++ b/apps/statistics/store_controller.h @@ -4,7 +4,7 @@ #include #include "store.h" #include "../shared/store_controller.h" -#include "../shared/buffer_function_title_cell.h" +#include "../shared/store_title_cell.h" namespace Statistics { @@ -17,7 +17,7 @@ private: HighlightCell * titleCells(int index) override; View * loadView() override; void unloadView(View * view) override; - Shared::BufferFunctionTitleCell * m_titleCells[k_numberOfTitleCells]; + Shared::StoreTitleCell * m_titleCells[k_numberOfTitleCells]; }; } From 076ac081cddf0d8ea38bdd2b9a8a7c0b4c99a80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 24 May 2018 15:36:58 +0200 Subject: [PATCH 053/156] [apps/stats] StoreCell, StoreTitleCell inherit from StoreSeparatorCell --- apps/shared/store_cell.cpp | 6 +++--- apps/shared/store_cell.h | 10 ++++------ apps/shared/store_separator_cell.h | 21 +++++++++++++++++++++ apps/shared/store_title_cell.cpp | 10 +++++----- apps/shared/store_title_cell.h | 10 ++++------ 5 files changed, 37 insertions(+), 20 deletions(-) create mode 100644 apps/shared/store_separator_cell.h diff --git a/apps/shared/store_cell.cpp b/apps/shared/store_cell.cpp index a2329b753..5a40d0dcf 100644 --- a/apps/shared/store_cell.cpp +++ b/apps/shared/store_cell.cpp @@ -3,8 +3,8 @@ namespace Shared { void StoreCell::setSeparatorRight(bool separator) { - if (m_separatorRight != separator) { - m_separatorRight = separator; + if (separatorRight() != separator) { + StoreSeparatorCell::setSeparatorRight(separator); reloadCell(); } } @@ -12,7 +12,7 @@ void StoreCell::setSeparatorRight(bool separator) { void StoreCell::drawRect(KDContext * ctx, KDRect rect) const { HideableEvenOddEditableTextCell::drawRect(ctx, rect); // Draw the separator - if (m_separatorRight) { + if (separatorRight()) { ctx->fillRect(KDRect(bounds().width() - k_separatorThickness, 0, k_separatorThickness, bounds().height()), HideableEvenOddEditableTextCell::hideColor()); } } diff --git a/apps/shared/store_cell.h b/apps/shared/store_cell.h index ea0acfa05..4434ddc1e 100644 --- a/apps/shared/store_cell.h +++ b/apps/shared/store_cell.h @@ -2,21 +2,19 @@ #define APPS_SHARED_STORE_CELL_H #include "hideable_even_odd_editable_text_cell.h" +#include "store_separator_cell.h" namespace Shared { -class StoreCell : public HideableEvenOddEditableTextCell { +class StoreCell : public HideableEvenOddEditableTextCell, public StoreSeparatorCell { public: StoreCell(Responder * parentResponder = nullptr, TextFieldDelegate * delegate = nullptr, char * draftTextBuffer = nullptr) : HideableEvenOddEditableTextCell(parentResponder, delegate, draftTextBuffer), - m_separatorRight(false) + StoreSeparatorCell() {} - void setSeparatorRight(bool separator); + void setSeparatorRight(bool separator) override; void drawRect(KDContext * ctx, KDRect rect) const override; void layoutSubviews() override; -private: - static constexpr KDCoordinate k_separatorThickness = 2; - bool m_separatorRight; }; } diff --git a/apps/shared/store_separator_cell.h b/apps/shared/store_separator_cell.h new file mode 100644 index 000000000..d41697488 --- /dev/null +++ b/apps/shared/store_separator_cell.h @@ -0,0 +1,21 @@ +#ifndef APPS_SHARED_STORE_SEPARATOR_CELL_H +#define APPS_SHARED_STORE_SEPARATOR_CELL_H + +namespace Shared { + +class StoreSeparatorCell { +public: + StoreSeparatorCell() : + m_separatorRight(false) + {} + bool separatorRight() const { return m_separatorRight; } + virtual void setSeparatorRight(bool separator) { m_separatorRight = separator; } +protected: + static constexpr KDCoordinate k_separatorThickness = 2; +private: + bool m_separatorRight; +}; + +} + +#endif diff --git a/apps/shared/store_title_cell.cpp b/apps/shared/store_title_cell.cpp index e0246bdb6..c57db4c67 100644 --- a/apps/shared/store_title_cell.cpp +++ b/apps/shared/store_title_cell.cpp @@ -4,8 +4,8 @@ namespace Shared { void StoreTitleCell::setSeparatorRight(bool separator) { - if (m_separatorRight != separator) { - m_separatorRight = separator; + if (separatorRight() != separator) { + StoreSeparatorCell::setSeparatorRight(separator); reloadCell(); } } @@ -13,8 +13,8 @@ void StoreTitleCell::setSeparatorRight(bool separator) { void StoreTitleCell::drawRect(KDContext * ctx, KDRect rect) const { BufferFunctionTitleCell::drawRect(ctx, rect); // Draw the separator - KDRect separatorRect(bounds().width() - k_separatorThickness, m_separatorRight ? 0 : k_colorIndicatorThickness, k_separatorThickness, bounds().height()); - if (m_separatorRight) { + KDRect separatorRect(bounds().width() - StoreSeparatorCell::k_separatorThickness, separatorRight() ? 0 : k_colorIndicatorThickness, StoreSeparatorCell::k_separatorThickness, bounds().height()); + if (separatorRight()) { ctx->fillRect(separatorRect, HideableEvenOddEditableTextCell::hideColor()); } else { ctx->fillRect(separatorRect, backgroundColor()); @@ -23,7 +23,7 @@ void StoreTitleCell::drawRect(KDContext * ctx, KDRect rect) const { void StoreTitleCell::layoutSubviews() { KDRect textFrame = bufferTextViewFrame(); - bufferTextView()->setFrame(KDRect(textFrame.topLeft(), textFrame.width() - k_separatorThickness, textFrame.height() )); + bufferTextView()->setFrame(KDRect(textFrame.topLeft(), textFrame.width() - StoreSeparatorCell::k_separatorThickness, textFrame.height() )); } } diff --git a/apps/shared/store_title_cell.h b/apps/shared/store_title_cell.h index 884346705..aa86af37e 100644 --- a/apps/shared/store_title_cell.h +++ b/apps/shared/store_title_cell.h @@ -2,21 +2,19 @@ #define SHARED_STORE_TITLE_CELL_H #include "buffer_function_title_cell.h" +#include "store_separator_cell.h" namespace Shared { -class StoreTitleCell : public BufferFunctionTitleCell { +class StoreTitleCell : public BufferFunctionTitleCell, public StoreSeparatorCell { public: StoreTitleCell(Orientation orientation, KDText::FontSize size = KDText::FontSize::Large) : BufferFunctionTitleCell(orientation, size), - m_separatorRight(false) + StoreSeparatorCell() {} - void setSeparatorRight(bool separator); + void setSeparatorRight(bool separator) override; void drawRect(KDContext * ctx, KDRect rect) const override; void layoutSubviews() override; -private: - static constexpr KDCoordinate k_separatorThickness = 2; - bool m_separatorRight; }; } From b7624715748b3015b380fa1b3b5544e3271ba793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 24 May 2018 15:53:08 +0200 Subject: [PATCH 054/156] [apps/stats] HIstogram::ContentView is MultipleHistogramsView --- apps/statistics/Makefile | 1 + apps/statistics/histogram_controller.cpp | 125 ----------------- apps/statistics/histogram_controller.h | 34 +---- apps/statistics/multiple_histograms_view.cpp | 133 +++++++++++++++++++ apps/statistics/multiple_histograms_view.h | 43 ++++++ 5 files changed, 179 insertions(+), 157 deletions(-) create mode 100644 apps/statistics/multiple_histograms_view.cpp create mode 100644 apps/statistics/multiple_histograms_view.h diff --git a/apps/statistics/Makefile b/apps/statistics/Makefile index 7372c2ef0..5d8a5adf7 100644 --- a/apps/statistics/Makefile +++ b/apps/statistics/Makefile @@ -12,6 +12,7 @@ app_objs += $(addprefix apps/statistics/,\ histogram_controller.o\ histogram_parameter_controller.o\ histogram_view.o\ + multiple_histograms_view.o\ store.o\ store_controller.o\ ) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index cccb75116..1120f89fe 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -13,131 +13,6 @@ namespace Statistics { static inline float min(float x, float y) { return (xy ? x : y); } -HistogramController::ContentView::ContentView(HistogramController * controller, Store * store) : - m_histogramView1(controller, store, 0, nullptr, Palette::Red), - m_histogramView2(controller, store, 1, nullptr, Palette::Blue), - m_histogramView3(controller, store, 2, nullptr, Palette::Green), - // TODO Share colors with stats/store_controller - m_bannerView(), - m_okView(), - m_displayBanner(false), - m_store(store) -{ - for (int i = 0; i < Store::k_numberOfSeries; i++) { - HistogramView * histView = histogramViewAtIndex(i); - histView->setDisplayBannerView(false); - histView->setDisplayLabels(false); - histView->setForceOkDisplay(true); - } -} - -void HistogramController::ContentView::reload() { - layoutSubviews(); - m_histogramView1.reload(); - m_histogramView2.reload(); - m_histogramView3.reload(); -} - -HistogramView * HistogramController::ContentView::histogramViewAtIndex(int index) { - assert(index >= 0 && index < 3); - HistogramView * views[] = {&m_histogramView1, &m_histogramView2, &m_histogramView3}; - return views[index]; -} - -int HistogramController::ContentView::seriesOfSubviewAtIndex(int index) { - assert(index >= 0 && index < numberOfSubviews() - 1); - return static_cast(subviewAtIndex(index))->series(); -} - -int HistogramController::ContentView::indexOfSubviewAtSeries(int series) { - int displayedSubviewIndex = 0; - for (int i = 0; i < 3; i++) { - if (!m_store->seriesIsEmpty(i)) { - if (i == series) { - return displayedSubviewIndex; - } - displayedSubviewIndex++; - } else if (i == series) { - return -1; - } - } - assert(false); - return -1; -} - -void HistogramController::ContentView::selectHistogram(int index) { - changeHistogramSelection(index, true); -} - -void HistogramController::ContentView::deselectHistogram(int index) { - changeHistogramSelection(index, false); -} - -void HistogramController::ContentView::drawRect(KDContext * ctx, KDRect rect) const { - if (!m_displayBanner) { - ctx->fillRect(bannerFrame(), KDColorWhite); - } -} - -int HistogramController::ContentView::numberOfSubviews() const { - int result = m_store->numberOfNonEmptySeries(); - assert(result <= Store::k_numberOfSeries); - return result + 1; // +1 for the banner view -} - -KDRect HistogramController::ContentView::bannerFrame() const { - KDCoordinate bannerHeight = m_bannerView.minimalSizeForOptimalDisplay().height(); - KDRect frame = KDRect(0, bounds().height() - bannerHeight, bounds().width(), bannerHeight); - return frame; -} - -View * HistogramController::ContentView::subviewAtIndex(int index) { - if (index == numberOfSubviews() -1) { - return &m_bannerView; - } - int seriesIndex = 0; - int nonEmptySeriesIndex = -1; - while (seriesIndex < Store::k_numberOfSeries) { - if (!m_store->seriesIsEmpty(seriesIndex)) { - nonEmptySeriesIndex++; - if (nonEmptySeriesIndex == index) { - return histogramViewAtIndex(seriesIndex); - } - } - seriesIndex++; - } - assert(false); - return nullptr; -} - -void HistogramController::ContentView::layoutSubviews() { - int numberHistogramSubviews = m_store->numberOfNonEmptySeries(); - assert(numberHistogramSubviews > 0); - KDCoordinate bannerHeight = bannerFrame().height(); - KDCoordinate subviewHeight = (bounds().height() - bannerHeight)/numberHistogramSubviews; - int displayedSubviewIndex = 0; - for (int i = 0; i < 3; i++) { - if (!m_store->seriesIsEmpty(i)) { - HistogramView * histView = histogramViewAtIndex(i); - KDRect frame = KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), subviewHeight); - histView->setFrame(frame); - displayedSubviewIndex++; - histView->setOkView(displayedSubviewIndex == numberHistogramSubviews ? &m_okView : nullptr); - } - } - if (m_displayBanner) { - m_bannerView.setFrame(bannerFrame()); - } else { - KDRect frame = KDRect(0, bounds().height() - bannerHeight, bounds().width(), 0); - m_bannerView.setFrame(frame); - } -} - -void HistogramController::ContentView::changeHistogramSelection(int index, bool select) { - histogramViewAtIndex(index)->selectMainView(select); - histogramViewAtIndex(index)->setDisplayLabels(select); -} - HistogramController::HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, int series, uint32_t * storeVersion, uint32_t * barVersion, uint32_t * rangeVersion, int * selectedBarIndex) : ViewController(parentResponder), ButtonRowDelegate(header, nullptr), diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index 7e753e692..9d93744c7 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -3,10 +3,7 @@ #include #include "store.h" -#include "histogram_view.h" -#include "histogram_banner_view.h" -#include "histogram_parameter_controller.h" -#include "../shared/ok_view.h" +#include "multiple_histograms_view.h" namespace Statistics { @@ -37,33 +34,6 @@ private: constexpr static int k_maxIntervalLegendLength = 33; constexpr static int k_maxLegendLength = 13; constexpr static int k_maxNumberOfCharacters = 30; - class ContentView : public View { - public: - ContentView(HistogramController * controller, Store * store); - void reload(); - HistogramView * histogramViewAtIndex(int index); - int seriesOfSubviewAtIndex(int index); - int indexOfSubviewAtSeries(int series); - HistogramBannerView * bannerView() { return &m_bannerView; } - void setDisplayBanner(bool display) { m_displayBanner = display; } - void selectHistogram(int index); - void deselectHistogram(int index); - // View - void drawRect(KDContext * ctx, KDRect rect) const override; - int numberOfSubviews() const override; - private: - KDRect bannerFrame() const; - View * subviewAtIndex(int index) override; - void layoutSubviews() override; - void changeHistogramSelection(int index, bool select); - HistogramView m_histogramView1; - HistogramView m_histogramView2; - HistogramView m_histogramView3; - HistogramBannerView m_bannerView; - Shared::OkView m_okView; - bool m_displayBanner; - Store * m_store; // TODO Do not duplicate this pointer - }; Responder * tabController() const; void reloadBannerView(); void initRangeParameters(); @@ -73,7 +43,7 @@ private: // return true if the window has scrolled bool moveSelection(int deltaIndex); Store * m_store; - ContentView m_view; + MultipleHistogramsView m_view; uint32_t * m_storeVersion; uint32_t * m_barVersion; uint32_t * m_rangeVersion; diff --git a/apps/statistics/multiple_histograms_view.cpp b/apps/statistics/multiple_histograms_view.cpp new file mode 100644 index 000000000..b709ae439 --- /dev/null +++ b/apps/statistics/multiple_histograms_view.cpp @@ -0,0 +1,133 @@ +#include "multiple_histograms_view.h" +#include + +using namespace Shared; + +namespace Statistics { + +MultipleHistogramsView::MultipleHistogramsView(HistogramController * controller, Store * store) : + m_histogramView1(controller, store, 0, nullptr, Palette::Red), + m_histogramView2(controller, store, 1, nullptr, Palette::Blue), + m_histogramView3(controller, store, 2, nullptr, Palette::Green), + // TODO Share colors with stats/store_controller + m_bannerView(), + m_okView(), + m_displayBanner(false), + m_store(store) +{ + for (int i = 0; i < Store::k_numberOfSeries; i++) { + HistogramView * histView = histogramViewAtIndex(i); + histView->setDisplayBannerView(false); + histView->setDisplayLabels(false); + histView->setForceOkDisplay(true); + } +} + +void MultipleHistogramsView::reload() { + layoutSubviews(); + m_histogramView1.reload(); + m_histogramView2.reload(); + m_histogramView3.reload(); +} + +HistogramView * MultipleHistogramsView::histogramViewAtIndex(int index) { + assert(index >= 0 && index < 3); + HistogramView * views[] = {&m_histogramView1, &m_histogramView2, &m_histogramView3}; + return views[index]; +} + +int MultipleHistogramsView::seriesOfSubviewAtIndex(int index) { + assert(index >= 0 && index < numberOfSubviews() - 1); + return static_cast(subviewAtIndex(index))->series(); +} + +int MultipleHistogramsView::indexOfSubviewAtSeries(int series) { + int displayedSubviewIndex = 0; + for (int i = 0; i < 3; i++) { + if (!m_store->seriesIsEmpty(i)) { + if (i == series) { + return displayedSubviewIndex; + } + displayedSubviewIndex++; + } else if (i == series) { + return -1; + } + } + assert(false); + return -1; +} + +void MultipleHistogramsView::selectHistogram(int index) { + changeHistogramSelection(index, true); +} + +void MultipleHistogramsView::deselectHistogram(int index) { + changeHistogramSelection(index, false); +} + +void MultipleHistogramsView::drawRect(KDContext * ctx, KDRect rect) const { + if (!m_displayBanner) { + ctx->fillRect(bannerFrame(), KDColorWhite); + } +} + +int MultipleHistogramsView::numberOfSubviews() const { + int result = m_store->numberOfNonEmptySeries(); + assert(result <= Store::k_numberOfSeries); + return result + 1; // +1 for the banner view +} + +KDRect MultipleHistogramsView::bannerFrame() const { + KDCoordinate bannerHeight = m_bannerView.minimalSizeForOptimalDisplay().height(); + KDRect frame = KDRect(0, bounds().height() - bannerHeight, bounds().width(), bannerHeight); + return frame; +} + +View * MultipleHistogramsView::subviewAtIndex(int index) { + if (index == numberOfSubviews() -1) { + return &m_bannerView; + } + int seriesIndex = 0; + int nonEmptySeriesIndex = -1; + while (seriesIndex < Store::k_numberOfSeries) { + if (!m_store->seriesIsEmpty(seriesIndex)) { + nonEmptySeriesIndex++; + if (nonEmptySeriesIndex == index) { + return histogramViewAtIndex(seriesIndex); + } + } + seriesIndex++; + } + assert(false); + return nullptr; +} + +void MultipleHistogramsView::layoutSubviews() { + int numberHistogramSubviews = m_store->numberOfNonEmptySeries(); + assert(numberHistogramSubviews > 0); + KDCoordinate bannerHeight = bannerFrame().height(); + KDCoordinate subviewHeight = (bounds().height() - bannerHeight)/numberHistogramSubviews; + int displayedSubviewIndex = 0; + for (int i = 0; i < 3; i++) { + if (!m_store->seriesIsEmpty(i)) { + HistogramView * histView = histogramViewAtIndex(i); + KDRect frame = KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), subviewHeight); + histView->setFrame(frame); + displayedSubviewIndex++; + histView->setOkView(displayedSubviewIndex == numberHistogramSubviews ? &m_okView : nullptr); + } + } + if (m_displayBanner) { + m_bannerView.setFrame(bannerFrame()); + } else { + KDRect frame = KDRect(0, bounds().height() - bannerHeight, bounds().width(), 0); + m_bannerView.setFrame(frame); + } +} + +void MultipleHistogramsView::changeHistogramSelection(int index, bool select) { + histogramViewAtIndex(index)->selectMainView(select); + histogramViewAtIndex(index)->setDisplayLabels(select); +} + +} diff --git a/apps/statistics/multiple_histograms_view.h b/apps/statistics/multiple_histograms_view.h new file mode 100644 index 000000000..abbaa7969 --- /dev/null +++ b/apps/statistics/multiple_histograms_view.h @@ -0,0 +1,43 @@ +#ifndef STATISTICS_MULTIPLE_HISTOGRAMS_VIEW_H +#define STATISTICS_MULTIPLE_HISTOGRAMS_VIEW_H + +#include +#include "store.h" +#include "histogram_view.h" +#include "histogram_banner_view.h" +#include "histogram_parameter_controller.h" +#include "../shared/ok_view.h" + +namespace Statistics { + +class MultipleHistogramsView : public View { +public: + MultipleHistogramsView(HistogramController * controller, Store * store); + void reload(); + HistogramView * histogramViewAtIndex(int index); + int seriesOfSubviewAtIndex(int index); + int indexOfSubviewAtSeries(int series); + HistogramBannerView * bannerView() { return &m_bannerView; } + void setDisplayBanner(bool display) { m_displayBanner = display; } + void selectHistogram(int index); + void deselectHistogram(int index); + // View + void drawRect(KDContext * ctx, KDRect rect) const override; + int numberOfSubviews() const override; +private: + KDRect bannerFrame() const; + View * subviewAtIndex(int index) override; + void layoutSubviews() override; + void changeHistogramSelection(int index, bool select); + HistogramView m_histogramView1; + HistogramView m_histogramView2; + HistogramView m_histogramView3; + HistogramBannerView m_bannerView; + Shared::OkView m_okView; + bool m_displayBanner; + Store * m_store; +}; + +} + +#endif From 7b37f4d0bc9137d88b4c37ce6dde941e73e145b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 24 May 2018 16:32:51 +0200 Subject: [PATCH 055/156] [apps/stats] Remove unnecessary constructor parameter --- apps/statistics/app.cpp | 2 +- apps/statistics/histogram_controller.cpp | 2 +- apps/statistics/histogram_controller.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/statistics/app.cpp b/apps/statistics/app.cpp index 301589027..eea3cb2dc 100644 --- a/apps/statistics/app.cpp +++ b/apps/statistics/app.cpp @@ -55,7 +55,7 @@ App::App(Container * container, Snapshot * snapshot) : m_boxController(&m_boxAlternateEmptyViewController, &m_boxHeader, snapshot->store(), snapshot->selectedBoxQuantile()), m_boxAlternateEmptyViewController(&m_boxHeader, &m_boxController, &m_boxController), m_boxHeader(&m_tabViewController, &m_boxAlternateEmptyViewController, &m_boxController), - m_histogramController(&m_histogramAlternateEmptyViewController, &m_histogramHeader, snapshot->store(), 0, snapshot->storeVersion(), snapshot->barVersion(), snapshot->rangeVersion(), snapshot->selectedHistogramBarIndex()), + m_histogramController(&m_histogramAlternateEmptyViewController, &m_histogramHeader, snapshot->store(), snapshot->storeVersion(), snapshot->barVersion(), snapshot->rangeVersion(), snapshot->selectedHistogramBarIndex()), m_histogramAlternateEmptyViewController(&m_histogramHeader, &m_histogramController, &m_histogramController), m_histogramHeader(&m_histogramStackViewController, &m_histogramAlternateEmptyViewController, &m_histogramController), m_histogramStackViewController(&m_tabViewController, &m_histogramHeader), diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 1120f89fe..95d725885 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -13,7 +13,7 @@ namespace Statistics { static inline float min(float x, float y) { return (xy ? x : y); } -HistogramController::HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, int series, uint32_t * storeVersion, uint32_t * barVersion, uint32_t * rangeVersion, int * selectedBarIndex) : +HistogramController::HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, uint32_t * storeVersion, uint32_t * barVersion, uint32_t * rangeVersion, int * selectedBarIndex) : ViewController(parentResponder), ButtonRowDelegate(header, nullptr), m_store(store), diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index 9d93744c7..db12fa8b1 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -10,7 +10,7 @@ namespace Statistics { class HistogramController : public ViewController, public ButtonRowDelegate, public AlternateEmptyViewDelegate { public: - HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, int series, uint32_t * m_storeVersion, uint32_t * m_barVersion, uint32_t * m_rangeVersion, int * m_selectedBarIndex); + HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, uint32_t * m_storeVersion, uint32_t * m_barVersion, uint32_t * m_rangeVersion, int * m_selectedBarIndex); StackViewController * stackController(); HistogramParameterController * histogramParameterController() { return &m_histogramParameterController; } void setCurrentDrawnSeries(int series); From f4eb4e3476af4a802f43081612eebc10c8478543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 24 May 2018 17:42:36 +0200 Subject: [PATCH 056/156] [apps/stats] Abstract multiple data view and controller --- apps/statistics/Makefile | 2 + apps/statistics/app.h | 1 + apps/statistics/histogram_controller.cpp | 156 +++++------------- apps/statistics/histogram_controller.h | 23 +-- apps/statistics/multiple_data_view.cpp | 102 ++++++++++++ apps/statistics/multiple_data_view.h | 47 ++++++ .../multiple_data_view_controller.cpp | 109 ++++++++++++ .../multiple_data_view_controller.h | 41 +++++ apps/statistics/multiple_histograms_view.cpp | 106 +----------- apps/statistics/multiple_histograms_view.h | 25 +-- 10 files changed, 366 insertions(+), 246 deletions(-) create mode 100644 apps/statistics/multiple_data_view.cpp create mode 100644 apps/statistics/multiple_data_view.h create mode 100644 apps/statistics/multiple_data_view_controller.cpp create mode 100644 apps/statistics/multiple_data_view_controller.h diff --git a/apps/statistics/Makefile b/apps/statistics/Makefile index 5d8a5adf7..99a2f0115 100644 --- a/apps/statistics/Makefile +++ b/apps/statistics/Makefile @@ -12,6 +12,8 @@ app_objs += $(addprefix apps/statistics/,\ histogram_controller.o\ histogram_parameter_controller.o\ histogram_view.o\ + multiple_data_view.o\ + multiple_data_view_controller.o\ multiple_histograms_view.o\ store.o\ store_controller.o\ diff --git a/apps/statistics/app.h b/apps/statistics/app.h index 791f809ed..5790b9bcf 100644 --- a/apps/statistics/app.h +++ b/apps/statistics/app.h @@ -38,6 +38,7 @@ public: uint32_t m_rangeVersion; int m_selectedHistogramBarIndex; BoxView::Quantile m_selectedBoxQuantile; + // TODO add selected Series for both histogram and box }; private: App(Container * container, Snapshot * snapshot); diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 95d725885..baacb40d4 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -14,111 +14,41 @@ static inline float min(float x, float y) { return (xy ? x : y); } HistogramController::HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, uint32_t * storeVersion, uint32_t * barVersion, uint32_t * rangeVersion, int * selectedBarIndex) : - ViewController(parentResponder), + MultipleDataViewController(parentResponder, store, selectedBarIndex), ButtonRowDelegate(header, nullptr), - m_store(store), m_view(this, store), m_storeVersion(storeVersion), m_barVersion(barVersion), m_rangeVersion(rangeVersion), - m_selectedSeries(-1), - m_selectedBarIndex(selectedBarIndex), m_histogramParameterController(nullptr, store) { } +void HistogramController::setCurrentDrawnSeries(int series) { + initYRangeParameters(series); +} + StackViewController * HistogramController::stackController() { StackViewController * stack = (StackViewController *)(parentResponder()->parentResponder()->parentResponder()); return stack; } -void HistogramController::setCurrentDrawnSeries(int series) { - initYRangeParameters(series); -} - -bool HistogramController::isEmpty() const { - return m_store->isEmpty(); -} - -I18n::Message HistogramController::emptyMessage() { - return I18n::Message::NoDataToPlot; -} - -Responder * HistogramController::defaultController() { - return tabController(); -} - const char * HistogramController::title() { return I18n::translate(I18n::Message::HistogramTab); } -void HistogramController::viewWillAppear() { - m_view.setDisplayBanner(true); - if (m_selectedSeries < 0) { - m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); - m_view.selectHistogram(m_selectedSeries); - } - reloadBannerView(); - m_view.reload(); -} - bool HistogramController::handleEvent(Ion::Events::Event event) { - assert(m_selectedSeries >= 0); - if (event == Ion::Events::Down) { - int currentSelectedSubview = m_view.indexOfSubviewAtSeries(m_selectedSeries); - if (currentSelectedSubview < m_view.numberOfSubviews() - 2) { - m_view.deselectHistogram(m_selectedSeries); - m_selectedSeries = m_view.seriesOfSubviewAtIndex(currentSelectedSubview+1); - *m_selectedBarIndex = 0; - m_view.selectHistogram(m_selectedSeries); - reloadBannerView(); - m_view.reload(); - app()->setFirstResponder(this); - return true; - } - return false; - } - if (event == Ion::Events::Up) { - int currentSelectedSubview = m_view.indexOfSubviewAtSeries(m_selectedSeries); - if (currentSelectedSubview > 0) { - m_view.deselectHistogram(m_selectedSeries); - assert(currentSelectedSubview > 0); - m_selectedSeries = m_view.seriesOfSubviewAtIndex(currentSelectedSubview-1); - m_view.selectHistogram(m_selectedSeries); - *m_selectedBarIndex = 0; - app()->setFirstResponder(this); - } else { - app()->setFirstResponder(tabController()); - } - reloadBannerView(); - m_view.reload(); - return true; - } - if (m_selectedSeries >= 0 && (event == Ion::Events::Left || event == Ion::Events::Right)) { - int direction = event == Ion::Events::Left ? -1 : 1; - if (moveSelection(direction)) { - reloadBannerView(); - m_view.reload(); - } - return true; - } + assert(selectedSeries() >= 0); if (event == Ion::Events::OK) { stackController()->push(histogramParameterController()); return true; } - return false; + return MultipleDataViewController::handleEvent(event); } void HistogramController::didBecomeFirstResponder() { - m_view.setDisplayBanner(true); - if (m_selectedSeries < 0 || m_store->sumOfOccurrences(m_selectedSeries) == 0) { - if (m_selectedSeries >= 0) { - m_view.deselectHistogram(m_selectedSeries); - } - m_selectedSeries = m_view.seriesOfSubviewAtIndex(0); - m_view.selectHistogram(m_selectedSeries); - m_view.reload(); - } + MultipleDataViewController::didBecomeFirstResponder(); + uint32_t storeChecksum = m_store->storeChecksum(); if (*m_storeVersion != storeChecksum) { *m_storeVersion = storeChecksum; @@ -136,19 +66,17 @@ void HistogramController::didBecomeFirstResponder() { initBarSelection(); reloadBannerView(); } - m_view.histogramViewAtIndex(m_selectedSeries)->setHighlight(m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex), m_store->endOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex)); + HistogramView * selectedHistogramView = static_cast(m_view.dataViewAtIndex(selectedSeries())); + selectedHistogramView->setHighlight(m_store->startOfBarAtIndex(selectedSeries(), *m_selectedBarIndex), m_store->endOfBarAtIndex(selectedSeries(), *m_selectedBarIndex)); } void HistogramController::willExitResponderChain(Responder * nextFirstResponder) { if (nextFirstResponder == nullptr || nextFirstResponder == tabController()) { - if (m_selectedSeries >= 0) { - m_view.histogramViewAtIndex(m_selectedSeries)->selectMainView(false); - m_view.histogramViewAtIndex(m_selectedSeries)->setForceOkDisplay(false); - m_selectedSeries = -1; - m_view.setDisplayBanner(false); + if (selectedSeries() >= 0) { + m_view.dataViewAtIndex(selectedSeries())->setForceOkDisplay(false); } - m_view.reload(); } + MultipleDataViewController::willExitResponderChain(nextFirstResponder); } Responder * HistogramController::tabController() const { @@ -156,7 +84,7 @@ Responder * HistogramController::tabController() const { } void HistogramController::reloadBannerView() { - if (m_selectedSeries < 0) { + if (selectedSeries() < 0) { return; } char buffer[k_maxNumberOfCharacters+ PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)*2]; @@ -169,16 +97,16 @@ void HistogramController::reloadBannerView() { numberOfChar += legendLength; // Add lower bound - if (m_selectedSeries >= 0) { - double lowerBound = m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex); + if (selectedSeries() >= 0) { + double lowerBound = m_store->startOfBarAtIndex(selectedSeries(), *m_selectedBarIndex); numberOfChar += PrintFloat::convertFloatToText(lowerBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); } buffer[numberOfChar++] = ';'; // Add upper bound - if (m_selectedSeries >= 0) { - double upperBound = m_store->endOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex); + if (selectedSeries() >= 0) { + double upperBound = m_store->endOfBarAtIndex(selectedSeries(), *m_selectedBarIndex); numberOfChar += PrintFloat::convertFloatToText(upperBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); } @@ -189,7 +117,7 @@ void HistogramController::reloadBannerView() { buffer[numberOfChar++] = ' '; } buffer[k_maxIntervalLegendLength] = 0; - m_view.bannerView()->setLegendAtIndex(buffer, 1); + m_view.editableBannerView()->setLegendAtIndex(buffer, 1); // Add Size Data numberOfChar = 0; @@ -198,8 +126,8 @@ void HistogramController::reloadBannerView() { strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; double size = 0; - if (m_selectedSeries >= 0) { - size = m_store->heightOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex); + if (selectedSeries() >= 0) { + size = m_store->heightOfBarAtIndex(selectedSeries(), *m_selectedBarIndex); numberOfChar += PrintFloat::convertFloatToText(size, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); } // Padding @@ -207,7 +135,7 @@ void HistogramController::reloadBannerView() { buffer[numberOfChar++] = ' '; } buffer[k_maxLegendLength] = 0; - m_view.bannerView()->setLegendAtIndex(buffer, 3); + m_view.editableBannerView()->setLegendAtIndex(buffer, 3); // Add Frequency Data numberOfChar = 0; @@ -215,8 +143,8 @@ void HistogramController::reloadBannerView() { legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; - if (m_selectedSeries >= 0) { - double frequency = size/m_store->sumOfOccurrences(m_selectedSeries); + if (selectedSeries() >= 0) { + double frequency = size/m_store->sumOfOccurrences(selectedSeries()); numberOfChar += PrintFloat::convertFloatToText(frequency, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); } // Padding @@ -224,31 +152,31 @@ void HistogramController::reloadBannerView() { buffer[numberOfChar++] = ' '; } buffer[k_maxLegendLength] = 0; - m_view.bannerView()->setLegendAtIndex(buffer, 5); + m_view.editableBannerView()->setLegendAtIndex(buffer, 5); } -bool HistogramController::moveSelection(int deltaIndex) { +bool HistogramController::moveSelectionHorizontally(int deltaIndex) { int newSelectedBarIndex = *m_selectedBarIndex; do { newSelectedBarIndex+=deltaIndex; - } while (m_store->heightOfBarAtIndex(m_selectedSeries, newSelectedBarIndex) == 0 + } while (m_store->heightOfBarAtIndex(selectedSeries(), newSelectedBarIndex) == 0 && newSelectedBarIndex >= 0 - && newSelectedBarIndex < m_store->numberOfBars(m_selectedSeries)); + && newSelectedBarIndex < m_store->numberOfBars(selectedSeries())); if (newSelectedBarIndex >= 0 - && newSelectedBarIndex < m_store->numberOfBars(m_selectedSeries) + && newSelectedBarIndex < m_store->numberOfBars(selectedSeries()) && *m_selectedBarIndex != newSelectedBarIndex) { *m_selectedBarIndex = newSelectedBarIndex; - m_view.histogramViewAtIndex(m_selectedSeries)->setHighlight(m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex), m_store->endOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex)); - m_store->scrollToSelectedBarIndex(m_selectedSeries, *m_selectedBarIndex); + m_view.dataViewAtIndex(selectedSeries())->setHighlight(m_store->startOfBarAtIndex(selectedSeries(), *m_selectedBarIndex), m_store->endOfBarAtIndex(selectedSeries(), *m_selectedBarIndex)); + m_store->scrollToSelectedBarIndex(selectedSeries(), *m_selectedBarIndex); return true; } return false; } void HistogramController::initRangeParameters() { - assert(m_selectedSeries >= 0 && m_store->sumOfOccurrences(m_selectedSeries) > 0); + assert(selectedSeries() >= 0 && m_store->sumOfOccurrences(selectedSeries()) > 0); float minValue = m_store->firstDrawnBarAbscissa(); float maxValue = -FLT_MAX; for (int i = 0; i < Store::k_numberOfSeries; i ++) { @@ -270,7 +198,7 @@ void HistogramController::initRangeParameters() { m_store->setXMin(xMin - Store::k_displayLeftMarginRatio*(xMax-xMin)); m_store->setXMax(xMax + Store::k_displayRightMarginRatio*(xMax-xMin)); - initYRangeParameters(m_selectedSeries); + initYRangeParameters(selectedSeries()); } void HistogramController::initYRangeParameters(int series) { @@ -297,11 +225,11 @@ void HistogramController::initYRangeParameters(int series) { * yMin = -k_bottomMargin/ratioFloatPixel = yMax*k_bottomMargin/(k_bottomMargin - viewHeight) * */ - m_store->setYMin(m_store->yMax()*(float)Store::k_bottomMargin/((float)Store::k_bottomMargin - m_view.histogramViewAtIndex(series)->bounds().height())); + m_store->setYMin(m_store->yMax()*(float)Store::k_bottomMargin/((float)Store::k_bottomMargin - m_view.dataViewAtIndex(series)->bounds().height())); } void HistogramController::initBarParameters() { - assert(m_selectedSeries >= 0 && m_store->sumOfOccurrences(m_selectedSeries) > 0); + assert(selectedSeries() >= 0 && m_store->sumOfOccurrences(selectedSeries()) > 0); float minValue = FLT_MAX; float maxValue = -FLT_MAX; for (int i = 0; i < Store::k_numberOfSeries; i ++) { @@ -320,20 +248,20 @@ void HistogramController::initBarParameters() { } void HistogramController::initBarSelection() { - assert(m_selectedSeries >= 0 && m_store->sumOfOccurrences(m_selectedSeries) > 0); + assert(selectedSeries() >= 0 && m_store->sumOfOccurrences(selectedSeries()) > 0); *m_selectedBarIndex = 0; - while ((m_store->heightOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex) == 0 || - m_store->startOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex) < m_store->firstDrawnBarAbscissa()) && *m_selectedBarIndex < m_store->numberOfBars(m_selectedSeries)) { + while ((m_store->heightOfBarAtIndex(selectedSeries(), *m_selectedBarIndex) == 0 || + m_store->startOfBarAtIndex(selectedSeries(), *m_selectedBarIndex) < m_store->firstDrawnBarAbscissa()) && *m_selectedBarIndex < m_store->numberOfBars(selectedSeries())) { *m_selectedBarIndex = *m_selectedBarIndex+1; } - if (*m_selectedBarIndex >= m_store->numberOfBars(m_selectedSeries)) { + if (*m_selectedBarIndex >= m_store->numberOfBars(selectedSeries())) { /* No bar is after m_firstDrawnBarAbscissa, so we select the first bar */ *m_selectedBarIndex = 0; - while (m_store->heightOfBarAtIndex(m_selectedSeries, *m_selectedBarIndex) == 0 && *m_selectedBarIndex < m_store->numberOfBars(m_selectedSeries)) { + while (m_store->heightOfBarAtIndex(selectedSeries(), *m_selectedBarIndex) == 0 && *m_selectedBarIndex < m_store->numberOfBars(selectedSeries())) { *m_selectedBarIndex = *m_selectedBarIndex+1; } } - m_store->scrollToSelectedBarIndex(m_selectedSeries, *m_selectedBarIndex); + m_store->scrollToSelectedBarIndex(selectedSeries(), *m_selectedBarIndex); } } diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index db12fa8b1..826075cc9 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -3,27 +3,23 @@ #include #include "store.h" +#include "multiple_data_view_controller.h" #include "multiple_histograms_view.h" namespace Statistics { -class HistogramController : public ViewController, public ButtonRowDelegate, public AlternateEmptyViewDelegate { +class HistogramController : public MultipleDataViewController, public ButtonRowDelegate { public: HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, uint32_t * m_storeVersion, uint32_t * m_barVersion, uint32_t * m_rangeVersion, int * m_selectedBarIndex); - StackViewController * stackController(); + HistogramParameterController * histogramParameterController() { return &m_histogramParameterController; } void setCurrentDrawnSeries(int series); - - // AlternateEmptyViewDelegate - bool isEmpty() const override; - I18n::Message emptyMessage() override; - Responder * defaultController() override; + StackViewController * stackController(); // ViewController const char * title() override; - View * view() override { return &m_view; } - void viewWillAppear() override; + MultipleDataView * multipleDataView() override { return &m_view; } // Responder bool handleEvent(Ion::Events::Event event) override; @@ -34,21 +30,18 @@ private: constexpr static int k_maxIntervalLegendLength = 33; constexpr static int k_maxLegendLength = 13; constexpr static int k_maxNumberOfCharacters = 30; - Responder * tabController() const; - void reloadBannerView(); + Responder * tabController() const override; + void reloadBannerView() override; void initRangeParameters(); void initYRangeParameters(int series); void initBarParameters(); void initBarSelection(); // return true if the window has scrolled - bool moveSelection(int deltaIndex); - Store * m_store; + bool moveSelectionHorizontally(int deltaIndex) override; MultipleHistogramsView m_view; uint32_t * m_storeVersion; uint32_t * m_barVersion; uint32_t * m_rangeVersion; - int m_selectedSeries; - int * m_selectedBarIndex; HistogramParameterController m_histogramParameterController; }; diff --git a/apps/statistics/multiple_data_view.cpp b/apps/statistics/multiple_data_view.cpp new file mode 100644 index 000000000..c07f49685 --- /dev/null +++ b/apps/statistics/multiple_data_view.cpp @@ -0,0 +1,102 @@ +#include "multiple_data_view.h" +#include + +using namespace Shared; + +namespace Statistics { + +void MultipleDataView::reload() { + layoutSubviews(); + for (int i = 0; i < Store::k_numberOfSeries; i++) { + dataViewAtIndex(i)->reload(); + } +} + +int MultipleDataView::indexOfSubviewAtSeries(int series) { + int displayedSubviewIndex = 0; + for (int i = 0; i < Store::k_numberOfSeries; i++) { + if (!m_store->seriesIsEmpty(i)) { + if (i == series) { + return displayedSubviewIndex; + } + displayedSubviewIndex++; + } else if (i == series) { + return -1; + } + } + assert(false); + return -1; +} + +void MultipleDataView::selectDataView(int index) { + changeDataViewSelection(index, true); +} + +void MultipleDataView::deselectDataView(int index) { + changeDataViewSelection(index, false); +} + +void MultipleDataView::drawRect(KDContext * ctx, KDRect rect) const { + if (!m_displayBanner) { + ctx->fillRect(bannerFrame(), KDColorWhite); + } +} + +int MultipleDataView::numberOfSubviews() const { + int result = m_store->numberOfNonEmptySeries(); + assert(result <= Store::k_numberOfSeries); + return result + 1; // +1 for the banner view +} + +KDRect MultipleDataView::bannerFrame() const { + KDCoordinate bannerHeight = bannerView()->minimalSizeForOptimalDisplay().height(); + KDRect frame = KDRect(0, bounds().height() - bannerHeight, bounds().width(), bannerHeight); + return frame; +} + +View * MultipleDataView::subviewAtIndex(int index) { + if (index == numberOfSubviews() -1) { + return editableBannerView(); + } + int seriesIndex = 0; + int nonEmptySeriesIndex = -1; + while (seriesIndex < Store::k_numberOfSeries) { + if (!m_store->seriesIsEmpty(seriesIndex)) { + nonEmptySeriesIndex++; + if (nonEmptySeriesIndex == index) { + return dataViewAtIndex(seriesIndex); + } + } + seriesIndex++; + } + assert(false); + return nullptr; +} + +void MultipleDataView::layoutSubviews() { + int numberDataSubviews = m_store->numberOfNonEmptySeries(); + assert(numberDataSubviews > 0); + KDCoordinate bannerHeight = bannerFrame().height(); + KDCoordinate subviewHeight = (bounds().height() - bannerHeight)/numberDataSubviews; + int displayedSubviewIndex = 0; + for (int i = 0; i < Store::k_numberOfSeries; i++) { + if (!m_store->seriesIsEmpty(i)) { + CurveView * dataView = dataViewAtIndex(i); + KDRect frame = KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), subviewHeight); + dataView->setFrame(frame); + displayedSubviewIndex++; + } + } + if (m_displayBanner) { + editableBannerView()->setFrame(bannerFrame()); + } else { + KDRect frame = KDRect(0, bounds().height() - bannerHeight, bounds().width(), 0); + editableBannerView()->setFrame(frame); + } +} + +void MultipleDataView::changeDataViewSelection(int index, bool select) { + dataViewAtIndex(index)->selectMainView(select); +} + +} diff --git a/apps/statistics/multiple_data_view.h b/apps/statistics/multiple_data_view.h new file mode 100644 index 000000000..c53b52cde --- /dev/null +++ b/apps/statistics/multiple_data_view.h @@ -0,0 +1,47 @@ +#ifndef STATISTICS_MULTIPLE_DATA_VIEW_H +#define STATISTICS_MULTIPLE_DATA_VIEW_H + +#include +#include "store.h" +#include "../shared/banner_view.h" +#include "../shared/curve_view.h" + +namespace Statistics { + +class MultipleDataView : public View { +public: + MultipleDataView(Store * store) : + m_displayBanner(false), + m_store(store) + {} + // Data views + void selectDataView(int index); + void deselectDataView(int index); + virtual Shared::CurveView * dataViewAtIndex(int index) = 0; + Shared::BannerView * editableBannerView() { return const_cast(bannerView()); } + + // Index/series + int indexOfSubviewAtSeries(int series); + virtual int seriesOfSubviewAtIndex(int index) = 0; + + // Display + void setDisplayBanner(bool display) { m_displayBanner = display; } + void reload(); + + // View + int numberOfSubviews() const override; +protected: + virtual const Shared::BannerView * bannerView() const = 0; + void layoutSubviews() override; + View * subviewAtIndex(int index) override; + virtual void changeDataViewSelection(int index, bool select); +private: + void drawRect(KDContext * ctx, KDRect rect) const override; + KDRect bannerFrame() const; + bool m_displayBanner; + Store * m_store; +}; + +} + +#endif diff --git a/apps/statistics/multiple_data_view_controller.cpp b/apps/statistics/multiple_data_view_controller.cpp new file mode 100644 index 000000000..02c2e9269 --- /dev/null +++ b/apps/statistics/multiple_data_view_controller.cpp @@ -0,0 +1,109 @@ +#include "multiple_data_view_controller.h" +#include "../i18n.h" +#include + +using namespace Shared; + +namespace Statistics { + +MultipleDataViewController::MultipleDataViewController(Responder * parentResponder, Store * store, int * selectedBarIndex) : + ViewController(parentResponder), + m_store(store), + m_selectedSeries(-1), + m_selectedBarIndex(selectedBarIndex) +{ +} + +MultipleDataView * MultipleDataViewController::multipleDataView() { + return static_cast(view()); +} + +bool MultipleDataViewController::isEmpty() const { + return m_store->isEmpty(); +} + +I18n::Message MultipleDataViewController::emptyMessage() { + return I18n::Message::NoDataToPlot; +} + +Responder * MultipleDataViewController::defaultController() { + return tabController(); +} + +void MultipleDataViewController::viewWillAppear() { + multipleDataView()->setDisplayBanner(true); + if (m_selectedSeries < 0) { + m_selectedSeries = multipleDataView()->seriesOfSubviewAtIndex(0); + multipleDataView()->selectDataView(m_selectedSeries); + } + reloadBannerView(); + multipleDataView()->reload(); +} + +bool MultipleDataViewController::handleEvent(Ion::Events::Event event) { + assert(m_selectedSeries >= 0); + if (event == Ion::Events::Down) { + int currentSelectedSubview = multipleDataView()->indexOfSubviewAtSeries(m_selectedSeries); + if (currentSelectedSubview < multipleDataView()->numberOfSubviews() - 2) { + multipleDataView()->deselectDataView(m_selectedSeries); + m_selectedSeries = multipleDataView()->seriesOfSubviewAtIndex(currentSelectedSubview+1); + *m_selectedBarIndex = 0; + multipleDataView()->selectDataView(m_selectedSeries); + reloadBannerView(); + multipleDataView()->reload(); + app()->setFirstResponder(this); + return true; + } + return false; + } + if (event == Ion::Events::Up) { + int currentSelectedSubview = multipleDataView()->indexOfSubviewAtSeries(m_selectedSeries); + if (currentSelectedSubview > 0) { + multipleDataView()->deselectDataView(m_selectedSeries); + assert(currentSelectedSubview > 0); + m_selectedSeries = multipleDataView()->seriesOfSubviewAtIndex(currentSelectedSubview-1); + multipleDataView()->selectDataView(m_selectedSeries); + *m_selectedBarIndex = 0; + app()->setFirstResponder(this); + } else { + app()->setFirstResponder(tabController()); + } + reloadBannerView(); + multipleDataView()->reload(); + return true; + } + if (m_selectedSeries >= 0 && (event == Ion::Events::Left || event == Ion::Events::Right)) { + int direction = event == Ion::Events::Left ? -1 : 1; + if (moveSelectionHorizontally(direction)) { + reloadBannerView(); + multipleDataView()->reload(); + } + return true; + } + return false; +} + +void MultipleDataViewController::didBecomeFirstResponder() { + multipleDataView()->setDisplayBanner(true); + if (m_selectedSeries < 0 || m_store->sumOfOccurrences(m_selectedSeries) == 0) { + if (m_selectedSeries >= 0) { + multipleDataView()->deselectDataView(m_selectedSeries); + } + m_selectedSeries = multipleDataView()->seriesOfSubviewAtIndex(0); + multipleDataView()->selectDataView(m_selectedSeries); + multipleDataView()->reload(); + } +} + +void MultipleDataViewController::willExitResponderChain(Responder * nextFirstResponder) { + if (nextFirstResponder == nullptr || nextFirstResponder == tabController()) { + if (m_selectedSeries >= 0) { + multipleDataView()->dataViewAtIndex(m_selectedSeries)->selectMainView(false); + m_selectedSeries = -1; + multipleDataView()->setDisplayBanner(false); + } + multipleDataView()->reload(); + } +} + +} diff --git a/apps/statistics/multiple_data_view_controller.h b/apps/statistics/multiple_data_view_controller.h new file mode 100644 index 000000000..b8f5041d0 --- /dev/null +++ b/apps/statistics/multiple_data_view_controller.h @@ -0,0 +1,41 @@ +#ifndef STATISTICS_MULTIPLE_DATA_VIEW_CONTROLLER_H +#define STATISTICS_MULTIPLE_DATA_VIEW_CONTROLLER_H + +#include +#include "store.h" +#include "multiple_data_view.h" + +namespace Statistics { + +class MultipleDataViewController : public ViewController, public AlternateEmptyViewDelegate { + +public: + MultipleDataViewController(Responder * parentResponder, Store * store, int * m_selectedBarIndex); + virtual MultipleDataView * multipleDataView() = 0; + int selectedSeries() const { return m_selectedSeries; } + // AlternateEmptyViewDelegate + bool isEmpty() const override; + I18n::Message emptyMessage() override; + Responder * defaultController() override; + + // ViewController + View * view() override { return multipleDataView(); } + void viewWillAppear() override; + + // Responder + bool handleEvent(Ion::Events::Event event) override; + void didBecomeFirstResponder() override; + void willExitResponderChain(Responder * nextFirstResponder) override; +protected: + virtual Responder * tabController() const = 0; + virtual void reloadBannerView() = 0; + virtual bool moveSelectionHorizontally(int deltaIndex) = 0; + Store * m_store; + int m_selectedSeries; + int * m_selectedBarIndex; +}; + +} + + +#endif diff --git a/apps/statistics/multiple_histograms_view.cpp b/apps/statistics/multiple_histograms_view.cpp index b709ae439..ff304d4d7 100644 --- a/apps/statistics/multiple_histograms_view.cpp +++ b/apps/statistics/multiple_histograms_view.cpp @@ -6,31 +6,23 @@ using namespace Shared; namespace Statistics { MultipleHistogramsView::MultipleHistogramsView(HistogramController * controller, Store * store) : + MultipleDataView(store), m_histogramView1(controller, store, 0, nullptr, Palette::Red), m_histogramView2(controller, store, 1, nullptr, Palette::Blue), m_histogramView3(controller, store, 2, nullptr, Palette::Green), // TODO Share colors with stats/store_controller m_bannerView(), - m_okView(), - m_displayBanner(false), - m_store(store) + m_okView() { for (int i = 0; i < Store::k_numberOfSeries; i++) { - HistogramView * histView = histogramViewAtIndex(i); + HistogramView * histView = dataViewAtIndex(i); histView->setDisplayBannerView(false); histView->setDisplayLabels(false); histView->setForceOkDisplay(true); } } -void MultipleHistogramsView::reload() { - layoutSubviews(); - m_histogramView1.reload(); - m_histogramView2.reload(); - m_histogramView3.reload(); -} - -HistogramView * MultipleHistogramsView::histogramViewAtIndex(int index) { +HistogramView * MultipleHistogramsView::dataViewAtIndex(int index) { assert(index >= 0 && index < 3); HistogramView * views[] = {&m_histogramView1, &m_histogramView2, &m_histogramView3}; return views[index]; @@ -41,93 +33,9 @@ int MultipleHistogramsView::seriesOfSubviewAtIndex(int index) { return static_cast(subviewAtIndex(index))->series(); } -int MultipleHistogramsView::indexOfSubviewAtSeries(int series) { - int displayedSubviewIndex = 0; - for (int i = 0; i < 3; i++) { - if (!m_store->seriesIsEmpty(i)) { - if (i == series) { - return displayedSubviewIndex; - } - displayedSubviewIndex++; - } else if (i == series) { - return -1; - } - } - assert(false); - return -1; -} - -void MultipleHistogramsView::selectHistogram(int index) { - changeHistogramSelection(index, true); -} - -void MultipleHistogramsView::deselectHistogram(int index) { - changeHistogramSelection(index, false); -} - -void MultipleHistogramsView::drawRect(KDContext * ctx, KDRect rect) const { - if (!m_displayBanner) { - ctx->fillRect(bannerFrame(), KDColorWhite); - } -} - -int MultipleHistogramsView::numberOfSubviews() const { - int result = m_store->numberOfNonEmptySeries(); - assert(result <= Store::k_numberOfSeries); - return result + 1; // +1 for the banner view -} - -KDRect MultipleHistogramsView::bannerFrame() const { - KDCoordinate bannerHeight = m_bannerView.minimalSizeForOptimalDisplay().height(); - KDRect frame = KDRect(0, bounds().height() - bannerHeight, bounds().width(), bannerHeight); - return frame; -} - -View * MultipleHistogramsView::subviewAtIndex(int index) { - if (index == numberOfSubviews() -1) { - return &m_bannerView; - } - int seriesIndex = 0; - int nonEmptySeriesIndex = -1; - while (seriesIndex < Store::k_numberOfSeries) { - if (!m_store->seriesIsEmpty(seriesIndex)) { - nonEmptySeriesIndex++; - if (nonEmptySeriesIndex == index) { - return histogramViewAtIndex(seriesIndex); - } - } - seriesIndex++; - } - assert(false); - return nullptr; -} - -void MultipleHistogramsView::layoutSubviews() { - int numberHistogramSubviews = m_store->numberOfNonEmptySeries(); - assert(numberHistogramSubviews > 0); - KDCoordinate bannerHeight = bannerFrame().height(); - KDCoordinate subviewHeight = (bounds().height() - bannerHeight)/numberHistogramSubviews; - int displayedSubviewIndex = 0; - for (int i = 0; i < 3; i++) { - if (!m_store->seriesIsEmpty(i)) { - HistogramView * histView = histogramViewAtIndex(i); - KDRect frame = KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), subviewHeight); - histView->setFrame(frame); - displayedSubviewIndex++; - histView->setOkView(displayedSubviewIndex == numberHistogramSubviews ? &m_okView : nullptr); - } - } - if (m_displayBanner) { - m_bannerView.setFrame(bannerFrame()); - } else { - KDRect frame = KDRect(0, bounds().height() - bannerHeight, bounds().width(), 0); - m_bannerView.setFrame(frame); - } -} - -void MultipleHistogramsView::changeHistogramSelection(int index, bool select) { - histogramViewAtIndex(index)->selectMainView(select); - histogramViewAtIndex(index)->setDisplayLabels(select); +void MultipleHistogramsView::changeDataViewSelection(int index, bool select) { + MultipleDataView::changeDataViewSelection(index, select); + dataViewAtIndex(index)->setDisplayLabels(select); } } diff --git a/apps/statistics/multiple_histograms_view.h b/apps/statistics/multiple_histograms_view.h index abbaa7969..1a658e02a 100644 --- a/apps/statistics/multiple_histograms_view.h +++ b/apps/statistics/multiple_histograms_view.h @@ -6,36 +6,25 @@ #include "histogram_view.h" #include "histogram_banner_view.h" #include "histogram_parameter_controller.h" +#include "multiple_data_view.h" #include "../shared/ok_view.h" namespace Statistics { -class MultipleHistogramsView : public View { +class MultipleHistogramsView : public MultipleDataView { public: MultipleHistogramsView(HistogramController * controller, Store * store); - void reload(); - HistogramView * histogramViewAtIndex(int index); - int seriesOfSubviewAtIndex(int index); - int indexOfSubviewAtSeries(int series); - HistogramBannerView * bannerView() { return &m_bannerView; } - void setDisplayBanner(bool display) { m_displayBanner = display; } - void selectHistogram(int index); - void deselectHistogram(int index); - // View - void drawRect(KDContext * ctx, KDRect rect) const override; - int numberOfSubviews() const override; + // MultipleDataView + int seriesOfSubviewAtIndex(int index) override; + const HistogramBannerView * bannerView() const override { return &m_bannerView; } + HistogramView * dataViewAtIndex(int index) override; private: - KDRect bannerFrame() const; - View * subviewAtIndex(int index) override; - void layoutSubviews() override; - void changeHistogramSelection(int index, bool select); + void changeDataViewSelection(int index, bool select) override; HistogramView m_histogramView1; HistogramView m_histogramView2; HistogramView m_histogramView3; HistogramBannerView m_bannerView; Shared::OkView m_okView; - bool m_displayBanner; - Store * m_store; }; } From 31ad501a144482ce8cede16526cc953d45d16f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 25 May 2018 10:43:43 +0200 Subject: [PATCH 057/156] [apps/stats] Median/Quartiles computation for non integer sizes --- apps/statistics/store.cpp | 46 ++++++++++++++++++++++++++------------- apps/statistics/store.h | 3 ++- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 49e976c20..1dbd34aff 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -174,13 +174,11 @@ double Store::sampleStandardDeviation(int series) { } double Store::firstQuartile(int series) { - double firstQuartileIndex = sumOfOccurrences(series)/4.0f; - return sortedElementNumber(series, firstQuartileIndex); + return sortedElementAtCumulatedFrequency(series, 1.0/4.0); } double Store::thirdQuartile(int series) { - double thirdQuartileIndex = 3*sumOfOccurrences(series)/4.0f; - return sortedElementNumber(series, thirdQuartileIndex); + return sortedElementAtCumulatedFrequency(series, 3.0/4.0); } double Store::quartileRange(int series) { @@ -188,15 +186,13 @@ double Store::quartileRange(int series) { } double Store::median(int series) { - double total = sumOfOccurrences(series); - double halfTotal = total/2; - int totalMod2 = total - 2*halfTotal; - if (totalMod2 == 0) { - double minusMedian = sortedElementNumber(series, halfTotal); - double maxMedian = sortedElementNumber(series, halfTotal+1.0); + bool exactElement = true; + double maxMedian = sortedElementAtCumulatedFrequency(series, 1.0/2.0, &exactElement); + if (!exactElement) { + double minusMedian = sortedElementAfter(series, maxMedian); return (minusMedian+maxMedian)/2.0; } else { - return sortedElementNumber(series, halfTotal+1.0); + return maxMedian; } } @@ -232,20 +228,40 @@ double Store::sumOfValuesBetween(int series, double x1, double x2) { return result; } -double Store::sortedElementNumber(int series, double k) { +double Store::sortedElementAtCumulatedFrequency(int series, double k, bool * exactElement) { // TODO: use an other algorithm (ex quickselect) to avoid quadratic complexity + assert(k >= 0.0 && k <= 1.0); + double totalNumberOfElements = sumOfOccurrences(series); double bufferValues[m_numberOfPairs[series]]; memcpy(bufferValues, m_data[series][0], m_numberOfPairs[series]*sizeof(double)); int sortedElementIndex = 0; - double cumulatedSize = 0.0; - while (cumulatedSize < k) { + double cumulatedFrequency = 0.0; + while (cumulatedFrequency < k) { sortedElementIndex = minIndex(bufferValues, m_numberOfPairs[series]); bufferValues[sortedElementIndex] = DBL_MAX; - cumulatedSize += m_data[series][1][sortedElementIndex]; + cumulatedFrequency += m_data[series][1][sortedElementIndex] / totalNumberOfElements; + if (exactElement != nullptr && cumulatedFrequency == k) { + *exactElement = false; + } } return m_data[series][0][sortedElementIndex]; } +double Store::sortedElementAfter(int series, double k) { + assert(m_numberOfPairs[series] > 0); + double result = DBL_MAX; + bool foundResult = false; + for (int i = 0; i < m_numberOfPairs[series]; i++) { + double currentElement = m_data[series][0][i]; + if (currentElement > k && currentElement < result) { + result = currentElement; + foundResult = true; + } + } + assert(foundResult); + return result; +} + int Store::minIndex(double * bufferValues, int bufferLength) { int index = 0; for (int i = 1; i < bufferLength; i++) { diff --git a/apps/statistics/store.h b/apps/statistics/store.h index fb72b74b6..ba1ce5cc8 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -52,7 +52,8 @@ public: private: double defaultValue(int series, int i, int j) override; double sumOfValuesBetween(int series, double x1, double x2); - double sortedElementNumber(int series, double k); + double sortedElementAtCumulatedFrequency(int series, double k, bool * exactElement = nullptr); + double sortedElementAfter(int series, double k); int minIndex(double * bufferValues, int bufferLength); // Histogram bars double m_barWidth; From 81b09501cb2bd4588a52daeb7e994b311d7edccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 25 May 2018 11:14:33 +0200 Subject: [PATCH 058/156] [apps/stats] Display multiple boxes --- apps/statistics/Makefile | 1 + apps/statistics/box_controller.cpp | 76 +++++-------------- apps/statistics/box_controller.h | 29 ++----- apps/statistics/box_view.cpp | 5 +- apps/statistics/box_view.h | 7 +- apps/statistics/multiple_boxes_view.cpp | 34 +++++++++ apps/statistics/multiple_boxes_view.h | 28 +++++++ .../multiple_data_view_controller.cpp | 4 - 8 files changed, 98 insertions(+), 86 deletions(-) create mode 100644 apps/statistics/multiple_boxes_view.cpp create mode 100644 apps/statistics/multiple_boxes_view.h diff --git a/apps/statistics/Makefile b/apps/statistics/Makefile index 99a2f0115..890e0d0da 100644 --- a/apps/statistics/Makefile +++ b/apps/statistics/Makefile @@ -12,6 +12,7 @@ app_objs += $(addprefix apps/statistics/,\ histogram_controller.o\ histogram_parameter_controller.o\ histogram_view.o\ + multiple_boxes_view.o\ multiple_data_view.o\ multiple_data_view_controller.o\ multiple_histograms_view.o\ diff --git a/apps/statistics/box_controller.cpp b/apps/statistics/box_controller.cpp index edc3c581a..a5b104a50 100644 --- a/apps/statistics/box_controller.cpp +++ b/apps/statistics/box_controller.cpp @@ -7,84 +7,48 @@ using namespace Poincare; namespace Statistics { BoxController::BoxController(Responder * parentResponder, ButtonRowController * header, Store * store, BoxView::Quantile * selectedQuantile) : - ViewController(parentResponder), + MultipleDataViewController(parentResponder, store, (int *)(selectedQuantile)), ButtonRowDelegate(header, nullptr), - m_boxBannerView(), - m_view(store, &m_boxBannerView, selectedQuantile), - m_store(store), - m_selectedSeries(0) + m_view(store, selectedQuantile) { } -bool BoxController::isEmpty() const { - for (int i = 0; i < Shared::FloatPairStore::k_numberOfSeries; i++) { - if (m_store->sumOfOccurrences(i) == 0) { - return true; - } +bool BoxController::moveSelectionHorizontally(int deltaIndex) { + int selectedQuantile = (int)m_view.dataViewAtIndex(selectedSeries())->selectedQuantile(); + int nextSelectedQuantile = selectedQuantile + deltaIndex; + if (m_view.dataViewAtIndex(selectedSeries())->selectQuantile(nextSelectedQuantile)) { + reloadBannerView(); + return true; } return false; } -I18n::Message BoxController::emptyMessage() { - return I18n::Message::NoDataToPlot; -} - -Responder * BoxController::defaultController() { - return tabController(); -} - const char * BoxController::title() { return I18n::translate(I18n::Message::BoxTab); } -void BoxController::viewWillAppear() { - m_view.selectMainView(true); - reloadBannerView(); - m_view.reload(); -} - -bool BoxController::handleEvent(Ion::Events::Event event) { - if (event == Ion::Events::Up) { - m_view.selectMainView(false); - app()->setFirstResponder(tabController()); - return true; - } - if (event == Ion::Events::Left || event == Ion::Events::Right) { - int nextSelectedQuantile = event == Ion::Events::Left ? (int)m_view.selectedQuantile()-1 : (int)m_view.selectedQuantile()+1; - if (m_view.selectQuantile(nextSelectedQuantile)) { - reloadBannerView(); - return true; - } - return false; - } - return false; -} - -void BoxController::didBecomeFirstResponder() { - m_view.selectMainView(true); - m_view.reload(); -} - -void BoxController::willExitResponderChain(Responder * nextFirstResponder) { - if (nextFirstResponder == tabController()) { - m_view.selectMainView(false); - m_view.reload(); - } -} - Responder * BoxController::tabController() const { return (parentResponder()->parentResponder()->parentResponder()); } void BoxController::reloadBannerView() { + if (selectedSeries() < 0) { + return; + } + + int selectedQuantile = (int)m_view.dataViewAtIndex(selectedSeries())->selectedQuantile(); + + // Set calculation name I18n::Message calculationName[5] = {I18n::Message::Minimum, I18n::Message::FirstQuartile, I18n::Message::Median, I18n::Message::ThirdQuartile, I18n::Message::Maximum}; - m_boxBannerView.setMessageAtIndex(calculationName[(int)m_view.selectedQuantile()], 0); + m_view.editableBannerView()->setMessageAtIndex(calculationName[selectedQuantile], 0); + + // Set calculation result 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()])(m_selectedSeries); + double calculation = (m_store->*calculationMethods[selectedQuantile])(selectedSeries()); PrintFloat::convertFloatToText(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); - m_boxBannerView.setLegendAtIndex(buffer, 1); + m_view.editableBannerView()->setLegendAtIndex(buffer, 1); } } diff --git a/apps/statistics/box_controller.h b/apps/statistics/box_controller.h index 215f11f63..2ee62a35e 100644 --- a/apps/statistics/box_controller.h +++ b/apps/statistics/box_controller.h @@ -3,37 +3,24 @@ #include #include "store.h" -#include "box_view.h" -#include "box_banner_view.h" +#include "multiple_boxes_view.h" +#include "multiple_data_view_controller.h" namespace Statistics { -class BoxController : public ViewController, public ButtonRowDelegate, public AlternateEmptyViewDelegate { +class BoxController : public MultipleDataViewController, public ButtonRowDelegate { public: BoxController(Responder * parentResponder, ButtonRowController * header, Store * store, BoxView::Quantile * selectedQuantile); - // AlternateEmptyViewDelegate - bool isEmpty() const override; - I18n::Message emptyMessage() override; - Responder * defaultController() override; + MultipleDataView * multipleDataView() override { return &m_view; } + bool moveSelectionHorizontally(int deltaIndex) override; // ViewController const char * title() override; - View * view() override { return &m_view; } - void viewWillAppear() override; - ViewController::DisplayParameter displayParameter() override { return ViewController::DisplayParameter::DoNotShowOwnTitle; } - - // Responder - bool handleEvent(Ion::Events::Event event) override; - void didBecomeFirstResponder() override; - void willExitResponderChain(Responder * nextFirstResponder) override; private: - Responder * tabController() const; - void reloadBannerView(); - BoxBannerView m_boxBannerView; - BoxView m_view; - Store * m_store; - int m_selectedSeries; + Responder * tabController() const override; + void reloadBannerView() override; + MultipleBoxesView m_view; }; } diff --git a/apps/statistics/box_view.cpp b/apps/statistics/box_view.cpp index 5dda1c82b..9eecc62bc 100644 --- a/apps/statistics/box_view.cpp +++ b/apps/statistics/box_view.cpp @@ -6,13 +6,14 @@ using namespace Shared; namespace Statistics { -BoxView::BoxView(Store * store, BannerView * bannerView, Quantile * selectedQuantile) : +BoxView::BoxView(Store * store, int series, Shared::BannerView * bannerView, Quantile * selectedQuantile, KDColor color) : CurveView(&m_boxRange, nullptr, bannerView, nullptr), m_store(store), m_boxRange(BoxRange(store)), m_labels{}, + m_series(series), m_selectedQuantile(selectedQuantile), - m_series(0) + m_selectedHistogramColor(color) { } diff --git a/apps/statistics/box_view.h b/apps/statistics/box_view.h index a9dd4d2a7..8e5ab039d 100644 --- a/apps/statistics/box_view.h +++ b/apps/statistics/box_view.h @@ -19,10 +19,10 @@ public: ThirdQuartile = 3, Max = 4 }; - BoxView(Store * store, Shared::BannerView * bannerView, Quantile * selectedQuantile); + BoxView(Store * store, int series, Shared::BannerView * bannerView, Quantile * selectedQuantile, KDColor color); Quantile selectedQuantile() const { return *m_selectedQuantile; } bool selectQuantile(int selectedQuantile); - void setSeries(int series) { m_series = series; } + int series() const { return m_series; } // CurveView void reload() override; @@ -34,8 +34,9 @@ private: Store * m_store; BoxRange m_boxRange; char m_labels[k_maxNumberOfXLabels][Poincare::PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits)]; - Quantile * m_selectedQuantile; int m_series; + Quantile * m_selectedQuantile; + KDColor m_selectedHistogramColor; }; } diff --git a/apps/statistics/multiple_boxes_view.cpp b/apps/statistics/multiple_boxes_view.cpp new file mode 100644 index 000000000..2fe56e6a8 --- /dev/null +++ b/apps/statistics/multiple_boxes_view.cpp @@ -0,0 +1,34 @@ +#include "multiple_boxes_view.h" +#include + +using namespace Shared; + +namespace Statistics { + +MultipleBoxesView::MultipleBoxesView(Store * store, BoxView::Quantile * selectedQuantile) : + MultipleDataView(store), + m_boxView1(store, 0, nullptr, selectedQuantile, Palette::Red), + m_boxView2(store, 1, nullptr, selectedQuantile, Palette::Blue), + m_boxView3(store, 2, nullptr, selectedQuantile, Palette::Green), + // TODO Share colors with stats/store_controller + m_bannerView() +{ + for (int i = 0; i < Store::k_numberOfSeries; i++) { + BoxView * boxView = dataViewAtIndex(i); + boxView->setDisplayBannerView(false); + //histView->setDisplayLabels(false); //TODO + } +} + +BoxView * MultipleBoxesView::dataViewAtIndex(int index) { + assert(index >= 0 && index < 3); + BoxView * views[] = {&m_boxView1, &m_boxView2, &m_boxView3}; + return views[index]; +} + +int MultipleBoxesView::seriesOfSubviewAtIndex(int index) { + assert(index >= 0 && index < numberOfSubviews() - 1); + return static_cast(subviewAtIndex(index))->series(); +} + +} diff --git a/apps/statistics/multiple_boxes_view.h b/apps/statistics/multiple_boxes_view.h new file mode 100644 index 000000000..e6b85335b --- /dev/null +++ b/apps/statistics/multiple_boxes_view.h @@ -0,0 +1,28 @@ +#ifndef STATISTICS_MULTIPLE_BOXES_VIEW_H +#define STATISTICS_MULTIPLE_BOXES_VIEW_H + +#include +#include "store.h" +#include "box_view.h" +#include "box_banner_view.h" +#include "multiple_data_view.h" + +namespace Statistics { + +class MultipleBoxesView : public MultipleDataView { +public: + MultipleBoxesView(Store * store, BoxView::Quantile * selectedQuantile); + // MultipleDataView + int seriesOfSubviewAtIndex(int index) override; + const BoxBannerView * bannerView() const override { return &m_bannerView; } + BoxView * dataViewAtIndex(int index) override; +private: + BoxView m_boxView1; + BoxView m_boxView2; + BoxView m_boxView3; + BoxBannerView m_bannerView; +}; + +} + +#endif diff --git a/apps/statistics/multiple_data_view_controller.cpp b/apps/statistics/multiple_data_view_controller.cpp index 02c2e9269..ca6aaf5e8 100644 --- a/apps/statistics/multiple_data_view_controller.cpp +++ b/apps/statistics/multiple_data_view_controller.cpp @@ -14,10 +14,6 @@ MultipleDataViewController::MultipleDataViewController(Responder * parentRespond { } -MultipleDataView * MultipleDataViewController::multipleDataView() { - return static_cast(view()); -} - bool MultipleDataViewController::isEmpty() const { return m_store->isEmpty(); } From 6b5cea34a77c3fbfa7866f48e1ad9507a36d54fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 25 May 2018 11:41:44 +0200 Subject: [PATCH 059/156] [apps/stats] Display only one axis in box view --- apps/statistics/box_view.cpp | 11 ++++++++--- apps/statistics/box_view.h | 2 ++ apps/statistics/multiple_boxes_view.cpp | 11 ++++++++++- apps/statistics/multiple_boxes_view.h | 1 + 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/apps/statistics/box_view.cpp b/apps/statistics/box_view.cpp index 9eecc62bc..ec8da7cde 100644 --- a/apps/statistics/box_view.cpp +++ b/apps/statistics/box_view.cpp @@ -13,7 +13,8 @@ BoxView::BoxView(Store * store, int series, Shared::BannerView * bannerView, Qua m_labels{}, m_series(series), m_selectedQuantile(selectedQuantile), - m_selectedHistogramColor(color) + m_selectedHistogramColor(color), + m_displayAxis(true) { } @@ -43,8 +44,12 @@ void BoxView::reload() { void BoxView::drawRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(rect, KDColorWhite); - drawAxes(ctx, rect, Axis::Horizontal); - drawLabels(ctx, rect, Axis::Horizontal, false); + if (m_displayAxis) { + drawAxes(ctx, rect, Axis::Horizontal); + drawLabels(ctx, rect, Axis::Horizontal, false); + } + + // TODO : recompute drawing position without axis float lowBound = 0.35f; float upBound = 0.65f; double minVal = m_store->minValue(m_series); diff --git a/apps/statistics/box_view.h b/apps/statistics/box_view.h index 8e5ab039d..a209b54be 100644 --- a/apps/statistics/box_view.h +++ b/apps/statistics/box_view.h @@ -23,6 +23,7 @@ public: Quantile selectedQuantile() const { return *m_selectedQuantile; } bool selectQuantile(int selectedQuantile); int series() const { return m_series; } + void setDisplayAxis(bool display) { m_displayAxis = display; } // CurveView void reload() override; @@ -37,6 +38,7 @@ private: int m_series; Quantile * m_selectedQuantile; KDColor m_selectedHistogramColor; + bool m_displayAxis; }; } diff --git a/apps/statistics/multiple_boxes_view.cpp b/apps/statistics/multiple_boxes_view.cpp index 2fe56e6a8..33ff18383 100644 --- a/apps/statistics/multiple_boxes_view.cpp +++ b/apps/statistics/multiple_boxes_view.cpp @@ -16,7 +16,7 @@ MultipleBoxesView::MultipleBoxesView(Store * store, BoxView::Quantile * selected for (int i = 0; i < Store::k_numberOfSeries; i++) { BoxView * boxView = dataViewAtIndex(i); boxView->setDisplayBannerView(false); - //histView->setDisplayLabels(false); //TODO + boxView->setDisplayAxis(false); } } @@ -31,4 +31,13 @@ int MultipleBoxesView::seriesOfSubviewAtIndex(int index) { return static_cast(subviewAtIndex(index))->series(); } +void MultipleBoxesView::layoutSubviews() { + MultipleDataView::layoutSubviews(); + int numberOfDataSubviews = numberOfSubviews() - 1; + assert(numberOfDataSubviews > 0); + for (int i = 0; i < numberOfDataSubviews; i++) { + static_cast(subviewAtIndex(i))->setDisplayAxis(i == numberOfDataSubviews - 1); + } +} + } diff --git a/apps/statistics/multiple_boxes_view.h b/apps/statistics/multiple_boxes_view.h index e6b85335b..bfcbd5aed 100644 --- a/apps/statistics/multiple_boxes_view.h +++ b/apps/statistics/multiple_boxes_view.h @@ -16,6 +16,7 @@ public: int seriesOfSubviewAtIndex(int index) override; const BoxBannerView * bannerView() const override { return &m_bannerView; } BoxView * dataViewAtIndex(int index) override; + void layoutSubviews() override; private: BoxView m_boxView1; BoxView m_boxView2; From 3ce71b4ef9c5b7f0c3bb0dde94d2eaa0dcf75fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 25 May 2018 12:10:08 +0200 Subject: [PATCH 060/156] [apps/stats] Give bigger frame the box view that display axis --- apps/statistics/multiple_boxes_view.cpp | 21 ++++++++++---- apps/statistics/multiple_boxes_view.h | 2 +- apps/statistics/multiple_data_view.cpp | 37 +++++++++++++++---------- apps/statistics/multiple_data_view.h | 12 ++++---- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/apps/statistics/multiple_boxes_view.cpp b/apps/statistics/multiple_boxes_view.cpp index 33ff18383..078694c71 100644 --- a/apps/statistics/multiple_boxes_view.cpp +++ b/apps/statistics/multiple_boxes_view.cpp @@ -31,12 +31,23 @@ int MultipleBoxesView::seriesOfSubviewAtIndex(int index) { return static_cast(subviewAtIndex(index))->series(); } -void MultipleBoxesView::layoutSubviews() { - MultipleDataView::layoutSubviews(); - int numberOfDataSubviews = numberOfSubviews() - 1; +void MultipleBoxesView::layoutDataSubviews() { + int numberOfDataSubviews = m_store->numberOfNonEmptySeries(); assert(numberOfDataSubviews > 0); - for (int i = 0; i < numberOfDataSubviews; i++) { - static_cast(subviewAtIndex(i))->setDisplayAxis(i == numberOfDataSubviews - 1); + KDCoordinate bannerHeight = bannerFrame().height(); + KDCoordinate axisHeight = 30; + KDCoordinate subviewHeight = (bounds().height() - bannerHeight - axisHeight)/numberOfDataSubviews; + int displayedSubviewIndex = 0; + for (int i = 0; i < Store::k_numberOfSeries; i++) { + if (!m_store->seriesIsEmpty(i)) { + BoxView * boxView = dataViewAtIndex(i); + bool displaysAxis = displayedSubviewIndex == numberOfDataSubviews - 1; + boxView->setDisplayAxis(displaysAxis); + KDCoordinate currentSubviewHeight = subviewHeight + (displaysAxis ? axisHeight : 0); + KDRect frame = KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), currentSubviewHeight); + boxView->setFrame(frame); + displayedSubviewIndex++; + } } } diff --git a/apps/statistics/multiple_boxes_view.h b/apps/statistics/multiple_boxes_view.h index bfcbd5aed..769bdec7b 100644 --- a/apps/statistics/multiple_boxes_view.h +++ b/apps/statistics/multiple_boxes_view.h @@ -16,7 +16,7 @@ public: int seriesOfSubviewAtIndex(int index) override; const BoxBannerView * bannerView() const override { return &m_bannerView; } BoxView * dataViewAtIndex(int index) override; - void layoutSubviews() override; + void layoutDataSubviews() override; private: BoxView m_boxView1; BoxView m_boxView2; diff --git a/apps/statistics/multiple_data_view.cpp b/apps/statistics/multiple_data_view.cpp index c07f49685..4e9ebd224 100644 --- a/apps/statistics/multiple_data_view.cpp +++ b/apps/statistics/multiple_data_view.cpp @@ -36,24 +36,12 @@ void MultipleDataView::deselectDataView(int index) { changeDataViewSelection(index, false); } -void MultipleDataView::drawRect(KDContext * ctx, KDRect rect) const { - if (!m_displayBanner) { - ctx->fillRect(bannerFrame(), KDColorWhite); - } -} - int MultipleDataView::numberOfSubviews() const { int result = m_store->numberOfNonEmptySeries(); assert(result <= Store::k_numberOfSeries); return result + 1; // +1 for the banner view } -KDRect MultipleDataView::bannerFrame() const { - KDCoordinate bannerHeight = bannerView()->minimalSizeForOptimalDisplay().height(); - KDRect frame = KDRect(0, bounds().height() - bannerHeight, bounds().width(), bannerHeight); - return frame; -} - View * MultipleDataView::subviewAtIndex(int index) { if (index == numberOfSubviews() -1) { return editableBannerView(); @@ -74,6 +62,11 @@ View * MultipleDataView::subviewAtIndex(int index) { } void MultipleDataView::layoutSubviews() { + layoutDataSubviews(); + layoutBanner(); +} + +void MultipleDataView::layoutDataSubviews() { int numberDataSubviews = m_store->numberOfNonEmptySeries(); assert(numberDataSubviews > 0); KDCoordinate bannerHeight = bannerFrame().height(); @@ -87,6 +80,20 @@ void MultipleDataView::layoutSubviews() { displayedSubviewIndex++; } } +} + +void MultipleDataView::changeDataViewSelection(int index, bool select) { + dataViewAtIndex(index)->selectMainView(select); +} + +KDRect MultipleDataView::bannerFrame() const { + KDCoordinate bannerHeight = bannerView()->minimalSizeForOptimalDisplay().height(); + KDRect frame = KDRect(0, bounds().height() - bannerHeight, bounds().width(), bannerHeight); + return frame; +} + +void MultipleDataView::layoutBanner() { + KDCoordinate bannerHeight = bannerFrame().height(); if (m_displayBanner) { editableBannerView()->setFrame(bannerFrame()); } else { @@ -95,8 +102,10 @@ void MultipleDataView::layoutSubviews() { } } -void MultipleDataView::changeDataViewSelection(int index, bool select) { - dataViewAtIndex(index)->selectMainView(select); +void MultipleDataView::drawRect(KDContext * ctx, KDRect rect) const { + if (!m_displayBanner) { + ctx->fillRect(bannerFrame(), KDColorWhite); + } } } diff --git a/apps/statistics/multiple_data_view.h b/apps/statistics/multiple_data_view.h index c53b52cde..116c7f199 100644 --- a/apps/statistics/multiple_data_view.h +++ b/apps/statistics/multiple_data_view.h @@ -11,8 +11,8 @@ namespace Statistics { class MultipleDataView : public View { public: MultipleDataView(Store * store) : - m_displayBanner(false), - m_store(store) + m_store(store), + m_displayBanner(false) {} // Data views void selectDataView(int index); @@ -33,13 +33,15 @@ public: protected: virtual const Shared::BannerView * bannerView() const = 0; void layoutSubviews() override; + virtual void layoutDataSubviews(); View * subviewAtIndex(int index) override; virtual void changeDataViewSelection(int index, bool select); -private: - void drawRect(KDContext * ctx, KDRect rect) const override; KDRect bannerFrame() const; - bool m_displayBanner; Store * m_store; +private: + void layoutBanner(); + void drawRect(KDContext * ctx, KDRect rect) const override; + bool m_displayBanner; }; } From 6e9021ed35800d42ba622fd3afd9bffb22844e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 25 May 2018 15:01:51 +0200 Subject: [PATCH 061/156] [apps/stats] Fix display one axis only in Box tab The axis view is now a separate view, so there is no problem of computing the height of the "axed" box view. --- apps/shared/curve_view.cpp | 14 ++++++----- apps/shared/curve_view.h | 2 +- apps/statistics/Makefile | 1 + apps/statistics/box_axis_view.cpp | 19 +++++++++++++++ apps/statistics/box_axis_view.h | 29 +++++++++++++++++++++++ apps/statistics/box_view.cpp | 19 ++++----------- apps/statistics/box_view.h | 5 +--- apps/statistics/multiple_boxes_view.cpp | 31 ++++++++++++++++++------- apps/statistics/multiple_boxes_view.h | 11 ++++++++- apps/statistics/multiple_data_view.cpp | 2 +- apps/statistics/multiple_data_view.h | 2 +- 11 files changed, 98 insertions(+), 37 deletions(-) create mode 100644 apps/statistics/box_axis_view.cpp create mode 100644 apps/statistics/box_axis_view.h diff --git a/apps/shared/curve_view.cpp b/apps/shared/curve_view.cpp index 5d7696834..62e69b4f0 100644 --- a/apps/shared/curve_view.cpp +++ b/apps/shared/curve_view.cpp @@ -145,10 +145,12 @@ void CurveView::computeLabels(Axis axis) { } } -void CurveView::drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin, bool graduationOnly) const { +void CurveView::drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin, bool graduationOnly, bool fixCoordinate, KDCoordinate fixedCoordinate) const { float step = gridUnit(axis); float start = 2.0f*step*(std::ceil(min(axis)/(2.0f*step))); float end = max(axis); + float verticalCoordinate = fixCoordinate ? fixedCoordinate : std::round(floatToPixel(Axis::Vertical, 0.0f)); + float horizontalCoordinate = fixCoordinate ? fixedCoordinate : std::round(floatToPixel(Axis::Horizontal, 0.0f)); int i = 0; for (float x = start; x < end; x += 2.0f*step) { /* When |start| >> step, start + step = start. In that case, quit the @@ -156,18 +158,18 @@ void CurveView::drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOr if (x == x-step || x == x+step) { return; } - KDRect graduation(std::round(floatToPixel(Axis::Horizontal, x)), std::round(floatToPixel(Axis::Vertical, 0.0f)) -(k_labelGraduationLength-2)/2, 1, k_labelGraduationLength); + KDRect graduation(std::round(floatToPixel(Axis::Horizontal, x)), verticalCoordinate -(k_labelGraduationLength-2)/2, 1, k_labelGraduationLength); if (axis == Axis::Vertical) { - graduation = KDRect(std::round(floatToPixel(Axis::Horizontal, 0.0f))-(k_labelGraduationLength-2)/2, std::round(floatToPixel(Axis::Vertical, x)), k_labelGraduationLength, 1); + graduation = KDRect(horizontalCoordinate-(k_labelGraduationLength-2)/2, std::round(floatToPixel(Axis::Vertical, x)), k_labelGraduationLength, 1); } if (!graduationOnly) { KDSize textSize = KDText::stringSize(label(axis, i), KDText::FontSize::Small); - KDPoint origin(std::round(floatToPixel(Axis::Horizontal, x)) - textSize.width()/2, std::round(floatToPixel(Axis::Vertical, 0.0f)) + k_labelMargin); + KDPoint origin(std::round(floatToPixel(Axis::Horizontal, x)) - textSize.width()/2, verticalCoordinate + k_labelMargin); if (axis == Axis::Vertical) { - origin = KDPoint(std::round(floatToPixel(Axis::Horizontal, 0.0f)) + k_labelMargin, std::round(floatToPixel(Axis::Vertical, x)) - textSize.height()/2); + origin = KDPoint(horizontalCoordinate + k_labelMargin, std::round(floatToPixel(Axis::Vertical, x)) - textSize.height()/2); } if (-step < x && x < step && shiftOrigin) { - origin = KDPoint(std::round(floatToPixel(Axis::Horizontal, 0.0f)) + k_labelMargin, std::round(floatToPixel(Axis::Vertical, 0.0f)) + k_labelMargin); + origin = KDPoint(horizontalCoordinate + k_labelMargin, verticalCoordinate + k_labelMargin); } if (rect.intersects(KDRect(origin, KDText::stringSize(label(axis, i), KDText::FontSize::Small)))) { ctx->blendString(label(axis, i), origin, KDText::FontSize::Small, KDColorBlack); diff --git a/apps/shared/curve_view.h b/apps/shared/curve_view.h index 9dea2c23f..a4a79cc9f 100644 --- a/apps/shared/curve_view.h +++ b/apps/shared/curve_view.h @@ -60,7 +60,7 @@ protected: 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; void computeLabels(Axis axis); - void drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin, bool graduationOnly = false) const; + void drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin, bool graduationOnly = false, bool fixCoordinate = false, KDCoordinate fixedCoordinate = 0) const; View * m_bannerView; CurveViewCursor * m_curveViewCursor; private: diff --git a/apps/statistics/Makefile b/apps/statistics/Makefile index 890e0d0da..a29315509 100644 --- a/apps/statistics/Makefile +++ b/apps/statistics/Makefile @@ -3,6 +3,7 @@ snapshot_headers += apps/statistics/app.h app_objs += $(addprefix apps/statistics/,\ app.o\ + box_axis_view.o\ box_banner_view.o\ box_controller.o\ box_range.o\ diff --git a/apps/statistics/box_axis_view.cpp b/apps/statistics/box_axis_view.cpp new file mode 100644 index 000000000..1510fc5da --- /dev/null +++ b/apps/statistics/box_axis_view.cpp @@ -0,0 +1,19 @@ +#include "box_axis_view.h" +#include + +using namespace Shared; + +namespace Statistics { + +void BoxAxisView::drawRect(KDContext * ctx, KDRect rect) const { + ctx->fillRect(rect, KDColorWhite); + KDRect lineRect = KDRect(0, k_axisMargin, bounds().width(), 1); + ctx->fillRect(lineRect, KDColorBlack); + drawLabels(ctx, rect, Axis::Horizontal, false, false, true, k_axisMargin); +} + +char * BoxAxisView::label(Axis axis, int index) const { + return axis == Axis::Vertical ? nullptr : (char *)m_labels[index]; +} + +} diff --git a/apps/statistics/box_axis_view.h b/apps/statistics/box_axis_view.h new file mode 100644 index 000000000..c913e7107 --- /dev/null +++ b/apps/statistics/box_axis_view.h @@ -0,0 +1,29 @@ +#ifndef STATISTICS_BOX_AXIS_VIEW_H +#define STATISTICS_BOX_AXIS_VIEW_H + +#include "box_range.h" +#include "store.h" +#include "../shared/curve_view.h" +#include "../constant.h" + +namespace Statistics { + +class BoxAxisView : public Shared::CurveView { +public: + BoxAxisView(Store * store) : + CurveView(&m_boxRange), + m_labels{}, + m_boxRange(BoxRange(store)) + {} + void drawRect(KDContext * ctx, KDRect rect) const override; +private: + constexpr static KDCoordinate k_axisMargin = 3; + char * label(Axis axis, int index) const override; + char m_labels[k_maxNumberOfXLabels][Poincare::PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits)]; + BoxRange m_boxRange; +}; + +} + + +#endif diff --git a/apps/statistics/box_view.cpp b/apps/statistics/box_view.cpp index ec8da7cde..823d79345 100644 --- a/apps/statistics/box_view.cpp +++ b/apps/statistics/box_view.cpp @@ -10,11 +10,9 @@ BoxView::BoxView(Store * store, int series, Shared::BannerView * bannerView, Qua CurveView(&m_boxRange, nullptr, bannerView, nullptr), m_store(store), m_boxRange(BoxRange(store)), - m_labels{}, m_series(series), m_selectedQuantile(selectedQuantile), - m_selectedHistogramColor(color), - m_displayAxis(true) + m_selectedHistogramColor(color) { } @@ -44,10 +42,6 @@ void BoxView::reload() { void BoxView::drawRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(rect, KDColorWhite); - if (m_displayAxis) { - drawAxes(ctx, rect, Axis::Horizontal); - drawLabels(ctx, rect, Axis::Horizontal, false); - } // TODO : recompute drawing position without axis float lowBound = 0.35f; @@ -65,14 +59,15 @@ void BoxView::drawRect(KDContext * ctx, KDRect rect) const { 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, minVal, firstQuart, Palette::GreyDark); - drawSegment(ctx, rect, Axis::Horizontal, 0.5f, thirdQuart, maxVal, Palette::GreyDark); + float segmentOrd = (lowBound + upBound)/ 2.0f; + drawSegment(ctx, rect, Axis::Horizontal, segmentOrd, minVal, firstQuart, Palette::GreyDark); + drawSegment(ctx, rect, Axis::Horizontal, segmentOrd, thirdQuart, maxVal, Palette::GreyDark); 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 - * lines. This solution could hide the highlighed line by coloring the next + * lines. This solution could hide the highlighted line by coloring the next * quantile if it has the same value. */ for (int k = 0; k < 5; k++) { drawSegment(ctx, rect, Axis::Vertical, calculations[k], lowBound, upBound, Palette::GreyMiddle, 2); @@ -82,8 +77,4 @@ void BoxView::drawRect(KDContext * ctx, KDRect rect) const { } } -char * BoxView::label(Axis axis, int index) const { - return axis == Axis::Vertical ? nullptr : (char *)m_labels[index]; -} - } diff --git a/apps/statistics/box_view.h b/apps/statistics/box_view.h index a209b54be..6247e6297 100644 --- a/apps/statistics/box_view.h +++ b/apps/statistics/box_view.h @@ -23,7 +23,6 @@ public: Quantile selectedQuantile() const { return *m_selectedQuantile; } bool selectQuantile(int selectedQuantile); int series() const { return m_series; } - void setDisplayAxis(bool display) { m_displayAxis = display; } // CurveView void reload() override; @@ -31,14 +30,12 @@ public: // View void drawRect(KDContext * ctx, KDRect rect) const override; private: - char * label(Axis axis, int index) const override; + char * label(Axis axis, int index) const override { return nullptr; } Store * m_store; BoxRange m_boxRange; - char m_labels[k_maxNumberOfXLabels][Poincare::PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits)]; int m_series; Quantile * m_selectedQuantile; KDColor m_selectedHistogramColor; - bool m_displayAxis; }; } diff --git a/apps/statistics/multiple_boxes_view.cpp b/apps/statistics/multiple_boxes_view.cpp index 078694c71..f9d575146 100644 --- a/apps/statistics/multiple_boxes_view.cpp +++ b/apps/statistics/multiple_boxes_view.cpp @@ -11,12 +11,12 @@ MultipleBoxesView::MultipleBoxesView(Store * store, BoxView::Quantile * selected m_boxView2(store, 1, nullptr, selectedQuantile, Palette::Blue), m_boxView3(store, 2, nullptr, selectedQuantile, Palette::Green), // TODO Share colors with stats/store_controller + m_axisView(store), m_bannerView() { for (int i = 0; i < Store::k_numberOfSeries; i++) { BoxView * boxView = dataViewAtIndex(i); boxView->setDisplayBannerView(false); - boxView->setDisplayAxis(false); } } @@ -35,20 +35,33 @@ void MultipleBoxesView::layoutDataSubviews() { int numberOfDataSubviews = m_store->numberOfNonEmptySeries(); assert(numberOfDataSubviews > 0); KDCoordinate bannerHeight = bannerFrame().height(); - KDCoordinate axisHeight = 30; - KDCoordinate subviewHeight = (bounds().height() - bannerHeight - axisHeight)/numberOfDataSubviews; + KDCoordinate subviewHeight = (bounds().height() - bannerHeight - k_axisViewHeight)/numberOfDataSubviews; int displayedSubviewIndex = 0; for (int i = 0; i < Store::k_numberOfSeries; i++) { if (!m_store->seriesIsEmpty(i)) { - BoxView * boxView = dataViewAtIndex(i); - bool displaysAxis = displayedSubviewIndex == numberOfDataSubviews - 1; - boxView->setDisplayAxis(displaysAxis); - KDCoordinate currentSubviewHeight = subviewHeight + (displaysAxis ? axisHeight : 0); - KDRect frame = KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), currentSubviewHeight); - boxView->setFrame(frame); + KDRect frame = KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), subviewHeight); + dataViewAtIndex(i)->setFrame(frame); displayedSubviewIndex++; } } + m_axisView.setFrame(KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), k_axisViewHeight)); } +void MultipleBoxesView::reload() { + MultipleDataView::reload(); + m_axisView.reload(); +} + +int MultipleBoxesView::numberOfSubviews() const { + return MultipleDataView::numberOfSubviews() + 1; // +1 for the axis view +} + +View * MultipleBoxesView::subviewAtIndex(int index) { + if (index == numberOfSubviews() - 1) { + return &m_axisView; + } + return MultipleDataView::subviewAtIndex(index); +} + + } diff --git a/apps/statistics/multiple_boxes_view.h b/apps/statistics/multiple_boxes_view.h index 769bdec7b..70a20c5f7 100644 --- a/apps/statistics/multiple_boxes_view.h +++ b/apps/statistics/multiple_boxes_view.h @@ -3,8 +3,9 @@ #include #include "store.h" -#include "box_view.h" +#include "box_axis_view.h" #include "box_banner_view.h" +#include "box_view.h" #include "multiple_data_view.h" namespace Statistics { @@ -17,10 +18,18 @@ public: const BoxBannerView * bannerView() const override { return &m_bannerView; } BoxView * dataViewAtIndex(int index) override; void layoutDataSubviews() override; + void reload() override; + + // View + int numberOfSubviews() const override; + View * subviewAtIndex(int index) override; + private: + static constexpr KDCoordinate k_axisViewHeight = 20; BoxView m_boxView1; BoxView m_boxView2; BoxView m_boxView3; + BoxAxisView m_axisView; BoxBannerView m_bannerView; }; diff --git a/apps/statistics/multiple_data_view.cpp b/apps/statistics/multiple_data_view.cpp index 4e9ebd224..893c2b6fd 100644 --- a/apps/statistics/multiple_data_view.cpp +++ b/apps/statistics/multiple_data_view.cpp @@ -43,7 +43,7 @@ int MultipleDataView::numberOfSubviews() const { } View * MultipleDataView::subviewAtIndex(int index) { - if (index == numberOfSubviews() -1) { + if (index == MultipleDataView::numberOfSubviews() -1) { return editableBannerView(); } int seriesIndex = 0; diff --git a/apps/statistics/multiple_data_view.h b/apps/statistics/multiple_data_view.h index 116c7f199..5937566ee 100644 --- a/apps/statistics/multiple_data_view.h +++ b/apps/statistics/multiple_data_view.h @@ -26,7 +26,7 @@ public: // Display void setDisplayBanner(bool display) { m_displayBanner = display; } - void reload(); + virtual void reload(); // View int numberOfSubviews() const override; From ed703c20cdd7dffe0a5935a7db0cfb69c5cc3c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 25 May 2018 15:56:37 +0200 Subject: [PATCH 062/156] [apps/stats] Adapt box vertical size to number of boxes. --- apps/statistics/box_view.cpp | 22 +++++++++++++++------- apps/statistics/box_view.h | 3 +++ apps/statistics/multiple_boxes_view.h | 2 +- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/apps/statistics/box_view.cpp b/apps/statistics/box_view.cpp index 823d79345..a6ba965a3 100644 --- a/apps/statistics/box_view.cpp +++ b/apps/statistics/box_view.cpp @@ -33,8 +33,8 @@ void BoxView::reload() { CalculPointer calculationMethods[5] = {&Store::minValue, &Store::firstQuartile, &Store::median, &Store::thirdQuartile, &Store::maxValue}; 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 pixelUpperBound = boxUpperBoundPixel(); + float pixelLowerBound = boxLowerBoundPixel(); float selectedValueInPixels = floatToPixel(Axis::Horizontal, calculation)-1; KDRect dirtyZone(KDRect(selectedValueInPixels, pixelLowerBound, 4, pixelUpperBound - pixelLowerBound)); markRectAsDirty(dirtyZone); @@ -43,9 +43,10 @@ void BoxView::reload() { void BoxView::drawRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(rect, KDColorWhite); - // TODO : recompute drawing position without axis - float lowBound = 0.35f; - float upBound = 0.65f; + KDCoordinate lowBoundPixel = boxLowerBoundPixel(); + KDCoordinate upBoundPixel = boxUpperBoundPixel(); + float lowBound = pixelToFloat(Axis::Vertical, upBoundPixel); + float upBound = pixelToFloat(Axis::Vertical, lowBoundPixel); double minVal = m_store->minValue(m_series); double firstQuart = m_store->firstQuartile(m_series); double thirdQuart = m_store->thirdQuartile(m_series); @@ -54,8 +55,6 @@ void BoxView::drawRect(KDContext * ctx, KDRect rect) const { // Draw the main box 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 @@ -77,4 +76,13 @@ void BoxView::drawRect(KDContext * ctx, KDRect rect) const { } } +KDCoordinate BoxView::boxLowerBoundPixel() const { + return bounds().height() / 2 - k_boxHeight / 2; +} + +KDCoordinate BoxView::boxUpperBoundPixel() const { + return bounds().height() / 2 + k_boxHeight / 2; +} + + } diff --git a/apps/statistics/box_view.h b/apps/statistics/box_view.h index 6247e6297..6741bd4cc 100644 --- a/apps/statistics/box_view.h +++ b/apps/statistics/box_view.h @@ -30,6 +30,9 @@ public: // View void drawRect(KDContext * ctx, KDRect rect) const override; private: + static constexpr KDCoordinate k_boxHeight = 40; + KDCoordinate boxLowerBoundPixel() const; + KDCoordinate boxUpperBoundPixel() const; char * label(Axis axis, int index) const override { return nullptr; } Store * m_store; BoxRange m_boxRange; diff --git a/apps/statistics/multiple_boxes_view.h b/apps/statistics/multiple_boxes_view.h index 70a20c5f7..c64176f0e 100644 --- a/apps/statistics/multiple_boxes_view.h +++ b/apps/statistics/multiple_boxes_view.h @@ -25,7 +25,7 @@ public: View * subviewAtIndex(int index) override; private: - static constexpr KDCoordinate k_axisViewHeight = 20; + static constexpr KDCoordinate k_axisViewHeight = 21; BoxView m_boxView1; BoxView m_boxView2; BoxView m_boxView3; From 0cb2c6215d67a1b64ab57bac19048818b31c1e55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 25 May 2018 16:32:31 +0200 Subject: [PATCH 063/156] [apps/stats] Color the boxes --- apps/statistics/box_controller.cpp | 2 +- apps/statistics/box_view.cpp | 20 +++++++++++++++++--- apps/statistics/box_view.h | 5 ++++- apps/statistics/multiple_boxes_view.cpp | 8 ++++---- apps/statistics/multiple_boxes_view.h | 4 +++- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/apps/statistics/box_controller.cpp b/apps/statistics/box_controller.cpp index a5b104a50..7c2f69daa 100644 --- a/apps/statistics/box_controller.cpp +++ b/apps/statistics/box_controller.cpp @@ -9,7 +9,7 @@ namespace Statistics { BoxController::BoxController(Responder * parentResponder, ButtonRowController * header, Store * store, BoxView::Quantile * selectedQuantile) : MultipleDataViewController(parentResponder, store, (int *)(selectedQuantile)), ButtonRowDelegate(header, nullptr), - m_view(store, selectedQuantile) + m_view(this, store, selectedQuantile) { } diff --git a/apps/statistics/box_view.cpp b/apps/statistics/box_view.cpp index a6ba965a3..c048c935d 100644 --- a/apps/statistics/box_view.cpp +++ b/apps/statistics/box_view.cpp @@ -1,4 +1,5 @@ #include "box_view.h" +#include "box_controller.h" #include #include @@ -6,9 +7,10 @@ using namespace Shared; namespace Statistics { -BoxView::BoxView(Store * store, int series, Shared::BannerView * bannerView, Quantile * selectedQuantile, KDColor color) : +BoxView::BoxView(BoxController * controller, Store * store, int series, Shared::BannerView * bannerView, Quantile * selectedQuantile, KDColor color) : CurveView(&m_boxRange, nullptr, bannerView, nullptr), m_store(store), + m_boxController(controller), m_boxRange(BoxRange(store)), m_series(series), m_selectedQuantile(selectedQuantile), @@ -35,9 +37,19 @@ void BoxView::reload() { float calculation = (m_store->*calculationMethods[(int)*m_selectedQuantile])(m_series); float pixelUpperBound = boxUpperBoundPixel(); float pixelLowerBound = boxLowerBoundPixel(); + + // Dirty the selected vertical bar float selectedValueInPixels = floatToPixel(Axis::Horizontal, calculation)-1; KDRect dirtyZone(KDRect(selectedValueInPixels, pixelLowerBound, 4, pixelUpperBound - pixelLowerBound)); markRectAsDirty(dirtyZone); + + // Dirty the colored horizontal bar + double minVal = std::round(floatToPixel(Axis::Horizontal, m_store->minValue(m_series))); + double firstQuart = std::round(floatToPixel(Axis::Horizontal, m_store->firstQuartile(m_series))); + double thirdQuart = std::round(floatToPixel(Axis::Horizontal, m_store->thirdQuartile(m_series))); + double maxVal = std::round(floatToPixel(Axis::Horizontal, m_store->maxValue(m_series))); + markRectAsDirty(KDRect(minVal, (pixelUpperBound + pixelUpperBound)/2, firstQuart - minVal, 1)); + markRectAsDirty(KDRect(thirdQuart, (pixelUpperBound + pixelUpperBound)/2, maxVal - thirdQuart, 1)); } void BoxView::drawRect(KDContext * ctx, KDRect rect) const { @@ -57,10 +69,12 @@ void BoxView::drawRect(KDContext * ctx, KDRect rect) const { KDCoordinate thirdQuartilePixels = std::round(floatToPixel(Axis::Horizontal, thirdQuart)); ctx->fillRect(KDRect(firstQuartilePixels, lowBoundPixel, thirdQuartilePixels - firstQuartilePixels+2, upBoundPixel-lowBoundPixel), Palette::GreyWhite); + // Draw the horizontal lines linking the box to the extreme bounds + KDColor horizontalColor = m_boxController->selectedSeries() == m_series ? m_selectedHistogramColor : Palette::GreyDark; float segmentOrd = (lowBound + upBound)/ 2.0f; - drawSegment(ctx, rect, Axis::Horizontal, segmentOrd, minVal, firstQuart, Palette::GreyDark); - drawSegment(ctx, rect, Axis::Horizontal, segmentOrd, thirdQuart, maxVal, Palette::GreyDark); + drawSegment(ctx, rect, Axis::Horizontal, segmentOrd, minVal, firstQuart, horizontalColor); + drawSegment(ctx, rect, Axis::Horizontal, segmentOrd, thirdQuart, maxVal, horizontalColor); 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 diff --git a/apps/statistics/box_view.h b/apps/statistics/box_view.h index 6741bd4cc..9e8d42632 100644 --- a/apps/statistics/box_view.h +++ b/apps/statistics/box_view.h @@ -9,6 +9,8 @@ namespace Statistics { +class BoxController; + class BoxView : public Shared::CurveView { public: enum class Quantile : int { @@ -19,7 +21,7 @@ public: ThirdQuartile = 3, Max = 4 }; - BoxView(Store * store, int series, Shared::BannerView * bannerView, Quantile * selectedQuantile, KDColor color); + BoxView(BoxController * controller, Store * store, int series, Shared::BannerView * bannerView, Quantile * selectedQuantile, KDColor color); Quantile selectedQuantile() const { return *m_selectedQuantile; } bool selectQuantile(int selectedQuantile); int series() const { return m_series; } @@ -35,6 +37,7 @@ private: KDCoordinate boxUpperBoundPixel() const; char * label(Axis axis, int index) const override { return nullptr; } Store * m_store; + BoxController * m_boxController; BoxRange m_boxRange; int m_series; Quantile * m_selectedQuantile; diff --git a/apps/statistics/multiple_boxes_view.cpp b/apps/statistics/multiple_boxes_view.cpp index f9d575146..87b8908d7 100644 --- a/apps/statistics/multiple_boxes_view.cpp +++ b/apps/statistics/multiple_boxes_view.cpp @@ -5,11 +5,11 @@ using namespace Shared; namespace Statistics { -MultipleBoxesView::MultipleBoxesView(Store * store, BoxView::Quantile * selectedQuantile) : +MultipleBoxesView::MultipleBoxesView(BoxController * controller, Store * store, BoxView::Quantile * selectedQuantile) : MultipleDataView(store), - m_boxView1(store, 0, nullptr, selectedQuantile, Palette::Red), - m_boxView2(store, 1, nullptr, selectedQuantile, Palette::Blue), - m_boxView3(store, 2, nullptr, selectedQuantile, Palette::Green), + m_boxView1(controller, store, 0, nullptr, selectedQuantile, Palette::Red), + m_boxView2(controller, store, 1, nullptr, selectedQuantile, Palette::Blue), + m_boxView3(controller, store, 2, nullptr, selectedQuantile, Palette::Green), // TODO Share colors with stats/store_controller m_axisView(store), m_bannerView() diff --git a/apps/statistics/multiple_boxes_view.h b/apps/statistics/multiple_boxes_view.h index c64176f0e..86f7bf986 100644 --- a/apps/statistics/multiple_boxes_view.h +++ b/apps/statistics/multiple_boxes_view.h @@ -10,9 +10,11 @@ namespace Statistics { +class BoxController; + class MultipleBoxesView : public MultipleDataView { public: - MultipleBoxesView(Store * store, BoxView::Quantile * selectedQuantile); + MultipleBoxesView(BoxController * controller, Store * store, BoxView::Quantile * selectedQuantile); // MultipleDataView int seriesOfSubviewAtIndex(int index) override; const BoxBannerView * bannerView() const override { return &m_bannerView; } From d5e75e2598180c6bdf7a7ce51aad41d59cc92af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 25 May 2018 16:37:48 +0200 Subject: [PATCH 064/156] [apps/stats] FIx navigation bug in boxes --- apps/statistics/multiple_data_view_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/statistics/multiple_data_view_controller.cpp b/apps/statistics/multiple_data_view_controller.cpp index ca6aaf5e8..ac186d859 100644 --- a/apps/statistics/multiple_data_view_controller.cpp +++ b/apps/statistics/multiple_data_view_controller.cpp @@ -40,7 +40,7 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) { assert(m_selectedSeries >= 0); if (event == Ion::Events::Down) { int currentSelectedSubview = multipleDataView()->indexOfSubviewAtSeries(m_selectedSeries); - if (currentSelectedSubview < multipleDataView()->numberOfSubviews() - 2) { + if (currentSelectedSubview < m_store->numberOfNonEmptySeries() - 1) { multipleDataView()->deselectDataView(m_selectedSeries); m_selectedSeries = multipleDataView()->seriesOfSubviewAtIndex(currentSelectedSubview+1); *m_selectedBarIndex = 0; From e7f074667aeb25f55ec9b99ec17f6e4cb7a97b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 25 May 2018 16:48:25 +0200 Subject: [PATCH 065/156] [apps/stats] Series name in the box banner --- apps/statistics/box_banner_view.cpp | 5 +++-- apps/statistics/box_banner_view.h | 3 ++- apps/statistics/box_controller.cpp | 10 ++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/apps/statistics/box_banner_view.cpp b/apps/statistics/box_banner_view.cpp index 22e84c683..6d3b87741 100644 --- a/apps/statistics/box_banner_view.cpp +++ b/apps/statistics/box_banner_view.cpp @@ -3,18 +3,19 @@ namespace Statistics { BoxBannerView::BoxBannerView() : + m_seriesName(KDText::FontSize::Small, 0.0f, 0.5f, KDColorBlack, Palette::GreyMiddle), m_calculationName(KDText::FontSize::Small, I18n::Message::Minimum, 0.0f, 0.5f, KDColorBlack, Palette::GreyMiddle), m_calculationValue(KDText::FontSize::Small, 1.0f, 0.5f, KDColorBlack, Palette::GreyMiddle) { } TextView * BoxBannerView::textViewAtIndex(int index) const { - const TextView * textViews[2] = {&m_calculationName, &m_calculationValue}; + const TextView * textViews[3] = {&m_seriesName, &m_calculationName, &m_calculationValue}; return (TextView *)textViews[index]; } MessageTextView * BoxBannerView::messageTextViewAtIndex(int index) const { - return index == 0 ? (MessageTextView *)&m_calculationName : nullptr; + return index == 1 ? (MessageTextView *)&m_calculationName : nullptr; } } diff --git a/apps/statistics/box_banner_view.h b/apps/statistics/box_banner_view.h index f69238901..9192e8275 100644 --- a/apps/statistics/box_banner_view.h +++ b/apps/statistics/box_banner_view.h @@ -11,9 +11,10 @@ class BoxBannerView : public Shared::BannerView { public: BoxBannerView(); private: - int numberOfSubviews() const override { return 2; } + int numberOfSubviews() const override { return 3; } TextView * textViewAtIndex(int i) const override; MessageTextView * messageTextViewAtIndex(int i) const override; + BufferTextView m_seriesName; MessageTextView m_calculationName; BufferTextView m_calculationValue; }; diff --git a/apps/statistics/box_controller.cpp b/apps/statistics/box_controller.cpp index 7c2f69daa..3e34c0838 100644 --- a/apps/statistics/box_controller.cpp +++ b/apps/statistics/box_controller.cpp @@ -38,9 +38,15 @@ void BoxController::reloadBannerView() { int selectedQuantile = (int)m_view.dataViewAtIndex(selectedSeries())->selectedQuantile(); + // Set series name + char seriesChar = '0' + selectedSeries() + 1; + char bufferName[] = {'V', seriesChar, '/', 'N', seriesChar, 0}; + m_view.editableBannerView()->setLegendAtIndex(bufferName, 0); + + // Set calculation name I18n::Message calculationName[5] = {I18n::Message::Minimum, I18n::Message::FirstQuartile, I18n::Message::Median, I18n::Message::ThirdQuartile, I18n::Message::Maximum}; - m_view.editableBannerView()->setMessageAtIndex(calculationName[selectedQuantile], 0); + m_view.editableBannerView()->setMessageAtIndex(calculationName[selectedQuantile], 1); // Set calculation result char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; @@ -48,7 +54,7 @@ void BoxController::reloadBannerView() { &Store::maxValue}; double calculation = (m_store->*calculationMethods[selectedQuantile])(selectedSeries()); PrintFloat::convertFloatToText(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); - m_view.editableBannerView()->setLegendAtIndex(buffer, 1); + m_view.editableBannerView()->setLegendAtIndex(buffer, 2); } } From 19509940196d770605d151ecf9a7e11c48959c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 25 May 2018 16:52:14 +0200 Subject: [PATCH 066/156] [apps/stats] Fix deletion on last non hidden data row --- apps/shared/store_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index c243a3813..af3ee0a87 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -123,7 +123,7 @@ bool StoreController::handleEvent(Ion::Events::Event event) { return true; } if (event == Ion::Events::Backspace) { - if (selectedRow() == 0 || selectedRow() == numberOfRows()-1) { + if (selectedRow() == 0 || selectedRow() > m_store->numberOfPairsOfSeries(selectedColumn()/FloatPairStore::k_numberOfColumnsPerSeries)) { return false; } m_store->deletePairOfSeriesAtIndex(series, selectedRow()-1); From 8ad6fd3c8c1d3e2f1ed48a28fec58d6ab3c94b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 25 May 2018 17:24:32 +0200 Subject: [PATCH 067/156] [apps/stats] Fix sizing problem It left one line of pixels non colored --- apps/statistics/multiple_boxes_view.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/statistics/multiple_boxes_view.cpp b/apps/statistics/multiple_boxes_view.cpp index 87b8908d7..183a7a0f8 100644 --- a/apps/statistics/multiple_boxes_view.cpp +++ b/apps/statistics/multiple_boxes_view.cpp @@ -44,7 +44,7 @@ void MultipleBoxesView::layoutDataSubviews() { displayedSubviewIndex++; } } - m_axisView.setFrame(KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), k_axisViewHeight)); + m_axisView.setFrame(KDRect(0, displayedSubviewIndex*subviewHeight, bounds().width(), bounds().height() - bannerHeight - displayedSubviewIndex*subviewHeight)); } void MultipleBoxesView::reload() { From 452957caf616ee7c8352cf0e87b7073b7f6a81cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 28 May 2018 11:43:29 +0200 Subject: [PATCH 068/156] [apps/shared] Class Hideable --- apps/shared/hideable.h | 22 +++++++++++++++++++ .../hideable_even_odd_editable_text_cell.cpp | 6 ++--- .../hideable_even_odd_editable_text_cell.h | 10 ++++----- 3 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 apps/shared/hideable.h diff --git a/apps/shared/hideable.h b/apps/shared/hideable.h new file mode 100644 index 000000000..bf71cc740 --- /dev/null +++ b/apps/shared/hideable.h @@ -0,0 +1,22 @@ +#ifndef APPS_SHARED_HIDEABLE_H +#define APPS_SHARED_HIDEABLE_H + +#include + +namespace Shared { + +class Hideable { +public: + Hideable() : + m_hide(false) + {} + static KDColor hideColor() { return Palette::WallScreenDark; } + bool hidden() const { return m_hide; } + virtual void setHide(bool hide) { m_hide = hide; } +private: + bool m_hide; +}; + +} + +#endif diff --git a/apps/shared/hideable_even_odd_editable_text_cell.cpp b/apps/shared/hideable_even_odd_editable_text_cell.cpp index e060e08a1..26cf50323 100644 --- a/apps/shared/hideable_even_odd_editable_text_cell.cpp +++ b/apps/shared/hideable_even_odd_editable_text_cell.cpp @@ -3,15 +3,15 @@ namespace Shared { KDColor HideableEvenOddEditableTextCell::backgroundColor() const { - if (m_hide) { + if (hidden()) { return hideColor(); } return EvenOddEditableTextCell::backgroundColor(); } void HideableEvenOddEditableTextCell::setHide(bool hide) { - if (m_hide != hide) { - m_hide = hide; + if (hidden() != hide) { + Hideable::setHide(hide); editableTextCell()->textField()->setBackgroundColor(backgroundColor()); reloadCell(); } diff --git a/apps/shared/hideable_even_odd_editable_text_cell.h b/apps/shared/hideable_even_odd_editable_text_cell.h index ce36bf478..c9385d89a 100644 --- a/apps/shared/hideable_even_odd_editable_text_cell.h +++ b/apps/shared/hideable_even_odd_editable_text_cell.h @@ -3,20 +3,18 @@ #include #include +#include "hideable.h" namespace Shared { -class HideableEvenOddEditableTextCell : public EvenOddEditableTextCell { +class HideableEvenOddEditableTextCell : public EvenOddEditableTextCell, public Hideable { public: HideableEvenOddEditableTextCell(Responder * parentResponder = nullptr, TextFieldDelegate * delegate = nullptr, char * draftTextBuffer = nullptr) : EvenOddEditableTextCell(parentResponder, delegate, draftTextBuffer), - m_hide(false) + Hideable() {} KDColor backgroundColor() const override; - static KDColor hideColor() { return Palette::WallScreenDark; } - void setHide(bool hide); -private: - bool m_hide; + void setHide(bool hide) override; }; } From fae5659bee136797c92a5bfdd04a620da73054d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 28 May 2018 11:44:10 +0200 Subject: [PATCH 069/156] [apps/shared] HideableEvenOddCell --- apps/shared/Makefile | 1 + apps/shared/hideable_even_odd_cell.cpp | 19 +++++++++++++++++++ apps/shared/hideable_even_odd_cell.h | 21 +++++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 apps/shared/hideable_even_odd_cell.cpp create mode 100644 apps/shared/hideable_even_odd_cell.h diff --git a/apps/shared/Makefile b/apps/shared/Makefile index 1187839ce..a07fccbc5 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -21,6 +21,7 @@ app_objs += $(addprefix apps/shared/,\ function_expression_cell.o\ function_title_cell.o\ go_to_parameter_controller.o\ + hideable_even_odd_cell.o\ hideable_even_odd_editable_text_cell.o\ initialisation_parameter_controller.o\ interactive_curve_view_controller.o\ diff --git a/apps/shared/hideable_even_odd_cell.cpp b/apps/shared/hideable_even_odd_cell.cpp new file mode 100644 index 000000000..7561bd4bf --- /dev/null +++ b/apps/shared/hideable_even_odd_cell.cpp @@ -0,0 +1,19 @@ +#include "hideable_even_odd_cell.h" + +namespace Shared { + +KDColor HideableEvenOddCell::backgroundColor() const { + if (hidden()) { + return hideColor(); + } + return EvenOddCell::backgroundColor(); +} + +void HideableEvenOddCell::setHide(bool hide) { + if (hidden() != hide) { + Hideable::setHide(hide); + reloadCell(); + } +} + +} diff --git a/apps/shared/hideable_even_odd_cell.h b/apps/shared/hideable_even_odd_cell.h new file mode 100644 index 000000000..ccb6b6e33 --- /dev/null +++ b/apps/shared/hideable_even_odd_cell.h @@ -0,0 +1,21 @@ +#ifndef APPS_SHARED_HIDEABLE_EVEN_ODD_CELL_H +#define APPS_SHARED_HIDEABLE_EVEN_ODD_CELL_H + +#include +#include "hideable.h" + +namespace Shared { + +class HideableEvenOddCell : public EvenOddCell, public Hideable { +public: + HideableEvenOddCell() : + EvenOddCell(), + Hideable() + {} + KDColor backgroundColor() const override; + void setHide(bool hide) override; +}; + +} + +#endif From 249b15750ae1912119f151f06a5df7e4928fffbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 28 May 2018 11:45:12 +0200 Subject: [PATCH 070/156] [apps/stats] Display all non empty series in calculation --- apps/statistics/calculation_controller.cpp | 146 ++++++++++++++++----- apps/statistics/calculation_controller.h | 32 +++-- apps/statistics/store.cpp | 14 ++ apps/statistics/store.h | 1 + escher/src/table_view.cpp | 2 +- 5 files changed, 155 insertions(+), 40 deletions(-) diff --git a/apps/statistics/calculation_controller.cpp b/apps/statistics/calculation_controller.cpp index f21897058..0be21c480 100644 --- a/apps/statistics/calculation_controller.cpp +++ b/apps/statistics/calculation_controller.cpp @@ -13,8 +13,10 @@ namespace Statistics { CalculationController::CalculationController(Responder * parentResponder, ButtonRowController * header, Store * store) : TabTableController(parentResponder, this), ButtonRowDelegate(header, nullptr), - m_titleCells{}, + m_seriesTitleCells{}, + m_calculationTitleCells{}, m_calculationCells{}, + m_hideableCell(nullptr), m_store(store) { } @@ -35,28 +37,62 @@ Responder * CalculationController::defaultController() { // TableViewDataSource +int CalculationController::numberOfColumns() { + return 1 + m_store->numberOfNonEmptySeries(); +} + void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { - EvenOddCell * myCell = (EvenOddCell *)cell; - myCell->setEven(j%2 == 0); - myCell->setHighlighted(i == selectedColumn() && j == selectedRow()); - if (i == 0) { - I18n::Message titles[k_totalNumberOfRows] = {I18n::Message::TotalSize, I18n::Message::Minimum, I18n::Message::Maximum, I18n::Message::Range, I18n::Message::Mean, I18n::Message::StandardDeviationSigma, I18n::Message::Deviation, I18n::Message::FirstQuartile, I18n::Message::ThirdQuartile, I18n::Message::Median, I18n::Message::InterquartileRange, I18n::Message::Sum, I18n::Message::SquareSum, I18n::Message::SampleStandardDeviationS}; - EvenOddMessageTextCell * myCell = (EvenOddMessageTextCell *)cell; - myCell->setMessage(titles[j]); - } else { - 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])(0); //TODO - EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)cell; - char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; - PrintFloat::convertFloatToText(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); - myCell->setText(buffer); + EvenOddCell * evenOddCell = (EvenOddCell *)cell; + evenOddCell->setEven(j%2 == 0); + evenOddCell->setHighlighted(i == selectedColumn() && j == selectedRow()); + if (i == 0 && j == 0) { + return; } + if (j == 0) { + // Display a series title cell + int seriesNumber = m_store->indexOfKthNonEmptySeries(i-1); + char titleBuffer[] = {'V', static_cast('0'+seriesNumber), '/', 'N', static_cast('0'+seriesNumber), 0}; + KDColor colors[] = {Palette::Red, Palette::Blue, Palette::Green}; + StoreTitleCell * storeTitleCell = static_cast(cell); + storeTitleCell->setText(titleBuffer); + storeTitleCell->setColor(colors[seriesNumber]); + return; + } + if (i == 0) { + // Display a calculation title cell + I18n::Message titles[k_totalNumberOfRows] = { + I18n::Message::TotalSize, + I18n::Message::Minimum, + I18n::Message::Maximum, + I18n::Message::Range, + I18n::Message::Mean, + I18n::Message::StandardDeviationSigma, + I18n::Message::Deviation, + I18n::Message::FirstQuartile, + I18n::Message::ThirdQuartile, + I18n::Message::Median, + I18n::Message::InterquartileRange, + I18n::Message::Sum, + I18n::Message::SquareSum, + I18n::Message::SampleStandardDeviationS}; + EvenOddMessageTextCell * evenOddMessageCell = static_cast(cell); + evenOddMessageCell->setMessage(titles[j-1]); + return; + } + // Display a calculation cell + 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}; + int seriesIndex = m_store->indexOfKthNonEmptySeries(i-1); + double calculation = (m_store->*calculationMethods[j-1])(seriesIndex); + EvenOddBufferTextCell * calculationCell = static_cast(cell); + char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; + PrintFloat::convertFloatToText(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + calculationCell->setText(buffer); } KDCoordinate CalculationController::columnWidth(int i) { - return i == 0 ? k_titleCellWidth : Ion::Display::Width - Metric::CommonRightMargin - Metric::CommonLeftMargin-k_titleCellWidth; + return i == 0 ? k_calculationTitleCellWidth : k_calculationCellWidth; } KDCoordinate CalculationController::cumulatedWidthFromIndex(int i) { @@ -85,12 +121,45 @@ int CalculationController::indexFromCumulatedHeight(KDCoordinate offsetY) { } HighlightCell * CalculationController::reusableCell(int index, int type) { - assert(index < k_totalNumberOfRows); - return type == 0 ? static_cast(m_titleCells[index]) : static_cast(m_calculationCells[index]); + assert(index >= 0 && index < reusableCellCount(type)); + if (type == k_hideableCellType) { + return m_hideableCell; + } + if (type == k_calculationTitleCellType) { + return static_cast(m_calculationTitleCells[index]); + } + if (type == k_seriesTitleCellType) { + return static_cast(m_seriesTitleCells[index]); + } + assert(type == k_calculationCellType); + return static_cast(m_calculationCells[index]); +} + +int CalculationController::reusableCellCount(int type) { + if (type == k_hideableCellType) { + return 1; + } + if (type == k_calculationTitleCellType) { + return k_numberOfCalculationTitleCells; + } + if (type == k_seriesTitleCellType) { + return k_numberOfSeriesTitleCells; + } + assert(type == k_calculationCellType); + return k_numberOfCalculationCells; } int CalculationController::typeAtLocation(int i, int j) { - return i; + if (i == 0 && j == 0) { + return k_hideableCellType; + } + if (i == 0) { + return k_calculationTitleCellType; + } + if (j == 0) { + return k_seriesTitleCellType; + } + return k_calculationCellType; } // ViewController @@ -105,7 +174,7 @@ bool CalculationController::handleEvent(Ion::Events::Event event) { app()->setFirstResponder(tabController()); return true; } - if (event == Ion::Events::Copy && selectedColumn() == 1) { + if (event == Ion::Events::Copy && selectedColumn() > 0 && selectedRow() > 0) { EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)selectableTableView()->selectedCell(); Clipboard::sharedClipboard()->store(myCell->text()); return true; @@ -115,7 +184,7 @@ bool CalculationController::handleEvent(Ion::Events::Event event) { void CalculationController::didBecomeFirstResponder() { if (selectedRow() == -1) { - selectCellAtLocation(0, 0); + selectCellAtLocation(0, 1); } else { selectCellAtLocation(selectedColumn(), selectedRow()); } @@ -129,23 +198,40 @@ Responder * CalculationController::tabController() const { } View * CalculationController::loadView() { - for (int i = 0; i < k_maxNumberOfDisplayableRows; i++) { - m_titleCells[i] = new EvenOddMessageTextCell(KDText::FontSize::Small); - m_titleCells[i]->setAlignment(1.0f, 0.5f); + for (int i = 0; i < k_numberOfSeriesTitleCells; i++) { + m_seriesTitleCells[i] = new StoreTitleCell(FunctionTitleCell::Orientation::HorizontalIndicator, KDText::FontSize::Small); + m_seriesTitleCells[i]->setSeparatorRight(true); + } + for (int i = 0; i < k_numberOfCalculationTitleCells; i++) { + m_calculationTitleCells[i] = new EvenOddMessageTextCell(KDText::FontSize::Small); + m_calculationTitleCells[i]->setAlignment(1.0f, 0.5f); + } + for (int i = 0; i < k_numberOfCalculationCells; i++) { m_calculationCells[i] = new EvenOddBufferTextCell(KDText::FontSize::Small); m_calculationCells[i]->setTextColor(Palette::GreyDark); } - return TabTableController::loadView(); + m_hideableCell = new HideableEvenOddCell(); + m_hideableCell->setHide(true); + View * result = TabTableController::loadView(); + return result; } void CalculationController::unloadView(View * view) { - for (int i = 0; i < k_maxNumberOfDisplayableRows; i++) { - delete m_titleCells[i]; - m_titleCells[i] = nullptr; + for (int i = 0; i < k_numberOfSeriesTitleCells; i++) { + delete m_seriesTitleCells[i]; + m_seriesTitleCells[i] = nullptr; + } + for (int i = 0; i < k_numberOfCalculationTitleCells; i++) { + delete m_calculationTitleCells[i]; + m_calculationTitleCells[i] = nullptr; + } + for (int i = 0; i < k_numberOfCalculationCells; i++) { delete m_calculationCells[i]; m_calculationCells[i] = nullptr; } + delete m_hideableCell; + m_hideableCell = nullptr; TabTableController::unloadView(view); } diff --git a/apps/statistics/calculation_controller.h b/apps/statistics/calculation_controller.h index d682720cf..056acab2f 100644 --- a/apps/statistics/calculation_controller.h +++ b/apps/statistics/calculation_controller.h @@ -3,6 +3,8 @@ #include #include "store.h" +#include "../shared/hideable_even_odd_cell.h" +#include "../shared/store_title_cell.h" #include "../shared/tab_table_controller.h" namespace Statistics { @@ -19,7 +21,7 @@ public: // TableViewDataSource int numberOfRows() override { return k_totalNumberOfRows; } - int numberOfColumns() override { return 2; } + int numberOfColumns() override; void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; KDCoordinate columnWidth(int i) override; KDCoordinate rowHeight(int j) override { return k_cellHeight; } @@ -28,26 +30,38 @@ public: KDCoordinate cumulatedWidthFromIndex(int i) override; int indexFromCumulatedWidth(KDCoordinate offsetX) override; HighlightCell * reusableCell(int index, int type) override; - int reusableCellCount(int type) override { return k_maxNumberOfDisplayableRows; } + int reusableCellCount(int type) override; int typeAtLocation(int i, int j) override; // ViewController const char * title() override; - ViewController::DisplayParameter displayParameter() override { return ViewController::DisplayParameter::DoNotShowOwnTitle; } // Responder bool handleEvent(Ion::Events::Event event) override; void didBecomeFirstResponder() override; private: + static constexpr int k_totalNumberOfRows = 15; + static constexpr int k_maxNumberOfDisplayableRows = 11; + static constexpr int k_numberOfCalculationCells = 3 * k_maxNumberOfDisplayableRows; + static constexpr int k_numberOfSeriesTitleCells = 3; + static constexpr int k_numberOfCalculationTitleCells = k_maxNumberOfDisplayableRows; + + static constexpr int k_calculationTitleCellType = 0; + static constexpr int k_calculationCellType = 1; + static constexpr int k_seriesTitleCellType = 2; + static constexpr int k_hideableCellType = 3; + static constexpr KDCoordinate k_cellHeight = 20; + static constexpr KDCoordinate k_calculationTitleCellWidth = 175; + static constexpr KDCoordinate k_calculationCellWidth = 50; + Responder * tabController() const override; View * loadView() override; void unloadView(View * view) override; - constexpr static int k_totalNumberOfRows = 14; - constexpr static int k_maxNumberOfDisplayableRows = 11; - static constexpr KDCoordinate k_cellHeight = 20; - static constexpr KDCoordinate k_titleCellWidth = 175; - EvenOddMessageTextCell * m_titleCells[k_maxNumberOfDisplayableRows]; - EvenOddBufferTextCell * m_calculationCells[k_maxNumberOfDisplayableRows]; + + Shared::StoreTitleCell * m_seriesTitleCells[k_numberOfSeriesTitleCells]; + EvenOddMessageTextCell * m_calculationTitleCells[k_numberOfCalculationTitleCells]; + EvenOddBufferTextCell * m_calculationCells[k_numberOfCalculationCells]; + Shared::HideableEvenOddCell * m_hideableCell; Store * m_store; }; diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 1dbd34aff..35704df36 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -99,6 +99,20 @@ bool Store::seriesIsEmpty(int i) { return sumOfOccurrences(i) == 0; } +int Store::indexOfKthNonEmptySeries(int k) { + assert(k >= 0 && k < numberOfNonEmptySeries()); + int nonEmptySeriesCount = 0; + for (int i = 0; i < k_numberOfSeries; i++) { + if (!seriesIsEmpty(i)) { + if (nonEmptySeriesCount == k) { + return i; + } + nonEmptySeriesCount++; + } + } + assert(false); + return 0; +} /* Calculation */ diff --git a/apps/statistics/store.h b/apps/statistics/store.h index ba1ce5cc8..94b389f00 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -25,6 +25,7 @@ public: bool isEmpty(); int numberOfNonEmptySeries(); bool seriesIsEmpty(int i); + int indexOfKthNonEmptySeries(int k); // Calculation double sumOfOccurrences(int series); diff --git a/escher/src/table_view.cpp b/escher/src/table_view.cpp index 333eb0f6e..194ad58c4 100644 --- a/escher/src/table_view.cpp +++ b/escher/src/table_view.cpp @@ -160,7 +160,7 @@ View * TableView::ContentView::subviewAtIndex(int index) { } void TableView::ContentView::layoutSubviews() { - for (int index=0; index Date: Mon, 11 Jun 2018 13:57:54 +0200 Subject: [PATCH 071/156] [escher] Add comment in table view --- escher/src/table_view.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/escher/src/table_view.cpp b/escher/src/table_view.cpp index 194ad58c4..69c7a231d 100644 --- a/escher/src/table_view.cpp +++ b/escher/src/table_view.cpp @@ -160,6 +160,8 @@ View * TableView::ContentView::subviewAtIndex(int index) { } void TableView::ContentView::layoutSubviews() { + /* The number of subviews might change during the layouting so it needs to be + * recomputed at each step of the for loop. */ for (int index = 0; index < numberOfSubviews(); index++) { View * cell = subview(index); int i = absoluteColumnNumberFromSubviewIndex(index); From 41215ac5931c510eb24f1469ca505664dd6ee9cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 28 May 2018 13:35:18 +0200 Subject: [PATCH 072/156] [apps/stats] Draw table separators in Calculation --- apps/shared/store_cell.cpp | 14 ++++++++------ apps/shared/store_cell.h | 9 +++++---- apps/shared/store_controller.cpp | 4 ++-- apps/shared/store_separator_cell.h | 21 --------------------- apps/shared/store_title_cell.cpp | 13 +++++++------ apps/shared/store_title_cell.h | 9 +++++---- apps/statistics/Makefile | 1 + apps/statistics/calculation_cell.cpp | 20 ++++++++++++++++++++ apps/statistics/calculation_cell.h | 17 +++++++++++++++++ apps/statistics/calculation_controller.cpp | 4 ++-- apps/statistics/calculation_controller.h | 3 ++- apps/statistics/store_controller.cpp | 2 +- escher/include/escher/metric.h | 1 + 13 files changed, 71 insertions(+), 47 deletions(-) delete mode 100644 apps/shared/store_separator_cell.h create mode 100644 apps/statistics/calculation_cell.cpp create mode 100644 apps/statistics/calculation_cell.h diff --git a/apps/shared/store_cell.cpp b/apps/shared/store_cell.cpp index 5a40d0dcf..cee473f52 100644 --- a/apps/shared/store_cell.cpp +++ b/apps/shared/store_cell.cpp @@ -1,10 +1,11 @@ #include "store_cell.h" +#include "escher/metric.h" namespace Shared { -void StoreCell::setSeparatorRight(bool separator) { - if (separatorRight() != separator) { - StoreSeparatorCell::setSeparatorRight(separator); +void StoreCell::setSeparatorLeft(bool separator) { + if (m_separatorLeft != separator) { + m_separatorLeft = separator; reloadCell(); } } @@ -12,14 +13,15 @@ void StoreCell::setSeparatorRight(bool separator) { void StoreCell::drawRect(KDContext * ctx, KDRect rect) const { HideableEvenOddEditableTextCell::drawRect(ctx, rect); // Draw the separator - if (separatorRight()) { - ctx->fillRect(KDRect(bounds().width() - k_separatorThickness, 0, k_separatorThickness, bounds().height()), HideableEvenOddEditableTextCell::hideColor()); + KDRect separatorRect(0, 0, Metric::TableSeparatorThickness, bounds().height()); + if (m_separatorLeft) { + ctx->fillRect(separatorRect, HideableEvenOddEditableTextCell::hideColor()); } } void StoreCell::layoutSubviews() { KDRect boundsThis = bounds(); - editableTextCell()->setFrame(KDRect(boundsThis.topLeft(), boundsThis.width() - k_separatorThickness, boundsThis.height())); + editableTextCell()->setFrame(KDRect(boundsThis.left() + Metric::TableSeparatorThickness, boundsThis.top(), boundsThis.width() - Metric::TableSeparatorThickness, boundsThis.height())); } } diff --git a/apps/shared/store_cell.h b/apps/shared/store_cell.h index 4434ddc1e..cecf7dc55 100644 --- a/apps/shared/store_cell.h +++ b/apps/shared/store_cell.h @@ -2,19 +2,20 @@ #define APPS_SHARED_STORE_CELL_H #include "hideable_even_odd_editable_text_cell.h" -#include "store_separator_cell.h" namespace Shared { -class StoreCell : public HideableEvenOddEditableTextCell, public StoreSeparatorCell { +class StoreCell : public HideableEvenOddEditableTextCell { public: StoreCell(Responder * parentResponder = nullptr, TextFieldDelegate * delegate = nullptr, char * draftTextBuffer = nullptr) : HideableEvenOddEditableTextCell(parentResponder, delegate, draftTextBuffer), - StoreSeparatorCell() + m_separatorLeft(false) {} - void setSeparatorRight(bool separator) override; + void setSeparatorLeft(bool separator); void drawRect(KDContext * ctx, KDRect rect) const override; void layoutSubviews() override; +private: + bool m_separatorLeft; }; } diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index af3ee0a87..a0e6a5ef2 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -81,8 +81,8 @@ int StoreController::typeAtLocation(int i, int j) { void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { // Handle the separator if (cellAtLocationIsEditable(i, j)) { - bool shoudHaveRightSeparator = i % FloatPairStore::k_numberOfColumnsPerSeries == 1; - static_cast(cell)->setSeparatorRight(shoudHaveRightSeparator); + bool shouldHaveLeftSeparator = i % FloatPairStore::k_numberOfColumnsPerSeries == 0; + static_cast(cell)->setSeparatorLeft(shouldHaveLeftSeparator); } // Handle empty cells if (j > 0 && j > m_store->numberOfPairsOfSeries(seriesAtColumn(i)) && j < numberOfRows()) { diff --git a/apps/shared/store_separator_cell.h b/apps/shared/store_separator_cell.h deleted file mode 100644 index d41697488..000000000 --- a/apps/shared/store_separator_cell.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef APPS_SHARED_STORE_SEPARATOR_CELL_H -#define APPS_SHARED_STORE_SEPARATOR_CELL_H - -namespace Shared { - -class StoreSeparatorCell { -public: - StoreSeparatorCell() : - m_separatorRight(false) - {} - bool separatorRight() const { return m_separatorRight; } - virtual void setSeparatorRight(bool separator) { m_separatorRight = separator; } -protected: - static constexpr KDCoordinate k_separatorThickness = 2; -private: - bool m_separatorRight; -}; - -} - -#endif diff --git a/apps/shared/store_title_cell.cpp b/apps/shared/store_title_cell.cpp index c57db4c67..8c341cfb4 100644 --- a/apps/shared/store_title_cell.cpp +++ b/apps/shared/store_title_cell.cpp @@ -1,11 +1,12 @@ #include "store_title_cell.h" #include "hideable_even_odd_editable_text_cell.h" +#include "escher/metric.h" namespace Shared { -void StoreTitleCell::setSeparatorRight(bool separator) { - if (separatorRight() != separator) { - StoreSeparatorCell::setSeparatorRight(separator); +void StoreTitleCell::setSeparatorLeft(bool separator) { + if (m_separatorLeft != separator) { + m_separatorLeft = separator; reloadCell(); } } @@ -13,8 +14,8 @@ void StoreTitleCell::setSeparatorRight(bool separator) { void StoreTitleCell::drawRect(KDContext * ctx, KDRect rect) const { BufferFunctionTitleCell::drawRect(ctx, rect); // Draw the separator - KDRect separatorRect(bounds().width() - StoreSeparatorCell::k_separatorThickness, separatorRight() ? 0 : k_colorIndicatorThickness, StoreSeparatorCell::k_separatorThickness, bounds().height()); - if (separatorRight()) { + KDRect separatorRect(0, m_separatorLeft ? 0 : k_colorIndicatorThickness, Metric::TableSeparatorThickness, bounds().height() - (m_separatorLeft ? 0 : k_colorIndicatorThickness)); + if (m_separatorLeft) { ctx->fillRect(separatorRect, HideableEvenOddEditableTextCell::hideColor()); } else { ctx->fillRect(separatorRect, backgroundColor()); @@ -23,7 +24,7 @@ void StoreTitleCell::drawRect(KDContext * ctx, KDRect rect) const { void StoreTitleCell::layoutSubviews() { KDRect textFrame = bufferTextViewFrame(); - bufferTextView()->setFrame(KDRect(textFrame.topLeft(), textFrame.width() - StoreSeparatorCell::k_separatorThickness, textFrame.height() )); + bufferTextView()->setFrame(KDRect(textFrame.left() + Metric::TableSeparatorThickness, textFrame.top(), textFrame.width() - Metric::TableSeparatorThickness, textFrame.height())); } } diff --git a/apps/shared/store_title_cell.h b/apps/shared/store_title_cell.h index aa86af37e..823c09c35 100644 --- a/apps/shared/store_title_cell.h +++ b/apps/shared/store_title_cell.h @@ -2,19 +2,20 @@ #define SHARED_STORE_TITLE_CELL_H #include "buffer_function_title_cell.h" -#include "store_separator_cell.h" namespace Shared { -class StoreTitleCell : public BufferFunctionTitleCell, public StoreSeparatorCell { +class StoreTitleCell : public BufferFunctionTitleCell { public: StoreTitleCell(Orientation orientation, KDText::FontSize size = KDText::FontSize::Large) : BufferFunctionTitleCell(orientation, size), - StoreSeparatorCell() + m_separatorLeft(false) {} - void setSeparatorRight(bool separator) override; + void setSeparatorLeft(bool separator); void drawRect(KDContext * ctx, KDRect rect) const override; void layoutSubviews() override; +private: + bool m_separatorLeft; }; } diff --git a/apps/statistics/Makefile b/apps/statistics/Makefile index a29315509..3e195aa67 100644 --- a/apps/statistics/Makefile +++ b/apps/statistics/Makefile @@ -8,6 +8,7 @@ app_objs += $(addprefix apps/statistics/,\ box_controller.o\ box_range.o\ box_view.o\ + calculation_cell.o\ calculation_controller.o\ histogram_banner_view.o\ histogram_controller.o\ diff --git a/apps/statistics/calculation_cell.cpp b/apps/statistics/calculation_cell.cpp new file mode 100644 index 000000000..8a7e3f1bd --- /dev/null +++ b/apps/statistics/calculation_cell.cpp @@ -0,0 +1,20 @@ +#include "calculation_cell.h" +#include "../shared/hideable_even_odd_editable_text_cell.h" +#include "escher/metric.h" + +namespace Statistics { + +void CalculationCell::drawRect(KDContext * ctx, KDRect rect) const { + EvenOddBufferTextCell::drawRect(ctx, rect); + // Draw the separator + KDRect separatorRect(0, 0, Metric::TableSeparatorThickness, bounds().height()); + ctx->fillRect(separatorRect, Shared::HideableEvenOddEditableTextCell::hideColor()); +} + +void CalculationCell::layoutSubviews() { + KDRect boundsThis = bounds(); + m_bufferTextView.setFrame(KDRect(boundsThis.left() + Metric::TableSeparatorThickness, boundsThis.top(), boundsThis.width() - Metric::TableSeparatorThickness, boundsThis.height())); +} + +} + diff --git a/apps/statistics/calculation_cell.h b/apps/statistics/calculation_cell.h new file mode 100644 index 000000000..bc33745ae --- /dev/null +++ b/apps/statistics/calculation_cell.h @@ -0,0 +1,17 @@ +#ifndef APPS_STATISTICS_CALCULATION_CELL_H +#define APPS_STATISTICS_CALCULATION_CELL_H + +#include + +namespace Statistics { + +class CalculationCell : public EvenOddBufferTextCell { +public: + using EvenOddBufferTextCell::EvenOddBufferTextCell; + void drawRect(KDContext * ctx, KDRect rect) const override; + void layoutSubviews() override; +}; + +} + +#endif diff --git a/apps/statistics/calculation_controller.cpp b/apps/statistics/calculation_controller.cpp index 0be21c480..ddf3cf922 100644 --- a/apps/statistics/calculation_controller.cpp +++ b/apps/statistics/calculation_controller.cpp @@ -200,14 +200,14 @@ Responder * CalculationController::tabController() const { View * CalculationController::loadView() { for (int i = 0; i < k_numberOfSeriesTitleCells; i++) { m_seriesTitleCells[i] = new StoreTitleCell(FunctionTitleCell::Orientation::HorizontalIndicator, KDText::FontSize::Small); - m_seriesTitleCells[i]->setSeparatorRight(true); + m_seriesTitleCells[i]->setSeparatorLeft(true); } for (int i = 0; i < k_numberOfCalculationTitleCells; i++) { m_calculationTitleCells[i] = new EvenOddMessageTextCell(KDText::FontSize::Small); m_calculationTitleCells[i]->setAlignment(1.0f, 0.5f); } for (int i = 0; i < k_numberOfCalculationCells; i++) { - m_calculationCells[i] = new EvenOddBufferTextCell(KDText::FontSize::Small); + m_calculationCells[i] = new CalculationCell(KDText::FontSize::Small); m_calculationCells[i]->setTextColor(Palette::GreyDark); } m_hideableCell = new HideableEvenOddCell(); diff --git a/apps/statistics/calculation_controller.h b/apps/statistics/calculation_controller.h index 056acab2f..4ade377be 100644 --- a/apps/statistics/calculation_controller.h +++ b/apps/statistics/calculation_controller.h @@ -3,6 +3,7 @@ #include #include "store.h" +#include "calculation_cell.h" #include "../shared/hideable_even_odd_cell.h" #include "../shared/store_title_cell.h" #include "../shared/tab_table_controller.h" @@ -60,7 +61,7 @@ private: Shared::StoreTitleCell * m_seriesTitleCells[k_numberOfSeriesTitleCells]; EvenOddMessageTextCell * m_calculationTitleCells[k_numberOfCalculationTitleCells]; - EvenOddBufferTextCell * m_calculationCells[k_numberOfCalculationCells]; + CalculationCell * m_calculationCells[k_numberOfCalculationCells]; Shared::HideableEvenOddCell * m_hideableCell; Store * m_store; }; diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index f917ea312..ac9a15638 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -22,7 +22,7 @@ void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int return; } Shared::StoreTitleCell * mytitleCell = static_cast(cell); - mytitleCell->setSeparatorRight(i % Store::k_numberOfColumnsPerSeries == 1); + mytitleCell->setSeparatorLeft(i % Store::k_numberOfColumnsPerSeries == 0); int seriesIndex = i/Store::k_numberOfColumnsPerSeries; bool valuesColumn = i%Store::k_numberOfColumnsPerSeries == 0; assert(seriesIndex >= 0 && seriesIndex < FloatPairStore::k_numberOfSeries); diff --git a/escher/include/escher/metric.h b/escher/include/escher/metric.h index a2a7a1021..42e3211c0 100644 --- a/escher/include/escher/metric.h +++ b/escher/include/escher/metric.h @@ -29,6 +29,7 @@ public: constexpr static KDCoordinate FractionAndConjugateHorizontalOverflow = 2; constexpr static KDCoordinate FractionAndConjugateHorizontalMargin = 2; constexpr static KDCoordinate MinimalBracketAndParenthesisHeight = 18; + constexpr static KDCoordinate TableSeparatorThickness = 2; }; #endif From 0d47a193b296fd28d93ac598dfa51db39180c709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 28 May 2018 14:23:59 +0200 Subject: [PATCH 073/156] [apps/stats] Fix table margins. --- apps/shared/store_controller.cpp | 2 +- apps/shared/store_controller.h | 1 + apps/statistics/calculation_controller.cpp | 4 +++- apps/statistics/calculation_controller.h | 2 ++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index a0e6a5ef2..09645831c 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -180,7 +180,7 @@ View * StoreController::loadView() { for (int i = 0; i < k_maxNumberOfEditableCells; i++) { m_editableCells[i] = new StoreCell(tableView, this, m_draftTextBuffer); } - tableView->setMargins(k_margin); + tableView->setMargins(k_margin, k_scrollBarMargin, k_scrollBarMargin, k_margin); return tableView; } diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 7ae903b9a..d6d7342ef 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -36,6 +36,7 @@ public: protected: static constexpr KDCoordinate k_cellWidth = 80; //TODO static constexpr KDCoordinate k_margin = 8; + static constexpr KDCoordinate k_scrollBarMargin = Metric::CommonRightMargin; constexpr static int k_maxNumberOfEditableCells = 22 * FloatPairStore::k_numberOfSeries; constexpr static int k_numberOfTitleCells = FloatPairStore::k_numberOfColumnsPerSeries * FloatPairStore::k_numberOfSeries; // TODO Put finer number of cells static constexpr int k_titleCellType = 0; diff --git a/apps/statistics/calculation_controller.cpp b/apps/statistics/calculation_controller.cpp index ddf3cf922..9fa60eb53 100644 --- a/apps/statistics/calculation_controller.cpp +++ b/apps/statistics/calculation_controller.cpp @@ -212,7 +212,9 @@ View * CalculationController::loadView() { } m_hideableCell = new HideableEvenOddCell(); m_hideableCell->setHide(true); - View * result = TabTableController::loadView(); + View * result = TabTableController::loadView(); + SelectableTableView * casterResult = static_cast(result); + casterResult->setMargins(k_margin, k_scrollBarMargin, k_scrollBarMargin, k_margin); return result; } diff --git a/apps/statistics/calculation_controller.h b/apps/statistics/calculation_controller.h index 4ade377be..fe012d30a 100644 --- a/apps/statistics/calculation_controller.h +++ b/apps/statistics/calculation_controller.h @@ -54,6 +54,8 @@ private: static constexpr KDCoordinate k_cellHeight = 20; static constexpr KDCoordinate k_calculationTitleCellWidth = 175; static constexpr KDCoordinate k_calculationCellWidth = 50; + static constexpr KDCoordinate k_margin = 8; + static constexpr KDCoordinate k_scrollBarMargin = Metric::CommonRightMargin; Responder * tabController() const override; View * loadView() override; From 17e0fcc29e21e34aec65aeb85029bdc745b620f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 28 May 2018 14:24:19 +0200 Subject: [PATCH 074/156] [apps/stats] Fix CalculationController::indexFromCumulatedWidth --- apps/statistics/calculation_controller.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/statistics/calculation_controller.cpp b/apps/statistics/calculation_controller.cpp index 9fa60eb53..b368f493f 100644 --- a/apps/statistics/calculation_controller.cpp +++ b/apps/statistics/calculation_controller.cpp @@ -110,8 +110,8 @@ KDCoordinate CalculationController::cumulatedHeightFromIndex(int j) { int CalculationController::indexFromCumulatedWidth(KDCoordinate offsetX) { int result = 0; int i = 0; - while (result < offsetX && i < numberOfRows()) { - result += rowHeight(i++); + while (result < offsetX && i < numberOfColumns()) { + result += columnWidth(i++); } return (result < offsetX || offsetX == 0) ? i : i - 1; } @@ -150,6 +150,8 @@ int CalculationController::reusableCellCount(int type) { } int CalculationController::typeAtLocation(int i, int j) { + assert(i >= 0 && i < numberOfColumns()); + assert(j >= 0 && j < numberOfRows()); if (i == 0 && j == 0) { return k_hideableCellType; } From 5f59feb1a30b394e317a78ba93b6db3099313f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 28 May 2018 14:25:09 +0200 Subject: [PATCH 075/156] [apps/stats] Wider calculation columns --- apps/statistics/calculation_controller.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/statistics/calculation_controller.h b/apps/statistics/calculation_controller.h index fe012d30a..cb88b0f3a 100644 --- a/apps/statistics/calculation_controller.h +++ b/apps/statistics/calculation_controller.h @@ -53,7 +53,7 @@ private: static constexpr int k_hideableCellType = 3; static constexpr KDCoordinate k_cellHeight = 20; static constexpr KDCoordinate k_calculationTitleCellWidth = 175; - static constexpr KDCoordinate k_calculationCellWidth = 50; + static constexpr KDCoordinate k_calculationCellWidth = 70; static constexpr KDCoordinate k_margin = 8; static constexpr KDCoordinate k_scrollBarMargin = Metric::CommonRightMargin; From 4e9767b3c94dd08f09f2028e455cea1c7e17577e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 28 May 2018 15:34:47 +0200 Subject: [PATCH 076/156] [apps/stats] Handle the upperleft cell navigation in calculation --- apps/statistics/Makefile | 1 + apps/statistics/calculation_controller.cpp | 13 ++++++++----- .../calculation_selectable_table_view.cpp | 15 +++++++++++++++ .../calculation_selectable_table_view.h | 16 ++++++++++++++++ 4 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 apps/statistics/calculation_selectable_table_view.cpp create mode 100644 apps/statistics/calculation_selectable_table_view.h diff --git a/apps/statistics/Makefile b/apps/statistics/Makefile index 3e195aa67..24cc6b475 100644 --- a/apps/statistics/Makefile +++ b/apps/statistics/Makefile @@ -10,6 +10,7 @@ app_objs += $(addprefix apps/statistics/,\ box_view.o\ calculation_cell.o\ calculation_controller.o\ + calculation_selectable_table_view.o\ histogram_banner_view.o\ histogram_controller.o\ histogram_parameter_controller.o\ diff --git a/apps/statistics/calculation_controller.cpp b/apps/statistics/calculation_controller.cpp index b368f493f..824417743 100644 --- a/apps/statistics/calculation_controller.cpp +++ b/apps/statistics/calculation_controller.cpp @@ -1,7 +1,8 @@ #include "calculation_controller.h" +#include "app.h" +#include "calculation_selectable_table_view.h" #include "../constant.h" #include "../apps_container.h" -#include "app.h" #include #include @@ -214,10 +215,12 @@ View * CalculationController::loadView() { } m_hideableCell = new HideableEvenOddCell(); m_hideableCell->setHide(true); - View * result = TabTableController::loadView(); - SelectableTableView * casterResult = static_cast(result); - casterResult->setMargins(k_margin, k_scrollBarMargin, k_scrollBarMargin, k_margin); - return result; + + CalculationSelectableTableView * selectableTableView = new CalculationSelectableTableView(this, this, this); + selectableTableView->setBackgroundColor(Palette::WallScreenDark); + selectableTableView->setVerticalCellOverlap(0); + selectableTableView->setMargins(k_margin, k_scrollBarMargin, k_scrollBarMargin, k_margin); + return selectableTableView; } diff --git a/apps/statistics/calculation_selectable_table_view.cpp b/apps/statistics/calculation_selectable_table_view.cpp new file mode 100644 index 000000000..e9bd9a300 --- /dev/null +++ b/apps/statistics/calculation_selectable_table_view.cpp @@ -0,0 +1,15 @@ +#include "calculation_selectable_table_view.h" + +namespace Statistics { + +bool CalculationSelectableTableView::handleEvent(Ion::Events::Event event) { + if (event == Ion::Events::Up && selectedColumn() == 0 && selectedRow() == 1) { + return false; + } + if (event == Ion::Events::Left && selectedColumn() == 1 && selectedRow() == 0) { + return selectCellAtLocation(0, 1); + } + return SelectableTableView::handleEvent(event); +} + +} diff --git a/apps/statistics/calculation_selectable_table_view.h b/apps/statistics/calculation_selectable_table_view.h new file mode 100644 index 000000000..5de8b15f6 --- /dev/null +++ b/apps/statistics/calculation_selectable_table_view.h @@ -0,0 +1,16 @@ +#ifndef APPS_STATISTICS_CALCULATION_SELECTABLE_TABLE_VIEW_H +#define APPS_STATISTICS_CALCULATION_SELECTABLE_TABLE_VIEW_H + +#include + +namespace Statistics { + +class CalculationSelectableTableView : public SelectableTableView { +public: + using SelectableTableView::SelectableTableView; + bool handleEvent(Ion::Events::Event event) override; +}; + +} + +#endif From b99cd21660408f35ef12daf480fd75ca12e5bec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 28 May 2018 16:10:48 +0200 Subject: [PATCH 077/156] [apps/stats] Fix the display of the last empty row in store Before, it appeared when it shouldn't --- apps/shared/store_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 09645831c..f5bd4971e 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -169,7 +169,7 @@ int StoreController::numberOfElements() { } int StoreController::maxNumberOfElements() const { - return FloatPairStore::k_numberOfSeries * FloatPairStore::k_maxNumberOfPairs; + return FloatPairStore::k_maxNumberOfPairs; } View * StoreController::loadView() { From c9407d6bdbf28092984cad42c053caad5b3665ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 28 May 2018 17:07:00 +0200 Subject: [PATCH 078/156] [apps/stats] Color the box of the selected box view --- apps/statistics/box_view.cpp | 11 +++++++---- apps/statistics/box_view.h | 3 ++- apps/statistics/multiple_boxes_view.cpp | 6 +++--- escher/include/escher/palette.h | 3 +++ escher/src/palette.cpp | 3 +++ 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/apps/statistics/box_view.cpp b/apps/statistics/box_view.cpp index c048c935d..8fa05f271 100644 --- a/apps/statistics/box_view.cpp +++ b/apps/statistics/box_view.cpp @@ -7,14 +7,15 @@ using namespace Shared; namespace Statistics { -BoxView::BoxView(BoxController * controller, Store * store, int series, Shared::BannerView * bannerView, Quantile * selectedQuantile, KDColor color) : +BoxView::BoxView(BoxController * controller, Store * store, int series, Shared::BannerView * bannerView, Quantile * selectedQuantile, KDColor color, KDColor lightColor) : CurveView(&m_boxRange, nullptr, bannerView, nullptr), m_store(store), m_boxController(controller), m_boxRange(BoxRange(store)), m_series(series), m_selectedQuantile(selectedQuantile), - m_selectedHistogramColor(color) + m_selectedHistogramColor(color), + m_selectedHistogramLightColor(lightColor) { } @@ -64,14 +65,16 @@ void BoxView::drawRect(KDContext * ctx, KDRect rect) const { double thirdQuart = m_store->thirdQuartile(m_series); double maxVal = m_store->maxValue(m_series); + bool isSelected = m_boxController->selectedSeries() == m_series; + KDColor boxColor = isSelected ? m_selectedHistogramLightColor : Palette::GreyWhite; // Draw the main box KDCoordinate firstQuartilePixels = std::round(floatToPixel(Axis::Horizontal, firstQuart)); KDCoordinate thirdQuartilePixels = std::round(floatToPixel(Axis::Horizontal, thirdQuart)); ctx->fillRect(KDRect(firstQuartilePixels, lowBoundPixel, thirdQuartilePixels - firstQuartilePixels+2, - upBoundPixel-lowBoundPixel), Palette::GreyWhite); + upBoundPixel-lowBoundPixel), boxColor); // Draw the horizontal lines linking the box to the extreme bounds - KDColor horizontalColor = m_boxController->selectedSeries() == m_series ? m_selectedHistogramColor : Palette::GreyDark; + KDColor horizontalColor = isSelected ? m_selectedHistogramColor : Palette::GreyDark; float segmentOrd = (lowBound + upBound)/ 2.0f; drawSegment(ctx, rect, Axis::Horizontal, segmentOrd, minVal, firstQuart, horizontalColor); drawSegment(ctx, rect, Axis::Horizontal, segmentOrd, thirdQuart, maxVal, horizontalColor); diff --git a/apps/statistics/box_view.h b/apps/statistics/box_view.h index 9e8d42632..43ce455d6 100644 --- a/apps/statistics/box_view.h +++ b/apps/statistics/box_view.h @@ -21,7 +21,7 @@ public: ThirdQuartile = 3, Max = 4 }; - BoxView(BoxController * controller, Store * store, int series, Shared::BannerView * bannerView, Quantile * selectedQuantile, KDColor color); + BoxView(BoxController * controller, Store * store, int series, Shared::BannerView * bannerView, Quantile * selectedQuantile, KDColor color, KDColor lightColor); Quantile selectedQuantile() const { return *m_selectedQuantile; } bool selectQuantile(int selectedQuantile); int series() const { return m_series; } @@ -42,6 +42,7 @@ private: int m_series; Quantile * m_selectedQuantile; KDColor m_selectedHistogramColor; + KDColor m_selectedHistogramLightColor; }; } diff --git a/apps/statistics/multiple_boxes_view.cpp b/apps/statistics/multiple_boxes_view.cpp index 183a7a0f8..26a27cd89 100644 --- a/apps/statistics/multiple_boxes_view.cpp +++ b/apps/statistics/multiple_boxes_view.cpp @@ -7,9 +7,9 @@ namespace Statistics { MultipleBoxesView::MultipleBoxesView(BoxController * controller, Store * store, BoxView::Quantile * selectedQuantile) : MultipleDataView(store), - m_boxView1(controller, store, 0, nullptr, selectedQuantile, Palette::Red), - m_boxView2(controller, store, 1, nullptr, selectedQuantile, Palette::Blue), - m_boxView3(controller, store, 2, nullptr, selectedQuantile, Palette::Green), + m_boxView1(controller, store, 0, nullptr, selectedQuantile, Palette::Red, Palette::RedLight), + m_boxView2(controller, store, 1, nullptr, selectedQuantile, Palette::Blue, Palette::BlueLight), + m_boxView3(controller, store, 2, nullptr, selectedQuantile, Palette::Green, Palette::GreenLight), // TODO Share colors with stats/store_controller m_axisView(store), m_bannerView() diff --git a/escher/include/escher/palette.h b/escher/include/escher/palette.h index ad47ce19d..b380a245b 100644 --- a/escher/include/escher/palette.h +++ b/escher/include/escher/palette.h @@ -21,12 +21,15 @@ public: constexpr static KDColor SubTab = KDColor::RGB24(0xb8bbc5); constexpr static KDColor LowBattery = KDColor::RGB24(0xf30211); constexpr static KDColor Red = KDColor::RGB24(0xff000c); + constexpr static KDColor RedLight = KDColor::RGB24(0xfe6363); constexpr static KDColor Magenta = KDColor::RGB24(0xff0588); constexpr static KDColor Turquoise = KDColor::RGB24(0x60c1ec); constexpr static KDColor Pink = KDColor::RGB24(0xffabb6); constexpr static KDColor Blue = KDColor::RGB24(0x5075f2); + constexpr static KDColor BlueLight = KDColor::RGB24(0x718fee); constexpr static KDColor Orange = KDColor::RGB24(0xfe871f); constexpr static KDColor Green = KDColor::RGB24(0x76d435); + constexpr static KDColor GreenLight = KDColor::RGB24(0x52db8f); }; #endif diff --git a/escher/src/palette.cpp b/escher/src/palette.cpp index 59da5fbb7..719638922 100644 --- a/escher/src/palette.cpp +++ b/escher/src/palette.cpp @@ -16,9 +16,12 @@ constexpr KDColor Palette::WallScreenDark; constexpr KDColor Palette::SubTab; constexpr KDColor Palette::LowBattery; constexpr KDColor Palette::Red; +constexpr KDColor Palette::RedLight; constexpr KDColor Palette::Magenta; constexpr KDColor Palette::Turquoise; constexpr KDColor Palette::Pink; constexpr KDColor Palette::Blue; +constexpr KDColor Palette::BlueLight; constexpr KDColor Palette::Orange; constexpr KDColor Palette::Green; +constexpr KDColor Palette::GreenLight; From 0f2b811692f721579d9f9d9890c15ed46c7a5ce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 28 May 2018 17:17:55 +0200 Subject: [PATCH 079/156] [asscher/palette] Change Palette::Green It is now more readable in Stats/Data --- escher/include/escher/palette.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/escher/include/escher/palette.h b/escher/include/escher/palette.h index b380a245b..ab6457712 100644 --- a/escher/include/escher/palette.h +++ b/escher/include/escher/palette.h @@ -28,7 +28,7 @@ public: constexpr static KDColor Blue = KDColor::RGB24(0x5075f2); constexpr static KDColor BlueLight = KDColor::RGB24(0x718fee); constexpr static KDColor Orange = KDColor::RGB24(0xfe871f); - constexpr static KDColor Green = KDColor::RGB24(0x76d435); + constexpr static KDColor Green = KDColor::RGB24(0x50c102); constexpr static KDColor GreenLight = KDColor::RGB24(0x52db8f); }; From 2997e991c4f638aee6df2b6dc8e30397c3b03371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 10:41:31 +0200 Subject: [PATCH 080/156] [apps/stats] Fix display ok OK view for histogram parameters --- apps/statistics/multiple_histograms_view.cpp | 13 +++++++++++++ apps/statistics/multiple_histograms_view.h | 1 + 2 files changed, 14 insertions(+) diff --git a/apps/statistics/multiple_histograms_view.cpp b/apps/statistics/multiple_histograms_view.cpp index ff304d4d7..8c7ac869a 100644 --- a/apps/statistics/multiple_histograms_view.cpp +++ b/apps/statistics/multiple_histograms_view.cpp @@ -33,6 +33,19 @@ int MultipleHistogramsView::seriesOfSubviewAtIndex(int index) { return static_cast(subviewAtIndex(index))->series(); } +void MultipleHistogramsView::layoutSubviews() { + MultipleDataView::layoutSubviews(); + int numberHistogramSubviews = m_store->numberOfNonEmptySeries(); + assert(numberHistogramSubviews > 0); + int displayedSubviewIndex = 0; + for (int i = 0; i < Store::k_numberOfSeries; i++) { + if (!m_store->seriesIsEmpty(i)) { + dataViewAtIndex(i)->setOkView(displayedSubviewIndex == numberHistogramSubviews - 1 ? &m_okView : nullptr); + displayedSubviewIndex++; + } + } +} + void MultipleHistogramsView::changeDataViewSelection(int index, bool select) { MultipleDataView::changeDataViewSelection(index, select); dataViewAtIndex(index)->setDisplayLabels(select); diff --git a/apps/statistics/multiple_histograms_view.h b/apps/statistics/multiple_histograms_view.h index 1a658e02a..34d02e041 100644 --- a/apps/statistics/multiple_histograms_view.h +++ b/apps/statistics/multiple_histograms_view.h @@ -19,6 +19,7 @@ public: const HistogramBannerView * bannerView() const override { return &m_bannerView; } HistogramView * dataViewAtIndex(int index) override; private: + void layoutSubviews() override; void changeDataViewSelection(int index, bool select) override; HistogramView m_histogramView1; HistogramView m_histogramView2; From 3e547bb44c5a08fcfc24da5030cc862fe11f6f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 12:02:29 +0200 Subject: [PATCH 081/156] [apps/stats] Fix banner size computation --- apps/statistics/multiple_data_view.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/statistics/multiple_data_view.cpp b/apps/statistics/multiple_data_view.cpp index 893c2b6fd..8eef9f9cc 100644 --- a/apps/statistics/multiple_data_view.cpp +++ b/apps/statistics/multiple_data_view.cpp @@ -62,6 +62,8 @@ View * MultipleDataView::subviewAtIndex(int index) { } void MultipleDataView::layoutSubviews() { + // We need to set the banner width first, so its height can be computed + editableBannerView()->setFrame(KDRect(0, 0, bounds().width(), 0)); layoutDataSubviews(); layoutBanner(); } From df9be81ce743273e04bde5868794a08db36dc0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 13:23:57 +0200 Subject: [PATCH 082/156] [apps/stats] Dirty the whole box view when reloading it This is needed because we now change the main box color on selection --- apps/statistics/box_view.cpp | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/apps/statistics/box_view.cpp b/apps/statistics/box_view.cpp index 8fa05f271..3338ec631 100644 --- a/apps/statistics/box_view.cpp +++ b/apps/statistics/box_view.cpp @@ -33,24 +33,7 @@ bool BoxView::selectQuantile(int selectedQuantile) { 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])(m_series); - float pixelUpperBound = boxUpperBoundPixel(); - float pixelLowerBound = boxLowerBoundPixel(); - - // Dirty the selected vertical bar - float selectedValueInPixels = floatToPixel(Axis::Horizontal, calculation)-1; - KDRect dirtyZone(KDRect(selectedValueInPixels, pixelLowerBound, 4, pixelUpperBound - pixelLowerBound)); - markRectAsDirty(dirtyZone); - - // Dirty the colored horizontal bar - double minVal = std::round(floatToPixel(Axis::Horizontal, m_store->minValue(m_series))); - double firstQuart = std::round(floatToPixel(Axis::Horizontal, m_store->firstQuartile(m_series))); - double thirdQuart = std::round(floatToPixel(Axis::Horizontal, m_store->thirdQuartile(m_series))); - double maxVal = std::round(floatToPixel(Axis::Horizontal, m_store->maxValue(m_series))); - markRectAsDirty(KDRect(minVal, (pixelUpperBound + pixelUpperBound)/2, firstQuart - minVal, 1)); - markRectAsDirty(KDRect(thirdQuart, (pixelUpperBound + pixelUpperBound)/2, maxVal - thirdQuart, 1)); + markRectAsDirty(bounds()); } void BoxView::drawRect(KDContext * ctx, KDRect rect) const { From 47fa6f4a41e2dc4c2b8dbf540ad00cdb99e35eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 14:03:22 +0200 Subject: [PATCH 083/156] [apps/escher] Share color selection for data (Red, blue, Green, Yellow) --- apps/graph/cartesian_function_store.cpp | 17 ----------------- apps/graph/cartesian_function_store.h | 4 ---- apps/sequence/sequence_store.cpp | 17 ----------------- apps/sequence/sequence_store.h | 3 --- apps/shared/float_pair_store.h | 11 +++++++++++ apps/shared/function_store.cpp | 16 ++++++++++++++++ apps/shared/function_store.h | 2 +- apps/statistics/calculation_controller.cpp | 3 +-- apps/statistics/multiple_boxes_view.cpp | 7 +++---- apps/statistics/multiple_histograms_view.cpp | 7 +++---- apps/statistics/store_controller.cpp | 3 +-- escher/include/escher/palette.h | 2 ++ escher/src/palette.cpp | 2 ++ 13 files changed, 40 insertions(+), 54 deletions(-) diff --git a/apps/graph/cartesian_function_store.cpp b/apps/graph/cartesian_function_store.cpp index d7acf19c9..b2a5db2ea 100644 --- a/apps/graph/cartesian_function_store.cpp +++ b/apps/graph/cartesian_function_store.cpp @@ -8,7 +8,6 @@ extern "C" { namespace Graph { constexpr int CartesianFunctionStore::k_maxNumberOfFunctions; -constexpr KDColor CartesianFunctionStore::k_defaultColors[k_maxNumberOfFunctions]; constexpr const char * CartesianFunctionStore::k_functionNames[k_maxNumberOfFunctions]; CartesianFunctionStore::CartesianFunctionStore() : @@ -89,22 +88,6 @@ const char * CartesianFunctionStore::firstAvailableName() { return k_functionNames[0]; } -const KDColor CartesianFunctionStore::firstAvailableColor() { - for (int k = 0; k < k_maxNumberOfFunctions; k++) { - int j = 0; - while (j < m_numberOfFunctions) { - if (m_functions[j].color() == k_defaultColors[k]) { - break; - } - j++; - } - if (j == m_numberOfFunctions) { - return k_defaultColors[k]; - } - } - return k_defaultColors[0]; -} - void CartesianFunctionStore::removeAll() { for (int i = 0; i < m_numberOfFunctions; i++) { CartesianFunction emptyFunction("", KDColorBlack); diff --git a/apps/graph/cartesian_function_store.h b/apps/graph/cartesian_function_store.h index ad2e0a497..fb37e9639 100644 --- a/apps/graph/cartesian_function_store.h +++ b/apps/graph/cartesian_function_store.h @@ -23,10 +23,6 @@ public: static constexpr int k_maxNumberOfFunctions = 4; private: const char * firstAvailableName() override; - const KDColor firstAvailableColor() override; - static constexpr KDColor k_defaultColors[k_maxNumberOfFunctions] = { - Palette::Red, Palette::Blue, Palette::Green, Palette::YellowDark, - }; static constexpr const char * k_functionNames[k_maxNumberOfFunctions] = { "f", "g", "h", "p", }; diff --git a/apps/sequence/sequence_store.cpp b/apps/sequence/sequence_store.cpp index 6a51047bc..1ec18d06e 100644 --- a/apps/sequence/sequence_store.cpp +++ b/apps/sequence/sequence_store.cpp @@ -7,7 +7,6 @@ extern "C" { namespace Sequence { -constexpr KDColor SequenceStore::k_defaultColors[MaxNumberOfSequences]; constexpr const char * SequenceStore::k_sequenceNames[MaxNumberOfSequences]; uint32_t SequenceStore::storeChecksum() { @@ -82,22 +81,6 @@ const char * SequenceStore::firstAvailableName() { return k_sequenceNames[0]; } -const KDColor SequenceStore::firstAvailableColor() { - for (int k = 0; k < MaxNumberOfSequences; k++) { - int j = 0; - while (j < m_numberOfFunctions) { - if (m_sequences[j].color() == k_defaultColors[k]) { - break; - } - j++; - } - if (j == m_numberOfFunctions) { - return k_defaultColors[k]; - } - } - return k_defaultColors[0]; -} - void SequenceStore::removeAll() { for (int i = 0; i < m_numberOfFunctions; i++) { Sequence emptySequence("", KDColorBlack); diff --git a/apps/sequence/sequence_store.h b/apps/sequence/sequence_store.h index 423bff97d..5d14004f3 100644 --- a/apps/sequence/sequence_store.h +++ b/apps/sequence/sequence_store.h @@ -29,9 +29,6 @@ public: }; private: const KDColor firstAvailableColor() override; - static constexpr KDColor k_defaultColors[MaxNumberOfSequences] = { - Palette::Red, Palette::Blue//, Palette::YellowDark - }; Sequence m_sequences[MaxNumberOfSequences]; }; diff --git a/apps/shared/float_pair_store.h b/apps/shared/float_pair_store.h index 7b9c05238..41eda91e5 100644 --- a/apps/shared/float_pair_store.h +++ b/apps/shared/float_pair_store.h @@ -1,6 +1,8 @@ #ifndef SHARED_FLOAT_PAIR_STORE_H #define SHARED_FLOAT_PAIR_STORE_H +#include +#include #include #include @@ -33,6 +35,15 @@ public: void resetColumn(int series, int i); double sumOfColumn(int series, int i) const; uint32_t storeChecksum(); + + static KDColor colorOfSeriesAtIndex(int i) { + assert(i >= 0 && i < k_numberOfSeries); + return Palette::DataColor[i]; + } + static KDColor colorLightOfSeriesAtIndex(int i) { + assert(i >= 0 && i < k_numberOfSeries); + return Palette::DataColorLight[i]; + } protected: virtual double defaultValue(int series, int i, int j); int m_numberOfPairs[k_numberOfSeries]; diff --git a/apps/shared/function_store.cpp b/apps/shared/function_store.cpp index 0f7894076..e3a1f56d4 100644 --- a/apps/shared/function_store.cpp +++ b/apps/shared/function_store.cpp @@ -70,4 +70,20 @@ void FunctionStore::tidy() { } } +const KDColor FunctionStore::firstAvailableColor() { + for (int k = 0; k < maxNumberOfFunctions(); k++) { + int j = 0; + while (j < m_numberOfFunctions) { + if (functionAtIndex(j)->color() == Palette::DataColor[k]) { + break; + } + j++; + } + if (j == m_numberOfFunctions) { + return Palette::DataColor[k]; + } + } + return Palette::DataColor[0]; +} + } diff --git a/apps/shared/function_store.h b/apps/shared/function_store.h index 93d1c2397..f3cae3faf 100644 --- a/apps/shared/function_store.h +++ b/apps/shared/function_store.h @@ -28,10 +28,10 @@ public: virtual char symbol() const = 0; void tidy(); protected: + const KDColor firstAvailableColor(); int m_numberOfFunctions; private: virtual const char * firstAvailableName() = 0; - virtual const KDColor firstAvailableColor() = 0; }; } diff --git a/apps/statistics/calculation_controller.cpp b/apps/statistics/calculation_controller.cpp index 824417743..b9cb84ea4 100644 --- a/apps/statistics/calculation_controller.cpp +++ b/apps/statistics/calculation_controller.cpp @@ -53,10 +53,9 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int // Display a series title cell int seriesNumber = m_store->indexOfKthNonEmptySeries(i-1); char titleBuffer[] = {'V', static_cast('0'+seriesNumber), '/', 'N', static_cast('0'+seriesNumber), 0}; - KDColor colors[] = {Palette::Red, Palette::Blue, Palette::Green}; StoreTitleCell * storeTitleCell = static_cast(cell); storeTitleCell->setText(titleBuffer); - storeTitleCell->setColor(colors[seriesNumber]); + storeTitleCell->setColor(FloatPairStore::colorOfSeriesAtIndex(seriesNumber)); return; } if (i == 0) { diff --git a/apps/statistics/multiple_boxes_view.cpp b/apps/statistics/multiple_boxes_view.cpp index 26a27cd89..79a184946 100644 --- a/apps/statistics/multiple_boxes_view.cpp +++ b/apps/statistics/multiple_boxes_view.cpp @@ -7,10 +7,9 @@ namespace Statistics { MultipleBoxesView::MultipleBoxesView(BoxController * controller, Store * store, BoxView::Quantile * selectedQuantile) : MultipleDataView(store), - m_boxView1(controller, store, 0, nullptr, selectedQuantile, Palette::Red, Palette::RedLight), - m_boxView2(controller, store, 1, nullptr, selectedQuantile, Palette::Blue, Palette::BlueLight), - m_boxView3(controller, store, 2, nullptr, selectedQuantile, Palette::Green, Palette::GreenLight), - // TODO Share colors with stats/store_controller + m_boxView1(controller, store, 0, nullptr, selectedQuantile, FloatPairStore::colorOfSeriesAtIndex(0), FloatPairStore::colorLightOfSeriesAtIndex(0)), + m_boxView2(controller, store, 1, nullptr, selectedQuantile, FloatPairStore::colorOfSeriesAtIndex(1), FloatPairStore::colorLightOfSeriesAtIndex(0)), + m_boxView3(controller, store, 2, nullptr, selectedQuantile, FloatPairStore::colorOfSeriesAtIndex(2), FloatPairStore::colorLightOfSeriesAtIndex(0)), m_axisView(store), m_bannerView() { diff --git a/apps/statistics/multiple_histograms_view.cpp b/apps/statistics/multiple_histograms_view.cpp index 8c7ac869a..7d4126c03 100644 --- a/apps/statistics/multiple_histograms_view.cpp +++ b/apps/statistics/multiple_histograms_view.cpp @@ -7,10 +7,9 @@ namespace Statistics { MultipleHistogramsView::MultipleHistogramsView(HistogramController * controller, Store * store) : MultipleDataView(store), - m_histogramView1(controller, store, 0, nullptr, Palette::Red), - m_histogramView2(controller, store, 1, nullptr, Palette::Blue), - m_histogramView3(controller, store, 2, nullptr, Palette::Green), - // TODO Share colors with stats/store_controller + m_histogramView1(controller, store, 0, nullptr, FloatPairStore::colorOfSeriesAtIndex(0)), + m_histogramView2(controller, store, 1, nullptr, FloatPairStore::colorOfSeriesAtIndex(1)), + m_histogramView3(controller, store, 2, nullptr, FloatPairStore::colorOfSeriesAtIndex(2)), m_bannerView(), m_okView() { diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index ac9a15638..24026b514 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -33,8 +33,7 @@ void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int I18n::Message sizesMessages[] = {I18n::Message::Sizes1, I18n::Message::Sizes2, I18n::Message::Sizes3}; mytitleCell->setText(I18n::translate(sizesMessages[seriesIndex])); } - KDColor colors[] = {Palette::Red, Palette::Blue, Palette::Green}; - mytitleCell->setColor(m_store->numberOfPairsOfSeries(seriesIndex) == 0 ? Palette::GreyDark : colors[seriesIndex]); // TODO Share GreyDark and other colors with graph/list_controller + mytitleCell->setColor(m_store->numberOfPairsOfSeries(seriesIndex) == 0 ? Palette::GreyDark : Store::colorOfSeriesAtIndex(seriesIndex)); // TODO Share GreyDark with graph/list_controller } HighlightCell * StoreController::titleCells(int index) { diff --git a/escher/include/escher/palette.h b/escher/include/escher/palette.h index ab6457712..c78c6aea9 100644 --- a/escher/include/escher/palette.h +++ b/escher/include/escher/palette.h @@ -30,6 +30,8 @@ public: constexpr static KDColor Orange = KDColor::RGB24(0xfe871f); constexpr static KDColor Green = KDColor::RGB24(0x50c102); constexpr static KDColor GreenLight = KDColor::RGB24(0x52db8f); + constexpr static KDColor DataColor[] = {Red, Blue, Green, YellowDark}; + constexpr static KDColor DataColorLight[] = {RedLight, BlueLight, GreenLight, YellowLight}; }; #endif diff --git a/escher/src/palette.cpp b/escher/src/palette.cpp index 719638922..ba4b72151 100644 --- a/escher/src/palette.cpp +++ b/escher/src/palette.cpp @@ -25,3 +25,5 @@ constexpr KDColor Palette::BlueLight; constexpr KDColor Palette::Orange; constexpr KDColor Palette::Green; constexpr KDColor Palette::GreenLight; +constexpr KDColor Palette::DataColor[]; +constexpr KDColor Palette::DataColorLight[]; From 357d3fbad0d01995ade210526e489e60a20d113f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 14:18:04 +0200 Subject: [PATCH 084/156] [apps/shared/stats] Make store functions const --- apps/shared/float_pair_store.cpp | 4 +- apps/shared/float_pair_store.h | 4 +- apps/statistics/store.cpp | 62 +++++++++++++++---------------- apps/statistics/store.h | 64 ++++++++++++++++---------------- 4 files changed, 67 insertions(+), 67 deletions(-) diff --git a/apps/shared/float_pair_store.cpp b/apps/shared/float_pair_store.cpp index 4ca63c8a3..c58ceb812 100644 --- a/apps/shared/float_pair_store.cpp +++ b/apps/shared/float_pair_store.cpp @@ -73,7 +73,7 @@ double FloatPairStore::sumOfColumn(int series, int i) const { return result; } -uint32_t FloatPairStore::storeChecksum() { +uint32_t FloatPairStore::storeChecksum() const { /* Ideally, we would only compute the checksum of the first m_numberOfPairs * pairs. However, the two values of a pair are not stored consecutively. We * thus compute the checksum on all pairs and ensure to set the pair at 0 @@ -83,7 +83,7 @@ uint32_t FloatPairStore::storeChecksum() { return Ion::crc32((uint32_t *)m_data, dataLengthInBytes/sizeof(uint32_t)); } -double FloatPairStore::defaultValue(int series, int i, int j) { +double FloatPairStore::defaultValue(int series, int i, int j) const { assert(series >= 0 && series < k_numberOfSeries); if(i == 0 && j > 1) { return 2*m_data[series][i][j-1]-m_data[series][i][j-2]; diff --git a/apps/shared/float_pair_store.h b/apps/shared/float_pair_store.h index 41eda91e5..788623a6f 100644 --- a/apps/shared/float_pair_store.h +++ b/apps/shared/float_pair_store.h @@ -34,7 +34,7 @@ public: void deleteAllPairs(); void resetColumn(int series, int i); double sumOfColumn(int series, int i) const; - uint32_t storeChecksum(); + uint32_t storeChecksum() const; static KDColor colorOfSeriesAtIndex(int i) { assert(i >= 0 && i < k_numberOfSeries); @@ -45,7 +45,7 @@ public: return Palette::DataColorLight[i]; } protected: - virtual double defaultValue(int series, int i, int j); + virtual double defaultValue(int series, int i, int j) const; int m_numberOfPairs[k_numberOfSeries]; double m_data[k_numberOfSeries][k_numberOfColumnsPerSeries][k_maxNumberOfPairs]; }; diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 35704df36..869da49fe 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -17,7 +17,7 @@ Store::Store() : { } -uint32_t Store::barChecksum() { +uint32_t Store::barChecksum() const { double data[2] = {m_barWidth, m_firstDrawnBarAbscissa}; size_t dataLengthInBytes = 2*sizeof(double); assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4 @@ -32,11 +32,11 @@ void Store::setBarWidth(double barWidth) { } } -double Store::heightOfBarAtIndex(int series, int index) { +double Store::heightOfBarAtIndex(int series, int index) const { return sumOfValuesBetween(series, startOfBarAtIndex(series, index), endOfBarAtIndex(series, index)); } -double Store::heightOfBarAtValue(int series, double value) { +double Store::heightOfBarAtValue(int series, double value) const { double width = barWidth(); int barNumber = std::floor((value - m_firstDrawnBarAbscissa)/width); double lowerBound = m_firstDrawnBarAbscissa + barNumber*width; @@ -44,16 +44,16 @@ double Store::heightOfBarAtValue(int series, double value) { return sumOfValuesBetween(series, lowerBound, upperBound); } -double Store::startOfBarAtIndex(int series, int index) { +double Store::startOfBarAtIndex(int series, int index) const { double firstBarAbscissa = m_firstDrawnBarAbscissa + m_barWidth*std::floor((minValue(series)- m_firstDrawnBarAbscissa)/m_barWidth); return firstBarAbscissa + index * m_barWidth; } -double Store::endOfBarAtIndex(int series, int index) { +double Store::endOfBarAtIndex(int series, int index) const { return startOfBarAtIndex(series, index+1); } -double Store::numberOfBars(int series) { +double Store::numberOfBars(int series) const { double firstBarAbscissa = m_firstDrawnBarAbscissa + m_barWidth*std::floor((minValue(series)- m_firstDrawnBarAbscissa)/m_barWidth); return std::ceil((maxValue(series) - firstBarAbscissa)/m_barWidth)+1; } @@ -76,7 +76,7 @@ bool Store::scrollToSelectedBarIndex(int series, int index) { return false; } -bool Store::isEmpty() { +bool Store::isEmpty() const { for (int i = 0; i < k_numberOfSeries; i ++) { if (!seriesIsEmpty(i)) { return false; @@ -85,7 +85,7 @@ bool Store::isEmpty() { return true; } -int Store::numberOfNonEmptySeries() { +int Store::numberOfNonEmptySeries() const { int result = 0; for (int i = 0; i < k_numberOfSeries; i ++) { if (!seriesIsEmpty(i)) { @@ -95,11 +95,11 @@ int Store::numberOfNonEmptySeries() { return result; } -bool Store::seriesIsEmpty(int i) { +bool Store::seriesIsEmpty(int i) const { return sumOfOccurrences(i) == 0; } -int Store::indexOfKthNonEmptySeries(int k) { +int Store::indexOfKthNonEmptySeries(int k) const { assert(k >= 0 && k < numberOfNonEmptySeries()); int nonEmptySeriesCount = 0; for (int i = 0; i < k_numberOfSeries; i++) { @@ -116,11 +116,11 @@ int Store::indexOfKthNonEmptySeries(int k) { /* Calculation */ -double Store::sumOfOccurrences(int series) { +double Store::sumOfOccurrences(int series) const { return sumOfColumn(series, 1); } -double Store::maxValueForAllSeries() { +double Store::maxValueForAllSeries() const { assert(FloatPairStore::k_numberOfSeries > 0); double result = maxValue(0); for (int i = 1; i < FloatPairStore::k_numberOfSeries; i++) { @@ -132,7 +132,7 @@ double Store::maxValueForAllSeries() { return result; } -double Store::minValueForAllSeries() { +double Store::minValueForAllSeries() const { assert(FloatPairStore::k_numberOfSeries > 0); double result = minValue(0); for (int i = 1; i < FloatPairStore::k_numberOfSeries; i++) { @@ -144,7 +144,7 @@ double Store::minValueForAllSeries() { return result; } -double Store::maxValue(int series) { +double Store::maxValue(int series) const { double max = -DBL_MAX; for (int k = 0; k < m_numberOfPairs[series]; k++) { if (m_data[series][0][k] > max && m_data[series][1][k] > 0) { @@ -154,7 +154,7 @@ double Store::maxValue(int series) { return max; } -double Store::minValue(int series) { +double Store::minValue(int series) const { double min = DBL_MAX; for (int k = 0; k < m_numberOfPairs[series]; k++) { if (m_data[series][0][k] < min && m_data[series][1][k] > 0) { @@ -164,42 +164,42 @@ double Store::minValue(int series) { return min; } -double Store::range(int series) { +double Store::range(int series) const { return maxValue(series)-minValue(series); } -double Store::mean(int series) { +double Store::mean(int series) const { return sum(series)/sumOfOccurrences(series); } -double Store::variance(int series) { +double Store::variance(int series) const { double m = mean(series); return squaredValueSum(series)/sumOfOccurrences(series) - m*m; } -double Store::standardDeviation(int series) { +double Store::standardDeviation(int series) const { return std::sqrt(variance(series)); } -double Store::sampleStandardDeviation(int series) { +double Store::sampleStandardDeviation(int series) const { double n = sumOfOccurrences(series); double s = std::sqrt(n/(n-1.0)); return s*standardDeviation(series); } -double Store::firstQuartile(int series) { +double Store::firstQuartile(int series) const { return sortedElementAtCumulatedFrequency(series, 1.0/4.0); } -double Store::thirdQuartile(int series) { +double Store::thirdQuartile(int series) const { return sortedElementAtCumulatedFrequency(series, 3.0/4.0); } -double Store::quartileRange(int series) { +double Store::quartileRange(int series) const { return thirdQuartile(series)-firstQuartile(series); } -double Store::median(int series) { +double Store::median(int series) const { bool exactElement = true; double maxMedian = sortedElementAtCumulatedFrequency(series, 1.0/2.0, &exactElement); if (!exactElement) { @@ -210,7 +210,7 @@ double Store::median(int series) { } } -double Store::sum(int series) { +double Store::sum(int series) const { double result = 0; for (int k = 0; k < m_numberOfPairs[series]; k++) { result += m_data[series][0][k]*m_data[series][1][k]; @@ -218,7 +218,7 @@ double Store::sum(int series) { return result; } -double Store::squaredValueSum(int series) { +double Store::squaredValueSum(int series) const { double result = 0; 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]; @@ -228,11 +228,11 @@ double Store::squaredValueSum(int series) { /* Private methods */ -double Store::defaultValue(int series, int i, int j) { +double Store::defaultValue(int series, int i, int j) const { return i == 0 ? FloatPairStore::defaultValue(series, i, j) : 1.0; } -double Store::sumOfValuesBetween(int series, double x1, double x2) { +double Store::sumOfValuesBetween(int series, double x1, double x2) const { 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]) { @@ -242,7 +242,7 @@ double Store::sumOfValuesBetween(int series, double x1, double x2) { return result; } -double Store::sortedElementAtCumulatedFrequency(int series, double k, bool * exactElement) { +double Store::sortedElementAtCumulatedFrequency(int series, double k, bool * exactElement) const { // TODO: use an other algorithm (ex quickselect) to avoid quadratic complexity assert(k >= 0.0 && k <= 1.0); double totalNumberOfElements = sumOfOccurrences(series); @@ -261,7 +261,7 @@ double Store::sortedElementAtCumulatedFrequency(int series, double k, bool * exa return m_data[series][0][sortedElementIndex]; } -double Store::sortedElementAfter(int series, double k) { +double Store::sortedElementAfter(int series, double k) const { assert(m_numberOfPairs[series] > 0); double result = DBL_MAX; bool foundResult = false; @@ -276,7 +276,7 @@ double Store::sortedElementAfter(int series, double k) { return result; } -int Store::minIndex(double * bufferValues, int bufferLength) { +int Store::minIndex(double * bufferValues, int bufferLength) const { int index = 0; for (int i = 1; i < bufferLength; i++) { if (bufferValues[index] > bufferValues[i]) { diff --git a/apps/statistics/store.h b/apps/statistics/store.h index 94b389f00..292d270bd 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -9,41 +9,41 @@ namespace Statistics { class Store : public Shared::MemoizedCurveViewRange, public Shared::FloatPairStore { public: Store(); - uint32_t barChecksum(); + uint32_t barChecksum() const; // Histogram bars double barWidth() const { return m_barWidth; } void setBarWidth(double barWidth); double firstDrawnBarAbscissa() const { return m_firstDrawnBarAbscissa; } 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); - double endOfBarAtIndex(int series, int index); - double numberOfBars(int series); + double heightOfBarAtIndex(int series, int index) const; + double heightOfBarAtValue(int series, double value) const; + double startOfBarAtIndex(int series, int index) const; + double endOfBarAtIndex(int series, int index) const; + double numberOfBars(int series) const; // return true if the window has scrolled bool scrollToSelectedBarIndex(int series, int index); - bool isEmpty(); - int numberOfNonEmptySeries(); - bool seriesIsEmpty(int i); - int indexOfKthNonEmptySeries(int k); + bool isEmpty() const; + int numberOfNonEmptySeries() const; + bool seriesIsEmpty(int i) const; + int indexOfKthNonEmptySeries(int k) const; // Calculation - double sumOfOccurrences(int series); - double maxValueForAllSeries(); - double minValueForAllSeries(); - double maxValue(int series); - double minValue(int series); - double range(int series); - double mean(int series); - double variance(int series); - double standardDeviation(int series); - double sampleStandardDeviation(int series); - double firstQuartile(int series); - double thirdQuartile(int series); - double quartileRange(int series); - double median(int series); - double sum(int series); - double squaredValueSum(int series); + double sumOfOccurrences(int series) const; + double maxValueForAllSeries() const; + double minValueForAllSeries() const; + double maxValue(int series) const; + double minValue(int series) const; + double range(int series) const; + double mean(int series) const; + double variance(int series) const; + double standardDeviation(int series) const; + double sampleStandardDeviation(int series) const; + double firstQuartile(int series) const; + double thirdQuartile(int series) const; + double quartileRange(int series) const; + double median(int series) const; + double sum(int series) const; + double squaredValueSum(int series) const; constexpr static double k_maxNumberOfBars = 10000.0; constexpr static float k_displayTopMarginRatio = 0.1f; constexpr static float k_displayRightMarginRatio = 0.04f; @@ -51,17 +51,17 @@ public: constexpr static float k_displayLeftMarginRatio = 0.04f; private: - double defaultValue(int series, int i, int j) override; - double sumOfValuesBetween(int series, double x1, double x2); - double sortedElementAtCumulatedFrequency(int series, double k, bool * exactElement = nullptr); - double sortedElementAfter(int series, double k); - int minIndex(double * bufferValues, int bufferLength); + double defaultValue(int series, int i, int j) const override; + double sumOfValuesBetween(int series, double x1, double x2) const; + double sortedElementAtCumulatedFrequency(int series, double k, bool * exactElement = nullptr) const; + double sortedElementAfter(int series, double k) const; + int minIndex(double * bufferValues, int bufferLength) const; // Histogram bars double m_barWidth; double m_firstDrawnBarAbscissa; }; -typedef double (Store::*CalculPointer)(int); +typedef double (Store::*CalculPointer)(int) const; } From 02c81e76fb13c2c69427e32a3cc1b1345cc72ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 14:19:47 +0200 Subject: [PATCH 085/156] [apps/stats] Remove compilation warning about non used value --- apps/statistics/store.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 869da49fe..982ba470f 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -264,15 +264,13 @@ double Store::sortedElementAtCumulatedFrequency(int series, double k, bool * exa double Store::sortedElementAfter(int series, double k) const { assert(m_numberOfPairs[series] > 0); double result = DBL_MAX; - bool foundResult = false; for (int i = 0; i < m_numberOfPairs[series]; i++) { double currentElement = m_data[series][0][i]; if (currentElement > k && currentElement < result) { result = currentElement; - foundResult = true; } } - assert(foundResult); + assert(result < DBL_MAX); return result; } From e0518ecaf9dfe6a4d6f2cf32a825a3c9aeb14455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 14:42:47 +0200 Subject: [PATCH 086/156] [apps/stats] Remove histogram blinking --- apps/statistics/histogram_view.cpp | 15 +++++++++++++-- apps/statistics/histogram_view.h | 1 + apps/statistics/multiple_data_view_controller.cpp | 1 - 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/statistics/histogram_view.cpp b/apps/statistics/histogram_view.cpp index 1bc1792fc..b48faf249 100644 --- a/apps/statistics/histogram_view.cpp +++ b/apps/statistics/histogram_view.cpp @@ -30,6 +30,17 @@ void HistogramView::reload() { markRectAsDirty(dirtyZone); } +void HistogramView::reloadSelectedBar() { + CurveView::reload(); + float pixelLowerBound = floatToPixel(Axis::Horizontal, m_highlightedBarStart)-2; + float pixelUpperBound = floatToPixel(Axis::Horizontal, m_highlightedBarEnd)+2; + /* We deliberately do not mark as dirty the frame of the banner view to avoid + *unpleasant blinking of the drawing of the banner view. */ + KDRect dirtyZone(KDRect(pixelLowerBound, 0, pixelUpperBound-pixelLowerBound, + bounds().height() - (displayBannerView() ? m_bannerView->bounds().height() : 0))); + markRectAsDirty(dirtyZone); +} + void HistogramView::drawRect(KDContext * ctx, KDRect rect) const { m_controller->setCurrentDrawnSeries(m_series); ctx->fillRect(rect, KDColorWhite); @@ -48,10 +59,10 @@ void HistogramView::drawRect(KDContext * ctx, KDRect rect) const { void HistogramView::setHighlight(float start, float end) { if (m_highlightedBarStart != start || m_highlightedBarEnd != end) { - reload(); + reloadSelectedBar(); m_highlightedBarStart = start; m_highlightedBarEnd = end; - reload(); + reloadSelectedBar(); } } diff --git a/apps/statistics/histogram_view.h b/apps/statistics/histogram_view.h index 91834219f..f871135db 100644 --- a/apps/statistics/histogram_view.h +++ b/apps/statistics/histogram_view.h @@ -15,6 +15,7 @@ public: HistogramView(HistogramController * controller, Store * store, int series, Shared::BannerView * bannerView, KDColor selectedHistogramColor = Palette::Select, KDColor notSelectedHistogramColor = Palette::GreyMiddle, KDColor selectedBarColor = Palette::YellowDark); int series() const { return m_series; } void reload() override; + void reloadSelectedBar(); void drawRect(KDContext * ctx, KDRect rect) const override; void setHighlight(float start, float end); void setDisplayLabels(bool display) { m_displayLabels = display; } diff --git a/apps/statistics/multiple_data_view_controller.cpp b/apps/statistics/multiple_data_view_controller.cpp index ac186d859..4f527979a 100644 --- a/apps/statistics/multiple_data_view_controller.cpp +++ b/apps/statistics/multiple_data_view_controller.cpp @@ -72,7 +72,6 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) { int direction = event == Ion::Events::Left ? -1 : 1; if (moveSelectionHorizontally(direction)) { reloadBannerView(); - multipleDataView()->reload(); } return true; } From ed7a29084c8357bd8123b3c31473659978299f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 14:48:39 +0200 Subject: [PATCH 087/156] [apps/stats] Remove histogram blinkng --- apps/statistics/multiple_data_view.cpp | 1 + apps/statistics/multiple_data_view_controller.cpp | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/statistics/multiple_data_view.cpp b/apps/statistics/multiple_data_view.cpp index 8eef9f9cc..2f0203ca9 100644 --- a/apps/statistics/multiple_data_view.cpp +++ b/apps/statistics/multiple_data_view.cpp @@ -86,6 +86,7 @@ void MultipleDataView::layoutDataSubviews() { void MultipleDataView::changeDataViewSelection(int index, bool select) { dataViewAtIndex(index)->selectMainView(select); + dataViewAtIndex(index)->reload(); } KDRect MultipleDataView::bannerFrame() const { diff --git a/apps/statistics/multiple_data_view_controller.cpp b/apps/statistics/multiple_data_view_controller.cpp index 4f527979a..8823bf457 100644 --- a/apps/statistics/multiple_data_view_controller.cpp +++ b/apps/statistics/multiple_data_view_controller.cpp @@ -46,7 +46,6 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) { *m_selectedBarIndex = 0; multipleDataView()->selectDataView(m_selectedSeries); reloadBannerView(); - multipleDataView()->reload(); app()->setFirstResponder(this); return true; } @@ -65,7 +64,6 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) { app()->setFirstResponder(tabController()); } reloadBannerView(); - multipleDataView()->reload(); return true; } if (m_selectedSeries >= 0 && (event == Ion::Events::Left || event == Ion::Events::Right)) { @@ -97,7 +95,6 @@ void MultipleDataViewController::willExitResponderChain(Responder * nextFirstRes m_selectedSeries = -1; multipleDataView()->setDisplayBanner(false); } - multipleDataView()->reload(); } } From c31527c54edeb51a64678b18e3b260d94201cc07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 15:24:51 +0200 Subject: [PATCH 088/156] [apps/stats] Fix histogram horizontal scrolling --- apps/statistics/histogram_controller.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index baacb40d4..7babaa9df 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -169,7 +169,9 @@ bool HistogramController::moveSelectionHorizontally(int deltaIndex) { { *m_selectedBarIndex = newSelectedBarIndex; m_view.dataViewAtIndex(selectedSeries())->setHighlight(m_store->startOfBarAtIndex(selectedSeries(), *m_selectedBarIndex), m_store->endOfBarAtIndex(selectedSeries(), *m_selectedBarIndex)); - m_store->scrollToSelectedBarIndex(selectedSeries(), *m_selectedBarIndex); + if (m_store->scrollToSelectedBarIndex(selectedSeries(), *m_selectedBarIndex)) { + multipleDataView()->reload(); + } return true; } return false; From e025cb30d53411482279bcc7b73da900665fc969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 15:41:55 +0200 Subject: [PATCH 089/156] [apps/stats] Clean code --- apps/statistics/multiple_data_view_controller.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/statistics/multiple_data_view_controller.cpp b/apps/statistics/multiple_data_view_controller.cpp index 8823bf457..3859ae295 100644 --- a/apps/statistics/multiple_data_view_controller.cpp +++ b/apps/statistics/multiple_data_view_controller.cpp @@ -55,10 +55,9 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) { int currentSelectedSubview = multipleDataView()->indexOfSubviewAtSeries(m_selectedSeries); if (currentSelectedSubview > 0) { multipleDataView()->deselectDataView(m_selectedSeries); - assert(currentSelectedSubview > 0); m_selectedSeries = multipleDataView()->seriesOfSubviewAtIndex(currentSelectedSubview-1); - multipleDataView()->selectDataView(m_selectedSeries); *m_selectedBarIndex = 0; + multipleDataView()->selectDataView(m_selectedSeries); app()->setFirstResponder(this); } else { app()->setFirstResponder(tabController()); From 2c1d2b402b764dfbc5d0ef59c169810a46ce7902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 16:08:06 +0200 Subject: [PATCH 090/156] [app/stats] Prevent histogram blinking --- apps/statistics/multiple_data_view.h | 1 + apps/statistics/multiple_data_view_controller.cpp | 4 ++-- apps/statistics/multiple_histograms_view.cpp | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/statistics/multiple_data_view.h b/apps/statistics/multiple_data_view.h index 5937566ee..ec2c2c4e5 100644 --- a/apps/statistics/multiple_data_view.h +++ b/apps/statistics/multiple_data_view.h @@ -10,6 +10,7 @@ namespace Statistics { class MultipleDataView : public View { public: + static constexpr int k_defaultSelectedBar = 0; MultipleDataView(Store * store) : m_store(store), m_displayBanner(false) diff --git a/apps/statistics/multiple_data_view_controller.cpp b/apps/statistics/multiple_data_view_controller.cpp index 3859ae295..6e0cc0f3f 100644 --- a/apps/statistics/multiple_data_view_controller.cpp +++ b/apps/statistics/multiple_data_view_controller.cpp @@ -43,7 +43,7 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) { if (currentSelectedSubview < m_store->numberOfNonEmptySeries() - 1) { multipleDataView()->deselectDataView(m_selectedSeries); m_selectedSeries = multipleDataView()->seriesOfSubviewAtIndex(currentSelectedSubview+1); - *m_selectedBarIndex = 0; + *m_selectedBarIndex = MultipleDataView::k_defaultSelectedBar; multipleDataView()->selectDataView(m_selectedSeries); reloadBannerView(); app()->setFirstResponder(this); @@ -56,7 +56,7 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) { if (currentSelectedSubview > 0) { multipleDataView()->deselectDataView(m_selectedSeries); m_selectedSeries = multipleDataView()->seriesOfSubviewAtIndex(currentSelectedSubview-1); - *m_selectedBarIndex = 0; + *m_selectedBarIndex = MultipleDataView::k_defaultSelectedBar; multipleDataView()->selectDataView(m_selectedSeries); app()->setFirstResponder(this); } else { diff --git a/apps/statistics/multiple_histograms_view.cpp b/apps/statistics/multiple_histograms_view.cpp index 7d4126c03..5acfc9a74 100644 --- a/apps/statistics/multiple_histograms_view.cpp +++ b/apps/statistics/multiple_histograms_view.cpp @@ -48,6 +48,10 @@ void MultipleHistogramsView::layoutSubviews() { void MultipleHistogramsView::changeDataViewSelection(int index, bool select) { MultipleDataView::changeDataViewSelection(index, select); dataViewAtIndex(index)->setDisplayLabels(select); + if (select == false) { + // Set the hightlight to default selected bar to prevent blinking + dataViewAtIndex(index)->setHighlight(m_store->startOfBarAtIndex(index, k_defaultSelectedBar), m_store->endOfBarAtIndex(index, k_defaultSelectedBar)); + } } } From e1b39b6f3a8eac007119174e0020f617aeff0eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 16:48:08 +0200 Subject: [PATCH 091/156] [apps/stats] Fix box drawing and dirtying --- apps/statistics/box_view.cpp | 26 ++++++++++++++++++++----- apps/statistics/box_view.h | 2 ++ apps/statistics/multiple_boxes_view.cpp | 4 ++-- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/apps/statistics/box_view.cpp b/apps/statistics/box_view.cpp index 3338ec631..c43212fa3 100644 --- a/apps/statistics/box_view.cpp +++ b/apps/statistics/box_view.cpp @@ -24,16 +24,32 @@ bool BoxView::selectQuantile(int selectedQuantile) { return false; } if ((int)*m_selectedQuantile != selectedQuantile) { - reload(); + reloadQuantile(); *m_selectedQuantile = (Quantile)selectedQuantile; - reload(); + reloadQuantile(); } return true; } +void BoxView::reloadQuantile() { + CurveView::reload(); + KDCoordinate minY = boxLowerBoundPixel(); + KDCoordinate maxY = boxUpperBoundPixel(); + CalculPointer calculationMethods[5] = {&Store::minValue, &Store::firstQuartile, &Store::median, &Store::thirdQuartile, &Store::maxValue}; + double calculation = (m_store->*calculationMethods[(int)*m_selectedQuantile])(m_series); + KDCoordinate minX = std::round(floatToPixel(Axis::Horizontal, calculation)); + KDRect dirtyRect = KDRect(minX, minY, k_quantileBarWidth, maxY - minY); + markRectAsDirty(dirtyRect); +} + void BoxView::reload() { CurveView::reload(); - markRectAsDirty(bounds()); + KDCoordinate minY = boxLowerBoundPixel(); + KDCoordinate maxY = boxUpperBoundPixel(); + KDCoordinate minX = std::round(floatToPixel(Axis::Horizontal, m_store->minValue(m_series))); + KDCoordinate maxX = std::round(floatToPixel(Axis::Horizontal, m_store->maxValue(m_series))); + KDRect dirtyRect = KDRect(minX, minY, maxX - minX + k_quantileBarWidth, maxY - minY + k_quantileBarWidth); + markRectAsDirty(dirtyRect); } void BoxView::drawRect(KDContext * ctx, KDRect rect) const { @@ -69,10 +85,10 @@ void BoxView::drawRect(KDContext * ctx, KDRect rect) const { * lines. This solution could hide the highlighted line by coloring the next * quantile if it has the same value. */ for (int k = 0; k < 5; k++) { - drawSegment(ctx, rect, Axis::Vertical, calculations[k], lowBound, upBound, Palette::GreyMiddle, 2); + drawSegment(ctx, rect, Axis::Vertical, calculations[k], lowBound, upBound, Palette::GreyMiddle, k_quantileBarWidth); } if (isMainViewSelected()) { - drawSegment(ctx, rect, Axis::Vertical, calculations[(int)*m_selectedQuantile], lowBound, upBound, Palette::YellowDark, 2); + drawSegment(ctx, rect, Axis::Vertical, calculations[(int)*m_selectedQuantile], lowBound, upBound, Palette::YellowDark, k_quantileBarWidth); } } diff --git a/apps/statistics/box_view.h b/apps/statistics/box_view.h index 43ce455d6..ab4b87b34 100644 --- a/apps/statistics/box_view.h +++ b/apps/statistics/box_view.h @@ -25,6 +25,7 @@ public: Quantile selectedQuantile() const { return *m_selectedQuantile; } bool selectQuantile(int selectedQuantile); int series() const { return m_series; } + void reloadQuantile(); // CurveView void reload() override; @@ -33,6 +34,7 @@ public: void drawRect(KDContext * ctx, KDRect rect) const override; private: static constexpr KDCoordinate k_boxHeight = 40; + static constexpr KDCoordinate k_quantileBarWidth = 2; KDCoordinate boxLowerBoundPixel() const; KDCoordinate boxUpperBoundPixel() const; char * label(Axis axis, int index) const override { return nullptr; } diff --git a/apps/statistics/multiple_boxes_view.cpp b/apps/statistics/multiple_boxes_view.cpp index 79a184946..edb29502d 100644 --- a/apps/statistics/multiple_boxes_view.cpp +++ b/apps/statistics/multiple_boxes_view.cpp @@ -8,8 +8,8 @@ namespace Statistics { MultipleBoxesView::MultipleBoxesView(BoxController * controller, Store * store, BoxView::Quantile * selectedQuantile) : MultipleDataView(store), m_boxView1(controller, store, 0, nullptr, selectedQuantile, FloatPairStore::colorOfSeriesAtIndex(0), FloatPairStore::colorLightOfSeriesAtIndex(0)), - m_boxView2(controller, store, 1, nullptr, selectedQuantile, FloatPairStore::colorOfSeriesAtIndex(1), FloatPairStore::colorLightOfSeriesAtIndex(0)), - m_boxView3(controller, store, 2, nullptr, selectedQuantile, FloatPairStore::colorOfSeriesAtIndex(2), FloatPairStore::colorLightOfSeriesAtIndex(0)), + m_boxView2(controller, store, 1, nullptr, selectedQuantile, FloatPairStore::colorOfSeriesAtIndex(1), FloatPairStore::colorLightOfSeriesAtIndex(1)), + m_boxView3(controller, store, 2, nullptr, selectedQuantile, FloatPairStore::colorOfSeriesAtIndex(2), FloatPairStore::colorLightOfSeriesAtIndex(2)), m_axisView(store), m_bannerView() { From e295dd678e64ac3338b77ff824452b09190b0491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 17:30:58 +0200 Subject: [PATCH 092/156] [apps/stats] Fix drawin and reloading --- apps/statistics/box_view.cpp | 2 +- apps/statistics/histogram_controller.cpp | 1 + apps/statistics/multiple_data_view_controller.cpp | 4 +--- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/statistics/box_view.cpp b/apps/statistics/box_view.cpp index c43212fa3..a81a4812c 100644 --- a/apps/statistics/box_view.cpp +++ b/apps/statistics/box_view.cpp @@ -48,7 +48,7 @@ void BoxView::reload() { KDCoordinate maxY = boxUpperBoundPixel(); KDCoordinate minX = std::round(floatToPixel(Axis::Horizontal, m_store->minValue(m_series))); KDCoordinate maxX = std::round(floatToPixel(Axis::Horizontal, m_store->maxValue(m_series))); - KDRect dirtyRect = KDRect(minX, minY, maxX - minX + k_quantileBarWidth, maxY - minY + k_quantileBarWidth); + KDRect dirtyRect = KDRect(minX, minY, maxX - minX + k_quantileBarWidth, maxY - minY); markRectAsDirty(dirtyRect); } diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 7babaa9df..185788d23 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -172,6 +172,7 @@ bool HistogramController::moveSelectionHorizontally(int deltaIndex) { if (m_store->scrollToSelectedBarIndex(selectedSeries(), *m_selectedBarIndex)) { multipleDataView()->reload(); } + reloadBannerView(); return true; } return false; diff --git a/apps/statistics/multiple_data_view_controller.cpp b/apps/statistics/multiple_data_view_controller.cpp index 6e0cc0f3f..df5b06b1f 100644 --- a/apps/statistics/multiple_data_view_controller.cpp +++ b/apps/statistics/multiple_data_view_controller.cpp @@ -67,9 +67,7 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) { } if (m_selectedSeries >= 0 && (event == Ion::Events::Left || event == Ion::Events::Right)) { int direction = event == Ion::Events::Left ? -1 : 1; - if (moveSelectionHorizontally(direction)) { - reloadBannerView(); - } + moveSelectionHorizontally(direction); return true; } return false; From b5032130379f3c099e50571eb01f8b0af281fdf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 17:59:53 +0200 Subject: [PATCH 093/156] [apps/stats] Memoize emptiness of series --- apps/shared/float_pair_store.h | 6 +++--- apps/statistics/store.cpp | 22 ++++++++++++++++++++-- apps/statistics/store.h | 6 ++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/apps/shared/float_pair_store.h b/apps/shared/float_pair_store.h index 788623a6f..3e6ae76ca 100644 --- a/apps/shared/float_pair_store.h +++ b/apps/shared/float_pair_store.h @@ -23,14 +23,14 @@ public: assert(j < m_numberOfPairs[series]); return m_data[series][i][j]; } - void set(double f, int series, int i, int j); + virtual void set(double f, int series, int i, int j); int numberOfPairs() const; int numberOfPairsOfSeries(int series) const { assert(series >= 0 && series < k_numberOfSeries); return m_numberOfPairs[series]; } - void deletePairOfSeriesAtIndex(int series, int j); - void deleteAllPairsOfSeries(int series); + virtual void deletePairOfSeriesAtIndex(int series, int j); + virtual void deleteAllPairsOfSeries(int series); void deleteAllPairs(); void resetColumn(int series, int i); double sumOfColumn(int series, int i) const; diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 982ba470f..1096b4e62 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -9,11 +9,14 @@ using namespace Shared; namespace Statistics { +static_assert(Store::k_numberOfSeries == 3, "The constructor of Statistics::Store should be changed"); + Store::Store() : MemoizedCurveViewRange(), FloatPairStore(), m_barWidth(1.0), - m_firstDrawnBarAbscissa(0.0) + m_firstDrawnBarAbscissa(0.0), + m_seriesEmpty{true, true, true} { } @@ -96,7 +99,7 @@ int Store::numberOfNonEmptySeries() const { } bool Store::seriesIsEmpty(int i) const { - return sumOfOccurrences(i) == 0; + return m_seriesEmpty[i]; } int Store::indexOfKthNonEmptySeries(int k) const { @@ -226,6 +229,21 @@ double Store::squaredValueSum(int series) const { return result; } +void Store::set(double f, int series, int i, int j) { + FloatPairStore::set(f, series, i, j); + m_seriesEmpty[series] = sumOfOccurrences(series) == 0; +} + +void Store::deletePairOfSeriesAtIndex(int series, int j) { + FloatPairStore::deletePairOfSeriesAtIndex(series, j); + m_seriesEmpty[series] = sumOfOccurrences(series) == 0; +} + +void Store::deleteAllPairsOfSeries(int series) { + FloatPairStore::deleteAllPairsOfSeries(series); + m_seriesEmpty[series] = true; +} + /* Private methods */ double Store::defaultValue(int series, int i, int j) const { diff --git a/apps/statistics/store.h b/apps/statistics/store.h index 292d270bd..31a43dd40 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -50,6 +50,11 @@ public: constexpr static int k_bottomMargin = 20; constexpr static float k_displayLeftMarginRatio = 0.04f; + // FloatPairStore + void set(double f, int series, int i, int j) override; + void deletePairOfSeriesAtIndex(int series, int j) override; + void deleteAllPairsOfSeries(int series) override; + private: double defaultValue(int series, int i, int j) const override; double sumOfValuesBetween(int series, double x1, double x2) const; @@ -59,6 +64,7 @@ private: // Histogram bars double m_barWidth; double m_firstDrawnBarAbscissa; + bool m_seriesEmpty[k_numberOfSeries]; }; typedef double (Store::*CalculPointer)(int) const; From cf03d7e7ac138e62ea0c7d729c5a026786bba4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 18:23:39 +0200 Subject: [PATCH 094/156] [apps/stats] Memoize number of non empty series --- apps/statistics/store.cpp | 24 ++++++++++++++++-------- apps/statistics/store.h | 3 +++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 1096b4e62..4ec861498 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -16,7 +16,8 @@ Store::Store() : FloatPairStore(), m_barWidth(1.0), m_firstDrawnBarAbscissa(0.0), - m_seriesEmpty{true, true, true} + m_seriesEmpty{true, true, true}, + m_numberOfNonEmptySeries(0) { } @@ -89,13 +90,7 @@ bool Store::isEmpty() const { } int Store::numberOfNonEmptySeries() const { - int result = 0; - for (int i = 0; i < k_numberOfSeries; i ++) { - if (!seriesIsEmpty(i)) { - result++; - } - } - return result; + return m_numberOfNonEmptySeries; } bool Store::seriesIsEmpty(int i) const { @@ -232,16 +227,29 @@ double Store::squaredValueSum(int series) const { void Store::set(double f, int series, int i, int j) { FloatPairStore::set(f, series, i, j); m_seriesEmpty[series] = sumOfOccurrences(series) == 0; + updateNonEmptySeriesCount(); } void Store::deletePairOfSeriesAtIndex(int series, int j) { FloatPairStore::deletePairOfSeriesAtIndex(series, j); m_seriesEmpty[series] = sumOfOccurrences(series) == 0; + updateNonEmptySeriesCount(); } void Store::deleteAllPairsOfSeries(int series) { FloatPairStore::deleteAllPairsOfSeries(series); m_seriesEmpty[series] = true; + updateNonEmptySeriesCount(); +} + +void Store::updateNonEmptySeriesCount() { + int nonEmptySeriesCount = 0; + for (int i = 0; i< k_numberOfSeries; i++) { + if (!m_seriesEmpty[i]) { + nonEmptySeriesCount++; + } + } + m_numberOfNonEmptySeries = nonEmptySeriesCount; } /* Private methods */ diff --git a/apps/statistics/store.h b/apps/statistics/store.h index 31a43dd40..99381d46d 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -55,6 +55,8 @@ public: void deletePairOfSeriesAtIndex(int series, int j) override; void deleteAllPairsOfSeries(int series) override; + void updateNonEmptySeriesCount(); + private: double defaultValue(int series, int i, int j) const override; double sumOfValuesBetween(int series, double x1, double x2) const; @@ -65,6 +67,7 @@ private: double m_barWidth; double m_firstDrawnBarAbscissa; bool m_seriesEmpty[k_numberOfSeries]; + int m_numberOfNonEmptySeries; }; typedef double (Store::*CalculPointer)(int) const; From 1e7069f00e3f68220297cf54c2865b2ceed3f1e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 10:00:51 +0200 Subject: [PATCH 095/156] [apps/stats] Fix margins and column widths --- apps/shared/store_cell.cpp | 2 +- apps/shared/store_cell.h | 1 + apps/shared/store_controller.h | 4 ++-- apps/statistics/Makefile | 1 + apps/statistics/box_controller.cpp | 8 +++++--- apps/statistics/calculation_cell.cpp | 2 +- apps/statistics/calculation_cell.h | 2 ++ apps/statistics/calculation_controller.cpp | 6 +++--- apps/statistics/calculation_controller.h | 5 +++-- apps/statistics/calculation_title_cell.cpp | 10 ++++++++++ apps/statistics/calculation_title_cell.h | 20 ++++++++++++++++++++ 11 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 apps/statistics/calculation_title_cell.cpp create mode 100644 apps/statistics/calculation_title_cell.h diff --git a/apps/shared/store_cell.cpp b/apps/shared/store_cell.cpp index cee473f52..57a62bbdf 100644 --- a/apps/shared/store_cell.cpp +++ b/apps/shared/store_cell.cpp @@ -21,7 +21,7 @@ void StoreCell::drawRect(KDContext * ctx, KDRect rect) const { void StoreCell::layoutSubviews() { KDRect boundsThis = bounds(); - editableTextCell()->setFrame(KDRect(boundsThis.left() + Metric::TableSeparatorThickness, boundsThis.top(), boundsThis.width() - Metric::TableSeparatorThickness, boundsThis.height())); + editableTextCell()->setFrame(KDRect(boundsThis.left() + Metric::TableSeparatorThickness, boundsThis.top(), boundsThis.width() - Metric::TableSeparatorThickness - k_rightMargin, boundsThis.height())); } } diff --git a/apps/shared/store_cell.h b/apps/shared/store_cell.h index cecf7dc55..9e0f70064 100644 --- a/apps/shared/store_cell.h +++ b/apps/shared/store_cell.h @@ -15,6 +15,7 @@ public: void drawRect(KDContext * ctx, KDRect rect) const override; void layoutSubviews() override; private: + static constexpr KDCoordinate k_rightMargin = 2; bool m_separatorLeft; }; diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index d6d7342ef..59ed0bea8 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -34,11 +34,11 @@ public: void didBecomeFirstResponder() override; protected: - static constexpr KDCoordinate k_cellWidth = 80; //TODO + static constexpr KDCoordinate k_cellWidth = 116; static constexpr KDCoordinate k_margin = 8; static constexpr KDCoordinate k_scrollBarMargin = Metric::CommonRightMargin; constexpr static int k_maxNumberOfEditableCells = 22 * FloatPairStore::k_numberOfSeries; - constexpr static int k_numberOfTitleCells = FloatPairStore::k_numberOfColumnsPerSeries * FloatPairStore::k_numberOfSeries; // TODO Put finer number of cells + constexpr static int k_numberOfTitleCells = 4; static constexpr int k_titleCellType = 0; static constexpr int k_editableCellType = 1; Responder * tabController() const override; diff --git a/apps/statistics/Makefile b/apps/statistics/Makefile index 24cc6b475..f1d5a02b3 100644 --- a/apps/statistics/Makefile +++ b/apps/statistics/Makefile @@ -11,6 +11,7 @@ app_objs += $(addprefix apps/statistics/,\ calculation_cell.o\ calculation_controller.o\ calculation_selectable_table_view.o\ + calculation_title_cell.o\ histogram_banner_view.o\ histogram_controller.o\ histogram_parameter_controller.o\ diff --git a/apps/statistics/box_controller.cpp b/apps/statistics/box_controller.cpp index 3e34c0838..29d0b55e0 100644 --- a/apps/statistics/box_controller.cpp +++ b/apps/statistics/box_controller.cpp @@ -40,7 +40,7 @@ void BoxController::reloadBannerView() { // Set series name char seriesChar = '0' + selectedSeries() + 1; - char bufferName[] = {'V', seriesChar, '/', 'N', seriesChar, 0}; + char bufferName[] = {' ', 'V', seriesChar, '/', 'N', seriesChar, 0}; m_view.editableBannerView()->setLegendAtIndex(bufferName, 0); @@ -49,11 +49,13 @@ void BoxController::reloadBannerView() { m_view.editableBannerView()->setMessageAtIndex(calculationName[selectedQuantile], 1); // Set calculation result - char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; + char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits) + 1]; CalculPointer calculationMethods[5] = {&Store::minValue, &Store::firstQuartile, &Store::median, &Store::thirdQuartile, &Store::maxValue}; double calculation = (m_store->*calculationMethods[selectedQuantile])(selectedSeries()); - PrintFloat::convertFloatToText(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + int numberOfChar = PrintFloat::convertFloatToText(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + buffer[numberOfChar++] = ' '; + buffer[numberOfChar] = 0; m_view.editableBannerView()->setLegendAtIndex(buffer, 2); } diff --git a/apps/statistics/calculation_cell.cpp b/apps/statistics/calculation_cell.cpp index 8a7e3f1bd..ccb886a26 100644 --- a/apps/statistics/calculation_cell.cpp +++ b/apps/statistics/calculation_cell.cpp @@ -13,7 +13,7 @@ void CalculationCell::drawRect(KDContext * ctx, KDRect rect) const { void CalculationCell::layoutSubviews() { KDRect boundsThis = bounds(); - m_bufferTextView.setFrame(KDRect(boundsThis.left() + Metric::TableSeparatorThickness, boundsThis.top(), boundsThis.width() - Metric::TableSeparatorThickness, boundsThis.height())); + m_bufferTextView.setFrame(KDRect(boundsThis.left() + Metric::TableSeparatorThickness, boundsThis.top(), boundsThis.width() - Metric::TableSeparatorThickness - k_rightMargin, boundsThis.height())); } } diff --git a/apps/statistics/calculation_cell.h b/apps/statistics/calculation_cell.h index bc33745ae..a4ca0a4bc 100644 --- a/apps/statistics/calculation_cell.h +++ b/apps/statistics/calculation_cell.h @@ -10,6 +10,8 @@ public: using EvenOddBufferTextCell::EvenOddBufferTextCell; void drawRect(KDContext * ctx, KDRect rect) const override; void layoutSubviews() override; +private: + constexpr static KDCoordinate k_rightMargin = 2; }; } diff --git a/apps/statistics/calculation_controller.cpp b/apps/statistics/calculation_controller.cpp index b9cb84ea4..269598328 100644 --- a/apps/statistics/calculation_controller.cpp +++ b/apps/statistics/calculation_controller.cpp @@ -75,8 +75,8 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int I18n::Message::Sum, I18n::Message::SquareSum, I18n::Message::SampleStandardDeviationS}; - EvenOddMessageTextCell * evenOddMessageCell = static_cast(cell); - evenOddMessageCell->setMessage(titles[j-1]); + CalculationTitleCell * calcTitleCell = static_cast(cell); + calcTitleCell->setMessage(titles[j-1]); return; } // Display a calculation cell @@ -205,7 +205,7 @@ View * CalculationController::loadView() { m_seriesTitleCells[i]->setSeparatorLeft(true); } for (int i = 0; i < k_numberOfCalculationTitleCells; i++) { - m_calculationTitleCells[i] = new EvenOddMessageTextCell(KDText::FontSize::Small); + m_calculationTitleCells[i] = new CalculationTitleCell(KDText::FontSize::Small); m_calculationTitleCells[i]->setAlignment(1.0f, 0.5f); } for (int i = 0; i < k_numberOfCalculationCells; i++) { diff --git a/apps/statistics/calculation_controller.h b/apps/statistics/calculation_controller.h index cb88b0f3a..90a25dfb1 100644 --- a/apps/statistics/calculation_controller.h +++ b/apps/statistics/calculation_controller.h @@ -4,6 +4,7 @@ #include #include "store.h" #include "calculation_cell.h" +#include "calculation_title_cell.h" #include "../shared/hideable_even_odd_cell.h" #include "../shared/store_title_cell.h" #include "../shared/tab_table_controller.h" @@ -53,7 +54,7 @@ private: static constexpr int k_hideableCellType = 3; static constexpr KDCoordinate k_cellHeight = 20; static constexpr KDCoordinate k_calculationTitleCellWidth = 175; - static constexpr KDCoordinate k_calculationCellWidth = 70; + static constexpr KDCoordinate k_calculationCellWidth = 84; static constexpr KDCoordinate k_margin = 8; static constexpr KDCoordinate k_scrollBarMargin = Metric::CommonRightMargin; @@ -62,7 +63,7 @@ private: void unloadView(View * view) override; Shared::StoreTitleCell * m_seriesTitleCells[k_numberOfSeriesTitleCells]; - EvenOddMessageTextCell * m_calculationTitleCells[k_numberOfCalculationTitleCells]; + CalculationTitleCell * m_calculationTitleCells[k_numberOfCalculationTitleCells]; CalculationCell * m_calculationCells[k_numberOfCalculationCells]; Shared::HideableEvenOddCell * m_hideableCell; Store * m_store; diff --git a/apps/statistics/calculation_title_cell.cpp b/apps/statistics/calculation_title_cell.cpp new file mode 100644 index 000000000..fcfb58bec --- /dev/null +++ b/apps/statistics/calculation_title_cell.cpp @@ -0,0 +1,10 @@ +#include "calculation_title_cell.h" + +namespace Statistics { + +void CalculationTitleCell::layoutSubviews() { + m_messageTextView.setFrame(KDRect(bounds().topLeft(), bounds().width() - k_rightMargin, bounds().height())); +} + +} + diff --git a/apps/statistics/calculation_title_cell.h b/apps/statistics/calculation_title_cell.h new file mode 100644 index 000000000..22a5f85e6 --- /dev/null +++ b/apps/statistics/calculation_title_cell.h @@ -0,0 +1,20 @@ +#ifndef APPS_STATISTICS_CALCULATION_TITLE_CELL_H +#define APPS_STATISTICS_CALCULATION_TITLE_CELL_H + +#include + +namespace Statistics { + +class CalculationTitleCell : public EvenOddMessageTextCell { +public: + CalculationTitleCell(KDText::FontSize size = KDText::FontSize::Small) : + EvenOddMessageTextCell(size) + {} + void layoutSubviews() override; +private: + constexpr static KDCoordinate k_rightMargin = 2; +}; + +} + +#endif From 73dd1cf3357f08e16ae23f8ef72b5562fcbd6fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 11:01:40 +0200 Subject: [PATCH 096/156] [shared] Make FloatPairStore::m_numberOfPairs private --- apps/shared/float_pair_store.h | 7 ++++--- apps/statistics/store.cpp | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/apps/shared/float_pair_store.h b/apps/shared/float_pair_store.h index 3e6ae76ca..68a8cb1f0 100644 --- a/apps/shared/float_pair_store.h +++ b/apps/shared/float_pair_store.h @@ -14,8 +14,8 @@ public: constexpr static int k_numberOfColumnsPerSeries = 2; constexpr static int k_maxNumberOfPairs = 100; FloatPairStore() : - m_numberOfPairs{}, - m_data{} + m_data{}, + m_numberOfPairs{} {} // Delete the implicit copy constructor: the object is heavy FloatPairStore(const FloatPairStore&) = delete; @@ -46,8 +46,9 @@ public: } protected: virtual double defaultValue(int series, int i, int j) const; - int m_numberOfPairs[k_numberOfSeries]; double m_data[k_numberOfSeries][k_numberOfColumnsPerSeries][k_maxNumberOfPairs]; +private: + int m_numberOfPairs[k_numberOfSeries]; }; } diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 4ec861498..cc633b7c9 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -144,7 +144,7 @@ double Store::minValueForAllSeries() const { double Store::maxValue(int series) const { double max = -DBL_MAX; - for (int k = 0; k < m_numberOfPairs[series]; k++) { + for (int k = 0; k < numberOfPairsOfSeries(series); k++) { if (m_data[series][0][k] > max && m_data[series][1][k] > 0) { max = m_data[series][0][k]; } @@ -154,7 +154,7 @@ double Store::maxValue(int series) const { double Store::minValue(int series) const { double min = DBL_MAX; - for (int k = 0; k < m_numberOfPairs[series]; k++) { + for (int k = 0; k < numberOfPairsOfSeries(series); k++) { if (m_data[series][0][k] < min && m_data[series][1][k] > 0) { min = m_data[series][0][k]; } @@ -210,7 +210,7 @@ double Store::median(int series) const { double Store::sum(int series) const { double result = 0; - for (int k = 0; k < m_numberOfPairs[series]; k++) { + for (int k = 0; k < numberOfPairsOfSeries(series); k++) { result += m_data[series][0][k]*m_data[series][1][k]; } return result; @@ -218,7 +218,7 @@ double Store::sum(int series) const { double Store::squaredValueSum(int series) const { double result = 0; - for (int k = 0; k < m_numberOfPairs[series]; k++) { + for (int k = 0; k < numberOfPairsOfSeries(series); k++) { result += m_data[series][0][k]*m_data[series][0][k]*m_data[series][1][k]; } return result; @@ -260,7 +260,7 @@ double Store::defaultValue(int series, int i, int j) const { double Store::sumOfValuesBetween(int series, double x1, double x2) const { double result = 0; - for (int k = 0; k < m_numberOfPairs[series]; k++) { + for (int k = 0; k < numberOfPairsOfSeries(series); k++) { if (m_data[series][0][k] < x2 && x1 <= m_data[series][0][k]) { result += m_data[series][1][k]; } @@ -272,12 +272,12 @@ double Store::sortedElementAtCumulatedFrequency(int series, double k, bool * exa // TODO: use an other algorithm (ex quickselect) to avoid quadratic complexity assert(k >= 0.0 && k <= 1.0); double totalNumberOfElements = sumOfOccurrences(series); - double bufferValues[m_numberOfPairs[series]]; - memcpy(bufferValues, m_data[series][0], m_numberOfPairs[series]*sizeof(double)); + double bufferValues[numberOfPairsOfSeries(series)]; + memcpy(bufferValues, m_data[series][0], numberOfPairsOfSeries(series)*sizeof(double)); int sortedElementIndex = 0; double cumulatedFrequency = 0.0; while (cumulatedFrequency < k) { - sortedElementIndex = minIndex(bufferValues, m_numberOfPairs[series]); + sortedElementIndex = minIndex(bufferValues, numberOfPairsOfSeries(series)); bufferValues[sortedElementIndex] = DBL_MAX; cumulatedFrequency += m_data[series][1][sortedElementIndex] / totalNumberOfElements; if (exactElement != nullptr && cumulatedFrequency == k) { @@ -288,9 +288,9 @@ double Store::sortedElementAtCumulatedFrequency(int series, double k, bool * exa } double Store::sortedElementAfter(int series, double k) const { - assert(m_numberOfPairs[series] > 0); + assert(numberOfPairsOfSeries(series) > 0); double result = DBL_MAX; - for (int i = 0; i < m_numberOfPairs[series]; i++) { + for (int i = 0; i < numberOfPairsOfSeries(series); i++) { double currentElement = m_data[series][0][i]; if (currentElement > k && currentElement < result) { result = currentElement; From 9f866febeaab3dab17d33380e18a8844661b9851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 11:44:19 +0200 Subject: [PATCH 097/156] [apps/stats] Make spanish title fit in cell --- apps/statistics/base.es.i18n | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/statistics/base.es.i18n b/apps/statistics/base.es.i18n index 08faa7bc1..770d85079 100644 --- a/apps/statistics/base.es.i18n +++ b/apps/statistics/base.es.i18n @@ -26,7 +26,7 @@ Range = "Rango" Mean = "Media" StandardDeviation = "Desviacion tipica" StandardDeviationSigma = "Desviacion tipica σ" -SampleStandardDeviationS = "Desviacion tipica muestral s" +SampleStandardDeviationS = "Desviacion tipica s" Deviation = "Varianza" InterquartileRange = "Rango intercuartilo" SquareSum = "Suma cuadrados" From 2f9de351d3064530ff0e46fa9bd8b8482c53293a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 12:19:18 +0200 Subject: [PATCH 098/156] [apps/i18n] Share statistics messages also used by regression --- apps/shared.de.i18n | 7 +++++++ apps/shared.en.i18n | 7 +++++++ apps/shared.es.i18n | 7 +++++++ apps/shared.fr.i18n | 7 +++++++ apps/shared.pt.i18n | 7 +++++++ apps/statistics/base.de.i18n | 7 ------- apps/statistics/base.en.i18n | 7 ------- apps/statistics/base.es.i18n | 7 ------- apps/statistics/base.fr.i18n | 7 ------- apps/statistics/base.pt.i18n | 7 ------- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index 70c127c76..113bd5e36 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -10,6 +10,7 @@ CopyColumnInList = "Die Spalte in einer Liste kopieren" DataTab = "Daten" DefaultSetting = "Grundeinstellungen" Deg = "gra" +Deviation = "Varianz" DisplayValues = "Werte anzeigen" ExitExamMode1 = "Wollen Sie den Testmodus " ExitExamMode2 = "verlassen?" @@ -27,9 +28,12 @@ IntervalSet = "Tabelleneinstell" Language = "Sprache" LowBattery = "Batterie leer" MathError = "Mathematischen Fehler" +Mean = "Mittelwert" Move = " Verschieben: " Next = "Nachste" +NoDataToPlot = "Keine Daten zu zeichnen" NoFunctionToDelete = "Keine Funktion zu loschen" +NoValueToCompute = "Keine Grosse zu berechnen" Ok = "Bestatigen" Or = " oder " Orthonormal = "Orthonormal" @@ -37,7 +41,10 @@ Plot = "Graphen zeichnen" Rad = "rad" RoundAbscissa = "Ganzzahl" Sci = "sci/" +StatTab = "Stats" +StandardDeviation = "Standardabweichung" Step = "Schrittwert" +SquareSum = "Quadratsumme" SyntaxError = "Syntaxfehler" ToZoom = "Zoom: " Trigonometric = "Trigonometrisch" diff --git a/apps/shared.en.i18n b/apps/shared.en.i18n index a1f9ed2e9..d79ebdd48 100644 --- a/apps/shared.en.i18n +++ b/apps/shared.en.i18n @@ -10,6 +10,7 @@ CopyColumnInList = "Export the column to a list" DataTab = "Data" DefaultSetting = "Basic settings" Deg = "deg" +Deviation = "Variance" DisplayValues = "Display values" ExitExamMode1 = "Exit the exam " ExitExamMode2 = "mode?" @@ -27,9 +28,12 @@ IntervalSet = "Set the interval" Language = "Language" LowBattery = "Low battery" MathError = "Math error" +Mean = "Mean" Move = " Move: " Next = "Next" +NoDataToPlot = "No data to draw" NoFunctionToDelete = "No function to delete" +NoValueToCompute = "No values to calculate" Ok = "Confirm" Or = " or " Orthonormal = "Orthonormal" @@ -37,7 +41,10 @@ Plot = "Plot graph" Rad = "rad" RoundAbscissa = "Integer" Sci = "sci/" +StandardDeviation = "Standard deviation" +StatTab = "Stats" Step = "Step" +SquareSum = "Sum of squares" SyntaxError = "Syntax error" ToZoom = "Zoom: " Trigonometric = "Trigonometrical" diff --git a/apps/shared.es.i18n b/apps/shared.es.i18n index c0c02ff46..bb8ae627a 100644 --- a/apps/shared.es.i18n +++ b/apps/shared.es.i18n @@ -10,6 +10,7 @@ CopyColumnInList = "Copiar la columna en una lista" DataTab = "Datos" DefaultSetting = "Ajustes basicos" Deg = "gra" +Deviation = "Varianza" DisplayValues = "Visualizar los valores" ExitExamMode1 = "Salir del modo " ExitExamMode2 = "examen ?" @@ -27,9 +28,12 @@ IntervalSet = "Ajustar el intervalo" Language = "Idioma" LowBattery = "Bateria baja" MathError = "Error matematico" +Mean = "Media" Move = " Mover : " Next = "Siguiente" +NoDataToPlot = "Ningunos datos que dibujar" NoFunctionToDelete = "Ninguna funcion que eliminar" +NoValueToCompute = "Ninguna medida que calcular" Ok = "Confirmar" Or = " o " Orthonormal = "Ortonormal" @@ -37,6 +41,9 @@ Plot = "Dibujar el grafico" Rad = "rad" RoundAbscissa = "Abscisas enteras" Sci = "sci/" +SquareSum = "Suma cuadrados" +StandardDeviation = "Desviacion tipica" +StatTab = "Medidas" Step = "Incremento" SyntaxError = "Error sintactico" ToZoom = "Zoom : " diff --git a/apps/shared.fr.i18n b/apps/shared.fr.i18n index 47cd17a61..48126b6ca 100644 --- a/apps/shared.fr.i18n +++ b/apps/shared.fr.i18n @@ -10,6 +10,7 @@ CopyColumnInList = "Copier la colonne dans une liste" DataTab = "Donnees" DefaultSetting = "Reglages de base" Deg = "deg" +Deviation = "Variance" DisplayValues = "Afficher les valeurs" ExitExamMode1 = "Voulez-vous sortir " ExitExamMode2 = "du mode examen ?" @@ -27,9 +28,12 @@ IntervalSet = "Regler l'intervalle" Language = "Langue" LowBattery = "Batterie faible" MathError = "Erreur mathematique" +Mean = "Moyenne" Move = " Deplacer : " Next = "Suivant" +NoDataToPlot = "Aucune donnee a tracer" NoFunctionToDelete = "Pas de fonction a supprimer" +NoValueToCompute = "Aucune grandeur a calculer" Ok = "Valider" Or = " ou " Orthonormal = "Orthonorme" @@ -37,6 +41,9 @@ Plot = "Tracer le graphique" Rad = "rad" RoundAbscissa = "Abscisses entieres" Sci = "sci/" +SquareSum = "Somme des carres" +StandardDeviation = "Ecart type" +StatTab = "Stats" Step = "Pas" SyntaxError = "Attention a la syntaxe" ToZoom = "Zoomer : " diff --git a/apps/shared.pt.i18n b/apps/shared.pt.i18n index e6c7263b2..da794f9ff 100644 --- a/apps/shared.pt.i18n +++ b/apps/shared.pt.i18n @@ -10,6 +10,7 @@ CopyColumnInList = "Copie a coluna em uma lista" DataTab = "Dados" DefaultSetting = "Configuracoes basicas" Deg = "gra" +Deviation = "Variancia" DisplayValues = "Exibir os valores" ExitExamMode1 = "Voce quer sair do modo de " ExitExamMode2 = "exame ?" @@ -27,9 +28,12 @@ IntervalSet = "Ajustar o intervalo" Language = "Idioma" LowBattery = "Bateria fraca" MathError = "Erro matematico" +Mean = "Media" Move = " Mover : " Next = "Seguinte" +NoDataToPlot = "Nao ha dados para desenhar" NoFunctionToDelete = "Sem funcao para eliminar" +NoValueToCompute = "Nenhuma quantidade para calcular" Ok = "Confirmar" Or = " ou " Orthonormal = "Ortonormado" @@ -37,6 +41,9 @@ Plot = "Tracar o grafico" Rad = "rad" RoundAbscissa = "Inteiro" Sci = "sci/" +SquareSum = "Soma dos quadrados" +StandardDeviation = "Desvio padrao" +StatTab = "Estat" Step = "Passo" SyntaxError = "Erro de sintaxe" ToZoom = "Zoom : " diff --git a/apps/statistics/base.de.i18n b/apps/statistics/base.de.i18n index aebe0c9f1..0731168e3 100644 --- a/apps/statistics/base.de.i18n +++ b/apps/statistics/base.de.i18n @@ -2,7 +2,6 @@ StatsApp = "Statistiken" StatsAppCapital = "STATISTIKEN" HistogramTab = "Histogramm" BoxTab = "Boxplot" -StatTab = "Stats" Values1 = "Werte V1" Values2 = "Werte V2" Values3 = "Werte V3" @@ -10,7 +9,6 @@ Sizes1 = "Haufigkeiten N1" Sizes2 = "Haufigkeiten N2" Sizes3 = "Haufigkeiten N3" ImportList = "Laden eine Liste" -NoDataToPlot = "Keine Daten zu zeichnen" Interval = " Intervall" Size = " Haufigkeit" Frequency = "Relative" @@ -20,13 +18,8 @@ BarStart = "Beginn der Serie" FirstQuartile = "Unteres Quartil" Median = "Median" ThirdQuartile = "Oberes Quartil" -NoValueToCompute = "Keine Grosse zu berechnen" TotalSize = "Anzahl der Elemente" Range = "Spannweite" -Mean = "Mittelwert" -StandardDeviation = "Standardabweichung" StandardDeviationSigma = "Standardabweichung σ" SampleStandardDeviationS = "Standardabweichung s" -Deviation = "Varianz" InterquartileRange = "Interquartilsabstand" -SquareSum = "Quadratsumme" diff --git a/apps/statistics/base.en.i18n b/apps/statistics/base.en.i18n index 107fcc7d2..61091e835 100644 --- a/apps/statistics/base.en.i18n +++ b/apps/statistics/base.en.i18n @@ -2,7 +2,6 @@ StatsApp = "Statistics" StatsAppCapital = "STATISTICS" HistogramTab = "Histogram" BoxTab = "Box" -StatTab = "Stats" Values1 = "Values V1" Values2 = "Values V2" Values3 = "Values V3" @@ -10,7 +9,6 @@ Sizes1 = "Sizes N1" Sizes2 = "Sizes N2" Sizes3 = "Sizes N3" ImportList = "Import from a list" -NoDataToPlot = "No data to draw" Interval = " Interval " Size = " Size" Frequency = "Frequency" @@ -20,13 +18,8 @@ BarStart = "X start" FirstQuartile = "First quartile" Median = "Median" ThirdQuartile = "Third quartile" -NoValueToCompute = "No values to calculate" TotalSize = "Total size" Range = "Range" -Mean = "Mean" -StandardDeviation = "Standard deviation" StandardDeviationSigma = "Standard deviation σ" SampleStandardDeviationS = "Sample std deviation s" -Deviation = "Variance" InterquartileRange = "Interquartile range" -SquareSum = "Sum of squares" diff --git a/apps/statistics/base.es.i18n b/apps/statistics/base.es.i18n index 770d85079..7a31fe73b 100644 --- a/apps/statistics/base.es.i18n +++ b/apps/statistics/base.es.i18n @@ -2,7 +2,6 @@ StatsApp = "Estadistica" StatsAppCapital = "ESTADISTICA" HistogramTab = "Histograma" BoxTab = "Caja" -StatTab = "Medidas" Values1 = "Valores V1" Values2 = "Valores V2" Values3 = "Valores V3" @@ -10,7 +9,6 @@ Sizes1 = "Frecuencias N1" Sizes2 = "Frecuencias N2" Sizes3 = "Frecuencias N3" ImportList = "Importar una lista" -NoDataToPlot = "Ningunos datos que dibujar" Interval = " Intervalo" Size = " Frecuencia" Frequency = "Relativa" @@ -20,13 +18,8 @@ BarStart = "Principio de la serie" FirstQuartile = "Primer cuartil" Median = "Mediana" ThirdQuartile = "Tercer cuartil" -NoValueToCompute = "Ninguna medida que calcular" TotalSize = "Poblacion" Range = "Rango" -Mean = "Media" -StandardDeviation = "Desviacion tipica" StandardDeviationSigma = "Desviacion tipica σ" SampleStandardDeviationS = "Desviacion tipica s" -Deviation = "Varianza" InterquartileRange = "Rango intercuartilo" -SquareSum = "Suma cuadrados" diff --git a/apps/statistics/base.fr.i18n b/apps/statistics/base.fr.i18n index ad4e9d212..1f4a6ee5d 100644 --- a/apps/statistics/base.fr.i18n +++ b/apps/statistics/base.fr.i18n @@ -2,7 +2,6 @@ StatsApp = "Statistiques" StatsAppCapital = "STATISTIQUES" HistogramTab = "Histogramme" BoxTab = "Boite" -StatTab = "Stats" Values1 = "Valeurs V1" Values2 = "Valeurs V2" Values3 = "Valeurs V3" @@ -10,7 +9,6 @@ Sizes1 = "Effectifs N1" Sizes2 = "Effectifs N2" Sizes3 = "Effectifs N3" ImportList = "Importer une liste" -NoDataToPlot = "Aucune donnee a tracer" Interval = " Intervalle " Size = " Effectif" Frequency = "Frequence" @@ -20,13 +18,8 @@ BarStart = "Debut de la serie" FirstQuartile = "Premier quartile" Median = "Mediane" ThirdQuartile = "Troisieme quartile" -NoValueToCompute = "Aucune grandeur a calculer" TotalSize = "Effectif total" Range = "Etendue" -Mean = "Moyenne" -StandardDeviation = "Ecart type" StandardDeviationSigma = "Ecart type" SampleStandardDeviationS = "Ecart type echantillon" -Deviation = "Variance" InterquartileRange = "Ecart interquartile" -SquareSum = "Somme des carres" diff --git a/apps/statistics/base.pt.i18n b/apps/statistics/base.pt.i18n index bf634f523..c8b93fa37 100644 --- a/apps/statistics/base.pt.i18n +++ b/apps/statistics/base.pt.i18n @@ -2,7 +2,6 @@ StatsApp = "Estatistica" StatsAppCapital = "ESTATISTICA" HistogramTab = "Histograma" BoxTab = "Caixa" -StatTab = "Estat" Values1 = "Valores V1" Values2 = "Valores V2" Values3 = "Valores V3" @@ -10,7 +9,6 @@ Sizes1 = "Frequencias N1" Sizes2 = "Frequencias N2" Sizes3 = "Frequencias N3" ImportList = "Importar de uma lista" -NoDataToPlot = "Nao ha dados para desenhar" Interval = " Intervalo" Size = " Frequencia" Frequency = "Relativa" @@ -20,13 +18,8 @@ BarStart = "Inicio da serie" FirstQuartile = "Quartil inferior" Median = "Mediana" ThirdQuartile = "Quartil superior" -NoValueToCompute = "Nenhuma quantidade para calcular" TotalSize = "Numero de itens" Range = "Amplitude" -Mean = "Media" -StandardDeviation = "Desvio padrao" StandardDeviationSigma = "Desvio padrao σ" SampleStandardDeviationS = "Desvio padrao amostral s" -Deviation = "Variancia" InterquartileRange = "Interquartil" -SquareSum = "Soma dos quadrados" From 1f02b8201e2a200bbdf3847964b5e01678a973b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 12:20:10 +0200 Subject: [PATCH 099/156] [apps/regression] Fix regression --- apps/regression/app.cpp | 2 +- apps/regression/calculation_controller.cpp | 2 +- apps/regression/graph_controller.cpp | 9 +- apps/regression/graph_view.cpp | 11 +- apps/regression/graph_view.h | 1 + apps/regression/store.cpp | 201 ++++++++++++--------- apps/regression/store.h | 49 ++--- 7 files changed, 156 insertions(+), 119 deletions(-) diff --git a/apps/regression/app.cpp b/apps/regression/app.cpp index d1b3f9ade..ecd8180a3 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.deleteAllPairsOfAllSeries(); + m_store.deleteAllPairs(); m_store.setDefault(); m_modelVersion = 0; m_rangeVersion = 0; diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index 00a7efa23..065d0e8fc 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -163,7 +163,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int } if (i == 1 && j > k_totalNumberOfDoubleBufferRows) { assert(j != 9); - CalculPointer calculationMethods[k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows] = {&Store::numberOfPairs, &Store::covariance, + CalculPointer calculationMethods[k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows] = {&Store::doubleCastedNumberOfPairsOfSeries, &Store::covariance, &Store::columnProductSum, nullptr, &Store::slope, &Store::yIntercept, &Store::correlationCoefficient, &Store::squaredCorrelationCoefficient}; double calculation = (m_store->*calculationMethods[j-k_totalNumberOfDoubleBufferRows-1])(); EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)cell; diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index b664a02b2..7abbd9416 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -26,10 +26,15 @@ ViewController * GraphController::initialisationParameterController() { } bool GraphController::isEmpty() const { - if (m_store->numberOfPairs() < 2 || std::isinf(m_store->slope()) || std::isnan(m_store->slope())) { + if (m_store->isEmpty()) { return true; } - return false; + for (int series = 0; series < FloatPairStore::k_numberOfSeries; series++) { + if (!m_store->seriesIsEmpty(series) && !std::isinf(m_store->slope(series)) && (std::isnan(m_store->slope(series)))) { + return false; + } + } + return true; } I18n::Message GraphController::emptyMessage() { diff --git a/apps/regression/graph_view.cpp b/apps/regression/graph_view.cpp index af696bbee..ae70ba10d 100644 --- a/apps/regression/graph_view.cpp +++ b/apps/regression/graph_view.cpp @@ -9,7 +9,8 @@ GraphView::GraphView(Store * store, CurveViewCursor * cursor, BannerView * banne CurveView(store, cursor, bannerView, cursorView), m_store(store), m_xLabels{}, - m_yLabels{} + m_yLabels{}, + m_series(0) //TODO { } @@ -20,17 +21,17 @@ void GraphView::drawRect(KDContext * ctx, KDRect rect) const { drawAxes(ctx, rect, Axis::Vertical); drawLabels(ctx, rect, Axis::Horizontal, true); drawLabels(ctx, rect, Axis::Vertical, true); - float regressionParameters[2] = {(float)m_store->slope(), (float)m_store->yIntercept()}; + float regressionParameters[2] = {(float)m_store->slope(m_series), (float)m_store->yIntercept(m_series)}; drawCurve(ctx, rect, [](float abscissa, void * model, void * context) { float * params = (float *)model; return params[0]*abscissa+params[1]; }, regressionParameters, nullptr, Palette::YellowDark); for (int index = 0; index < m_store->numberOfPairs(); index++) { - drawDot(ctx, rect, m_store->get(0,index), m_store->get(1,index), Palette::Red); + drawDot(ctx, rect, m_store->get(m_series, 0,index), m_store->get(m_series, 1,index), Palette::Red); } - drawDot(ctx, rect, m_store->meanOfColumn(0), m_store->meanOfColumn(1), Palette::Palette::YellowDark, true); - drawDot(ctx, rect, m_store->meanOfColumn(0), m_store->meanOfColumn(1), KDColorWhite); + drawDot(ctx, rect, m_store->meanOfColumn(m_series, 0), m_store->meanOfColumn(m_series, 1), Palette::Palette::YellowDark, true); + drawDot(ctx, rect, m_store->meanOfColumn(m_series, 0), m_store->meanOfColumn(m_series, 1), KDColorWhite); } char * GraphView::label(Axis axis, int index) const { diff --git a/apps/regression/graph_view.h b/apps/regression/graph_view.h index de60b5e91..76eebff34 100644 --- a/apps/regression/graph_view.h +++ b/apps/regression/graph_view.h @@ -17,6 +17,7 @@ private: Store * m_store; char m_xLabels[k_maxNumberOfXLabels][Poincare::PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits)]; char m_yLabels[k_maxNumberOfYLabels][Poincare::PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits)]; + int m_series; }; } diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index 8e9de77d3..7cfb5adbe 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -8,6 +8,9 @@ using namespace Shared; namespace Regression { +static inline float max(float x, float y) { return (x>y ? x : y); } +static inline float min(float x, float y) { return (x= 0) == (direction > 0))) { - // Handle edge case: if 2 dots have the same abscissa but different ordinates - if (nextX != m_data[series][0][index] || ((nextY - m_data[series][1][index] >= 0) == (direction > 0))) { - nextX = m_data[series][0][index]; - nextY = m_data[series][1][index]; - selectedDot = index; + for (int series = 0; series < k_numberOfSeries; series ++) { + if (!seriesIsEmpty(series)) { + for (int index = 0; index < numberOfPairsOfSeries(series); index++) { + if ((m_xMin <= m_data[series][0][index] && m_data[series][0][index] <= m_xMax) && + (std::fabs(m_data[series][0][index] - x) < std::fabs(nextX - x)) && + ((m_data[series][1][index] - yValueForXValue(series, m_data[series][0][index]) >= 0) == (direction > 0))) { + // Handle edge case: if 2 dots have the same abscissa but different ordinates + if (nextX != m_data[series][0][index] || ((nextY - m_data[series][1][index] >= 0) == (direction > 0))) { + nextX = m_data[series][0][index]; + nextY = m_data[series][1][index]; + selectedDot = index; + } + } } } - } - // Compare with the mean dot - double meanX = meanOfColumn(series, 0); - double meanY = meanOfColumn(series, 1); - if (m_xMin <= meanX && meanX <= m_xMax && - (std::fabs(meanX - x) < std::fabs(nextX - x)) && - ((meanY - yValueForXValue(meanX) >= 0) == (direction > 0))) { - if (nextX != meanX || ((nextY - meanY >= 0) == (direction > 0))) { - selectedDot = m_numberOfPairs[series]; + // Compare with the mean dot + double meanX = meanOfColumn(series, 0); + double meanY = meanOfColumn(series, 1); + if (m_xMin <= meanX && meanX <= m_xMax && + (std::fabs(meanX - x) < std::fabs(nextX - x)) && + ((meanY - yValueForXValue(series, meanX) >= 0) == (direction > 0))) { + if (nextX != meanX || ((nextY - meanY >= 0) == (direction > 0))) { + selectedDot = numberOfPairsOfSeries(series); + } } + } return selectedDot; } -int Store::nextDot(int direction, int dot) { +int Store::nextDot(int series, int direction, int dot) { float nextX = INFINITY; int selectedDot = -1; double meanX = meanOfColumn(series, 0); float x = meanX; - if (dot >= 0 && dot < m_numberOfPairs[series]) { - x = get(0, dot); + if (dot >= 0 && dot < numberOfPairsOfSeries(series)) { + x = get(series, 0, dot); } /* We have to scan the Store in opposite ways for the 2 directions to ensure to * select all dots (even with equal abscissa) */ if (direction > 0) { - for (int index = 0; index < m_numberOfPairs[series]; index++) { + for (int index = 0; index < numberOfPairsOfSeries(series); index++) { /* The conditions to test are in this order: * - the next dot is the closest one in abscissa to x * - the next dot is not the same as the selected one * - the next dot is at the right of the selected one */ - if (std::fabs(m_date[series][0][index] - x) < std::fabs(nextX - x) && + if (std::fabs(m_data[series][0][index] - x) < std::fabs(nextX - x) && (index != dot) && - (m_date[series][0][index] >= x)) { + (m_data[series][0][index] >= x)) { // Handle edge case: 2 dots have same abscissa - if (m_date[series][0][index] != x || (index > dot)) { - nextX = m_date[series][0][index]; + if (m_data[series][0][index] != x || (index > dot)) { + nextX = m_data[series][0][index]; selectedDot = index; } } } // Compare with the mean dot if (std::fabs(meanX - x) < std::fabs(nextX - x) && - (m_numberOfPairs[series] != dot) && + (numberOfPairsOfSeries(series) != dot) && (meanX >= x)) { if (meanX != x || (x > dot)) { - selectedDot = m_numberOfPairs[series]; + selectedDot = numberOfPairsOfSeries(series); } } } else { // Compare with the mean dot if (std::fabs(meanX - x) < std::fabs(nextX - x) && - (m_numberOfPairs[series] != dot) && + (numberOfPairsOfSeries(series) != dot) && (meanX <= x)) { - if (meanX != x || (m_numberOfPairs[series] < dot)) { + if (meanX != x || (numberOfPairsOfSeries(series) < dot)) { nextX = meanX; - selectedDot = m_numberOfPairs[series]; + selectedDot = numberOfPairsOfSeries(series); } } - for (int index = m_numberOfPairs[series]-1; index >= 0; index--) { - if (std::fabs(m_date[series][0][index] - x) < std::fabs(nextX - x) && + for (int index = numberOfPairsOfSeries(series)-1; index >= 0; index--) { + if (std::fabs(m_data[series][0][index] - x) < std::fabs(nextX - x) && (index != dot) && - (m_date[series][0][index] <= x)) { + (m_data[series][0][index] <= x)) { // Handle edge case: 2 dots have same abscissa - if (m_date[series][0][index] != x || (index < dot)) { - nextX = m_date[series][0][index]; + if (m_data[series][0][index] != x || (index < dot)) { + nextX = m_data[series][0][index]; selectedDot = index; } } @@ -111,115 +119,132 @@ int Store::nextDot(int direction, int dot) { /* Window */ -void Store::setDefault(int series) { - float min = minValueOfColumn(series, 0); - float max = maxValueOfColumn(series, 0); - float range = max - min; - setXMin(min - k_displayLeftMarginRatio*range); - setXMax(max + k_displayRightMarginRatio*range); +void Store::setDefault() { + float minX = FLT_MAX; + float maxX = -FLT_MAX; + for (int series = 0; series < k_numberOfSeries; series ++) { + if (!seriesIsEmpty(series)) { + minX = min(minX, minValueOfColumn(series, 0)); + maxX = max(maxX, maxValueOfColumn(series, 0)); + } + } + float range = maxX - minX; + setXMin(minX - k_displayLeftMarginRatio*range); + setXMax(maxX + k_displayRightMarginRatio*range); setYAuto(true); } +bool Store::isEmpty() const { + for (int i = 0; i < k_numberOfSeries; i ++) { + if (!seriesIsEmpty(i)) { + return false; + } + } + return true; +} + +bool Store::seriesIsEmpty(int series) const { + return numberOfPairsOfSeries(series) < 2; +} + /* Calculations */ -float Store::maxValueOfColumn(int series, int i) { - float max = -FLT_MAX; - for (int k = 0; k < m_numberOfPairs[series]; k++) { - if (m_data[series][i][k] > max) { - max = m_data[series][i][k]; - } - } - return max; +double Store::doubleCastedNumberOfPairsOfSeries(int series) const { + return FloatPairStore::numberOfPairsOfSeries(series); } -float Store::minValueOfColumn(int series, int i) { - float min = FLT_MAX; - for (int k = 0; k < m_numberOfPairs[series]; k++) { - if (m_data[series][i][k] < min) { - min = m_data[series][i][k]; - } +float Store::maxValueOfColumn(int series, int i) const { + float maxColumn = -FLT_MAX; + for (int k = 0; k < numberOfPairsOfSeries(series); k++) { + maxColumn = max(maxColumn, m_data[series][i][k]); } - return min; + return maxColumn; } -double Store::squaredValueSumOfColumn(int series, int i) { +float Store::minValueOfColumn(int series, int i) const { + float minColumn = FLT_MAX; + for (int k = 0; k < numberOfPairsOfSeries(series); k++) { + minColumn = min(minColumn, m_data[series][i][k]); + } + return minColumn; +} + +double Store::squaredValueSumOfColumn(int series, int i) const { double result = 0; - for (int k = 0; k < m_numberOfPairs[series]; k++) { + for (int k = 0; k < numberOfPairsOfSeries(series); k++) { result += m_data[series][i][k]*m_data[series][i][k]; } return result; } -double Store::columnProductSum(int series) { +double Store::columnProductSum(int series) const { double result = 0; - for (int k = 0; k < m_numberOfPairs[series]; k++) { - result += m_date[series][0][k]*m_date[series][1][k]; + for (int k = 0; k < numberOfPairsOfSeries(series); k++) { + result += m_data[series][0][k]*m_data[series][1][k]; } return result; } -double Store::meanOfColumn(int series, int i) { - return m_numberOfPairs[series] == 0 ? 0 : sumOfColumn(series, i)/m_numberOfPairs[series]; +double Store::meanOfColumn(int series, int i) const { + return numberOfPairsOfSeries(series) == 0 ? 0 : sumOfColumn(series, i)/numberOfPairsOfSeries(series); } -double Store::varianceOfColumn(int series, int i) { +double Store::varianceOfColumn(int series, int i) const { double mean = meanOfColumn(series, i); - return squaredValueSumOfColumn(series, i)/m_numberOfPairs[series] - mean*mean; + return squaredValueSumOfColumn(series, i)/numberOfPairsOfSeries(series) - mean*mean; } -double Store::standardDeviationOfColumn(int series, int i) { +double Store::standardDeviationOfColumn(int series, int i) const { return std::sqrt(varianceOfColumn(series, i)); } -double Store::covariance(int series) { - return columnProductSum(series)/m_numberOfPairs[series] - meanOfColumn(series, 0)*meanOfColumn(series, 1); +double Store::covariance(int series) const { + return columnProductSum(series)/numberOfPairsOfSeries(series) - meanOfColumn(series, 0)*meanOfColumn(series, 1); } -double Store::slope(int series) { +double Store::slope(int series) const { return covariance(series)/varianceOfColumn(series, 0); } -double Store::yIntercept(int series) { +double Store::yIntercept(int series) const { return meanOfColumn(series, 1) - slope(series)*meanOfColumn(series, 0); } -double Store::yValueForXValue(int series, double x) { +double Store::yValueForXValue(int series, double x) const { return slope(series)*x+yIntercept(series); } -double Store::xValueForYValue(int series, double y) { +double Store::xValueForYValue(int series, double y) const { return std::fabs(slope(series)) < DBL_EPSILON ? NAN : (y - yIntercept(series))/slope(series); } -double Store::correlationCoefficient(int series) { +double Store::correlationCoefficient(int series) const { double sd0 = standardDeviationOfColumn(series, 0); double sd1 = standardDeviationOfColumn(series, 1); return (sd0 == 0.0 || sd1 == 0.0) ? 1.0 : covariance(series)/(sd0*sd1); } -double Store::squaredCorrelationCoefficient(int series) { +double Store::squaredCorrelationCoefficient(int series) const { double cov = covariance(series); double v0 = varianceOfColumn(series, 0); double v1 = varianceOfColumn(series, 1); return (v0 == 0.0 || v1 == 0.0) ? 1.0 : cov*cov/(v0*v1); } -InteractiveCurveViewRangeDelegate::Range Store::computeYRange(int series, InteractiveCurveViewRange * interactiveCurveViewRange) { - float min = FLT_MAX; - float max = -FLT_MAX; - for (int k = 0; k < m_numberOfPairs[series]; k++) { - if (m_xMin <= m_date[series][0][k] && m_date[series][0][k] <= m_xMax) { - if (m_date[series][1][k] < min) { - min = m_date[series][1][k]; - } - if (m_date[series][1][k] > max) { - max = m_date[series][1][k]; +InteractiveCurveViewRangeDelegate::Range Store::computeYRange(InteractiveCurveViewRange * interactiveCurveViewRange) { + float minY = FLT_MAX; + float maxY = -FLT_MAX; + for (int series = 0; series < k_numberOfSeries; series++) { + for (int k = 0; k < numberOfPairsOfSeries(series); k++) { + if (m_xMin <= m_data[series][0][k] && m_data[series][0][k] <= m_xMax) { + minY = min(minY, m_data[series][1][k]); + maxY = max(maxY, m_data[series][1][k]); } } } InteractiveCurveViewRangeDelegate::Range range; - range.min = min; - range.max = max; + range.min = minY; + range.max = maxY; return range; } diff --git a/apps/regression/store.h b/apps/regression/store.h index 59dd10fdc..25a5b41e9 100644 --- a/apps/regression/store.h +++ b/apps/regression/store.h @@ -9,43 +9,48 @@ namespace Regression { class Store : public Shared::InteractiveCurveViewRange, public Shared::FloatPairStore, public Shared::InteractiveCurveViewRangeDelegate { public: Store(); + // Dots - /* Return the closest dot to x above the regression curve if direction > 0, - * below otherwise*/ - int closestVerticalDot(int series, int direction, float x); - /* Return the closest dot to dot given on the right if direction > 0, - * on the left otherwise*/ + /* Return the closest dot to abscissa x above the regression curve if + * direction > 0, below otherwise */ + int closestVerticalDot(int direction, float x); + /* Return the closest dot to given dot, on the right if direction > 0, + * on the left otherwise */ int nextDot(int series, int direction, int dot); // Window void setDefault() override; + // Series + bool isEmpty() const; + bool seriesIsEmpty(int series) const; + // Calculation - double numberOfPairs(int series) const { return m_numberOfPairs; } - double squaredValueSumOfColumn(int series, int i); - double columnProductSum(int series); - double meanOfColumn(int series, int i); - double varianceOfColumn(int series, int i); - double standardDeviationOfColumn(int series, int i); - double covariance(int series); - double slope(int series); - double yIntercept(int series); - double yValueForXValue(int series, double x); - double xValueForYValue(int series, double y); - double correlationCoefficient(int series); - double squaredCorrelationCoefficient(int series); + double doubleCastedNumberOfPairsOfSeries(int series) const; + double squaredValueSumOfColumn(int series, int i) const; + double columnProductSum(int series) const; + double meanOfColumn(int series, int i) const; + double varianceOfColumn(int series, int i) const; + double standardDeviationOfColumn(int series, int i) const; + double covariance(int series) const; + double slope(int series) const; + double yIntercept(int series) const; + double yValueForXValue(int series, double x) const; + double xValueForYValue(int series, double y) const; + double correlationCoefficient(int series) const; + double squaredCorrelationCoefficient(int series) const; private: constexpr static float k_displayTopMarginRatio = 0.12f; constexpr static float k_displayRightMarginRatio = 0.05f; constexpr static float k_displayBottomMarginRatio = 0.5f; constexpr static float k_displayLeftMarginRatio = 0.05f; - InteractiveCurveViewRangeDelegate::Range computeYRange(int series, InteractiveCurveViewRange * interactiveCurveViewRange) override; + InteractiveCurveViewRangeDelegate::Range computeYRange(InteractiveCurveViewRange * interactiveCurveViewRange) override; float addMargin(float x, float range, bool isMin) override; - float maxValueOfColumn(int series, int i); - float minValueOfColumn(int series, int i); + float maxValueOfColumn(int series, int i) const; + float minValueOfColumn(int series, int i) const; }; -typedef double (Store::*ArgCalculPointer)(int); +typedef double (Store::*ArgCalculPointer)(int, int) const; typedef double (Store::*CalculPointer)(); typedef void (Store::*RangeMethodPointer)(); From 36e19af5141d1121a57cd2af70f1d4fe29c6c2aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 14:00:17 +0200 Subject: [PATCH 100/156] [apps/share] Fix include guard in class header --- apps/shared/function_title_cell.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/shared/function_title_cell.h b/apps/shared/function_title_cell.h index eba48301e..427926758 100644 --- a/apps/shared/function_title_cell.h +++ b/apps/shared/function_title_cell.h @@ -1,5 +1,5 @@ -#ifndef SEQUENCE_FUNCTION_TITLE_CELL_H -#define SEQUENCE_FUNCTION_TITLE_CELL_H +#ifndef SHARED_FUNCTION_TITLE_CELL_H +#define SHARED_FUNCTION_TITLE_CELL_H #include From f6823c038f993eea41ea09a14256aace03c3ddc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 14:01:38 +0200 Subject: [PATCH 101/156] [[apps/regression] Fix regression (now compiles) --- apps/regression/calculation_controller.cpp | 53 ++++++++++------- apps/regression/calculation_controller.h | 6 +- .../regression/go_to_parameter_controller.cpp | 6 +- apps/regression/graph_controller.cpp | 57 ++++++++++--------- apps/regression/graph_controller.h | 2 + apps/regression/store.cpp | 4 +- apps/regression/store.h | 4 +- apps/regression/store_controller.cpp | 8 +-- 8 files changed, 79 insertions(+), 61 deletions(-) diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index 065d0e8fc..170391962 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -121,12 +121,8 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int EvenOddCell * myCell = (EvenOddCell *)cell; myCell->setEven(j%2 == 0); myCell->setHighlighted(i == selectedColumn() && j == selectedRow()); - if (j == 0 && i > 0) { - EvenOddDoubleBufferTextCell * myCell = (EvenOddDoubleBufferTextCell *)cell; - myCell->setFirstText("x"); - myCell->setSecondText("y"); - return; - } + + // Calculation title if (i == 0) { if (j == numberOfRows()-1) { EvenOddExpressionCell * myCell = (EvenOddExpressionCell *)cell; @@ -143,11 +139,25 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int myCell->setMessage(titles[j-1]); return; } + + int seriesNumber = i - 1;; + assert(i >= 0 && seriesNumber < FloatPairStore::k_numberOfSeries); + + // Coordinate and series title + if (j == 0 && i > 0) { + EvenOddDoubleBufferTextCell * myCell = (EvenOddDoubleBufferTextCell *)cell; + char buffer[] = {'x', static_cast('0' + seriesNumber), 0}; + myCell->setFirstText(buffer); + buffer[0] = 'y'; + myCell->setSecondText(buffer); + return; + } + + // Calculation cell if (i == 1 && j > 0 && j <= k_totalNumberOfDoubleBufferRows) { - ArgCalculPointer calculationMethods[k_totalNumberOfDoubleBufferRows] = {&Store::meanOfColumn, &Store::sumOfColumn, - &Store::squaredValueSumOfColumn, &Store::standardDeviationOfColumn, &Store::varianceOfColumn}; - double calculation1 = (m_store->*calculationMethods[j-1])(0); - double calculation2 = (m_store->*calculationMethods[j-1])(1); + ArgCalculPointer calculationMethods[k_totalNumberOfDoubleBufferRows] = {&Store::meanOfColumn, &Store::sumOfColumn, &Store::squaredValueSumOfColumn, &Store::standardDeviationOfColumn, &Store::varianceOfColumn}; + double calculation1 = (m_store->*calculationMethods[j-1])(seriesNumber, 0); + double calculation2 = (m_store->*calculationMethods[j-1])(seriesNumber, 1); EvenOddDoubleBufferTextCell * myCell = (EvenOddDoubleBufferTextCell *)cell; char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; PrintFloat::convertFloatToText(calculation1, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); @@ -163,9 +173,8 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int } if (i == 1 && j > k_totalNumberOfDoubleBufferRows) { assert(j != 9); - CalculPointer calculationMethods[k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows] = {&Store::doubleCastedNumberOfPairsOfSeries, &Store::covariance, - &Store::columnProductSum, nullptr, &Store::slope, &Store::yIntercept, &Store::correlationCoefficient, &Store::squaredCorrelationCoefficient}; - double calculation = (m_store->*calculationMethods[j-k_totalNumberOfDoubleBufferRows-1])(); + CalculPointer calculationMethods[k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows] = {&Store::doubleCastedNumberOfPairsOfSeries, &Store::covariance, &Store::columnProductSum, nullptr, &Store::slope, &Store::yIntercept, &Store::correlationCoefficient, &Store::squaredCorrelationCoefficient}; + double calculation = (m_store->*calculationMethods[j-k_totalNumberOfDoubleBufferRows-1])(seriesNumber); EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)cell; char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; PrintFloat::convertFloatToText(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); @@ -197,12 +206,12 @@ HighlightCell * CalculationController::reusableCell(int index, int type) { return m_columnTitleCell; } if (type == 3) { - assert(index < k_totalNumberOfDoubleBufferRows); + assert(index < k_numberOfDoubleCalculationCells); assert(m_doubleCalculationCells[index] != nullptr); return m_doubleCalculationCells[index]; } - assert(index < k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows); - assert(m_calculationCells[index] != nullptr); + assert(index < k_numberOfCalculationCells); + assert(m_calculationCells[index] != nullptr); return m_calculationCells[index]; } @@ -217,9 +226,9 @@ int CalculationController::reusableCellCount(int type) { return 1; } if (type == 3) { - return k_totalNumberOfDoubleBufferRows; + return k_numberOfDoubleCalculationCells; } - return k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows; + return k_numberOfCalculationCells; } int CalculationController::typeAtLocation(int i, int j) { @@ -252,12 +261,12 @@ View * CalculationController::loadView() { for (int i = 0; i < k_maxNumberOfDisplayableRows; i++) { m_titleCells[i] = new EvenOddMessageTextCell(KDText::FontSize::Small); } - for (int i = 0; i < k_totalNumberOfDoubleBufferRows; i++) { + for (int i = 0; i < k_numberOfDoubleCalculationCells; i++) { m_doubleCalculationCells[i] = new EvenOddDoubleBufferTextCell(); m_doubleCalculationCells[i]->setTextColor(Palette::GreyDark); m_doubleCalculationCells[i]->setParentResponder(tableView); } - for (int i = 0; i < k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows;i++) { + for (int i = 0; i < k_numberOfCalculationCells;i++) { m_calculationCells[i] = new EvenOddBufferTextCell(KDText::FontSize::Small); m_calculationCells[i]->setTextColor(Palette::GreyDark); } @@ -269,11 +278,11 @@ void CalculationController::unloadView(View * view) { m_r2TitleCell = nullptr; delete m_columnTitleCell; m_columnTitleCell = nullptr; - for (int i = 0; i < k_totalNumberOfDoubleBufferRows; i++) { + for (int i = 0; i < k_numberOfDoubleCalculationCells; i++) { delete m_doubleCalculationCells[i]; m_doubleCalculationCells[i] = nullptr; } - for (int i = 0; i < k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows;i++) { + for (int i = 0; i < k_numberOfCalculationCells;i++) { delete m_calculationCells[i]; m_calculationCells[i] = nullptr; } diff --git a/apps/regression/calculation_controller.h b/apps/regression/calculation_controller.h index 2de5133d3..b0ff8d8a0 100644 --- a/apps/regression/calculation_controller.h +++ b/apps/regression/calculation_controller.h @@ -43,14 +43,16 @@ private: constexpr static int k_totalNumberOfColumns = 2; constexpr static int k_maxNumberOfDisplayableRows = 11; constexpr static int k_totalNumberOfDoubleBufferRows = 5; + constexpr static int k_numberOfDoubleCalculationCells = Store::k_numberOfSeries * k_totalNumberOfDoubleBufferRows; + constexpr static int k_numberOfCalculationCells = Store::k_numberOfSeries * k_totalNumberOfRows - k_numberOfDoubleCalculationCells; static constexpr KDCoordinate k_cellHeight = 25; static constexpr KDCoordinate k_cellWidth = Ion::Display::Width/2 - Metric::CommonRightMargin/2 - Metric::CommonLeftMargin/2; EvenOddMessageTextCell * m_titleCells[k_maxNumberOfDisplayableRows]; EvenOddExpressionCell * m_r2TitleCell; Poincare::ExpressionLayout * m_r2Layout; EvenOddDoubleBufferTextCell * m_columnTitleCell; - EvenOddDoubleBufferTextCell * m_doubleCalculationCells[k_totalNumberOfDoubleBufferRows]; - EvenOddBufferTextCell * m_calculationCells[k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows]; + EvenOddDoubleBufferTextCell * m_doubleCalculationCells[k_numberOfDoubleCalculationCells]; + EvenOddBufferTextCell * m_calculationCells[k_numberOfCalculationCells]; Store * m_store; }; diff --git a/apps/regression/go_to_parameter_controller.cpp b/apps/regression/go_to_parameter_controller.cpp index 0d521b97f..2b71431a1 100644 --- a/apps/regression/go_to_parameter_controller.cpp +++ b/apps/regression/go_to_parameter_controller.cpp @@ -43,16 +43,16 @@ bool GoToParameterController::setParameterAtIndex(int parameterIndex, double f) app()->displayWarning(I18n::Message::ForbiddenValue); return false; } - double x = m_store->xValueForYValue(f); + double x = m_store->xValueForYValue(m_graphController->selectedSeries(), f); if (m_xPrediction) { - x = m_store->yValueForXValue(f); + x = m_store->yValueForXValue(m_graphController->selectedSeries(), f); } if (std::fabs(x) > k_maxDisplayableFloat) { app()->displayWarning(I18n::Message::ForbiddenValue); return false; } if (std::isnan(x)) { - if (m_store->slope() < DBL_EPSILON && f == m_store->yIntercept()) { + if (m_store->slope(m_graphController->selectedSeries()) < DBL_EPSILON && f == m_store->yIntercept(m_graphController->selectedSeries())) { m_graphController->selectRegressionCurve(); m_cursor->moveTo(m_cursor->x(), f); return true; diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index 7abbd9416..625942ebf 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -16,7 +16,8 @@ GraphController::GraphController(Responder * parentResponder, ButtonRowControlle m_store(store), m_initialisationParameterController(this, m_store), m_predictionParameterController(this, m_store, m_cursor, this), - m_selectedDotIndex(selectedDotIndex) + m_selectedDotIndex(selectedDotIndex), + m_selectedSeries(0) // TODO -1 { m_store->setCursor(m_cursor); } @@ -76,7 +77,7 @@ void GraphController::reloadBannerView() { int legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; - if (*m_selectedDotIndex == m_store->numberOfPairs()) { + if (*m_selectedDotIndex == m_store->numberOfPairsOfSeries(m_selectedSeries)) { legend = I18n::translate(I18n::Message::MeanDot); legendLength = strlen(legend); strlcpy(buffer+numberOfChar, legend, legendLength+1); @@ -99,10 +100,10 @@ void GraphController::reloadBannerView() { legend = "x="; double x = m_cursor->x(); // Display a specific legend if the mean dot is selected - if (*m_selectedDotIndex == m_store->numberOfPairs()) { + if (*m_selectedDotIndex == m_store->numberOfPairsOfSeries(m_selectedSeries)) { constexpr static char legX[] = {Ion::Charset::XBar, '=', 0}; legend = legX; - x = m_store->meanOfColumn(0); + x = m_store->meanOfColumn(m_selectedSeries, 0); } legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); @@ -117,10 +118,10 @@ void GraphController::reloadBannerView() { numberOfChar = 0; legend = "y="; double y = m_cursor->y(); - if (*m_selectedDotIndex == m_store->numberOfPairs()) { + if (*m_selectedDotIndex == m_store->numberOfPairsOfSeries(m_selectedSeries)) { constexpr static char legY[] = {Ion::Charset::YBar, '=', 0}; legend = legY; - y = m_store->meanOfColumn(1); + y = m_store->meanOfColumn(m_selectedSeries, 1); } legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); @@ -134,7 +135,7 @@ void GraphController::reloadBannerView() { numberOfChar = 0; legend = " a="; - double slope = m_store->slope(); + double slope = m_store->slope(m_selectedSeries); legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; @@ -147,7 +148,7 @@ void GraphController::reloadBannerView() { numberOfChar = 0; legend = " b="; - double yIntercept = m_store->yIntercept(); + double yIntercept = m_store->yIntercept(m_selectedSeries); legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; @@ -160,7 +161,7 @@ void GraphController::reloadBannerView() { numberOfChar = 0; legend = " r="; - double r = m_store->correlationCoefficient(); + double r = m_store->correlationCoefficient(m_selectedSeries); legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; @@ -173,7 +174,7 @@ void GraphController::reloadBannerView() { numberOfChar = 0; legend = " r2="; - double r2 = m_store->squaredCorrelationCoefficient(); + double r2 = m_store->squaredCorrelationCoefficient(m_selectedSeries); legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; @@ -190,25 +191,26 @@ void GraphController::initRangeParameters() { } void GraphController::initCursorParameters() { - double x = m_store->meanOfColumn(0); - double y = m_store->meanOfColumn(1); + assert(m_selectedSeries >= 0); + double x = m_store->meanOfColumn(m_selectedSeries, 0); + double y = m_store->meanOfColumn(m_selectedSeries, 1); m_cursor->moveTo(x, y); m_store->panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); - *m_selectedDotIndex = m_store->numberOfPairs(); + *m_selectedDotIndex = m_store->numberOfPairsOfSeries(m_selectedSeries); } bool GraphController::moveCursorHorizontally(int direction) { if (*m_selectedDotIndex >= 0) { - int dotSelected = m_store->nextDot(direction, *m_selectedDotIndex); - if (dotSelected >= 0 && dotSelected < m_store->numberOfPairs()) { + int dotSelected = m_store->nextDot(m_selectedSeries, direction, *m_selectedDotIndex); + if (dotSelected >= 0 && dotSelected < m_store->numberOfPairsOfSeries(m_selectedSeries)) { *m_selectedDotIndex = dotSelected; - m_cursor->moveTo(m_store->get(0, *m_selectedDotIndex), m_store->get(1, *m_selectedDotIndex)); + m_cursor->moveTo(m_store->get(m_selectedSeries, 0, *m_selectedDotIndex), m_store->get(m_selectedSeries, 1, *m_selectedDotIndex)); m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); return true; } - if (dotSelected == m_store->numberOfPairs()) { + if (dotSelected == m_store->numberOfPairsOfSeries(m_selectedSeries)) { *m_selectedDotIndex = dotSelected; - m_cursor->moveTo(m_store->meanOfColumn(0), m_store->meanOfColumn(1)); + m_cursor->moveTo(m_store->meanOfColumn(m_selectedSeries, 0), m_store->meanOfColumn(m_selectedSeries, 1)); m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); return true; } @@ -216,14 +218,14 @@ bool GraphController::moveCursorHorizontally(int direction) { } double x = direction > 0 ? m_cursor->x() + m_store->xGridUnit()/k_numberOfCursorStepsInGradUnit : m_cursor->x() - m_store->xGridUnit()/k_numberOfCursorStepsInGradUnit; - double y = m_store->yValueForXValue(x); + double y = m_store->yValueForXValue(m_selectedSeries, x); m_cursor->moveTo(x, y); m_store->panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); return true; } bool GraphController::moveCursorVertically(int direction) { - double yRegressionCurve = m_store->yValueForXValue(m_cursor->x()); + double yRegressionCurve = m_store->yValueForXValue(m_selectedSeries, m_cursor->x()); if (*m_selectedDotIndex >= 0) { if ((yRegressionCurve - m_cursor->y() > 0) == (direction > 0)) { selectRegressionCurve(); @@ -234,17 +236,18 @@ bool GraphController::moveCursorVertically(int direction) { return false; } } else { - int dotSelected = m_store->closestVerticalDot(direction, m_cursor->x()); - if (dotSelected >= 0 && dotSelected <= m_store->numberOfPairs()) { + int selectedSeries = -1; + int dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), &selectedSeries); + if (dotSelected >= 0 && dotSelected <= m_store->numberOfPairsOfSeries(selectedSeries)) { m_view.setCursorView(&m_crossCursorView); - if (dotSelected == m_store->numberOfPairs()) { - *m_selectedDotIndex = dotSelected; - m_cursor->moveTo(m_store->meanOfColumn(0), m_store->meanOfColumn(1)); + m_selectedSeries = selectedSeries; + *m_selectedDotIndex = dotSelected; + if (dotSelected == m_store->numberOfPairsOfSeries(m_selectedSeries)) { + m_cursor->moveTo(m_store->meanOfColumn(m_selectedSeries, 0), m_store->meanOfColumn(m_selectedSeries, 1)); m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); return true; } - *m_selectedDotIndex = dotSelected; - m_cursor->moveTo(m_store->get(0, *m_selectedDotIndex), m_store->get(1, *m_selectedDotIndex)); + m_cursor->moveTo(m_store->get(m_selectedSeries, 0, *m_selectedDotIndex), m_store->get(m_selectedSeries, 1, *m_selectedDotIndex)); m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); return true; } diff --git a/apps/regression/graph_controller.h b/apps/regression/graph_controller.h index 234e5dd19..4d582b009 100644 --- a/apps/regression/graph_controller.h +++ b/apps/regression/graph_controller.h @@ -23,6 +23,7 @@ public: I18n::Message emptyMessage() override; void viewWillAppear() override; void selectRegressionCurve(); + int selectedSeries() const { return m_selectedSeries; } private: constexpr static float k_cursorTopMarginRatio = 0.07f; // (cursorHeight/2)/graphViewHeight constexpr static float k_cursorBottomMarginRatio = 0.3f; // (cursorHeight/2+bannerHeigh)/graphViewHeight @@ -49,6 +50,7 @@ private: /* The selectedDotIndex is -1 when no dot is selected, m_numberOfPairs when * the mean dot is selected and the dot index otherwise */ int * m_selectedDotIndex; + int m_selectedSeries; // TODO store in the Snapshot }; } diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index 7cfb5adbe..44d286b43 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -19,7 +19,7 @@ Store::Store() : /* Dots */ -int Store::closestVerticalDot(int direction, float x) { +int Store::closestVerticalDot(int direction, float x, int * nextSeries) { float nextX = INFINITY; float nextY = INFINITY; int selectedDot = -1; @@ -39,6 +39,7 @@ int Store::closestVerticalDot(int direction, float x) { nextX = m_data[series][0][index]; nextY = m_data[series][1][index]; selectedDot = index; + *nextSeries = series; } } } @@ -51,6 +52,7 @@ int Store::closestVerticalDot(int direction, float x) { ((meanY - yValueForXValue(series, meanX) >= 0) == (direction > 0))) { if (nextX != meanX || ((nextY - meanY >= 0) == (direction > 0))) { selectedDot = numberOfPairsOfSeries(series); + *nextSeries = series; } } diff --git a/apps/regression/store.h b/apps/regression/store.h index 25a5b41e9..8dc09ab9f 100644 --- a/apps/regression/store.h +++ b/apps/regression/store.h @@ -13,7 +13,7 @@ public: // Dots /* Return the closest dot to abscissa x above the regression curve if * direction > 0, below otherwise */ - int closestVerticalDot(int direction, float x); + int closestVerticalDot(int direction, float x, int * nextSeries); /* Return the closest dot to given dot, on the right if direction > 0, * on the left otherwise */ int nextDot(int series, int direction, int dot); @@ -51,7 +51,7 @@ private: }; typedef double (Store::*ArgCalculPointer)(int, int) const; -typedef double (Store::*CalculPointer)(); +typedef double (Store::*CalculPointer)(int) const; typedef void (Store::*RangeMethodPointer)(); } diff --git a/apps/regression/store_controller.cpp b/apps/regression/store_controller.cpp index 280689011..fec8d8569 100644 --- a/apps/regression/store_controller.cpp +++ b/apps/regression/store_controller.cpp @@ -16,12 +16,12 @@ StoreController::StoreController(Responder * parentResponder, Store * store, But Shared::StoreController(parentResponder, store, header), m_titleCells{} { - for (int i = 0; i < k_numberOfSeries; i++) { + for (int i = 0; i < Store::k_numberOfSeries; i++) { /* If the index is too big, the layout creation should take into account the * possibility of a two-digits index. */ - assert(k_numberOfSeries < 10); - m_titleLayout[k_numberOfColumnsPerSeries*i] = new HorizontalLayout(new CharLayout('X', KDText::FontSize::Small), new VerticalOffsetLayout(new CharLayout('0'+ i, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false), false); - m_titleLayout[k_numberOfColumnsPerSeries*i+1] = new HorizontalLayout(new CharLayout('Y', KDText::FontSize::Small), new VerticalOffsetLayout(new CharLayout('0' + i, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false), false); + assert(Store::k_numberOfSeries < 10); + m_titleLayout[Store::k_numberOfColumnsPerSeries*i] = new HorizontalLayout(new CharLayout('X', KDText::FontSize::Small), new VerticalOffsetLayout(new CharLayout('0'+ i, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false), false); + m_titleLayout[Store::k_numberOfColumnsPerSeries*i+1] = new HorizontalLayout(new CharLayout('Y', KDText::FontSize::Small), new VerticalOffsetLayout(new CharLayout('0' + i, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false), false); } } From 5c3ffecb9b5427e80b1424edf7c5b4acb958fe6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 14:45:47 +0200 Subject: [PATCH 102/156] [apps/shared] Take into account the separator width when setting margins --- apps/shared/store_cell.cpp | 2 +- apps/shared/store_controller.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/shared/store_cell.cpp b/apps/shared/store_cell.cpp index 57a62bbdf..b7179763e 100644 --- a/apps/shared/store_cell.cpp +++ b/apps/shared/store_cell.cpp @@ -1,5 +1,5 @@ #include "store_cell.h" -#include "escher/metric.h" +#include namespace Shared { diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index f5bd4971e..0dc5b2da7 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -2,6 +2,7 @@ #include "store_selectable_table_view.h" #include "../apps_container.h" #include "../constant.h" +#include #include using namespace Poincare; @@ -180,7 +181,7 @@ View * StoreController::loadView() { for (int i = 0; i < k_maxNumberOfEditableCells; i++) { m_editableCells[i] = new StoreCell(tableView, this, m_draftTextBuffer); } - tableView->setMargins(k_margin, k_scrollBarMargin, k_scrollBarMargin, k_margin); + tableView->setMargins(k_margin, k_scrollBarMargin, k_scrollBarMargin, k_margin - Metric::TableSeparatorThickness); return tableView; } From 5c8f9c60efb1ac209cf1b7abaa3e59ce1552002c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 14:56:27 +0200 Subject: [PATCH 103/156] [apps/shared] Simplify StoreTitleCell constructor --- apps/shared/store_title_cell.h | 4 ++-- apps/statistics/calculation_controller.cpp | 2 +- apps/statistics/store_controller.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/shared/store_title_cell.h b/apps/shared/store_title_cell.h index 823c09c35..51e3952bf 100644 --- a/apps/shared/store_title_cell.h +++ b/apps/shared/store_title_cell.h @@ -7,8 +7,8 @@ namespace Shared { class StoreTitleCell : public BufferFunctionTitleCell { public: - StoreTitleCell(Orientation orientation, KDText::FontSize size = KDText::FontSize::Large) : - BufferFunctionTitleCell(orientation, size), + StoreTitleCell() : + BufferFunctionTitleCell(Orientation::HorizontalIndicator, KDText::FontSize::Small), m_separatorLeft(false) {} void setSeparatorLeft(bool separator); diff --git a/apps/statistics/calculation_controller.cpp b/apps/statistics/calculation_controller.cpp index 269598328..a6a3f649b 100644 --- a/apps/statistics/calculation_controller.cpp +++ b/apps/statistics/calculation_controller.cpp @@ -201,7 +201,7 @@ Responder * CalculationController::tabController() const { View * CalculationController::loadView() { for (int i = 0; i < k_numberOfSeriesTitleCells; i++) { - m_seriesTitleCells[i] = new StoreTitleCell(FunctionTitleCell::Orientation::HorizontalIndicator, KDText::FontSize::Small); + m_seriesTitleCells[i] = new StoreTitleCell(); m_seriesTitleCells[i]->setSeparatorLeft(true); } for (int i = 0; i < k_numberOfCalculationTitleCells; i++) { diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index 24026b514..887f58fc6 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -55,7 +55,7 @@ bool StoreController::setDataAtLocation(double floatBody, int columnIndex, int r View * StoreController::loadView() { for (int i = 0; i < k_numberOfTitleCells; i++) { - m_titleCells[i] = new Shared::StoreTitleCell(FunctionTitleCell::Orientation::HorizontalIndicator, KDText::FontSize::Small); + m_titleCells[i] = new Shared::StoreTitleCell(); } return Shared::StoreController::loadView(); } From 4ac8b0ce93201c4b517833f99ff00def945c7303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 14:59:25 +0200 Subject: [PATCH 104/156] [apps/statistics] Clean StoreController::willDisplayCellAtLocation --- apps/statistics/store_controller.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index 887f58fc6..e4dbbc78e 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -22,11 +22,11 @@ void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int return; } Shared::StoreTitleCell * mytitleCell = static_cast(cell); - mytitleCell->setSeparatorLeft(i % Store::k_numberOfColumnsPerSeries == 0); + bool isValuesColumn = i%Store::k_numberOfColumnsPerSeries == 0; + mytitleCell->setSeparatorLeft(isValuesColumn); int seriesIndex = i/Store::k_numberOfColumnsPerSeries; - bool valuesColumn = i%Store::k_numberOfColumnsPerSeries == 0; assert(seriesIndex >= 0 && seriesIndex < FloatPairStore::k_numberOfSeries); - if (valuesColumn) { + if (isValuesColumn) { I18n::Message valuesMessages[] = {I18n::Message::Values1, I18n::Message::Values2, I18n::Message::Values3}; mytitleCell->setText(I18n::translate(valuesMessages[seriesIndex])); } else { From 0828525a6546808117d3ebe1a451a86fd6e116e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 15:06:25 +0200 Subject: [PATCH 105/156] [apps/regression] Change data titles --- apps/regression/store_controller.cpp | 27 ++++++++------------------- apps/regression/store_controller.h | 9 ++------- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/apps/regression/store_controller.cpp b/apps/regression/store_controller.cpp index fec8d8569..5c7227dd6 100644 --- a/apps/regression/store_controller.cpp +++ b/apps/regression/store_controller.cpp @@ -16,22 +16,6 @@ StoreController::StoreController(Responder * parentResponder, Store * store, But Shared::StoreController(parentResponder, store, header), m_titleCells{} { - for (int i = 0; i < Store::k_numberOfSeries; i++) { - /* If the index is too big, the layout creation should take into account the - * possibility of a two-digits index. */ - assert(Store::k_numberOfSeries < 10); - m_titleLayout[Store::k_numberOfColumnsPerSeries*i] = new HorizontalLayout(new CharLayout('X', KDText::FontSize::Small), new VerticalOffsetLayout(new CharLayout('0'+ i, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false), false); - m_titleLayout[Store::k_numberOfColumnsPerSeries*i+1] = new HorizontalLayout(new CharLayout('Y', KDText::FontSize::Small), new VerticalOffsetLayout(new CharLayout('0' + i, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false), false); - } -} - -StoreController::~StoreController() { - for (int i = 0; i < k_numberOfTitleCells; i++) { - if (m_titleLayout[i]) { - delete m_titleLayout[i]; - m_titleLayout[i] = nullptr; - } - } } void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { @@ -39,8 +23,13 @@ void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int if (cellAtLocationIsEditable(i, j)) { return; } - EvenOddExpressionCell * mytitleCell = (EvenOddExpressionCell *)cell; - mytitleCell->setExpressionLayout(m_titleLayout[i]); + Shared::StoreTitleCell * mytitleCell = static_cast(cell); + bool isValuesColumn = i%Store::k_numberOfColumnsPerSeries == 0; + mytitleCell->setSeparatorLeft(isValuesColumn); + int seriesIndex = i/Store::k_numberOfColumnsPerSeries; + mytitleCell->setColor(m_store->numberOfPairsOfSeries(seriesIndex) == 0 ? Palette::GreyDark : Store::colorOfSeriesAtIndex(seriesIndex)); // TODO Share GreyDark with graph/list_controller and statistics/store_controller + char name[] = {isValuesColumn ? 'X' : 'Y', static_cast('1' + seriesIndex), 0}; + mytitleCell->setText(name); } HighlightCell * StoreController::titleCells(int index) { @@ -50,7 +39,7 @@ HighlightCell * StoreController::titleCells(int index) { View * StoreController::loadView() { for (int i = 0; i < k_numberOfTitleCells; i++) { - m_titleCells[i] = new EvenOddExpressionCell(0.5f, 0.5f); + m_titleCells[i] = new Shared::StoreTitleCell(); } return Shared::StoreController::loadView(); } diff --git a/apps/regression/store_controller.h b/apps/regression/store_controller.h index 822f3a4ca..f8720c4a2 100644 --- a/apps/regression/store_controller.h +++ b/apps/regression/store_controller.h @@ -4,24 +4,19 @@ #include #include "store.h" #include "../shared/store_controller.h" +#include "../shared/store_title_cell.h" namespace Regression { class StoreController : public Shared::StoreController { public: StoreController(Responder * parentResponder, Store * store, ButtonRowController * header); - ~StoreController(); - StoreController(const StoreController& other) = delete; - StoreController(StoreController&& other) = delete; - StoreController& operator=(const StoreController& other) = delete; - StoreController& operator=(StoreController&& other) = delete; void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; private: HighlightCell * titleCells(int index) override; View * loadView() override; void unloadView(View * view) override; - EvenOddExpressionCell * m_titleCells[k_numberOfTitleCells]; - Poincare::ExpressionLayout * m_titleLayout[k_numberOfTitleCells]; + Shared::StoreTitleCell * m_titleCells[k_numberOfTitleCells]; }; } From 7f71faf0c25c9d3c8f1b4e751bbcca0ae62f9c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 15:18:37 +0200 Subject: [PATCH 106/156] [apps/regression] Fix GraphController::isEmpty --- apps/regression/graph_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index 625942ebf..6f1bb4bb5 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -31,7 +31,7 @@ bool GraphController::isEmpty() const { return true; } for (int series = 0; series < FloatPairStore::k_numberOfSeries; series++) { - if (!m_store->seriesIsEmpty(series) && !std::isinf(m_store->slope(series)) && (std::isnan(m_store->slope(series)))) { + if (!m_store->seriesIsEmpty(series) && !std::isinf(m_store->slope(series)) && !std::isnan(m_store->slope(series))) { return false; } } From 32e00b5e1e52d7af586675aef45b7e1c02dad239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 15:31:50 +0200 Subject: [PATCH 107/156] [apps/reg] Init the graph selected series at cursor initialization --- apps/regression/graph_controller.cpp | 4 ++-- apps/regression/store.cpp | 29 +++++++++++++++++++++++++++- apps/regression/store.h | 2 ++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index 6f1bb4bb5..96a4b0727 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -17,7 +17,7 @@ GraphController::GraphController(Responder * parentResponder, ButtonRowControlle m_initialisationParameterController(this, m_store), m_predictionParameterController(this, m_store, m_cursor, this), m_selectedDotIndex(selectedDotIndex), - m_selectedSeries(0) // TODO -1 + m_selectedSeries(-1) { m_store->setCursor(m_cursor); } @@ -191,7 +191,7 @@ void GraphController::initRangeParameters() { } void GraphController::initCursorParameters() { - assert(m_selectedSeries >= 0); + m_selectedSeries = m_store->indexOfKthNonEmptySeries(0); double x = m_store->meanOfColumn(m_selectedSeries, 0); double y = m_store->meanOfColumn(m_selectedSeries, 1); m_cursor->moveTo(x, y); diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index 44d286b43..7675f24d8 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -137,7 +137,7 @@ void Store::setDefault() { } bool Store::isEmpty() const { - for (int i = 0; i < k_numberOfSeries; i ++) { + for (int i = 0; i < k_numberOfSeries; i++) { if (!seriesIsEmpty(i)) { return false; } @@ -145,10 +145,37 @@ bool Store::isEmpty() const { return true; } +int Store::numberOfNonEmptySeries() const { + // TODO Share with stats in FLoatPairStore + int nonEmptySeriesCount = 0; + for (int i = 0; i< k_numberOfSeries; i++) { + if (!seriesIsEmpty(i)) { + nonEmptySeriesCount++; + } + } + return nonEmptySeriesCount; +} + bool Store::seriesIsEmpty(int series) const { return numberOfPairsOfSeries(series) < 2; } +int Store::indexOfKthNonEmptySeries(int k) const { + // TODO put in FloatPairStore (it is also in stats/store) + assert(k >= 0 && k < numberOfNonEmptySeries()); + int nonEmptySeriesCount = 0; + for (int i = 0; i < k_numberOfSeries; i++) { + if (!seriesIsEmpty(i)) { + if (nonEmptySeriesCount == k) { + return i; + } + nonEmptySeriesCount++; + } + } + assert(false); + return 0; +} + /* Calculations */ double Store::doubleCastedNumberOfPairsOfSeries(int series) const { diff --git a/apps/regression/store.h b/apps/regression/store.h index 8dc09ab9f..b1c3a9400 100644 --- a/apps/regression/store.h +++ b/apps/regression/store.h @@ -23,7 +23,9 @@ public: // Series bool isEmpty() const; + int numberOfNonEmptySeries() const; bool seriesIsEmpty(int series) const; + int indexOfKthNonEmptySeries(int k) const; // Calculation double doubleCastedNumberOfPairsOfSeries(int series) const; From ab346eba79f77eab3f8c3e27e33ca25a6283f0f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 15:43:35 +0200 Subject: [PATCH 108/156] [apps/regression] Display all the non empty regression series --- apps/regression/graph_view.cpp | 28 ++++++++++++++++------------ apps/regression/graph_view.h | 1 - 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/apps/regression/graph_view.cpp b/apps/regression/graph_view.cpp index ae70ba10d..6088ab40e 100644 --- a/apps/regression/graph_view.cpp +++ b/apps/regression/graph_view.cpp @@ -9,8 +9,7 @@ GraphView::GraphView(Store * store, CurveViewCursor * cursor, BannerView * banne CurveView(store, cursor, bannerView, cursorView), m_store(store), m_xLabels{}, - m_yLabels{}, - m_series(0) //TODO + m_yLabels{} { } @@ -21,17 +20,22 @@ void GraphView::drawRect(KDContext * ctx, KDRect rect) const { drawAxes(ctx, rect, Axis::Vertical); drawLabels(ctx, rect, Axis::Horizontal, true); drawLabels(ctx, rect, Axis::Vertical, true); - float regressionParameters[2] = {(float)m_store->slope(m_series), (float)m_store->yIntercept(m_series)}; - drawCurve(ctx, rect, [](float abscissa, void * model, void * context) { - float * params = (float *)model; - return params[0]*abscissa+params[1]; - }, - regressionParameters, nullptr, Palette::YellowDark); - for (int index = 0; index < m_store->numberOfPairs(); index++) { - drawDot(ctx, rect, m_store->get(m_series, 0,index), m_store->get(m_series, 1,index), Palette::Red); + for (int series = 0; series < Store::k_numberOfSeries; series++) { + if (!m_store->seriesIsEmpty(series)) { + KDColor color = Palette::DataColor[series]; + float regressionParameters[2] = {(float)m_store->slope(series), (float)m_store->yIntercept(series)}; + drawCurve(ctx, rect, [](float abscissa, void * model, void * context) { + float * params = (float *)model; + return params[0]*abscissa+params[1]; + }, + regressionParameters, nullptr, color); + for (int index = 0; index < m_store->numberOfPairsOfSeries(series); index++) { + drawDot(ctx, rect, m_store->get(series, 0, index), m_store->get(series, 1, index), color); + } + drawDot(ctx, rect, m_store->meanOfColumn(series, 0), m_store->meanOfColumn(series, 1), color, true); + drawDot(ctx, rect, m_store->meanOfColumn(series, 0), m_store->meanOfColumn(series, 1), KDColorWhite); + } } - drawDot(ctx, rect, m_store->meanOfColumn(m_series, 0), m_store->meanOfColumn(m_series, 1), Palette::Palette::YellowDark, true); - drawDot(ctx, rect, m_store->meanOfColumn(m_series, 0), m_store->meanOfColumn(m_series, 1), KDColorWhite); } char * GraphView::label(Axis axis, int index) const { diff --git a/apps/regression/graph_view.h b/apps/regression/graph_view.h index 76eebff34..de60b5e91 100644 --- a/apps/regression/graph_view.h +++ b/apps/regression/graph_view.h @@ -17,7 +17,6 @@ private: Store * m_store; char m_xLabels[k_maxNumberOfXLabels][Poincare::PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits)]; char m_yLabels[k_maxNumberOfYLabels][Poincare::PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits)]; - int m_series; }; } From 060d2a7c4327f080d0e47d2d30e54f81477890b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 30 May 2018 16:43:40 +0200 Subject: [PATCH 109/156] [apps/regression] Fix graph cursor navigation --- apps/regression/store.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index 7675f24d8..3df320aa2 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -90,7 +90,7 @@ int Store::nextDot(int series, int direction, int dot) { if (std::fabs(meanX - x) < std::fabs(nextX - x) && (numberOfPairsOfSeries(series) != dot) && (meanX >= x)) { - if (meanX != x || (x > dot)) { + if (meanX != x || (numberOfPairsOfSeries(series) > dot)) { selectedDot = numberOfPairsOfSeries(series); } } @@ -99,7 +99,7 @@ int Store::nextDot(int series, int direction, int dot) { if (std::fabs(meanX - x) < std::fabs(nextX - x) && (numberOfPairsOfSeries(series) != dot) && (meanX <= x)) { - if (meanX != x || (numberOfPairsOfSeries(series) < dot)) { + if ((meanX != x) || (numberOfPairsOfSeries(series) < dot)) { nextX = meanX; selectedDot = numberOfPairsOfSeries(series); } From 0c2c998362a87705adfdbac6ac998d5791b9fd49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 31 May 2018 11:25:43 +0200 Subject: [PATCH 110/156] [apps/regression] Navigation between different series in graph --- apps/regression/graph_controller.cpp | 89 +++++++++++++++++++------- apps/regression/store.cpp | 95 +++++++++++++++++++--------- apps/regression/store.h | 9 ++- 3 files changed, 140 insertions(+), 53 deletions(-) diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index 96a4b0727..da596897c 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -225,34 +225,79 @@ bool GraphController::moveCursorHorizontally(int direction) { } bool GraphController::moveCursorVertically(int direction) { - double yRegressionCurve = m_store->yValueForXValue(m_selectedSeries, m_cursor->x()); - if (*m_selectedDotIndex >= 0) { - if ((yRegressionCurve - m_cursor->y() > 0) == (direction > 0)) { - selectRegressionCurve(); - m_cursor->moveTo(m_cursor->x(), yRegressionCurve); - m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); - return true; - } else { - return false; - } + volatile int closestRegressionSeries = -1; + int closestDotSeries = -1; + volatile int dotSelected = -1; + + if (*m_selectedDotIndex == -1) { + // The current cursor is on a regression + // Check the closest regression + closestRegressionSeries = m_store->closestVerticalRegression(direction, m_cursor->x(), m_cursor->y(), m_selectedSeries); + // Check the closest dot + dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), direction > 0 ? -FLT_MAX : FLT_MAX, m_selectedSeries, *m_selectedDotIndex, &closestDotSeries); } else { - int selectedSeries = -1; - int dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), &selectedSeries); - if (dotSelected >= 0 && dotSelected <= m_store->numberOfPairsOfSeries(selectedSeries)) { - m_view.setCursorView(&m_crossCursorView); - m_selectedSeries = selectedSeries; - *m_selectedDotIndex = dotSelected; - if (dotSelected == m_store->numberOfPairsOfSeries(m_selectedSeries)) { - m_cursor->moveTo(m_store->meanOfColumn(m_selectedSeries, 0), m_store->meanOfColumn(m_selectedSeries, 1)); - m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); - return true; + // The current cursor is on a dot + // Check the closest regression + closestRegressionSeries = m_store->closestVerticalRegression(direction, m_cursor->x(), m_cursor->y(), -1); + // Check the closest dot + dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), m_cursor->y(), m_selectedSeries, *m_selectedDotIndex, &closestDotSeries); + } + + bool validRegression = closestRegressionSeries > -1; + bool validDot = dotSelected >= 0 && dotSelected <= m_store->numberOfPairsOfSeries(closestDotSeries); + + if (validRegression && validDot) { + /* Compare the abscissa distances to select either the dot or the + * regression. If they are equal, compare the ordinate distances. */ + double dotDistanceX = -1; + if (dotSelected == m_store->numberOfPairsOfSeries(closestDotSeries)) { + dotDistanceX = std::fabs(m_store->meanOfColumn(closestDotSeries, 0) - m_cursor->x()); + } else { + dotDistanceX = std::fabs(m_store->get(closestDotSeries, 0, dotSelected) - m_cursor->x()); + } + if (dotDistanceX != 0) { + /* The regression X distance to the point is 0, so it is closer than the + * dot. */ + validDot = false; + } else { + // Compare the y distances + double regressionDistanceY = std::fabs(m_store->yValueForXValue(closestRegressionSeries, m_cursor->x()) - m_cursor->y()); + double dotDistanceY = -1; + if (dotSelected == m_store->numberOfPairsOfSeries(closestDotSeries)) { + dotDistanceY = std::fabs(m_store->meanOfColumn(closestDotSeries, 1) - m_cursor->y()); + } else { + dotDistanceY = std::fabs(m_store->get(closestDotSeries, 1, dotSelected) - m_cursor->y()); } - m_cursor->moveTo(m_store->get(m_selectedSeries, 0, *m_selectedDotIndex), m_store->get(m_selectedSeries, 1, *m_selectedDotIndex)); + if (regressionDistanceY <= dotDistanceY) { + validDot = false; + } else { + validRegression = false; + } + } + } + if (!validDot && validRegression) { + // Select the regression + m_selectedSeries = closestRegressionSeries; + selectRegressionCurve(); + m_cursor->moveTo(m_cursor->x(), m_store->yValueForXValue(m_selectedSeries, m_cursor->x())); + m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); + return true; + } + + if (validDot && !validRegression) { + m_view.setCursorView(&m_crossCursorView); + m_selectedSeries = closestDotSeries; + *m_selectedDotIndex = dotSelected; + if (dotSelected == m_store->numberOfPairsOfSeries(m_selectedSeries)) { + m_cursor->moveTo(m_store->meanOfColumn(m_selectedSeries, 0), m_store->meanOfColumn(m_selectedSeries, 1)); m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); return true; } - return false; + m_cursor->moveTo(m_store->get(m_selectedSeries, 0, *m_selectedDotIndex), m_store->get(m_selectedSeries, 1, *m_selectedDotIndex)); + m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); + return true; } + return false; } uint32_t GraphController::modelVersion() { diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index 3df320aa2..86a6acc28 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -17,45 +17,80 @@ Store::Store() : { } +/* Regressions */ + +int Store::closestVerticalRegression(int direction, float x, float y, int currentRegressionSeries) { + int regressionSeries = -1; + float closestDistance = INFINITY; + /* The conditions to test on all the regressions are in this order: + * - the current regression is not the current regression + * - the next regression point should be within the window abscissa bounds + * - it is the closest one in abscissa to x + * - it is above y if direction > 0 and below otherwise */ + for (int series = 0; series < k_numberOfSeries; series ++) { + if (!seriesIsEmpty(series) && series != currentRegressionSeries) { + double regressionY = yValueForXValue(series, x); + if ((m_yMin <= regressionY && regressionY <= m_yMax) + && (std::fabs(regressionY - y) < closestDistance) + && (regressionY - y > 0) == (direction > 0)) { + closestDistance = std::fabs(regressionY - y); + regressionSeries = series; + } + } + } + return regressionSeries; +} + /* Dots */ -int Store::closestVerticalDot(int direction, float x, int * nextSeries) { +int Store::closestVerticalDot(int direction, float x, float y, int currentSeries, int currentDot, int * nextSeries) { float nextX = INFINITY; float nextY = INFINITY; int selectedDot = -1; /* The conditions to test on all dots are in this order: - * - the next dot should be within the window abscissa bounds - * - the next dot is the closest one in abscissa to x - * - the next dot is above the regression curve if direction == 1 and below - * otherwise */ + * - if the currentDot is valid, the next series should not be the current series + * - the next dot should not be the current dot + * - the next dot should be within the window abscissa bounds + * - the next dot is the closest one in abscissa to x + * - the next dot is above the regression curve if direction == 1 and below + * otherwise + * - the next dot is above/under y + * - if the current dot is valid, do not select a dot of the same series */ for (int series = 0; series < k_numberOfSeries; series ++) { - if (!seriesIsEmpty(series)) { + if (!seriesIsEmpty(series) && (currentDot < 0 || currentSeries != series)) { for (int index = 0; index < numberOfPairsOfSeries(series); index++) { - if ((m_xMin <= m_data[series][0][index] && m_data[series][0][index] <= m_xMax) && - (std::fabs(m_data[series][0][index] - x) < std::fabs(nextX - x)) && - ((m_data[series][1][index] - yValueForXValue(series, m_data[series][0][index]) >= 0) == (direction > 0))) { - // Handle edge case: if 2 dots have the same abscissa but different ordinates - if (nextX != m_data[series][0][index] || ((nextY - m_data[series][1][index] >= 0) == (direction > 0))) { - nextX = m_data[series][0][index]; - nextY = m_data[series][1][index]; - selectedDot = index; + if ((currentSeries != series) || (index != currentDot)) { + double currentDataX = m_data[series][0][index]; + double currentDataY = m_data[series][1][index]; + if ((m_xMin <= currentDataX && currentDataX <= m_xMax) && + (std::fabs(currentDataX - x) <= std::fabs(nextX - x)) && + ((currentDataY - yValueForXValue(currentSeries, currentDataX) >= 0) == (direction > 0)) && + ((currentDataY > y) == (direction > 0))) { + // Handle edge case: if 2 dots have the same abscissa but different ordinates + if (nextX != currentDataX || ((nextY - currentDataY >= 0) == (direction > 0))) { + nextX = currentDataX; + nextY = currentDataY; + selectedDot = index; + *nextSeries = series; + } + } + } + } + // Compare with the mean dot + if ((currentSeries != series) || (numberOfPairsOfSeries(series) != currentDot)) { + double meanX = meanOfColumn(series, 0); + double meanY = meanOfColumn(series, 1); + if (m_xMin <= meanX && meanX <= m_xMax && + (std::fabs(meanX - x) <= std::fabs(nextX - x)) && + ((meanY - yValueForXValue(currentSeries, meanX) >= 0) == (direction > 0)) && + ((meanY > y) == (direction > 0))) { + if (nextX != meanX || ((nextY - meanY >= 0) == (direction > 0))) { + selectedDot = numberOfPairsOfSeries(series); *nextSeries = series; } } } } - // Compare with the mean dot - double meanX = meanOfColumn(series, 0); - double meanY = meanOfColumn(series, 1); - if (m_xMin <= meanX && meanX <= m_xMax && - (std::fabs(meanX - x) < std::fabs(nextX - x)) && - ((meanY - yValueForXValue(series, meanX) >= 0) == (direction > 0))) { - if (nextX != meanX || ((nextY - meanY >= 0) == (direction > 0))) { - selectedDot = numberOfPairsOfSeries(series); - *nextSeries = series; - } - } - } return selectedDot; } @@ -88,8 +123,8 @@ int Store::nextDot(int series, int direction, int dot) { } // Compare with the mean dot if (std::fabs(meanX - x) < std::fabs(nextX - x) && - (numberOfPairsOfSeries(series) != dot) && - (meanX >= x)) { + (numberOfPairsOfSeries(series) != dot) && + (meanX >= x)) { if (meanX != x || (numberOfPairsOfSeries(series) > dot)) { selectedDot = numberOfPairsOfSeries(series); } @@ -97,8 +132,8 @@ int Store::nextDot(int series, int direction, int dot) { } else { // Compare with the mean dot if (std::fabs(meanX - x) < std::fabs(nextX - x) && - (numberOfPairsOfSeries(series) != dot) && - (meanX <= x)) { + (numberOfPairsOfSeries(series) != dot) && + (meanX <= x)) { if ((meanX != x) || (numberOfPairsOfSeries(series) < dot)) { nextX = meanX; selectedDot = numberOfPairsOfSeries(series); diff --git a/apps/regression/store.h b/apps/regression/store.h index b1c3a9400..ed1d7d206 100644 --- a/apps/regression/store.h +++ b/apps/regression/store.h @@ -3,6 +3,9 @@ #include "../shared/interactive_curve_view_range.h" #include "../shared/float_pair_store.h" +extern "C" { +#include +} namespace Regression { @@ -10,10 +13,14 @@ class Store : public Shared::InteractiveCurveViewRange, public Shared::FloatPair public: Store(); + // Regression + /* Return the series index of the closest regression at abscissa x, above + * ordinate y if direction > 0, below otherwise */ + int closestVerticalRegression(int direction, float x, float y, int currentRegressionSeries); // Dots /* Return the closest dot to abscissa x above the regression curve if * direction > 0, below otherwise */ - int closestVerticalDot(int direction, float x, int * nextSeries); + int closestVerticalDot(int direction, float x, float y, int currentSeries, int currentDot, int * nextSeries); /* Return the closest dot to given dot, on the right if direction > 0, * on the left otherwise */ int nextDot(int series, int direction, int dot); From 0a2a06edbb65667dc77f47bcae711696c95e3583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 31 May 2018 11:55:49 +0200 Subject: [PATCH 111/156] [apps/reg] Display deveral columns in stats --- apps/regression/calculation_controller.cpp | 53 ++++++++++++---------- apps/regression/calculation_controller.h | 11 ++++- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index 170391962..f63f7628e 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -17,7 +17,7 @@ CalculationController::CalculationController(Responder * parentResponder, Button ButtonRowDelegate(header, nullptr), m_titleCells{}, m_r2TitleCell(nullptr), - m_columnTitleCell(nullptr), + m_columnTitleCells{}, m_doubleCalculationCells{}, m_calculationCells{}, m_store(store) @@ -83,7 +83,7 @@ void CalculationController::tableViewDidChangeSelection(SelectableTableView * t, t->selectCellAtLocation(previousSelectedCellX, previousSelectedCellY); } } - if (t->selectedColumn() == 1 && t->selectedRow() >= 0 && t->selectedRow() <= k_totalNumberOfDoubleBufferRows) { + if (t->selectedColumn() > 0 && t->selectedRow() >= 0 && t->selectedRow() <= k_totalNumberOfDoubleBufferRows) { EvenOddDoubleBufferTextCell * myCell = (EvenOddDoubleBufferTextCell *)t->selectedCell(); bool firstSubCellSelected = true; if (previousSelectedCellX == 1 && previousSelectedCellY >= 0 && previousSelectedCellY <= k_totalNumberOfDoubleBufferRows) { @@ -192,59 +192,60 @@ KDCoordinate CalculationController::rowHeight(int j) { } HighlightCell * CalculationController::reusableCell(int index, int type) { - if (type == 0) { - assert(index < k_maxNumberOfDisplayableRows); + if (type == k_standardCalculationTitleCellType) { + assert(index >= 0 && index < k_maxNumberOfDisplayableRows); assert(m_titleCells[index] != nullptr); return m_titleCells[index]; } - if (type == 1) { + if (type == k_r2CellType) { assert(index == 0); return m_r2TitleCell; } - if (type == 2) { - assert(index == 0); - return m_columnTitleCell; + if (type == k_columnTitleCellType) { + assert(index >= 0 && index < Store::k_numberOfSeries); + return m_columnTitleCells[index]; } - if (type == 3) { - assert(index < k_numberOfDoubleCalculationCells); + if (type == k_doubleBufferCalculationCellType) { + assert(index >= 0 && index < k_numberOfDoubleCalculationCells); assert(m_doubleCalculationCells[index] != nullptr); return m_doubleCalculationCells[index]; } - assert(index < k_numberOfCalculationCells); + assert(index >= 0 && index < k_numberOfCalculationCells); assert(m_calculationCells[index] != nullptr); return m_calculationCells[index]; } int CalculationController::reusableCellCount(int type) { - if (type == 0) { + if (type == k_standardCalculationTitleCellType) { return k_maxNumberOfDisplayableRows; } - if (type == 1) { + if (type == k_r2CellType) { return 1; } - if (type == 2) { - return 1; + if (type == k_columnTitleCellType) { + return Store::k_numberOfSeries; } - if (type == 3) { + if (type == k_doubleBufferCalculationCellType) { return k_numberOfDoubleCalculationCells; } + assert(type == k_standardCalculationCellType); return k_numberOfCalculationCells; } int CalculationController::typeAtLocation(int i, int j) { if (i == 0 && j == k_totalNumberOfRows-1) { - return 1; + return k_r2CellType; } if (i == 0) { - return 0; + return k_standardCalculationTitleCellType; } if (j == 0) { - return 2; + return k_columnTitleCellType; } if (j > 0 && j <= k_totalNumberOfDoubleBufferRows) { - return 3; + return k_doubleBufferCalculationCellType; } - return 4; + return k_standardCalculationCellType; } Responder * CalculationController::tabController() const { @@ -257,7 +258,9 @@ View * CalculationController::loadView() { tableView->setBackgroundColor(Palette::WallScreenDark); ; m_r2TitleCell = new EvenOddExpressionCell(1.0f, 0.5f); - m_columnTitleCell = new EvenOddDoubleBufferTextCell(tableView); + for (int i = 0; i < Store::k_numberOfSeries; i++) { + m_columnTitleCells[i] = new EvenOddDoubleBufferTextCell(tableView); + } for (int i = 0; i < k_maxNumberOfDisplayableRows; i++) { m_titleCells[i] = new EvenOddMessageTextCell(KDText::FontSize::Small); } @@ -276,8 +279,10 @@ View * CalculationController::loadView() { void CalculationController::unloadView(View * view) { delete m_r2TitleCell; m_r2TitleCell = nullptr; - delete m_columnTitleCell; - m_columnTitleCell = nullptr; + for (int i = 0; i < Store::k_numberOfSeries; i++) { + delete m_columnTitleCells[i]; + m_columnTitleCells[i] = nullptr; + } for (int i = 0; i < k_numberOfDoubleCalculationCells; i++) { delete m_doubleCalculationCells[i]; m_doubleCalculationCells[i] = nullptr; diff --git a/apps/regression/calculation_controller.h b/apps/regression/calculation_controller.h index b0ff8d8a0..07c68effb 100644 --- a/apps/regression/calculation_controller.h +++ b/apps/regression/calculation_controller.h @@ -40,17 +40,24 @@ private: View * loadView() override; void unloadView(View * view) override; constexpr static int k_totalNumberOfRows = 14; - constexpr static int k_totalNumberOfColumns = 2; + constexpr static int k_totalNumberOfColumns = 1 + Store::k_numberOfSeries;; constexpr static int k_maxNumberOfDisplayableRows = 11; constexpr static int k_totalNumberOfDoubleBufferRows = 5; constexpr static int k_numberOfDoubleCalculationCells = Store::k_numberOfSeries * k_totalNumberOfDoubleBufferRows; constexpr static int k_numberOfCalculationCells = Store::k_numberOfSeries * k_totalNumberOfRows - k_numberOfDoubleCalculationCells; + + constexpr static int k_standardCalculationTitleCellType = 0; + constexpr static int k_r2CellType = 1; + constexpr static int k_columnTitleCellType = 2; + constexpr static int k_doubleBufferCalculationCellType = 3; + constexpr static int k_standardCalculationCellType = 4; + static constexpr KDCoordinate k_cellHeight = 25; static constexpr KDCoordinate k_cellWidth = Ion::Display::Width/2 - Metric::CommonRightMargin/2 - Metric::CommonLeftMargin/2; EvenOddMessageTextCell * m_titleCells[k_maxNumberOfDisplayableRows]; EvenOddExpressionCell * m_r2TitleCell; Poincare::ExpressionLayout * m_r2Layout; - EvenOddDoubleBufferTextCell * m_columnTitleCell; + EvenOddDoubleBufferTextCell * m_columnTitleCells[Store::k_numberOfSeries]; EvenOddDoubleBufferTextCell * m_doubleCalculationCells[k_numberOfDoubleCalculationCells]; EvenOddBufferTextCell * m_calculationCells[k_numberOfCalculationCells]; Store * m_store; From 4470a9c51e24361f14a2607ff9b8621d2bb74f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 31 May 2018 12:00:12 +0200 Subject: [PATCH 112/156] [apps/reg] Display correct data in stats --- apps/regression/calculation_controller.cpp | 14 +++++++------- apps/regression/calculation_controller.h | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index f63f7628e..78cfdddaa 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -114,7 +114,7 @@ int CalculationController::numberOfRows() { } int CalculationController::numberOfColumns() { - return k_totalNumberOfColumns; + return 1 + m_store->numberOfNonEmptySeries(); } void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { @@ -140,21 +140,21 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int return; } - int seriesNumber = i - 1;; + int seriesNumber = m_store->indexOfKthNonEmptySeries(i - 1); assert(i >= 0 && seriesNumber < FloatPairStore::k_numberOfSeries); // Coordinate and series title if (j == 0 && i > 0) { EvenOddDoubleBufferTextCell * myCell = (EvenOddDoubleBufferTextCell *)cell; - char buffer[] = {'x', static_cast('0' + seriesNumber), 0}; + char buffer[] = {'X', static_cast('0' + seriesNumber), 0}; myCell->setFirstText(buffer); - buffer[0] = 'y'; + buffer[0] = 'Y'; myCell->setSecondText(buffer); return; } // Calculation cell - if (i == 1 && j > 0 && j <= k_totalNumberOfDoubleBufferRows) { + if (i > 0 && j > 0 && j <= k_totalNumberOfDoubleBufferRows) { ArgCalculPointer calculationMethods[k_totalNumberOfDoubleBufferRows] = {&Store::meanOfColumn, &Store::sumOfColumn, &Store::squaredValueSumOfColumn, &Store::standardDeviationOfColumn, &Store::varianceOfColumn}; double calculation1 = (m_store->*calculationMethods[j-1])(seriesNumber, 0); double calculation2 = (m_store->*calculationMethods[j-1])(seriesNumber, 1); @@ -166,12 +166,12 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int myCell->setSecondText(buffer); return; } - if (i == 1 && j == 9) { + if (i > 0 && j == 9) { EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)cell; myCell->setText("ax+b"); return; } - if (i == 1 && j > k_totalNumberOfDoubleBufferRows) { + if (i > 0 && j > k_totalNumberOfDoubleBufferRows) { assert(j != 9); CalculPointer calculationMethods[k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows] = {&Store::doubleCastedNumberOfPairsOfSeries, &Store::covariance, &Store::columnProductSum, nullptr, &Store::slope, &Store::yIntercept, &Store::correlationCoefficient, &Store::squaredCorrelationCoefficient}; double calculation = (m_store->*calculationMethods[j-k_totalNumberOfDoubleBufferRows-1])(seriesNumber); diff --git a/apps/regression/calculation_controller.h b/apps/regression/calculation_controller.h index 07c68effb..382127586 100644 --- a/apps/regression/calculation_controller.h +++ b/apps/regression/calculation_controller.h @@ -40,7 +40,6 @@ private: View * loadView() override; void unloadView(View * view) override; constexpr static int k_totalNumberOfRows = 14; - constexpr static int k_totalNumberOfColumns = 1 + Store::k_numberOfSeries;; constexpr static int k_maxNumberOfDisplayableRows = 11; constexpr static int k_totalNumberOfDoubleBufferRows = 5; constexpr static int k_numberOfDoubleCalculationCells = Store::k_numberOfSeries * k_totalNumberOfDoubleBufferRows; From 6f71ff8a2cc89f2e740747dd1befe52ac60d0cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 31 May 2018 14:03:38 +0200 Subject: [PATCH 113/156] [apps/reg] Add separators in Stats --- apps/regression/Makefile | 2 +- apps/regression/calculation_controller.cpp | 26 ++--- apps/regression/calculation_controller.h | 13 ++- .../even_odd_double_buffer_text_cell.cpp | 100 ---------------- ...double_buffer_text_cell_with_separator.cpp | 109 ++++++++++++++++++ ..._double_buffer_text_cell_with_separator.h} | 9 +- apps/shared/Makefile | 4 +- .../margin_even_odd_message_text_cell.cpp | 10 ++ .../margin_even_odd_message_text_cell.h | 20 ++++ .../separator_even_odd_buffer_text_cell.cpp} | 12 +- .../separator_even_odd_buffer_text_cell.h} | 8 +- apps/statistics/Makefile | 2 - apps/statistics/calculation_controller.cpp | 6 +- apps/statistics/calculation_controller.h | 6 +- apps/statistics/calculation_title_cell.cpp | 10 -- apps/statistics/calculation_title_cell.h | 20 ---- 16 files changed, 186 insertions(+), 171 deletions(-) delete mode 100644 apps/regression/even_odd_double_buffer_text_cell.cpp create mode 100644 apps/regression/even_odd_double_buffer_text_cell_with_separator.cpp rename apps/regression/{even_odd_double_buffer_text_cell.h => even_odd_double_buffer_text_cell_with_separator.h} (68%) create mode 100644 apps/shared/margin_even_odd_message_text_cell.cpp create mode 100644 apps/shared/margin_even_odd_message_text_cell.h rename apps/{statistics/calculation_cell.cpp => shared/separator_even_odd_buffer_text_cell.cpp} (62%) rename apps/{statistics/calculation_cell.h => shared/separator_even_odd_buffer_text_cell.h} (60%) delete mode 100644 apps/statistics/calculation_title_cell.cpp delete mode 100644 apps/statistics/calculation_title_cell.h diff --git a/apps/regression/Makefile b/apps/regression/Makefile index c35c184a3..60595c5fc 100644 --- a/apps/regression/Makefile +++ b/apps/regression/Makefile @@ -5,7 +5,7 @@ app_objs += $(addprefix apps/regression/,\ app.o\ banner_view.o\ calculation_controller.o\ - even_odd_double_buffer_text_cell.o\ + even_odd_double_buffer_text_cell_with_separator.o\ go_to_parameter_controller.o\ graph_controller.o\ graph_view.o\ diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index 78cfdddaa..3bd262ee5 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -44,14 +44,14 @@ bool CalculationController::handleEvent(Ion::Events::Event event) { } if (event == Ion::Events::Copy && selectedColumn() == 1 && selectedRow() > 0) { if (selectedRow() <= k_totalNumberOfDoubleBufferRows) { - EvenOddDoubleBufferTextCell * myCell = (EvenOddDoubleBufferTextCell *)selectableTableView()->selectedCell(); + EvenOddDoubleBufferTextCellWithSeparator * myCell = (EvenOddDoubleBufferTextCellWithSeparator *)selectableTableView()->selectedCell(); if (myCell->firstTextSelected()) { Clipboard::sharedClipboard()->store(myCell->firstText()); } else { Clipboard::sharedClipboard()->store(myCell->secondText()); } } else { - EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)selectableTableView()->selectedCell(); + SeparatorEvenOddBufferTextCell * myCell = (SeparatorEvenOddBufferTextCell *)selectableTableView()->selectedCell(); Clipboard::sharedClipboard()->store(myCell->text()); } return true; @@ -84,10 +84,10 @@ void CalculationController::tableViewDidChangeSelection(SelectableTableView * t, } } if (t->selectedColumn() > 0 && t->selectedRow() >= 0 && t->selectedRow() <= k_totalNumberOfDoubleBufferRows) { - EvenOddDoubleBufferTextCell * myCell = (EvenOddDoubleBufferTextCell *)t->selectedCell(); + EvenOddDoubleBufferTextCellWithSeparator * myCell = (EvenOddDoubleBufferTextCellWithSeparator *)t->selectedCell(); bool firstSubCellSelected = true; if (previousSelectedCellX == 1 && previousSelectedCellY >= 0 && previousSelectedCellY <= k_totalNumberOfDoubleBufferRows) { - EvenOddDoubleBufferTextCell * myPreviousCell = (EvenOddDoubleBufferTextCell *)t->cellAtLocation(previousSelectedCellX, previousSelectedCellY); + EvenOddDoubleBufferTextCellWithSeparator * myPreviousCell = (EvenOddDoubleBufferTextCellWithSeparator *)t->cellAtLocation(previousSelectedCellX, previousSelectedCellY); firstSubCellSelected = myPreviousCell->firstTextSelected(); } myCell->selectFirstText(firstSubCellSelected); @@ -129,7 +129,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int myCell->setExpressionLayout(m_r2Layout); return; } - EvenOddMessageTextCell * myCell = (EvenOddMessageTextCell *)cell; + MarginEvenOddMessageTextCell * myCell = (MarginEvenOddMessageTextCell *)cell; if (j == 0) { myCell->setMessage(I18n::Message::Default); return; @@ -145,7 +145,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int // Coordinate and series title if (j == 0 && i > 0) { - EvenOddDoubleBufferTextCell * myCell = (EvenOddDoubleBufferTextCell *)cell; + EvenOddDoubleBufferTextCellWithSeparator * myCell = (EvenOddDoubleBufferTextCellWithSeparator *)cell; char buffer[] = {'X', static_cast('0' + seriesNumber), 0}; myCell->setFirstText(buffer); buffer[0] = 'Y'; @@ -158,7 +158,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int ArgCalculPointer calculationMethods[k_totalNumberOfDoubleBufferRows] = {&Store::meanOfColumn, &Store::sumOfColumn, &Store::squaredValueSumOfColumn, &Store::standardDeviationOfColumn, &Store::varianceOfColumn}; double calculation1 = (m_store->*calculationMethods[j-1])(seriesNumber, 0); double calculation2 = (m_store->*calculationMethods[j-1])(seriesNumber, 1); - EvenOddDoubleBufferTextCell * myCell = (EvenOddDoubleBufferTextCell *)cell; + EvenOddDoubleBufferTextCellWithSeparator * myCell = (EvenOddDoubleBufferTextCellWithSeparator *)cell; char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; PrintFloat::convertFloatToText(calculation1, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); myCell->setFirstText(buffer); @@ -167,7 +167,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int return; } if (i > 0 && j == 9) { - EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)cell; + SeparatorEvenOddBufferTextCell * myCell = (SeparatorEvenOddBufferTextCell *)cell; myCell->setText("ax+b"); return; } @@ -175,7 +175,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int assert(j != 9); CalculPointer calculationMethods[k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows] = {&Store::doubleCastedNumberOfPairsOfSeries, &Store::covariance, &Store::columnProductSum, nullptr, &Store::slope, &Store::yIntercept, &Store::correlationCoefficient, &Store::squaredCorrelationCoefficient}; double calculation = (m_store->*calculationMethods[j-k_totalNumberOfDoubleBufferRows-1])(seriesNumber); - EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)cell; + SeparatorEvenOddBufferTextCell * myCell = (SeparatorEvenOddBufferTextCell *)cell; char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; PrintFloat::convertFloatToText(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); myCell->setText(buffer); @@ -259,18 +259,18 @@ View * CalculationController::loadView() { ; m_r2TitleCell = new EvenOddExpressionCell(1.0f, 0.5f); for (int i = 0; i < Store::k_numberOfSeries; i++) { - m_columnTitleCells[i] = new EvenOddDoubleBufferTextCell(tableView); + m_columnTitleCells[i] = new EvenOddDoubleBufferTextCellWithSeparator(tableView); } for (int i = 0; i < k_maxNumberOfDisplayableRows; i++) { - m_titleCells[i] = new EvenOddMessageTextCell(KDText::FontSize::Small); + m_titleCells[i] = new MarginEvenOddMessageTextCell(KDText::FontSize::Small); } for (int i = 0; i < k_numberOfDoubleCalculationCells; i++) { - m_doubleCalculationCells[i] = new EvenOddDoubleBufferTextCell(); + m_doubleCalculationCells[i] = new EvenOddDoubleBufferTextCellWithSeparator(); m_doubleCalculationCells[i]->setTextColor(Palette::GreyDark); m_doubleCalculationCells[i]->setParentResponder(tableView); } for (int i = 0; i < k_numberOfCalculationCells;i++) { - m_calculationCells[i] = new EvenOddBufferTextCell(KDText::FontSize::Small); + m_calculationCells[i] = new SeparatorEvenOddBufferTextCell(KDText::FontSize::Small); m_calculationCells[i]->setTextColor(Palette::GreyDark); } return tableView; diff --git a/apps/regression/calculation_controller.h b/apps/regression/calculation_controller.h index 382127586..98bb9245f 100644 --- a/apps/regression/calculation_controller.h +++ b/apps/regression/calculation_controller.h @@ -3,9 +3,12 @@ #include #include "store.h" -#include "even_odd_double_buffer_text_cell.h" +#include "even_odd_double_buffer_text_cell_with_separator.h" +#include "../shared/margin_even_odd_message_text_cell.h" #include "../shared/tab_table_controller.h" #include "../shared/regular_table_view_data_source.h" +#include "../shared/separator_even_odd_buffer_text_cell.h" +#include "../shared/store_cell.h" namespace Regression { @@ -53,12 +56,12 @@ private: static constexpr KDCoordinate k_cellHeight = 25; static constexpr KDCoordinate k_cellWidth = Ion::Display::Width/2 - Metric::CommonRightMargin/2 - Metric::CommonLeftMargin/2; - EvenOddMessageTextCell * m_titleCells[k_maxNumberOfDisplayableRows]; + Shared::MarginEvenOddMessageTextCell * m_titleCells[k_maxNumberOfDisplayableRows]; EvenOddExpressionCell * m_r2TitleCell; Poincare::ExpressionLayout * m_r2Layout; - EvenOddDoubleBufferTextCell * m_columnTitleCells[Store::k_numberOfSeries]; - EvenOddDoubleBufferTextCell * m_doubleCalculationCells[k_numberOfDoubleCalculationCells]; - EvenOddBufferTextCell * m_calculationCells[k_numberOfCalculationCells]; + EvenOddDoubleBufferTextCellWithSeparator * m_columnTitleCells[Store::k_numberOfSeries]; + EvenOddDoubleBufferTextCellWithSeparator * m_doubleCalculationCells[k_numberOfDoubleCalculationCells]; + Shared::SeparatorEvenOddBufferTextCell * m_calculationCells[k_numberOfCalculationCells]; Store * m_store; }; diff --git a/apps/regression/even_odd_double_buffer_text_cell.cpp b/apps/regression/even_odd_double_buffer_text_cell.cpp deleted file mode 100644 index 84776c85c..000000000 --- a/apps/regression/even_odd_double_buffer_text_cell.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "even_odd_double_buffer_text_cell.h" -#include - -EvenOddDoubleBufferTextCell::EvenOddDoubleBufferTextCell(Responder * parentResponder) : - EvenOddCell(), - Responder(parentResponder), - m_firstTextSelected(true), - m_firstBufferTextView(KDText::FontSize::Small), - m_secondBufferTextView(KDText::FontSize::Small) -{ -} - -const char * EvenOddDoubleBufferTextCell::firstText() { - return m_firstBufferTextView.text(); -} - -const char * EvenOddDoubleBufferTextCell::secondText() { - return m_secondBufferTextView.text(); -} - -bool EvenOddDoubleBufferTextCell::firstTextSelected() { - return m_firstTextSelected; -} - -void EvenOddDoubleBufferTextCell::selectFirstText(bool selectFirstText) { - m_firstTextSelected = selectFirstText; - m_firstBufferTextView.setHighlighted(selectFirstText); - m_secondBufferTextView.setHighlighted(!selectFirstText); - reloadCell(); -} - -void EvenOddDoubleBufferTextCell::reloadCell() { - m_firstBufferTextView.reloadCell(); - m_secondBufferTextView.reloadCell(); -} - -void EvenOddDoubleBufferTextCell::setHighlighted(bool highlight) { - m_firstBufferTextView.setHighlighted(false); - m_secondBufferTextView.setHighlighted(false); - HighlightCell::setHighlighted(highlight); - if (isHighlighted()) { - if (m_firstTextSelected) { - m_firstBufferTextView.setHighlighted(true); - } else { - m_secondBufferTextView.setHighlighted(false); - } - } - reloadCell(); -} - -void EvenOddDoubleBufferTextCell::setEven(bool even) { - m_firstBufferTextView.setEven(even); - m_secondBufferTextView.setEven(even); - reloadCell(); -} - -void EvenOddDoubleBufferTextCell::setFirstText(const char * textContent) { - m_firstBufferTextView.setText(textContent); -} - -void EvenOddDoubleBufferTextCell::setSecondText(const char * textContent) { - m_secondBufferTextView.setText(textContent); -} - -void EvenOddDoubleBufferTextCell::setTextColor(KDColor textColor) { - m_firstBufferTextView.setTextColor(textColor); - m_secondBufferTextView.setTextColor(textColor); -} - -int EvenOddDoubleBufferTextCell::numberOfSubviews() const { - return 2; -} - -View * EvenOddDoubleBufferTextCell::subviewAtIndex(int index) { - assert(index == 0 || index == 1); - if (index == 0) { - return &m_firstBufferTextView; - } - return &m_secondBufferTextView; -} - -void EvenOddDoubleBufferTextCell::layoutSubviews() { - KDCoordinate width = bounds().width(); - KDCoordinate height = bounds().height(); - m_firstBufferTextView.setFrame(KDRect(0, 0, width/2, height)); - m_secondBufferTextView.setFrame(KDRect(width/2, 0, width/2, height)); -} - -bool EvenOddDoubleBufferTextCell::handleEvent(Ion::Events::Event event) { - if (m_firstTextSelected && event == Ion::Events::Right) { - selectFirstText(false); - return true; - } - if (!m_firstTextSelected && event == Ion::Events::Left) { - selectFirstText(true); - return true; - } - return false; -} - diff --git a/apps/regression/even_odd_double_buffer_text_cell_with_separator.cpp b/apps/regression/even_odd_double_buffer_text_cell_with_separator.cpp new file mode 100644 index 000000000..966fbd8b8 --- /dev/null +++ b/apps/regression/even_odd_double_buffer_text_cell_with_separator.cpp @@ -0,0 +1,109 @@ +#include "even_odd_double_buffer_text_cell_with_separator.h" +#include "../shared/hideable_even_odd_editable_text_cell.h" +#include "escher/metric.h" +#include + +EvenOddDoubleBufferTextCellWithSeparator::EvenOddDoubleBufferTextCellWithSeparator(Responder * parentResponder) : + EvenOddCell(), + Responder(parentResponder), + m_firstTextSelected(true), + m_firstBufferTextView(KDText::FontSize::Small), + m_secondBufferTextView(KDText::FontSize::Small) +{ +} + +const char * EvenOddDoubleBufferTextCellWithSeparator::firstText() { + return m_firstBufferTextView.text(); +} + +const char * EvenOddDoubleBufferTextCellWithSeparator::secondText() { + return m_secondBufferTextView.text(); +} + +bool EvenOddDoubleBufferTextCellWithSeparator::firstTextSelected() { + return m_firstTextSelected; +} + +void EvenOddDoubleBufferTextCellWithSeparator::selectFirstText(bool selectFirstText) { + m_firstTextSelected = selectFirstText; + m_firstBufferTextView.setHighlighted(selectFirstText); + m_secondBufferTextView.setHighlighted(!selectFirstText); + reloadCell(); +} + +void EvenOddDoubleBufferTextCellWithSeparator::reloadCell() { + m_firstBufferTextView.reloadCell(); + m_secondBufferTextView.reloadCell(); +} + +void EvenOddDoubleBufferTextCellWithSeparator::setHighlighted(bool highlight) { + m_firstBufferTextView.setHighlighted(false); + m_secondBufferTextView.setHighlighted(false); + HighlightCell::setHighlighted(highlight); + if (isHighlighted()) { + if (m_firstTextSelected) { + m_firstBufferTextView.setHighlighted(true); + } else { + m_secondBufferTextView.setHighlighted(false); + } + } + reloadCell(); +} + +void EvenOddDoubleBufferTextCellWithSeparator::setEven(bool even) { + m_firstBufferTextView.setEven(even); + m_secondBufferTextView.setEven(even); + reloadCell(); +} + +void EvenOddDoubleBufferTextCellWithSeparator::setFirstText(const char * textContent) { + m_firstBufferTextView.setText(textContent); +} + +void EvenOddDoubleBufferTextCellWithSeparator::setSecondText(const char * textContent) { + m_secondBufferTextView.setText(textContent); +} + +void EvenOddDoubleBufferTextCellWithSeparator::setTextColor(KDColor textColor) { + m_firstBufferTextView.setTextColor(textColor); + m_secondBufferTextView.setTextColor(textColor); +} + +void EvenOddDoubleBufferTextCellWithSeparator::drawRect(KDContext * ctx, KDRect rect) const { + EvenOddCell::drawRect(ctx, rect); + // Draw the separator + KDRect separatorRect(0, 0, Metric::TableSeparatorThickness, bounds().height()); + ctx->fillRect(separatorRect, Shared::HideableEvenOddEditableTextCell::hideColor()); +} + +int EvenOddDoubleBufferTextCellWithSeparator::numberOfSubviews() const { + return 2; +} + +View * EvenOddDoubleBufferTextCellWithSeparator::subviewAtIndex(int index) { + assert(index == 0 || index == 1); + if (index == 0) { + return &m_firstBufferTextView; + } + return &m_secondBufferTextView; +} + +void EvenOddDoubleBufferTextCellWithSeparator::layoutSubviews() { + KDCoordinate width = bounds().width() - Metric::TableSeparatorThickness; + KDCoordinate height = bounds().height(); + m_firstBufferTextView.setFrame(KDRect(Metric::TableSeparatorThickness, 0, width/2, height)); + m_secondBufferTextView.setFrame(KDRect(Metric::TableSeparatorThickness + width/2, 0, width/2, height)); +} + +bool EvenOddDoubleBufferTextCellWithSeparator::handleEvent(Ion::Events::Event event) { + if (m_firstTextSelected && event == Ion::Events::Right) { + selectFirstText(false); + return true; + } + if (!m_firstTextSelected && event == Ion::Events::Left) { + selectFirstText(true); + return true; + } + return false; +} + diff --git a/apps/regression/even_odd_double_buffer_text_cell.h b/apps/regression/even_odd_double_buffer_text_cell_with_separator.h similarity index 68% rename from apps/regression/even_odd_double_buffer_text_cell.h rename to apps/regression/even_odd_double_buffer_text_cell_with_separator.h index b3e8b6b2d..a79a2aad6 100644 --- a/apps/regression/even_odd_double_buffer_text_cell.h +++ b/apps/regression/even_odd_double_buffer_text_cell_with_separator.h @@ -1,11 +1,11 @@ -#ifndef REGRESSION_EVEN_ODD_DOUBLE_BUFFER_TEXT_CELL_H -#define REGRESSION_EVEN_ODD_DOUBLE_BUFFER_TEXT_CELL_H +#ifndef REGRESSION_EVEN_ODD_DOUBLE_BUFFER_TEXT_CELL_WITH_SEPARATOR_H +#define REGRESSION_EVEN_ODD_DOUBLE_BUFFER_TEXT_CELL_WITH_SEPARATOR_H #include -class EvenOddDoubleBufferTextCell : public EvenOddCell, public Responder{ +class EvenOddDoubleBufferTextCellWithSeparator : public EvenOddCell, public Responder{ public: - EvenOddDoubleBufferTextCell(Responder * parentResponder = nullptr); + EvenOddDoubleBufferTextCellWithSeparator(Responder * parentResponder = nullptr); const char * firstText(); const char * secondText(); void reloadCell() override; @@ -19,6 +19,7 @@ public: void setFirstText(const char * textContent); void setSecondText(const char * textContent); void setTextColor(KDColor textColor); + void drawRect(KDContext * ctx, KDRect rect) const override; int numberOfSubviews() const override; View * subviewAtIndex(int index) override; void layoutSubviews() override; diff --git a/apps/shared/Makefile b/apps/shared/Makefile index a07fccbc5..4eb69c7ed 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -8,6 +8,7 @@ app_objs += $(addprefix apps/shared/,\ curve_view_range.o\ editable_cell_table_view_controller.o\ expression_field_delegate_app.o\ + expression_layout_field_delegate.o\ float_pair_store.o\ float_parameter_controller.o\ function.o\ @@ -32,6 +33,7 @@ app_objs += $(addprefix apps/shared/,\ language_controller.o\ list_controller.o\ list_parameter_controller.o\ + margin_even_odd_message_text_cell.o\ memoized_curve_view_range.o\ message_view.o\ new_function_cell.o\ @@ -40,8 +42,8 @@ app_objs += $(addprefix apps/shared/,\ range_parameter_controller.o\ regular_table_view_data_source.o\ round_cursor_view.o\ + separator_even_odd_buffer_text_cell.o\ simple_interactive_curve_view_controller.o\ - expression_layout_field_delegate.o\ store_cell.o\ store_controller.o\ store_parameter_controller.o\ diff --git a/apps/shared/margin_even_odd_message_text_cell.cpp b/apps/shared/margin_even_odd_message_text_cell.cpp new file mode 100644 index 000000000..7ed79a3b3 --- /dev/null +++ b/apps/shared/margin_even_odd_message_text_cell.cpp @@ -0,0 +1,10 @@ +#include "margin_even_odd_message_text_cell.h" + +namespace Shared { + +void MarginEvenOddMessageTextCell::layoutSubviews() { + m_messageTextView.setFrame(KDRect(bounds().topLeft(), bounds().width() - k_rightMargin, bounds().height())); +} + +} + diff --git a/apps/shared/margin_even_odd_message_text_cell.h b/apps/shared/margin_even_odd_message_text_cell.h new file mode 100644 index 000000000..051b988ab --- /dev/null +++ b/apps/shared/margin_even_odd_message_text_cell.h @@ -0,0 +1,20 @@ +#ifndef APPS_SHARED_MARGIN_EVEN_ODD_MESSAGE_TEXT_CELL_H +#define APPS_SHARED_MARGIN_EVEN_ODD_MESSAGE_TEXT_CELL_H + +#include + +namespace Shared { + +class MarginEvenOddMessageTextCell : public EvenOddMessageTextCell { +public: + MarginEvenOddMessageTextCell(KDText::FontSize size = KDText::FontSize::Small) : + EvenOddMessageTextCell(size) + {} + void layoutSubviews() override; +private: + constexpr static KDCoordinate k_rightMargin = 2; +}; + +} + +#endif diff --git a/apps/statistics/calculation_cell.cpp b/apps/shared/separator_even_odd_buffer_text_cell.cpp similarity index 62% rename from apps/statistics/calculation_cell.cpp rename to apps/shared/separator_even_odd_buffer_text_cell.cpp index ccb886a26..c87c45f91 100644 --- a/apps/statistics/calculation_cell.cpp +++ b/apps/shared/separator_even_odd_buffer_text_cell.cpp @@ -1,17 +1,17 @@ -#include "calculation_cell.h" -#include "../shared/hideable_even_odd_editable_text_cell.h" -#include "escher/metric.h" +#include "separator_even_odd_buffer_text_cell.h" +#include "hideable_even_odd_editable_text_cell.h" +#include -namespace Statistics { +namespace Shared { -void CalculationCell::drawRect(KDContext * ctx, KDRect rect) const { +void SeparatorEvenOddBufferTextCell::drawRect(KDContext * ctx, KDRect rect) const { EvenOddBufferTextCell::drawRect(ctx, rect); // Draw the separator KDRect separatorRect(0, 0, Metric::TableSeparatorThickness, bounds().height()); ctx->fillRect(separatorRect, Shared::HideableEvenOddEditableTextCell::hideColor()); } -void CalculationCell::layoutSubviews() { +void SeparatorEvenOddBufferTextCell::layoutSubviews() { KDRect boundsThis = bounds(); m_bufferTextView.setFrame(KDRect(boundsThis.left() + Metric::TableSeparatorThickness, boundsThis.top(), boundsThis.width() - Metric::TableSeparatorThickness - k_rightMargin, boundsThis.height())); } diff --git a/apps/statistics/calculation_cell.h b/apps/shared/separator_even_odd_buffer_text_cell.h similarity index 60% rename from apps/statistics/calculation_cell.h rename to apps/shared/separator_even_odd_buffer_text_cell.h index a4ca0a4bc..1fd885ed5 100644 --- a/apps/statistics/calculation_cell.h +++ b/apps/shared/separator_even_odd_buffer_text_cell.h @@ -1,11 +1,11 @@ -#ifndef APPS_STATISTICS_CALCULATION_CELL_H -#define APPS_STATISTICS_CALCULATION_CELL_H +#ifndef APPS_SHARED_SEPARATOR_EVEN_ODD_CELL_H +#define APPS_SHARED_SEPARATOR_EVEN_ODD_CELL_H #include -namespace Statistics { +namespace Shared { -class CalculationCell : public EvenOddBufferTextCell { +class SeparatorEvenOddBufferTextCell : public EvenOddBufferTextCell { public: using EvenOddBufferTextCell::EvenOddBufferTextCell; void drawRect(KDContext * ctx, KDRect rect) const override; diff --git a/apps/statistics/Makefile b/apps/statistics/Makefile index f1d5a02b3..17245d25d 100644 --- a/apps/statistics/Makefile +++ b/apps/statistics/Makefile @@ -8,10 +8,8 @@ app_objs += $(addprefix apps/statistics/,\ box_controller.o\ box_range.o\ box_view.o\ - calculation_cell.o\ calculation_controller.o\ calculation_selectable_table_view.o\ - calculation_title_cell.o\ histogram_banner_view.o\ histogram_controller.o\ histogram_parameter_controller.o\ diff --git a/apps/statistics/calculation_controller.cpp b/apps/statistics/calculation_controller.cpp index a6a3f649b..48fe4e700 100644 --- a/apps/statistics/calculation_controller.cpp +++ b/apps/statistics/calculation_controller.cpp @@ -75,7 +75,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int I18n::Message::Sum, I18n::Message::SquareSum, I18n::Message::SampleStandardDeviationS}; - CalculationTitleCell * calcTitleCell = static_cast(cell); + MarginEvenOddMessageTextCell * calcTitleCell = static_cast(cell); calcTitleCell->setMessage(titles[j-1]); return; } @@ -205,11 +205,11 @@ View * CalculationController::loadView() { m_seriesTitleCells[i]->setSeparatorLeft(true); } for (int i = 0; i < k_numberOfCalculationTitleCells; i++) { - m_calculationTitleCells[i] = new CalculationTitleCell(KDText::FontSize::Small); + m_calculationTitleCells[i] = new MarginEvenOddMessageTextCell(KDText::FontSize::Small); m_calculationTitleCells[i]->setAlignment(1.0f, 0.5f); } for (int i = 0; i < k_numberOfCalculationCells; i++) { - m_calculationCells[i] = new CalculationCell(KDText::FontSize::Small); + m_calculationCells[i] = new SeparatorEvenOddBufferTextCell(KDText::FontSize::Small); m_calculationCells[i]->setTextColor(Palette::GreyDark); } m_hideableCell = new HideableEvenOddCell(); diff --git a/apps/statistics/calculation_controller.h b/apps/statistics/calculation_controller.h index 90a25dfb1..2744272b6 100644 --- a/apps/statistics/calculation_controller.h +++ b/apps/statistics/calculation_controller.h @@ -6,6 +6,8 @@ #include "calculation_cell.h" #include "calculation_title_cell.h" #include "../shared/hideable_even_odd_cell.h" +#include "../shared/margin_even_odd_message_text_cell.h" +#include "../shared/separator_even_odd_buffer_text_cell.h" #include "../shared/store_title_cell.h" #include "../shared/tab_table_controller.h" @@ -63,8 +65,8 @@ private: void unloadView(View * view) override; Shared::StoreTitleCell * m_seriesTitleCells[k_numberOfSeriesTitleCells]; - CalculationTitleCell * m_calculationTitleCells[k_numberOfCalculationTitleCells]; - CalculationCell * m_calculationCells[k_numberOfCalculationCells]; + Shared::MarginEvenOddMessageTextCell * m_calculationTitleCells[k_numberOfCalculationTitleCells]; + Shared::SeparatorEvenOddBufferTextCell * m_calculationCells[k_numberOfCalculationCells]; Shared::HideableEvenOddCell * m_hideableCell; Store * m_store; }; diff --git a/apps/statistics/calculation_title_cell.cpp b/apps/statistics/calculation_title_cell.cpp deleted file mode 100644 index fcfb58bec..000000000 --- a/apps/statistics/calculation_title_cell.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "calculation_title_cell.h" - -namespace Statistics { - -void CalculationTitleCell::layoutSubviews() { - m_messageTextView.setFrame(KDRect(bounds().topLeft(), bounds().width() - k_rightMargin, bounds().height())); -} - -} - diff --git a/apps/statistics/calculation_title_cell.h b/apps/statistics/calculation_title_cell.h deleted file mode 100644 index 22a5f85e6..000000000 --- a/apps/statistics/calculation_title_cell.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef APPS_STATISTICS_CALCULATION_TITLE_CELL_H -#define APPS_STATISTICS_CALCULATION_TITLE_CELL_H - -#include - -namespace Statistics { - -class CalculationTitleCell : public EvenOddMessageTextCell { -public: - CalculationTitleCell(KDText::FontSize size = KDText::FontSize::Small) : - EvenOddMessageTextCell(size) - {} - void layoutSubviews() override; -private: - constexpr static KDCoordinate k_rightMargin = 2; -}; - -} - -#endif From 0cf20433ba51d2fd61a66015d791ddae10a03ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 31 May 2018 16:33:19 +0200 Subject: [PATCH 114/156] [apps/regression] Fix number of columns assumptions --- apps/regression/calculation_controller.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index 3bd262ee5..257575f88 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -42,7 +42,7 @@ bool CalculationController::handleEvent(Ion::Events::Event event) { app()->setFirstResponder(tabController()); return true; } - if (event == Ion::Events::Copy && selectedColumn() == 1 && selectedRow() > 0) { + if (event == Ion::Events::Copy && selectedColumn() > 0 && selectedRow() > 0) { if (selectedRow() <= k_totalNumberOfDoubleBufferRows) { EvenOddDoubleBufferTextCellWithSeparator * myCell = (EvenOddDoubleBufferTextCellWithSeparator *)selectableTableView()->selectedCell(); if (myCell->firstTextSelected()) { @@ -86,7 +86,7 @@ void CalculationController::tableViewDidChangeSelection(SelectableTableView * t, if (t->selectedColumn() > 0 && t->selectedRow() >= 0 && t->selectedRow() <= k_totalNumberOfDoubleBufferRows) { EvenOddDoubleBufferTextCellWithSeparator * myCell = (EvenOddDoubleBufferTextCellWithSeparator *)t->selectedCell(); bool firstSubCellSelected = true; - if (previousSelectedCellX == 1 && previousSelectedCellY >= 0 && previousSelectedCellY <= k_totalNumberOfDoubleBufferRows) { + if (previousSelectedCellX > 0 && previousSelectedCellY >= 0 && previousSelectedCellY <= k_totalNumberOfDoubleBufferRows) { EvenOddDoubleBufferTextCellWithSeparator * myPreviousCell = (EvenOddDoubleBufferTextCellWithSeparator *)t->cellAtLocation(previousSelectedCellX, previousSelectedCellY); firstSubCellSelected = myPreviousCell->firstTextSelected(); } From 45b9f9a2f50c9a2fe4afd2804002fd92a88eb1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 31 May 2018 16:34:27 +0200 Subject: [PATCH 115/156] [apps/regression] Fix margins in Stats --- apps/regression/Makefile | 2 ++ apps/regression/calculation_controller.cpp | 4 ++-- apps/regression/calculation_controller.h | 3 ++- .../even_odd_buffer_text_cell_with_margin.cpp | 12 ++++++++++++ .../even_odd_buffer_text_cell_with_margin.h | 17 +++++++++++++++++ ..._double_buffer_text_cell_with_separator.cpp | 3 +++ ...dd_double_buffer_text_cell_with_separator.h | 9 +++++++-- .../even_odd_expression_cell_with_margin.cpp | 10 ++++++++++ .../even_odd_expression_cell_with_margin.h | 18 ++++++++++++++++++ escher/include/escher/text_view.h | 1 - 10 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 apps/regression/even_odd_buffer_text_cell_with_margin.cpp create mode 100644 apps/regression/even_odd_buffer_text_cell_with_margin.h create mode 100644 apps/regression/even_odd_expression_cell_with_margin.cpp create mode 100644 apps/regression/even_odd_expression_cell_with_margin.h diff --git a/apps/regression/Makefile b/apps/regression/Makefile index 60595c5fc..8266a5b9a 100644 --- a/apps/regression/Makefile +++ b/apps/regression/Makefile @@ -5,7 +5,9 @@ app_objs += $(addprefix apps/regression/,\ app.o\ banner_view.o\ calculation_controller.o\ + even_odd_buffer_text_cell_with_margin.o\ even_odd_double_buffer_text_cell_with_separator.o\ + even_odd_expression_cell_with_margin.o\ go_to_parameter_controller.o\ graph_controller.o\ graph_view.o\ diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index 257575f88..e73dfe314 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -125,7 +125,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int // Calculation title if (i == 0) { if (j == numberOfRows()-1) { - EvenOddExpressionCell * myCell = (EvenOddExpressionCell *)cell; + EvenOddExpressionCellWithMargin * myCell = (EvenOddExpressionCellWithMargin *)cell; myCell->setExpressionLayout(m_r2Layout); return; } @@ -257,7 +257,7 @@ View * CalculationController::loadView() { tableView->setVerticalCellOverlap(0); tableView->setBackgroundColor(Palette::WallScreenDark); ; - m_r2TitleCell = new EvenOddExpressionCell(1.0f, 0.5f); + m_r2TitleCell = new EvenOddExpressionCellWithMargin(1.0f, 0.5f); for (int i = 0; i < Store::k_numberOfSeries; i++) { m_columnTitleCells[i] = new EvenOddDoubleBufferTextCellWithSeparator(tableView); } diff --git a/apps/regression/calculation_controller.h b/apps/regression/calculation_controller.h index 98bb9245f..8f1f0f207 100644 --- a/apps/regression/calculation_controller.h +++ b/apps/regression/calculation_controller.h @@ -4,6 +4,7 @@ #include #include "store.h" #include "even_odd_double_buffer_text_cell_with_separator.h" +#include "even_odd_expression_cell_with_margin.h" #include "../shared/margin_even_odd_message_text_cell.h" #include "../shared/tab_table_controller.h" #include "../shared/regular_table_view_data_source.h" @@ -57,7 +58,7 @@ private: static constexpr KDCoordinate k_cellHeight = 25; static constexpr KDCoordinate k_cellWidth = Ion::Display::Width/2 - Metric::CommonRightMargin/2 - Metric::CommonLeftMargin/2; Shared::MarginEvenOddMessageTextCell * m_titleCells[k_maxNumberOfDisplayableRows]; - EvenOddExpressionCell * m_r2TitleCell; + EvenOddExpressionCellWithMargin * m_r2TitleCell; Poincare::ExpressionLayout * m_r2Layout; EvenOddDoubleBufferTextCellWithSeparator * m_columnTitleCells[Store::k_numberOfSeries]; EvenOddDoubleBufferTextCellWithSeparator * m_doubleCalculationCells[k_numberOfDoubleCalculationCells]; diff --git a/apps/regression/even_odd_buffer_text_cell_with_margin.cpp b/apps/regression/even_odd_buffer_text_cell_with_margin.cpp new file mode 100644 index 000000000..63602fe64 --- /dev/null +++ b/apps/regression/even_odd_buffer_text_cell_with_margin.cpp @@ -0,0 +1,12 @@ +#include "even_odd_buffer_text_cell_with_margin.h" +#include + +namespace Regression { + +void EvenOddBufferTextCellWithMargin::layoutSubviews() { + KDRect boundsThis = bounds(); + KDRect boundsBuffer = KDRect(boundsThis.left() + k_horizontalMargin, boundsThis.top(), boundsThis.width() - 2*k_horizontalMargin, boundsThis.height()); + m_bufferTextView.setFrame(boundsBuffer); +} + +} diff --git a/apps/regression/even_odd_buffer_text_cell_with_margin.h b/apps/regression/even_odd_buffer_text_cell_with_margin.h new file mode 100644 index 000000000..0e22e1fd8 --- /dev/null +++ b/apps/regression/even_odd_buffer_text_cell_with_margin.h @@ -0,0 +1,17 @@ +#ifndef APPS_REGRESSION_EVEN_ODD_BUFFER_TEXT_CELL_WITH_MARGIN_H +#define APPS_REGRESSION_EVEN_ODD_BUFFER_TEXT_CELL_WITH_MARGIN_H + +#include + +namespace Regression { + +class EvenOddBufferTextCellWithMargin : public EvenOddBufferTextCell { +public: + using EvenOddBufferTextCell::EvenOddBufferTextCell; + void layoutSubviews() override; +private: + static constexpr KDCoordinate k_horizontalMargin = 2; +}; + +} +#endif diff --git a/apps/regression/even_odd_double_buffer_text_cell_with_separator.cpp b/apps/regression/even_odd_double_buffer_text_cell_with_separator.cpp index 966fbd8b8..0375ee3d7 100644 --- a/apps/regression/even_odd_double_buffer_text_cell_with_separator.cpp +++ b/apps/regression/even_odd_double_buffer_text_cell_with_separator.cpp @@ -3,6 +3,8 @@ #include "escher/metric.h" #include +namespace Regression { + EvenOddDoubleBufferTextCellWithSeparator::EvenOddDoubleBufferTextCellWithSeparator(Responder * parentResponder) : EvenOddCell(), Responder(parentResponder), @@ -107,3 +109,4 @@ bool EvenOddDoubleBufferTextCellWithSeparator::handleEvent(Ion::Events::Event ev return false; } +} diff --git a/apps/regression/even_odd_double_buffer_text_cell_with_separator.h b/apps/regression/even_odd_double_buffer_text_cell_with_separator.h index a79a2aad6..e140bdeba 100644 --- a/apps/regression/even_odd_double_buffer_text_cell_with_separator.h +++ b/apps/regression/even_odd_double_buffer_text_cell_with_separator.h @@ -2,6 +2,9 @@ #define REGRESSION_EVEN_ODD_DOUBLE_BUFFER_TEXT_CELL_WITH_SEPARATOR_H #include +#include "even_odd_buffer_text_cell_with_margin.h" + +namespace Regression { class EvenOddDoubleBufferTextCellWithSeparator : public EvenOddCell, public Responder{ public: @@ -26,8 +29,10 @@ public: bool handleEvent(Ion::Events::Event event) override; protected: bool m_firstTextSelected; - EvenOddBufferTextCell m_firstBufferTextView; - EvenOddBufferTextCell m_secondBufferTextView; + EvenOddBufferTextCellWithMargin m_firstBufferTextView; + EvenOddBufferTextCellWithMargin m_secondBufferTextView; }; +} + #endif diff --git a/apps/regression/even_odd_expression_cell_with_margin.cpp b/apps/regression/even_odd_expression_cell_with_margin.cpp new file mode 100644 index 000000000..7dba966c1 --- /dev/null +++ b/apps/regression/even_odd_expression_cell_with_margin.cpp @@ -0,0 +1,10 @@ +#include "even_odd_expression_cell_with_margin.h" + +namespace Regression { + +void EvenOddExpressionCellWithMargin::layoutSubviews() { + KDRect boundsThis = bounds(); + m_expressionView.setFrame(KDRect(boundsThis.topLeft(), boundsThis.width() - k_rightMargin, boundsThis.height())); +} + +} diff --git a/apps/regression/even_odd_expression_cell_with_margin.h b/apps/regression/even_odd_expression_cell_with_margin.h new file mode 100644 index 000000000..f6478a6ca --- /dev/null +++ b/apps/regression/even_odd_expression_cell_with_margin.h @@ -0,0 +1,18 @@ +#ifndef APPS_REGRESSION_EVEN_ODD_EXPRESSION_CELL_WITH_MARGIN_H +#define APPS_REGRESSION_EVEN_ODD_EXPRESSION_CELL_WITH_MARGIN_H + +#include + +namespace Regression { + +class EvenOddExpressionCellWithMargin : public EvenOddExpressionCell { +public: + using EvenOddExpressionCell::EvenOddExpressionCell; + void layoutSubviews() override; +private: + static constexpr KDCoordinate k_rightMargin = 2; +}; + +} + +#endif diff --git a/escher/include/escher/text_view.h b/escher/include/escher/text_view.h index 6baef8b84..e2309a661 100644 --- a/escher/include/escher/text_view.h +++ b/escher/include/escher/text_view.h @@ -24,7 +24,6 @@ protected: const char * className() const override; #endif KDText::FontSize m_fontSize; -private: float m_horizontalAlignment; float m_verticalAlignment; KDColor m_textColor; From c0e80a3934299c2f2d796e717a009102bbb01e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 31 May 2018 17:03:48 +0200 Subject: [PATCH 116/156] [apps/reg] Color the columns titles in Stats --- apps/regression/Makefile | 1 + apps/regression/calculation_controller.cpp | 5 ++-- apps/regression/calculation_controller.h | 3 ++- apps/regression/column_title_cell.cpp | 24 ++++++++++++++++++ apps/regression/column_title_cell.h | 25 +++++++++++++++++++ ...double_buffer_text_cell_with_separator.cpp | 6 ++--- ...d_double_buffer_text_cell_with_separator.h | 2 +- 7 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 apps/regression/column_title_cell.cpp create mode 100644 apps/regression/column_title_cell.h diff --git a/apps/regression/Makefile b/apps/regression/Makefile index 8266a5b9a..455d988f1 100644 --- a/apps/regression/Makefile +++ b/apps/regression/Makefile @@ -5,6 +5,7 @@ app_objs += $(addprefix apps/regression/,\ app.o\ banner_view.o\ calculation_controller.o\ + column_title_cell.o\ even_odd_buffer_text_cell_with_margin.o\ even_odd_double_buffer_text_cell_with_separator.o\ even_odd_expression_cell_with_margin.o\ diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index e73dfe314..38ca5df2d 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -145,11 +145,12 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int // Coordinate and series title if (j == 0 && i > 0) { - EvenOddDoubleBufferTextCellWithSeparator * myCell = (EvenOddDoubleBufferTextCellWithSeparator *)cell; + ColumnTitleCell * myCell = (ColumnTitleCell *)cell; char buffer[] = {'X', static_cast('0' + seriesNumber), 0}; myCell->setFirstText(buffer); buffer[0] = 'Y'; myCell->setSecondText(buffer); + myCell->setColor(Palette::DataColor[seriesNumber]); return; } @@ -259,7 +260,7 @@ View * CalculationController::loadView() { ; m_r2TitleCell = new EvenOddExpressionCellWithMargin(1.0f, 0.5f); for (int i = 0; i < Store::k_numberOfSeries; i++) { - m_columnTitleCells[i] = new EvenOddDoubleBufferTextCellWithSeparator(tableView); + m_columnTitleCells[i] = new ColumnTitleCell(tableView); } for (int i = 0; i < k_maxNumberOfDisplayableRows; i++) { m_titleCells[i] = new MarginEvenOddMessageTextCell(KDText::FontSize::Small); diff --git a/apps/regression/calculation_controller.h b/apps/regression/calculation_controller.h index 8f1f0f207..341b82828 100644 --- a/apps/regression/calculation_controller.h +++ b/apps/regression/calculation_controller.h @@ -3,6 +3,7 @@ #include #include "store.h" +#include "column_title_cell.h" #include "even_odd_double_buffer_text_cell_with_separator.h" #include "even_odd_expression_cell_with_margin.h" #include "../shared/margin_even_odd_message_text_cell.h" @@ -60,7 +61,7 @@ private: Shared::MarginEvenOddMessageTextCell * m_titleCells[k_maxNumberOfDisplayableRows]; EvenOddExpressionCellWithMargin * m_r2TitleCell; Poincare::ExpressionLayout * m_r2Layout; - EvenOddDoubleBufferTextCellWithSeparator * m_columnTitleCells[Store::k_numberOfSeries]; + ColumnTitleCell * m_columnTitleCells[Store::k_numberOfSeries]; EvenOddDoubleBufferTextCellWithSeparator * m_doubleCalculationCells[k_numberOfDoubleCalculationCells]; Shared::SeparatorEvenOddBufferTextCell * m_calculationCells[k_numberOfCalculationCells]; Store * m_store; diff --git a/apps/regression/column_title_cell.cpp b/apps/regression/column_title_cell.cpp new file mode 100644 index 000000000..12eeaf985 --- /dev/null +++ b/apps/regression/column_title_cell.cpp @@ -0,0 +1,24 @@ +#include "column_title_cell.h" + +namespace Regression { + +void ColumnTitleCell::setColor(KDColor color) { + m_functionColor = color; + m_firstBufferTextView.setTextColor(color); + m_secondBufferTextView.setTextColor(color); + reloadCell(); +} + +void ColumnTitleCell::drawRect(KDContext * ctx, KDRect rect) const { + EvenOddDoubleBufferTextCellWithSeparator::drawRect(ctx, rect); + ctx->fillRect(KDRect(Metric::TableSeparatorThickness, 0, bounds().width(), k_colorIndicatorThickness), m_functionColor); +} + +void ColumnTitleCell::layoutSubviews() { + KDCoordinate width = bounds().width() - Metric::TableSeparatorThickness; + KDCoordinate height = bounds().height(); + m_firstBufferTextView.setFrame(KDRect(Metric::TableSeparatorThickness, k_colorIndicatorThickness, width/2, height - k_colorIndicatorThickness)); + m_secondBufferTextView.setFrame(KDRect(Metric::TableSeparatorThickness + width/2, k_colorIndicatorThickness, width/2, height - k_colorIndicatorThickness)); +} + +} diff --git a/apps/regression/column_title_cell.h b/apps/regression/column_title_cell.h new file mode 100644 index 000000000..fa85817a1 --- /dev/null +++ b/apps/regression/column_title_cell.h @@ -0,0 +1,25 @@ +#ifndef REGRESSION_COLUMN_TITLE_CELL_H +#define REGRESSION_COLUMN_TITLE_CELL_H + +#include "even_odd_double_buffer_text_cell_with_separator.h" + +namespace Regression { + +class ColumnTitleCell : public EvenOddDoubleBufferTextCellWithSeparator { +public: + ColumnTitleCell(Responder * parentResponder = nullptr) : + EvenOddDoubleBufferTextCellWithSeparator(parentResponder, 0.5f, 0.5f), + m_functionColor(Palette::Red) + { + } + virtual void setColor(KDColor color); + void drawRect(KDContext * ctx, KDRect rect) const override; + void layoutSubviews() override; +private: + constexpr static KDCoordinate k_colorIndicatorThickness = 2; + KDColor m_functionColor; +}; + +} + +#endif diff --git a/apps/regression/even_odd_double_buffer_text_cell_with_separator.cpp b/apps/regression/even_odd_double_buffer_text_cell_with_separator.cpp index 0375ee3d7..3921dd6e5 100644 --- a/apps/regression/even_odd_double_buffer_text_cell_with_separator.cpp +++ b/apps/regression/even_odd_double_buffer_text_cell_with_separator.cpp @@ -5,12 +5,12 @@ namespace Regression { -EvenOddDoubleBufferTextCellWithSeparator::EvenOddDoubleBufferTextCellWithSeparator(Responder * parentResponder) : +EvenOddDoubleBufferTextCellWithSeparator::EvenOddDoubleBufferTextCellWithSeparator(Responder * parentResponder, float horizontalAlignment, float verticalAlignment) : EvenOddCell(), Responder(parentResponder), m_firstTextSelected(true), - m_firstBufferTextView(KDText::FontSize::Small), - m_secondBufferTextView(KDText::FontSize::Small) + m_firstBufferTextView(KDText::FontSize::Small, horizontalAlignment, verticalAlignment), + m_secondBufferTextView(KDText::FontSize::Small, horizontalAlignment, verticalAlignment) { } diff --git a/apps/regression/even_odd_double_buffer_text_cell_with_separator.h b/apps/regression/even_odd_double_buffer_text_cell_with_separator.h index e140bdeba..830655ef1 100644 --- a/apps/regression/even_odd_double_buffer_text_cell_with_separator.h +++ b/apps/regression/even_odd_double_buffer_text_cell_with_separator.h @@ -8,7 +8,7 @@ namespace Regression { class EvenOddDoubleBufferTextCellWithSeparator : public EvenOddCell, public Responder{ public: - EvenOddDoubleBufferTextCellWithSeparator(Responder * parentResponder = nullptr); + EvenOddDoubleBufferTextCellWithSeparator(Responder * parentResponder = nullptr, float horizontalAlignment = 1.0f, float verticalAlignment = 0.5f); const char * firstText(); const char * secondText(); void reloadCell() override; From 95e0a8babbc62c88cca93d755729873194e7d516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 31 May 2018 17:09:35 +0200 Subject: [PATCH 117/156] [apps/reg] hide the upper left cell --- apps/regression/calculation_controller.cpp | 21 +++++++++++++++++---- apps/regression/calculation_controller.h | 3 +++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index 38ca5df2d..a8a9bed6c 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -20,6 +20,7 @@ CalculationController::CalculationController(Responder * parentResponder, Button m_columnTitleCells{}, m_doubleCalculationCells{}, m_calculationCells{}, + m_hideableCell(nullptr), m_store(store) { m_r2Layout = new HorizontalLayout(new CharLayout('r', KDText::FontSize::Small), new VerticalOffsetLayout(new CharLayout('2', KDText::FontSize::Small), VerticalOffsetLayout::Type::Superscript, false), false); @@ -118,6 +119,9 @@ int CalculationController::numberOfColumns() { } void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { + if (i == 0 & j == 0) { + return; + } EvenOddCell * myCell = (EvenOddCell *)cell; myCell->setEven(j%2 == 0); myCell->setHighlighted(i == selectedColumn() && j == selectedRow()); @@ -130,10 +134,6 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int return; } MarginEvenOddMessageTextCell * myCell = (MarginEvenOddMessageTextCell *)cell; - if (j == 0) { - myCell->setMessage(I18n::Message::Default); - return; - } myCell->setAlignment(1.0f, 0.5f); I18n::Message titles[k_totalNumberOfRows-1] = {I18n::Message::Mean, I18n::Message::Sum, I18n::Message::SquareSum, I18n::Message::StandardDeviation, I18n::Message::Deviation, I18n::Message::NumberOfDots, I18n::Message::Covariance, I18n::Message::Sxy, I18n::Message::Regression, I18n::Message::A, I18n::Message::B, I18n::Message::R, I18n::Message::Default}; myCell->setMessage(titles[j-1]); @@ -211,6 +211,9 @@ HighlightCell * CalculationController::reusableCell(int index, int type) { assert(m_doubleCalculationCells[index] != nullptr); return m_doubleCalculationCells[index]; } + if (type == k_hideableCellType) { + return m_hideableCell; + } assert(index >= 0 && index < k_numberOfCalculationCells); assert(m_calculationCells[index] != nullptr); return m_calculationCells[index]; @@ -229,11 +232,17 @@ int CalculationController::reusableCellCount(int type) { if (type == k_doubleBufferCalculationCellType) { return k_numberOfDoubleCalculationCells; } + if (type == k_hideableCellType) { + return 1; + } assert(type == k_standardCalculationCellType); return k_numberOfCalculationCells; } int CalculationController::typeAtLocation(int i, int j) { + if (i == 0 && j == 0) { + return k_hideableCellType; + } if (i == 0 && j == k_totalNumberOfRows-1) { return k_r2CellType; } @@ -274,6 +283,8 @@ View * CalculationController::loadView() { m_calculationCells[i] = new SeparatorEvenOddBufferTextCell(KDText::FontSize::Small); m_calculationCells[i]->setTextColor(Palette::GreyDark); } + m_hideableCell = new HideableEvenOddCell(); + m_hideableCell->setHide(true); return tableView; } @@ -296,6 +307,8 @@ void CalculationController::unloadView(View * view) { delete m_titleCells[i]; m_titleCells[i] = nullptr; } + delete m_hideableCell; + m_hideableCell = nullptr; TabTableController::unloadView(view); } diff --git a/apps/regression/calculation_controller.h b/apps/regression/calculation_controller.h index 341b82828..cb97a5c20 100644 --- a/apps/regression/calculation_controller.h +++ b/apps/regression/calculation_controller.h @@ -6,6 +6,7 @@ #include "column_title_cell.h" #include "even_odd_double_buffer_text_cell_with_separator.h" #include "even_odd_expression_cell_with_margin.h" +#include "../shared/hideable_even_odd_cell.h" #include "../shared/margin_even_odd_message_text_cell.h" #include "../shared/tab_table_controller.h" #include "../shared/regular_table_view_data_source.h" @@ -55,6 +56,7 @@ private: constexpr static int k_columnTitleCellType = 2; constexpr static int k_doubleBufferCalculationCellType = 3; constexpr static int k_standardCalculationCellType = 4; + static constexpr int k_hideableCellType = 5; static constexpr KDCoordinate k_cellHeight = 25; static constexpr KDCoordinate k_cellWidth = Ion::Display::Width/2 - Metric::CommonRightMargin/2 - Metric::CommonLeftMargin/2; @@ -64,6 +66,7 @@ private: ColumnTitleCell * m_columnTitleCells[Store::k_numberOfSeries]; EvenOddDoubleBufferTextCellWithSeparator * m_doubleCalculationCells[k_numberOfDoubleCalculationCells]; Shared::SeparatorEvenOddBufferTextCell * m_calculationCells[k_numberOfCalculationCells]; + Shared::HideableEvenOddCell * m_hideableCell; Store * m_store; }; From 6b3c0c64d2e4e231e36b15b0bdfa6a619ac98f5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 31 May 2018 17:14:49 +0200 Subject: [PATCH 118/156] [apps/reg] Fix the margins --- apps/regression/calculation_controller.cpp | 2 +- apps/regression/calculation_controller.h | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index a8a9bed6c..1f389766d 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -266,7 +266,7 @@ View * CalculationController::loadView() { SelectableTableView * tableView = new SelectableTableView(this, this, this, this); tableView->setVerticalCellOverlap(0); tableView->setBackgroundColor(Palette::WallScreenDark); -; + tableView->setMargins(k_margin, k_scrollBarMargin, k_scrollBarMargin, k_margin); m_r2TitleCell = new EvenOddExpressionCellWithMargin(1.0f, 0.5f); for (int i = 0; i < Store::k_numberOfSeries; i++) { m_columnTitleCells[i] = new ColumnTitleCell(tableView); diff --git a/apps/regression/calculation_controller.h b/apps/regression/calculation_controller.h index cb97a5c20..3b581be69 100644 --- a/apps/regression/calculation_controller.h +++ b/apps/regression/calculation_controller.h @@ -42,9 +42,6 @@ public: int reusableCellCount(int type) override; int typeAtLocation(int i, int j) override; private: - Responder * tabController() const override; - View * loadView() override; - void unloadView(View * view) override; constexpr static int k_totalNumberOfRows = 14; constexpr static int k_maxNumberOfDisplayableRows = 11; constexpr static int k_totalNumberOfDoubleBufferRows = 5; @@ -60,6 +57,12 @@ private: static constexpr KDCoordinate k_cellHeight = 25; static constexpr KDCoordinate k_cellWidth = Ion::Display::Width/2 - Metric::CommonRightMargin/2 - Metric::CommonLeftMargin/2; + static constexpr KDCoordinate k_margin = 8; + static constexpr KDCoordinate k_scrollBarMargin = Metric::CommonRightMargin; + + Responder * tabController() const override; + View * loadView() override; + void unloadView(View * view) override; Shared::MarginEvenOddMessageTextCell * m_titleCells[k_maxNumberOfDisplayableRows]; EvenOddExpressionCellWithMargin * m_r2TitleCell; Poincare::ExpressionLayout * m_r2Layout; From 9ddac70d27aea3ac64ca0280f835077c96835636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 31 May 2018 17:19:13 +0200 Subject: [PATCH 119/156] apps/regs] Fix header guards --- apps/regression/even_odd_buffer_text_cell_with_margin.h | 4 ++-- apps/regression/even_odd_expression_cell_with_margin.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/regression/even_odd_buffer_text_cell_with_margin.h b/apps/regression/even_odd_buffer_text_cell_with_margin.h index 0e22e1fd8..414f9cfd5 100644 --- a/apps/regression/even_odd_buffer_text_cell_with_margin.h +++ b/apps/regression/even_odd_buffer_text_cell_with_margin.h @@ -1,5 +1,5 @@ -#ifndef APPS_REGRESSION_EVEN_ODD_BUFFER_TEXT_CELL_WITH_MARGIN_H -#define APPS_REGRESSION_EVEN_ODD_BUFFER_TEXT_CELL_WITH_MARGIN_H +#ifndef REGRESSION_EVEN_ODD_BUFFER_TEXT_CELL_WITH_MARGIN_H +#define REGRESSION_EVEN_ODD_BUFFER_TEXT_CELL_WITH_MARGIN_H #include diff --git a/apps/regression/even_odd_expression_cell_with_margin.h b/apps/regression/even_odd_expression_cell_with_margin.h index f6478a6ca..cf74ac1da 100644 --- a/apps/regression/even_odd_expression_cell_with_margin.h +++ b/apps/regression/even_odd_expression_cell_with_margin.h @@ -1,5 +1,5 @@ -#ifndef APPS_REGRESSION_EVEN_ODD_EXPRESSION_CELL_WITH_MARGIN_H -#define APPS_REGRESSION_EVEN_ODD_EXPRESSION_CELL_WITH_MARGIN_H +#ifndef REGRESSION_EVEN_ODD_EXPRESSION_CELL_WITH_MARGIN_H +#define REGRESSION_EVEN_ODD_EXPRESSION_CELL_WITH_MARGIN_H #include From b2c84b3f4561d264968a897b983ac4f126079498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 31 May 2018 17:22:49 +0200 Subject: [PATCH 120/156] [apps] Fix compilation --- apps/regression/calculation_controller.cpp | 2 +- apps/sequence/sequence_store.h | 1 - apps/statistics/calculation_controller.h | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index 1f389766d..9751ad8e8 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -119,7 +119,7 @@ int CalculationController::numberOfColumns() { } void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { - if (i == 0 & j == 0) { + if (i == 0 && j == 0) { return; } EvenOddCell * myCell = (EvenOddCell *)cell; diff --git a/apps/sequence/sequence_store.h b/apps/sequence/sequence_store.h index 5d14004f3..266d2b33c 100644 --- a/apps/sequence/sequence_store.h +++ b/apps/sequence/sequence_store.h @@ -28,7 +28,6 @@ public: "u", "v"//, "w" }; private: - const KDColor firstAvailableColor() override; Sequence m_sequences[MaxNumberOfSequences]; }; diff --git a/apps/statistics/calculation_controller.h b/apps/statistics/calculation_controller.h index 2744272b6..1c3800736 100644 --- a/apps/statistics/calculation_controller.h +++ b/apps/statistics/calculation_controller.h @@ -3,8 +3,6 @@ #include #include "store.h" -#include "calculation_cell.h" -#include "calculation_title_cell.h" #include "../shared/hideable_even_odd_cell.h" #include "../shared/margin_even_odd_message_text_cell.h" #include "../shared/separator_even_odd_buffer_text_cell.h" From de1ccac62fd434df0691a782cdf67bdd36c5c873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 31 May 2018 17:27:23 +0200 Subject: [PATCH 121/156] [apps/reg] Modify navigation on hidden cell in Stats --- apps/regression/calculation_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index 9751ad8e8..a424df843 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -81,7 +81,7 @@ void CalculationController::tableViewDidChangeSelection(SelectableTableView * t, selectableTableView()->deselectTable(); app()->setFirstResponder(tabController()); } else { - t->selectCellAtLocation(previousSelectedCellX, previousSelectedCellY); + t->selectCellAtLocation(0, 1); } } if (t->selectedColumn() > 0 && t->selectedRow() >= 0 && t->selectedRow() <= k_totalNumberOfDoubleBufferRows) { From 31ee2daf50966540eecde2c19c82008bc8a921c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 28 May 2018 16:18:29 +0200 Subject: [PATCH 122/156] [apps/stats] Add option fill with formula (does nothing) --- apps/shared.de.i18n | 1 + apps/shared.en.i18n | 1 + apps/shared.es.i18n | 1 + apps/shared.fr.i18n | 1 + apps/shared.pt.i18n | 1 + apps/shared/store_controller.cpp | 6 +++++- apps/shared/store_controller.h | 3 +++ apps/shared/store_parameter_controller.cpp | 19 +++++++++++++++---- apps/shared/store_parameter_controller.h | 10 +++++++--- 9 files changed, 35 insertions(+), 8 deletions(-) diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index 113bd5e36..12cc7f5ae 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -14,6 +14,7 @@ Deviation = "Varianz" DisplayValues = "Werte anzeigen" ExitExamMode1 = "Wollen Sie den Testmodus " ExitExamMode2 = "verlassen?" +FillWithFormula = "Fill with formula" ForbiddenValue = "Verbotener Wert" FunctionColumn = "0(0) Spalte" FunctionOptions = "Optionen Funktion" diff --git a/apps/shared.en.i18n b/apps/shared.en.i18n index d79ebdd48..c1bf80b6b 100644 --- a/apps/shared.en.i18n +++ b/apps/shared.en.i18n @@ -14,6 +14,7 @@ Deviation = "Variance" DisplayValues = "Display values" ExitExamMode1 = "Exit the exam " ExitExamMode2 = "mode?" +FillWithFormula = "Fill with formula" ForbiddenValue = "Forbidden value" FunctionColumn = "0(0) column" FunctionOptions = "Function options" diff --git a/apps/shared.es.i18n b/apps/shared.es.i18n index bb8ae627a..77e646102 100644 --- a/apps/shared.es.i18n +++ b/apps/shared.es.i18n @@ -14,6 +14,7 @@ Deviation = "Varianza" DisplayValues = "Visualizar los valores" ExitExamMode1 = "Salir del modo " ExitExamMode2 = "examen ?" +FillWithFormula = "Fill with formula" ForbiddenValue = "Valor prohibido" FunctionColumn = "Columna 0(0)" FunctionOptions = "Opciones de la funcion" diff --git a/apps/shared.fr.i18n b/apps/shared.fr.i18n index 48126b6ca..1548bed93 100644 --- a/apps/shared.fr.i18n +++ b/apps/shared.fr.i18n @@ -14,6 +14,7 @@ Deviation = "Variance" DisplayValues = "Afficher les valeurs" ExitExamMode1 = "Voulez-vous sortir " ExitExamMode2 = "du mode examen ?" +FillWithFormula = "Fill with formula" ForbiddenValue = "Valeur interdite" FunctionColumn = "Colonne 0(0)" FunctionOptions = "Options de la fonction" diff --git a/apps/shared.pt.i18n b/apps/shared.pt.i18n index da794f9ff..6742fd2fd 100644 --- a/apps/shared.pt.i18n +++ b/apps/shared.pt.i18n @@ -14,6 +14,7 @@ Deviation = "Variancia" DisplayValues = "Exibir os valores" ExitExamMode1 = "Voce quer sair do modo de " ExitExamMode2 = "exame ?" +FillWithFormula = "Fill with formula" ForbiddenValue = "Valor proibida" FunctionColumn = "Coluna 0(0)" FunctionOptions = "Opcoes de funcao" diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 0dc5b2da7..3b2fcd304 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -14,10 +14,14 @@ StoreController::StoreController(Responder * parentResponder, FloatPairStore * s ButtonRowDelegate(header, nullptr), m_editableCells{}, m_store(store), - m_storeParameterController(this, store) + m_storeParameterController(this, store, this) { } +void StoreController::displayFormulaInput() { + +} + bool StoreController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { AppsContainer * appsContainer = ((TextFieldDelegateApp *)app())->container(); Context * globalContext = appsContainer->globalContext(); diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 59ed0bea8..8019a9428 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -13,6 +13,8 @@ class StoreController : public EditableCellTableViewController, public ButtonRow public: StoreController(Responder * parentResponder, FloatPairStore * store, ButtonRowController * header); + void displayFormulaInput(); + // TextFieldDelegate bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override; @@ -57,6 +59,7 @@ protected: StoreParameterController m_storeParameterController; private: bool cellShouldBeTransparent(int i, int j); + }; } diff --git a/apps/shared/store_parameter_controller.cpp b/apps/shared/store_parameter_controller.cpp index 2bceea42e..6625dd84e 100644 --- a/apps/shared/store_parameter_controller.cpp +++ b/apps/shared/store_parameter_controller.cpp @@ -1,17 +1,20 @@ #include "store_parameter_controller.h" +#include "store_controller.h" #include namespace Shared { -StoreParameterController::StoreParameterController(Responder * parentResponder, FloatPairStore * store) : +StoreParameterController::StoreParameterController(Responder * parentResponder, FloatPairStore * store, StoreController * storeController) : ViewController(parentResponder), m_deleteColumn(I18n::Message::ClearColumn), + m_fillWithFormula(I18n::Message::FillWithFormula), #if COPY_IMPORT_LIST m_copyColumn(I18n::Message::CopyColumnInList), m_importList(I18n::Message::ImportList), #endif m_selectableTableView(this, this, this), m_store(store), + m_storeController(storeController), m_xColumnSelected(true), m_series(0) { @@ -40,12 +43,20 @@ bool StoreParameterController::handleEvent(Ion::Events::Event event) { stack->pop(); return true; } + case 1: + { + m_storeController->displayFormulaInput(); + StackViewController * stack = ((StackViewController *)parentResponder()); + stack->pop(); + return true; + } + #if COPY_IMPORT_LIST /* TODO: implement copy column and import list */ - case 1: - return true; case 2: return true; + case 3: + return true; #endif default: assert(false); @@ -58,7 +69,7 @@ bool StoreParameterController::handleEvent(Ion::Events::Event event) { HighlightCell * StoreParameterController::reusableCell(int index) { assert(index >= 0); assert(index < k_totalNumberOfCell); - HighlightCell * cells[] = {&m_deleteColumn};// {&m_deleteColumn, &m_copyColumn, &m_importList}; + HighlightCell * cells[] = {&m_deleteColumn, &m_fillWithFormula};// {&m_deleteColumn, &m_fillWithFormula, &m_copyColumn, &m_importList}; return cells[index]; } diff --git a/apps/shared/store_parameter_controller.h b/apps/shared/store_parameter_controller.h index 3efdf94a5..c7df1c8a0 100644 --- a/apps/shared/store_parameter_controller.h +++ b/apps/shared/store_parameter_controller.h @@ -7,9 +7,11 @@ namespace Shared { +class StoreController; + class StoreParameterController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource { public: - StoreParameterController(Responder * parentResponder, FloatPairStore * store); + StoreParameterController(Responder * parentResponder, FloatPairStore * store, StoreController * storeController); void selectXColumn(bool xColumnSelected) { m_xColumnSelected = xColumnSelected; } void selectSeries(int series) { m_series = series; } View * view() override { return &m_selectableTableView; } @@ -22,15 +24,17 @@ public: int reusableCellCount() override { return k_totalNumberOfCell; } private: #if COPY_IMPORT_LIST - constexpr static int k_totalNumberOfCell = 3; + constexpr static int k_totalNumberOfCell = 4; MessageTableCellWithChevron m_copyColumn; MessageTableCellWithChevron m_importList; #else - constexpr static int k_totalNumberOfCell = 1; + constexpr static int k_totalNumberOfCell = 2; #endif MessageTableCell m_deleteColumn; + MessageTableCell m_fillWithFormula; SelectableTableView m_selectableTableView; FloatPairStore * m_store; + StoreController * m_storeController; bool m_xColumnSelected; int m_series; }; From 84131db41c34c05b44ddc6e914d3f25e2d67f458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 28 May 2018 18:35:38 +0200 Subject: [PATCH 123/156] [apps/stats] Start developping the input formula view in data --- apps/shared/Makefile | 1 + .../buffer_text_view_with_text_field.cpp | 37 +++++++++++++++ .../shared/buffer_text_view_with_text_field.h | 30 ++++++++++++ .../editable_cell_table_view_controller.h | 2 +- apps/shared/store_controller.cpp | 47 +++++++++++++++---- apps/shared/store_controller.h | 22 ++++++++- apps/shared/tab_table_controller.cpp | 2 +- apps/shared/tab_table_controller.h | 2 +- 8 files changed, 130 insertions(+), 13 deletions(-) create mode 100644 apps/shared/buffer_text_view_with_text_field.cpp create mode 100644 apps/shared/buffer_text_view_with_text_field.h diff --git a/apps/shared/Makefile b/apps/shared/Makefile index 4eb69c7ed..5f92d24d0 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -1,6 +1,7 @@ app_objs += $(addprefix apps/shared/,\ banner_view.o\ buffer_function_title_cell.o\ + buffer_text_view_with_text_field.o\ button_with_separator.o\ cursor_view.o\ curve_view.o\ diff --git a/apps/shared/buffer_text_view_with_text_field.cpp b/apps/shared/buffer_text_view_with_text_field.cpp new file mode 100644 index 000000000..7a0138d8c --- /dev/null +++ b/apps/shared/buffer_text_view_with_text_field.cpp @@ -0,0 +1,37 @@ +#include "buffer_text_view_with_text_field.h" + +namespace Shared { + +BufferTextViewWithTextField::BufferTextViewWithTextField(Responder * parentResponder, TextFieldDelegate * delegate, KDText::FontSize size) : + View(), + Responder(parentResponder), + m_bufferTextView(size, 0.0f, 0.5f), + m_textField(this, m_textFieldBuffer, m_textFieldBuffer, k_textFieldBufferSize, delegate, false, size, 0.0f, 0.5f), + m_textFieldBuffer{} +{ +} + +KDSize BufferTextViewWithTextField::minimalSizeForOptimalDisplay() const { + return m_bufferTextView.minimalSizeForOptimalDisplay(); +} + +void BufferTextViewWithTextField::setBufferText(const char * text) { + m_bufferTextView.setText(text); +} + +void BufferTextViewWithTextField::didBecomeFirstResponder() { + app()->setFirstResponder(&m_textField); +} + +View * BufferTextViewWithTextField::subviewAtIndex(int index) { + assert(index >= 0 && index < numberOfSubviews()); + View * views[] = {&m_bufferTextView, &m_textField}; + return views[index]; +} + +void BufferTextViewWithTextField::layoutSubviews() { + m_bufferTextView.setFrame(KDRect(0, 0, k_height, k_bufferTextWidth)); + m_textField.setFrame(KDRect(k_bufferTextWidth, 0, k_height, bounds().width() - k_bufferTextWidth)); +} + +} diff --git a/apps/shared/buffer_text_view_with_text_field.h b/apps/shared/buffer_text_view_with_text_field.h new file mode 100644 index 000000000..b6f8c5006 --- /dev/null +++ b/apps/shared/buffer_text_view_with_text_field.h @@ -0,0 +1,30 @@ +#ifndef SHARED_BUFFER_TEXT_VIEW_WITH_TEXT_FIELD_H +#define SHARED_BUFFER_TEXT_VIEW_WITH_TEXT_FIELD_H +#include + +namespace Shared { + +class BufferTextViewWithTextField : public View, public Responder { +public: + constexpr static KDCoordinate k_height = 50; //TODO + BufferTextViewWithTextField(Responder * parentResponder, TextFieldDelegate * delegate = nullptr, KDText::FontSize size = KDText::FontSize::Large); + KDSize minimalSizeForOptimalDisplay() const override; + void setBufferText(const char * text); + void setTextFieldText(const char * text); + + // Responder + void didBecomeFirstResponder() override; +private: + constexpr static int k_textFieldBufferSize = TextField::maxBufferSize(); + constexpr static KDCoordinate k_bufferTextWidth = 70; //TODO + int numberOfSubviews() const override { return 2; } + View * subviewAtIndex(int index) override; + void layoutSubviews() override; + BufferTextView m_bufferTextView; + TextField m_textField; + char m_textFieldBuffer[k_textFieldBufferSize]; +}; + +} + +#endif diff --git a/apps/shared/editable_cell_table_view_controller.h b/apps/shared/editable_cell_table_view_controller.h index 3171ce411..80c6660a9 100644 --- a/apps/shared/editable_cell_table_view_controller.h +++ b/apps/shared/editable_cell_table_view_controller.h @@ -9,7 +9,7 @@ namespace Shared { -class EditableCellTableViewController : public TabTableController , public RegularTableViewDataSource , public TextFieldDelegate { +class EditableCellTableViewController : public TabTableController , public RegularTableViewDataSource, public TextFieldDelegate { public: EditableCellTableViewController(Responder * parentResponder); bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override; diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 3b2fcd304..2f9b4f256 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -1,5 +1,4 @@ #include "store_controller.h" -#include "store_selectable_table_view.h" #include "../apps_container.h" #include "../constant.h" #include @@ -9,6 +8,35 @@ using namespace Poincare; namespace Shared { +StoreController::ContentView::ContentView(FloatPairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, TextFieldDelegate * textFieldDelegate) : + View(), + Responder(parentResponder), + m_dataView(store, this, dataSource, selectionDataSource), + m_formulaInputView(this, textFieldDelegate), + m_displayInputFormulaView(false) +{ + m_dataView.setBackgroundColor(Palette::WallScreenDark); + m_dataView.setVerticalCellOverlap(0); + m_dataView.setMargins(k_margin, k_scrollBarMargin, k_scrollBarMargin, k_margin); +} + +void StoreController::ContentView::didBecomeFirstResponder() { + app()->setFirstResponder(&m_dataView); +} + +View * StoreController::ContentView::subviewAtIndex(int index) { + assert(index >= 0 && index < numberOfSubviews()); + View * views[] = {&m_dataView, &m_formulaInputView}; + return views[index]; +} + +void StoreController::ContentView::layoutSubviews() { + KDRect dataViewFrame(0, 0, bounds().width(), bounds().height() - (m_displayInputFormulaView ? BufferTextViewWithTextField::k_height : 0)); + m_dataView.setFrame(dataViewFrame); + KDRect formulaFrame(0, bounds().height() - BufferTextViewWithTextField::k_height, bounds().width(), m_displayInputFormulaView ? BufferTextViewWithTextField::k_height : 0); + m_formulaInputView.setFrame(formulaFrame); +} + StoreController::StoreController(Responder * parentResponder, FloatPairStore * store, ButtonRowController * header) : EditableCellTableViewController(parentResponder), ButtonRowDelegate(header, nullptr), @@ -143,12 +171,17 @@ void StoreController::didBecomeFirstResponder() { selectCellAtLocation(0, 0); } EditableCellTableViewController::didBecomeFirstResponder(); + app()->setFirstResponder(static_cast(view())); } Responder * StoreController::tabController() const { return (parentResponder()->parentResponder()->parentResponder()); } +SelectableTableView * StoreController::selectableTableView() { + return static_cast(view())->dataView(); +} + bool StoreController::cellAtLocationIsEditable(int columnIndex, int rowIndex) { if (rowIndex > 0) { return true; @@ -178,15 +211,11 @@ int StoreController::maxNumberOfElements() const { } View * StoreController::loadView() { - StoreSelectableTableView * tableView = new StoreSelectableTableView(m_store, this, this, this); - tableView->setBackgroundColor(Palette::WallScreenDark); - tableView->setVerticalCellOverlap(0); - + ContentView * contentView = new ContentView(m_store, this, this, this, this); for (int i = 0; i < k_maxNumberOfEditableCells; i++) { - m_editableCells[i] = new StoreCell(tableView, this, m_draftTextBuffer); + m_editableCells[i] = new StoreCell(contentView->dataView(), this, m_draftTextBuffer); } - tableView->setMargins(k_margin, k_scrollBarMargin, k_scrollBarMargin, k_margin - Metric::TableSeparatorThickness); - return tableView; + return contentView; } void StoreController::unloadView(View * view) { @@ -194,7 +223,7 @@ void StoreController::unloadView(View * view) { delete m_editableCells[i]; m_editableCells[i] = nullptr; } - EditableCellTableViewController::unloadView(view); + delete view; } bool StoreController::cellShouldBeTransparent(int i, int j) { diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 8019a9428..88f895bfc 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -2,10 +2,12 @@ #define SHARED_STORE_CONTROLLER_H #include +#include "buffer_text_view_with_text_field.h" #include "editable_cell_table_view_controller.h" #include "float_pair_store.h" #include "store_cell.h" #include "store_parameter_controller.h" +#include "store_selectable_table_view.h" namespace Shared { @@ -43,7 +45,26 @@ protected: constexpr static int k_numberOfTitleCells = 4; static constexpr int k_titleCellType = 0; static constexpr int k_editableCellType = 1; + + class ContentView : public View , public Responder { + public: + ContentView(FloatPairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, TextFieldDelegate * textFieldDelegate); + StoreSelectableTableView * dataView() { return &m_dataView; } + // Responder + void didBecomeFirstResponder() override; + private: + static constexpr KDCoordinate k_margin = 8; + static constexpr KDCoordinate k_scrollBarMargin = Metric::CommonRightMargin; + int numberOfSubviews() const override { return 1 + m_displayInputFormulaView; } + View * subviewAtIndex(int index) override; + void layoutSubviews() override; + StoreSelectableTableView m_dataView; + BufferTextViewWithTextField m_formulaInputView; + bool m_displayInputFormulaView; + }; + Responder * tabController() const override; + SelectableTableView * selectableTableView() override; View * loadView() override; void unloadView(View * view) override; bool cellAtLocationIsEditable(int columnIndex, int rowIndex) override; @@ -59,7 +80,6 @@ protected: StoreParameterController m_storeParameterController; private: bool cellShouldBeTransparent(int i, int j); - }; } diff --git a/apps/shared/tab_table_controller.cpp b/apps/shared/tab_table_controller.cpp index 4f598f430..824018371 100644 --- a/apps/shared/tab_table_controller.cpp +++ b/apps/shared/tab_table_controller.cpp @@ -25,7 +25,7 @@ void TabTableController::willExitResponderChain(Responder * nextFirstResponder) } SelectableTableView * TabTableController::selectableTableView() { - return (SelectableTableView *)view(); + return static_cast(view()); } View * TabTableController::loadView() { diff --git a/apps/shared/tab_table_controller.h b/apps/shared/tab_table_controller.h index 935dc0679..f00dcd22a 100644 --- a/apps/shared/tab_table_controller.h +++ b/apps/shared/tab_table_controller.h @@ -15,7 +15,7 @@ public: void viewWillAppear() override; void willExitResponderChain(Responder * nextFirstResponder) override; protected: - SelectableTableView * selectableTableView(); + virtual SelectableTableView * selectableTableView(); virtual View * loadView() override; void unloadView(View * view) override; virtual Responder * tabController() const = 0; From 70e1f5b25b6dcf385de950107e9656a2482f3cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 09:09:07 +0200 Subject: [PATCH 124/156] [apps/stats] Inline StoreController::contentView() --- apps/shared/store_controller.cpp | 4 ++-- apps/shared/store_controller.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 2f9b4f256..72ca5aa91 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -171,7 +171,7 @@ void StoreController::didBecomeFirstResponder() { selectCellAtLocation(0, 0); } EditableCellTableViewController::didBecomeFirstResponder(); - app()->setFirstResponder(static_cast(view())); + app()->setFirstResponder(contentView()); } Responder * StoreController::tabController() const { @@ -179,7 +179,7 @@ Responder * StoreController::tabController() const { } SelectableTableView * StoreController::selectableTableView() { - return static_cast(view())->dataView(); + return contentView()->dataView(); } bool StoreController::cellAtLocationIsEditable(int columnIndex, int rowIndex) { diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 88f895bfc..cddad9805 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -80,6 +80,7 @@ protected: StoreParameterController m_storeParameterController; private: bool cellShouldBeTransparent(int i, int j); + ContentView * contentView() { return static_cast(view()); } }; } From dbf1597ef3e05e08f9f9799e34b35a62be65ae72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 10:02:11 +0200 Subject: [PATCH 125/156] [apps/stats] Display or not the formula input view --- .../buffer_text_view_with_text_field.cpp | 5 +++- .../shared/buffer_text_view_with_text_field.h | 3 +- apps/shared/store_controller.cpp | 29 +++++++++++++++---- apps/shared/store_controller.h | 7 +++-- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/apps/shared/buffer_text_view_with_text_field.cpp b/apps/shared/buffer_text_view_with_text_field.cpp index 7a0138d8c..112ca1216 100644 --- a/apps/shared/buffer_text_view_with_text_field.cpp +++ b/apps/shared/buffer_text_view_with_text_field.cpp @@ -9,6 +9,7 @@ BufferTextViewWithTextField::BufferTextViewWithTextField(Responder * parentRespo m_textField(this, m_textFieldBuffer, m_textFieldBuffer, k_textFieldBufferSize, delegate, false, size, 0.0f, 0.5f), m_textFieldBuffer{} { + m_bufferTextView.setText("TODO"); } KDSize BufferTextViewWithTextField::minimalSizeForOptimalDisplay() const { @@ -21,6 +22,8 @@ void BufferTextViewWithTextField::setBufferText(const char * text) { void BufferTextViewWithTextField::didBecomeFirstResponder() { app()->setFirstResponder(&m_textField); + m_textField.setEditing(true, true); + markRectAsDirty(bounds()); } View * BufferTextViewWithTextField::subviewAtIndex(int index) { @@ -31,7 +34,7 @@ View * BufferTextViewWithTextField::subviewAtIndex(int index) { void BufferTextViewWithTextField::layoutSubviews() { m_bufferTextView.setFrame(KDRect(0, 0, k_height, k_bufferTextWidth)); - m_textField.setFrame(KDRect(k_bufferTextWidth, 0, k_height, bounds().width() - k_bufferTextWidth)); + m_textField.setFrame(KDRect(k_bufferTextWidth, 0, bounds().width() - k_bufferTextWidth, k_height)); } } diff --git a/apps/shared/buffer_text_view_with_text_field.h b/apps/shared/buffer_text_view_with_text_field.h index b6f8c5006..4f6f69709 100644 --- a/apps/shared/buffer_text_view_with_text_field.h +++ b/apps/shared/buffer_text_view_with_text_field.h @@ -9,6 +9,7 @@ public: constexpr static KDCoordinate k_height = 50; //TODO BufferTextViewWithTextField(Responder * parentResponder, TextFieldDelegate * delegate = nullptr, KDText::FontSize size = KDText::FontSize::Large); KDSize minimalSizeForOptimalDisplay() const override; + TextField * textField() { return &m_textField; } void setBufferText(const char * text); void setTextFieldText(const char * text); @@ -16,7 +17,7 @@ public: void didBecomeFirstResponder() override; private: constexpr static int k_textFieldBufferSize = TextField::maxBufferSize(); - constexpr static KDCoordinate k_bufferTextWidth = 70; //TODO + constexpr static KDCoordinate k_bufferTextWidth = 50; //TODO int numberOfSubviews() const override { return 2; } View * subviewAtIndex(int index) override; void layoutSubviews() override; diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 72ca5aa91..54bc7138a 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -13,15 +13,23 @@ StoreController::ContentView::ContentView(FloatPairStore * store, Responder * pa Responder(parentResponder), m_dataView(store, this, dataSource, selectionDataSource), m_formulaInputView(this, textFieldDelegate), - m_displayInputFormulaView(false) + m_displayFormulaInputView(false) { m_dataView.setBackgroundColor(Palette::WallScreenDark); m_dataView.setVerticalCellOverlap(0); m_dataView.setMargins(k_margin, k_scrollBarMargin, k_scrollBarMargin, k_margin); } +void StoreController::ContentView::displayFormulaInput(bool display) { + if (m_displayFormulaInputView != display) { + m_displayFormulaInputView = display; + layoutSubviews(); + markRectAsDirty(bounds()); + } +} + void StoreController::ContentView::didBecomeFirstResponder() { - app()->setFirstResponder(&m_dataView); + app()->setFirstResponder(m_displayFormulaInputView ? static_cast(&m_formulaInputView) : static_cast(&m_dataView)); } View * StoreController::ContentView::subviewAtIndex(int index) { @@ -31,10 +39,13 @@ View * StoreController::ContentView::subviewAtIndex(int index) { } void StoreController::ContentView::layoutSubviews() { - KDRect dataViewFrame(0, 0, bounds().width(), bounds().height() - (m_displayInputFormulaView ? BufferTextViewWithTextField::k_height : 0)); + KDRect dataViewFrame(0, 0, bounds().width(), bounds().height() - (m_displayFormulaInputView ? BufferTextViewWithTextField::k_height : 0)); m_dataView.setFrame(dataViewFrame); - KDRect formulaFrame(0, bounds().height() - BufferTextViewWithTextField::k_height, bounds().width(), m_displayInputFormulaView ? BufferTextViewWithTextField::k_height : 0); - m_formulaInputView.setFrame(formulaFrame); + m_formulaInputView.setFrame(formulaFrame()); +} + +KDRect StoreController::ContentView::formulaFrame() const { + return KDRect(0, bounds().height() - BufferTextViewWithTextField::k_height, bounds().width(), m_displayFormulaInputView ? BufferTextViewWithTextField::k_height : 0); } StoreController::StoreController(Responder * parentResponder, FloatPairStore * store, ButtonRowController * header) : @@ -47,10 +58,16 @@ StoreController::StoreController(Responder * parentResponder, FloatPairStore * s } void StoreController::displayFormulaInput() { - + contentView()->displayFormulaInput(true); } bool StoreController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { + if (textField == contentView()->formulaInputView()->textField()) { + // Handle formula input + contentView()->displayFormulaInput(false); + app()->setFirstResponder(contentView()); + return true; + } AppsContainer * appsContainer = ((TextFieldDelegateApp *)app())->container(); Context * globalContext = appsContainer->globalContext(); double floatBody = Expression::approximateToScalar(text, *globalContext); diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index cddad9805..aa6b6e5e1 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -50,17 +50,20 @@ protected: public: ContentView(FloatPairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, TextFieldDelegate * textFieldDelegate); StoreSelectableTableView * dataView() { return &m_dataView; } + BufferTextViewWithTextField * formulaInputView() { return &m_formulaInputView; } + void displayFormulaInput(bool display); // Responder void didBecomeFirstResponder() override; private: static constexpr KDCoordinate k_margin = 8; static constexpr KDCoordinate k_scrollBarMargin = Metric::CommonRightMargin; - int numberOfSubviews() const override { return 1 + m_displayInputFormulaView; } + int numberOfSubviews() const override { return 1 + m_displayFormulaInputView; } View * subviewAtIndex(int index) override; void layoutSubviews() override; + KDRect formulaFrame() const; StoreSelectableTableView m_dataView; BufferTextViewWithTextField m_formulaInputView; - bool m_displayInputFormulaView; + bool m_displayFormulaInputView; }; Responder * tabController() const override; From 7491277713894eca4adb80cbaf2c86514e56fa63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 29 May 2018 10:23:09 +0200 Subject: [PATCH 126/156] [apps/poincare] Add V1, N1, V2, N2, ... symbols to lexer --- apps/statistics/app.h | 2 +- poincare/include/poincare/symbol.h | 9 ++++++++- poincare/src/expression_lexer.l | 6 ++++++ poincare/src/symbol.cpp | 24 +++++++++++++++++++++--- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/apps/statistics/app.h b/apps/statistics/app.h index 5790b9bcf..60784bebc 100644 --- a/apps/statistics/app.h +++ b/apps/statistics/app.h @@ -4,9 +4,9 @@ #include #include "box_controller.h" #include "calculation_controller.h" +#include "histogram_controller.h" #include "store.h" #include "store_controller.h" -#include "histogram_controller.h" #include "../shared/text_field_delegate_app.h" namespace Statistics { diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index db70d832e..d81e43743 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -26,7 +26,13 @@ public: M6, M7, M8, - M9 = 17 + M9 = 17, + V1, + N1, + V2, + N2, + V3, + N3 = 23 }; static SpecialSymbols matrixSymbol(char index); Symbol(char name); @@ -39,6 +45,7 @@ public: Sign sign() const override; bool isMatrixSymbol() const; bool isScalarSymbol() const; + bool isSeriesSymbol() const; bool isApproximate(Context & context) const; float characteristicXRange(Context & context, AngleUnit angleUnit = AngleUnit::Default) const override; bool hasAnExactRepresentation(Context & context) const; diff --git a/poincare/src/expression_lexer.l b/poincare/src/expression_lexer.l index 4e09dccaf..0ae032d42 100644 --- a/poincare/src/expression_lexer.l +++ b/poincare/src/expression_lexer.l @@ -93,6 +93,12 @@ u\_\{n\} { poincare_expression_yylval.character = Symbol::SpecialSymbols::un; re u\_\{n\+1\} { poincare_expression_yylval.character = Symbol::SpecialSymbols::un1; return SYMBOL; } v\_\{n\} { poincare_expression_yylval.character = Symbol::SpecialSymbols::vn; return SYMBOL; } v\_\{n\+1\} { poincare_expression_yylval.character = Symbol::SpecialSymbols::vn1; return SYMBOL; } +V1 { poincare_expression_yylval.character = Symbol::SpecialSymbols::V1; return SYMBOL; } +N1 { poincare_expression_yylval.character = Symbol::SpecialSymbols::N1; return SYMBOL; } +V2 { poincare_expression_yylval.character = Symbol::SpecialSymbols::V2; return SYMBOL; } +N2 { poincare_expression_yylval.character = Symbol::SpecialSymbols::N2; return SYMBOL; } +V3 { poincare_expression_yylval.character = Symbol::SpecialSymbols::V3; return SYMBOL; } +N3 { poincare_expression_yylval.character = Symbol::SpecialSymbols::N3; return SYMBOL; } acos { poincare_expression_yylval.expression = new ArcCosine(); return FUNCTION; } acosh { poincare_expression_yylval.expression = new HyperbolicArcCosine(); return FUNCTION; } abs { poincare_expression_yylval.expression = new AbsoluteValue(); return FUNCTION; } diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index abcb886f2..718be60b0 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -53,6 +53,18 @@ const char * Symbol::textForSpecialSymbols(char name) const { return "M8"; case SpecialSymbols::M9: return "M9"; + case SpecialSymbols::V1: + return "V1"; + case SpecialSymbols::N1: + return "N1"; + case SpecialSymbols::V2: + return "V2"; + case SpecialSymbols::N2: + return "N2"; + case SpecialSymbols::V3: + return "V3"; + case SpecialSymbols::N3: + return "N3"; default: assert(false); return nullptr; @@ -232,9 +244,8 @@ ExpressionLayout * Symbol::privateCreateLayout(PrintFloat::Mode floatDisplayMode false), false); } - if (isMatrixSymbol()) { - const char mi[] = { 'M', (char)(m_name-(char)SpecialSymbols::M0+'0') }; - return LayoutEngine::createStringLayout(mi, sizeof(mi)); + if (isMatrixSymbol() || isSeriesSymbol()) { + return LayoutEngine::createStringLayout(textForSpecialSymbols(m_name), 2); } return LayoutEngine::createStringLayout(&m_name, 1); } @@ -270,6 +281,13 @@ bool Symbol::isScalarSymbol() const { return false; } +bool Symbol::isSeriesSymbol() const { + if (m_name >= (char)SpecialSymbols::V1 && m_name <= (char)SpecialSymbols::N3) { + return true; + } + return false; +} + int Symbol::simplificationOrderSameType(const Expression * e, bool canBeInterrupted) const { assert(e->type() == Expression::Type::Symbol); if ((uint8_t)m_name == ((uint8_t)static_cast(e)->name())) { From a49c1b61544868280d4bc32c3a1cfee2da197467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 31 May 2018 18:28:23 +0200 Subject: [PATCH 127/156] [apps/stats] Series context --- apps/statistics/Makefile | 1 + apps/statistics/series_context.cpp | 31 ++++++++++++++++++++++++++++++ apps/statistics/series_context.h | 26 +++++++++++++++++++++++++ poincare/include/poincare/symbol.h | 2 +- poincare/src/symbol.cpp | 2 +- 5 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 apps/statistics/series_context.cpp create mode 100644 apps/statistics/series_context.h diff --git a/apps/statistics/Makefile b/apps/statistics/Makefile index 17245d25d..bfafe7973 100644 --- a/apps/statistics/Makefile +++ b/apps/statistics/Makefile @@ -18,6 +18,7 @@ app_objs += $(addprefix apps/statistics/,\ multiple_data_view.o\ multiple_data_view_controller.o\ multiple_histograms_view.o\ + series_context.o\ store.o\ store_controller.o\ ) diff --git a/apps/statistics/series_context.cpp b/apps/statistics/series_context.cpp new file mode 100644 index 000000000..a63f254ea --- /dev/null +++ b/apps/statistics/series_context.cpp @@ -0,0 +1,31 @@ +#include "series_context.h" +#include +#include + +using namespace Poincare; +using namespace Shared; + +namespace Statistics { + +const Expression * SeriesContext::expressionForSymbol(const Symbol * symbol) { + assert(m_seriesPairIndex >= 0); + if (!symbol->isSeriesSymbol()) { + return nullptr; + } + const char * seriesName = Symbol::textForSpecialSymbols(symbol->name()); + assert(strlen(seriesName) == 2); + + int series = seriesName[1] - '0'; + assert(series >= 0 && series < Store::k_numberOfSeries); + + assert((seriesName[0] == 'V') || (seriesName[0] == 'N')); + int storeI = seriesName[0] == 'V' ? 0 : 1; + + assert(m_seriesPairIndex < store->numberOfPairsOfSeries(series)); + + Expression * result = new Decimal(m_store->get(series, storeI, m_seriesPairIndex)); + assert(result != nullptr); + return result; +} + +} diff --git a/apps/statistics/series_context.h b/apps/statistics/series_context.h new file mode 100644 index 000000000..1ffe4986a --- /dev/null +++ b/apps/statistics/series_context.h @@ -0,0 +1,26 @@ +#ifndef STATISTICS_SERIES_CONTEXT_H +#define STATISTICS_SERIES_CONTEXT_H + +#include +#include "../shared/float_pair_store.h" + +namespace Statistics { + +class SeriesContext : public Poincare::Context { +public: + SeriesContext(Shared::FloatPairStore * store) : + Poincare::Context(), + m_store(store), + m_seriesPairIndex(-1) + {} + void setStorePosition(int series, int i, int j); + void setExpressionForSymbolName(const Poincare::Expression * expression, const Poincare::Symbol * symbol, Poincare::Context & context) override {} + const Poincare::Expression * expressionForSymbol(const Poincare::Symbol * symbol) override; +private: + Shared::FloatPairStore * m_store; + int m_seriesPairIndex; +}; + +} + +#endif diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index d81e43743..26a89aac4 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -49,8 +49,8 @@ public: bool isApproximate(Context & context) const; float characteristicXRange(Context & context, AngleUnit angleUnit = AngleUnit::Default) const override; bool hasAnExactRepresentation(Context & context) const; + static const char * textForSpecialSymbols(char name); private: - const char * textForSpecialSymbols(char name) const; Expression * replaceSymbolWithExpression(char symbol, Expression * expression) override; /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index 718be60b0..63b60dd40 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -21,7 +21,7 @@ extern "C" { namespace Poincare { -const char * Symbol::textForSpecialSymbols(char name) const { +const char * Symbol::textForSpecialSymbols(char name) { switch (name) { case SpecialSymbols::Ans: return "ans"; From 5f3ea724f42bca8d66e1bc0d2101bab77c0d0093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 1 Jun 2018 10:09:57 +0200 Subject: [PATCH 128/156] [apps/stats] Prevent negative values in "Size" columns --- apps/statistics/store_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index e4dbbc78e..7ec96e246 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -45,7 +45,7 @@ bool StoreController::setDataAtLocation(double floatBody, int columnIndex, int r if (std::fabs(floatBody) > FLT_MAX) { return false; } - if (columnIndex == 1) { + if (columnIndex % Store::k_numberOfColumnsPerSeries == 1) { if (floatBody < 0) { return false; } From 5da90b51601e2fbf776cdbbf447e3363723c8a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 1 Jun 2018 10:30:54 +0200 Subject: [PATCH 129/156] [apps/stats] Fill with formula in statistics --- apps/regression/store_controller.h | 1 + apps/shared/store_controller.cpp | 7 +++++ apps/shared/store_controller.h | 1 + apps/statistics/series_context.cpp | 40 ++++++++++++++++------------ apps/statistics/series_context.h | 10 ++++--- apps/statistics/store_controller.cpp | 22 ++++++++++++++- apps/statistics/store_controller.h | 2 ++ 7 files changed, 61 insertions(+), 22 deletions(-) diff --git a/apps/regression/store_controller.h b/apps/regression/store_controller.h index f8720c4a2..3de9511d9 100644 --- a/apps/regression/store_controller.h +++ b/apps/regression/store_controller.h @@ -11,6 +11,7 @@ namespace Regression { class StoreController : public Shared::StoreController { public: StoreController(Responder * parentResponder, Store * store, ButtonRowController * header); + void fillColumnWithFormula(Poincare::Expression * formula) {} void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; private: HighlightCell * titleCells(int index) override; diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 54bc7138a..54752db42 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -64,7 +64,14 @@ void StoreController::displayFormulaInput() { bool StoreController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { if (textField == contentView()->formulaInputView()->textField()) { // Handle formula input + Expression * expression = Expression::parse(textField->text()); + if (expression == nullptr) { + app()->displayWarning(I18n::Message::SyntaxError); + return false; + } contentView()->displayFormulaInput(false); + fillColumnWithFormula(expression); + delete expression; app()->setFirstResponder(contentView()); return true; } diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index aa6b6e5e1..2e2b1b5b0 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -16,6 +16,7 @@ public: StoreController(Responder * parentResponder, FloatPairStore * store, ButtonRowController * header); void displayFormulaInput(); + virtual void fillColumnWithFormula(Poincare::Expression * formula) = 0; // TextFieldDelegate bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override; diff --git a/apps/statistics/series_context.cpp b/apps/statistics/series_context.cpp index a63f254ea..d02587d02 100644 --- a/apps/statistics/series_context.cpp +++ b/apps/statistics/series_context.cpp @@ -1,31 +1,37 @@ #include "series_context.h" #include #include +#include using namespace Poincare; using namespace Shared; namespace Statistics { +void SeriesContext::setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) { + m_parentContext->setExpressionForSymbolName(expression, symbol, context); +} + const Expression * SeriesContext::expressionForSymbol(const Symbol * symbol) { - assert(m_seriesPairIndex >= 0); - if (!symbol->isSeriesSymbol()) { - return nullptr; + if (symbol->isSeriesSymbol()) { + const char * seriesName = Symbol::textForSpecialSymbols(symbol->name()); + assert(strlen(seriesName) == 2); + + int series = (int)(seriesName[1] - '0') - 1; + assert(series >= 0 && series < FloatPairStore::k_numberOfSeries); + + assert((seriesName[0] == 'V') || (seriesName[0] == 'N')); + int storeI = seriesName[0] == 'V' ? 0 : 1; + + assert(m_seriesPairIndex >= 0); + assert(m_seriesPairIndex < m_store->numberOfPairsOfSeries(series)); + + Expression * result = new Decimal(m_store->get(series, storeI, m_seriesPairIndex)); + assert(result != nullptr); + return result; + } else { + return m_parentContext->expressionForSymbol(symbol); } - const char * seriesName = Symbol::textForSpecialSymbols(symbol->name()); - assert(strlen(seriesName) == 2); - - int series = seriesName[1] - '0'; - assert(series >= 0 && series < Store::k_numberOfSeries); - - assert((seriesName[0] == 'V') || (seriesName[0] == 'N')); - int storeI = seriesName[0] == 'V' ? 0 : 1; - - assert(m_seriesPairIndex < store->numberOfPairsOfSeries(series)); - - Expression * result = new Decimal(m_store->get(series, storeI, m_seriesPairIndex)); - assert(result != nullptr); - return result; } } diff --git a/apps/statistics/series_context.h b/apps/statistics/series_context.h index 1ffe4986a..fac98891f 100644 --- a/apps/statistics/series_context.h +++ b/apps/statistics/series_context.h @@ -8,17 +8,19 @@ namespace Statistics { class SeriesContext : public Poincare::Context { public: - SeriesContext(Shared::FloatPairStore * store) : + SeriesContext(Shared::FloatPairStore * store, Poincare::Context * parentContext = nullptr) : Poincare::Context(), m_store(store), - m_seriesPairIndex(-1) + m_seriesPairIndex(-1), + m_parentContext(parentContext) {} - void setStorePosition(int series, int i, int j); - void setExpressionForSymbolName(const Poincare::Expression * expression, const Poincare::Symbol * symbol, Poincare::Context & context) override {} + void setSeriesPairIndex(int j) { m_seriesPairIndex = j; } + void setExpressionForSymbolName(const Poincare::Expression * expression, const Poincare::Symbol * symbol, Poincare::Context & context) override; const Poincare::Expression * expressionForSymbol(const Poincare::Symbol * symbol) override; private: Shared::FloatPairStore * m_store; int m_seriesPairIndex; + Poincare::Context * m_parentContext; }; } diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index 7ec96e246..3ea77f6ed 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -1,4 +1,5 @@ #include "store_controller.h" +#include "series_context.h" #include "app.h" #include "../apps_container.h" #include "../constant.h" @@ -6,16 +7,35 @@ #include #include +using namespace Poincare; using namespace Shared; namespace Statistics { StoreController::StoreController(Responder * parentResponder, Store * store, ButtonRowController * header) : Shared::StoreController(parentResponder, store, header), - m_titleCells{} + m_titleCells{}, + m_store(store) { } +void StoreController::fillColumnWithFormula(Expression * formula) { + int currentColumn = selectedColumn(); + // Fetch the series used in the formula to: + // - Make sure the current filled column is not inside + // - Compute the size of the filled in series + + SeriesContext seriesContext(m_store, const_cast(static_cast(app()->container()))->globalContext()); + for (int j = 0; j < 2; j++) { + // Set the context + seriesContext.setSeriesPairIndex(j); + // Compute the new value using the formula + double evaluation = formula->approximateToScalar(seriesContext); + setDataAtLocation(evaluation, currentColumn, j + 1); + } + selectableTableView()->reloadData(); +} + void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { Shared::StoreController::willDisplayCellAtLocation(cell, i, j); if (cellAtLocationIsEditable(i, j)) { diff --git a/apps/statistics/store_controller.h b/apps/statistics/store_controller.h index 8427ab5af..6a7d20143 100644 --- a/apps/statistics/store_controller.h +++ b/apps/statistics/store_controller.h @@ -11,6 +11,7 @@ namespace Statistics { class StoreController : public Shared::StoreController { public: StoreController(Responder * parentResponder, Store * store, ButtonRowController * header); + void fillColumnWithFormula(Poincare::Expression * formula) override; void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; private: bool setDataAtLocation(double floatBody, int columnIndex, int rowIndex) override; @@ -18,6 +19,7 @@ private: View * loadView() override; void unloadView(View * view) override; Shared::StoreTitleCell * m_titleCells[k_numberOfTitleCells]; + Store * m_store; }; } From 79435ff3fb6ba8a9e6d213cd905420aeea3e413f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 1 Jun 2018 11:22:46 +0200 Subject: [PATCH 130/156] [apps/stats] Compute the number of values to fill in with the formula --- apps/statistics/series_context.cpp | 2 +- apps/statistics/store_controller.cpp | 26 ++++++++++++++++++++++---- poincare/include/poincare/expression.h | 9 +++++++++ poincare/include/poincare/symbol.h | 3 ++- poincare/src/expression.cpp | 14 ++++++++++++++ poincare/src/symbol.cpp | 26 +++++++++++++++++++++++--- 6 files changed, 71 insertions(+), 9 deletions(-) diff --git a/apps/statistics/series_context.cpp b/apps/statistics/series_context.cpp index d02587d02..68b875705 100644 --- a/apps/statistics/series_context.cpp +++ b/apps/statistics/series_context.cpp @@ -13,7 +13,7 @@ void SeriesContext::setExpressionForSymbolName(const Expression * expression, co } const Expression * SeriesContext::expressionForSymbol(const Symbol * symbol) { - if (symbol->isSeriesSymbol()) { + if (Symbol::isSeriesSymbol(symbol->name())) { const char * seriesName = Symbol::textForSpecialSymbols(symbol->name()); assert(strlen(seriesName) == 2); diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index 3ea77f6ed..b0ebad399 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -21,12 +21,30 @@ StoreController::StoreController(Responder * parentResponder, Store * store, But void StoreController::fillColumnWithFormula(Expression * formula) { int currentColumn = selectedColumn(); - // Fetch the series used in the formula to: - // - Make sure the current filled column is not inside - // - Compute the size of the filled in series + // Fetch the series used in the formula to compute the size of the filled in series + char variables[7] = {0, 0, 0, 0, 0, 0, 0}; + int numberOfVariables = formula->getVariables(Symbol::isSeriesSymbol, variables); + assert(numberOfVariables >= 0); + int numberOfValuesToCompute = -1; + int index = 0; + while (variables[index] != 0) { + const char * seriesName = Symbol::textForSpecialSymbols(variables[index]); + assert(strlen(seriesName) == 2); + int series = (int)(seriesName[1] - '0') - 1; + assert(series >= 0 && series < FloatPairStore::k_numberOfSeries); + if (numberOfValuesToCompute == -1) { + numberOfValuesToCompute = m_store->numberOfPairsOfSeries(series); + } else { + numberOfValuesToCompute = min(numberOfValuesToCompute, m_store->numberOfPairsOfSeries(series)); + } + index++; + } + if (numberOfValuesToCompute == -1) { + numberOfValuesToCompute = m_store->numberOfPairsOfSeries(selectedColumn()/FloatPairStore::k_numberOfColumnsPerSeries); + } SeriesContext seriesContext(m_store, const_cast(static_cast(app()->container()))->globalContext()); - for (int j = 0; j < 2; j++) { + for (int j = 0; j < numberOfValuesToCompute; j++) { // Set the context seriesContext.setSeriesPairIndex(j); // Compute the new value using the formula diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index c8e2467c7..dbf547b4e 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -238,6 +238,15 @@ public: template Expression * approximate(Context& context, AngleUnit angleUnit = AngleUnit::Default) const; template T approximateToScalar(Context& context, AngleUnit angleUnit = AngleUnit::Default) const; template static T approximateToScalar(const char * text, Context& context, AngleUnit angleUnit = AngleUnit::Default); + + /* getVariables fills the table variables with the variable present in the + * expression and returns the number of entries in filled in variables. + * For instance getVariables('x+y+2*w/cos(4)') would result in + * variables = « xyw » and would return 3. If the final number of + * variables would overflow the maxNumberOfVariables, getVariables return -1 */ + static constexpr int k_maxNumberOfVariables = 6; + typedef bool (*isVariableTest)(char c); + virtual int getVariables(isVariableTest isVariable, char * variables) const; protected: /* Constructor */ Expression() : m_parent(nullptr) {} diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index 26a89aac4..a5cddd6db 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -45,11 +45,12 @@ public: Sign sign() const override; bool isMatrixSymbol() const; bool isScalarSymbol() const; - bool isSeriesSymbol() const; + static bool isSeriesSymbol(char c); bool isApproximate(Context & context) const; float characteristicXRange(Context & context, AngleUnit angleUnit = AngleUnit::Default) const override; bool hasAnExactRepresentation(Context & context) const; static const char * textForSpecialSymbols(char name); + int getVariables(isVariableTest isVariable, char * variables) const override; private: Expression * replaceSymbolWithExpression(char symbol, Expression * expression) override; /* Simplification */ diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 61ba5af80..1efb4f095 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -371,6 +371,20 @@ template T Expression::epsilon() { return epsilon; } +/* Get variables */ + +int Expression::getVariables(isVariableTest isVariable, char * variables) const { + int numberOfVariables = 0; + for (int i = 0; i < numberOfOperands(); i++) { + int n = operand(i)->getVariables(isVariable, variables); + if (n < 0) { + return -1; + } + numberOfVariables = n > numberOfVariables ? n : numberOfVariables; + } + return numberOfVariables; +} + } template Poincare::Expression * Poincare::Expression::approximate(Context& context, AngleUnit angleUnit) const; diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index 63b60dd40..911620d47 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -71,6 +71,26 @@ const char * Symbol::textForSpecialSymbols(char name) { } } +int Symbol::getVariables(isVariableTest isVariable, char * variables) const { + size_t variablesLength = strlen(variables); + if (isVariable(m_name)) { + char * currentChar = variables; + while (*currentChar != 0) { + if (*currentChar == m_name) { + return variablesLength; + } + currentChar++; + } + if (variablesLength < k_maxNumberOfVariables) { + variables[variablesLength] = m_name; + variables[variablesLength+1] = 0; + return variablesLength+1; + } + return -1; + } + return variablesLength; +} + Symbol::SpecialSymbols Symbol::matrixSymbol(char index) { switch (index - '0') { case 0: @@ -244,7 +264,7 @@ ExpressionLayout * Symbol::privateCreateLayout(PrintFloat::Mode floatDisplayMode false), false); } - if (isMatrixSymbol() || isSeriesSymbol()) { + if (isMatrixSymbol() || isSeriesSymbol(m_name)) { return LayoutEngine::createStringLayout(textForSpecialSymbols(m_name), 2); } return LayoutEngine::createStringLayout(&m_name, 1); @@ -281,8 +301,8 @@ bool Symbol::isScalarSymbol() const { return false; } -bool Symbol::isSeriesSymbol() const { - if (m_name >= (char)SpecialSymbols::V1 && m_name <= (char)SpecialSymbols::N3) { +bool Symbol::isSeriesSymbol(char c) { + if (c >= (char)SpecialSymbols::V1 && c <= (char)SpecialSymbols::N3) { return true; } return false; From 185d77fcb7813097c6240f03ac7e8a2261b2ddc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 1 Jun 2018 11:42:02 +0200 Subject: [PATCH 131/156] [apps/statistics] Set the formula input view label --- apps/regression/store_controller.h | 3 ++- apps/shared/buffer_text_view_with_text_field.cpp | 3 +-- apps/shared/store_controller.cpp | 1 + apps/shared/store_controller.h | 1 + apps/statistics/store_controller.cpp | 10 ++++++++-- apps/statistics/store_controller.h | 1 + 6 files changed, 14 insertions(+), 5 deletions(-) diff --git a/apps/regression/store_controller.h b/apps/regression/store_controller.h index 3de9511d9..6da10235c 100644 --- a/apps/regression/store_controller.h +++ b/apps/regression/store_controller.h @@ -11,7 +11,8 @@ namespace Regression { class StoreController : public Shared::StoreController { public: StoreController(Responder * parentResponder, Store * store, ButtonRowController * header); - void fillColumnWithFormula(Poincare::Expression * formula) {} + void setFormulaLabel() override {} //TODO + void fillColumnWithFormula(Poincare::Expression * formula) override {} //TODO void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; private: HighlightCell * titleCells(int index) override; diff --git a/apps/shared/buffer_text_view_with_text_field.cpp b/apps/shared/buffer_text_view_with_text_field.cpp index 112ca1216..1f27faaef 100644 --- a/apps/shared/buffer_text_view_with_text_field.cpp +++ b/apps/shared/buffer_text_view_with_text_field.cpp @@ -5,11 +5,10 @@ namespace Shared { BufferTextViewWithTextField::BufferTextViewWithTextField(Responder * parentResponder, TextFieldDelegate * delegate, KDText::FontSize size) : View(), Responder(parentResponder), - m_bufferTextView(size, 0.0f, 0.5f), + m_bufferTextView(size, 1.0f, 0.5f), m_textField(this, m_textFieldBuffer, m_textFieldBuffer, k_textFieldBufferSize, delegate, false, size, 0.0f, 0.5f), m_textFieldBuffer{} { - m_bufferTextView.setText("TODO"); } KDSize BufferTextViewWithTextField::minimalSizeForOptimalDisplay() const { diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 54752db42..a7e360236 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -58,6 +58,7 @@ StoreController::StoreController(Responder * parentResponder, FloatPairStore * s } void StoreController::displayFormulaInput() { + setFormulaLabel(); contentView()->displayFormulaInput(true); } diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 2e2b1b5b0..9164c1044 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -16,6 +16,7 @@ public: StoreController(Responder * parentResponder, FloatPairStore * store, ButtonRowController * header); void displayFormulaInput(); + virtual void setFormulaLabel() = 0; virtual void fillColumnWithFormula(Poincare::Expression * formula) = 0; // TextFieldDelegate diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index b0ebad399..c17b5974c 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -19,12 +19,18 @@ StoreController::StoreController(Responder * parentResponder, Store * store, But { } +void StoreController::setFormulaLabel() { + int series = selectedColumn() / Store::k_numberOfColumnsPerSeries; + int isValueColumn = selectedColumn() % Store::k_numberOfColumnsPerSeries == 0; + char text[] = {isValueColumn ? 'V' : 'N', static_cast('1' + series), '=', 0}; + static_cast(view())->formulaInputView()->setBufferText(text); +} + void StoreController::fillColumnWithFormula(Expression * formula) { int currentColumn = selectedColumn(); // Fetch the series used in the formula to compute the size of the filled in series char variables[7] = {0, 0, 0, 0, 0, 0, 0}; - int numberOfVariables = formula->getVariables(Symbol::isSeriesSymbol, variables); - assert(numberOfVariables >= 0); + formula->getVariables(Symbol::isSeriesSymbol, variables); int numberOfValuesToCompute = -1; int index = 0; while (variables[index] != 0) { diff --git a/apps/statistics/store_controller.h b/apps/statistics/store_controller.h index 6a7d20143..9e81df313 100644 --- a/apps/statistics/store_controller.h +++ b/apps/statistics/store_controller.h @@ -11,6 +11,7 @@ namespace Statistics { class StoreController : public Shared::StoreController { public: StoreController(Responder * parentResponder, Store * store, ButtonRowController * header); + void setFormulaLabel() override; void fillColumnWithFormula(Poincare::Expression * formula) override; void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; private: From b8c74835de74e20165d201ef8e656e9a20cc26f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 1 Jun 2018 11:47:41 +0200 Subject: [PATCH 132/156] [poincare] Parse regression symbols: X1, Y1, X2, ... --- poincare/include/poincare/symbol.h | 9 ++++++++- poincare/src/expression_lexer.l | 6 ++++++ poincare/src/symbol.cpp | 19 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index a5cddd6db..287092009 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -32,7 +32,13 @@ public: V2, N2, V3, - N3 = 23 + N3 = 23, + X1 = 24, + Y1, + X2, + Y2, + X3, + Y3 = 29 }; static SpecialSymbols matrixSymbol(char index); Symbol(char name); @@ -46,6 +52,7 @@ public: bool isMatrixSymbol() const; bool isScalarSymbol() const; static bool isSeriesSymbol(char c); + static bool isRegressionSymbol(char c); bool isApproximate(Context & context) const; float characteristicXRange(Context & context, AngleUnit angleUnit = AngleUnit::Default) const override; bool hasAnExactRepresentation(Context & context) const; diff --git a/poincare/src/expression_lexer.l b/poincare/src/expression_lexer.l index 0ae032d42..78025a7af 100644 --- a/poincare/src/expression_lexer.l +++ b/poincare/src/expression_lexer.l @@ -99,6 +99,12 @@ V2 { poincare_expression_yylval.character = Symbol::SpecialSymbols::V2; return S N2 { poincare_expression_yylval.character = Symbol::SpecialSymbols::N2; return SYMBOL; } V3 { poincare_expression_yylval.character = Symbol::SpecialSymbols::V3; return SYMBOL; } N3 { poincare_expression_yylval.character = Symbol::SpecialSymbols::N3; return SYMBOL; } +X1 { poincare_expression_yylval.character = Symbol::SpecialSymbols::X1; return SYMBOL; } +Y1 { poincare_expression_yylval.character = Symbol::SpecialSymbols::Y1; return SYMBOL; } +X2 { poincare_expression_yylval.character = Symbol::SpecialSymbols::X2; return SYMBOL; } +Y2 { poincare_expression_yylval.character = Symbol::SpecialSymbols::Y2; return SYMBOL; } +X3 { poincare_expression_yylval.character = Symbol::SpecialSymbols::X3; return SYMBOL; } +Y3 { poincare_expression_yylval.character = Symbol::SpecialSymbols::Y3; return SYMBOL; } acos { poincare_expression_yylval.expression = new ArcCosine(); return FUNCTION; } acosh { poincare_expression_yylval.expression = new HyperbolicArcCosine(); return FUNCTION; } abs { poincare_expression_yylval.expression = new AbsoluteValue(); return FUNCTION; } diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index 911620d47..8364b512b 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -65,6 +65,18 @@ const char * Symbol::textForSpecialSymbols(char name) { return "V3"; case SpecialSymbols::N3: return "N3"; + case SpecialSymbols::X1: + return "X1"; + case SpecialSymbols::Y1: + return "Y1"; + case SpecialSymbols::X2: + return "X2"; + case SpecialSymbols::Y2: + return "Y2"; + case SpecialSymbols::X3: + return "X3"; + case SpecialSymbols::Y3: + return "Y3"; default: assert(false); return nullptr; @@ -308,6 +320,13 @@ bool Symbol::isSeriesSymbol(char c) { return false; } +bool Symbol::isRegressionSymbol(char c) { + if (c >= (char)SpecialSymbols::X1 && c <= (char)SpecialSymbols::Y3) { + return true; + } + return false; +} + int Symbol::simplificationOrderSameType(const Expression * e, bool canBeInterrupted) const { assert(e->type() == Expression::Type::Symbol); if ((uint8_t)m_name == ((uint8_t)static_cast(e)->name())) { From 589b0443eacd615e94ca91a991c9814eb92f7e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 1 Jun 2018 11:54:25 +0200 Subject: [PATCH 133/156] [apps/regression] Fill column with formula --- apps/regression/Makefile | 1 + apps/regression/regression_context.cpp | 37 +++++++++++++++++++++++ apps/regression/regression_context.h | 28 +++++++++++++++++ apps/regression/store_controller.cpp | 42 ++++++++++++++++++++++++++ apps/regression/store_controller.h | 4 +-- 5 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 apps/regression/regression_context.cpp create mode 100644 apps/regression/regression_context.h diff --git a/apps/regression/Makefile b/apps/regression/Makefile index 455d988f1..ade5e836f 100644 --- a/apps/regression/Makefile +++ b/apps/regression/Makefile @@ -16,6 +16,7 @@ app_objs += $(addprefix apps/regression/,\ prediction_parameter_controller.o\ store.o\ store_controller.o\ + regression_context.o\ ) i18n_files += $(addprefix apps/regression/,\ diff --git a/apps/regression/regression_context.cpp b/apps/regression/regression_context.cpp new file mode 100644 index 000000000..edc1ac6cd --- /dev/null +++ b/apps/regression/regression_context.cpp @@ -0,0 +1,37 @@ +#include "regression_context.h" +#include +#include +#include + +using namespace Poincare; +using namespace Shared; + +namespace Regression { + +void RegressionContext::setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) { + m_parentContext->setExpressionForSymbolName(expression, symbol, context); +} + +const Expression * RegressionContext::expressionForSymbol(const Symbol * symbol) { + if (Symbol::isRegressionSymbol(symbol->name())) { + const char * seriesName = Symbol::textForSpecialSymbols(symbol->name()); + assert(strlen(seriesName) == 2); + + int series = (int)(seriesName[1] - '0') - 1; + assert(series >= 0 && series < FloatPairStore::k_numberOfSeries); + + assert((seriesName[0] == 'X') || (seriesName[0] == 'Y')); + int storeI = seriesName[0] == 'X' ? 0 : 1; + + assert(m_seriesPairIndex >= 0); + assert(m_seriesPairIndex < m_store->numberOfPairsOfSeries(series)); + + Expression * result = new Decimal(m_store->get(series, storeI, m_seriesPairIndex)); + assert(result != nullptr); + return result; + } else { + return m_parentContext->expressionForSymbol(symbol); + } +} + +} diff --git a/apps/regression/regression_context.h b/apps/regression/regression_context.h new file mode 100644 index 000000000..8c4237a9a --- /dev/null +++ b/apps/regression/regression_context.h @@ -0,0 +1,28 @@ +#ifndef REGRESSION_REGRESSION_CONTEXT_H +#define REGRESSION_REGRESSION_CONTEXT_H + +#include +#include "../shared/float_pair_store.h" + +namespace Regression { + +class RegressionContext : public Poincare::Context { +public: + RegressionContext(Shared::FloatPairStore * store, Poincare::Context * parentContext = nullptr) : + Poincare::Context(), + m_store(store), + m_seriesPairIndex(-1), + m_parentContext(parentContext) + {} + void setSeriesPairIndex(int j) { m_seriesPairIndex = j; } + void setExpressionForSymbolName(const Poincare::Expression * expression, const Poincare::Symbol * symbol, Poincare::Context & context) override; + const Poincare::Expression * expressionForSymbol(const Poincare::Symbol * symbol) override; +private: + Shared::FloatPairStore * m_store; + int m_seriesPairIndex; + Poincare::Context * m_parentContext; +}; + +} + +#endif diff --git a/apps/regression/store_controller.cpp b/apps/regression/store_controller.cpp index 5c7227dd6..0de633e93 100644 --- a/apps/regression/store_controller.cpp +++ b/apps/regression/store_controller.cpp @@ -1,5 +1,6 @@ #include "store_controller.h" #include "app.h" +#include "regression_context.h" #include "../apps_container.h" #include "../constant.h" #include "../../poincare/src/layout/char_layout.h" @@ -18,6 +19,47 @@ StoreController::StoreController(Responder * parentResponder, Store * store, But { } +void StoreController::setFormulaLabel() { + int series = selectedColumn() / Store::k_numberOfColumnsPerSeries; + int isXColumn = selectedColumn() % Store::k_numberOfColumnsPerSeries == 0; + char text[] = {isXColumn ? 'X' : 'Y', static_cast('1' + series), '=', 0}; + static_cast(view())->formulaInputView()->setBufferText(text); +} + +void StoreController::fillColumnWithFormula(Expression * formula) { + int currentColumn = selectedColumn(); + // Fetch the series used in the formula to compute the size of the filled in series + char variables[7] = {0, 0, 0, 0, 0, 0, 0}; + formula->getVariables(Symbol::isRegressionSymbol, variables); + int numberOfValuesToCompute = -1; + int index = 0; + while (variables[index] != 0) { + const char * seriesName = Symbol::textForSpecialSymbols(variables[index]); + assert(strlen(seriesName) == 2); + int series = (int)(seriesName[1] - '0') - 1; + assert(series >= 0 && series < FloatPairStore::k_numberOfSeries); + if (numberOfValuesToCompute == -1) { + numberOfValuesToCompute = m_store->numberOfPairsOfSeries(series); + } else { + numberOfValuesToCompute = min(numberOfValuesToCompute, m_store->numberOfPairsOfSeries(series)); + } + index++; + } + if (numberOfValuesToCompute == -1) { + numberOfValuesToCompute = m_store->numberOfPairsOfSeries(selectedColumn()/FloatPairStore::k_numberOfColumnsPerSeries); + } + + RegressionContext regressionContext(m_store, const_cast(static_cast(app()->container()))->globalContext()); + for (int j = 0; j < numberOfValuesToCompute; j++) { + // Set the context + regressionContext.setSeriesPairIndex(j); + // Compute the new value using the formula + double evaluation = formula->approximateToScalar(regressionContext); + setDataAtLocation(evaluation, currentColumn, j + 1); + } + selectableTableView()->reloadData(); +} + void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { ::StoreController::willDisplayCellAtLocation(cell, i, j); if (cellAtLocationIsEditable(i, j)) { diff --git a/apps/regression/store_controller.h b/apps/regression/store_controller.h index 6da10235c..efbb25326 100644 --- a/apps/regression/store_controller.h +++ b/apps/regression/store_controller.h @@ -11,8 +11,8 @@ namespace Regression { class StoreController : public Shared::StoreController { public: StoreController(Responder * parentResponder, Store * store, ButtonRowController * header); - void setFormulaLabel() override {} //TODO - void fillColumnWithFormula(Poincare::Expression * formula) override {} //TODO + void setFormulaLabel() override; + void fillColumnWithFormula(Poincare::Expression * formula) override; void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; private: HighlightCell * titleCells(int index) override; From 8c883b71bf27ea8bb524fbc7fddc22b80d01da94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 1 Jun 2018 12:00:03 +0200 Subject: [PATCH 134/156] [apps/shared] Handle formula input abort --- apps/shared/store_controller.cpp | 10 ++++++++++ apps/shared/store_controller.h | 1 + 2 files changed, 11 insertions(+) diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index a7e360236..02c60d582 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -97,6 +97,16 @@ bool StoreController::textFieldDidFinishEditing(TextField * textField, const cha return true; } +bool StoreController::textFieldDidAbortEditing(TextField * textField) { + if (textField == contentView()->formulaInputView()->textField()) { + contentView()->displayFormulaInput(false); + app()->setFirstResponder(contentView()); + return true; + } + return EditableCellTableViewController::textFieldDidAbortEditing(textField); +} + + int StoreController::numberOfColumns() { return FloatPairStore::k_numberOfColumnsPerSeries * FloatPairStore::k_numberOfSeries; } diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 9164c1044..d04a7028b 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -21,6 +21,7 @@ public: // TextFieldDelegate bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override; + bool textFieldDidAbortEditing(TextField * textField) override; // TableViewDataSource int numberOfColumns() override; From 70ad21232dd6752ff7643a6160a11edf5e98880b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 4 Jun 2018 09:55:25 +0200 Subject: [PATCH 135/156] [apps/shared] FloatPairStore -> DoublePairStore --- apps/regression/calculation_controller.cpp | 2 +- apps/regression/graph_controller.cpp | 2 +- apps/regression/regression_context.cpp | 2 +- apps/regression/regression_context.h | 6 ++--- apps/regression/store.cpp | 6 ++--- apps/regression/store.h | 4 ++-- apps/regression/store_controller.cpp | 4 ++-- apps/shared/Makefile | 2 +- ...t_pair_store.cpp => double_pair_store.cpp} | 20 ++++++++--------- ...float_pair_store.h => double_pair_store.h} | 10 ++++----- apps/shared/store_controller.cpp | 22 +++++++++---------- apps/shared/store_controller.h | 12 +++++----- apps/shared/store_parameter_controller.cpp | 2 +- apps/shared/store_parameter_controller.h | 6 ++--- apps/shared/store_selectable_table_view.cpp | 4 ++-- apps/shared/store_selectable_table_view.h | 6 ++--- apps/statistics/calculation_controller.cpp | 2 +- .../histogram_parameter_controller.cpp | 16 +++++++------- apps/statistics/multiple_boxes_view.cpp | 6 ++--- apps/statistics/multiple_histograms_view.cpp | 6 ++--- apps/statistics/series_context.cpp | 2 +- apps/statistics/series_context.h | 6 ++--- apps/statistics/store.cpp | 18 +++++++-------- apps/statistics/store.h | 6 ++--- apps/statistics/store_controller.cpp | 6 ++--- 25 files changed, 89 insertions(+), 89 deletions(-) rename apps/shared/{float_pair_store.cpp => double_pair_store.cpp} (81%) rename apps/shared/{float_pair_store.h => double_pair_store.h} (89%) diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index a424df843..d48de5009 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -141,7 +141,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int } int seriesNumber = m_store->indexOfKthNonEmptySeries(i - 1); - assert(i >= 0 && seriesNumber < FloatPairStore::k_numberOfSeries); + assert(i >= 0 && seriesNumber < DoublePairStore::k_numberOfSeries); // Coordinate and series title if (j == 0 && i > 0) { diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index da596897c..60657f202 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -30,7 +30,7 @@ bool GraphController::isEmpty() const { if (m_store->isEmpty()) { return true; } - for (int series = 0; series < FloatPairStore::k_numberOfSeries; series++) { + for (int series = 0; series < DoublePairStore::k_numberOfSeries; series++) { if (!m_store->seriesIsEmpty(series) && !std::isinf(m_store->slope(series)) && !std::isnan(m_store->slope(series))) { return false; } diff --git a/apps/regression/regression_context.cpp b/apps/regression/regression_context.cpp index edc1ac6cd..89070e89a 100644 --- a/apps/regression/regression_context.cpp +++ b/apps/regression/regression_context.cpp @@ -18,7 +18,7 @@ const Expression * RegressionContext::expressionForSymbol(const Symbol * symbol) assert(strlen(seriesName) == 2); int series = (int)(seriesName[1] - '0') - 1; - assert(series >= 0 && series < FloatPairStore::k_numberOfSeries); + assert(series >= 0 && series < DoublePairStore::k_numberOfSeries); assert((seriesName[0] == 'X') || (seriesName[0] == 'Y')); int storeI = seriesName[0] == 'X' ? 0 : 1; diff --git a/apps/regression/regression_context.h b/apps/regression/regression_context.h index 8c4237a9a..966b63a0b 100644 --- a/apps/regression/regression_context.h +++ b/apps/regression/regression_context.h @@ -2,13 +2,13 @@ #define REGRESSION_REGRESSION_CONTEXT_H #include -#include "../shared/float_pair_store.h" +#include "../shared/double_pair_store.h" namespace Regression { class RegressionContext : public Poincare::Context { public: - RegressionContext(Shared::FloatPairStore * store, Poincare::Context * parentContext = nullptr) : + RegressionContext(Shared::DoublePairStore * store, Poincare::Context * parentContext = nullptr) : Poincare::Context(), m_store(store), m_seriesPairIndex(-1), @@ -18,7 +18,7 @@ public: void setExpressionForSymbolName(const Poincare::Expression * expression, const Poincare::Symbol * symbol, Poincare::Context & context) override; const Poincare::Expression * expressionForSymbol(const Poincare::Symbol * symbol) override; private: - Shared::FloatPairStore * m_store; + Shared::DoublePairStore * m_store; int m_seriesPairIndex; Poincare::Context * m_parentContext; }; diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index 86a6acc28..eb0a7be38 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -13,7 +13,7 @@ static inline float min(float x, float y) { return (x= 0 && k < numberOfNonEmptySeries()); int nonEmptySeriesCount = 0; for (int i = 0; i < k_numberOfSeries; i++) { @@ -214,7 +214,7 @@ int Store::indexOfKthNonEmptySeries(int k) const { /* Calculations */ double Store::doubleCastedNumberOfPairsOfSeries(int series) const { - return FloatPairStore::numberOfPairsOfSeries(series); + return DoublePairStore::numberOfPairsOfSeries(series); } float Store::maxValueOfColumn(int series, int i) const { diff --git a/apps/regression/store.h b/apps/regression/store.h index ed1d7d206..1e34e8d77 100644 --- a/apps/regression/store.h +++ b/apps/regression/store.h @@ -2,14 +2,14 @@ #define REGRESSION_STORE_H #include "../shared/interactive_curve_view_range.h" -#include "../shared/float_pair_store.h" +#include "../shared/double_pair_store.h" extern "C" { #include } namespace Regression { -class Store : public Shared::InteractiveCurveViewRange, public Shared::FloatPairStore, public Shared::InteractiveCurveViewRangeDelegate { +class Store : public Shared::InteractiveCurveViewRange, public Shared::DoublePairStore, public Shared::InteractiveCurveViewRangeDelegate { public: Store(); diff --git a/apps/regression/store_controller.cpp b/apps/regression/store_controller.cpp index 0de633e93..70e822e51 100644 --- a/apps/regression/store_controller.cpp +++ b/apps/regression/store_controller.cpp @@ -37,7 +37,7 @@ void StoreController::fillColumnWithFormula(Expression * formula) { const char * seriesName = Symbol::textForSpecialSymbols(variables[index]); assert(strlen(seriesName) == 2); int series = (int)(seriesName[1] - '0') - 1; - assert(series >= 0 && series < FloatPairStore::k_numberOfSeries); + assert(series >= 0 && series < DoublePairStore::k_numberOfSeries); if (numberOfValuesToCompute == -1) { numberOfValuesToCompute = m_store->numberOfPairsOfSeries(series); } else { @@ -46,7 +46,7 @@ void StoreController::fillColumnWithFormula(Expression * formula) { index++; } if (numberOfValuesToCompute == -1) { - numberOfValuesToCompute = m_store->numberOfPairsOfSeries(selectedColumn()/FloatPairStore::k_numberOfColumnsPerSeries); + numberOfValuesToCompute = m_store->numberOfPairsOfSeries(selectedColumn()/DoublePairStore::k_numberOfColumnsPerSeries); } RegressionContext regressionContext(m_store, const_cast(static_cast(app()->container()))->globalContext()); diff --git a/apps/shared/Makefile b/apps/shared/Makefile index 5f92d24d0..3242b58ae 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -10,7 +10,7 @@ app_objs += $(addprefix apps/shared/,\ editable_cell_table_view_controller.o\ expression_field_delegate_app.o\ expression_layout_field_delegate.o\ - float_pair_store.o\ + double_pair_store.o\ float_parameter_controller.o\ function.o\ function_app.o\ diff --git a/apps/shared/float_pair_store.cpp b/apps/shared/double_pair_store.cpp similarity index 81% rename from apps/shared/float_pair_store.cpp rename to apps/shared/double_pair_store.cpp index c58ceb812..32e84c558 100644 --- a/apps/shared/float_pair_store.cpp +++ b/apps/shared/double_pair_store.cpp @@ -1,4 +1,4 @@ -#include "float_pair_store.h" +#include "double_pair_store.h" #include #include #include @@ -6,7 +6,7 @@ namespace Shared { -void FloatPairStore::set(double f, int series, int i, int j) { +void DoublePairStore::set(double f, int series, int i, int j) { assert(series >= 0 && series < k_numberOfSeries); if (j >= k_maxNumberOfPairs) { return; @@ -19,7 +19,7 @@ void FloatPairStore::set(double f, int series, int i, int j) { } } -int FloatPairStore::numberOfPairs() const { +int DoublePairStore::numberOfPairs() const { int result = 0; for (int i = 0; i < k_numberOfSeries; i++) { result += m_numberOfPairs[i]; @@ -27,7 +27,7 @@ int FloatPairStore::numberOfPairs() const { return result; } -void FloatPairStore::deletePairOfSeriesAtIndex(int series, int j) { +void DoublePairStore::deletePairOfSeriesAtIndex(int series, int j) { m_numberOfPairs[series]--; for (int k = j; k < m_numberOfPairs[series]; k++) { m_data[series][0][k] = m_data[series][0][k+1]; @@ -39,7 +39,7 @@ void FloatPairStore::deletePairOfSeriesAtIndex(int series, int j) { m_data[series][1][m_numberOfPairs[series]] = 0; } -void FloatPairStore::deleteAllPairsOfSeries(int series) { +void DoublePairStore::deleteAllPairsOfSeries(int series) { assert(series >= 0 && series < k_numberOfSeries); /* We reset all values to 0 to ensure the correctness of the checksum.*/ for (int k = 0; k < m_numberOfPairs[series]; k++) { @@ -49,13 +49,13 @@ void FloatPairStore::deleteAllPairsOfSeries(int series) { m_numberOfPairs[series] = 0; } -void FloatPairStore::deleteAllPairs() { +void DoublePairStore::deleteAllPairs() { for (int i = 0; i < k_numberOfSeries; i ++) { deleteAllPairsOfSeries(i); } } -void FloatPairStore::resetColumn(int series, int i) { +void DoublePairStore::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++) { @@ -63,7 +63,7 @@ void FloatPairStore::resetColumn(int series, int i) { } } -double FloatPairStore::sumOfColumn(int series, int i) const { +double DoublePairStore::sumOfColumn(int series, int i) const { assert(series >= 0 && series < k_numberOfSeries); assert(i == 0 || i == 1); double result = 0; @@ -73,7 +73,7 @@ double FloatPairStore::sumOfColumn(int series, int i) const { return result; } -uint32_t FloatPairStore::storeChecksum() const { +uint32_t DoublePairStore::storeChecksum() const { /* Ideally, we would only compute the checksum of the first m_numberOfPairs * pairs. However, the two values of a pair are not stored consecutively. We * thus compute the checksum on all pairs and ensure to set the pair at 0 @@ -83,7 +83,7 @@ uint32_t FloatPairStore::storeChecksum() const { return Ion::crc32((uint32_t *)m_data, dataLengthInBytes/sizeof(uint32_t)); } -double FloatPairStore::defaultValue(int series, int i, int j) const { +double DoublePairStore::defaultValue(int series, int i, int j) const { assert(series >= 0 && series < k_numberOfSeries); if(i == 0 && j > 1) { return 2*m_data[series][i][j-1]-m_data[series][i][j-2]; diff --git a/apps/shared/float_pair_store.h b/apps/shared/double_pair_store.h similarity index 89% rename from apps/shared/float_pair_store.h rename to apps/shared/double_pair_store.h index 68a8cb1f0..f172e3e11 100644 --- a/apps/shared/float_pair_store.h +++ b/apps/shared/double_pair_store.h @@ -1,5 +1,5 @@ -#ifndef SHARED_FLOAT_PAIR_STORE_H -#define SHARED_FLOAT_PAIR_STORE_H +#ifndef SHARED_DOUBLE_PAIR_STORE_H +#define SHARED_DOUBLE_PAIR_STORE_H #include #include @@ -8,17 +8,17 @@ namespace Shared { -class FloatPairStore { +class DoublePairStore { public: constexpr static int k_numberOfSeries = 3; constexpr static int k_numberOfColumnsPerSeries = 2; constexpr static int k_maxNumberOfPairs = 100; - FloatPairStore() : + DoublePairStore() : m_data{}, m_numberOfPairs{} {} // Delete the implicit copy constructor: the object is heavy - FloatPairStore(const FloatPairStore&) = delete; + DoublePairStore(const DoublePairStore&) = delete; double get(int series, int i, int j) const { assert(j < m_numberOfPairs[series]); return m_data[series][i][j]; diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 02c60d582..5879ddb89 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -8,7 +8,7 @@ using namespace Poincare; namespace Shared { -StoreController::ContentView::ContentView(FloatPairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, TextFieldDelegate * textFieldDelegate) : +StoreController::ContentView::ContentView(DoublePairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, TextFieldDelegate * textFieldDelegate) : View(), Responder(parentResponder), m_dataView(store, this, dataSource, selectionDataSource), @@ -48,7 +48,7 @@ KDRect StoreController::ContentView::formulaFrame() const { return KDRect(0, bounds().height() - BufferTextViewWithTextField::k_height, bounds().width(), m_displayFormulaInputView ? BufferTextViewWithTextField::k_height : 0); } -StoreController::StoreController(Responder * parentResponder, FloatPairStore * store, ButtonRowController * header) : +StoreController::StoreController(Responder * parentResponder, DoublePairStore * store, ButtonRowController * header) : EditableCellTableViewController(parentResponder), ButtonRowDelegate(header, nullptr), m_editableCells{}, @@ -108,7 +108,7 @@ bool StoreController::textFieldDidAbortEditing(TextField * textField) { int StoreController::numberOfColumns() { - return FloatPairStore::k_numberOfColumnsPerSeries * FloatPairStore::k_numberOfSeries; + return DoublePairStore::k_numberOfColumnsPerSeries * DoublePairStore::k_numberOfSeries; } KDCoordinate StoreController::columnWidth(int i) { @@ -149,7 +149,7 @@ int StoreController::typeAtLocation(int i, int j) { void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { // Handle the separator if (cellAtLocationIsEditable(i, j)) { - bool shouldHaveLeftSeparator = i % FloatPairStore::k_numberOfColumnsPerSeries == 0; + bool shouldHaveLeftSeparator = i % DoublePairStore::k_numberOfColumnsPerSeries == 0; static_cast(cell)->setSeparatorLeft(shouldHaveLeftSeparator); } // Handle empty cells @@ -184,14 +184,14 @@ bool StoreController::handleEvent(Ion::Events::Event event) { assert(selectedColumn() >= 0 && selectedColumn() < numberOfColumns()); int series = seriesAtColumn(selectedColumn()); if ((event == Ion::Events::OK || event == Ion::Events::EXE) && selectedRow() == 0) { - m_storeParameterController.selectXColumn(selectedColumn()%FloatPairStore::k_numberOfColumnsPerSeries == 0); + m_storeParameterController.selectXColumn(selectedColumn()%DoublePairStore::k_numberOfColumnsPerSeries == 0); m_storeParameterController.selectSeries(series); StackViewController * stack = ((StackViewController *)parentResponder()->parentResponder()); stack->push(&m_storeParameterController); return true; } if (event == Ion::Events::Backspace) { - if (selectedRow() == 0 || selectedRow() > m_store->numberOfPairsOfSeries(selectedColumn()/FloatPairStore::k_numberOfColumnsPerSeries)) { + if (selectedRow() == 0 || selectedRow() > m_store->numberOfPairsOfSeries(selectedColumn()/DoublePairStore::k_numberOfColumnsPerSeries)) { return false; } m_store->deletePairOfSeriesAtIndex(series, selectedRow()-1); @@ -225,24 +225,24 @@ bool StoreController::cellAtLocationIsEditable(int columnIndex, int rowIndex) { } bool StoreController::setDataAtLocation(double floatBody, int columnIndex, int rowIndex) { - m_store->set(floatBody, seriesAtColumn(columnIndex), columnIndex%FloatPairStore::k_numberOfColumnsPerSeries, rowIndex-1); + m_store->set(floatBody, seriesAtColumn(columnIndex), columnIndex%DoublePairStore::k_numberOfColumnsPerSeries, rowIndex-1); return true; } double StoreController::dataAtLocation(int columnIndex, int rowIndex) { - return m_store->get(seriesAtColumn(columnIndex), columnIndex%FloatPairStore::k_numberOfColumnsPerSeries, rowIndex-1); + return m_store->get(seriesAtColumn(columnIndex), columnIndex%DoublePairStore::k_numberOfColumnsPerSeries, rowIndex-1); } int StoreController::numberOfElements() { int result = 0; - for (int i = 0; i < FloatPairStore::k_numberOfSeries; i++) { + for (int i = 0; i < DoublePairStore::k_numberOfSeries; i++) { result = max(result, m_store->numberOfPairsOfSeries(i)); } return result; } int StoreController::maxNumberOfElements() const { - return FloatPairStore::k_maxNumberOfPairs; + return DoublePairStore::k_maxNumberOfPairs; } View * StoreController::loadView() { @@ -262,7 +262,7 @@ void StoreController::unloadView(View * view) { } bool StoreController::cellShouldBeTransparent(int i, int j) { - int seriesIndex = i/FloatPairStore::k_numberOfColumnsPerSeries; + int seriesIndex = i/DoublePairStore::k_numberOfColumnsPerSeries; return j > 1 + m_store->numberOfPairsOfSeries(seriesIndex); } diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index d04a7028b..a99191040 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -4,7 +4,7 @@ #include #include "buffer_text_view_with_text_field.h" #include "editable_cell_table_view_controller.h" -#include "float_pair_store.h" +#include "double_pair_store.h" #include "store_cell.h" #include "store_parameter_controller.h" #include "store_selectable_table_view.h" @@ -13,7 +13,7 @@ namespace Shared { class StoreController : public EditableCellTableViewController, public ButtonRowDelegate { public: - StoreController(Responder * parentResponder, FloatPairStore * store, ButtonRowController * header); + StoreController(Responder * parentResponder, DoublePairStore * store, ButtonRowController * header); void displayFormulaInput(); virtual void setFormulaLabel() = 0; @@ -44,14 +44,14 @@ protected: static constexpr KDCoordinate k_cellWidth = 116; static constexpr KDCoordinate k_margin = 8; static constexpr KDCoordinate k_scrollBarMargin = Metric::CommonRightMargin; - constexpr static int k_maxNumberOfEditableCells = 22 * FloatPairStore::k_numberOfSeries; + constexpr static int k_maxNumberOfEditableCells = 22 * DoublePairStore::k_numberOfSeries; constexpr static int k_numberOfTitleCells = 4; static constexpr int k_titleCellType = 0; static constexpr int k_editableCellType = 1; class ContentView : public View , public Responder { public: - ContentView(FloatPairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, TextFieldDelegate * textFieldDelegate); + ContentView(DoublePairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, TextFieldDelegate * textFieldDelegate); StoreSelectableTableView * dataView() { return &m_dataView; } BufferTextViewWithTextField * formulaInputView() { return &m_formulaInputView; } void displayFormulaInput(bool display); @@ -80,9 +80,9 @@ protected: int maxNumberOfElements() const override; virtual HighlightCell * titleCells(int index) = 0; char m_draftTextBuffer[TextField::maxBufferSize()]; - int seriesAtColumn(int column) const { return column / FloatPairStore::k_numberOfColumnsPerSeries; } + int seriesAtColumn(int column) const { return column / DoublePairStore::k_numberOfColumnsPerSeries; } StoreCell * m_editableCells[k_maxNumberOfEditableCells]; - FloatPairStore * m_store; + DoublePairStore * m_store; StoreParameterController m_storeParameterController; private: bool cellShouldBeTransparent(int i, int j); diff --git a/apps/shared/store_parameter_controller.cpp b/apps/shared/store_parameter_controller.cpp index 6625dd84e..49ffb86ce 100644 --- a/apps/shared/store_parameter_controller.cpp +++ b/apps/shared/store_parameter_controller.cpp @@ -4,7 +4,7 @@ namespace Shared { -StoreParameterController::StoreParameterController(Responder * parentResponder, FloatPairStore * store, StoreController * storeController) : +StoreParameterController::StoreParameterController(Responder * parentResponder, DoublePairStore * store, StoreController * storeController) : ViewController(parentResponder), m_deleteColumn(I18n::Message::ClearColumn), m_fillWithFormula(I18n::Message::FillWithFormula), diff --git a/apps/shared/store_parameter_controller.h b/apps/shared/store_parameter_controller.h index c7df1c8a0..be713c2ef 100644 --- a/apps/shared/store_parameter_controller.h +++ b/apps/shared/store_parameter_controller.h @@ -2,7 +2,7 @@ #define SHARED_STORE_PARAM_CONTROLLER_H #include -#include "float_pair_store.h" +#include "double_pair_store.h" #include "../i18n.h" namespace Shared { @@ -11,7 +11,7 @@ class StoreController; class StoreParameterController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource { public: - StoreParameterController(Responder * parentResponder, FloatPairStore * store, StoreController * storeController); + StoreParameterController(Responder * parentResponder, DoublePairStore * store, StoreController * storeController); void selectXColumn(bool xColumnSelected) { m_xColumnSelected = xColumnSelected; } void selectSeries(int series) { m_series = series; } View * view() override { return &m_selectableTableView; } @@ -33,7 +33,7 @@ private: MessageTableCell m_deleteColumn; MessageTableCell m_fillWithFormula; SelectableTableView m_selectableTableView; - FloatPairStore * m_store; + DoublePairStore * m_store; StoreController * m_storeController; bool m_xColumnSelected; int m_series; diff --git a/apps/shared/store_selectable_table_view.cpp b/apps/shared/store_selectable_table_view.cpp index ec6adcf3d..d7826f164 100644 --- a/apps/shared/store_selectable_table_view.cpp +++ b/apps/shared/store_selectable_table_view.cpp @@ -2,7 +2,7 @@ namespace Shared { -StoreSelectableTableView::StoreSelectableTableView(FloatPairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, SelectableTableViewDelegate * delegate) : +StoreSelectableTableView::StoreSelectableTableView(DoublePairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, SelectableTableViewDelegate * delegate) : SelectableTableView(parentResponder, dataSource, selectionDataSource, delegate), m_store(store) { @@ -31,7 +31,7 @@ bool StoreSelectableTableView::selecNonHiddenCellAtLocation(int i, int j) { if (j < 0 || j >= dataSource()->numberOfRows()) { return false; } - int seriesIndex = i/FloatPairStore::k_numberOfColumnsPerSeries; + int seriesIndex = i/DoublePairStore::k_numberOfColumnsPerSeries; int numberOfPairsOfCurrentSeries = m_store->numberOfPairsOfSeries(seriesIndex); if (j > 1 + numberOfPairsOfCurrentSeries) { return selectCellAtLocation(i, 1 + numberOfPairsOfCurrentSeries); diff --git a/apps/shared/store_selectable_table_view.h b/apps/shared/store_selectable_table_view.h index ca370b3ef..33a4d3042 100644 --- a/apps/shared/store_selectable_table_view.h +++ b/apps/shared/store_selectable_table_view.h @@ -2,17 +2,17 @@ #define APPS_SHARED_STORE_SELECTABLE_TABLE_VIEW_H #include -#include "float_pair_store.h" +#include "double_pair_store.h" namespace Shared { class StoreSelectableTableView : public SelectableTableView { public: - StoreSelectableTableView(FloatPairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource = nullptr, SelectableTableViewDelegate * delegate = nullptr); + StoreSelectableTableView(DoublePairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource = nullptr, SelectableTableViewDelegate * delegate = nullptr); bool handleEvent(Ion::Events::Event event) override; private: bool selecNonHiddenCellAtLocation(int i, int j); - FloatPairStore * m_store; + DoublePairStore * m_store; }; } diff --git a/apps/statistics/calculation_controller.cpp b/apps/statistics/calculation_controller.cpp index 48fe4e700..372143634 100644 --- a/apps/statistics/calculation_controller.cpp +++ b/apps/statistics/calculation_controller.cpp @@ -55,7 +55,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int char titleBuffer[] = {'V', static_cast('0'+seriesNumber), '/', 'N', static_cast('0'+seriesNumber), 0}; StoreTitleCell * storeTitleCell = static_cast(cell); storeTitleCell->setText(titleBuffer); - storeTitleCell->setColor(FloatPairStore::colorOfSeriesAtIndex(seriesNumber)); + storeTitleCell->setColor(DoublePairStore::colorOfSeriesAtIndex(seriesNumber)); return; } if (i == 0) { diff --git a/apps/statistics/histogram_parameter_controller.cpp b/apps/statistics/histogram_parameter_controller.cpp index 1ffc16f13..e2d187afd 100644 --- a/apps/statistics/histogram_parameter_controller.cpp +++ b/apps/statistics/histogram_parameter_controller.cpp @@ -43,19 +43,19 @@ bool HistogramParameterController::setParameterAtIndex(int parameterIndex, doubl } // There should be at least one value in the drawn bin - for (int i = 0; i < FloatPairStore::k_numberOfSeries; i++) { + for (int i = 0; i < DoublePairStore::k_numberOfSeries; i++) { if (m_store->firstDrawnBarAbscissa() <= m_store->maxValue(i)+f) { break; - } else if (i == FloatPairStore::k_numberOfSeries - 1) { + } else if (i == DoublePairStore::k_numberOfSeries - 1) { app()->displayWarning(I18n::Message::ForbiddenValue); return false; } } // The number of bars cannot be above the max - assert(FloatPairStore::k_numberOfSeries > 0); + assert(DoublePairStore::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++) { + for (int i = 1; i < DoublePairStore::k_numberOfSeries; i++) { double numberOfBars = std::ceil((m_store->maxValue(i) - m_store->minValue(i))/f); if (maxNewNumberOfBars < numberOfBars) { maxNewNumberOfBars = numberOfBars; @@ -70,9 +70,9 @@ bool HistogramParameterController::setParameterAtIndex(int parameterIndex, doubl m_store->setBarWidth(f); } else { // The number of bars cannot be above the max - assert(FloatPairStore::k_numberOfSeries > 0); + assert(DoublePairStore::k_numberOfSeries > 0); double maxNewNumberOfBars = ceilf((m_store->maxValue(0) - f)/m_store->barWidth()); - for (int i = 1; i < FloatPairStore::k_numberOfSeries; i++) { + for (int i = 1; i < DoublePairStore::k_numberOfSeries; i++) { double numberOfBars = ceilf((m_store->maxValue(i) - f)/m_store->barWidth()); if (maxNewNumberOfBars < numberOfBars) { maxNewNumberOfBars = numberOfBars; @@ -83,10 +83,10 @@ bool HistogramParameterController::setParameterAtIndex(int parameterIndex, doubl return false; } // There should be at least one value in the drawn bin - for (int i = 0; i < FloatPairStore::k_numberOfSeries; i++) { + for (int i = 0; i < DoublePairStore::k_numberOfSeries; i++) { if (f <= m_store->maxValue(i)+m_store->barWidth()) { break; - } else if (i == FloatPairStore::k_numberOfSeries - 1) { + } else if (i == DoublePairStore::k_numberOfSeries - 1) { app()->displayWarning(I18n::Message::ForbiddenValue); return false; } diff --git a/apps/statistics/multiple_boxes_view.cpp b/apps/statistics/multiple_boxes_view.cpp index edb29502d..dc4a8c24f 100644 --- a/apps/statistics/multiple_boxes_view.cpp +++ b/apps/statistics/multiple_boxes_view.cpp @@ -7,9 +7,9 @@ namespace Statistics { MultipleBoxesView::MultipleBoxesView(BoxController * controller, Store * store, BoxView::Quantile * selectedQuantile) : MultipleDataView(store), - m_boxView1(controller, store, 0, nullptr, selectedQuantile, FloatPairStore::colorOfSeriesAtIndex(0), FloatPairStore::colorLightOfSeriesAtIndex(0)), - m_boxView2(controller, store, 1, nullptr, selectedQuantile, FloatPairStore::colorOfSeriesAtIndex(1), FloatPairStore::colorLightOfSeriesAtIndex(1)), - m_boxView3(controller, store, 2, nullptr, selectedQuantile, FloatPairStore::colorOfSeriesAtIndex(2), FloatPairStore::colorLightOfSeriesAtIndex(2)), + m_boxView1(controller, store, 0, nullptr, selectedQuantile, DoublePairStore::colorOfSeriesAtIndex(0), DoublePairStore::colorLightOfSeriesAtIndex(0)), + m_boxView2(controller, store, 1, nullptr, selectedQuantile, DoublePairStore::colorOfSeriesAtIndex(1), DoublePairStore::colorLightOfSeriesAtIndex(1)), + m_boxView3(controller, store, 2, nullptr, selectedQuantile, DoublePairStore::colorOfSeriesAtIndex(2), DoublePairStore::colorLightOfSeriesAtIndex(2)), m_axisView(store), m_bannerView() { diff --git a/apps/statistics/multiple_histograms_view.cpp b/apps/statistics/multiple_histograms_view.cpp index 5acfc9a74..2ac29e4b3 100644 --- a/apps/statistics/multiple_histograms_view.cpp +++ b/apps/statistics/multiple_histograms_view.cpp @@ -7,9 +7,9 @@ namespace Statistics { MultipleHistogramsView::MultipleHistogramsView(HistogramController * controller, Store * store) : MultipleDataView(store), - m_histogramView1(controller, store, 0, nullptr, FloatPairStore::colorOfSeriesAtIndex(0)), - m_histogramView2(controller, store, 1, nullptr, FloatPairStore::colorOfSeriesAtIndex(1)), - m_histogramView3(controller, store, 2, nullptr, FloatPairStore::colorOfSeriesAtIndex(2)), + m_histogramView1(controller, store, 0, nullptr, DoublePairStore::colorOfSeriesAtIndex(0)), + m_histogramView2(controller, store, 1, nullptr, DoublePairStore::colorOfSeriesAtIndex(1)), + m_histogramView3(controller, store, 2, nullptr, DoublePairStore::colorOfSeriesAtIndex(2)), m_bannerView(), m_okView() { diff --git a/apps/statistics/series_context.cpp b/apps/statistics/series_context.cpp index 68b875705..8628a83db 100644 --- a/apps/statistics/series_context.cpp +++ b/apps/statistics/series_context.cpp @@ -18,7 +18,7 @@ const Expression * SeriesContext::expressionForSymbol(const Symbol * symbol) { assert(strlen(seriesName) == 2); int series = (int)(seriesName[1] - '0') - 1; - assert(series >= 0 && series < FloatPairStore::k_numberOfSeries); + assert(series >= 0 && series < DoublePairStore::k_numberOfSeries); assert((seriesName[0] == 'V') || (seriesName[0] == 'N')); int storeI = seriesName[0] == 'V' ? 0 : 1; diff --git a/apps/statistics/series_context.h b/apps/statistics/series_context.h index fac98891f..81799a113 100644 --- a/apps/statistics/series_context.h +++ b/apps/statistics/series_context.h @@ -2,13 +2,13 @@ #define STATISTICS_SERIES_CONTEXT_H #include -#include "../shared/float_pair_store.h" +#include "../shared/double_pair_store.h" namespace Statistics { class SeriesContext : public Poincare::Context { public: - SeriesContext(Shared::FloatPairStore * store, Poincare::Context * parentContext = nullptr) : + SeriesContext(Shared::DoublePairStore * store, Poincare::Context * parentContext = nullptr) : Poincare::Context(), m_store(store), m_seriesPairIndex(-1), @@ -18,7 +18,7 @@ public: void setExpressionForSymbolName(const Poincare::Expression * expression, const Poincare::Symbol * symbol, Poincare::Context & context) override; const Poincare::Expression * expressionForSymbol(const Poincare::Symbol * symbol) override; private: - Shared::FloatPairStore * m_store; + Shared::DoublePairStore * m_store; int m_seriesPairIndex; Poincare::Context * m_parentContext; }; diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index cc633b7c9..293c1e1f4 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -13,7 +13,7 @@ static_assert(Store::k_numberOfSeries == 3, "The constructor of Statistics::Stor Store::Store() : MemoizedCurveViewRange(), - FloatPairStore(), + DoublePairStore(), m_barWidth(1.0), m_firstDrawnBarAbscissa(0.0), m_seriesEmpty{true, true, true}, @@ -119,9 +119,9 @@ double Store::sumOfOccurrences(int series) const { } double Store::maxValueForAllSeries() const { - assert(FloatPairStore::k_numberOfSeries > 0); + assert(DoublePairStore::k_numberOfSeries > 0); double result = maxValue(0); - for (int i = 1; i < FloatPairStore::k_numberOfSeries; i++) { + for (int i = 1; i < DoublePairStore::k_numberOfSeries; i++) { double maxCurrentSeries = maxValue(i); if (result < maxCurrentSeries) { result = maxCurrentSeries; @@ -131,9 +131,9 @@ double Store::maxValueForAllSeries() const { } double Store::minValueForAllSeries() const { - assert(FloatPairStore::k_numberOfSeries > 0); + assert(DoublePairStore::k_numberOfSeries > 0); double result = minValue(0); - for (int i = 1; i < FloatPairStore::k_numberOfSeries; i++) { + for (int i = 1; i < DoublePairStore::k_numberOfSeries; i++) { double minCurrentSeries = minValue(i); if (result > minCurrentSeries) { result = minCurrentSeries; @@ -225,19 +225,19 @@ double Store::squaredValueSum(int series) const { } void Store::set(double f, int series, int i, int j) { - FloatPairStore::set(f, series, i, j); + DoublePairStore::set(f, series, i, j); m_seriesEmpty[series] = sumOfOccurrences(series) == 0; updateNonEmptySeriesCount(); } void Store::deletePairOfSeriesAtIndex(int series, int j) { - FloatPairStore::deletePairOfSeriesAtIndex(series, j); + DoublePairStore::deletePairOfSeriesAtIndex(series, j); m_seriesEmpty[series] = sumOfOccurrences(series) == 0; updateNonEmptySeriesCount(); } void Store::deleteAllPairsOfSeries(int series) { - FloatPairStore::deleteAllPairsOfSeries(series); + DoublePairStore::deleteAllPairsOfSeries(series); m_seriesEmpty[series] = true; updateNonEmptySeriesCount(); } @@ -255,7 +255,7 @@ void Store::updateNonEmptySeriesCount() { /* Private methods */ double Store::defaultValue(int series, int i, int j) const { - return i == 0 ? FloatPairStore::defaultValue(series, i, j) : 1.0; + return i == 0 ? DoublePairStore::defaultValue(series, i, j) : 1.0; } double Store::sumOfValuesBetween(int series, double x1, double x2) const { diff --git a/apps/statistics/store.h b/apps/statistics/store.h index 99381d46d..fa4839c83 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -2,11 +2,11 @@ #define STATISTICS_STORE_H #include "../shared/memoized_curve_view_range.h" -#include "../shared/float_pair_store.h" +#include "../shared/double_pair_store.h" namespace Statistics { -class Store : public Shared::MemoizedCurveViewRange, public Shared::FloatPairStore { +class Store : public Shared::MemoizedCurveViewRange, public Shared::DoublePairStore { public: Store(); uint32_t barChecksum() const; @@ -50,7 +50,7 @@ public: constexpr static int k_bottomMargin = 20; constexpr static float k_displayLeftMarginRatio = 0.04f; - // FloatPairStore + // DoublePairStore void set(double f, int series, int i, int j) override; void deletePairOfSeriesAtIndex(int series, int j) override; void deleteAllPairsOfSeries(int series) override; diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index c17b5974c..943c36a9b 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -37,7 +37,7 @@ void StoreController::fillColumnWithFormula(Expression * formula) { const char * seriesName = Symbol::textForSpecialSymbols(variables[index]); assert(strlen(seriesName) == 2); int series = (int)(seriesName[1] - '0') - 1; - assert(series >= 0 && series < FloatPairStore::k_numberOfSeries); + assert(series >= 0 && series < DoublePairStore::k_numberOfSeries); if (numberOfValuesToCompute == -1) { numberOfValuesToCompute = m_store->numberOfPairsOfSeries(series); } else { @@ -46,7 +46,7 @@ void StoreController::fillColumnWithFormula(Expression * formula) { index++; } if (numberOfValuesToCompute == -1) { - numberOfValuesToCompute = m_store->numberOfPairsOfSeries(selectedColumn()/FloatPairStore::k_numberOfColumnsPerSeries); + numberOfValuesToCompute = m_store->numberOfPairsOfSeries(selectedColumn()/DoublePairStore::k_numberOfColumnsPerSeries); } SeriesContext seriesContext(m_store, const_cast(static_cast(app()->container()))->globalContext()); @@ -69,7 +69,7 @@ void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int bool isValuesColumn = i%Store::k_numberOfColumnsPerSeries == 0; mytitleCell->setSeparatorLeft(isValuesColumn); int seriesIndex = i/Store::k_numberOfColumnsPerSeries; - assert(seriesIndex >= 0 && seriesIndex < FloatPairStore::k_numberOfSeries); + assert(seriesIndex >= 0 && seriesIndex < DoublePairStore::k_numberOfSeries); if (isValuesColumn) { I18n::Message valuesMessages[] = {I18n::Message::Values1, I18n::Message::Values2, I18n::Message::Values3}; mytitleCell->setText(I18n::translate(valuesMessages[seriesIndex])); From 4fec4f0b9e6d85413e6f687bffb3fd43b66f800b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 4 Jun 2018 10:07:26 +0200 Subject: [PATCH 136/156] [apps/statistics] SeriesContext -> StatisticsContext --- apps/statistics/Makefile | 2 +- .../{series_context.cpp => statistics_context.cpp} | 6 +++--- .../statistics/{series_context.h => statistics_context.h} | 8 ++++---- apps/statistics/store_controller.cpp | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) rename apps/statistics/{series_context.cpp => statistics_context.cpp} (79%) rename apps/statistics/{series_context.h => statistics_context.h} (73%) diff --git a/apps/statistics/Makefile b/apps/statistics/Makefile index bfafe7973..0caf06634 100644 --- a/apps/statistics/Makefile +++ b/apps/statistics/Makefile @@ -18,7 +18,7 @@ app_objs += $(addprefix apps/statistics/,\ multiple_data_view.o\ multiple_data_view_controller.o\ multiple_histograms_view.o\ - series_context.o\ + statistics_context.o\ store.o\ store_controller.o\ ) diff --git a/apps/statistics/series_context.cpp b/apps/statistics/statistics_context.cpp similarity index 79% rename from apps/statistics/series_context.cpp rename to apps/statistics/statistics_context.cpp index 8628a83db..e8fecc66e 100644 --- a/apps/statistics/series_context.cpp +++ b/apps/statistics/statistics_context.cpp @@ -1,4 +1,4 @@ -#include "series_context.h" +#include "statistics_context.h" #include #include #include @@ -8,11 +8,11 @@ using namespace Shared; namespace Statistics { -void SeriesContext::setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) { +void StatisticsContext::setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) { m_parentContext->setExpressionForSymbolName(expression, symbol, context); } -const Expression * SeriesContext::expressionForSymbol(const Symbol * symbol) { +const Expression * StatisticsContext::expressionForSymbol(const Symbol * symbol) { if (Symbol::isSeriesSymbol(symbol->name())) { const char * seriesName = Symbol::textForSpecialSymbols(symbol->name()); assert(strlen(seriesName) == 2); diff --git a/apps/statistics/series_context.h b/apps/statistics/statistics_context.h similarity index 73% rename from apps/statistics/series_context.h rename to apps/statistics/statistics_context.h index 81799a113..fde874032 100644 --- a/apps/statistics/series_context.h +++ b/apps/statistics/statistics_context.h @@ -1,14 +1,14 @@ -#ifndef STATISTICS_SERIES_CONTEXT_H -#define STATISTICS_SERIES_CONTEXT_H +#ifndef STATISTICS_STATISTICS_CONTEXT_H +#define STATISTICS_STATISTICS_CONTEXT_H #include #include "../shared/double_pair_store.h" namespace Statistics { -class SeriesContext : public Poincare::Context { +class StatisticsContext : public Poincare::Context { public: - SeriesContext(Shared::DoublePairStore * store, Poincare::Context * parentContext = nullptr) : + StatisticsContext(Shared::DoublePairStore * store, Poincare::Context * parentContext = nullptr) : Poincare::Context(), m_store(store), m_seriesPairIndex(-1), diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index 943c36a9b..c346f91bc 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -1,5 +1,5 @@ #include "store_controller.h" -#include "series_context.h" +#include "statistics_context.h" #include "app.h" #include "../apps_container.h" #include "../constant.h" @@ -49,7 +49,7 @@ void StoreController::fillColumnWithFormula(Expression * formula) { numberOfValuesToCompute = m_store->numberOfPairsOfSeries(selectedColumn()/DoublePairStore::k_numberOfColumnsPerSeries); } - SeriesContext seriesContext(m_store, const_cast(static_cast(app()->container()))->globalContext()); + StatisticsContext seriesContext(m_store, const_cast(static_cast(app()->container()))->globalContext()); for (int j = 0; j < numberOfValuesToCompute; j++) { // Set the context seriesContext.setSeriesPairIndex(j); From 163513dfc039a4b2953c5fd8a7437514ce7849b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 5 Jun 2018 17:37:59 +0200 Subject: [PATCH 137/156] [apps/stats/reg] More beautiful formul input view --- .../buffer_text_view_with_text_field.cpp | 30 +++++++++++++++++-- .../shared/buffer_text_view_with_text_field.h | 8 +++-- apps/shared/store_controller.cpp | 4 +-- apps/shared/store_controller.h | 1 + 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/apps/shared/buffer_text_view_with_text_field.cpp b/apps/shared/buffer_text_view_with_text_field.cpp index 1f27faaef..3de524f41 100644 --- a/apps/shared/buffer_text_view_with_text_field.cpp +++ b/apps/shared/buffer_text_view_with_text_field.cpp @@ -1,11 +1,12 @@ #include "buffer_text_view_with_text_field.h" +#include namespace Shared { BufferTextViewWithTextField::BufferTextViewWithTextField(Responder * parentResponder, TextFieldDelegate * delegate, KDText::FontSize size) : View(), Responder(parentResponder), - m_bufferTextView(size, 1.0f, 0.5f), + m_bufferTextView(size, 0.0f, 0.5f), m_textField(this, m_textFieldBuffer, m_textFieldBuffer, k_textFieldBufferSize, delegate, false, size, 0.0f, 0.5f), m_textFieldBuffer{} { @@ -19,6 +20,25 @@ void BufferTextViewWithTextField::setBufferText(const char * text) { m_bufferTextView.setText(text); } +void BufferTextViewWithTextField::drawRect(KDContext * ctx, KDRect rect) const { + KDRect textFieldRect = textFieldFrame(); + + // Fill margins with white + // Left margin + ctx->fillRect(KDRect(0, 0, Metric::TitleBarExternHorizontalMargin, bounds().height()), KDColorWhite); + ctx->fillRect(KDRect(bounds().width() - Metric::TitleBarExternHorizontalMargin, 0, Metric::TitleBarExternHorizontalMargin, bounds().height()), KDColorWhite); + // Right margin + ctx->fillRect(KDRect(bounds().width() - Metric::TitleBarExternHorizontalMargin, 0, Metric::TitleBarExternHorizontalMargin, bounds().height()), KDColorWhite); + // Above the text field + ctx->fillRect(KDRect(textFieldRect.x() - k_borderWidth, 0, textFieldRect.width() + 2*k_borderWidth, bounds().height()), KDColorWhite); + // Under the text field + ctx->fillRect(KDRect(textFieldRect.x() - k_borderWidth, textFieldRect.bottom() + k_borderWidth, textFieldRect.width() + 2*k_borderWidth, bounds().height()), KDColorWhite); + + // Draw the text field border + KDRect borderRect = KDRect(textFieldRect.x()-k_borderWidth, textFieldRect.y()-k_borderWidth, textFieldRect.width()+2*k_borderWidth, textFieldRect.height()+2*k_borderWidth); + ctx->strokeRect(borderRect, Palette::GreyMiddle); +} + void BufferTextViewWithTextField::didBecomeFirstResponder() { app()->setFirstResponder(&m_textField); m_textField.setEditing(true, true); @@ -32,8 +52,12 @@ View * BufferTextViewWithTextField::subviewAtIndex(int index) { } void BufferTextViewWithTextField::layoutSubviews() { - m_bufferTextView.setFrame(KDRect(0, 0, k_height, k_bufferTextWidth)); - m_textField.setFrame(KDRect(k_bufferTextWidth, 0, bounds().width() - k_bufferTextWidth, k_height)); + m_bufferTextView.setFrame(KDRect(Metric::TitleBarExternHorizontalMargin, 0, k_bufferTextWidth, bounds().height())); + m_textField.setFrame(textFieldFrame()); +} + +KDRect BufferTextViewWithTextField::textFieldFrame() const { + return KDRect(Metric::TitleBarExternHorizontalMargin + k_bufferTextWidth + k_borderWidth, k_textFieldVerticalMargin + k_borderWidth, bounds().width() - 2 * Metric::TitleBarExternHorizontalMargin - k_bufferTextWidth - 2 * k_borderWidth, bounds().height() - 2 * k_textFieldVerticalMargin - 2 * k_borderWidth); } } diff --git a/apps/shared/buffer_text_view_with_text_field.h b/apps/shared/buffer_text_view_with_text_field.h index 4f6f69709..755efd693 100644 --- a/apps/shared/buffer_text_view_with_text_field.h +++ b/apps/shared/buffer_text_view_with_text_field.h @@ -6,21 +6,23 @@ namespace Shared { class BufferTextViewWithTextField : public View, public Responder { public: - constexpr static KDCoordinate k_height = 50; //TODO BufferTextViewWithTextField(Responder * parentResponder, TextFieldDelegate * delegate = nullptr, KDText::FontSize size = KDText::FontSize::Large); KDSize minimalSizeForOptimalDisplay() const override; TextField * textField() { return &m_textField; } void setBufferText(const char * text); - void setTextFieldText(const char * text); + void drawRect(KDContext * ctx, KDRect rect) const override; // Responder void didBecomeFirstResponder() override; private: constexpr static int k_textFieldBufferSize = TextField::maxBufferSize(); - constexpr static KDCoordinate k_bufferTextWidth = 50; //TODO + constexpr static KDCoordinate k_bufferTextWidth = 35; + constexpr static KDCoordinate k_textFieldVerticalMargin = 3; + constexpr static KDCoordinate k_borderWidth = 1; int numberOfSubviews() const override { return 2; } View * subviewAtIndex(int index) override; void layoutSubviews() override; + KDRect textFieldFrame() const; BufferTextView m_bufferTextView; TextField m_textField; char m_textFieldBuffer[k_textFieldBufferSize]; diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 5879ddb89..fad5b0a30 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -39,13 +39,13 @@ View * StoreController::ContentView::subviewAtIndex(int index) { } void StoreController::ContentView::layoutSubviews() { - KDRect dataViewFrame(0, 0, bounds().width(), bounds().height() - (m_displayFormulaInputView ? BufferTextViewWithTextField::k_height : 0)); + KDRect dataViewFrame(0, 0, bounds().width(), bounds().height() - (m_displayFormulaInputView ? k_formulaInputHeight : 0)); m_dataView.setFrame(dataViewFrame); m_formulaInputView.setFrame(formulaFrame()); } KDRect StoreController::ContentView::formulaFrame() const { - return KDRect(0, bounds().height() - BufferTextViewWithTextField::k_height, bounds().width(), m_displayFormulaInputView ? BufferTextViewWithTextField::k_height : 0); + return KDRect(0, bounds().height() - k_formulaInputHeight, bounds().width(), m_displayFormulaInputView ? k_formulaInputHeight : 0); } StoreController::StoreController(Responder * parentResponder, DoublePairStore * store, ButtonRowController * header) : diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index a99191040..c8498ad17 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -60,6 +60,7 @@ protected: private: static constexpr KDCoordinate k_margin = 8; static constexpr KDCoordinate k_scrollBarMargin = Metric::CommonRightMargin; + static constexpr KDCoordinate k_formulaInputHeight = 31; int numberOfSubviews() const override { return 1 + m_displayFormulaInputView; } View * subviewAtIndex(int index) override; void layoutSubviews() override; From 0187592dc1cb91d383942ed096ac4039a5df1292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 5 Jun 2018 17:48:45 +0200 Subject: [PATCH 138/156] [apps/stats/reg] Fix formula input edition end Befor, textFieldShouldFinishEditing was true for Right event --- apps/shared/store_controller.cpp | 7 +++++++ apps/shared/store_controller.h | 1 + 2 files changed, 8 insertions(+) diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index fad5b0a30..2a2795d70 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -62,6 +62,13 @@ void StoreController::displayFormulaInput() { contentView()->displayFormulaInput(true); } +bool StoreController::textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) { + if (textField == contentView()->formulaInputView()->textField()) { + return event == Ion::Events::OK || event == Ion::Events::EXE; + } + return EditableCellTableViewController::textFieldShouldFinishEditing(textField, event); +} + bool StoreController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { if (textField == contentView()->formulaInputView()->textField()) { // Handle formula input diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index c8498ad17..233520df7 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -20,6 +20,7 @@ public: virtual void fillColumnWithFormula(Poincare::Expression * formula) = 0; // TextFieldDelegate + bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override; bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override; bool textFieldDidAbortEditing(TextField * textField) override; From 2fffe97fee4eebd4b327cb47ff5cb768b79c26ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 7 Jun 2018 15:28:42 +0200 Subject: [PATCH 139/156] [apps/reg/stats] Do not leave formula input view on Up event --- apps/shared/store_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 2a2795d70..a977cf2c7 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -182,7 +182,7 @@ const char * StoreController::title() { } bool StoreController::handleEvent(Ion::Events::Event event) { - if (event == Ion::Events::Up) { + if (event == Ion::Events::Up && !static_cast(view())->formulaInputView()->textField()->isEditing()) { selectableTableView()->deselectTable(); assert(selectedRow() == -1); app()->setFirstResponder(tabController()); From 30448ec1445e2ec1ad62700d7ae7f14c53630043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 09:44:28 +0200 Subject: [PATCH 140/156] [apps/stats] Fix series notation (V1/N1 instead of VO/NO) --- apps/statistics/calculation_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/statistics/calculation_controller.cpp b/apps/statistics/calculation_controller.cpp index 372143634..5aeeb5001 100644 --- a/apps/statistics/calculation_controller.cpp +++ b/apps/statistics/calculation_controller.cpp @@ -52,7 +52,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int if (j == 0) { // Display a series title cell int seriesNumber = m_store->indexOfKthNonEmptySeries(i-1); - char titleBuffer[] = {'V', static_cast('0'+seriesNumber), '/', 'N', static_cast('0'+seriesNumber), 0}; + char titleBuffer[] = {'V', static_cast('1'+seriesNumber), '/', 'N', static_cast('1'+seriesNumber), 0}; StoreTitleCell * storeTitleCell = static_cast(cell); storeTitleCell->setText(titleBuffer); storeTitleCell->setColor(DoublePairStore::colorOfSeriesAtIndex(seriesNumber)); From 3c536523c59fde8f6d24c0ccbfc04d85dc91b63d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 10:06:30 +0200 Subject: [PATCH 141/156] [app/stats] Fix wrong assert When a series has two elements only and they are the same, the median is this element and there is no element after that. --- apps/statistics/store.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 293c1e1f4..f60bd3a3c 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -199,12 +199,15 @@ double Store::quartileRange(int series) const { double Store::median(int series) const { bool exactElement = true; - double maxMedian = sortedElementAtCumulatedFrequency(series, 1.0/2.0, &exactElement); + double minMedian = sortedElementAtCumulatedFrequency(series, 1.0/2.0, &exactElement); if (!exactElement) { - double minusMedian = sortedElementAfter(series, maxMedian); - return (minusMedian+maxMedian)/2.0; + double maxMedian = sortedElementAfter(series, minMedian); + if (maxMedian == DBL_MAX) { + return minMedian; + } + return (minMedian + maxMedian)/2.0; } else { - return maxMedian; + return minMedian; } } @@ -296,7 +299,6 @@ double Store::sortedElementAfter(int series, double k) const { result = currentElement; } } - assert(result < DBL_MAX); return result; } From 2723e37c0013cc19d6c06e1b14abb1e4af02e6bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 10:26:29 +0200 Subject: [PATCH 142/156] [poincare] Fix regression symbols layout --- poincare/src/symbol.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index 8364b512b..e404714d3 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -276,7 +276,7 @@ ExpressionLayout * Symbol::privateCreateLayout(PrintFloat::Mode floatDisplayMode false), false); } - if (isMatrixSymbol() || isSeriesSymbol(m_name)) { + if (isMatrixSymbol() || isSeriesSymbol(m_name) || isRegressionSymbol(m_name)) { return LayoutEngine::createStringLayout(textForSpecialSymbols(m_name), 2); } return LayoutEngine::createStringLayout(&m_name, 1); From d44459bba2b9538366f4cf95a9c79cb067d4d950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 11:02:57 +0200 Subject: [PATCH 143/156] [apps/reg/stats] Share StoreContext --- apps/regression/regression_context.cpp | 4 --- apps/regression/regression_context.h | 17 ++--------- apps/regression/store_controller.cpp | 40 ++++++-------------------- apps/regression/store_controller.h | 3 ++ apps/shared/Makefile | 1 + apps/shared/store_context.cpp | 14 +++++++++ apps/shared/store_context.h | 28 ++++++++++++++++++ apps/shared/store_controller.cpp | 34 ++++++++++++++++++++++ apps/shared/store_controller.h | 3 ++ apps/statistics/statistics_context.cpp | 4 --- apps/statistics/statistics_context.h | 18 ++---------- apps/statistics/store_controller.cpp | 40 ++++++-------------------- apps/statistics/store_controller.h | 3 ++ 13 files changed, 108 insertions(+), 101 deletions(-) create mode 100644 apps/shared/store_context.cpp create mode 100644 apps/shared/store_context.h diff --git a/apps/regression/regression_context.cpp b/apps/regression/regression_context.cpp index 89070e89a..6499e64fc 100644 --- a/apps/regression/regression_context.cpp +++ b/apps/regression/regression_context.cpp @@ -8,10 +8,6 @@ using namespace Shared; namespace Regression { -void RegressionContext::setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) { - m_parentContext->setExpressionForSymbolName(expression, symbol, context); -} - const Expression * RegressionContext::expressionForSymbol(const Symbol * symbol) { if (Symbol::isRegressionSymbol(symbol->name())) { const char * seriesName = Symbol::textForSpecialSymbols(symbol->name()); diff --git a/apps/regression/regression_context.h b/apps/regression/regression_context.h index 966b63a0b..1d9543103 100644 --- a/apps/regression/regression_context.h +++ b/apps/regression/regression_context.h @@ -2,25 +2,14 @@ #define REGRESSION_REGRESSION_CONTEXT_H #include -#include "../shared/double_pair_store.h" +#include "../shared/store_context.h" namespace Regression { -class RegressionContext : public Poincare::Context { +class RegressionContext : public Shared::StoreContext { public: - RegressionContext(Shared::DoublePairStore * store, Poincare::Context * parentContext = nullptr) : - Poincare::Context(), - m_store(store), - m_seriesPairIndex(-1), - m_parentContext(parentContext) - {} - void setSeriesPairIndex(int j) { m_seriesPairIndex = j; } - void setExpressionForSymbolName(const Poincare::Expression * expression, const Poincare::Symbol * symbol, Poincare::Context & context) override; + using Shared::StoreContext::StoreContext; const Poincare::Expression * expressionForSymbol(const Poincare::Symbol * symbol) override; -private: - Shared::DoublePairStore * m_store; - int m_seriesPairIndex; - Poincare::Context * m_parentContext; }; } diff --git a/apps/regression/store_controller.cpp b/apps/regression/store_controller.cpp index 70e822e51..91e143495 100644 --- a/apps/regression/store_controller.cpp +++ b/apps/regression/store_controller.cpp @@ -15,10 +15,16 @@ namespace Regression { StoreController::StoreController(Responder * parentResponder, Store * store, ButtonRowController * header) : Shared::StoreController(parentResponder, store, header), - m_titleCells{} + m_titleCells{}, + m_regressionContext(store) { } +StoreContext * StoreController::storeContext() { + m_regressionContext.setParentContext(const_cast(static_cast(app()->container()))->globalContext()); + return &m_regressionContext; +} + void StoreController::setFormulaLabel() { int series = selectedColumn() / Store::k_numberOfColumnsPerSeries; int isXColumn = selectedColumn() % Store::k_numberOfColumnsPerSeries == 0; @@ -27,37 +33,7 @@ void StoreController::setFormulaLabel() { } void StoreController::fillColumnWithFormula(Expression * formula) { - int currentColumn = selectedColumn(); - // Fetch the series used in the formula to compute the size of the filled in series - char variables[7] = {0, 0, 0, 0, 0, 0, 0}; - formula->getVariables(Symbol::isRegressionSymbol, variables); - int numberOfValuesToCompute = -1; - int index = 0; - while (variables[index] != 0) { - const char * seriesName = Symbol::textForSpecialSymbols(variables[index]); - assert(strlen(seriesName) == 2); - int series = (int)(seriesName[1] - '0') - 1; - assert(series >= 0 && series < DoublePairStore::k_numberOfSeries); - if (numberOfValuesToCompute == -1) { - numberOfValuesToCompute = m_store->numberOfPairsOfSeries(series); - } else { - numberOfValuesToCompute = min(numberOfValuesToCompute, m_store->numberOfPairsOfSeries(series)); - } - index++; - } - if (numberOfValuesToCompute == -1) { - numberOfValuesToCompute = m_store->numberOfPairsOfSeries(selectedColumn()/DoublePairStore::k_numberOfColumnsPerSeries); - } - - RegressionContext regressionContext(m_store, const_cast(static_cast(app()->container()))->globalContext()); - for (int j = 0; j < numberOfValuesToCompute; j++) { - // Set the context - regressionContext.setSeriesPairIndex(j); - // Compute the new value using the formula - double evaluation = formula->approximateToScalar(regressionContext); - setDataAtLocation(evaluation, currentColumn, j + 1); - } - selectableTableView()->reloadData(); + privateFillColumnWithFormula(formula, Symbol::isRegressionSymbol); } void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { diff --git a/apps/regression/store_controller.h b/apps/regression/store_controller.h index efbb25326..a009b3557 100644 --- a/apps/regression/store_controller.h +++ b/apps/regression/store_controller.h @@ -3,6 +3,7 @@ #include #include "store.h" +#include "regression_context.h" #include "../shared/store_controller.h" #include "../shared/store_title_cell.h" @@ -11,6 +12,7 @@ namespace Regression { class StoreController : public Shared::StoreController { public: StoreController(Responder * parentResponder, Store * store, ButtonRowController * header); + Shared::StoreContext * storeContext() override; void setFormulaLabel() override; void fillColumnWithFormula(Poincare::Expression * formula) override; void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; @@ -19,6 +21,7 @@ private: View * loadView() override; void unloadView(View * view) override; Shared::StoreTitleCell * m_titleCells[k_numberOfTitleCells]; + RegressionContext m_regressionContext; }; } diff --git a/apps/shared/Makefile b/apps/shared/Makefile index 3242b58ae..aa878289a 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -46,6 +46,7 @@ app_objs += $(addprefix apps/shared/,\ separator_even_odd_buffer_text_cell.o\ simple_interactive_curve_view_controller.o\ store_cell.o\ + store_context.o\ store_controller.o\ store_parameter_controller.o\ store_selectable_table_view.o\ diff --git a/apps/shared/store_context.cpp b/apps/shared/store_context.cpp new file mode 100644 index 000000000..db4dd28bf --- /dev/null +++ b/apps/shared/store_context.cpp @@ -0,0 +1,14 @@ +#include "store_context.h" +#include +#include +#include + +using namespace Poincare; + +namespace Shared { + +void StoreContext::setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) { + m_parentContext->setExpressionForSymbolName(expression, symbol, context); +} + +} diff --git a/apps/shared/store_context.h b/apps/shared/store_context.h new file mode 100644 index 000000000..7b0513fb7 --- /dev/null +++ b/apps/shared/store_context.h @@ -0,0 +1,28 @@ +#ifndef SHARED_STORE_CONTEXT_H +#define SHARED_STORE_CONTEXT_H + +#include +#include "double_pair_store.h" + +namespace Shared { + +class StoreContext : public Poincare::Context { +public: + StoreContext(Shared::DoublePairStore * store) : + Poincare::Context(), + m_store(store), + m_seriesPairIndex(-1), + m_parentContext(nullptr) + {} + void setParentContext(Poincare::Context * parentContext) { m_parentContext = parentContext; } + void setSeriesPairIndex(int j) { m_seriesPairIndex = j; } + void setExpressionForSymbolName(const Poincare::Expression * expression, const Poincare::Symbol * symbol, Poincare::Context & context) override; +protected: + Shared::DoublePairStore * m_store; + int m_seriesPairIndex; + Poincare::Context * m_parentContext; +}; + +} + +#endif diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index a977cf2c7..d5e333b5c 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -268,6 +268,40 @@ void StoreController::unloadView(View * view) { delete view; } +void StoreController::privateFillColumnWithFormula(Expression * formula, Expression::isVariableTest isVariable) { + int currentColumn = selectedColumn(); + // Fetch the series used in the formula to compute the size of the filled in series + char variables[7] = {0, 0, 0, 0, 0, 0, 0}; + formula->getVariables(isVariable, variables); + int numberOfValuesToCompute = -1; + int index = 0; + while (variables[index] != 0) { + const char * seriesName = Symbol::textForSpecialSymbols(variables[index]); + assert(strlen(seriesName) == 2); + int series = (int)(seriesName[1] - '0') - 1; + assert(series >= 0 && series < DoublePairStore::k_numberOfSeries); + if (numberOfValuesToCompute == -1) { + numberOfValuesToCompute = m_store->numberOfPairsOfSeries(series); + } else { + numberOfValuesToCompute = min(numberOfValuesToCompute, m_store->numberOfPairsOfSeries(series)); + } + index++; + } + if (numberOfValuesToCompute == -1) { + numberOfValuesToCompute = m_store->numberOfPairsOfSeries(selectedColumn()/DoublePairStore::k_numberOfColumnsPerSeries); + } + + StoreContext * store = storeContext(); + for (int j = 0; j < numberOfValuesToCompute; j++) { + // Set the context + store->setSeriesPairIndex(j); + // Compute the new value using the formula + double evaluation = formula->approximateToScalar(*store); + setDataAtLocation(evaluation, currentColumn, j + 1); + } + selectableTableView()->reloadData(); +} + bool StoreController::cellShouldBeTransparent(int i, int j) { int seriesIndex = i/DoublePairStore::k_numberOfColumnsPerSeries; return j > 1 + m_store->numberOfPairsOfSeries(seriesIndex); diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index 233520df7..f478a921e 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -6,6 +6,7 @@ #include "editable_cell_table_view_controller.h" #include "double_pair_store.h" #include "store_cell.h" +#include "store_context.h" #include "store_parameter_controller.h" #include "store_selectable_table_view.h" @@ -15,6 +16,7 @@ class StoreController : public EditableCellTableViewController, public ButtonRow public: StoreController(Responder * parentResponder, DoublePairStore * store, ButtonRowController * header); + virtual StoreContext * storeContext() = 0; void displayFormulaInput(); virtual void setFormulaLabel() = 0; virtual void fillColumnWithFormula(Poincare::Expression * formula) = 0; @@ -83,6 +85,7 @@ protected: virtual HighlightCell * titleCells(int index) = 0; char m_draftTextBuffer[TextField::maxBufferSize()]; int seriesAtColumn(int column) const { return column / DoublePairStore::k_numberOfColumnsPerSeries; } + void privateFillColumnWithFormula(Poincare::Expression * formula, Poincare::Expression::isVariableTest isVariable); StoreCell * m_editableCells[k_maxNumberOfEditableCells]; DoublePairStore * m_store; StoreParameterController m_storeParameterController; diff --git a/apps/statistics/statistics_context.cpp b/apps/statistics/statistics_context.cpp index e8fecc66e..301345d7c 100644 --- a/apps/statistics/statistics_context.cpp +++ b/apps/statistics/statistics_context.cpp @@ -8,10 +8,6 @@ using namespace Shared; namespace Statistics { -void StatisticsContext::setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) { - m_parentContext->setExpressionForSymbolName(expression, symbol, context); -} - const Expression * StatisticsContext::expressionForSymbol(const Symbol * symbol) { if (Symbol::isSeriesSymbol(symbol->name())) { const char * seriesName = Symbol::textForSpecialSymbols(symbol->name()); diff --git a/apps/statistics/statistics_context.h b/apps/statistics/statistics_context.h index fde874032..6263bf4fd 100644 --- a/apps/statistics/statistics_context.h +++ b/apps/statistics/statistics_context.h @@ -1,26 +1,14 @@ #ifndef STATISTICS_STATISTICS_CONTEXT_H #define STATISTICS_STATISTICS_CONTEXT_H -#include -#include "../shared/double_pair_store.h" +#include "../shared/store_context.h" namespace Statistics { -class StatisticsContext : public Poincare::Context { +class StatisticsContext : public Shared::StoreContext { public: - StatisticsContext(Shared::DoublePairStore * store, Poincare::Context * parentContext = nullptr) : - Poincare::Context(), - m_store(store), - m_seriesPairIndex(-1), - m_parentContext(parentContext) - {} - void setSeriesPairIndex(int j) { m_seriesPairIndex = j; } - void setExpressionForSymbolName(const Poincare::Expression * expression, const Poincare::Symbol * symbol, Poincare::Context & context) override; + using Shared::StoreContext::StoreContext; const Poincare::Expression * expressionForSymbol(const Poincare::Symbol * symbol) override; -private: - Shared::DoublePairStore * m_store; - int m_seriesPairIndex; - Poincare::Context * m_parentContext; }; } diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index c346f91bc..e57d95913 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -15,10 +15,16 @@ namespace Statistics { StoreController::StoreController(Responder * parentResponder, Store * store, ButtonRowController * header) : Shared::StoreController(parentResponder, store, header), m_titleCells{}, - m_store(store) + m_store(store), + m_statisticsContext(m_store) { } +StoreContext * StoreController::storeContext() { + m_statisticsContext.setParentContext(const_cast(static_cast(app()->container()))->globalContext()); + return &m_statisticsContext; +} + void StoreController::setFormulaLabel() { int series = selectedColumn() / Store::k_numberOfColumnsPerSeries; int isValueColumn = selectedColumn() % Store::k_numberOfColumnsPerSeries == 0; @@ -27,37 +33,7 @@ void StoreController::setFormulaLabel() { } void StoreController::fillColumnWithFormula(Expression * formula) { - int currentColumn = selectedColumn(); - // Fetch the series used in the formula to compute the size of the filled in series - char variables[7] = {0, 0, 0, 0, 0, 0, 0}; - formula->getVariables(Symbol::isSeriesSymbol, variables); - int numberOfValuesToCompute = -1; - int index = 0; - while (variables[index] != 0) { - const char * seriesName = Symbol::textForSpecialSymbols(variables[index]); - assert(strlen(seriesName) == 2); - int series = (int)(seriesName[1] - '0') - 1; - assert(series >= 0 && series < DoublePairStore::k_numberOfSeries); - if (numberOfValuesToCompute == -1) { - numberOfValuesToCompute = m_store->numberOfPairsOfSeries(series); - } else { - numberOfValuesToCompute = min(numberOfValuesToCompute, m_store->numberOfPairsOfSeries(series)); - } - index++; - } - if (numberOfValuesToCompute == -1) { - numberOfValuesToCompute = m_store->numberOfPairsOfSeries(selectedColumn()/DoublePairStore::k_numberOfColumnsPerSeries); - } - - StatisticsContext seriesContext(m_store, const_cast(static_cast(app()->container()))->globalContext()); - for (int j = 0; j < numberOfValuesToCompute; j++) { - // Set the context - seriesContext.setSeriesPairIndex(j); - // Compute the new value using the formula - double evaluation = formula->approximateToScalar(seriesContext); - setDataAtLocation(evaluation, currentColumn, j + 1); - } - selectableTableView()->reloadData(); + privateFillColumnWithFormula(formula, Symbol::isSeriesSymbol); } void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { diff --git a/apps/statistics/store_controller.h b/apps/statistics/store_controller.h index 9e81df313..e744e885d 100644 --- a/apps/statistics/store_controller.h +++ b/apps/statistics/store_controller.h @@ -3,6 +3,7 @@ #include #include "store.h" +#include "statistics_context.h" #include "../shared/store_controller.h" #include "../shared/store_title_cell.h" @@ -11,6 +12,7 @@ namespace Statistics { class StoreController : public Shared::StoreController { public: StoreController(Responder * parentResponder, Store * store, ButtonRowController * header); + Shared::StoreContext * storeContext() override; void setFormulaLabel() override; void fillColumnWithFormula(Poincare::Expression * formula) override; void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; @@ -21,6 +23,7 @@ private: void unloadView(View * view) override; Shared::StoreTitleCell * m_titleCells[k_numberOfTitleCells]; Store * m_store; + StatisticsContext m_statisticsContext; }; } From 13a4057786c0ad78c6c47f27001410b211105279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 11:24:16 +0200 Subject: [PATCH 144/156] [apps/stats/reg] Do not fill with formula if undefined values --- apps/regression/store_controller.cpp | 4 ++-- apps/regression/store_controller.h | 2 +- apps/shared/store_controller.cpp | 20 +++++++++++++++++--- apps/shared/store_controller.h | 4 ++-- apps/statistics/store_controller.cpp | 4 ++-- apps/statistics/store_controller.h | 2 +- 6 files changed, 25 insertions(+), 11 deletions(-) diff --git a/apps/regression/store_controller.cpp b/apps/regression/store_controller.cpp index 91e143495..22e8bb92a 100644 --- a/apps/regression/store_controller.cpp +++ b/apps/regression/store_controller.cpp @@ -32,8 +32,8 @@ void StoreController::setFormulaLabel() { static_cast(view())->formulaInputView()->setBufferText(text); } -void StoreController::fillColumnWithFormula(Expression * formula) { - privateFillColumnWithFormula(formula, Symbol::isRegressionSymbol); +bool StoreController::fillColumnWithFormula(Expression * formula) { + return privateFillColumnWithFormula(formula, Symbol::isRegressionSymbol); } void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { diff --git a/apps/regression/store_controller.h b/apps/regression/store_controller.h index a009b3557..d7a37834c 100644 --- a/apps/regression/store_controller.h +++ b/apps/regression/store_controller.h @@ -14,7 +14,7 @@ public: StoreController(Responder * parentResponder, Store * store, ButtonRowController * header); Shared::StoreContext * storeContext() override; void setFormulaLabel() override; - void fillColumnWithFormula(Poincare::Expression * formula) override; + bool fillColumnWithFormula(Poincare::Expression * formula) override; void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; private: HighlightCell * titleCells(int index) override; diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index d5e333b5c..098fdb26d 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -78,9 +78,10 @@ bool StoreController::textFieldDidFinishEditing(TextField * textField, const cha return false; } contentView()->displayFormulaInput(false); - fillColumnWithFormula(expression); + if (fillColumnWithFormula(expression)) { + app()->setFirstResponder(contentView()); + } delete expression; - app()->setFirstResponder(contentView()); return true; } AppsContainer * appsContainer = ((TextFieldDelegateApp *)app())->container(); @@ -268,7 +269,7 @@ void StoreController::unloadView(View * view) { delete view; } -void StoreController::privateFillColumnWithFormula(Expression * formula, Expression::isVariableTest isVariable) { +bool StoreController::privateFillColumnWithFormula(Expression * formula, Expression::isVariableTest isVariable) { int currentColumn = selectedColumn(); // Fetch the series used in the formula to compute the size of the filled in series char variables[7] = {0, 0, 0, 0, 0, 0, 0}; @@ -292,14 +293,27 @@ void StoreController::privateFillColumnWithFormula(Expression * formula, Express } StoreContext * store = storeContext(); + + // Make sure no value is undef, else display an error for (int j = 0; j < numberOfValuesToCompute; j++) { // Set the context store->setSeriesPairIndex(j); // Compute the new value using the formula double evaluation = formula->approximateToScalar(*store); + if (std::isnan(evaluation) || std::isinf(evaluation)) { + app()->displayWarning(I18n::Message::UndefinedValue); + return false; + } + } + + // Fill in the table with the formula values + for (int j = 0; j < numberOfValuesToCompute; j++) { + store->setSeriesPairIndex(j); + double evaluation = formula->approximateToScalar(*store); setDataAtLocation(evaluation, currentColumn, j + 1); } selectableTableView()->reloadData(); + return true; } bool StoreController::cellShouldBeTransparent(int i, int j) { diff --git a/apps/shared/store_controller.h b/apps/shared/store_controller.h index f478a921e..368abd7a2 100644 --- a/apps/shared/store_controller.h +++ b/apps/shared/store_controller.h @@ -19,7 +19,7 @@ public: virtual StoreContext * storeContext() = 0; void displayFormulaInput(); virtual void setFormulaLabel() = 0; - virtual void fillColumnWithFormula(Poincare::Expression * formula) = 0; + virtual bool fillColumnWithFormula(Poincare::Expression * formula) = 0; // TextFieldDelegate bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override; @@ -85,7 +85,7 @@ protected: virtual HighlightCell * titleCells(int index) = 0; char m_draftTextBuffer[TextField::maxBufferSize()]; int seriesAtColumn(int column) const { return column / DoublePairStore::k_numberOfColumnsPerSeries; } - void privateFillColumnWithFormula(Poincare::Expression * formula, Poincare::Expression::isVariableTest isVariable); + bool privateFillColumnWithFormula(Poincare::Expression * formula, Poincare::Expression::isVariableTest isVariable); StoreCell * m_editableCells[k_maxNumberOfEditableCells]; DoublePairStore * m_store; StoreParameterController m_storeParameterController; diff --git a/apps/statistics/store_controller.cpp b/apps/statistics/store_controller.cpp index e57d95913..a7a437be3 100644 --- a/apps/statistics/store_controller.cpp +++ b/apps/statistics/store_controller.cpp @@ -32,8 +32,8 @@ void StoreController::setFormulaLabel() { static_cast(view())->formulaInputView()->setBufferText(text); } -void StoreController::fillColumnWithFormula(Expression * formula) { - privateFillColumnWithFormula(formula, Symbol::isSeriesSymbol); +bool StoreController::fillColumnWithFormula(Expression * formula) { + return privateFillColumnWithFormula(formula, Symbol::isSeriesSymbol); } void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { diff --git a/apps/statistics/store_controller.h b/apps/statistics/store_controller.h index e744e885d..bd95da265 100644 --- a/apps/statistics/store_controller.h +++ b/apps/statistics/store_controller.h @@ -14,7 +14,7 @@ public: StoreController(Responder * parentResponder, Store * store, ButtonRowController * header); Shared::StoreContext * storeContext() override; void setFormulaLabel() override; - void fillColumnWithFormula(Poincare::Expression * formula) override; + bool fillColumnWithFormula(Poincare::Expression * formula) override; void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; private: bool setDataAtLocation(double floatBody, int columnIndex, int rowIndex) override; From 4ee9715711f07c3774814e159b7ea2fec2e02db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 11:25:38 +0200 Subject: [PATCH 145/156] [apps/shared] Remove unneeded message --- apps/shared.de.i18n | 1 - apps/shared.en.i18n | 1 - apps/shared.es.i18n | 1 - apps/shared.fr.i18n | 1 - apps/shared.pt.i18n | 1 - 5 files changed, 5 deletions(-) diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index 12cc7f5ae..dd2539a20 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -28,7 +28,6 @@ Initialization = "Initialisierung" IntervalSet = "Tabelleneinstell" Language = "Sprache" LowBattery = "Batterie leer" -MathError = "Mathematischen Fehler" Mean = "Mittelwert" Move = " Verschieben: " Next = "Nachste" diff --git a/apps/shared.en.i18n b/apps/shared.en.i18n index c1bf80b6b..85394a23a 100644 --- a/apps/shared.en.i18n +++ b/apps/shared.en.i18n @@ -28,7 +28,6 @@ Initialization = "Preadjustment" IntervalSet = "Set the interval" Language = "Language" LowBattery = "Low battery" -MathError = "Math error" Mean = "Mean" Move = " Move: " Next = "Next" diff --git a/apps/shared.es.i18n b/apps/shared.es.i18n index 77e646102..d25e838d2 100644 --- a/apps/shared.es.i18n +++ b/apps/shared.es.i18n @@ -28,7 +28,6 @@ Initialization = "Inicializacion" IntervalSet = "Ajustar el intervalo" Language = "Idioma" LowBattery = "Bateria baja" -MathError = "Error matematico" Mean = "Media" Move = " Mover : " Next = "Siguiente" diff --git a/apps/shared.fr.i18n b/apps/shared.fr.i18n index 1548bed93..207e82836 100644 --- a/apps/shared.fr.i18n +++ b/apps/shared.fr.i18n @@ -28,7 +28,6 @@ Initialization = "Initialisation" IntervalSet = "Regler l'intervalle" Language = "Langue" LowBattery = "Batterie faible" -MathError = "Erreur mathematique" Mean = "Moyenne" Move = " Deplacer : " Next = "Suivant" diff --git a/apps/shared.pt.i18n b/apps/shared.pt.i18n index 6742fd2fd..20733146d 100644 --- a/apps/shared.pt.i18n +++ b/apps/shared.pt.i18n @@ -28,7 +28,6 @@ Initialization = "Inicializacao" IntervalSet = "Ajustar o intervalo" Language = "Idioma" LowBattery = "Bateria fraca" -MathError = "Erro matematico" Mean = "Media" Move = " Mover : " Next = "Seguinte" From c7de90dd99f54eb0f977efd0bc24caf91f919840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 11:43:20 +0200 Subject: [PATCH 146/156] [apps/reg] Fix GraphController::reloadBannerView --- apps/regression/graph_controller.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index 60657f202..1aa509a00 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -69,6 +69,10 @@ bool GraphController::handleEnter() { } void GraphController::reloadBannerView() { + if (m_selectedSeries < 0) { + return; + } + m_bannerView.setMessageAtIndex(I18n::Message::RegressionFormula, 3); char buffer[k_maxNumberOfCharacters + PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; From 8e292cafb8689f02b59870c2d7592bce1a298e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 11:46:16 +0200 Subject: [PATCH 147/156] [apps/reg] Save the selected series in the snapshot --- apps/regression/app.cpp | 3 ++- apps/regression/app.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/regression/app.cpp b/apps/regression/app.cpp index ecd8180a3..8420d61ea 100644 --- a/apps/regression/app.cpp +++ b/apps/regression/app.cpp @@ -23,7 +23,8 @@ App::Snapshot::Snapshot() : m_cursor(), m_graphSelectedDotIndex(-1), m_modelVersion(0), - m_rangeVersion(0) + m_rangeVersion(0), + m_selectedSeriesIndex(-1) { } diff --git a/apps/regression/app.h b/apps/regression/app.h index c07eea624..8a7c8fcf7 100644 --- a/apps/regression/app.h +++ b/apps/regression/app.h @@ -27,6 +27,7 @@ public: Store * store(); Shared::CurveViewCursor * cursor(); int * graphSelectedDotIndex(); + int * selectedSeriesIndex() { return &m_selectedSeriesIndex; } uint32_t * modelVersion(); uint32_t * rangeVersion(); private: @@ -35,6 +36,7 @@ public: int m_graphSelectedDotIndex; uint32_t m_modelVersion; uint32_t m_rangeVersion; + int m_selectedSeriesIndex; }; private: App(Container * container, Snapshot * snapshot); From 3be2b0e414ce3e91842bc1764b2bc99922b6bb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 11:47:51 +0200 Subject: [PATCH 148/156] [app/reg] Inline getter funcitons --- apps/regression/app.cpp | 20 -------------------- apps/regression/app.h | 10 +++++----- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/apps/regression/app.cpp b/apps/regression/app.cpp index 8420d61ea..4f2fae593 100644 --- a/apps/regression/app.cpp +++ b/apps/regression/app.cpp @@ -45,26 +45,6 @@ App::Descriptor * App::Snapshot::descriptor() { return &descriptor; } -Store * App::Snapshot::store() { - return &m_store; -} - -CurveViewCursor * App::Snapshot::cursor() { - return &m_cursor; -} - -int * App::Snapshot::graphSelectedDotIndex() { - return &m_graphSelectedDotIndex; -} - -uint32_t * App::Snapshot::modelVersion() { - return &m_modelVersion; -} - -uint32_t * App::Snapshot::rangeVersion() { - return &m_rangeVersion; -} - App::App(Container * container, Snapshot * snapshot) : TextFieldDelegateApp(container, snapshot, &m_tabViewController), m_calculationController(&m_calculationAlternateEmptyViewController, &m_calculationHeader, snapshot->store()), diff --git a/apps/regression/app.h b/apps/regression/app.h index 8a7c8fcf7..aa976f617 100644 --- a/apps/regression/app.h +++ b/apps/regression/app.h @@ -24,12 +24,12 @@ public: App * unpack(Container * container) override; void reset() override; Descriptor * descriptor() override; - Store * store(); - Shared::CurveViewCursor * cursor(); - int * graphSelectedDotIndex(); + Store * store() { return &m_store; } + Shared::CurveViewCursor * cursor() { return &m_cursor; } + int * graphSelectedDotIndex() { return &m_graphSelectedDotIndex; } int * selectedSeriesIndex() { return &m_selectedSeriesIndex; } - uint32_t * modelVersion(); - uint32_t * rangeVersion(); + uint32_t * modelVersion() { return &m_modelVersion; } + uint32_t * rangeVersion() { return &m_rangeVersion; } private: Store m_store; Shared::CurveViewCursor m_cursor; From 6d5c987178ec2fb6dc2977452b899edbf7345e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 11:53:20 +0200 Subject: [PATCH 149/156] [apps/reg] Use selected series index stored in snapshot --- apps/regression/app.cpp | 2 +- .../regression/go_to_parameter_controller.cpp | 6 +- apps/regression/graph_controller.cpp | 62 +++++++++---------- apps/regression/graph_controller.h | 6 +- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/apps/regression/app.cpp b/apps/regression/app.cpp index 4f2fae593..a5bca541e 100644 --- a/apps/regression/app.cpp +++ b/apps/regression/app.cpp @@ -50,7 +50,7 @@ App::App(Container * container, Snapshot * snapshot) : m_calculationController(&m_calculationAlternateEmptyViewController, &m_calculationHeader, snapshot->store()), m_calculationAlternateEmptyViewController(&m_calculationHeader, &m_calculationController, &m_calculationController), m_calculationHeader(&m_tabViewController, &m_calculationAlternateEmptyViewController, &m_calculationController), - m_graphController(&m_graphAlternateEmptyViewController, &m_graphHeader, snapshot->store(), snapshot->cursor(), snapshot->modelVersion(), snapshot->rangeVersion(), snapshot->graphSelectedDotIndex()), + m_graphController(&m_graphAlternateEmptyViewController, &m_graphHeader, snapshot->store(), snapshot->cursor(), snapshot->modelVersion(), snapshot->rangeVersion(), snapshot->graphSelectedDotIndex(), snapshot->selectedSeriesIndex()), m_graphAlternateEmptyViewController(&m_graphHeader, &m_graphController, &m_graphController), m_graphHeader(&m_graphStackViewController, &m_graphAlternateEmptyViewController, &m_graphController), m_graphStackViewController(&m_tabViewController, &m_graphHeader), diff --git a/apps/regression/go_to_parameter_controller.cpp b/apps/regression/go_to_parameter_controller.cpp index 2b71431a1..e1170931a 100644 --- a/apps/regression/go_to_parameter_controller.cpp +++ b/apps/regression/go_to_parameter_controller.cpp @@ -43,16 +43,16 @@ bool GoToParameterController::setParameterAtIndex(int parameterIndex, double f) app()->displayWarning(I18n::Message::ForbiddenValue); return false; } - double x = m_store->xValueForYValue(m_graphController->selectedSeries(), f); + double x = m_store->xValueForYValue(m_graphController->selectedSeriesIndex(), f); if (m_xPrediction) { - x = m_store->yValueForXValue(m_graphController->selectedSeries(), f); + x = m_store->yValueForXValue(m_graphController->selectedSeriesIndex(), f); } if (std::fabs(x) > k_maxDisplayableFloat) { app()->displayWarning(I18n::Message::ForbiddenValue); return false; } if (std::isnan(x)) { - if (m_store->slope(m_graphController->selectedSeries()) < DBL_EPSILON && f == m_store->yIntercept(m_graphController->selectedSeries())) { + if (m_store->slope(m_graphController->selectedSeriesIndex()) < DBL_EPSILON && f == m_store->yIntercept(m_graphController->selectedSeriesIndex())) { m_graphController->selectRegressionCurve(); m_cursor->moveTo(m_cursor->x(), f); return true; diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index 1aa509a00..9023283fb 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -7,7 +7,7 @@ using namespace Shared; namespace Regression { -GraphController::GraphController(Responder * parentResponder, ButtonRowController * header, Store * store, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion, int * selectedDotIndex) : +GraphController::GraphController(Responder * parentResponder, ButtonRowController * header, Store * store, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex) : InteractiveCurveViewController(parentResponder, header, store, &m_view, cursor, modelVersion, rangeVersion), m_crossCursorView(), m_roundCursorView(Palette::YellowDark), @@ -17,7 +17,7 @@ GraphController::GraphController(Responder * parentResponder, ButtonRowControlle m_initialisationParameterController(this, m_store), m_predictionParameterController(this, m_store, m_cursor, this), m_selectedDotIndex(selectedDotIndex), - m_selectedSeries(-1) + m_selectedSeriesIndex(selectedSeriesIndex) { m_store->setCursor(m_cursor); } @@ -69,7 +69,7 @@ bool GraphController::handleEnter() { } void GraphController::reloadBannerView() { - if (m_selectedSeries < 0) { + if (*m_selectedSeriesIndex < 0) { return; } @@ -81,7 +81,7 @@ void GraphController::reloadBannerView() { int legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; - if (*m_selectedDotIndex == m_store->numberOfPairsOfSeries(m_selectedSeries)) { + if (*m_selectedDotIndex == m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) { legend = I18n::translate(I18n::Message::MeanDot); legendLength = strlen(legend); strlcpy(buffer+numberOfChar, legend, legendLength+1); @@ -104,10 +104,10 @@ void GraphController::reloadBannerView() { legend = "x="; double x = m_cursor->x(); // Display a specific legend if the mean dot is selected - if (*m_selectedDotIndex == m_store->numberOfPairsOfSeries(m_selectedSeries)) { + if (*m_selectedDotIndex == m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) { constexpr static char legX[] = {Ion::Charset::XBar, '=', 0}; legend = legX; - x = m_store->meanOfColumn(m_selectedSeries, 0); + x = m_store->meanOfColumn(*m_selectedSeriesIndex, 0); } legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); @@ -122,10 +122,10 @@ void GraphController::reloadBannerView() { numberOfChar = 0; legend = "y="; double y = m_cursor->y(); - if (*m_selectedDotIndex == m_store->numberOfPairsOfSeries(m_selectedSeries)) { + if (*m_selectedDotIndex == m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) { constexpr static char legY[] = {Ion::Charset::YBar, '=', 0}; legend = legY; - y = m_store->meanOfColumn(m_selectedSeries, 1); + y = m_store->meanOfColumn(*m_selectedSeriesIndex, 1); } legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); @@ -139,7 +139,7 @@ void GraphController::reloadBannerView() { numberOfChar = 0; legend = " a="; - double slope = m_store->slope(m_selectedSeries); + double slope = m_store->slope(*m_selectedSeriesIndex); legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; @@ -152,7 +152,7 @@ void GraphController::reloadBannerView() { numberOfChar = 0; legend = " b="; - double yIntercept = m_store->yIntercept(m_selectedSeries); + double yIntercept = m_store->yIntercept(*m_selectedSeriesIndex); legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; @@ -165,7 +165,7 @@ void GraphController::reloadBannerView() { numberOfChar = 0; legend = " r="; - double r = m_store->correlationCoefficient(m_selectedSeries); + double r = m_store->correlationCoefficient(*m_selectedSeriesIndex); legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; @@ -178,7 +178,7 @@ void GraphController::reloadBannerView() { numberOfChar = 0; legend = " r2="; - double r2 = m_store->squaredCorrelationCoefficient(m_selectedSeries); + double r2 = m_store->squaredCorrelationCoefficient(*m_selectedSeriesIndex); legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; @@ -195,26 +195,26 @@ void GraphController::initRangeParameters() { } void GraphController::initCursorParameters() { - m_selectedSeries = m_store->indexOfKthNonEmptySeries(0); - double x = m_store->meanOfColumn(m_selectedSeries, 0); - double y = m_store->meanOfColumn(m_selectedSeries, 1); + *m_selectedSeriesIndex = m_store->indexOfKthNonEmptySeries(0); + double x = m_store->meanOfColumn(*m_selectedSeriesIndex, 0); + double y = m_store->meanOfColumn(*m_selectedSeriesIndex, 1); m_cursor->moveTo(x, y); m_store->panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); - *m_selectedDotIndex = m_store->numberOfPairsOfSeries(m_selectedSeries); + *m_selectedDotIndex = m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex); } bool GraphController::moveCursorHorizontally(int direction) { if (*m_selectedDotIndex >= 0) { - int dotSelected = m_store->nextDot(m_selectedSeries, direction, *m_selectedDotIndex); - if (dotSelected >= 0 && dotSelected < m_store->numberOfPairsOfSeries(m_selectedSeries)) { + int dotSelected = m_store->nextDot(*m_selectedSeriesIndex, direction, *m_selectedDotIndex); + if (dotSelected >= 0 && dotSelected < m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) { *m_selectedDotIndex = dotSelected; - m_cursor->moveTo(m_store->get(m_selectedSeries, 0, *m_selectedDotIndex), m_store->get(m_selectedSeries, 1, *m_selectedDotIndex)); + m_cursor->moveTo(m_store->get(*m_selectedSeriesIndex, 0, *m_selectedDotIndex), m_store->get(*m_selectedSeriesIndex, 1, *m_selectedDotIndex)); m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); return true; } - if (dotSelected == m_store->numberOfPairsOfSeries(m_selectedSeries)) { + if (dotSelected == m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) { *m_selectedDotIndex = dotSelected; - m_cursor->moveTo(m_store->meanOfColumn(m_selectedSeries, 0), m_store->meanOfColumn(m_selectedSeries, 1)); + m_cursor->moveTo(m_store->meanOfColumn(*m_selectedSeriesIndex, 0), m_store->meanOfColumn(*m_selectedSeriesIndex, 1)); m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); return true; } @@ -222,7 +222,7 @@ bool GraphController::moveCursorHorizontally(int direction) { } double x = direction > 0 ? m_cursor->x() + m_store->xGridUnit()/k_numberOfCursorStepsInGradUnit : m_cursor->x() - m_store->xGridUnit()/k_numberOfCursorStepsInGradUnit; - double y = m_store->yValueForXValue(m_selectedSeries, x); + double y = m_store->yValueForXValue(*m_selectedSeriesIndex, x); m_cursor->moveTo(x, y); m_store->panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); return true; @@ -236,15 +236,15 @@ bool GraphController::moveCursorVertically(int direction) { if (*m_selectedDotIndex == -1) { // The current cursor is on a regression // Check the closest regression - closestRegressionSeries = m_store->closestVerticalRegression(direction, m_cursor->x(), m_cursor->y(), m_selectedSeries); + closestRegressionSeries = m_store->closestVerticalRegression(direction, m_cursor->x(), m_cursor->y(), *m_selectedSeriesIndex); // Check the closest dot - dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), direction > 0 ? -FLT_MAX : FLT_MAX, m_selectedSeries, *m_selectedDotIndex, &closestDotSeries); + dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), direction > 0 ? -FLT_MAX : FLT_MAX, *m_selectedSeriesIndex, *m_selectedDotIndex, &closestDotSeries); } else { // The current cursor is on a dot // Check the closest regression closestRegressionSeries = m_store->closestVerticalRegression(direction, m_cursor->x(), m_cursor->y(), -1); // Check the closest dot - dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), m_cursor->y(), m_selectedSeries, *m_selectedDotIndex, &closestDotSeries); + dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), m_cursor->y(), *m_selectedSeriesIndex, *m_selectedDotIndex, &closestDotSeries); } bool validRegression = closestRegressionSeries > -1; @@ -281,23 +281,23 @@ bool GraphController::moveCursorVertically(int direction) { } if (!validDot && validRegression) { // Select the regression - m_selectedSeries = closestRegressionSeries; + *m_selectedSeriesIndex = closestRegressionSeries; selectRegressionCurve(); - m_cursor->moveTo(m_cursor->x(), m_store->yValueForXValue(m_selectedSeries, m_cursor->x())); + m_cursor->moveTo(m_cursor->x(), m_store->yValueForXValue(*m_selectedSeriesIndex, m_cursor->x())); m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); return true; } if (validDot && !validRegression) { m_view.setCursorView(&m_crossCursorView); - m_selectedSeries = closestDotSeries; + *m_selectedSeriesIndex = closestDotSeries; *m_selectedDotIndex = dotSelected; - if (dotSelected == m_store->numberOfPairsOfSeries(m_selectedSeries)) { - m_cursor->moveTo(m_store->meanOfColumn(m_selectedSeries, 0), m_store->meanOfColumn(m_selectedSeries, 1)); + if (dotSelected == m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) { + m_cursor->moveTo(m_store->meanOfColumn(*m_selectedSeriesIndex, 0), m_store->meanOfColumn(*m_selectedSeriesIndex, 1)); m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); return true; } - m_cursor->moveTo(m_store->get(m_selectedSeries, 0, *m_selectedDotIndex), m_store->get(m_selectedSeries, 1, *m_selectedDotIndex)); + m_cursor->moveTo(m_store->get(*m_selectedSeriesIndex, 0, *m_selectedDotIndex), m_store->get(*m_selectedSeriesIndex, 1, *m_selectedDotIndex)); m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); return true; } diff --git a/apps/regression/graph_controller.h b/apps/regression/graph_controller.h index 4d582b009..d18e8e139 100644 --- a/apps/regression/graph_controller.h +++ b/apps/regression/graph_controller.h @@ -17,13 +17,13 @@ namespace Regression { class GraphController : public Shared::InteractiveCurveViewController { public: - GraphController(Responder * parentResponder, ButtonRowController * header, Store * store, Shared::CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion, int * selectedDotIndex); + GraphController(Responder * parentResponder, ButtonRowController * header, Store * store, Shared::CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex); ViewController * initialisationParameterController() override; bool isEmpty() const override; I18n::Message emptyMessage() override; void viewWillAppear() override; void selectRegressionCurve(); - int selectedSeries() const { return m_selectedSeries; } + int selectedSeriesIndex() const { return *m_selectedSeriesIndex; } private: constexpr static float k_cursorTopMarginRatio = 0.07f; // (cursorHeight/2)/graphViewHeight constexpr static float k_cursorBottomMarginRatio = 0.3f; // (cursorHeight/2+bannerHeigh)/graphViewHeight @@ -50,7 +50,7 @@ private: /* The selectedDotIndex is -1 when no dot is selected, m_numberOfPairs when * the mean dot is selected and the dot index otherwise */ int * m_selectedDotIndex; - int m_selectedSeries; // TODO store in the Snapshot + int * m_selectedSeriesIndex; }; } From 3a3522faef044d9c87ecc9361b9308fe732ab2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 12:06:49 +0200 Subject: [PATCH 150/156] [apps/statistics] Store selected series in snapshot --- apps/statistics/app.cpp | 6 +- apps/statistics/app.h | 5 +- apps/statistics/box_controller.cpp | 16 ++--- apps/statistics/box_controller.h | 2 +- apps/statistics/box_view.cpp | 2 +- apps/statistics/histogram_controller.cpp | 60 +++++++++---------- apps/statistics/histogram_controller.h | 2 +- .../multiple_data_view_controller.cpp | 46 +++++++------- .../multiple_data_view_controller.h | 6 +- 9 files changed, 75 insertions(+), 70 deletions(-) diff --git a/apps/statistics/app.cpp b/apps/statistics/app.cpp index eea3cb2dc..fd396fdaa 100644 --- a/apps/statistics/app.cpp +++ b/apps/statistics/app.cpp @@ -23,7 +23,9 @@ App::Snapshot::Snapshot() : m_storeVersion(0), m_barVersion(0), m_rangeVersion(0), + m_selectedHistogramSeriesIndex(-1), m_selectedHistogramBarIndex(0), + m_selectedBoxSeriesIndex(-1), m_selectedBoxQuantile(BoxView::Quantile::Min) { } @@ -52,10 +54,10 @@ App::App(Container * container, Snapshot * snapshot) : m_calculationController(&m_calculationAlternateEmptyViewController, &m_calculationHeader, snapshot->store()), m_calculationAlternateEmptyViewController(&m_calculationHeader, &m_calculationController, &m_calculationController), m_calculationHeader(&m_tabViewController, &m_calculationAlternateEmptyViewController, &m_calculationController), - m_boxController(&m_boxAlternateEmptyViewController, &m_boxHeader, snapshot->store(), snapshot->selectedBoxQuantile()), + m_boxController(&m_boxAlternateEmptyViewController, &m_boxHeader, snapshot->store(), snapshot->selectedBoxQuantile(), snapshot->selectedBoxSeriesIndex()), m_boxAlternateEmptyViewController(&m_boxHeader, &m_boxController, &m_boxController), m_boxHeader(&m_tabViewController, &m_boxAlternateEmptyViewController, &m_boxController), - m_histogramController(&m_histogramAlternateEmptyViewController, &m_histogramHeader, snapshot->store(), snapshot->storeVersion(), snapshot->barVersion(), snapshot->rangeVersion(), snapshot->selectedHistogramBarIndex()), + m_histogramController(&m_histogramAlternateEmptyViewController, &m_histogramHeader, snapshot->store(), snapshot->storeVersion(), snapshot->barVersion(), snapshot->rangeVersion(), snapshot->selectedHistogramBarIndex(), snapshot->selectedHistogramSeriesIndex()), m_histogramAlternateEmptyViewController(&m_histogramHeader, &m_histogramController, &m_histogramController), m_histogramHeader(&m_histogramStackViewController, &m_histogramAlternateEmptyViewController, &m_histogramController), m_histogramStackViewController(&m_tabViewController, &m_histogramHeader), diff --git a/apps/statistics/app.h b/apps/statistics/app.h index 60784bebc..06871fae4 100644 --- a/apps/statistics/app.h +++ b/apps/statistics/app.h @@ -29,16 +29,19 @@ public: uint32_t * storeVersion() { return &m_storeVersion; } uint32_t * barVersion() { return &m_barVersion; } uint32_t * rangeVersion() { return &m_rangeVersion; } + int * selectedHistogramSeriesIndex() { return &m_selectedHistogramSeriesIndex; } int * selectedHistogramBarIndex() { return &m_selectedHistogramBarIndex; } + int * selectedBoxSeriesIndex() { return &m_selectedBoxSeriesIndex; } BoxView::Quantile * selectedBoxQuantile() { return &m_selectedBoxQuantile; } private: Store m_store; uint32_t m_storeVersion; uint32_t m_barVersion; uint32_t m_rangeVersion; + int m_selectedHistogramSeriesIndex; int m_selectedHistogramBarIndex; + int m_selectedBoxSeriesIndex; BoxView::Quantile m_selectedBoxQuantile; - // TODO add selected Series for both histogram and box }; private: App(Container * container, Snapshot * snapshot); diff --git a/apps/statistics/box_controller.cpp b/apps/statistics/box_controller.cpp index 29d0b55e0..80129022f 100644 --- a/apps/statistics/box_controller.cpp +++ b/apps/statistics/box_controller.cpp @@ -6,17 +6,17 @@ using namespace Poincare; namespace Statistics { -BoxController::BoxController(Responder * parentResponder, ButtonRowController * header, Store * store, BoxView::Quantile * selectedQuantile) : - MultipleDataViewController(parentResponder, store, (int *)(selectedQuantile)), +BoxController::BoxController(Responder * parentResponder, ButtonRowController * header, Store * store, BoxView::Quantile * selectedQuantile, int * selectedSeriesIndex) : + MultipleDataViewController(parentResponder, store, (int *)(selectedQuantile), selectedSeriesIndex), ButtonRowDelegate(header, nullptr), m_view(this, store, selectedQuantile) { } bool BoxController::moveSelectionHorizontally(int deltaIndex) { - int selectedQuantile = (int)m_view.dataViewAtIndex(selectedSeries())->selectedQuantile(); + int selectedQuantile = (int)m_view.dataViewAtIndex(selectedSeriesIndex())->selectedQuantile(); int nextSelectedQuantile = selectedQuantile + deltaIndex; - if (m_view.dataViewAtIndex(selectedSeries())->selectQuantile(nextSelectedQuantile)) { + if (m_view.dataViewAtIndex(selectedSeriesIndex())->selectQuantile(nextSelectedQuantile)) { reloadBannerView(); return true; } @@ -32,14 +32,14 @@ Responder * BoxController::tabController() const { } void BoxController::reloadBannerView() { - if (selectedSeries() < 0) { + if (selectedSeriesIndex() < 0) { return; } - int selectedQuantile = (int)m_view.dataViewAtIndex(selectedSeries())->selectedQuantile(); + int selectedQuantile = (int)m_view.dataViewAtIndex(selectedSeriesIndex())->selectedQuantile(); // Set series name - char seriesChar = '0' + selectedSeries() + 1; + char seriesChar = '0' + selectedSeriesIndex() + 1; char bufferName[] = {' ', 'V', seriesChar, '/', 'N', seriesChar, 0}; m_view.editableBannerView()->setLegendAtIndex(bufferName, 0); @@ -52,7 +52,7 @@ void BoxController::reloadBannerView() { char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits) + 1]; CalculPointer calculationMethods[5] = {&Store::minValue, &Store::firstQuartile, &Store::median, &Store::thirdQuartile, &Store::maxValue}; - double calculation = (m_store->*calculationMethods[selectedQuantile])(selectedSeries()); + double calculation = (m_store->*calculationMethods[selectedQuantile])(selectedSeriesIndex()); int numberOfChar = PrintFloat::convertFloatToText(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); buffer[numberOfChar++] = ' '; buffer[numberOfChar] = 0; diff --git a/apps/statistics/box_controller.h b/apps/statistics/box_controller.h index 2ee62a35e..1a9b30084 100644 --- a/apps/statistics/box_controller.h +++ b/apps/statistics/box_controller.h @@ -10,7 +10,7 @@ namespace Statistics { class BoxController : public MultipleDataViewController, public ButtonRowDelegate { public: - BoxController(Responder * parentResponder, ButtonRowController * header, Store * store, BoxView::Quantile * selectedQuantile); + BoxController(Responder * parentResponder, ButtonRowController * header, Store * store, BoxView::Quantile * selectedQuantile, int * selectedSeriesIndex); MultipleDataView * multipleDataView() override { return &m_view; } bool moveSelectionHorizontally(int deltaIndex) override; diff --git a/apps/statistics/box_view.cpp b/apps/statistics/box_view.cpp index a81a4812c..0ff506527 100644 --- a/apps/statistics/box_view.cpp +++ b/apps/statistics/box_view.cpp @@ -64,7 +64,7 @@ void BoxView::drawRect(KDContext * ctx, KDRect rect) const { double thirdQuart = m_store->thirdQuartile(m_series); double maxVal = m_store->maxValue(m_series); - bool isSelected = m_boxController->selectedSeries() == m_series; + bool isSelected = m_boxController->selectedSeriesIndex() == m_series; KDColor boxColor = isSelected ? m_selectedHistogramLightColor : Palette::GreyWhite; // Draw the main box KDCoordinate firstQuartilePixels = std::round(floatToPixel(Axis::Horizontal, firstQuart)); diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 185788d23..d0a818f33 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -13,8 +13,8 @@ namespace Statistics { static inline float min(float x, float y) { return (xy ? x : y); } -HistogramController::HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, uint32_t * storeVersion, uint32_t * barVersion, uint32_t * rangeVersion, int * selectedBarIndex) : - MultipleDataViewController(parentResponder, store, selectedBarIndex), +HistogramController::HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, uint32_t * storeVersion, uint32_t * barVersion, uint32_t * rangeVersion, int * selectedBarIndex, int * selectedSeriesIndex) : + MultipleDataViewController(parentResponder, store, selectedBarIndex, selectedSeriesIndex), ButtonRowDelegate(header, nullptr), m_view(this, store), m_storeVersion(storeVersion), @@ -38,7 +38,7 @@ const char * HistogramController::title() { } bool HistogramController::handleEvent(Ion::Events::Event event) { - assert(selectedSeries() >= 0); + assert(selectedSeriesIndex() >= 0); if (event == Ion::Events::OK) { stackController()->push(histogramParameterController()); return true; @@ -66,14 +66,14 @@ void HistogramController::didBecomeFirstResponder() { initBarSelection(); reloadBannerView(); } - HistogramView * selectedHistogramView = static_cast(m_view.dataViewAtIndex(selectedSeries())); - selectedHistogramView->setHighlight(m_store->startOfBarAtIndex(selectedSeries(), *m_selectedBarIndex), m_store->endOfBarAtIndex(selectedSeries(), *m_selectedBarIndex)); + HistogramView * selectedHistogramView = static_cast(m_view.dataViewAtIndex(selectedSeriesIndex())); + selectedHistogramView->setHighlight(m_store->startOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex), m_store->endOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex)); } void HistogramController::willExitResponderChain(Responder * nextFirstResponder) { if (nextFirstResponder == nullptr || nextFirstResponder == tabController()) { - if (selectedSeries() >= 0) { - m_view.dataViewAtIndex(selectedSeries())->setForceOkDisplay(false); + if (selectedSeriesIndex() >= 0) { + m_view.dataViewAtIndex(selectedSeriesIndex())->setForceOkDisplay(false); } } MultipleDataViewController::willExitResponderChain(nextFirstResponder); @@ -84,7 +84,7 @@ Responder * HistogramController::tabController() const { } void HistogramController::reloadBannerView() { - if (selectedSeries() < 0) { + if (selectedSeriesIndex() < 0) { return; } char buffer[k_maxNumberOfCharacters+ PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)*2]; @@ -97,16 +97,16 @@ void HistogramController::reloadBannerView() { numberOfChar += legendLength; // Add lower bound - if (selectedSeries() >= 0) { - double lowerBound = m_store->startOfBarAtIndex(selectedSeries(), *m_selectedBarIndex); + if (selectedSeriesIndex() >= 0) { + double lowerBound = m_store->startOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex); numberOfChar += PrintFloat::convertFloatToText(lowerBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); } buffer[numberOfChar++] = ';'; // Add upper bound - if (selectedSeries() >= 0) { - double upperBound = m_store->endOfBarAtIndex(selectedSeries(), *m_selectedBarIndex); + if (selectedSeriesIndex() >= 0) { + double upperBound = m_store->endOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex); numberOfChar += PrintFloat::convertFloatToText(upperBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); } @@ -126,8 +126,8 @@ void HistogramController::reloadBannerView() { strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; double size = 0; - if (selectedSeries() >= 0) { - size = m_store->heightOfBarAtIndex(selectedSeries(), *m_selectedBarIndex); + if (selectedSeriesIndex() >= 0) { + size = m_store->heightOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex); numberOfChar += PrintFloat::convertFloatToText(size, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); } // Padding @@ -143,8 +143,8 @@ void HistogramController::reloadBannerView() { legendLength = strlen(legend); strlcpy(buffer, legend, legendLength+1); numberOfChar += legendLength; - if (selectedSeries() >= 0) { - double frequency = size/m_store->sumOfOccurrences(selectedSeries()); + if (selectedSeriesIndex() >= 0) { + double frequency = size/m_store->sumOfOccurrences(selectedSeriesIndex()); numberOfChar += PrintFloat::convertFloatToText(frequency, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); } // Padding @@ -159,17 +159,17 @@ bool HistogramController::moveSelectionHorizontally(int deltaIndex) { int newSelectedBarIndex = *m_selectedBarIndex; do { newSelectedBarIndex+=deltaIndex; - } while (m_store->heightOfBarAtIndex(selectedSeries(), newSelectedBarIndex) == 0 + } while (m_store->heightOfBarAtIndex(selectedSeriesIndex(), newSelectedBarIndex) == 0 && newSelectedBarIndex >= 0 - && newSelectedBarIndex < m_store->numberOfBars(selectedSeries())); + && newSelectedBarIndex < m_store->numberOfBars(selectedSeriesIndex())); if (newSelectedBarIndex >= 0 - && newSelectedBarIndex < m_store->numberOfBars(selectedSeries()) + && newSelectedBarIndex < m_store->numberOfBars(selectedSeriesIndex()) && *m_selectedBarIndex != newSelectedBarIndex) { *m_selectedBarIndex = newSelectedBarIndex; - m_view.dataViewAtIndex(selectedSeries())->setHighlight(m_store->startOfBarAtIndex(selectedSeries(), *m_selectedBarIndex), m_store->endOfBarAtIndex(selectedSeries(), *m_selectedBarIndex)); - if (m_store->scrollToSelectedBarIndex(selectedSeries(), *m_selectedBarIndex)) { + m_view.dataViewAtIndex(selectedSeriesIndex())->setHighlight(m_store->startOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex), m_store->endOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex)); + if (m_store->scrollToSelectedBarIndex(selectedSeriesIndex(), *m_selectedBarIndex)) { multipleDataView()->reload(); } reloadBannerView(); @@ -179,7 +179,7 @@ bool HistogramController::moveSelectionHorizontally(int deltaIndex) { } void HistogramController::initRangeParameters() { - assert(selectedSeries() >= 0 && m_store->sumOfOccurrences(selectedSeries()) > 0); + assert(selectedSeriesIndex() >= 0 && m_store->sumOfOccurrences(selectedSeriesIndex()) > 0); float minValue = m_store->firstDrawnBarAbscissa(); float maxValue = -FLT_MAX; for (int i = 0; i < Store::k_numberOfSeries; i ++) { @@ -201,7 +201,7 @@ void HistogramController::initRangeParameters() { m_store->setXMin(xMin - Store::k_displayLeftMarginRatio*(xMax-xMin)); m_store->setXMax(xMax + Store::k_displayRightMarginRatio*(xMax-xMin)); - initYRangeParameters(selectedSeries()); + initYRangeParameters(selectedSeriesIndex()); } void HistogramController::initYRangeParameters(int series) { @@ -232,7 +232,7 @@ void HistogramController::initYRangeParameters(int series) { } void HistogramController::initBarParameters() { - assert(selectedSeries() >= 0 && m_store->sumOfOccurrences(selectedSeries()) > 0); + assert(selectedSeriesIndex() >= 0 && m_store->sumOfOccurrences(selectedSeriesIndex()) > 0); float minValue = FLT_MAX; float maxValue = -FLT_MAX; for (int i = 0; i < Store::k_numberOfSeries; i ++) { @@ -251,20 +251,20 @@ void HistogramController::initBarParameters() { } void HistogramController::initBarSelection() { - assert(selectedSeries() >= 0 && m_store->sumOfOccurrences(selectedSeries()) > 0); + assert(selectedSeriesIndex() >= 0 && m_store->sumOfOccurrences(selectedSeriesIndex()) > 0); *m_selectedBarIndex = 0; - while ((m_store->heightOfBarAtIndex(selectedSeries(), *m_selectedBarIndex) == 0 || - m_store->startOfBarAtIndex(selectedSeries(), *m_selectedBarIndex) < m_store->firstDrawnBarAbscissa()) && *m_selectedBarIndex < m_store->numberOfBars(selectedSeries())) { + while ((m_store->heightOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex) == 0 || + m_store->startOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex) < m_store->firstDrawnBarAbscissa()) && *m_selectedBarIndex < m_store->numberOfBars(selectedSeriesIndex())) { *m_selectedBarIndex = *m_selectedBarIndex+1; } - if (*m_selectedBarIndex >= m_store->numberOfBars(selectedSeries())) { + if (*m_selectedBarIndex >= m_store->numberOfBars(selectedSeriesIndex())) { /* No bar is after m_firstDrawnBarAbscissa, so we select the first bar */ *m_selectedBarIndex = 0; - while (m_store->heightOfBarAtIndex(selectedSeries(), *m_selectedBarIndex) == 0 && *m_selectedBarIndex < m_store->numberOfBars(selectedSeries())) { + while (m_store->heightOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex) == 0 && *m_selectedBarIndex < m_store->numberOfBars(selectedSeriesIndex())) { *m_selectedBarIndex = *m_selectedBarIndex+1; } } - m_store->scrollToSelectedBarIndex(selectedSeries(), *m_selectedBarIndex); + m_store->scrollToSelectedBarIndex(selectedSeriesIndex(), *m_selectedBarIndex); } } diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index 826075cc9..1790723b6 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -11,7 +11,7 @@ namespace Statistics { class HistogramController : public MultipleDataViewController, public ButtonRowDelegate { public: - HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, uint32_t * m_storeVersion, uint32_t * m_barVersion, uint32_t * m_rangeVersion, int * m_selectedBarIndex); + HistogramController(Responder * parentResponder, ButtonRowController * header, Store * store, uint32_t * m_storeVersion, uint32_t * m_barVersion, uint32_t * m_rangeVersion, int * m_selectedBarIndex, int * selectedSeriesIndex); HistogramParameterController * histogramParameterController() { return &m_histogramParameterController; } void setCurrentDrawnSeries(int series); diff --git a/apps/statistics/multiple_data_view_controller.cpp b/apps/statistics/multiple_data_view_controller.cpp index df5b06b1f..876164e51 100644 --- a/apps/statistics/multiple_data_view_controller.cpp +++ b/apps/statistics/multiple_data_view_controller.cpp @@ -6,10 +6,10 @@ using namespace Shared; namespace Statistics { -MultipleDataViewController::MultipleDataViewController(Responder * parentResponder, Store * store, int * selectedBarIndex) : +MultipleDataViewController::MultipleDataViewController(Responder * parentResponder, Store * store, int * selectedBarIndex, int * selectedSeriesIndex) : ViewController(parentResponder), m_store(store), - m_selectedSeries(-1), + m_selectedSeriesIndex(selectedSeriesIndex), m_selectedBarIndex(selectedBarIndex) { } @@ -28,23 +28,23 @@ Responder * MultipleDataViewController::defaultController() { void MultipleDataViewController::viewWillAppear() { multipleDataView()->setDisplayBanner(true); - if (m_selectedSeries < 0) { - m_selectedSeries = multipleDataView()->seriesOfSubviewAtIndex(0); - multipleDataView()->selectDataView(m_selectedSeries); + if (*m_selectedSeriesIndex < 0) { + *m_selectedSeriesIndex = multipleDataView()->seriesOfSubviewAtIndex(0); + multipleDataView()->selectDataView(*m_selectedSeriesIndex); } reloadBannerView(); multipleDataView()->reload(); } bool MultipleDataViewController::handleEvent(Ion::Events::Event event) { - assert(m_selectedSeries >= 0); + assert(*m_selectedSeriesIndex >= 0); if (event == Ion::Events::Down) { - int currentSelectedSubview = multipleDataView()->indexOfSubviewAtSeries(m_selectedSeries); + int currentSelectedSubview = multipleDataView()->indexOfSubviewAtSeries(*m_selectedSeriesIndex); if (currentSelectedSubview < m_store->numberOfNonEmptySeries() - 1) { - multipleDataView()->deselectDataView(m_selectedSeries); - m_selectedSeries = multipleDataView()->seriesOfSubviewAtIndex(currentSelectedSubview+1); + multipleDataView()->deselectDataView(*m_selectedSeriesIndex); + *m_selectedSeriesIndex = multipleDataView()->seriesOfSubviewAtIndex(currentSelectedSubview+1); *m_selectedBarIndex = MultipleDataView::k_defaultSelectedBar; - multipleDataView()->selectDataView(m_selectedSeries); + multipleDataView()->selectDataView(*m_selectedSeriesIndex); reloadBannerView(); app()->setFirstResponder(this); return true; @@ -52,12 +52,12 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) { return false; } if (event == Ion::Events::Up) { - int currentSelectedSubview = multipleDataView()->indexOfSubviewAtSeries(m_selectedSeries); + int currentSelectedSubview = multipleDataView()->indexOfSubviewAtSeries(*m_selectedSeriesIndex); if (currentSelectedSubview > 0) { - multipleDataView()->deselectDataView(m_selectedSeries); - m_selectedSeries = multipleDataView()->seriesOfSubviewAtIndex(currentSelectedSubview-1); + multipleDataView()->deselectDataView(*m_selectedSeriesIndex); + *m_selectedSeriesIndex = multipleDataView()->seriesOfSubviewAtIndex(currentSelectedSubview-1); *m_selectedBarIndex = MultipleDataView::k_defaultSelectedBar; - multipleDataView()->selectDataView(m_selectedSeries); + multipleDataView()->selectDataView(*m_selectedSeriesIndex); app()->setFirstResponder(this); } else { app()->setFirstResponder(tabController()); @@ -65,7 +65,7 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) { reloadBannerView(); return true; } - if (m_selectedSeries >= 0 && (event == Ion::Events::Left || event == Ion::Events::Right)) { + if (*m_selectedSeriesIndex >= 0 && (event == Ion::Events::Left || event == Ion::Events::Right)) { int direction = event == Ion::Events::Left ? -1 : 1; moveSelectionHorizontally(direction); return true; @@ -75,21 +75,21 @@ bool MultipleDataViewController::handleEvent(Ion::Events::Event event) { void MultipleDataViewController::didBecomeFirstResponder() { multipleDataView()->setDisplayBanner(true); - if (m_selectedSeries < 0 || m_store->sumOfOccurrences(m_selectedSeries) == 0) { - if (m_selectedSeries >= 0) { - multipleDataView()->deselectDataView(m_selectedSeries); + if (*m_selectedSeriesIndex < 0 || m_store->sumOfOccurrences(*m_selectedSeriesIndex) == 0) { + if (*m_selectedSeriesIndex >= 0) { + multipleDataView()->deselectDataView(*m_selectedSeriesIndex); } - m_selectedSeries = multipleDataView()->seriesOfSubviewAtIndex(0); - multipleDataView()->selectDataView(m_selectedSeries); + *m_selectedSeriesIndex = multipleDataView()->seriesOfSubviewAtIndex(0); + multipleDataView()->selectDataView(*m_selectedSeriesIndex); multipleDataView()->reload(); } } void MultipleDataViewController::willExitResponderChain(Responder * nextFirstResponder) { if (nextFirstResponder == nullptr || nextFirstResponder == tabController()) { - if (m_selectedSeries >= 0) { - multipleDataView()->dataViewAtIndex(m_selectedSeries)->selectMainView(false); - m_selectedSeries = -1; + if (*m_selectedSeriesIndex >= 0) { + multipleDataView()->dataViewAtIndex(*m_selectedSeriesIndex)->selectMainView(false); + *m_selectedSeriesIndex = -1; multipleDataView()->setDisplayBanner(false); } } diff --git a/apps/statistics/multiple_data_view_controller.h b/apps/statistics/multiple_data_view_controller.h index b8f5041d0..e3dfa92ed 100644 --- a/apps/statistics/multiple_data_view_controller.h +++ b/apps/statistics/multiple_data_view_controller.h @@ -10,9 +10,9 @@ namespace Statistics { class MultipleDataViewController : public ViewController, public AlternateEmptyViewDelegate { public: - MultipleDataViewController(Responder * parentResponder, Store * store, int * m_selectedBarIndex); + MultipleDataViewController(Responder * parentResponder, Store * store, int * m_selectedBarIndex, int * selectedSeriesIndex); virtual MultipleDataView * multipleDataView() = 0; - int selectedSeries() const { return m_selectedSeries; } + int selectedSeriesIndex() const { return *m_selectedSeriesIndex; } // AlternateEmptyViewDelegate bool isEmpty() const override; I18n::Message emptyMessage() override; @@ -31,7 +31,7 @@ protected: virtual void reloadBannerView() = 0; virtual bool moveSelectionHorizontally(int deltaIndex) = 0; Store * m_store; - int m_selectedSeries; + int * m_selectedSeriesIndex; int * m_selectedBarIndex; }; From ec207747399ca934c7bf24a20ec27e11cf4b1574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 13:31:44 +0200 Subject: [PATCH 151/156] [app/shared] Fix textfield emptying on syntax error --- apps/shared/buffer_text_view_with_text_field.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/shared/buffer_text_view_with_text_field.cpp b/apps/shared/buffer_text_view_with_text_field.cpp index 3de524f41..4e97fec39 100644 --- a/apps/shared/buffer_text_view_with_text_field.cpp +++ b/apps/shared/buffer_text_view_with_text_field.cpp @@ -41,7 +41,7 @@ void BufferTextViewWithTextField::drawRect(KDContext * ctx, KDRect rect) const { void BufferTextViewWithTextField::didBecomeFirstResponder() { app()->setFirstResponder(&m_textField); - m_textField.setEditing(true, true); + m_textField.setEditing(true, false); markRectAsDirty(bounds()); } From 9ed4e55d57d53737ec666a299da1ace5a837a939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 15:17:55 +0200 Subject: [PATCH 152/156] [apps/shared] translation --- apps/shared.de.i18n | 3 ++- apps/shared.en.i18n | 3 ++- apps/shared.es.i18n | 3 ++- apps/shared.fr.i18n | 3 ++- apps/shared.pt.i18n | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index dd2539a20..be4a61637 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -14,7 +14,7 @@ Deviation = "Varianz" DisplayValues = "Werte anzeigen" ExitExamMode1 = "Wollen Sie den Testmodus " ExitExamMode2 = "verlassen?" -FillWithFormula = "Fill with formula" +FillWithFormula = "Mit einer Formel füllen" ForbiddenValue = "Verbotener Wert" FunctionColumn = "0(0) Spalte" FunctionOptions = "Optionen Funktion" @@ -33,6 +33,7 @@ Move = " Verschieben: " Next = "Nachste" NoDataToPlot = "Keine Daten zu zeichnen" NoFunctionToDelete = "Keine Funktion zu loschen" +NonCompliantFormula = "Die Formel kann für diese Daten nicht verwendet werden" NoValueToCompute = "Keine Grosse zu berechnen" Ok = "Bestatigen" Or = " oder " diff --git a/apps/shared.en.i18n b/apps/shared.en.i18n index 85394a23a..a1c2da5f2 100644 --- a/apps/shared.en.i18n +++ b/apps/shared.en.i18n @@ -14,7 +14,7 @@ Deviation = "Variance" DisplayValues = "Display values" ExitExamMode1 = "Exit the exam " ExitExamMode2 = "mode?" -FillWithFormula = "Fill with formula" +FillWithFormula = "Fill with a formula" ForbiddenValue = "Forbidden value" FunctionColumn = "0(0) column" FunctionOptions = "Function options" @@ -33,6 +33,7 @@ Move = " Move: " Next = "Next" NoDataToPlot = "No data to draw" NoFunctionToDelete = "No function to delete" +NonCompliantFormula = "The formula cannot be used for these data" NoValueToCompute = "No values to calculate" Ok = "Confirm" Or = " or " diff --git a/apps/shared.es.i18n b/apps/shared.es.i18n index d25e838d2..246259752 100644 --- a/apps/shared.es.i18n +++ b/apps/shared.es.i18n @@ -14,7 +14,7 @@ Deviation = "Varianza" DisplayValues = "Visualizar los valores" ExitExamMode1 = "Salir del modo " ExitExamMode2 = "examen ?" -FillWithFormula = "Fill with formula" +FillWithFormula = "Rellenar con una fórmula" ForbiddenValue = "Valor prohibido" FunctionColumn = "Columna 0(0)" FunctionOptions = "Opciones de la funcion" @@ -33,6 +33,7 @@ Move = " Mover : " Next = "Siguiente" NoDataToPlot = "Ningunos datos que dibujar" NoFunctionToDelete = "Ninguna funcion que eliminar" +NonCompliantFormula = "La fórmula no se puede utilizar para estos datos" NoValueToCompute = "Ninguna medida que calcular" Ok = "Confirmar" Or = " o " diff --git a/apps/shared.fr.i18n b/apps/shared.fr.i18n index 207e82836..146efe9a4 100644 --- a/apps/shared.fr.i18n +++ b/apps/shared.fr.i18n @@ -14,7 +14,7 @@ Deviation = "Variance" DisplayValues = "Afficher les valeurs" ExitExamMode1 = "Voulez-vous sortir " ExitExamMode2 = "du mode examen ?" -FillWithFormula = "Fill with formula" +FillWithFormula = "Remplir avec une formule" ForbiddenValue = "Valeur interdite" FunctionColumn = "Colonne 0(0)" FunctionOptions = "Options de la fonction" @@ -33,6 +33,7 @@ Move = " Deplacer : " Next = "Suivant" NoDataToPlot = "Aucune donnee a tracer" NoFunctionToDelete = "Pas de fonction a supprimer" +NonCompliantFormula = "La formule ne peut pas s'appliquer à ces données" NoValueToCompute = "Aucune grandeur a calculer" Ok = "Valider" Or = " ou " diff --git a/apps/shared.pt.i18n b/apps/shared.pt.i18n index 20733146d..c78c6f7b1 100644 --- a/apps/shared.pt.i18n +++ b/apps/shared.pt.i18n @@ -14,7 +14,7 @@ Deviation = "Variancia" DisplayValues = "Exibir os valores" ExitExamMode1 = "Voce quer sair do modo de " ExitExamMode2 = "exame ?" -FillWithFormula = "Fill with formula" +FillWithFormula = "Preencha com uma fórmula" ForbiddenValue = "Valor proibida" FunctionColumn = "Coluna 0(0)" FunctionOptions = "Opcoes de funcao" @@ -33,6 +33,7 @@ Move = " Mover : " Next = "Seguinte" NoDataToPlot = "Nao ha dados para desenhar" NoFunctionToDelete = "Sem funcao para eliminar" +NonCompliantFormula = "A fórmula não pode ser usada para esses dados" NoValueToCompute = "Nenhuma quantidade para calcular" Ok = "Confirmar" Or = " ou " From a46a059f8bff397972b3888845d43b2575082b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 15:26:55 +0200 Subject: [PATCH 153/156] [apps/shared] Use better warning message --- apps/shared/store_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 098fdb26d..179c4eeeb 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -301,7 +301,7 @@ bool StoreController::privateFillColumnWithFormula(Expression * formula, Express // Compute the new value using the formula double evaluation = formula->approximateToScalar(*store); if (std::isnan(evaluation) || std::isinf(evaluation)) { - app()->displayWarning(I18n::Message::UndefinedValue); + app()->displayWarning(I18n::Message::NonCompliantFormula); return false; } } From 3a4dab2898f48c906f24c0a9e6ffef4ca002ef91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 15:41:36 +0200 Subject: [PATCH 154/156] [apps/reg] Fix Series title in calculation --- apps/regression/calculation_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index d48de5009..ea04e128f 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -146,7 +146,7 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int // Coordinate and series title if (j == 0 && i > 0) { ColumnTitleCell * myCell = (ColumnTitleCell *)cell; - char buffer[] = {'X', static_cast('0' + seriesNumber), 0}; + char buffer[] = {'X', static_cast('1' + seriesNumber), 0}; myCell->setFirstText(buffer); buffer[0] = 'Y'; myCell->setSecondText(buffer); From 625e4dc5cf1919b86a6fc6813be224a3a32cc3ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 16:03:18 +0200 Subject: [PATCH 155/156] [apps/shared] Fix translation --- apps/shared.pt.i18n | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/shared.pt.i18n b/apps/shared.pt.i18n index c78c6f7b1..39da61127 100644 --- a/apps/shared.pt.i18n +++ b/apps/shared.pt.i18n @@ -14,7 +14,7 @@ Deviation = "Variancia" DisplayValues = "Exibir os valores" ExitExamMode1 = "Voce quer sair do modo de " ExitExamMode2 = "exame ?" -FillWithFormula = "Preencha com uma fórmula" +FillWithFormula = "Preencher com uma fórmula" ForbiddenValue = "Valor proibida" FunctionColumn = "Coluna 0(0)" FunctionOptions = "Opcoes de funcao" From de5cc75ac33250dfd92d2c3f133d01de89daa54a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Jun 2018 17:49:24 +0200 Subject: [PATCH 156/156] [apps/stats/reg] Data not suitable message --- apps/shared.de.i18n | 2 +- apps/shared.en.i18n | 2 +- apps/shared.es.i18n | 2 +- apps/shared.fr.i18n | 2 +- apps/shared.pt.i18n | 2 +- apps/shared/store_controller.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index be4a61637..fa42b7a50 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -7,6 +7,7 @@ Cancel = "Abbrechen" ClearColumn = "Spalte loschen" ColumnOptions = "Optionen Spalte" CopyColumnInList = "Die Spalte in einer Liste kopieren" +DataNotSuitable = "Daten nicht geeignet" DataTab = "Daten" DefaultSetting = "Grundeinstellungen" Deg = "gra" @@ -33,7 +34,6 @@ Move = " Verschieben: " Next = "Nachste" NoDataToPlot = "Keine Daten zu zeichnen" NoFunctionToDelete = "Keine Funktion zu loschen" -NonCompliantFormula = "Die Formel kann für diese Daten nicht verwendet werden" NoValueToCompute = "Keine Grosse zu berechnen" Ok = "Bestatigen" Or = " oder " diff --git a/apps/shared.en.i18n b/apps/shared.en.i18n index a1c2da5f2..0f79756a6 100644 --- a/apps/shared.en.i18n +++ b/apps/shared.en.i18n @@ -7,6 +7,7 @@ Cancel = "Cancel" ClearColumn = "Clear column" ColumnOptions = "Column options" CopyColumnInList = "Export the column to a list" +DataNotSuitable = "Data not suitable" DataTab = "Data" DefaultSetting = "Basic settings" Deg = "deg" @@ -33,7 +34,6 @@ Move = " Move: " Next = "Next" NoDataToPlot = "No data to draw" NoFunctionToDelete = "No function to delete" -NonCompliantFormula = "The formula cannot be used for these data" NoValueToCompute = "No values to calculate" Ok = "Confirm" Or = " or " diff --git a/apps/shared.es.i18n b/apps/shared.es.i18n index 246259752..1b75c1c2d 100644 --- a/apps/shared.es.i18n +++ b/apps/shared.es.i18n @@ -7,6 +7,7 @@ Cancel = "Cancelar" ClearColumn = "Borrar la columna" ColumnOptions = "Opciones de la columna" CopyColumnInList = "Copiar la columna en una lista" +DataNotSuitable = "Datos no adecuados" DataTab = "Datos" DefaultSetting = "Ajustes basicos" Deg = "gra" @@ -33,7 +34,6 @@ Move = " Mover : " Next = "Siguiente" NoDataToPlot = "Ningunos datos que dibujar" NoFunctionToDelete = "Ninguna funcion que eliminar" -NonCompliantFormula = "La fórmula no se puede utilizar para estos datos" NoValueToCompute = "Ninguna medida que calcular" Ok = "Confirmar" Or = " o " diff --git a/apps/shared.fr.i18n b/apps/shared.fr.i18n index 146efe9a4..7e2b8c770 100644 --- a/apps/shared.fr.i18n +++ b/apps/shared.fr.i18n @@ -7,6 +7,7 @@ Cancel = "Annuler" ClearColumn = "Effacer la colonne" ColumnOptions = "Options de la colonne" CopyColumnInList = "Copier la colonne dans une liste" +DataNotSuitable = "Les données ne conviennent pas" DataTab = "Donnees" DefaultSetting = "Reglages de base" Deg = "deg" @@ -33,7 +34,6 @@ Move = " Deplacer : " Next = "Suivant" NoDataToPlot = "Aucune donnee a tracer" NoFunctionToDelete = "Pas de fonction a supprimer" -NonCompliantFormula = "La formule ne peut pas s'appliquer à ces données" NoValueToCompute = "Aucune grandeur a calculer" Ok = "Valider" Or = " ou " diff --git a/apps/shared.pt.i18n b/apps/shared.pt.i18n index 39da61127..92132d19a 100644 --- a/apps/shared.pt.i18n +++ b/apps/shared.pt.i18n @@ -7,6 +7,7 @@ Cancel = "Cancelar" ClearColumn = "Excluir coluna" ColumnOptions = "Opcoes de coluna" CopyColumnInList = "Copie a coluna em uma lista" +DataNotSuitable = "Dados não adequados" DataTab = "Dados" DefaultSetting = "Configuracoes basicas" Deg = "gra" @@ -33,7 +34,6 @@ Move = " Mover : " Next = "Seguinte" NoDataToPlot = "Nao ha dados para desenhar" NoFunctionToDelete = "Sem funcao para eliminar" -NonCompliantFormula = "A fórmula não pode ser usada para esses dados" NoValueToCompute = "Nenhuma quantidade para calcular" Ok = "Confirmar" Or = " ou " diff --git a/apps/shared/store_controller.cpp b/apps/shared/store_controller.cpp index 179c4eeeb..dc8c1c2f5 100644 --- a/apps/shared/store_controller.cpp +++ b/apps/shared/store_controller.cpp @@ -301,7 +301,7 @@ bool StoreController::privateFillColumnWithFormula(Expression * formula, Express // Compute the new value using the formula double evaluation = formula->approximateToScalar(*store); if (std::isnan(evaluation) || std::isinf(evaluation)) { - app()->displayWarning(I18n::Message::NonCompliantFormula); + app()->displayWarning(I18n::Message::DataNotSuitable); return false; } }