Merge branch 'lea-statistics' into upgrade-1.6.0

This commit is contained in:
Émilie Feral
2018-06-12 10:16:05 +02:00
132 changed files with 3623 additions and 1519 deletions

View File

@@ -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\

View File

@@ -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() :

View File

@@ -22,9 +22,6 @@ public:
void removeAll() override;
static constexpr int k_maxNumberOfFunctions = 4;
private:
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",
};
@@ -34,9 +31,6 @@ private:
const char * firstAvailableName() override {
return firstAvailableAttribute(k_functionNames, FunctionStore::name);
}
const KDColor firstAvailableColor() override {
return firstAvailableAttribute(k_defaultColors, FunctionStore::color);
}
CartesianFunction m_functions[k_maxNumberOfFunctions];
};

View File

@@ -1,50 +0,0 @@
#include "function_title_cell.h"
#include <assert.h>
using namespace Shared;
namespace Graph {
FunctionTitleCell::FunctionTitleCell(Orientation orientation, KDText::FontSize size) :
Shared::FunctionTitleCell(orientation),
m_bufferTextView(size, 0.5f, 0.5f)
{
}
void FunctionTitleCell::setHighlighted(bool highlight) {
EvenOddCell::setHighlighted(highlight);
m_bufferTextView.setHighlighted(highlight);
}
void FunctionTitleCell::setEven(bool even) {
EvenOddCell::setEven(even);
m_bufferTextView.setEven(even);
}
void FunctionTitleCell::setColor(KDColor color) {
Shared::FunctionTitleCell::setColor(color);
m_bufferTextView.setTextColor(color);
}
void FunctionTitleCell::setText(const char * title) {
m_bufferTextView.setText(title);
}
int FunctionTitleCell::numberOfSubviews() const {
return 1;
}
View * FunctionTitleCell::subviewAtIndex(int index) {
assert(index == 0);
return &m_bufferTextView;
}
void FunctionTitleCell::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-k_separatorThickness, bounds().height()-k_separatorThickness);
}
m_bufferTextView.setFrame(textFrame);
}
}

View File

@@ -40,7 +40,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)->modelAtIndex(j);
char bufferName[5] = {*function->name(),'(', m_functionStore->symbol(),')', 0};
myFunctionCell->setText(bufferName);
@@ -66,7 +66,7 @@ bool ListController::removeModelRow(ExpressionModel * model) {
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();
m_expressionCells[i]->setMargin(k_expressionMargin);
}

View File

@@ -2,10 +2,10 @@
#define GRAPH_LIST_CONTROLLER_H
#include <escher.h>
#include "../function_title_cell.h"
#include "../cartesian_function_store.h"
#include "../../shared/function_expression_cell.h"
#include "../../shared/function_list_controller.h"
#include "../cartesian_function_store.h"
#include "../../shared/buffer_function_title_cell.h"
#include "../../shared/function_expression_cell.h"
#include "../../shared/list_parameter_controller.h"
namespace Graph {
@@ -25,7 +25,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;
};

View File

@@ -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();

View File

@@ -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;

View File

@@ -5,7 +5,10 @@ app_objs += $(addprefix apps/regression/,\
app.o\
banner_view.o\
calculation_controller.o\
even_odd_double_buffer_text_cell.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\
go_to_parameter_controller.o\
graph_controller.o\
graph_view.o\
@@ -13,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/,\

View File

@@ -23,7 +23,8 @@ App::Snapshot::Snapshot() :
m_cursor(),
m_graphSelectedDotIndex(-1),
m_modelVersion(0),
m_rangeVersion(0)
m_rangeVersion(0),
m_selectedSeriesIndex(-1)
{
}
@@ -44,32 +45,12 @@ 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()),
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),

View File

@@ -24,17 +24,19 @@ public:
App * unpack(Container * container) override;
void reset() override;
Descriptor * descriptor() override;
Store * store();
Shared::CurveViewCursor * cursor();
int * graphSelectedDotIndex();
uint32_t * modelVersion();
uint32_t * rangeVersion();
Store * store() { return &m_store; }
Shared::CurveViewCursor * cursor() { return &m_cursor; }
int * graphSelectedDotIndex() { return &m_graphSelectedDotIndex; }
int * selectedSeriesIndex() { return &m_selectedSeriesIndex; }
uint32_t * modelVersion() { return &m_modelVersion; }
uint32_t * rangeVersion() { return &m_rangeVersion; }
private:
Store m_store;
Shared::CurveViewCursor m_cursor;
int m_graphSelectedDotIndex;
uint32_t m_modelVersion;
uint32_t m_rangeVersion;
int m_selectedSeriesIndex;
};
private:
App(Container * container, Snapshot * snapshot);

View File

@@ -17,9 +17,10 @@ CalculationController::CalculationController(Responder * parentResponder, Button
ButtonRowDelegate(header, nullptr),
m_titleCells{},
m_r2TitleCell(nullptr),
m_columnTitleCell(nullptr),
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);
@@ -66,14 +67,14 @@ void CalculationController::tableViewDidChangeSelection(SelectableTableView * t,
selectableTableView()->deselectTable();
app()->setFirstResponder(tabController());
} else {
t->selectCellAtLocation(previousSelectedCellX, previousSelectedCellY);
t->selectCellAtLocation(0, 1);
}
}
if (t->selectedColumn() == 1 && t->selectedRow() >= 0 && t->selectedRow() <= k_totalNumberOfDoubleBufferRows) {
EvenOddDoubleBufferTextCell * myCell = (EvenOddDoubleBufferTextCell *)t->selectedCell();
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) {
EvenOddDoubleBufferTextCell * myPreviousCell = (EvenOddDoubleBufferTextCell *)t->cellAtLocation(previousSelectedCellX, previousSelectedCellY);
if (previousSelectedCellX > 0 && previousSelectedCellY >= 0 && previousSelectedCellY <= k_totalNumberOfDoubleBufferRows) {
EvenOddDoubleBufferTextCellWithSeparator * myPreviousCell = (EvenOddDoubleBufferTextCellWithSeparator *)t->cellAtLocation(previousSelectedCellX, previousSelectedCellY);
firstSubCellSelected = myPreviousCell->firstTextSelected();
}
myCell->selectFirstText(firstSubCellSelected);
@@ -100,41 +101,51 @@ int CalculationController::numberOfRows() {
}
int CalculationController::numberOfColumns() {
return k_totalNumberOfColumns;
return 1 + m_store->numberOfNonEmptySeries();
}
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());
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;
EvenOddExpressionCellWithMargin * myCell = (EvenOddExpressionCellWithMargin *)cell;
myCell->setExpressionLayout(m_r2Layout);
return;
}
EvenOddMessageTextCell * myCell = (EvenOddMessageTextCell *)cell;
if (j == 0) {
myCell->setMessage(I18n::Message::Default);
return;
}
MarginEvenOddMessageTextCell * myCell = (MarginEvenOddMessageTextCell *)cell;
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]);
return;
}
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);
EvenOddDoubleBufferTextCell * myCell = (EvenOddDoubleBufferTextCell *)cell;
int seriesNumber = m_store->indexOfKthNonEmptySeries(i - 1);
assert(i >= 0 && seriesNumber < DoublePairStore::k_numberOfSeries);
// Coordinate and series title
if (j == 0 && i > 0) {
ColumnTitleCell * myCell = (ColumnTitleCell *)cell;
char buffer[] = {'X', static_cast<char>('1' + seriesNumber), 0};
myCell->setFirstText(buffer);
buffer[0] = 'Y';
myCell->setSecondText(buffer);
myCell->setColor(Palette::DataColor[seriesNumber]);
return;
}
// Calculation cell
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);
EvenOddDoubleBufferTextCellWithSeparator * myCell = (EvenOddDoubleBufferTextCellWithSeparator *)cell;
char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)];
PrintFloat::convertFloatToText<double>(calculation1, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits);
myCell->setFirstText(buffer);
@@ -142,17 +153,16 @@ void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int
myCell->setSecondText(buffer);
return;
}
if (i == 1 && j == 9) {
EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)cell;
if (i > 0 && j == 9) {
SeparatorEvenOddBufferTextCell * myCell = (SeparatorEvenOddBufferTextCell *)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::numberOfPairs, &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;
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);
SeparatorEvenOddBufferTextCell * myCell = (SeparatorEvenOddBufferTextCell *)cell;
char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)];
PrintFloat::convertFloatToText<double>(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits);
myCell->setText(buffer);
@@ -169,59 +179,69 @@ 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_totalNumberOfDoubleBufferRows);
if (type == k_doubleBufferCalculationCellType) {
assert(index >= 0 && index < k_numberOfDoubleCalculationCells);
assert(m_doubleCalculationCells[index] != nullptr);
return m_doubleCalculationCells[index];
}
assert(index < k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows);
assert(m_calculationCells[index] != nullptr);
if (type == k_hideableCellType) {
return m_hideableCell;
}
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) {
if (type == k_columnTitleCellType) {
return Store::k_numberOfSeries;
}
if (type == k_doubleBufferCalculationCellType) {
return k_numberOfDoubleCalculationCells;
}
if (type == k_hideableCellType) {
return 1;
}
if (type == 3) {
return k_totalNumberOfDoubleBufferRows;
}
return k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows;
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 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 {
@@ -232,34 +252,40 @@ View * CalculationController::loadView() {
SelectableTableView * tableView = new SelectableTableView(this, this, this, this);
tableView->setVerticalCellOverlap(0);
tableView->setBackgroundColor(Palette::WallScreenDark);
;
m_r2TitleCell = new EvenOddExpressionCell(1.0f, 0.5f);
m_columnTitleCell = new EvenOddDoubleBufferTextCell(tableView);
for (int i = 0; i < k_maxNumberOfDisplayableRows; i++) {
m_titleCells[i] = new EvenOddMessageTextCell(KDText::FontSize::Small);
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);
}
for (int i = 0; i < k_totalNumberOfDoubleBufferRows; i++) {
m_doubleCalculationCells[i] = new EvenOddDoubleBufferTextCell();
for (int i = 0; i < k_maxNumberOfDisplayableRows; i++) {
m_titleCells[i] = new MarginEvenOddMessageTextCell(KDText::FontSize::Small);
}
for (int i = 0; i < k_numberOfDoubleCalculationCells; i++) {
m_doubleCalculationCells[i] = new EvenOddDoubleBufferTextCellWithSeparator();
m_doubleCalculationCells[i]->setTextColor(Palette::GreyDark);
m_doubleCalculationCells[i]->setParentResponder(tableView);
}
for (int i = 0; i < k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows;i++) {
m_calculationCells[i] = new EvenOddBufferTextCell(KDText::FontSize::Small);
for (int i = 0; i < k_numberOfCalculationCells;i++) {
m_calculationCells[i] = new SeparatorEvenOddBufferTextCell(KDText::FontSize::Small);
m_calculationCells[i]->setTextColor(Palette::GreyDark);
}
m_hideableCell = new HideableEvenOddCell();
m_hideableCell->setHide(true);
return tableView;
}
void CalculationController::unloadView(View * view) {
delete m_r2TitleCell;
m_r2TitleCell = nullptr;
delete m_columnTitleCell;
m_columnTitleCell = nullptr;
for (int i = 0; i < k_totalNumberOfDoubleBufferRows; i++) {
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;
}
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;
}
@@ -267,6 +293,8 @@ void CalculationController::unloadView(View * view) {
delete m_titleCells[i];
m_titleCells[i] = nullptr;
}
delete m_hideableCell;
m_hideableCell = nullptr;
TabTableController::unloadView(view);
}

View File

@@ -3,9 +3,15 @@
#include <escher.h>
#include "store.h"
#include "even_odd_double_buffer_text_cell.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/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"
#include "../shared/separator_even_odd_buffer_text_cell.h"
#include "../shared/store_cell.h"
namespace Regression {
@@ -36,21 +42,34 @@ public:
int reusableCellCount(int type) override;
int typeAtLocation(int i, int j) override;
private:
constexpr static int k_totalNumberOfRows = 14;
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 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;
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;
constexpr static int k_totalNumberOfRows = 14;
constexpr static int k_totalNumberOfColumns = 2;
constexpr static int k_maxNumberOfDisplayableRows = 11;
constexpr static int k_totalNumberOfDoubleBufferRows = 5;
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;
Shared::MarginEvenOddMessageTextCell * m_titleCells[k_maxNumberOfDisplayableRows];
EvenOddExpressionCellWithMargin * m_r2TitleCell;
Poincare::ExpressionLayout * m_r2Layout;
EvenOddDoubleBufferTextCell * m_columnTitleCell;
EvenOddDoubleBufferTextCell * m_doubleCalculationCells[k_totalNumberOfDoubleBufferRows];
EvenOddBufferTextCell * m_calculationCells[k_totalNumberOfRows-k_totalNumberOfDoubleBufferRows];
ColumnTitleCell * m_columnTitleCells[Store::k_numberOfSeries];
EvenOddDoubleBufferTextCellWithSeparator * m_doubleCalculationCells[k_numberOfDoubleCalculationCells];
Shared::SeparatorEvenOddBufferTextCell * m_calculationCells[k_numberOfCalculationCells];
Shared::HideableEvenOddCell * m_hideableCell;
Store * m_store;
};

View File

@@ -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));
}
}

