mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-20 14:20:39 +01:00
Merge branch 'lea-statistics' into upgrade-1.6.0
This commit is contained in:
@@ -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\
|
||||
|
||||
@@ -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() :
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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/,\
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
24
apps/regression/column_title_cell.cpp
Normal file
24
apps/regression/column_title_cell.cpp
Normal 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));
|
||||
}
|
||||
|
||||
}
|
||||
25
apps/regression/column_title_cell.h
Normal file
25
apps/regression/column_title_cell.h
Normal 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
|
||||
12
apps/regression/even_odd_buffer_text_cell_with_margin.cpp
Normal file
12
apps/regression/even_odd_buffer_text_cell_with_margin.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
17
apps/regression/even_odd_buffer_text_cell_with_margin.h
Normal file
17
apps/regression/even_odd_buffer_text_cell_with_margin.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
10
apps/regression/even_odd_expression_cell_with_margin.cpp
Normal file
10
apps/regression/even_odd_expression_cell_with_margin.cpp
Normal 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()));
|
||||
}
|
||||
|
||||
}
|
||||
18
apps/regression/even_odd_expression_cell_with_margin.h
Normal file
18
apps/regression/even_odd_expression_cell_with_margin.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
33
apps/regression/regression_context.cpp
Normal file
33
apps/regression/regression_context.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
17
apps/regression/regression_context.h
Normal file
17
apps/regression/regression_context.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)();
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 : "
|
||||
|
||||
@@ -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 : "
|
||||
|
||||
@@ -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 : "
|
||||
|
||||
@@ -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\
|
||||
|
||||
52
apps/shared/buffer_function_title_cell.cpp
Normal file
52
apps/shared/buffer_function_title_cell.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
63
apps/shared/buffer_text_view_with_text_field.cpp
Normal file
63
apps/shared/buffer_text_view_with_text_field.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
33
apps/shared/buffer_text_view_with_text_field.h
Normal file
33
apps/shared/buffer_text_view_with_text_field.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
95
apps/shared/double_pair_store.cpp
Normal file
95
apps/shared/double_pair_store.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
56
apps/shared/double_pair_store.h
Normal file
56
apps/shared/double_pair_store.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
22
apps/shared/hideable.h
Normal 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
|
||||
19
apps/shared/hideable_even_odd_cell.cpp
Normal file
19
apps/shared/hideable_even_odd_cell.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
21
apps/shared/hideable_even_odd_cell.h
Normal file
21
apps/shared/hideable_even_odd_cell.h
Normal 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
|
||||
20
apps/shared/hideable_even_odd_editable_text_cell.cpp
Normal file
20
apps/shared/hideable_even_odd_editable_text_cell.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
22
apps/shared/hideable_even_odd_editable_text_cell.h
Normal file
22
apps/shared/hideable_even_odd_editable_text_cell.h
Normal 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
|
||||
10
apps/shared/margin_even_odd_message_text_cell.cpp
Normal file
10
apps/shared/margin_even_odd_message_text_cell.cpp
Normal 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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
20
apps/shared/margin_even_odd_message_text_cell.h
Normal file
20
apps/shared/margin_even_odd_message_text_cell.h
Normal 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
|
||||
20
apps/shared/separator_even_odd_buffer_text_cell.cpp
Normal file
20
apps/shared/separator_even_odd_buffer_text_cell.cpp
Normal 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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
19
apps/shared/separator_even_odd_buffer_text_cell.h
Normal file
19
apps/shared/separator_even_odd_buffer_text_cell.h
Normal 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
|
||||
28
apps/shared/store_cell.cpp
Normal file
28
apps/shared/store_cell.cpp
Normal 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
24
apps/shared/store_cell.h
Normal 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
|
||||
14
apps/shared/store_context.cpp
Normal file
14
apps/shared/store_context.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
28
apps/shared/store_context.h
Normal file
28
apps/shared/store_context.h
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
42
apps/shared/store_selectable_table_view.cpp
Normal file
42
apps/shared/store_selectable_table_view.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
20
apps/shared/store_selectable_table_view.h
Normal file
20
apps/shared/store_selectable_table_view.h
Normal 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
|
||||
30
apps/shared/store_title_cell.cpp
Normal file
30
apps/shared/store_title_cell.cpp
Normal 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()));
|
||||
}
|
||||
|
||||
}
|
||||
23
apps/shared/store_title_cell.h
Normal file
23
apps/shared/store_title_cell.h
Normal 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
|
||||
@@ -25,7 +25,7 @@ void TabTableController::willExitResponderChain(Responder * nextFirstResponder)
|
||||
}
|
||||
|
||||
SelectableTableView * TabTableController::selectableTableView() {
|
||||
return (SelectableTableView *)view();
|
||||
return static_cast<SelectableTableView *>(view());
|
||||
}
|
||||
|
||||
View * TabTableController::loadView() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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\
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
19
apps/statistics/box_axis_view.cpp
Normal file
19
apps/statistics/box_axis_view.cpp
Normal 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];
|
||||
}
|
||||
|
||||
}
|
||||
29
apps/statistics/box_axis_view.h
Normal file
29
apps/statistics/box_axis_view.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
15
apps/statistics/calculation_selectable_table_view.cpp
Normal file
15
apps/statistics/calculation_selectable_table_view.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
16
apps/statistics/calculation_selectable_table_view.h
Normal file
16
apps/statistics/calculation_selectable_table_view.h
Normal 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
|
||||
@@ -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
Reference in New Issue
Block a user