View File

@@ -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

View File

@@ -0,0 +1,12 @@
#include "even_odd_buffer_text_cell_with_margin.h"
#include <assert.h>
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);
}
}

View File

@@ -0,0 +1,17 @@
#ifndef REGRESSION_EVEN_ODD_BUFFER_TEXT_CELL_WITH_MARGIN_H
#define REGRESSION_EVEN_ODD_BUFFER_TEXT_CELL_WITH_MARGIN_H
#include <escher/even_odd_buffer_text_cell.h>
namespace Regression {
class EvenOddBufferTextCellWithMargin : public EvenOddBufferTextCell {
public:
using EvenOddBufferTextCell::EvenOddBufferTextCell;
void layoutSubviews() override;
private:
static constexpr KDCoordinate k_horizontalMargin = 2;
};
}
#endif

View File

@@ -1,108 +0,0 @@
#include "even_odd_double_buffer_text_cell.h"
#include <assert.h>
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();
}
const char * EvenOddDoubleBufferTextCell::text() const {
if (m_firstTextSelected) {
return m_firstBufferTextView.text();
} else {
return m_secondBufferTextView.text();
}
}
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;
}

View File

@@ -1,33 +0,0 @@
#ifndef REGRESSION_EVEN_ODD_DOUBLE_BUFFER_TEXT_CELL_H
#define REGRESSION_EVEN_ODD_DOUBLE_BUFFER_TEXT_CELL_H
#include <escher.h>
class EvenOddDoubleBufferTextCell : public EvenOddCell, public Responder{
public:
EvenOddDoubleBufferTextCell(Responder * parentResponder = nullptr);
const char * firstText();
const char * secondText();
void reloadCell() override;
void setHighlighted(bool highlight) override;
Responder * responder() override {
return this;
}
const char * text() const override;
void setEven(bool even) override;
bool firstTextSelected();
void selectFirstText(bool selectFirstText);
void setFirstText(const char * textContent);
void setSecondText(const char * textContent);
void setTextColor(KDColor textColor);
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
bool handleEvent(Ion::Events::Event event) override;
protected:
bool m_firstTextSelected;
EvenOddBufferTextCell m_firstBufferTextView;
EvenOddBufferTextCell m_secondBufferTextView;
};
#endif

View File

@@ -0,0 +1,120 @@
#include "even_odd_double_buffer_text_cell_with_separator.h"
#include "../shared/hideable_even_odd_editable_text_cell.h"
#include "escher/metric.h"
#include <assert.h>
namespace Regression {
EvenOddDoubleBufferTextCellWithSeparator::EvenOddDoubleBufferTextCellWithSeparator(Responder * parentResponder, float horizontalAlignment, float verticalAlignment) :
EvenOddCell(),
Responder(parentResponder),
m_firstTextSelected(true),
m_firstBufferTextView(KDText::FontSize::Small, horizontalAlignment, verticalAlignment),
m_secondBufferTextView(KDText::FontSize::Small, horizontalAlignment, verticalAlignment)
{
}
const char * EvenOddDoubleBufferTextCellWithSeparator::text() const {
if (m_firstTextSelected) {
return firstText();
} else {
return secondText();
}
}
const char * EvenOddDoubleBufferTextCellWithSeparator::firstText() const {
return m_firstBufferTextView.text();
}
const char * EvenOddDoubleBufferTextCellWithSeparator::secondText() const {
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;
}
}

View File

@@ -0,0 +1,39 @@
#ifndef REGRESSION_EVEN_ODD_DOUBLE_BUFFER_TEXT_CELL_WITH_SEPARATOR_H
#define REGRESSION_EVEN_ODD_DOUBLE_BUFFER_TEXT_CELL_WITH_SEPARATOR_H
#include <escher.h>
#include "even_odd_buffer_text_cell_with_margin.h"
namespace Regression {
class EvenOddDoubleBufferTextCellWithSeparator : public EvenOddCell, public Responder{
public:
EvenOddDoubleBufferTextCellWithSeparator(Responder * parentResponder = nullptr, float horizontalAlignment = 1.0f, float verticalAlignment = 0.5f);
const char * text() const override;
const char * firstText() const;
const char * secondText() const;
void reloadCell() override;
void setHighlighted(bool highlight) override;
Responder * responder() override {
return this;
}
void setEven(bool even) override;
bool firstTextSelected();
void selectFirstText(bool selectFirstText);
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;
bool handleEvent(Ion::Events::Event event) override;
protected:
bool m_firstTextSelected;
EvenOddBufferTextCellWithMargin m_firstBufferTextView;
EvenOddBufferTextCellWithMargin m_secondBufferTextView;
};
}
#endif

View File

@@ -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()));
}
}

View File

@@ -0,0 +1,18 @@
#ifndef REGRESSION_EVEN_ODD_EXPRESSION_CELL_WITH_MARGIN_H
#define REGRESSION_EVEN_ODD_EXPRESSION_CELL_WITH_MARGIN_H
#include <escher/even_odd_expression_cell.h>
namespace Regression {
class EvenOddExpressionCellWithMargin : public EvenOddExpressionCell {
public:
using EvenOddExpressionCell::EvenOddExpressionCell;
void layoutSubviews() override;
private:
static constexpr KDCoordinate k_rightMargin = 2;
};
}
#endif

View File

@@ -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->selectedSeriesIndex(), f);
if (m_xPrediction) {
x = m_store->yValueForXValue(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() < DBL_EPSILON && f == m_store->yIntercept()) {
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;

View File

@@ -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),
@@ -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_selectedSeriesIndex(selectedSeriesIndex)
{
m_store->setCursor(m_cursor);
}
@@ -26,10 +27,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 < DoublePairStore::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() {
@@ -63,6 +69,10 @@ bool GraphController::handleEnter() {
}
void GraphController::reloadBannerView() {
if (*m_selectedSeriesIndex < 0) {
return;
}
m_bannerView.setMessageAtIndex(I18n::Message::RegressionFormula, 3);
char buffer[k_maxNumberOfCharacters + PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)];
@@ -71,7 +81,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_selectedSeriesIndex)) {
legend = I18n::translate(I18n::Message::MeanDot);
legendLength = strlen(legend);
strlcpy(buffer+numberOfChar, legend, legendLength+1);
@@ -94,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->numberOfPairs()) {
if (*m_selectedDotIndex == m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) {
constexpr static char legX[] = {Ion::Charset::XBar, '=', 0};
legend = legX;
x = m_store->meanOfColumn(0);
x = m_store->meanOfColumn(*m_selectedSeriesIndex, 0);
}
legendLength = strlen(legend);
strlcpy(buffer, legend, legendLength+1);
@@ -112,10 +122,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_selectedSeriesIndex)) {
constexpr static char legY[] = {Ion::Charset::YBar, '=', 0};
legend = legY;
y = m_store->meanOfColumn(1);
y = m_store->meanOfColumn(*m_selectedSeriesIndex, 1);
}
legendLength = strlen(legend);
strlcpy(buffer, legend, legendLength+1);
@@ -129,7 +139,7 @@ void GraphController::reloadBannerView() {
numberOfChar = 0;
legend = " a=";
double slope = m_store->slope();
double slope = m_store->slope(*m_selectedSeriesIndex);
legendLength = strlen(legend);
strlcpy(buffer, legend, legendLength+1);
numberOfChar += legendLength;
@@ -142,7 +152,7 @@ void GraphController::reloadBannerView() {
numberOfChar = 0;
legend = " b=";
double yIntercept = m_store->yIntercept();
double yIntercept = m_store->yIntercept(*m_selectedSeriesIndex);
legendLength = strlen(legend);
strlcpy(buffer, legend, legendLength+1);
numberOfChar += legendLength;
@@ -155,7 +165,7 @@ void GraphController::reloadBannerView() {
numberOfChar = 0;
legend = " r=";
double r = m_store->correlationCoefficient();
double r = m_store->correlationCoefficient(*m_selectedSeriesIndex);
legendLength = strlen(legend);
strlcpy(buffer, legend, legendLength+1);
numberOfChar += legendLength;
@@ -168,7 +178,7 @@ void GraphController::reloadBannerView() {
numberOfChar = 0;
legend = " r2=";
double r2 = m_store->squaredCorrelationCoefficient();
double r2 = m_store->squaredCorrelationCoefficient(*m_selectedSeriesIndex);
legendLength = strlen(legend);
strlcpy(buffer, legend, legendLength+1);
numberOfChar += legendLength;
@@ -185,25 +195,26 @@ void GraphController::initRangeParameters() {
}
void GraphController::initCursorParameters() {
double x = m_store->meanOfColumn(0);
double y = m_store->meanOfColumn(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->numberOfPairs();
*m_selectedDotIndex = m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex);
}
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_selectedSeriesIndex, direction, *m_selectedDotIndex);
if (dotSelected >= 0 && dotSelected < m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) {
*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_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->numberOfPairs()) {
if (dotSelected == m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) {
*m_selectedDotIndex = dotSelected;
m_cursor->moveTo(m_store->meanOfColumn(0), m_store->meanOfColumn(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;
}
@@ -211,40 +222,86 @@ 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_selectedSeriesIndex, 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());
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_selectedSeriesIndex);
// Check the closest dot
dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), direction > 0 ? -FLT_MAX : FLT_MAX, *m_selectedSeriesIndex, *m_selectedDotIndex, &closestDotSeries);
} else {
int dotSelected = m_store->closestVerticalDot(direction, m_cursor->x());
if (dotSelected >= 0 && dotSelected <= m_store->numberOfPairs()) {
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_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_selectedSeriesIndex, *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_selectedDotIndex = dotSelected;
m_cursor->moveTo(m_store->get(0, *m_selectedDotIndex), m_store->get(1, *m_selectedDotIndex));
if (regressionDistanceY <= dotDistanceY) {
validDot = false;
} else {
validRegression = false;
}
}
}
if (!validDot && validRegression) {
// Select the regression
*m_selectedSeriesIndex = closestRegressionSeries;
selectRegressionCurve();
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_selectedSeriesIndex = closestDotSeries;
*m_selectedDotIndex = dotSelected;
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;
}
return false;
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;
}
return false;
}
uint32_t GraphController::modelVersion() {

View File

@@ -17,12 +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 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
@@ -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_selectedSeriesIndex;
};
}

View File

@@ -20,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(), (float)m_store->yIntercept()};
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);
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(0), m_store->meanOfColumn(1), Palette::Palette::YellowDark, true);
drawDot(ctx, rect, m_store->meanOfColumn(0), m_store->meanOfColumn(1), KDColorWhite);
}
char * GraphView::label(Axis axis, int index) const {

View File

@@ -0,0 +1,33 @@
#include "regression_context.h"
#include <poincare/decimal.h>
#include <assert.h>
#include <string.h>
using namespace Poincare;
using namespace Shared;
namespace Regression {
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 < DoublePairStore::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);
}
}
}

View File

@@ -0,0 +1,17 @@
#ifndef REGRESSION_REGRESSION_CONTEXT_H
#define REGRESSION_REGRESSION_CONTEXT_H
#include <poincare/context.h>
#include "../shared/store_context.h"
namespace Regression {
class RegressionContext : public Shared::StoreContext {
public:
using Shared::StoreContext::StoreContext;
const Poincare::Expression * expressionForSymbol(const Poincare::Symbol * symbol) override;
};
}
#endif

View File

@@ -8,96 +8,144 @@ 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<y ? x : y); }
Store::Store() :
InteractiveCurveViewRange(nullptr, this),
FloatPairStore()
DoublePairStore()
{
}
/* 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 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 */
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))) {
// 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];
selectedDot = index;
* - 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) && (currentDot < 0 || currentSeries != series)) {
for (int index = 0; index < numberOfPairsOfSeries(series); 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
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))) {
selectedDot = m_numberOfPairs;
}
}
return selectedDot;
}
int Store::nextDot(int direction, int dot) {
int Store::nextDot(int series, int direction, int dot) {
float nextX = INFINITY;
int selectedDot = -1;
float x = meanOfColumn(0);
if (dot >= 0 && dot < m_numberOfPairs) {
x = get(0, dot);
double meanX = meanOfColumn(series, 0);
float x = meanX;
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; 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_data[0][index] - x) < std::fabs(nextX - x) &&
if (std::fabs(m_data[series][0][index] - x) < std::fabs(nextX - x) &&
(index != dot) &&
(m_data[0][index] >= x)) {
(m_data[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_data[series][0][index] != x || (index > dot)) {
nextX = m_data[series][0][index];
selectedDot = index;
}
}
}
// Compare with the mean dot
if (std::fabs(meanOfColumn(0) - x) < std::fabs(nextX - x) &&
(m_numberOfPairs != dot) &&
(meanOfColumn(0) >= x)) {
if (meanOfColumn(0) != x || (x > dot)) {
selectedDot = m_numberOfPairs;
if (std::fabs(meanX - x) < std::fabs(nextX - x) &&
(numberOfPairsOfSeries(series) != dot) &&
(meanX >= x)) {
if (meanX != x || (numberOfPairsOfSeries(series) > dot)) {
selectedDot = numberOfPairsOfSeries(series);
}
}
} else {
// Compare with the mean dot
if (std::fabs(meanOfColumn(0) - x) < std::fabs(nextX - x) &&
(m_numberOfPairs != dot) &&
(meanOfColumn(0) <= x)) {
if (meanOfColumn(0) != x || (m_numberOfPairs < dot)) {
nextX = meanOfColumn(0);
selectedDot = m_numberOfPairs;
if (std::fabs(meanX - x) < std::fabs(nextX - x) &&
(numberOfPairsOfSeries(series) != dot) &&
(meanX <= x)) {
if ((meanX != x) || (numberOfPairsOfSeries(series) < dot)) {
nextX = meanX;
selectedDot = numberOfPairsOfSeries(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 = numberOfPairsOfSeries(series)-1; index >= 0; index--) {
if (std::fabs(m_data[series][0][index] - x) < std::fabs(nextX - x) &&
(index != dot) &&
(m_data[0][index] <= x)) {
(m_data[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_data[series][0][index] != x || (index < dot)) {
nextX = m_data[series][0][index];
selectedDot = index;
}
}
@@ -109,130 +157,158 @@ int Store::nextDot(int direction, int dot) {
/* Window */
void Store::setDefault() {
float min = minValueOfColumn(0);
float max = maxValueOfColumn(0);
float range = max - min;
setXMin(min - k_displayLeftMarginRatio*range);
setXMax(max + k_displayRightMarginRatio*range);
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;
}
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 DoublePairStore (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::numberOfPairs() {
return m_numberOfPairs;
double Store::doubleCastedNumberOfPairsOfSeries(int series) const {
return DoublePairStore::numberOfPairsOfSeries(series);
}
float Store::maxValueOfColumn(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];
}
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 max;
return maxColumn;
}
float Store::minValueOfColumn(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];
}
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 min;
return minColumn;
}
double Store::squaredValueSumOfColumn(int i) {
double Store::squaredValueSumOfColumn(int series, int i) const {
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 < numberOfPairsOfSeries(series); k++) {
result += m_data[series][i][k]*m_data[series][i][k];
}
return result;
}
double Store::columnProductSum() {
double Store::columnProductSum(int series) const {
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 < numberOfPairsOfSeries(series); k++) {
result += m_data[series][0][k]*m_data[series][1][k];
}
return result;
}
double Store::meanOfColumn(int i) {
if (m_numberOfPairs == 0) {
return 0;
}
return sumOfColumn(i)/m_numberOfPairs;
double Store::meanOfColumn(int series, int i) const {
return numberOfPairsOfSeries(series) == 0 ? 0 : sumOfColumn(series, i)/numberOfPairsOfSeries(series);
}
double Store::varianceOfColumn(int i) {
double mean = meanOfColumn(i);
return squaredValueSumOfColumn(i)/m_numberOfPairs - mean*mean;
double Store::varianceOfColumn(int series, int i) const {
double mean = meanOfColumn(series, i);
return squaredValueSumOfColumn(series, i)/numberOfPairsOfSeries(series) - mean*mean;
}
double Store::standardDeviationOfColumn(int i) {
return std::sqrt(varianceOfColumn(i));
double Store::standardDeviationOfColumn(int series, int i) const {
return std::sqrt(varianceOfColumn(series, i));
}
double Store::covariance() {
return columnProductSum()/m_numberOfPairs - meanOfColumn(0)*meanOfColumn(1);
double Store::covariance(int series) const {
return columnProductSum(series)/numberOfPairsOfSeries(series) - meanOfColumn(series, 0)*meanOfColumn(series, 1);
}
double Store::slope() {
return covariance()/varianceOfColumn(0);
double Store::slope(int series) const {
return covariance(series)/varianceOfColumn(series, 0);
}
double Store::yIntercept() {
return meanOfColumn(1) - slope()*meanOfColumn(0);
double Store::yIntercept(int series) const {
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) const {
return slope(series)*x+yIntercept(series);
}
double Store::xValueForYValue(double y) {
if (std::fabs(slope()) < DBL_EPSILON) {
return NAN;
}
return (y - yIntercept())/slope();
double Store::xValueForYValue(int series, double y) const {
return std::fabs(slope(series)) < DBL_EPSILON ? NAN : (y - yIntercept(series))/slope(series);
}
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);
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() {
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);
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(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];
}
if (m_data[1][k] > max) {
max = m_data[1][k];
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;
}

View File

@@ -2,38 +2,52 @@
#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 <float.h>
}
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();
// 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 x above the regression curve if direction > 0,
* below otherwise*/
int closestVerticalDot(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);
/* Return the closest dot to abscissa x above the regression curve if
* direction > 0, below otherwise */
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);
// Window
void setDefault() override;
// Series
bool isEmpty() const;
int numberOfNonEmptySeries() const;
bool seriesIsEmpty(int series) const;
int indexOfKthNonEmptySeries(int k) const;
// Calculation
double 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 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;
@@ -41,12 +55,12 @@ private:
constexpr static float k_displayLeftMarginRatio = 0.05f;
InteractiveCurveViewRangeDelegate::Range computeYRange(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) const;
float minValueOfColumn(int series, int i) const;
};
typedef double (Store::*ArgCalculPointer)(int);
typedef double (Store::*CalculPointer)();
typedef double (Store::*ArgCalculPointer)(int, int) const;
typedef double (Store::*CalculPointer)(int) const;
typedef void (Store::*RangeMethodPointer)();
}

View File

@@ -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"
@@ -14,19 +15,25 @@ namespace Regression {
StoreController::StoreController(Responder * parentResponder, Store * store, ButtonRowController * header) :
Shared::StoreController(parentResponder, store, header),
m_titleCells{}
m_titleCells{},
m_regressionContext(store)
{
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);
}
StoreController::~StoreController() {
for (int i = 0; i < 2; i++) {
if (m_titleLayout[i]) {
delete m_titleLayout[i];
m_titleLayout[i] = nullptr;
}
}
StoreContext * StoreController::storeContext() {
m_regressionContext.setParentContext(const_cast<AppsContainer *>(static_cast<const AppsContainer *>(app()->container()))->globalContext());
return &m_regressionContext;
}
void StoreController::setFormulaLabel() {
int series = selectedColumn() / Store::k_numberOfColumnsPerSeries;
int isXColumn = selectedColumn() % Store::k_numberOfColumnsPerSeries == 0;
char text[] = {isXColumn ? 'X' : 'Y', static_cast<char>('1' + series), '=', 0};
static_cast<ContentView *>(view())->formulaInputView()->setBufferText(text);
}
bool StoreController::fillColumnWithFormula(Expression * formula) {
return privateFillColumnWithFormula(formula, Symbol::isRegressionSymbol);
}
void StoreController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
@@ -34,8 +41,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<Shared::StoreTitleCell *>(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<char>('1' + seriesIndex), 0};
mytitleCell->setText(name);
}
HighlightCell * StoreController::titleCells(int index) {
@@ -45,7 +57,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();
}

View File

@@ -3,25 +3,25 @@
#include <escher.h>
#include "store.h"
#include "regression_context.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;
Shared::StoreContext * storeContext() override;
void setFormulaLabel() override;
bool fillColumnWithFormula(Poincare::Expression * formula) override;
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[2];
Shared::StoreTitleCell * m_titleCells[k_numberOfTitleCells];
RegressionContext m_regressionContext;
};
}

View File

@@ -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() {

View File

@@ -33,12 +33,6 @@ private:
Sequence * emptyModel() override;
Sequence * nullModel() override;
void setModelAtIndex(Shared::ExpressionModel * f, int i) override;
static constexpr KDColor k_defaultColors[MaxNumberOfSequences] = {
Palette::Red, Palette::Blue//, Palette::YellowDark
};
const KDColor firstAvailableColor() override {
return firstAvailableAttribute(k_defaultColors, FunctionStore::color);
}
Sequence m_sequences[MaxNumberOfSequences];
};

View File

@@ -7,13 +7,16 @@ 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"
Deviation = "Varianz"
DisplayValues = "Werte anzeigen"
Empty = "Leer"
ExitExamMode1 = "Wollen Sie den Testmodus "
ExitExamMode2 = "verlassen?"
FillWithFormula = "Mit einer Formel füllen"
ForbiddenValue = "Verbotener Wert"
FunctionColumn = "0(0) Spalte"
FunctionOptions = "Optionen Funktion"
@@ -27,10 +30,12 @@ Initialization = "Initialisierung"
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"
@@ -38,7 +43,10 @@ Plot = "Graphen zeichnen"
Rad = "rad"
RoundAbscissa = "Ganzzahl"
Sci = "sci/"
StatTab = "Stats"
StandardDeviation = "Standardabweichung"
Step = "Schrittwert"
SquareSum = "Quadratsumme"
SyntaxError = "Syntaxfehler"
ToZoom = "Zoom: "
Trigonometric = "Trigonometrisch"

View File

@@ -7,13 +7,16 @@ 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"
Deviation = "Variance"
DisplayValues = "Display values"
Empty = "Empty"
ExitExamMode1 = "Exit the exam "
ExitExamMode2 = "mode?"
FillWithFormula = "Fill with a formula"
ForbiddenValue = "Forbidden value"
FunctionColumn = "0(0) column"
FunctionOptions = "Function options"
@@ -27,10 +30,12 @@ Initialization = "Preadjustment"
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"
@@ -38,7 +43,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"

View File

@@ -7,13 +7,16 @@ 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"
Deviation = "Varianza"
DisplayValues = "Visualizar los valores"
Empty = "Vacio"
ExitExamMode1 = "Salir del modo "
ExitExamMode2 = "examen ?"
FillWithFormula = "Rellenar con una fórmula"
ForbiddenValue = "Valor prohibido"
FunctionColumn = "Columna 0(0)"
FunctionOptions = "Opciones de la funcion"
@@ -27,10 +30,12 @@ Initialization = "Inicializacion"
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"
@@ -38,6 +43,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 : "

View File

@@ -7,13 +7,16 @@ 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"
Deviation = "Variance"
DisplayValues = "Afficher les valeurs"
Empty = "Vide"
ExitExamMode1 = "Voulez-vous sortir "
ExitExamMode2 = "du mode examen ?"
FillWithFormula = "Remplir avec une formule"
ForbiddenValue = "Valeur interdite"
FunctionColumn = "Colonne 0(0)"
FunctionOptions = "Options de la fonction"
@@ -27,10 +30,12 @@ Initialization = "Initialisation"
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"
@@ -38,6 +43,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 : "

View File

@@ -7,13 +7,16 @@ 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"
Deviation = "Variancia"
DisplayValues = "Exibir os valores"
Empty = "Vacuo"
ExitExamMode1 = "Voce quer sair do modo de "
ExitExamMode2 = "exame ?"
FillWithFormula = "Preencher com uma fórmula"
ForbiddenValue = "Valor proibida"
FunctionColumn = "Coluna 0(0)"
FunctionOptions = "Opcoes de funcao"
@@ -27,10 +30,12 @@ Initialization = "Inicializacao"
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"
@@ -38,6 +43,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 : "

View File

@@ -1,16 +1,19 @@
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\
curve_view_cursor.o\
curve_view_range.o\
double_pair_store.o\
editable_cell_table_view_controller.o\
expression_field_delegate_app.o\
expression_layout_field_delegate.o\
expression_model.o\
expression_model_list_controller.o\
expression_model_store.o\
float_pair_store.o\
float_parameter_controller.o\
function.o\
function_app.o\
@@ -24,6 +27,8 @@ app_objs += $(addprefix apps/shared/,\
function_store.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\
interactive_curve_view_range.o\
@@ -32,6 +37,7 @@ app_objs += $(addprefix apps/shared/,\
interval_parameter_controller.o\
language_controller.o\
list_parameter_controller.o\
margin_even_odd_message_text_cell.o\
memoized_curve_view_range.o\
message_view.o\
ok_view.o\
@@ -41,10 +47,14 @@ app_objs += $(addprefix apps/shared/,\
round_cursor_view.o\
scrollable_exact_approximate_expressions_cell.o\
scrollable_exact_approximate_expressions_view.o\
separator_even_odd_buffer_text_cell.o\
simple_interactive_curve_view_controller.o\
expression_layout_field_delegate.o\
store_cell.o\
store_context.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\

View File

@@ -0,0 +1,52 @@
#include "buffer_function_title_cell.h"
#include <assert.h>
namespace Shared {
BufferFunctionTitleCell::BufferFunctionTitleCell(Orientation orientation, KDText::FontSize size) :
FunctionTitleCell(orientation),
m_bufferTextView(size, 0.5f, 0.5f)
{
}
void BufferFunctionTitleCell::setHighlighted(bool highlight) {
EvenOddCell::setHighlighted(highlight);
m_bufferTextView.setHighlighted(highlight);
}
void BufferFunctionTitleCell::setEven(bool even) {
EvenOddCell::setEven(even);
m_bufferTextView.setEven(even);
}
void BufferFunctionTitleCell::setColor(KDColor color) {
FunctionTitleCell::setColor(color);
m_bufferTextView.setTextColor(color);
}
void BufferFunctionTitleCell::setText(const char * title) {
m_bufferTextView.setText(title);
}
int BufferFunctionTitleCell::numberOfSubviews() const {
return 1;
}
View * BufferFunctionTitleCell::subviewAtIndex(int index) {
assert(index == 0);
return &m_bufferTextView;
}
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-k_separatorThickness, bounds().height()-k_separatorThickness);
}
return textFrame;
}
}

View File

@@ -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;
@@ -15,10 +15,13 @@ public:
const char * text() const override {
return m_bufferTextView.text();
}
private:
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;
};

View File

@@ -0,0 +1,63 @@
#include "buffer_text_view_with_text_field.h"
#include <escher/palette.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::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, false);
markRectAsDirty(bounds());
}
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(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);
}
}

View File

@@ -0,0 +1,33 @@
#ifndef SHARED_BUFFER_TEXT_VIEW_WITH_TEXT_FIELD_H
#define SHARED_BUFFER_TEXT_VIEW_WITH_TEXT_FIELD_H
#include <escher.h>
namespace Shared {
class BufferTextViewWithTextField : public View, public Responder {
public:
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 drawRect(KDContext * ctx, KDRect rect) const override;
// Responder
void didBecomeFirstResponder() override;
private:
constexpr static int k_textFieldBufferSize = TextField::maxBufferSize();
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];
};
}
#endif

View File

@@ -10,15 +10,17 @@ 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),
m_curveViewRange(curveViewRange),
m_cursorView(cursorView),
m_okView(okView),
m_forceOkDisplay(false),
m_mainViewSelected(false),
m_drawnRangeVersion(0)
m_drawnRangeVersion(0),
m_displayBanner(displayBanner)
{
}
@@ -27,7 +29,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);
@@ -143,10 +145,12 @@ 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, 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
@@ -154,18 +158,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);
KDRect graduation(std::round(floatToPixel(Axis::Horizontal, x)), verticalCoordinate -(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);
graduation = KDRect(horizontalCoordinate-(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, verticalCoordinate + k_labelMargin);
if (axis == Axis::Vertical) {
origin = KDPoint(horizontalCoordinate + k_labelMargin, std::round(floatToPixel(Axis::Vertical, x)) - textSize.height()/2);
}
if (-step < x && x < step && shiftOrigin) {
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);
}
}
ctx->fillRect(graduation, KDColorBlack);
i++;
@@ -514,7 +522,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 +538,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 +547,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);
}
@@ -548,19 +556,19 @@ 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) {
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;
}
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 +580,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;

View File

@@ -17,25 +17,33 @@ 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; }
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;
@@ -52,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) 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:
@@ -84,8 +92,10 @@ private:
CurveViewRange * m_curveViewRange;
View * m_cursorView;
View * m_okView;
bool m_forceOkDisplay;
bool m_mainViewSelected;
uint32_t m_drawnRangeVersion;
bool m_displayBanner;
};
}

View File

@@ -0,0 +1,95 @@
#include "double_pair_store.h"
#include <cmath>
#include <assert.h>
#include <stddef.h>
#include <ion.h>
namespace Shared {
void DoublePairStore::set(double f, int series, int i, int j) {
assert(series >= 0 && series < k_numberOfSeries);
if (j >= k_maxNumberOfPairs) {
return;
}
m_data[series][i][j] = f;
if (j >= m_numberOfPairs[series]) {
int otherI = i == 0 ? 1 : 0;
m_data[series][otherI][j] = defaultValue(series, otherI, j);
m_numberOfPairs[series]++;
}
}
int DoublePairStore::numberOfPairs() const {
int result = 0;
for (int i = 0; i < k_numberOfSeries; i++) {
result += m_numberOfPairs[i];
}
return result;
}
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];
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[series][0][m_numberOfPairs[series]] = 0;
m_data[series][1][m_numberOfPairs[series]] = 0;
}
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++) {
m_data[series][0][k] = 0;
m_data[series][1][k] = 0;
}
m_numberOfPairs[series] = 0;
}
void DoublePairStore::deleteAllPairs() {
for (int i = 0; i < k_numberOfSeries; i ++) {
deleteAllPairsOfSeries(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++) {
m_data[series][i][k] = defaultValue(series, i, k);
}
}
double DoublePairStore::sumOfColumn(int series, int i) const {
assert(series >= 0 && series < k_numberOfSeries);
assert(i == 0 || i == 1);
double result = 0;
for (int k = 0; k < m_numberOfPairs[series]; k++) {
result += m_data[series][i][k];
}
return result;
}
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
* when removing them. */
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));
}
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];
} else {
return 0.0;
}
}
}

View File

@@ -0,0 +1,56 @@
#ifndef SHARED_DOUBLE_PAIR_STORE_H
#define SHARED_DOUBLE_PAIR_STORE_H
#include <kandinsky/color.h>
#include <escher/palette.h>
#include <stdint.h>
#include <assert.h>
namespace Shared {
class DoublePairStore {
public:
constexpr static int k_numberOfSeries = 3;
constexpr static int k_numberOfColumnsPerSeries = 2;
constexpr static int k_maxNumberOfPairs = 100;
DoublePairStore() :
m_data{},
m_numberOfPairs{}
{}
// Delete the implicit copy constructor: the object is heavy
DoublePairStore(const DoublePairStore&) = delete;
double get(int series, int i, int j) const {
assert(j < m_numberOfPairs[series]);
return m_data[series][i][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];
}
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;
uint32_t storeChecksum() const;
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) const;
double m_data[k_numberOfSeries][k_numberOfColumnsPerSeries][k_maxNumberOfPairs];
private:
int m_numberOfPairs[k_numberOfSeries];
};
}
#endif

View File

@@ -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;

View File

@@ -1,89 +0,0 @@
#include "float_pair_store.h"
#include <cmath>
#include <assert.h>
#include <stddef.h>
#include <ion.h>
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) {
if (j >= k_maxNumberOfPairs) {
return;
}
m_data[i][j] = f;
if (j >= m_numberOfPairs) {
int otherI = i == 0 ? 1 : 0;
m_data[otherI][j] = defaultValue(otherI, j);
m_numberOfPairs++;
}
}
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];
}
/* 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;
}
void FloatPairStore::deleteAllPairs() {
/* 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;
}
m_numberOfPairs = 0;
}
void FloatPairStore::resetColumn(int i) {
for (int k = 0; k < m_numberOfPairs; k++) {
m_data[i][k] = defaultValue(i, k);
}
}
double FloatPairStore::sumOfColumn(int i) {
double result = 0;
for (int k = 0; k < m_numberOfPairs; k++) {
result += m_data[i][k];
}
return result;
}
uint32_t FloatPairStore::storeChecksum() {
/* 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
* when removing them. */
size_t dataLengthInBytes = 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) {
if(i == 0 && j > 1) {
return 2*m_data[i][j-1]-m_data[i][j-2];
} else {
return 0.0;
}
}
}

View File

@@ -1,30 +0,0 @@
#ifndef SHARED_FLOAT_PAIR_STORE_H
#define SHARED_FLOAT_PAIR_STORE_H
#include <stdint.h>
namespace Shared {
class FloatPairStore {
public:
FloatPairStore();
// 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);
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];
};
}
#endif

View File

@@ -25,9 +25,11 @@ protected:
static KDColor const color(Shared::Function * f) { return f->color(); }
template<typename T> using AttributeGetter = T (*)(Function * f);
template<typename T> T firstAvailableAttribute(T attributes[], AttributeGetter<T> attribute);
const KDColor firstAvailableColor() {
return firstAvailableAttribute(Palette::DataColor, FunctionStore::color);
}
private:
virtual const char * firstAvailableName() = 0;
virtual const KDColor firstAvailableColor() = 0;
};
}

22
apps/shared/hideable.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef APPS_SHARED_HIDEABLE_H
#define APPS_SHARED_HIDEABLE_H
#include <escher/palette.h>
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

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,21 @@
#ifndef APPS_SHARED_HIDEABLE_EVEN_ODD_CELL_H
#define APPS_SHARED_HIDEABLE_EVEN_ODD_CELL_H
#include <escher/even_odd_cell.h>
#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

View File

@@ -0,0 +1,20 @@
#include "hideable_even_odd_editable_text_cell.h"
namespace Shared {
KDColor HideableEvenOddEditableTextCell::backgroundColor() const {
if (hidden()) {
return hideColor();
}
return EvenOddEditableTextCell::backgroundColor();
}
void HideableEvenOddEditableTextCell::setHide(bool hide) {
if (hidden() != hide) {
Hideable::setHide(hide);
editableTextCell()->textField()->setBackgroundColor(backgroundColor());
reloadCell();
}
}
}

View File

@@ -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 <escher/even_odd_editable_text_cell.h>
#include <escher/palette.h>
#include "hideable.h"
namespace Shared {
class HideableEvenOddEditableTextCell : public EvenOddEditableTextCell, public Hideable {
public:
HideableEvenOddEditableTextCell(Responder * parentResponder = nullptr, TextFieldDelegate * delegate = nullptr, char * draftTextBuffer = nullptr) :
EvenOddEditableTextCell(parentResponder, delegate, draftTextBuffer),
Hideable()
{}
KDColor backgroundColor() const override;
void setHide(bool hide) override;
};
}
#endif

View File

@@ -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()));
}
}

View File

@@ -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 <escher/even_odd_message_text_cell.h>
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

View File

@@ -0,0 +1,20 @@
#include "separator_even_odd_buffer_text_cell.h"
#include "hideable_even_odd_editable_text_cell.h"
#include <escher/metric.h>
namespace Shared {
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 SeparatorEvenOddBufferTextCell::layoutSubviews() {
KDRect boundsThis = bounds();
m_bufferTextView.setFrame(KDRect(boundsThis.left() + Metric::TableSeparatorThickness, boundsThis.top(), boundsThis.width() - Metric::TableSeparatorThickness - k_rightMargin, boundsThis.height()));
}
}

View File

@@ -0,0 +1,19 @@
#ifndef APPS_SHARED_SEPARATOR_EVEN_ODD_CELL_H
#define APPS_SHARED_SEPARATOR_EVEN_ODD_CELL_H
#include <escher/even_odd_buffer_text_cell.h>
namespace Shared {
class SeparatorEvenOddBufferTextCell : public EvenOddBufferTextCell {
public:
using EvenOddBufferTextCell::EvenOddBufferTextCell;
void drawRect(KDContext * ctx, KDRect rect) const override;
void layoutSubviews() override;
private:
constexpr static KDCoordinate k_rightMargin = 2;
};
}
#endif

View File

@@ -0,0 +1,28 @@
#include "store_cell.h"
#include <escher/metric.h>
namespace Shared {
void StoreCell::setSeparatorLeft(bool separator) {
if (m_separatorLeft != separator) {
m_separatorLeft = separator;
reloadCell();
}
}
void StoreCell::drawRect(KDContext * ctx, KDRect rect) const {
HideableEvenOddEditableTextCell::drawRect(ctx, rect);
// Draw the separator
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.left() + Metric::TableSeparatorThickness, boundsThis.top(), boundsThis.width() - Metric::TableSeparatorThickness - k_rightMargin, boundsThis.height()));
}
}

24
apps/shared/store_cell.h Normal file
View File

@@ -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_separatorLeft(false)
{}
void setSeparatorLeft(bool separator);
void drawRect(KDContext * ctx, KDRect rect) const override;
void layoutSubviews() override;
private:
static constexpr KDCoordinate k_rightMargin = 2;
bool m_separatorLeft;
};
}
#endif

View File

@@ -0,0 +1,14 @@
#include "store_context.h"
#include <poincare/decimal.h>
#include <assert.h>
#include <string.h>
using namespace Poincare;
namespace Shared {
void StoreContext::setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) {
m_parentContext->setExpressionForSymbolName(expression, symbol, context);
}
}

View File

@@ -0,0 +1,28 @@
#ifndef SHARED_STORE_CONTEXT_H
#define SHARED_STORE_CONTEXT_H
#include <poincare/context.h>
#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

View File

@@ -1,28 +1,123 @@
#include "store_controller.h"
#include "../apps_container.h"
#include "../constant.h"
#include <escher/metric.h>
#include <assert.h>
using namespace Poincare;
namespace Shared {
StoreController::StoreController(Responder * parentResponder, FloatPairStore * store, ButtonRowController * header) :
StoreController::ContentView::ContentView(DoublePairStore * store, Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, TextFieldDelegate * textFieldDelegate) :
View(),
Responder(parentResponder),
m_dataView(store, this, dataSource, selectionDataSource),
m_formulaInputView(this, textFieldDelegate),
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_displayFormulaInputView ? static_cast<Responder *>(&m_formulaInputView) : static_cast<Responder *>(&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_displayFormulaInputView ? k_formulaInputHeight : 0));
m_dataView.setFrame(dataViewFrame);
m_formulaInputView.setFrame(formulaFrame());
}
KDRect StoreController::ContentView::formulaFrame() const {
return KDRect(0, bounds().height() - k_formulaInputHeight, bounds().width(), m_displayFormulaInputView ? k_formulaInputHeight : 0);
}
StoreController::StoreController(Responder * parentResponder, DoublePairStore * store, ButtonRowController * header) :
EditableCellTableViewController(parentResponder),
ButtonRowDelegate(header, nullptr),
m_editableCells{},
m_store(store),
m_storeParameterController(this, store)
m_storeParameterController(this, store, this)
{
}
const char * StoreController::title() {
return I18n::translate(I18n::Message::DataTab);
void StoreController::displayFormulaInput() {
setFormulaLabel();
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
Expression * expression = Expression::parse(textField->text());
if (expression == nullptr) {
app()->displayWarning(I18n::Message::SyntaxError);
return false;
}
contentView()->displayFormulaInput(false);
if (fillColumnWithFormula(expression)) {
app()->setFirstResponder(contentView());
}
delete expression;
return true;
}
AppsContainer * appsContainer = ((TextFieldDelegateApp *)app())->container();
Context * globalContext = appsContainer->globalContext();
double floatBody = Expression::approximateToScalar<double>(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;
}
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 2;
};
return DoublePairStore::k_numberOfColumnsPerSeries * DoublePairStore::k_numberOfSeries;
}
KDCoordinate StoreController::columnWidth(int i) {
return k_cellWidth;
@@ -39,10 +134,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,55 +147,84 @@ HighlightCell * StoreController::reusableCell(int index, int type) {
}
int StoreController::reusableCellCount(int type) {
if (type == 0) {
return k_numberOfTitleCells;
}
return k_maxNumberOfEditableCells;
return type == k_titleCellType ? k_numberOfTitleCells : 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) {
// Handle the separator
if (cellAtLocationIsEditable(i, j)) {
bool shouldHaveLeftSeparator = i % DoublePairStore::k_numberOfColumnsPerSeries == 0;
static_cast<StoreCell *>(cell)->setSeparatorLeft(shouldHaveLeftSeparator);
}
// Handle empty cells
if (j > 0 && j > m_store->numberOfPairsOfSeries(seriesAtColumn(i)) && j < numberOfRows()) {
StoreCell * myCell = static_cast<StoreCell *>(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<StoreCell *>(cell)->setHide(false);
}
willDisplayCellAtLocationWithDisplayMode(cell, i, j, PrintFloat::Mode::Decimal);
}
void StoreController::didBecomeFirstResponder() {
if (selectedRow() < 0) {
selectCellAtLocation(0, 0);
}
EditableCellTableViewController::didBecomeFirstResponder();
const char * StoreController::title() {
return I18n::translate(I18n::Message::DataTab);
}
bool StoreController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::Up) {
if (event == Ion::Events::Up && !static_cast<ContentView *>(view())->formulaInputView()->textField()->isEditing()) {
selectableTableView()->deselectTable();
assert(selectedRow() == -1);
app()->setFirstResponder(tabController());
return true;
}
assert(selectedColumn() >= 0 && selectedColumn() < numberOfColumns());
int series = seriesAtColumn(selectedColumn());
if ((event == Ion::Events::OK || event == Ion::Events::EXE) && selectedRow() == 0) {
m_storeParameterController.selectXColumn(selectedColumn() == 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() == numberOfRows()-1) {
if (selectedRow() == 0 || selectedRow() > m_store->numberOfPairsOfSeries(selectedColumn()/DoublePairStore::k_numberOfColumnsPerSeries)) {
return false;
}
m_store->deletePairAtIndex(selectedRow()-1);
m_store->deletePairOfSeriesAtIndex(series, selectedRow()-1);
selectableTableView()->reloadData();
return true;
}
return false;
}
void StoreController::didBecomeFirstResponder() {
if (selectedRow() < 0 || selectedColumn() < 0) {
selectCellAtLocation(0, 0);
}
EditableCellTableViewController::didBecomeFirstResponder();
app()->setFirstResponder(contentView());
}
Responder * StoreController::tabController() const {
return (parentResponder()->parentResponder()->parentResponder());
}
SelectableTableView * StoreController::selectableTableView() {
return contentView()->dataView();
}
bool StoreController::cellAtLocationIsEditable(int columnIndex, int rowIndex) {
if (rowIndex > 0) {
return true;
@@ -109,28 +233,32 @@ 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, seriesAtColumn(columnIndex), columnIndex%DoublePairStore::k_numberOfColumnsPerSeries, rowIndex-1);
return true;
}
double StoreController::dataAtLocation(int columnIndex, int rowIndex) {
return m_store->get(columnIndex, rowIndex-1);
return m_store->get(seriesAtColumn(columnIndex), columnIndex%DoublePairStore::k_numberOfColumnsPerSeries, rowIndex-1);
}
int StoreController::numberOfElements() {
return m_store->numberOfPairs();
int result = 0;
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() {
SelectableTableView * tableView = (SelectableTableView*)EditableCellTableViewController::loadView();
ContentView * contentView = new ContentView(m_store, this, this, this, this);
for (int i = 0; i < k_maxNumberOfEditableCells; i++) {
m_editableCells[i] = new EvenOddEditableTextCell(tableView, this, m_draftTextBuffer);
m_editableCells[i] = new StoreCell(contentView->dataView(), this, m_draftTextBuffer);
}
return tableView;
return contentView;
}
void StoreController::unloadView(View * view) {
@@ -138,7 +266,59 @@ void StoreController::unloadView(View * view) {
delete m_editableCells[i];
m_editableCells[i] = nullptr;
}
EditableCellTableViewController::unloadView(view);
delete view;
}
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};
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();
// 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<double>(*store);
if (std::isnan(evaluation) || std::isinf(evaluation)) {
app()->displayWarning(I18n::Message::DataNotSuitable);
return false;
}
}
// Fill in the table with the formula values
for (int j = 0; j < numberOfValuesToCompute; j++) {
store->setSeriesPairIndex(j);
double evaluation = formula->approximateToScalar<double>(*store);
setDataAtLocation(evaluation, currentColumn, j + 1);
}
selectableTableView()->reloadData();
return true;
}
bool StoreController::cellShouldBeTransparent(int i, int j) {
int seriesIndex = i/DoublePairStore::k_numberOfColumnsPerSeries;
return j > 1 + m_store->numberOfPairsOfSeries(seriesIndex);
}
}

View File

@@ -2,16 +2,31 @@
#define SHARED_STORE_CONTROLLER_H
#include <escher.h>
#include "float_pair_store.h"
#include "store_parameter_controller.h"
#include "buffer_text_view_with_text_field.h"
#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"
namespace Shared {
class StoreController : public EditableCellTableViewController, public ButtonRowDelegate {
public:
StoreController(Responder * parentResponder, FloatPairStore * store, ButtonRowController * header);
const char * title() override;
StoreController(Responder * parentResponder, DoublePairStore * store, ButtonRowController * header);
virtual StoreContext * storeContext() = 0;
void displayFormulaInput();
virtual void setFormulaLabel() = 0;
virtual bool 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;
// TableViewDataSource
int numberOfColumns() override;
KDCoordinate columnWidth(int i) override;
KDCoordinate cumulatedWidthFromIndex(int i) override;
@@ -20,13 +35,46 @@ 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 = Ion::Display::Width/2 - Metric::CommonRightMargin/2 - Metric::CommonLeftMargin/2;
constexpr static int k_maxNumberOfEditableCells = 22;
constexpr static int k_numberOfTitleCells = 2;
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 * 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(DoublePairStore * 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;
static constexpr KDCoordinate k_formulaInputHeight = 31;
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_displayFormulaInputView;
};
Responder * tabController() const override;
SelectableTableView * selectableTableView() override;
View * loadView() override;
void unloadView(View * view) override;
bool cellAtLocationIsEditable(int columnIndex, int rowIndex) override;
@@ -36,9 +84,14 @@ protected:
int maxNumberOfElements() const override;
virtual HighlightCell * titleCells(int index) = 0;
char m_draftTextBuffer[TextField::maxBufferSize()];
EvenOddEditableTextCell * m_editableCells[k_maxNumberOfEditableCells];
FloatPairStore * m_store;
int seriesAtColumn(int column) const { return column / DoublePairStore::k_numberOfColumnsPerSeries; }
bool privateFillColumnWithFormula(Poincare::Expression * formula, Poincare::Expression::isVariableTest isVariable);
StoreCell * m_editableCells[k_maxNumberOfEditableCells];
DoublePairStore * m_store;
StoreParameterController m_storeParameterController;
private:
bool cellShouldBeTransparent(int i, int j);
ContentView * contentView() { return static_cast<ContentView *>(view()); }
};
}

View File

@@ -1,33 +1,29 @@
#include "store_parameter_controller.h"
#include "store_controller.h"
#include <assert.h>
namespace Shared {
StoreParameterController::StoreParameterController(Responder * parentResponder, FloatPairStore * store) :
StoreParameterController::StoreParameterController(Responder * parentResponder, DoublePairStore * 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_xColumnSelected(true)
m_storeController(storeController),
m_xColumnSelected(true),
m_series(0)
{
}
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);
@@ -39,20 +35,28 @@ bool StoreParameterController::handleEvent(Ion::Events::Event event) {
case 0:
{
if (m_xColumnSelected) {
m_store->deleteAllPairs();
m_store->deleteAllPairsOfSeries(m_series);
} else {
m_store->resetColumn(!m_xColumnSelected);
m_store->resetColumn(m_series, !m_xColumnSelected);
}
StackViewController * stack = ((StackViewController *)parentResponder());
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);
@@ -62,23 +66,11 @@ 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);
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];
}
int StoreParameterController::reusableCellCount() {
return k_totalNumberOfCell;
}
KDCoordinate StoreParameterController::cellHeight() {
return Metric::ParameterCellHeight;
}
}

View File

@@ -2,35 +2,41 @@
#define SHARED_STORE_PARAM_CONTROLLER_H
#include <escher.h>
#include "float_pair_store.h"
#include "double_pair_store.h"
#include "../i18n.h"
namespace Shared {
class StoreController;
class StoreParameterController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource {
public:
StoreParameterController(Responder * parentResponder, FloatPairStore * store);
void selectXColumn(bool xColumnSelected);
View * view() override;
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; }
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;
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;
DoublePairStore * m_store;
StoreController * m_storeController;
bool m_xColumnSelected;
int m_series;
};
}

View File

@@ -0,0 +1,42 @@
#include "store_selectable_table_view.h"
namespace Shared {
StoreSelectableTableView::StoreSelectableTableView(DoublePairStore * 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/DoublePairStore::k_numberOfColumnsPerSeries;
int numberOfPairsOfCurrentSeries = m_store->numberOfPairsOfSeries(seriesIndex);
if (j > 1 + numberOfPairsOfCurrentSeries) {
return selectCellAtLocation(i, 1 + numberOfPairsOfCurrentSeries);
}
return selectCellAtLocation(i, j);
}
}

View File

@@ -0,0 +1,20 @@
#ifndef APPS_SHARED_STORE_SELECTABLE_TABLE_VIEW_H
#define APPS_SHARED_STORE_SELECTABLE_TABLE_VIEW_H
#include <escher/selectable_table_view.h>
#include "double_pair_store.h"
namespace Shared {
class StoreSelectableTableView : public SelectableTableView {
public:
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);
DoublePairStore * m_store;
};
}
#endif

View File

@@ -0,0 +1,30 @@
#include "store_title_cell.h"
#include "hideable_even_odd_editable_text_cell.h"
#include "escher/metric.h"
namespace Shared {
void StoreTitleCell::setSeparatorLeft(bool separator) {
if (m_separatorLeft != separator) {
m_separatorLeft = separator;
reloadCell();
}
}
void StoreTitleCell::drawRect(KDContext * ctx, KDRect rect) const {
BufferFunctionTitleCell::drawRect(ctx, rect);
// Draw the separator
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());
}
}
void StoreTitleCell::layoutSubviews() {
KDRect textFrame = bufferTextViewFrame();
bufferTextView()->setFrame(KDRect(textFrame.left() + Metric::TableSeparatorThickness, textFrame.top(), textFrame.width() - Metric::TableSeparatorThickness, textFrame.height()));
}
}

View File

@@ -0,0 +1,23 @@
#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() :
BufferFunctionTitleCell(Orientation::HorizontalIndicator, KDText::FontSize::Small),
m_separatorLeft(false)
{}
void setSeparatorLeft(bool separator);
void drawRect(KDContext * ctx, KDRect rect) const override;
void layoutSubviews() override;
private:
bool m_separatorLeft;
};
}
#endif

View File

@@ -25,7 +25,7 @@ void TabTableController::willExitResponderChain(Responder * nextFirstResponder)
}
SelectableTableView * TabTableController::selectableTableView() {
return (SelectableTableView *)view();
return static_cast<SelectableTableView *>(view());
}
View * TabTableController::loadView() {

View File

@@ -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;

View File

@@ -97,7 +97,7 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) {
if (definedModelAtIndex(i)->standardForm(context) == nullptr) {
return Error::EquationUndefined;
}
numberOfVariables = definedModelAtIndex(i)->standardForm(context)->getVariables(m_variables);
numberOfVariables = definedModelAtIndex(i)->standardForm(context)->getVariables(Symbol::isVariableSymbol, m_variables);
if (numberOfVariables < 0) {
return Error::TooManyVariables;
}

View File

@@ -3,15 +3,22 @@ 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\
box_view.o\
calculation_controller.o\
calculation_selectable_table_view.o\
histogram_banner_view.o\
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\
statistics_context.o\
store.o\
store_controller.o\
)

View File

@@ -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)
{
}
@@ -47,39 +49,15 @@ 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()),
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),

View File

@@ -4,9 +4,9 @@
#include <escher.h>
#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 {
@@ -25,18 +25,22 @@ 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 * 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;
};
private:

View File

@@ -2,11 +2,13 @@ StatsApp = "Statistiken"
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"
Size = " Haufigkeit"
Frequency = "Relative"
@@ -16,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"

View File

@@ -2,11 +2,13 @@ StatsApp = "Statistics"
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 "
Size = " Size"
Frequency = "Frequency"
@@ -16,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"

View File

@@ -2,11 +2,13 @@ StatsApp = "Estadistica"
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"
Size = " Frecuencia"
Frequency = "Relativa"
@@ -16,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 muestral s"
Deviation = "Varianza"
SampleStandardDeviationS = "Desviacion tipica s"
InterquartileRange = "Rango intercuartilo"
SquareSum = "Suma cuadrados"

View File

@@ -2,11 +2,13 @@ StatsApp = "Statistiques"
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 "
Size = " Effectif"
Frequency = "Frequence"
@@ -16,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"

View File

@@ -2,11 +2,13 @@ StatsApp = "Estatistica"
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"
Size = " Frequencia"
Frequency = "Relativa"
@@ -16,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"

View File

@@ -0,0 +1,19 @@
#include "box_axis_view.h"
#include <assert.h>
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];
}
}

View File

@@ -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

View File

@@ -3,25 +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)
{
}
int BoxBannerView::numberOfSubviews() const {
return 2;
}
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 {
if (index == 0) {
return (MessageTextView *)&m_calculationName;
}
return nullptr;
return index == 1 ? (MessageTextView *)&m_calculationName : nullptr;
}
}

View File

@@ -11,9 +11,10 @@ class BoxBannerView : public Shared::BannerView {
public:
BoxBannerView();
private:
int numberOfSubviews() const override;
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;
};

View File

@@ -6,86 +6,57 @@ using namespace Poincare;
namespace Statistics {
BoxController::BoxController(Responder * parentResponder, ButtonRowController * header, Store * store, BoxView::Quantile * selectedQuantile) :
ViewController(parentResponder),
BoxController::BoxController(Responder * parentResponder, ButtonRowController * header, Store * store, BoxView::Quantile * selectedQuantile, int * selectedSeriesIndex) :
MultipleDataViewController(parentResponder, store, (int *)(selectedQuantile), selectedSeriesIndex),
ButtonRowDelegate(header, nullptr),
m_boxBannerView(),
m_view(store, &m_boxBannerView, selectedQuantile),
m_store(store)
m_view(this, store, selectedQuantile)
{
}
bool BoxController::moveSelectionHorizontally(int deltaIndex) {
int selectedQuantile = (int)m_view.dataViewAtIndex(selectedSeriesIndex())->selectedQuantile();
int nextSelectedQuantile = selectedQuantile + deltaIndex;
if (m_view.dataViewAtIndex(selectedSeriesIndex())->selectQuantile(nextSelectedQuantile)) {
reloadBannerView();
return true;
}
return false;
}
const char * BoxController::title() {
return I18n::translate(I18n::Message::BoxTab);
}
View * BoxController::view() {
return &m_view;
}
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();
}
bool BoxController::isEmpty() const {
if (m_store->sumOfColumn(1) == 0) {
return true;
}
return false;
}
I18n::Message BoxController::emptyMessage() {
return I18n::Message::NoDataToPlot;
}
Responder * BoxController::defaultController() {
return tabController();
}
Responder * BoxController::tabController() const {
return (parentResponder()->parentResponder()->parentResponder());
}
void BoxController::reloadBannerView() {
if (selectedSeriesIndex() < 0) {
return;
}
int selectedQuantile = (int)m_view.dataViewAtIndex(selectedSeriesIndex())->selectedQuantile();
// Set series name
char seriesChar = '0' + selectedSeriesIndex() + 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_boxBannerView.setMessageAtIndex(calculationName[(int)m_view.selectedQuantile()], 0);
char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)];
m_view.editableBannerView()->setMessageAtIndex(calculationName[selectedQuantile], 1);
// Set calculation result
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[(int)m_view.selectedQuantile()])();
PrintFloat::convertFloatToText<double>(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits);
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();
}
double calculation = (m_store->*calculationMethods[selectedQuantile])(selectedSeriesIndex());
int numberOfChar = PrintFloat::convertFloatToText<double>(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits);
buffer[numberOfChar++] = ' ';
buffer[numberOfChar] = 0;
m_view.editableBannerView()->setLegendAtIndex(buffer, 2);
}
}

View File

@@ -3,29 +3,24 @@
#include <escher.h>
#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);
BoxController(Responder * parentResponder, ButtonRowController * header, Store * store, BoxView::Quantile * selectedQuantile, int * selectedSeriesIndex);
MultipleDataView * multipleDataView() override { return &m_view; }
bool moveSelectionHorizontally(int deltaIndex) override;
// ViewController
const char * title() override;
View * view() override;
bool handleEvent(Ion::Events::Event event) override;
void didBecomeFirstResponder() override;
bool isEmpty() const override;
I18n::Message emptyMessage() override;
Responder * defaultController() override;
void viewWillAppear() override;
void willExitResponderChain(Responder * nextFirstResponder) override;
private:
Responder * tabController() const;
void reloadBannerView();
BoxBannerView m_boxBannerView;
BoxView m_view;
Store * m_store;
Responder * tabController() const override;
void reloadBannerView() override;
MultipleBoxesView m_view;
};
}

View File

@@ -8,27 +8,19 @@ 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);
}
float BoxRange::yMin() {
return -k_displayBottomMarginRatio;
}
float BoxRange::yMax() {
return 1.0f+k_displayTopMarginRatio;
}
float BoxRange::xGridUnit() {
return computeGridUnit(Axis::X, xMin(), xMax());
}

View File

@@ -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;

View File

@@ -1,4 +1,5 @@
#include "box_view.h"
#include "box_controller.h"
#include <assert.h>
#include <cmath>
@@ -6,79 +7,98 @@ using namespace Shared;
namespace Statistics {
BoxView::BoxView(Store * store, BannerView * bannerView, Quantile * selectedQuantile) :
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_labels{},
m_selectedQuantile(selectedQuantile)
m_series(series),
m_selectedQuantile(selectedQuantile),
m_selectedHistogramColor(color),
m_selectedHistogramLightColor(lightColor)
{
}
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 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;
}
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();
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);
markRectAsDirty(dirtyRect);
}
void BoxView::drawRect(KDContext * ctx, KDRect rect) const {
ctx->fillRect(rect, KDColorWhite);
drawAxes(ctx, rect, Axis::Horizontal);
drawLabels(ctx, rect, Axis::Horizontal, false);
float lowBound = 0.35f;
float upBound = 0.65f;
// 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 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);
double calculations[5] = {m_store->minValue(), m_store->firstQuartile(), m_store->median(), m_store->thirdQuartile(), m_store->maxValue()};
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);
double maxVal = m_store->maxValue(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));
KDCoordinate thirdQuartilePixels = std::round(floatToPixel(Axis::Horizontal, thirdQuart));
ctx->fillRect(KDRect(firstQuartilePixels, lowBoundPixel, thirdQuartilePixels - firstQuartilePixels+2,
upBoundPixel-lowBoundPixel), boxColor);
// Draw the horizontal lines linking the box to the extreme bounds
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);
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);
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);
}
}
char * BoxView::label(Axis axis, int index) const {
if (axis == Axis::Vertical) {
return nullptr;
}
return (char *)m_labels[index];
KDCoordinate BoxView::boxLowerBoundPixel() const {
return bounds().height() / 2 - k_boxHeight / 2;
}
KDCoordinate BoxView::boxUpperBoundPixel() const {
return bounds().height() / 2 + k_boxHeight / 2;
}
}

View File

@@ -9,6 +9,8 @@
namespace Statistics {
class BoxController;
class BoxView : public Shared::CurveView {
public:
enum class Quantile : int {
@@ -19,17 +21,30 @@ public:
ThirdQuartile = 3,
Max = 4
};
BoxView(Store * store, Shared::BannerView * bannerView, Quantile * selectedQuantile);
void reload() override;
Quantile selectedQuantile();
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; }
void reloadQuantile();
// CurveView
void reload() override;
// View
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
char * label(Axis axis, int index) const override;
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; }
Store * m_store;
BoxController * m_boxController;
BoxRange m_boxRange;
char m_labels[k_maxNumberOfXLabels][Poincare::PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits)];
int m_series;
Quantile * m_selectedQuantile;
KDColor m_selectedHistogramColor;
KDColor m_selectedHistogramLightColor;
};
}

View File

@@ -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 <poincare.h>
#include <assert.h>
@@ -13,16 +14,145 @@ 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)
{
}
// AlternateEmptyViewDelegate
bool CalculationController::isEmpty() const {
return m_store->isEmpty();
}
I18n::Message CalculationController::emptyMessage() {
return I18n::Message::NoValueToCompute;
}
Responder * CalculationController::defaultController() {
return tabController();
}
// TableViewDataSource
int CalculationController::numberOfColumns() {
return 1 + m_store->numberOfNonEmptySeries();
}
void CalculationController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
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<char>('1'+seriesNumber), '/', 'N', static_cast<char>('1'+seriesNumber), 0};
StoreTitleCell * storeTitleCell = static_cast<StoreTitleCell *>(cell);
storeTitleCell->setText(titleBuffer);
storeTitleCell->setColor(DoublePairStore::colorOfSeriesAtIndex(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};
MarginEvenOddMessageTextCell * calcTitleCell = static_cast<MarginEvenOddMessageTextCell *>(cell);
calcTitleCell->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<EvenOddBufferTextCell *>(cell);
char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)];
PrintFloat::convertFloatToText<double>(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits);
calculationCell->setText(buffer);
}
KDCoordinate CalculationController::columnWidth(int i) {
return i == 0 ? k_calculationTitleCellWidth : k_calculationCellWidth;
}
KDCoordinate CalculationController::cumulatedHeightFromIndex(int j) {
return j*rowHeight(0);
}
int CalculationController::indexFromCumulatedHeight(KDCoordinate offsetY) {
return (offsetY-1) / rowHeight(0);
}
HighlightCell * CalculationController::reusableCell(int index, int type) {
assert(index >= 0 && index < reusableCellCount(type));
if (type == k_hideableCellType) {
return m_hideableCell;
}
if (type == k_calculationTitleCellType) {
return static_cast<HighlightCell *>(m_calculationTitleCells[index]);
}
if (type == k_seriesTitleCellType) {
return static_cast<HighlightCell *>(m_seriesTitleCells[index]);
}
assert(type == k_calculationCellType);
return static_cast<HighlightCell *>(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) {
assert(i >= 0 && i < numberOfColumns());
assert(j >= 0 && j < numberOfRows());
if (i == 0 && j == 0) {
return k_hideableCellType;
}
if (i == 0) {
return k_calculationTitleCellType;
}
if (j == 0) {
return k_seriesTitleCellType;
}
return k_calculationCellType;
}
// 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();
@@ -34,114 +164,58 @@ bool CalculationController::handleEvent(Ion::Events::Event event) {
void CalculationController::didBecomeFirstResponder() {
if (selectedRow() == -1) {
selectCellAtLocation(0, 0);
selectCellAtLocation(0, 1);
} else {
selectCellAtLocation(selectedColumn(), selectedRow());
}
TabTableController::didBecomeFirstResponder();
}
bool CalculationController::isEmpty() const {
if (m_store->sumOfColumn(1) == 0) {
return true;
}
return false;
}
I18n::Message CalculationController::emptyMessage() {
return I18n::Message::NoValueToCompute;
}
Responder * CalculationController::defaultController() {
return tabController();
}
int CalculationController::numberOfRows() {
return k_totalNumberOfRows;
}
int CalculationController::numberOfColumns() {
return 2;
}
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])();
EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)cell;
char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)];
PrintFloat::convertFloatToText<double>(calculation, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits);
myCell->setText(buffer);
}
}
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;
}
KDCoordinate CalculationController::cumulatedHeightFromIndex(int j) {
return j*rowHeight(0);
}
int CalculationController::indexFromCumulatedHeight(KDCoordinate offsetY) {
return (offsetY-1) / rowHeight(0);
}
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;
}
int CalculationController::typeAtLocation(int i, int j) {
return i;
}
// Private
Responder * CalculationController::tabController() const {
return (parentResponder()->parentResponder()->parentResponder());
}
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);
m_calculationCells[i] = new EvenOddBufferTextCell(KDText::FontSize::Small);
for (int i = 0; i < k_numberOfSeriesTitleCells; i++) {
m_seriesTitleCells[i] = new StoreTitleCell();
m_seriesTitleCells[i]->setSeparatorLeft(true);
}
for (int i = 0; i < k_numberOfCalculationTitleCells; i++) {
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 SeparatorEvenOddBufferTextCell(KDText::FontSize::Small);
m_calculationCells[i]->setTextColor(Palette::GreyDark);
}
return TabTableController::loadView();
m_hideableCell = new HideableEvenOddCell();
m_hideableCell->setHide(true);
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;
}
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);
}

View File

@@ -3,6 +3,10 @@
#include <escher.h>
#include "store.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"
namespace Statistics {
@@ -11,34 +15,55 @@ 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;
// TableViewDataSource
int numberOfRows() override { return k_totalNumberOfRows; }
int numberOfColumns() override;
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;
HighlightCell * reusableCell(int index, int type) override;
int reusableCellCount(int type) override;
int typeAtLocation(int i, int j) override;
// ViewController
const char * title() override;
// 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 = 84;
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;
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];
Shared::MarginEvenOddMessageTextCell * m_calculationTitleCells[k_numberOfCalculationTitleCells];
Shared::SeparatorEvenOddBufferTextCell * m_calculationCells[k_numberOfCalculationCells];
Shared::HideableEvenOddCell * m_hideableCell;
Store * m_store;
};

View File

@@ -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);
}
}

View File

@@ -0,0 +1,16 @@
#ifndef APPS_STATISTICS_CALCULATION_SELECTABLE_TABLE_VIEW_H
#define APPS_STATISTICS_CALCULATION_SELECTABLE_TABLE_VIEW_H
#include <escher/selectable_table_view.h>
namespace Statistics {
class CalculationSelectableTableView : public SelectableTableView {
public:
using SelectableTableView::SelectableTableView;
bool handleEvent(Ion::Events::Event event) override;
};
}
#endif

View File

@@ -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];
}

Some files were not shown because too many files have changed in this diff Show More