Merge branch 'solver' into upgrade-1.6.0

This commit is contained in:
Émilie Feral
2018-06-12 09:47:41 +02:00
230 changed files with 4554 additions and 1876 deletions

View File

@@ -9,9 +9,7 @@ app_objs += $(addprefix apps/calculation/,\
expression_field.o\
history_view_cell.o\
history_controller.o\
output_expressions_view.o\
scrollable_expression_view.o\
scrollable_output_expressions_view.o\
selectable_table_view.o\
)

View File

@@ -199,16 +199,7 @@ Calculation::EqualSign Calculation::exactAndApproximateDisplayedOutputsAreEqual(
if (m_equalSign != EqualSign::Unknown) {
return m_equalSign;
}
char buffer[k_printedExpressionSize];
approximateOutput(context)->writeTextInBuffer(buffer, k_printedExpressionSize, Preferences::sharedPreferences()->numberOfSignificantDigits());
/* Warning: we cannot use directly the m_approximateOutputText but we have to
* re-serialize the approximateOutput because the number of stored
* significative numbers and the number of displayed significative numbers
* are not identical. (For example, 0.000025 might be displayed "0.00003"
* which requires in an approximative equal) */
Expression * approximateOutput = Expression::ParseAndSimplify(buffer, *context);
m_equalSign = approximateOutput->isIdenticalTo(exactOutput(context)) ? EqualSign::Equal : EqualSign::Approximation;
delete approximateOutput;
m_equalSign = exactOutput(context)->isEqualToItsApproximationLayout(approximateOutput(context), k_printedExpressionSize, Preferences::sharedPreferences()->numberOfSignificantDigits(), *context) ? EqualSign::Equal : EqualSign::Approximation;
return m_equalSign;
}

View File

@@ -95,15 +95,15 @@ Expression * CalculationStore::ansExpression(Context * context) {
return &defaultExpression;
}
Calculation * lastCalculation = calculationAtIndex(numberOfCalculations()-1);
/* Special case: the exact output is a Store expression.
* Remark: Store expressions are always reduced but if the simplification
* process was interrupted, the exact output is identical to the input.
* To avoid turning 'ans->A' in '2->A->A' (which cannot be parsed), ans
* is replaced by the approximation output in that special case.*/
bool exactOuptutInvolvesStore = lastCalculation->exactOutput(context)->recursivelyMatches([](const Expression * e, Context & context) {
return e->type() == Expression::Type::Store;
/* Special case: the exact output is a Store/Equal expression.
* Store/Equal expression must be final root of an expression.
* To avoid turning 'ans->A' in '2->A->A' (or 2->A=A) which cannot be parsed),
* ans is replaced by the approximation output in when any Store or Equal
* expression appears.*/
bool exactOuptutInvolvesStoreEqual = lastCalculation->exactOutput(context)->recursivelyMatches([](const Expression * e, Context & context) {
return e->type() == Expression::Type::Store || e->type() == Expression::Type::Equal;
}, *context);
if (lastCalculation->input()->isApproximate(*context) || exactOuptutInvolvesStore) {
if (lastCalculation->input()->isApproximate(*context) || exactOuptutInvolvesStoreEqual) {
return lastCalculation->approximateOutput(context);
}
return lastCalculation->exactOutput(context);

View File

@@ -3,6 +3,8 @@
#include "../apps_container.h"
#include <assert.h>
using namespace Shared;
namespace Calculation {
HistoryController::HistoryController(Responder * parentResponder, CalculationStore * calculationStore) :
@@ -47,8 +49,8 @@ bool HistoryController::handleEvent(Ion::Events::Event event) {
if (subviewType == HistoryViewCell::SubviewType::Input) {
editController->insertTextBody(calculation->inputText());
} else {
OutputExpressionsView::SubviewType outputSubviewType = selectedCell->outputView()->selectedSubviewType();
if (outputSubviewType == OutputExpressionsView::SubviewType::ExactOutput) {
ScrollableExactApproximateExpressionsView::SubviewType outputSubviewType = selectedCell->outputView()->selectedSubviewType();
if (outputSubviewType == ScrollableExactApproximateExpressionsView::SubviewType::ExactOutput) {
editController->insertTextBody(calculation->exactOutputText());
} else {
editController->insertTextBody(calculation->approximateOutputText());
@@ -94,23 +96,6 @@ bool HistoryController::handleEvent(Ion::Events::Event event) {
app()->setFirstResponder(editController);
return true;
}
if (event == Ion::Events::Copy) {
HistoryViewCell * selectedCell = (HistoryViewCell *)selectableTableView()->selectedCell();
HistoryViewCell::SubviewType subviewType = selectedCell->selectedSubviewType();
int focusRow = selectedRow();
Calculation * calculation = m_calculationStore->calculationAtIndex(focusRow);
if (subviewType == HistoryViewCell::SubviewType::Input) {
Clipboard::sharedClipboard()->store(calculation->inputText());
} else {
OutputExpressionsView::SubviewType outputSubviewType = selectedCell->outputView()->selectedSubviewType();
if (outputSubviewType == OutputExpressionsView::SubviewType::ExactOutput) {
Clipboard::sharedClipboard()->store(calculation->exactOutputText());
} else {
Clipboard::sharedClipboard()->store(calculation->approximateOutputText());
}
}
return true;
}
return false;
}

View File

@@ -33,32 +33,40 @@ HistoryViewCell::~HistoryViewCell() {
}
}
OutputExpressionsView * HistoryViewCell::outputView() {
return m_scrollableOutputView.outputView();
Shared::ScrollableExactApproximateExpressionsView * HistoryViewCell::outputView() {
return &m_scrollableOutputView;
}
void HistoryViewCell::setEven(bool even) {
EvenOddCell::setEven(even);
m_inputView.setBackgroundColor(backgroundColor());
m_scrollableOutputView.outputView()->setEven(even);
m_scrollableOutputView.evenOddCell()->setEven(even);
}
void HistoryViewCell::setHighlighted(bool highlight) {
m_highlighted = highlight;
m_inputView.setBackgroundColor(backgroundColor());
m_scrollableOutputView.outputView()->setHighlighted(false);
m_scrollableOutputView.evenOddCell()->setHighlighted(false);
if (isHighlighted()) {
if (m_selectedSubviewType == SubviewType::Input) {
m_inputView.setBackgroundColor(Palette::Select);
} else {
m_scrollableOutputView.outputView()->setHighlighted(true);
m_scrollableOutputView.evenOddCell()->setHighlighted(true);
}
}
reloadScroll();
}
Poincare::ExpressionLayout * HistoryViewCell::expressionLayout() const {
if (m_selectedSubviewType == SubviewType::Input) {
return m_inputLayout;
} else {
return m_scrollableOutputView.expressionLayout();
}
}
void HistoryViewCell::reloadCell() {
m_scrollableOutputView.outputView()->reloadCell();
m_scrollableOutputView.evenOddCell()->reloadCell();
layoutSubviews();
reloadScroll();
}
@@ -69,7 +77,7 @@ void HistoryViewCell::reloadScroll() {
}
KDColor HistoryViewCell::backgroundColor() const {
KDColor background = m_even ? Palette::WallScreen : KDColorWhite;
KDColor background = m_even ? KDColorWhite : Palette::WallScreen;
return background;
}
@@ -107,9 +115,9 @@ void HistoryViewCell::setCalculation(Calculation * calculation) {
m_inputLayout = calculation->createInputLayout();
m_inputView.setExpressionLayout(m_inputLayout);
App * calculationApp = (App *)app();
/* Both output expressions have to be updated at the same time. The
* outputView points to deleted layouts and a call to
* outputView()->layoutSubviews() is going to fail. */
/* Both output expressions have to be updated at the same time. Otherwise,
* when updating one layout, if the second one still points to a deleted
* layout, calling to layoutSubviews() would fail. */
if (m_exactOutputLayout) {
delete m_exactOutputLayout;
m_exactOutputLayout = nullptr;
@@ -122,9 +130,9 @@ void HistoryViewCell::setCalculation(Calculation * calculation) {
}
m_approximateOutputLayout = calculation->createApproximateOutputLayout(calculationApp->localContext());
Poincare::ExpressionLayout * outputExpressions[2] = {m_approximateOutputLayout, m_exactOutputLayout};
m_scrollableOutputView.outputView()->setExpressions(outputExpressions);
m_scrollableOutputView.setExpressions(outputExpressions);
I18n::Message equalMessage = calculation->exactAndApproximateDisplayedOutputsAreEqual(calculationApp->localContext()) == Calculation::EqualSign::Equal ? I18n::Message::Equal : I18n::Message::AlmostEqual;
m_scrollableOutputView.outputView()->setEqualMessage(equalMessage);
m_scrollableOutputView.setEqualMessage(equalMessage);
}
void HistoryViewCell::didBecomeFirstResponder() {

View File

@@ -4,7 +4,7 @@
#include <escher.h>
#include "calculation.h"
#include "scrollable_expression_view.h"
#include "scrollable_output_expressions_view.h"
#include "../shared/scrollable_exact_approximate_expressions_view.h"
namespace Calculation {
@@ -23,6 +23,7 @@ public:
Responder * responder() override {
return this;
}
Poincare::ExpressionLayout * expressionLayout() const override;
KDColor backgroundColor() const override;
void setCalculation(Calculation * calculation);
int numberOfSubviews() const override;
@@ -33,14 +34,14 @@ public:
constexpr static KDCoordinate k_digitVerticalMargin = 5;
SubviewType selectedSubviewType();
void setSelectedSubviewType(HistoryViewCell::SubviewType subviewType);
OutputExpressionsView * outputView();
Shared::ScrollableExactApproximateExpressionsView * outputView();
private:
constexpr static KDCoordinate k_resultWidth = 80;
Poincare::ExpressionLayout * m_inputLayout;
Poincare::ExpressionLayout * m_exactOutputLayout;
Poincare::ExpressionLayout * m_approximateOutputLayout;
ScrollableExpressionView m_inputView;
ScrollableOutputExpressionsView m_scrollableOutputView;
Shared::ScrollableExactApproximateExpressionsView m_scrollableOutputView;
SubviewType m_selectedSubviewType;
};

View File

@@ -1,41 +0,0 @@
#ifndef CALCULATION_OUTPUT_EXPRESSIONS_VIEW_H
#define CALCULATION_OUTPUT_EXPRESSIONS_VIEW_H
#include <escher.h>
namespace Calculation {
class OutputExpressionsView : public EvenOddCell, public Responder {
public:
enum class SubviewType {
ExactOutput,
ApproximativeOutput
};
OutputExpressionsView(Responder * parentResponder);
void setExpressions(Poincare::ExpressionLayout ** expressionsLayout);
void setEqualMessage(I18n::Message equalSignMessage);
KDColor backgroundColor() const override;
void setHighlighted(bool highlight) override;
Responder * responder() override {
return this;
}
void reloadCell() override;
KDSize minimalSizeForOptimalDisplay() const override;
void didBecomeFirstResponder() override;
bool handleEvent(Ion::Events::Event event) override;
SubviewType selectedSubviewType();
void setSelectedSubviewType(SubviewType subviewType);
private:
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
constexpr static KDCoordinate k_digitHorizontalMargin = 10;
ExpressionView m_approximateExpressionView;
MessageTextView m_approximateSign;
ExpressionView m_exactExpressionView;
SubviewType m_selectedSubviewType;
};
}
#endif

View File

@@ -1,29 +0,0 @@
#include "scrollable_output_expressions_view.h"
#include <assert.h>
using namespace Poincare;
namespace Calculation {
ScrollableOutputExpressionsView::ScrollableOutputExpressionsView(Responder * parentResponder) :
ScrollableView(parentResponder, &m_outputView, this),
m_outputView(this)
{
}
OutputExpressionsView * ScrollableOutputExpressionsView::outputView() {
return &m_outputView;
}
void ScrollableOutputExpressionsView::didBecomeFirstResponder() {
app()->setFirstResponder(&m_outputView);
}
KDSize ScrollableOutputExpressionsView::minimalSizeForOptimalDisplay() const {
return m_outputView.minimalSizeForOptimalDisplay();
}
KDPoint ScrollableOutputExpressionsView::manualScrollingOffset() const {
return m_manualScrollingOffset;
}
}

View File

@@ -1,22 +0,0 @@
#ifndef CALCULATION_SCROLLABLE_OUTPUT_EXPRESSIONS_VIEW_H
#define CALCULATION_SCROLLABLE_OUTPUT_EXPRESSIONS_VIEW_H
#include <escher.h>
#include "output_expressions_view.h"
namespace Calculation {
class ScrollableOutputExpressionsView : public ScrollableView, public ScrollViewDataSource {
public:
ScrollableOutputExpressionsView(Responder * parentResponder);
OutputExpressionsView * outputView();
void didBecomeFirstResponder() override;
KDSize minimalSizeForOptimalDisplay() const override;
KDPoint manualScrollingOffset() const;
private:
OutputExpressionsView m_outputView;
};
}
#endif

View File

@@ -153,8 +153,6 @@ bool ConsoleController::handleEvent(Ion::Events::Event event) {
app()->setFirstResponder(&m_editCell);
return m_editCell.insertText(text);
}
} else if (event == Ion::Events::Copy) {
return copyCurrentLineToClipboard();
} else if (event == Ion::Events::Clear) {
m_selectableTableView.deselectTable();
m_consoleStore.clear();
@@ -392,13 +390,4 @@ StackViewController * ConsoleController::stackViewController() {
return static_cast<StackViewController *>(parentResponder());
}
bool ConsoleController::copyCurrentLineToClipboard() {
int row = m_selectableTableView.selectedRow();
if (row < m_consoleStore.numberOfLines()) {
Clipboard::sharedClipboard()->store(m_consoleStore.lineAtIndex(row).text());
return true;
}
return false;
}
}

View File

@@ -88,7 +88,6 @@ private:
void emptyOutputAccumulationBuffer();
size_t firstNewLineCharIndex(const char * text, size_t length);
StackViewController * stackViewController();
bool copyCurrentLineToClipboard();
int m_rowHeight;
bool m_importScriptsWhenViewAppears;
ConsoleStore m_consoleStore;

View File

@@ -28,7 +28,7 @@ public:
// Edit cell
void setEditing(bool isEditing, bool reinitDraftBuffer = false);
const char * text() const { return m_textField.text(); }
const char * text() const override { return m_textField.text(); }
void setText(const char * text);
bool insertText(const char * text);
void setPrompt(const char * prompt);

View File

@@ -24,7 +24,9 @@ public:
Responder * responder() override {
return this;
}
const char * text() const override {
return m_line.text();
}
/* View */
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;

View File

@@ -12,7 +12,7 @@ MenuController::MenuController(Responder * parentResponder, ScriptStore * script
ViewController(parentResponder),
ButtonRowDelegate(nullptr, footer),
m_scriptStore(scriptStore),
m_addNewScriptCell(I18n::Message::AddScript),
m_addNewScriptCell(),
m_consoleButton(this, I18n::Message::Console, Invocation([](void * context, void * sender) {
MenuController * menu = (MenuController *)context;
if (menu->consoleController()->loadPythonEnvironment()) {
@@ -29,6 +29,7 @@ MenuController::MenuController(Responder * parentResponder, ScriptStore * script
{
m_selectableTableView.setMargins(0);
m_selectableTableView.setShowsIndicators(false);
m_addNewScriptCell.setMessage(I18n::Message::AddScript);
for (int i = 0; i < k_maxNumberOfDisplayableScriptCells; i++) {
m_scriptCells[i].setParentResponder(&m_selectableTableView);
m_scriptCells[i].editableTextCell()->textField()->setDelegate(this);

View File

@@ -2,7 +2,6 @@
#define CODE_MENU_CONTROLLER_H
#include <escher.h>
#include <apps/shared/new_function_cell.h>
#include "console_controller.h"
#include "editor_controller.h"
#include "script_parameter_controller.h"
@@ -99,7 +98,7 @@ private:
* constructor of an EvenOddEditableTextCell. */
char m_draftTextBuffer[TextField::maxBufferSize()];
EvenOddCellWithEllipsis m_scriptParameterCells[k_maxNumberOfDisplayableScriptCells];
Shared::NewFunctionCell m_addNewScriptCell;
EvenOddMessageTextCell m_addNewScriptCell;
EvenOddCell m_emptyCell;
Button m_consoleButton;
SelectableTableView m_selectableTableView;

View File

@@ -21,6 +21,7 @@ public:
/* HighlightCell */
void setHighlighted(bool highlight) override;
void reloadCell() override;
const char * text() const override { return m_scriptNodeView.text(); }
constexpr static char k_parentheses[] = "()";
constexpr static char k_parenthesesWithEmpty[] = {'(', Ion::Charset::Empty, ')', 0};
@@ -32,6 +33,9 @@ protected:
void setScriptStore(ScriptStore * scriptStore);
void drawRect(KDContext * ctx, KDRect rect) const override;
virtual KDSize minimalSizeForOptimalDisplay() const override;
const char * text() const override {
return m_scriptStore->scriptAtIndex(m_scriptNode->scriptIndex()).name();
}
private:
constexpr static KDText::FontSize k_fontSize = KDText::FontSize::Small;
constexpr static KDCoordinate k_verticalMargin = 7;

View File

@@ -41,315 +41,24 @@ double CartesianFunction::sumBetweenBounds(double start, double end, Poincare::C
return integral.approximateToScalar<double>(*context);
}
CartesianFunction::Point CartesianFunction::nextMinimumFrom(double start, double step, double max, Context * context) const {
return nextMinimumOfFunction(start, step, max, [](double x, Context * context, const Shared::Function * function0, const Shared::Function * function1 = nullptr) {
return function0->evaluateAtAbscissa(x, context);
}, context);
Expression::Coordinate2D CartesianFunction::nextMinimumFrom(double start, double step, double max, Context * context) const {
return expression(context)->nextMinimum(symbol(), start, step, max, *context);
}
CartesianFunction::Point CartesianFunction::nextMaximumFrom(double start, double step, double max, Context * context) const {
Point minimumOfOpposite = nextMinimumOfFunction(start, step, max, [](double x, Context * context, const Shared::Function * function0, const Shared::Function * function1 = nullptr) {
return -function0->evaluateAtAbscissa(x, context);
}, context);
return {.abscissa = minimumOfOpposite.abscissa, .value = -minimumOfOpposite.value};
Expression::Coordinate2D CartesianFunction::nextMaximumFrom(double start, double step, double max, Context * context) const {
return expression(context)->nextMaximum(symbol(), start, step, max, *context);
}
double CartesianFunction::nextRootFrom(double start, double step, double max, Context * context) const {
return nextIntersectionWithFunction(start, step, max, [](double x, Context * context, const Shared::Function * function0, const Shared::Function * function1 = nullptr) {
return function0->evaluateAtAbscissa(x, context);
}, context, nullptr);
return expression(context)->nextRoot(symbol(), start, step, max, *context);
}
CartesianFunction::Point CartesianFunction::nextIntersectionFrom(double start, double step, double max, Poincare::Context * context, const Shared::Function * function) const {
double resultAbscissa = nextIntersectionWithFunction(start, step, max, [](double x, Context * context, const Shared::Function * function0, const Shared::Function * function1) {
return function0->evaluateAtAbscissa(x, context)-function1->evaluateAtAbscissa(x, context);
}, context, function);
CartesianFunction::Point result = {.abscissa = resultAbscissa, .value = evaluateAtAbscissa(resultAbscissa, context)};
if (std::fabs(result.value) < step*k_precision) {
result.value = 0.0;
}
return result;
}
CartesianFunction::Point CartesianFunction::nextMinimumOfFunction(double start, double step, double max, Evaluation evaluate, Context * context, const Shared::Function * function, bool lookForRootMinimum) const {
double bracket[3];
Point result = {.abscissa = NAN, .value = NAN};
double x = start;
bool endCondition = false;
do {
bracketMinimum(x, step, max, bracket, evaluate, context, function);
result = brentMinimum(bracket[0], bracket[2], evaluate, context, function);
x = bracket[1];
endCondition = std::isnan(result.abscissa) && (step > 0.0 ? x <= max : x >= max);
if (lookForRootMinimum) {
endCondition |= std::fabs(result.value) >= k_sqrtEps && (step > 0.0 ? x <= max : x >= max);
}
} while (endCondition);
if (std::fabs(result.abscissa) < step*k_precision) {
result.abscissa = 0;
result.value = evaluate(0, context, this, function);
}
if (std::fabs(result.value) < step*k_precision) {
result.value = 0;
}
if (lookForRootMinimum) {
result.abscissa = std::fabs(result.value) >= k_sqrtEps ? NAN : result.abscissa;
}
return result;
}
void CartesianFunction::bracketMinimum(double start, double step, double max, double result[3], Evaluation evaluate, Context * context, const Shared::Function * function) const {
Point p[3];
p[0] = {.abscissa = start, .value = evaluate(start, context, this, function)};
p[1] = {.abscissa = start+step, .value = evaluate(start+step, context, this, function)};
double x = start+2.0*step;
while (step > 0.0 ? x <= max : x >= max) {
p[2] = {.abscissa = x, .value = evaluate(x, context, this, function)};
if (p[0].value > p[1].value && p[2].value > p[1].value) {
result[0] = p[0].abscissa;
result[1] = p[1].abscissa;
result[2] = p[2].abscissa;
return;
}
if (p[0].value > p[1].value && p[1].value == p[2].value) {
} else {
p[0] = p[1];
p[1] = p[2];
}
x += step;
}
result[0] = NAN;
result[1] = NAN;
result[2] = NAN;
Expression::Coordinate2D CartesianFunction::nextIntersectionFrom(double start, double step, double max, Poincare::Context * context, const Shared::Function * function) const {
return expression(context)->nextIntersection(symbol(), start, step, max, *context, function->expression(context));
}
char CartesianFunction::symbol() const {
return 'x';
}
CartesianFunction::Point CartesianFunction::brentMinimum(double ax, double bx, Evaluation evaluate, Context * context, const Shared::Function * function) const {
/* Bibliography: R. P. Brent, Algorithms for finding zeros and extrema of
* functions without calculating derivatives */
if (ax > bx) {
return brentMinimum(bx, ax, evaluate, context, function);
}
double e = 0.0;
double a = ax;
double b = bx;
double x = a+k_goldenRatio*(b-a);
double v = x;
double w = x;
double fx = evaluate(x, context, this, function);
double fw = fx;
double fv = fw;
double d = NAN;
double u, fu;
for (int i = 0; i < 100; i++) {
double m = 0.5*(a+b);
double tol1 = k_sqrtEps*std::fabs(x)+1E-10;
double tol2 = 2.0*tol1;
if (std::fabs(x-m) <= tol2-0.5*(b-a)) {
double middleFax = evaluate((x+a)/2.0, context, this, function);
double middleFbx = evaluate((x+b)/2.0, context, this, function);
double fa = evaluate(a, context, this, function);
double fb = evaluate(b, context, this, function);
if (middleFax-fa <= k_sqrtEps && fx-middleFax <= k_sqrtEps && fx-middleFbx <= k_sqrtEps && middleFbx-fb <= k_sqrtEps) {
Point result = {.abscissa = x, .value = fx};
return result;
}
}
double p = 0;
double q = 0;
double r = 0;
if (std::fabs(e) > tol1) {
r = (x-w)*(fx-fv);
q = (x-v)*(fx-fw);
p = (x-v)*q -(x-w)*r;
q = 2.0*(q-r);
if (q>0.0) {
p = -p;
} else {
q = -q;
}
r = e;
e = d;
}
if (std::fabs(p) < std::fabs(0.5*q*r) && p<q*(a-x) && p<q*(b-x)) {
d = p/q;
u= x+d;
if (u-a < tol2 || b-u < tol2) {
d = x < m ? tol1 : -tol1;
}
} else {
e = x<m ? b-x : a-x;
d = k_goldenRatio*e;
}
u = x + (std::fabs(d) >= tol1 ? d : (d>0 ? tol1 : -tol1));
fu = evaluate(u, context, this, function);
if (fu <= fx) {
if (u<x) {
b = x;
} else {
a = x;
}
v = w;
fv = fw;
w = x;
fw = fx;
x = u;
fx = fu;
} else {
if (u<x) {
a = u;
} else {
b = u;
}
if (fu <= fw || w == x) {
v = w;
fv = fw;
w = u;
fw = fu;
} else if (fu <= fv || v == x || v == w) {
v = u;
fv = fu;
}
}
}
Point result = {.abscissa = NAN, .value = NAN};
return result;
}
double CartesianFunction::nextIntersectionWithFunction(double start, double step, double max, Evaluation evaluation, Context * context, const Shared::Function * function) const {
double bracket[2];
double result = NAN;
static double precisionByGradUnit = 1E6;
double x = start+step;
do {
bracketRoot(x, step, max, bracket, evaluation, context, function);
result = brentRoot(bracket[0], bracket[1], std::fabs(step/precisionByGradUnit), evaluation, context, function);
x = bracket[1];
} while (std::isnan(result) && (step > 0.0 ? x <= max : x >= max));
double extremumMax = std::isnan(result) ? max : result;
Point resultExtremum[2] = {
nextMinimumOfFunction(start, step, extremumMax, [](double x, Context * context, const Shared::Function * function0, const Shared::Function * function1) {
if (function1) {
return function0->evaluateAtAbscissa(x, context)-function1->evaluateAtAbscissa(x, context);
} else {
return function0->evaluateAtAbscissa(x, context);
}
}, context, function, true),
nextMinimumOfFunction(start, step, extremumMax, [](double x, Context * context, const Shared::Function * function0, const Shared::Function * function1) {
if (function1) {
return function1->evaluateAtAbscissa(x, context)-function0->evaluateAtAbscissa(x, context);
} else {
return -function0->evaluateAtAbscissa(x, context);
}
}, context, function, true)};
for (int i = 0; i < 2; i++) {
if (!std::isnan(resultExtremum[i].abscissa) && (std::isnan(result) || std::fabs(result - start) > std::fabs(resultExtremum[i].abscissa - start))) {
result = resultExtremum[i].abscissa;
}
}
if (std::fabs(result) < step*k_precision) {
result = 0;
}
return result;
}
void CartesianFunction::bracketRoot(double start, double step, double max, double result[2], Evaluation evaluation, Context * context, const Shared::Function * function) const {
double a = start;
double b = start+step;
while (step > 0.0 ? b <= max : b >= max) {
double fa = evaluation(a, context, this, function);
double fb = evaluation(b, context, this, function);
if (fa*fb <= 0) {
result[0] = a;
result[1] = b;
return;
}
a = b;
b = b+step;
}
result[0] = NAN;
result[1] = NAN;
}
double CartesianFunction::brentRoot(double ax, double bx, double precision, Evaluation evaluation, Poincare::Context * context, const Shared::Function * function) const {
if (ax > bx) {
return brentRoot(bx, ax, precision, evaluation, context, function);
}
double a = ax;
double b = bx;
double c = bx;
double d = b-a;
double e = b-a;
double fa = evaluation(a, context, this, function);
double fb = evaluation(b, context, this, function);
double fc = fb;
for (int i = 0; i < 100; i++) {
if ((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) {
c = a;
fc = fa;
e = b-a;
d = b-a;
}
if (std::fabs(fc) < std::fabs(fb)) {
a = b;
b = c;
c = a;
fa = fb;
fb = fc;
fc = fa;
}
double tol1 = 2.0*DBL_EPSILON*std::fabs(b)+0.5*precision;
double xm = 0.5*(c-b);
if (std::fabs(xm) <= tol1 || fb == 0.0) {
double fbcMiddle = evaluation(0.5*(b+c), context, this, function);
double isContinuous = (fb <= fbcMiddle && fbcMiddle <= fc) || (fc <= fbcMiddle && fbcMiddle <= fb);
if (isContinuous) {
return b;
}
}
if (std::fabs(e) >= tol1 && std::fabs(fa) > std::fabs(b)) {
double s = fb/fa;
double p = 2.0*xm*s;
double q = 1.0-s;
if (a != c) {
q = fa/fc;
double r = fb/fc;
p = s*(2.0*xm*q*(q-r)-(b-a)*(r-1.0));
q = (q-1.0)*(r-1.0)*(s-1.0);
}
q = p > 0.0 ? -q : q;
p = std::fabs(p);
double min1 = 3.0*xm*q-std::fabs(tol1*q);
double min2 = std::fabs(e*q);
if (2.0*p < (min1 < min2 ? min1 : min2)) {
e = d;
d = p/q;
} else {
d = xm;
e =d;
}
} else {
d = xm;
e = d;
}
a = b;
fa = fb;
if (std::fabs(d) > tol1) {
b += d;
} else {
b += xm > 0.0 ? tol1 : tol1;
}
fb = evaluation(b, context, this, function);
}
return NAN;
}
}

View File

@@ -13,26 +13,12 @@ public:
void setDisplayDerivative(bool display);
double approximateDerivative(double x, Poincare::Context * context) const;
double sumBetweenBounds(double start, double end, Poincare::Context * context) const override;
struct Point {
double abscissa;
double value;
};
Point nextMinimumFrom(double start, double step, double max, Poincare::Context * context) const;
Point nextMaximumFrom(double start, double step, double max, Poincare::Context * context) const;
Poincare::Expression::Coordinate2D nextMinimumFrom(double start, double step, double max, Poincare::Context * context) const;
Poincare::Expression::Coordinate2D nextMaximumFrom(double start, double step, double max, Poincare::Context * context) const;
double nextRootFrom(double start, double step, double max, Poincare::Context * context) const;
Point nextIntersectionFrom(double start, double step, double max, Poincare::Context * context, const Shared::Function * function) const;
Poincare::Expression::Coordinate2D nextIntersectionFrom(double start, double step, double max, Poincare::Context * context, const Shared::Function * function) const;
char symbol() const override;
private:
constexpr static double k_precision = 1.0E-5;
constexpr static double k_sqrtEps = 1.4901161193847656E-8; // sqrt(DBL_EPSILON)
constexpr static double k_goldenRatio = 0.381966011250105151795413165634361882279690820194237137864; // (3-sqrt(5))/2
typedef double (*Evaluation)(double abscissa, Poincare::Context * context, const Shared::Function * function0, const Shared::Function * function1);
Point nextMinimumOfFunction(double start, double step, double max, Evaluation evaluation, Poincare::Context * context, const Shared::Function * function = nullptr, bool lookForRootMinimum = false) const;
void bracketMinimum(double start, double step, double max, double result[3], Evaluation evaluation, Poincare::Context * context, const Shared::Function * function= nullptr) const;
Point brentMinimum(double ax, double bx, Evaluation evaluation, Poincare::Context * context, const Shared::Function * function = nullptr) const;
double nextIntersectionWithFunction(double start, double step, double max, Evaluation evaluation, Poincare::Context * context, const Shared::Function * function) const;
void bracketRoot(double start, double step, double max, double result[2], Evaluation evaluation, Poincare::Context * context, const Shared::Function * function) const;
double brentRoot(double ax, double bx, double precision, Evaluation evaluation, Poincare::Context * context, const Shared::Function * function) const;
bool m_displayDerivative;
};

View File

@@ -14,7 +14,7 @@ constexpr const char * CartesianFunctionStore::k_functionNames[k_maxNumberOfFunc
CartesianFunctionStore::CartesianFunctionStore() :
Shared::FunctionStore()
{
addEmptyFunction();
addEmptyModel();
}
uint32_t CartesianFunctionStore::storeChecksum() {
@@ -27,91 +27,30 @@ uint32_t CartesianFunctionStore::storeChecksum() {
return Ion::crc32((uint32_t *)checksums, dataLengthInBytes/sizeof(uint32_t));
}
CartesianFunction * CartesianFunctionStore::functionAtIndex(int i) {
assert(i>=0 && i<m_numberOfFunctions);
return &m_functions[i];
}
CartesianFunction * CartesianFunctionStore::activeFunctionAtIndex(int i) {
return (CartesianFunction *)Shared::FunctionStore::activeFunctionAtIndex(i);
}
CartesianFunction * CartesianFunctionStore::definedFunctionAtIndex(int i) {
return (CartesianFunction *)Shared::FunctionStore::definedFunctionAtIndex(i);
}
CartesianFunction * CartesianFunctionStore::addEmptyFunction() {
assert(m_numberOfFunctions < k_maxNumberOfFunctions);
const char * name = firstAvailableName();
KDColor color = firstAvailableColor();
CartesianFunction addedFunction(name, color);
m_functions[m_numberOfFunctions] = addedFunction;
CartesianFunction * result = &m_functions[m_numberOfFunctions];
m_numberOfFunctions++;
return result;
}
void CartesianFunctionStore::removeFunction(Shared::Function * f) {
int i = 0;
while (&m_functions[i] != f && i < m_numberOfFunctions) {
i++;
}
assert(i>=0 && i<m_numberOfFunctions);
m_numberOfFunctions--;
for (int j = i; j<m_numberOfFunctions; j++) {
m_functions[j] = m_functions[j+1];
}
CartesianFunction emptyFunction("", KDColorBlack);
m_functions[m_numberOfFunctions] = emptyFunction;
}
int CartesianFunctionStore::maxNumberOfFunctions() {
return k_maxNumberOfFunctions;
}
char CartesianFunctionStore::symbol() const {
return 'x';
}
const char * CartesianFunctionStore::firstAvailableName() {
for (int k = 0; k < k_maxNumberOfFunctions; k++) {
int j = 0;
while (j < m_numberOfFunctions) {
if (m_functions[j].name() == k_functionNames[k]) {
break;
}
j++;
}
if (j == m_numberOfFunctions) {
return k_functionNames[k];
}
}
return k_functionNames[0];
}
const KDColor CartesianFunctionStore::firstAvailableColor() {
for (int k = 0; k < k_maxNumberOfFunctions; k++) {
int j = 0;
while (j < m_numberOfFunctions) {
if (m_functions[j].color() == k_defaultColors[k]) {
break;
}
j++;
}
if (j == m_numberOfFunctions) {
return k_defaultColors[k];
}
}
return k_defaultColors[0];
}
void CartesianFunctionStore::removeAll() {
for (int i = 0; i < m_numberOfFunctions; i++) {
CartesianFunction emptyFunction("", KDColorBlack);
m_functions[i] = emptyFunction;
}
m_numberOfFunctions = 0;
addEmptyFunction();
FunctionStore::removeAll();
addEmptyModel();
}
CartesianFunction * CartesianFunctionStore::emptyModel() {
static CartesianFunction addedFunction("", KDColorBlack);
addedFunction = CartesianFunction(firstAvailableName(), firstAvailableColor());
return &addedFunction;
}
CartesianFunction * CartesianFunctionStore::nullModel() {
static CartesianFunction emptyFunction("", KDColorBlack);
return &emptyFunction;
}
void CartesianFunctionStore::setModelAtIndex(Shared::ExpressionModel * e, int i) {
assert(i>=0 && i<m_numberOfModels);
m_functions[i] = *(static_cast<CartesianFunction *>(e));
}
}

View File

@@ -12,24 +12,31 @@ class CartesianFunctionStore : public Shared::FunctionStore {
public:
CartesianFunctionStore();
uint32_t storeChecksum() override;
CartesianFunction * functionAtIndex(int i) override;
CartesianFunction * activeFunctionAtIndex(int i) override;
CartesianFunction * definedFunctionAtIndex(int i) override;
CartesianFunction * addEmptyFunction() override;
void removeFunction(Shared::Function * f) override;
int maxNumberOfFunctions() override;
CartesianFunction * modelAtIndex(int i) override { return &m_functions[i]; }
CartesianFunction * activeFunctionAtIndex(int i) override { return (CartesianFunction *)Shared::FunctionStore::activeFunctionAtIndex(i); }
CartesianFunction * definedFunctionAtIndex(int i) override { return (CartesianFunction *)Shared::FunctionStore::definedFunctionAtIndex(i); }
int maxNumberOfModels() const override {
return k_maxNumberOfFunctions;
}
char symbol() const override;
void removeAll() override;
static constexpr int k_maxNumberOfFunctions = 4;
private:
const char * firstAvailableName() override;
const KDColor firstAvailableColor() override;
static constexpr KDColor k_defaultColors[k_maxNumberOfFunctions] = {
Palette::Red, Palette::Blue, Palette::Green, Palette::YellowDark,
};
static constexpr const char * k_functionNames[k_maxNumberOfFunctions] = {
"f", "g", "h", "p",
};
CartesianFunction * emptyModel() override;
CartesianFunction * nullModel() override;
void setModelAtIndex(Shared::ExpressionModel * f, int i) override;
const char * firstAvailableName() override {
return firstAvailableAttribute(k_functionNames, FunctionStore::name);
}
const KDColor firstAvailableColor() override {
return firstAvailableAttribute(k_defaultColors, FunctionStore::color);
}
CartesianFunction m_functions[k_maxNumberOfFunctions];
};

View File

@@ -42,7 +42,7 @@ View * FunctionTitleCell::subviewAtIndex(int index) {
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, bounds().height()-k_separatorThickness);
textFrame = KDRect(k_colorIndicatorThickness, 0, bounds().width() - k_colorIndicatorThickness-k_separatorThickness, bounds().height()-k_separatorThickness);
}
m_bufferTextView.setFrame(textFrame);
}

View File

@@ -12,10 +12,13 @@ public:
void setHighlighted(bool highlight) override;
void setColor(KDColor color) override;
void setText(const char * textContent);
const char * text() const override {
return m_bufferTextView.text();
}
private:
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
private:
EvenOddBufferTextCell m_bufferTextView;
};

View File

@@ -24,7 +24,7 @@ View * CalculationGraphController::view() {
void CalculationGraphController::viewWillAppear() {
assert(m_function != nullptr);
CartesianFunction::Point pointOfInterest = computeNewPointOfInteresetFromAbscissa(m_graphRange->xMin(), 1);
Expression::Coordinate2D pointOfInterest = computeNewPointOfInteresetFromAbscissa(m_graphRange->xMin(), 1);
if (std::isnan(pointOfInterest.abscissa)) {
m_isActive = false;
m_graphView->setCursorView(nullptr);
@@ -67,7 +67,7 @@ void CalculationGraphController::reloadBannerView() {
}
bool CalculationGraphController::moveCursor(int direction) {
CartesianFunction::Point newPointOfInterest = computeNewPointOfInteresetFromAbscissa(m_cursor->x(), direction);
Expression::Coordinate2D newPointOfInterest = computeNewPointOfInteresetFromAbscissa(m_cursor->x(), direction);
if (std::isnan(newPointOfInterest.abscissa)) {
return false;
}
@@ -76,7 +76,7 @@ bool CalculationGraphController::moveCursor(int direction) {
return true;
}
CartesianFunction::Point CalculationGraphController::computeNewPointOfInteresetFromAbscissa(double start, int direction) {
Expression::Coordinate2D CalculationGraphController::computeNewPointOfInteresetFromAbscissa(double start, int direction) {
TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app();
double step = m_graphRange->xGridUnit()/10.0;
step = direction < 0 ? -step : step;

View File

@@ -23,8 +23,8 @@ protected:
BannerView * bannerView() override { return m_bannerView; }
virtual void reloadBannerView();
bool moveCursor(int direction);
CartesianFunction::Point computeNewPointOfInteresetFromAbscissa(double start, int direction);
virtual CartesianFunction::Point computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) = 0;
Poincare::Expression::Coordinate2D computeNewPointOfInteresetFromAbscissa(double start, int direction);
virtual Poincare::Expression::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) = 0;
GraphView * m_graphView;
BannerView * m_bannerView;
Shared::InteractiveCurveViewRange * m_graphRange;

View File

@@ -15,7 +15,7 @@ const char * MinimumGraphController::title() {
return I18n::translate(I18n::Message::Minimum);
}
CartesianFunction::Point MinimumGraphController::computeNewPointOfInterest(double start, double step, double max, Context * context) {
Expression::Coordinate2D MinimumGraphController::computeNewPointOfInterest(double start, double step, double max, Context * context) {
return m_function->nextMinimumFrom(start, step, max, context);
}
@@ -28,7 +28,7 @@ const char * MaximumGraphController::title() {
return I18n::translate(I18n::Message::Maximum);
}
CartesianFunction::Point MaximumGraphController::computeNewPointOfInterest(double start, double step, double max, Context * context) {
Expression::Coordinate2D MaximumGraphController::computeNewPointOfInterest(double start, double step, double max, Context * context) {
return m_function->nextMaximumFrom(start, step, max, context);
}

View File

@@ -10,7 +10,7 @@ public:
MinimumGraphController(Responder * parentResponder, GraphView * graphView, BannerView * bannerView, Shared::InteractiveCurveViewRange * curveViewRange, Shared::CurveViewCursor * cursor);
const char * title() override;
private:
CartesianFunction::Point computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override;
Poincare::Expression::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override;
};
class MaximumGraphController : public CalculationGraphController {
@@ -18,7 +18,7 @@ public:
MaximumGraphController(Responder * parentResponder, GraphView * graphView, BannerView * bannerView, Shared::InteractiveCurveViewRange * curveViewRange, Shared::CurveViewCursor * cursor);
const char * title() override;
private:
CartesianFunction::Point computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override;
Poincare::Expression::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override;
};
}

View File

@@ -19,7 +19,7 @@ GraphController::GraphController(Responder * parentResponder, CartesianFunctionS
}
I18n::Message GraphController::emptyMessage() {
if (m_functionStore->numberOfDefinedFunctions() == 0) {
if (m_functionStore->numberOfDefinedModels() == 0) {
return I18n::Message::NoFunction;
}
return I18n::Message::NoActivatedFunction;

View File

@@ -36,12 +36,12 @@ void IntersectionGraphController::reloadBannerView() {
bannerView()->setLegendAtIndex(buffer, 1);
}
CartesianFunction::Point IntersectionGraphController::computeNewPointOfInterest(double start, double step, double max, Context * context) {
CartesianFunction::Point result = {.abscissa = NAN, .value = NAN};
Expression::Coordinate2D IntersectionGraphController::computeNewPointOfInterest(double start, double step, double max, Context * context) {
Expression::Coordinate2D result = {.abscissa = NAN, .value = NAN};
for (int i = 0; i < m_functionStore->numberOfActiveFunctions(); i++) {
Function * f = m_functionStore->activeFunctionAtIndex(i);
if (f != m_function) {
CartesianFunction::Point intersection = m_function->nextIntersectionFrom(start, step, max, context, f);
Expression::Coordinate2D intersection = m_function->nextIntersectionFrom(start, step, max, context, f);
if ((std::isnan(result.abscissa) || std::fabs(intersection.abscissa-start) < std::fabs(result.abscissa-start)) && !std::isnan(intersection.abscissa)) {
m_intersectedFunction = f;
result = (std::isnan(result.abscissa) || std::fabs(intersection.abscissa-start) < std::fabs(result.abscissa-start)) ? intersection : result;

View File

@@ -11,7 +11,7 @@ public:
const char * title() override;
private:
void reloadBannerView() override;
CartesianFunction::Point computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override;
Poincare::Expression::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override;
Shared::Function * m_intersectedFunction;
CartesianFunctionStore * m_functionStore;
};

View File

@@ -15,7 +15,7 @@ const char * RootGraphController::title() {
return I18n::translate(I18n::Message::Zeros);
}
CartesianFunction::Point RootGraphController::computeNewPointOfInterest(double start, double step, double max, Context * context) {
Expression::Coordinate2D RootGraphController::computeNewPointOfInterest(double start, double step, double max, Context * context) {
return {.abscissa = m_function->nextRootFrom(start, step, max, context), .value = 0.0};
}

View File

@@ -10,7 +10,7 @@ public:
RootGraphController(Responder * parentResponder, GraphView * graphView, BannerView * bannerView, Shared::InteractiveCurveViewRange * curveViewRange, Shared::CurveViewCursor * cursor);
const char * title() override;
private:
CartesianFunction::Point computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override;
Poincare::Expression::Coordinate2D computeNewPointOfInterest(double start, double step, double max, Poincare::Context * context) override;
};
}

View File

@@ -9,7 +9,7 @@ using namespace Shared;
namespace Graph {
ListController::ListController(Responder * parentResponder, CartesianFunctionStore * functionStore, ButtonRowController * header, ButtonRowController * footer) :
Shared::ListController(parentResponder, functionStore, header, footer, I18n::Message::AddFunction),
Shared::FunctionListController(parentResponder, functionStore, header, footer, I18n::Message::AddFunction),
m_functionTitleCells{},
m_expressionCells{},
m_parameterController(this, functionStore, I18n::Message::FunctionColor, I18n::Message::DeleteFunction)
@@ -20,45 +20,6 @@ const char * ListController::title() {
return I18n::translate(I18n::Message::FunctionTab);
}
int ListController::numberOfRows() {
if (m_functionStore->numberOfFunctions() == m_functionStore->maxNumberOfFunctions()) {
return m_functionStore->numberOfFunctions();
}
return 1 + m_functionStore->numberOfFunctions();
};
KDCoordinate ListController::rowHeight(int j) {
if (m_functionStore->numberOfFunctions() < m_functionStore->maxNumberOfFunctions() && j == numberOfRows() - 1) {
return Metric::StoreRowHeight;
}
Function * function = m_functionStore->functionAtIndex(j);
if (function->layout() == nullptr) {
return Metric::StoreRowHeight;
}
KDCoordinate functionSize = function->layout()->size().height();
return functionSize + Metric::StoreRowHeight - KDText::charSize().height();
}
void ListController::editExpression(Function * function, Ion::Events::Event event) {
char * initialText = nullptr;
char initialTextContent[TextField::maxBufferSize()];
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
strlcpy(initialTextContent, function->text(), sizeof(initialTextContent));
initialText = initialTextContent;
}
App * myApp = (App *)app();
InputViewController * inputController = myApp->inputViewController();
inputController->edit(this, event, function, initialText,
[](void * context, void * sender){
Shared::Function * myFunction = (Shared::Function *)context;
InputViewController * myInputViewController = (InputViewController *)sender;
const char * textBody = myInputViewController->textBody();
myFunction->setContent(textBody);
},
[](void * context, void * sender){
});
}
ListParameterController * ListController::parameterController() {
return &m_parameterController;
}
@@ -80,7 +41,7 @@ HighlightCell * ListController::expressionCells(int index) {
void ListController::willDisplayTitleCellAtIndex(HighlightCell * cell, int j) {
FunctionTitleCell * myFunctionCell = (FunctionTitleCell *)cell;
CartesianFunction * function = ((CartesianFunctionStore *)m_functionStore)->functionAtIndex(j);
CartesianFunction * function = ((CartesianFunctionStore *)m_functionStore)->modelAtIndex(j);
char bufferName[5] = {*function->name(),'(', m_functionStore->symbol(),')', 0};
myFunctionCell->setText(bufferName);
KDColor functionNameColor = function->isActive() ? function->color() : Palette::GreyDark;
@@ -88,18 +49,17 @@ void ListController::willDisplayTitleCellAtIndex(HighlightCell * cell, int j) {
}
void ListController::willDisplayExpressionCellAtIndex(HighlightCell * cell, int j) {
Shared::FunctionListController::willDisplayExpressionCellAtIndex(cell, j);
FunctionExpressionCell * myCell = (FunctionExpressionCell *)cell;
Function * f = m_functionStore->functionAtIndex(j);
myCell->setExpressionLayout(f->layout());
Function * f = m_functionStore->modelAtIndex(j);
bool active = f->isActive();
KDColor textColor = active ? KDColorBlack : Palette::GreyDark;
myCell->setTextColor(textColor);
}
bool ListController::removeFunctionRow(Function * function) {
if (m_functionStore->numberOfFunctions() > 1) {
m_functionStore->removeFunction(function);
return true;
bool ListController::removeModelRow(ExpressionModel * model) {
if (m_functionStore->numberOfModels() > 1) {
return Shared::FunctionListController::removeModelRow(model);
}
return false;
}
@@ -108,8 +68,9 @@ View * ListController::loadView() {
for (int i = 0; i < k_maxNumberOfRows; i++) {
m_functionTitleCells[i] = new FunctionTitleCell(FunctionTitleCell::Orientation::VerticalIndicator);
m_expressionCells[i] = new FunctionExpressionCell();
m_expressionCells[i]->setMargin(k_expressionMargin);
}
return Shared::ListController::loadView();
return Shared::FunctionListController::loadView();
}
void ListController::unloadView(View * view) {
@@ -119,7 +80,7 @@ void ListController::unloadView(View * view) {
delete m_expressionCells[i];
m_expressionCells[i] = nullptr;
}
Shared::ListController::unloadView(view);
Shared::FunctionListController::unloadView(view);
}
}

View File

@@ -3,29 +3,25 @@
#include <escher.h>
#include "../function_title_cell.h"
#include "../../shared/function_expression_cell.h"
#include "../cartesian_function_store.h"
#include "../../shared/new_function_cell.h"
#include "../../shared/list_controller.h"
#include "../../shared/function_expression_cell.h"
#include "../../shared/function_list_controller.h"
#include "../../shared/list_parameter_controller.h"
namespace Graph {
class ListController : public Shared::ListController {
class ListController : public Shared::FunctionListController {
public:
ListController(Responder * parentResponder, CartesianFunctionStore * functionStore, ButtonRowController * header, ButtonRowController * footer);
const char * title() override;
int numberOfRows() override;
KDCoordinate rowHeight(int j) override;
private:
void editExpression(Shared::Function * function, Ion::Events::Event event) override;
Shared::ListParameterController * parameterController() override;
int maxNumberOfRows() override;
HighlightCell * titleCells(int index) override;
HighlightCell * expressionCells(int index) override;
void willDisplayTitleCellAtIndex(HighlightCell * cell, int j) override;
void willDisplayExpressionCellAtIndex(HighlightCell * cell, int j) override;
bool removeFunctionRow(Shared::Function * function) override;
bool removeModelRow(Shared::ExpressionModel * function) override;
View * loadView() override;
void unloadView(View * view) override;
constexpr static int k_maxNumberOfRows = 5;

View File

@@ -55,7 +55,7 @@ void ValuesController::willDisplayCellAtLocation(HighlightCell * cell, int i, in
}
I18n::Message ValuesController::emptyMessage() {
if (m_functionStore->numberOfDefinedFunctions() == 0) {
if (m_functionStore->numberOfDefinedModels() == 0) {
return I18n::Message::NoFunction;
}
return I18n::Message::NoActivatedFunction;
@@ -68,7 +68,7 @@ IntervalParameterController * ValuesController::intervalParameterController() {
CartesianFunction * ValuesController::functionAtColumn(int i) {
assert(i > 0);
int index = 1;
for (int k = 0; k < m_functionStore->numberOfDefinedFunctions(); k++) {
for (int k = 0; k < m_functionStore->numberOfDefinedModels(); k++) {
if (m_functionStore->definedFunctionAtIndex(k)->isActive()) {
if (i == index) {
return m_functionStore->definedFunctionAtIndex(k);
@@ -89,7 +89,7 @@ CartesianFunction * ValuesController::functionAtColumn(int i) {
bool ValuesController::isDerivativeColumn(int i) {
assert(i >= 1);
int index = 1;
for (int k = 0; k < m_functionStore->numberOfDefinedFunctions(); k++) {
for (int k = 0; k < m_functionStore->numberOfDefinedModels(); k++) {
if (m_functionStore->definedFunctionAtIndex(k)->isActive()) {
if (i == index) {
return false;

View File

@@ -45,7 +45,6 @@ void AppCell::setVisible(bool visible) {
}
void AppCell::reloadCell() {
HighlightCell::reloadCell();
m_nameView.setTextColor(isHighlighted() ? KDColorWhite : KDColorBlack);
m_nameView.setBackgroundColor(isHighlighted() ? Palette::YellowDark : KDColorWhite);
}

View File

@@ -18,11 +18,11 @@ public:
void reloadCell() override;
void setAppDescriptor(::App::Descriptor * appDescriptor);
private:
static constexpr KDCoordinate k_iconMargin = 18;
static constexpr KDCoordinate k_iconMargin = 22;
static constexpr KDCoordinate k_iconWidth = 55;
static constexpr KDCoordinate k_iconHeight = 56;
static constexpr KDCoordinate k_nameWidthMargin = 4;
static constexpr KDCoordinate k_nameHeightMargin = 2;
static constexpr KDCoordinate k_nameHeightMargin = 1;
ImageView m_iconView;
MessageTextView m_nameView;
bool m_visible;

View File

@@ -10,10 +10,9 @@ Controller::ContentView::ContentView(Controller * controller, SelectableTableVie
m_selectableTableView(controller, controller, selectionDataSource, controller)
{
m_selectableTableView.setVerticalCellOverlap(0);
m_selectableTableView.setMargins(0, k_sideMargin, 0, k_sideMargin);
m_selectableTableView.setMargins(0, k_sideMargin, k_bottomMargin, k_sideMargin);
m_selectableTableView.setColorsBackground(false);
m_selectableTableView.setIndicatorThickness(k_indicatorThickness);
m_selectableTableView.horizontalScrollIndicator()->setMargin(k_indicatorMargin);
m_selectableTableView.verticalScrollIndicator()->setMargin(k_indicatorMargin);
}
@@ -53,13 +52,20 @@ Controller::Controller(Responder * parentResponder, ::AppsContainer * container,
bool Controller::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
m_container->switchTo(m_container->appSnapshotAtIndex(m_selectionDataSource->selectedColumn()*k_numberOfRows+m_selectionDataSource->selectedRow()+1));
m_container->switchTo(m_container->appSnapshotAtIndex(m_selectionDataSource->selectedRow()*k_numberOfColumns+m_selectionDataSource->selectedColumn()+1));
return true;
}
if (event == Ion::Events::Home || event == Ion::Events::Back) {
return m_view.selectableTableView()->selectCellAtLocation(0,0);
}
}
if (event == Ion::Events::Right && m_selectionDataSource->selectedRow() < numberOfRows()) {
return m_view.selectableTableView()->selectCellAtLocation(0, m_selectionDataSource->selectedRow()+1);
}
if (event == Ion::Events::Left && m_selectionDataSource->selectedRow() > 0) {
return m_view.selectableTableView()->selectCellAtLocation(numberOfColumns()-1, m_selectionDataSource->selectedRow()-1);
}
return false;
}
@@ -81,11 +87,11 @@ View * Controller::view() {
}
int Controller::numberOfRows() {
return k_numberOfRows;
return ((numberOfIcons()-1)/k_numberOfColumns)+1;
}
int Controller::numberOfColumns() {
return ((numberOfIcons()-1)/k_numberOfRows)+1;
return k_numberOfColumns;
}
KDCoordinate Controller::cellHeight() {
@@ -106,7 +112,7 @@ int Controller::reusableCellCount() {
void Controller::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
AppCell * appCell = (AppCell *)cell;
int appIndex = (i*k_numberOfRows+j)+1;
int appIndex = (j*k_numberOfColumns+i)+1;
if (appIndex >= m_container->numberOfApps()) {
appCell->setVisible(false);
} else {
@@ -130,7 +136,7 @@ void Controller::tableViewDidChangeSelection(SelectableTableView * t, int previo
* redrawing takes time and is visible at scrolling. Here, we avoid the
* background complete redrawing but the code is a bit
* clumsy. */
if (m_container->numberOfApps()%2 == 0 && t->selectedColumn() == numberOfColumns() -1) {
if (m_container->numberOfApps()%2 == 1 && t->selectedRow() == numberOfRows() -1) {
m_view.reloadBottomRightCorner(this);
}
/* To prevent the selectable table view to select cells that are unvisible,
@@ -138,7 +144,7 @@ void Controller::tableViewDidChangeSelection(SelectableTableView * t, int previo
* unvisible. This trick does not create an endless loop as we ensure not to
* stay on a unvisible cell and to initialize the first cell on a visible one
* (so the previous one is always visible). */
int appIndex = (t->selectedColumn()*k_numberOfRows+t->selectedRow())+1;
int appIndex = (t->selectedColumn()+t->selectedRow()*k_numberOfColumns)+1;
if (appIndex >= m_container->numberOfApps()) {
t->selectCellAtLocation(previousSelectedCellX, previousSelectedCellY);
}

View File

@@ -42,11 +42,12 @@ private:
};
AppsContainer * m_container;
static constexpr KDCoordinate k_sideMargin = 4;
static constexpr KDCoordinate k_indicatorThickness = 28;
static constexpr KDCoordinate k_indicatorMargin = 116;
static constexpr int k_numberOfRows = 2;
static constexpr KDCoordinate k_bottomMargin = 14;
static constexpr KDCoordinate k_indicatorThickness = 15;
static constexpr KDCoordinate k_indicatorMargin = 61;
static constexpr int k_numberOfColumns = 3;
static constexpr int k_maxNumberOfCells = 16;
static constexpr int k_cellHeight = 98;
static constexpr int k_cellHeight = 104;
static constexpr int k_cellWidth = 104;
ContentView m_view;
AppCell m_cells[k_maxNumberOfCells];

View File

@@ -7,6 +7,7 @@ import argparse
import io
ion_special_characters = {
u'Δ': "Ion::Charset::CapitalDelta",
u'Σ': "Ion::Charset::CapitalSigma",
u'λ': "Ion::Charset::SmallLambda",
u'μ': "Ion::Charset::SmallMu",

View File

@@ -15,6 +15,9 @@ public:
void drawRect(KDContext * ctx, KDRect rect) const override;
EditableTextCell * editableTextCell();
MessageTextView * messageTextView();
const char * text() const override {
return m_calculation.text();
}
private:
constexpr static KDCoordinate k_margin = 5;
constexpr static KDCoordinate k_minTextFieldWidth = 4*KDText::charSize().width()+TextCursorView::k_width;

View File

@@ -88,15 +88,6 @@ void CalculationController::didBecomeFirstResponder() {
app()->setFirstResponder(&m_selectableTableView);
}
bool CalculationController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::Copy && selectedColumn() > 0) {
CalculationCell * myCell = static_cast<CalculationCell *>(m_selectableTableView.selectedCell());
Clipboard::sharedClipboard()->store(myCell->editableTextCell()->textField()->text());
return true;
}
return false;
}
View * CalculationController::view() {
return &m_contentView;
}
@@ -141,23 +132,6 @@ KDCoordinate CalculationController::rowHeight(int j) {
return ResponderImageCell::k_oneCellHeight;
}
KDCoordinate CalculationController::cumulatedWidthFromIndex(int j) {
int result = 0;
for (int k = 0; k < j; k++) {
result += columnWidth(k);
}
return result;
}
int CalculationController::indexFromCumulatedWidth(KDCoordinate offsetX) {
int result = 0;
int i = 0;
while (result < offsetX && i < numberOfColumns()) {
result += columnWidth(i++);
}
return (result < offsetX || offsetX == 0) ? i : i - 1;
}
KDCoordinate CalculationController::cumulatedHeightFromIndex(int j) {
return rowHeight(0) * j;
}

View File

@@ -17,7 +17,6 @@ public:
/* Responder */
void didEnterResponderChain(Responder * previousResponder) override;
void didBecomeFirstResponder() override;
bool handleEvent(Ion::Events::Event event) override;
/* ViewController */
View * view() override;
@@ -30,9 +29,7 @@ public:
int numberOfColumns() override;
KDCoordinate columnWidth(int i) override;
KDCoordinate rowHeight(int j) override;
KDCoordinate cumulatedWidthFromIndex(int i) override;
KDCoordinate cumulatedHeightFromIndex(int j) override;
int indexFromCumulatedWidth(KDCoordinate offsetX) override;
int indexFromCumulatedHeight(KDCoordinate offsetY) override;
HighlightCell * reusableCell(int index, int type) override;
int reusableCellCount(int type) override;

View File

@@ -42,20 +42,6 @@ bool CalculationController::handleEvent(Ion::Events::Event event) {
app()->setFirstResponder(tabController());
return true;
}
if (event == Ion::Events::Copy && selectedColumn() == 1 && selectedRow() > 0) {
if (selectedRow() <= k_totalNumberOfDoubleBufferRows) {
EvenOddDoubleBufferTextCell * myCell = (EvenOddDoubleBufferTextCell *)selectableTableView()->selectedCell();
if (myCell->firstTextSelected()) {
Clipboard::sharedClipboard()->store(myCell->firstText());
} else {
Clipboard::sharedClipboard()->store(myCell->secondText());
}
} else {
EvenOddBufferTextCell * myCell = (EvenOddBufferTextCell *)selectableTableView()->selectedCell();
Clipboard::sharedClipboard()->store(myCell->text());
}
return true;
}
return false;
}

View File

@@ -48,6 +48,14 @@ void EvenOddDoubleBufferTextCell::setHighlighted(bool highlight) {
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);

View File

@@ -13,6 +13,7 @@ public:
Responder * responder() override {
return this;
}
const char * text() const override;
void setEven(bool even) override;
bool firstTextSelected();
void selectFirstText(bool selectFirstText);

View File

@@ -35,6 +35,6 @@ tests += $(addprefix apps/sequence/test/,\
sequence.cpp\
)
test_objs += $(addprefix apps/sequence/, sequence.o sequence_store.o sequence_context.o cache_context.o)
test_objs += $(addprefix apps/shared/, function.o function_store.o)
test_objs += $(addprefix apps/shared/, expression_model.o expression_model_store.o function.o function_store.o)
app_images += apps/sequence/sequence_icon.png

View File

@@ -19,7 +19,7 @@ GraphController::GraphController(Responder * parentResponder, SequenceStore * se
}
I18n::Message GraphController::emptyMessage() {
if (m_sequenceStore->numberOfDefinedFunctions() == 0) {
if (m_sequenceStore->numberOfDefinedModels() == 0) {
return I18n::Message::NoSequence;
}
return I18n::Message::NoActivatedSequence;

View File

@@ -8,7 +8,7 @@ using namespace Poincare;
namespace Sequence {
ListController::ListController(Responder * parentResponder, SequenceStore * sequenceStore, ButtonRowController * header, ButtonRowController * footer) :
Shared::ListController(parentResponder, sequenceStore, header, footer, I18n::Message::AddSequence),
Shared::FunctionListController(parentResponder, sequenceStore, header, footer, I18n::Message::AddSequence),
m_sequenceStore(sequenceStore),
m_sequenceTitleCells{},
m_expressionCells{},
@@ -39,23 +39,23 @@ ExpressionFieldDelegateApp * ListController::expressionFieldDelegateApp() {
return (App *)app();
}
int ListController::numberOfRows() {
int ListController::numberOfExpressionRows() {
int numberOfRows = 0;
for (int i = 0; i < m_sequenceStore->numberOfFunctions(); i++) {
Sequence * sequence = m_sequenceStore->functionAtIndex(i);
for (int i = 0; i < m_sequenceStore->numberOfModels(); i++) {
Sequence * sequence = m_sequenceStore->modelAtIndex(i);
numberOfRows += sequence->numberOfElements();
}
if (m_sequenceStore->numberOfFunctions() == m_sequenceStore->maxNumberOfFunctions()) {
if (m_sequenceStore->numberOfModels() == m_sequenceStore->maxNumberOfModels()) {
return numberOfRows;
}
return 1 + numberOfRows;
};
KDCoordinate ListController::rowHeight(int j) {
if (m_sequenceStore->numberOfFunctions() < m_sequenceStore->maxNumberOfFunctions() && j == numberOfRows() - 1) {
KDCoordinate ListController::expressionRowHeight(int j) {
if (m_sequenceStore->numberOfModels() < m_sequenceStore->maxNumberOfModels() && j == numberOfRows() - 1) {
return Metric::StoreRowHeight;
}
Sequence * sequence = m_sequenceStore->functionAtIndex(functionIndexForRow(j));
Sequence * sequence = m_sequenceStore->modelAtIndex(modelIndexForRow(j));
KDCoordinate defaultHeight = sequence->type() == Sequence::Type::Explicit ? Metric::StoreRowHeight : k_emptySubRowHeight;
ExpressionLayout * layout = sequence->layout();
if (sequenceDefinitionForRow(j) == 1) {
@@ -72,9 +72,9 @@ KDCoordinate ListController::rowHeight(int j) {
}
void ListController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
Shared::ListController::willDisplayCellAtLocation(cell, i, j);
Shared::FunctionListController::willDisplayCellAtLocation(cell, i, j);
EvenOddCell * myCell = (EvenOddCell *)cell;
myCell->setEven(functionIndexForRow(j)%2 == 0);
myCell->setEven(modelIndexForRow(j)%2 == 0);
}
void ListController::selectPreviousNewSequenceCell() {
@@ -87,7 +87,7 @@ Toolbox * ListController::toolboxForSender(Responder * sender) {
// Set extra cells
int recurrenceDepth = -1;
int sequenceDefinition = sequenceDefinitionForRow(selectedRow());
Sequence * sequence = m_sequenceStore->functionAtIndex(functionIndexForRow(selectedRow()));
Sequence * sequence = m_sequenceStore->modelAtIndex(modelIndexForRow(selectedRow()));
if (sequenceDefinition == 0) {
recurrenceDepth = sequence->numberOfElements()-1;
}
@@ -154,8 +154,8 @@ void ListController::editExpression(Sequence * sequence, int sequenceDefinition,
}
}
bool ListController::removeFunctionRow(Function * function) {
m_functionStore->removeFunction(function);
bool ListController::removeModelRow(ExpressionModel * model) {
Shared::FunctionListController::removeModelRow(model);
// Invalidate the sequences context cache
static_cast<App *>(app())->localContext()->resetCache();
return true;
@@ -182,7 +182,7 @@ HighlightCell * ListController::expressionCells(int index) {
void ListController::willDisplayTitleCellAtIndex(HighlightCell * cell, int j) {
SequenceTitleCell * myCell = (SequenceTitleCell *)cell;
Sequence * sequence = m_sequenceStore->functionAtIndex(functionIndexForRow(j));
Sequence * sequence = m_sequenceStore->modelAtIndex(modelIndexForRow(j));
if (sequenceDefinitionForRow(j) == 0) {
myCell->setExpressionLayout(sequence->definitionName());
}
@@ -198,7 +198,7 @@ void ListController::willDisplayTitleCellAtIndex(HighlightCell * cell, int j) {
void ListController::willDisplayExpressionCellAtIndex(HighlightCell * cell, int j) {
FunctionExpressionCell * myCell = (FunctionExpressionCell *)cell;
Sequence * sequence = m_sequenceStore->functionAtIndex(functionIndexForRow(j));
Sequence * sequence = m_sequenceStore->modelAtIndex(modelIndexForRow(j));
if (sequenceDefinitionForRow(j) == 0) {
myCell->setExpressionLayout(sequence->layout());
}
@@ -213,45 +213,32 @@ void ListController::willDisplayExpressionCellAtIndex(HighlightCell * cell, int
myCell->setTextColor(textColor);
}
int ListController::functionIndexForRow(int j) {
int ListController::modelIndexForRow(int j) {
if (j < 0) {
return j;
}
if (m_sequenceStore->numberOfFunctions() < m_sequenceStore->maxNumberOfFunctions() &&
j == numberOfRows() - 1) {
return functionIndexForRow(j-1)+1;
if (isAddEmptyRow(j)) {
return modelIndexForRow(j-1)+1;
}
int rowIndex = 0;
int sequenceIndex = -1;
do {
sequenceIndex++;
Sequence * sequence = m_sequenceStore->functionAtIndex(sequenceIndex);
Sequence * sequence = m_sequenceStore->modelAtIndex(sequenceIndex);
rowIndex += sequence->numberOfElements();
} while (rowIndex <= j);
return sequenceIndex;
}
const char * ListController::textForRow(int j) {
Sequence * sequence = (Sequence *)m_functionStore->functionAtIndex(functionIndexForRow(j));
switch (sequenceDefinitionForRow(j)) {
case 0:
return sequence->text();
case 1:
return sequence->firstInitialConditionText();
case 2:
return sequence->secondInitialConditionText();
default:
assert(false);
return nullptr;
}
bool ListController::isAddEmptyRow(int j) {
return m_sequenceStore->numberOfModels() < m_sequenceStore->maxNumberOfModels() && j == numberOfRows() - 1;
}
int ListController::sequenceDefinitionForRow(int j) {
if (j < 0) {
return j;
}
if (m_sequenceStore->numberOfFunctions() < m_sequenceStore->maxNumberOfFunctions() &&
j == numberOfRows() - 1) {
if (isAddEmptyRow(j)) {
return 0;
}
int rowIndex = 0;
@@ -259,25 +246,25 @@ int ListController::sequenceDefinitionForRow(int j) {
Sequence * sequence = nullptr;
do {
sequenceIndex++;
sequence = m_sequenceStore->functionAtIndex(sequenceIndex);
sequence = m_sequenceStore->modelAtIndex(sequenceIndex);
rowIndex += sequence->numberOfElements();
} while (rowIndex <= j);
return sequence->numberOfElements()-rowIndex+j;
}
void ListController::addEmptyFunction() {
void ListController::addEmptyModel() {
app()->displayModalViewController(&m_typeStackController, 0.f, 0.f, Metric::TabHeight+Metric::ModalTopMargin, Metric::CommonRightMargin, Metric::ModalBottomMargin, Metric::CommonLeftMargin);
}
void ListController::editExpression(Shared::Function * function, Ion::Events::Event event) {
Sequence * sequence = (Sequence *)function;
void ListController::editExpression(Shared::ExpressionModel * model, Ion::Events::Event event) {
Sequence * sequence = static_cast<Sequence *>(model);
editExpression(sequence, sequenceDefinitionForRow(selectedRow()), event);
}
void ListController::reinitExpression(Shared::Function * function) {
void ListController::reinitExpression(Shared::ExpressionModel * model) {
// Invalidate the sequences context cache
static_cast<App *>(app())->localContext()->resetCache();
Sequence * sequence = (Sequence *)function;
Sequence * sequence = static_cast<Sequence *>(model);
switch (sequenceDefinitionForRow(selectedRow())) {
case 1:
if (strlen(sequence->firstInitialConditionText()) == 0) {
@@ -305,8 +292,9 @@ View * ListController::loadView() {
for (int i = 0; i < k_maxNumberOfRows; i++) {
m_sequenceTitleCells[i] = new SequenceTitleCell(FunctionTitleCell::Orientation::VerticalIndicator);
m_expressionCells[i] = new FunctionExpressionCell();
m_expressionCells[i]->setMargin(k_expressionMargin);
}
return Shared::ListController::loadView();
return Shared::FunctionListController::loadView();
}
void ListController::unloadView(View * view) {
@@ -316,7 +304,7 @@ void ListController::unloadView(View * view) {
delete m_expressionCells[i];
m_expressionCells[i] = nullptr;
}
Shared::ListController::unloadView(view);
Shared::FunctionListController::unloadView(view);
}
}

View File

@@ -5,8 +5,7 @@
#include "../sequence_title_cell.h"
#include "../sequence_store.h"
#include "../../shared/function_expression_cell.h"
#include "../../shared/list_controller.h"
#include "../../shared/new_function_cell.h"
#include "../../shared/function_list_controller.h"
#include "../../shared/expression_layout_field_delegate.h"
#include "../../shared/text_field_delegate.h"
#include "list_parameter_controller.h"
@@ -15,34 +14,34 @@
namespace Sequence {
class ListController : public Shared::ListController, public Shared::TextFieldDelegate, public Shared::ExpressionLayoutFieldDelegate {
class ListController : public Shared::FunctionListController, public Shared::TextFieldDelegate, public Shared::ExpressionLayoutFieldDelegate {
public:
ListController(Responder * parentResponder, SequenceStore * sequenceStore, ButtonRowController * header, ButtonRowController * footer);
const char * title() override;
int numberOfRows() override;
virtual KDCoordinate rowHeight(int j) override;
int numberOfExpressionRows() override;
KDCoordinate expressionRowHeight(int j) override;
void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override;
Toolbox * toolboxForTextInput(TextInput * textInput) override;
Toolbox * toolboxForExpressionLayoutField(ExpressionLayoutField * expressionLayoutField) override;
void selectPreviousNewSequenceCell();
void editExpression(Sequence * sequence, int sequenceDefinitionIndex, Ion::Events::Event event);
private:
Toolbox * toolboxForSender(Responder * sender);
Shared::TextFieldDelegateApp * textFieldDelegateApp() override;
Shared::ExpressionFieldDelegateApp * expressionFieldDelegateApp() override;
void editExpression(Sequence * sequence, int sequenceDefinitionIndex, Ion::Events::Event event);
ListParameterController * parameterController() override;
int maxNumberOfRows() override;
HighlightCell * titleCells(int index) override;
HighlightCell * expressionCells(int index) override;
void willDisplayTitleCellAtIndex(HighlightCell * cell, int j) override;
void willDisplayExpressionCellAtIndex(HighlightCell * cell, int j) override;
int functionIndexForRow(int j) override;
const char * textForRow(int j) override;
int modelIndexForRow(int j) override;
bool isAddEmptyRow(int j) override;
int sequenceDefinitionForRow(int j);
void addEmptyFunction() override;
void editExpression(Shared::Function * function, Ion::Events::Event event) override;
bool removeFunctionRow(Shared::Function * function) override;
void reinitExpression(Shared::Function * function) override;
void addEmptyModel() override;
void reinitExpression(Shared::ExpressionModel * model) override;
void editExpression(Shared::ExpressionModel * model, Ion::Events::Event event) override;
bool removeModelRow(Shared::ExpressionModel * model) override;
View * loadView() override;
void unloadView(View * view) override;
static constexpr KDCoordinate k_emptySubRowHeight = 30;

View File

@@ -61,8 +61,8 @@ bool ListParameterController::handleEvent(Ion::Events::Event event) {
#else
if (selectedRowIndex == 2+hasAdditionalRow) {
#endif
if (m_functionStore->numberOfFunctions() > 0) {
m_functionStore->removeFunction(m_function);
if (m_functionStore->numberOfModels() > 0) {
m_functionStore->removeModel(m_function);
static_cast<App *>(app())->localContext()->resetCache();
StackViewController * stack = (StackViewController *)(parentResponder());
stack->pop();

View File

@@ -82,9 +82,10 @@ bool TypeParameterController::handleEvent(Ion::Events::Event event) {
stack->pop();
return true;
}
Sequence * newSequence = m_sequenceStore->addEmptyFunction();
Sequence * newSequence = static_cast<Sequence *>(m_sequenceStore->addEmptyModel());
newSequence->setType((Sequence::Type)selectedRow());
app()->dismissModalViewController();
m_listController->editExpression(newSequence, 0, Ion::Events::OK);
return true;
}
if (event == Ion::Events::Left && m_sequence) {

View File

@@ -185,10 +185,6 @@ Poincare::ExpressionLayout * Sequence::secondInitialConditionLayout() {
return m_secondInitialConditionLayout;
}
void Sequence::setContent(const char * c) {
Function::setContent(c);
}
void Sequence::setFirstInitialConditionContent(const char * c) {
strlcpy(m_firstInitialConditionText, c, sizeof(m_firstInitialConditionText));
if (m_firstInitialConditionExpression != nullptr) {
@@ -236,19 +232,19 @@ Poincare::ExpressionLayout * Sequence::definitionName() {
if (m_type == Type::Explicit) {
m_definitionName = new HorizontalLayout(
new CharLayout(name()[0], KDText::FontSize::Large),
new VerticalOffsetLayout(LayoutEngine::createStringLayout("n ", 2, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false),
new VerticalOffsetLayout(LayoutEngine::createStringLayout("n", 1, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false),
false);
}
if (m_type == Type::SingleRecurrence) {
m_definitionName = new HorizontalLayout(
new CharLayout(name()[0], KDText::FontSize::Large),
new VerticalOffsetLayout(LayoutEngine::createStringLayout("n+1 ", 4, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false),
new VerticalOffsetLayout(LayoutEngine::createStringLayout("n+1", 3, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false),
false);
}
if (m_type == Type::DoubleRecurrence) {
m_definitionName = new HorizontalLayout(
new CharLayout(name()[0], KDText::FontSize::Large),
new VerticalOffsetLayout(LayoutEngine::createStringLayout("n+2 ", 4, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false),
new VerticalOffsetLayout(LayoutEngine::createStringLayout("n+2", 3, KDText::FontSize::Small), VerticalOffsetLayout::Type::Subscript, false),
false);
}
}
@@ -340,9 +336,7 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const {
{
ctx.setValueForSymbol(un, &unSymbol);
ctx.setValueForSymbol(vn, &vnSymbol);
Poincare::Complex<T> e = Poincare::Complex<T>::Float(n);
ctx.setExpressionForSymbolName(&e, &nSymbol, *sqctx);
return expression(sqctx)->template approximateToScalar<T>(ctx);
return expression(sqctx)->approximateWithValueForSymbol(symbol(), (T)n, ctx);
}
case Type::SingleRecurrence:
{
@@ -353,9 +347,7 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const {
ctx.setValueForSymbol(unm1, &unSymbol);
ctx.setValueForSymbol(vn, &vn1Symbol);
ctx.setValueForSymbol(vnm1, &vnSymbol);
Poincare::Complex<T> e = Poincare::Complex<T>::Float(n-1);
ctx.setExpressionForSymbolName(&e, &nSymbol, *sqctx);
return expression(sqctx)->template approximateToScalar<T>(ctx);
return expression(sqctx)->approximateWithValueForSymbol(symbol(), (T)(n-1), ctx);
}
default:
{
@@ -369,9 +361,7 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const {
ctx.setValueForSymbol(unm2, &unSymbol);
ctx.setValueForSymbol(vnm1, &vn1Symbol);
ctx.setValueForSymbol(vnm2, &vnSymbol);
Poincare::Complex<T> e = Poincare::Complex<T>::Float(n-2);
ctx.setExpressionForSymbolName(&e, &nSymbol, *sqctx);
return expression(sqctx)->template approximateToScalar<T>(ctx);
return expression(sqctx)->approximateWithValueForSymbol(symbol(), (T)(n-2), ctx);
}
}
}

View File

@@ -17,7 +17,7 @@ public:
Sequence(const char * text = nullptr, KDColor color = KDColorBlack);
~Sequence();
Sequence& operator=(const Sequence& other);
Sequence& operator=(Sequence&& other) = delete;
//Sequence& operator=(Sequence&& other) = delete;
Sequence(const Sequence& other) = delete;
Sequence(Sequence&& other) = delete;
uint32_t checksum() override;
@@ -36,7 +36,6 @@ public:
* invalidate the cache because the sequences evaluations might have changed. */
void setType(Type type);
void setInitialRank(int rank);
void setContent(const char * c) override;
void setFirstInitialConditionContent(const char * c);
void setSecondInitialConditionContent(const char * c);
int numberOfElements();

View File

@@ -49,9 +49,9 @@ void TemplatedSequenceContext<T>::step(SequenceStore * sequenceStore, SequenceCo
}
/* Evaluate new u(n) and v(n) */
Sequence * u = sequenceStore->numberOfFunctions() > 0 ? sequenceStore->functionAtIndex(0) : nullptr;
Sequence * u = sequenceStore->numberOfModels() > 0 ? sequenceStore->modelAtIndex(0) : nullptr;
u = u && u->isDefined() ? u : nullptr;
Sequence * v = sequenceStore->numberOfFunctions() > 1 ? sequenceStore->functionAtIndex(1) : nullptr;
Sequence * v = sequenceStore->numberOfModels() > 1 ? sequenceStore->modelAtIndex(1) : nullptr;
v = v && v->isDefined() ? v : nullptr;
/* Switch u & v if the name of u is v */
if (u != nullptr && u->name()[0] == SequenceStore::k_sequenceNames[1][0]) {

View File

@@ -20,90 +20,24 @@ uint32_t SequenceStore::storeChecksum() {
return Ion::crc32((uint32_t *)checksums, dataLengthInBytes/sizeof(uint32_t));
}
Sequence * SequenceStore::functionAtIndex(int i) {
assert(i>=0 && i<m_numberOfFunctions);
return &m_sequences[i];
}
Sequence * SequenceStore::activeFunctionAtIndex(int i) {
return (Sequence *)Shared::FunctionStore::activeFunctionAtIndex(i);
}
Sequence * SequenceStore::definedFunctionAtIndex(int i) {
return (Sequence *)Shared::FunctionStore::definedFunctionAtIndex(i);
}
Sequence * SequenceStore::addEmptyFunction() {
assert(m_numberOfFunctions < MaxNumberOfSequences);
const char * name = firstAvailableName();
KDColor color = firstAvailableColor();
Sequence addedSequence(name, color);
m_sequences[m_numberOfFunctions] = addedSequence;
Sequence * result = &m_sequences[m_numberOfFunctions];
m_numberOfFunctions++;
return result;
}
void SequenceStore::removeFunction(Shared::Function * f) {
int i = 0;
while (&m_sequences[i] != f && i < m_numberOfFunctions) {
i++;
}
assert(i>=0 && i<m_numberOfFunctions);
m_numberOfFunctions--;
for (int j = i; j<m_numberOfFunctions; j++) {
m_sequences[j] = m_sequences[j+1];
}
Sequence emptySequence("", KDColorBlack);
m_sequences[m_numberOfFunctions] = emptySequence;
}
int SequenceStore::maxNumberOfFunctions() {
return MaxNumberOfSequences;
}
char SequenceStore::symbol() const {
return 'n';
}
const char * SequenceStore::firstAvailableName() {
for (int k = 0; k < MaxNumberOfSequences; k++) {
int j = 0;
while (j < m_numberOfFunctions) {
if (m_sequences[j].name() == k_sequenceNames[k]) {
break;
}
j++;
}
if (j == m_numberOfFunctions) {
return k_sequenceNames[k];
}
}
return k_sequenceNames[0];
Sequence * SequenceStore::emptyModel() {
static Sequence addedSequence("", KDColorBlack);
addedSequence = Sequence(firstAvailableName(), firstAvailableColor());
return &addedSequence;
}
const KDColor SequenceStore::firstAvailableColor() {
for (int k = 0; k < MaxNumberOfSequences; k++) {
int j = 0;
while (j < m_numberOfFunctions) {
if (m_sequences[j].color() == k_defaultColors[k]) {
break;
}
j++;
}
if (j == m_numberOfFunctions) {
return k_defaultColors[k];
}
}
return k_defaultColors[0];
Sequence * SequenceStore::nullModel() {
static Sequence emptyFunction("", KDColorBlack);
return &emptyFunction;
}
void SequenceStore::removeAll() {
for (int i = 0; i < m_numberOfFunctions; i++) {
Sequence emptySequence("", KDColorBlack);
m_sequences[i] = emptySequence;
}
m_numberOfFunctions = 0;
void SequenceStore::setModelAtIndex(Shared::ExpressionModel * f, int i) {
assert(i>=0 && i<m_numberOfModels);
m_sequences[i] = *(static_cast<Sequence *>(f));
}
}

View File

@@ -12,26 +12,33 @@ class SequenceStore : public Shared::FunctionStore {
public:
using Shared::FunctionStore::FunctionStore;
uint32_t storeChecksum() override;
Sequence * functionAtIndex(int i) override;
Sequence * activeFunctionAtIndex(int i) override;
Sequence * definedFunctionAtIndex(int i) override;
Sequence * addEmptyFunction() override;
/* WARNING: after calling removeFunction or removeAll, the sequence context
Sequence * modelAtIndex(int i) override {
assert(i>=0 && i<m_numberOfModels);
return &m_sequences[i];
}
Sequence * activeFunctionAtIndex(int i) override { return (Sequence *)Shared::FunctionStore::activeFunctionAtIndex(i); }
Sequence * definedFunctionAtIndex(int i) override { return (Sequence *)Shared::FunctionStore::definedFunctionAtIndex(i); }
/* WARNING: after calling removeModel or removeAll, the sequence context
* need to invalidate its cache as the sequences evaluations might have
* changed */
void removeFunction(Shared::Function * f) override;
void removeAll() override;
int maxNumberOfFunctions() override;
const char * firstAvailableName() override;
int maxNumberOfModels() const override { return MaxNumberOfSequences; }
char symbol() const override;
static constexpr const char * k_sequenceNames[MaxNumberOfSequences] = {
"u", "v"//, "w"
};
const char * firstAvailableName() override {
return firstAvailableAttribute(k_sequenceNames, FunctionStore::name);
}
private:
const KDColor firstAvailableColor() override;
Sequence * emptyModel() override;
Sequence * nullModel() override;
void setModelAtIndex(Shared::ExpressionModel * f, int i) override;
static constexpr KDColor k_defaultColors[MaxNumberOfSequences] = {
Palette::Red, Palette::Blue//, Palette::YellowDark
};
const KDColor firstAvailableColor() override {
return firstAvailableAttribute(k_defaultColors, FunctionStore::color);
}
Sequence m_sequences[MaxNumberOfSequences];
};

View File

@@ -43,7 +43,7 @@ View * SequenceTitleCell::subviewAtIndex(int index) {
void SequenceTitleCell::layoutSubviews() {
KDRect textFrame(0, k_colorIndicatorThickness, bounds().width(), bounds().height() - k_colorIndicatorThickness);
if (m_orientation == Orientation::VerticalIndicator){
textFrame = KDRect(k_colorIndicatorThickness, 0, bounds().width() - k_colorIndicatorThickness, bounds().height()-k_separatorThickness);
textFrame = KDRect(k_colorIndicatorThickness, 0, bounds().width() - k_colorIndicatorThickness-k_separatorThickness, bounds().height()-k_separatorThickness);
}
m_titleTextView.setFrame(textFrame);
}

View File

@@ -12,10 +12,13 @@ public:
void setEven(bool even) override;
void setHighlighted(bool highlight) override;
void setColor(KDColor color) override;
Poincare::ExpressionLayout * expressionLayout() const override {
return m_titleTextView.expressionLayout();
}
private:
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
private:
EvenOddExpressionCell m_titleTextView;
};

View File

@@ -17,7 +17,7 @@ void check_sequences_defined_by(double result[2][10], Sequence::Type typeU, cons
Sequence * u = nullptr;
Sequence * v = nullptr;
if (definitionU) {
u = store.addEmptyFunction();
u = static_cast<Sequence *>(store.addEmptyModel());
assert(u->name()[0] == 'u');
u->setType(typeU);
u->setContent(definitionU);
@@ -29,14 +29,14 @@ void check_sequences_defined_by(double result[2][10], Sequence::Type typeU, cons
}
}
if (definitionV) {
if (store.numberOfFunctions() == 0) {
Sequence * tempU = store.addEmptyFunction();
v = store.addEmptyFunction();
store.removeFunction(tempU);
v = store.functionAtIndex(0);
if (store.numberOfModels() == 0) {
Sequence * tempU = static_cast<Sequence *>(store.addEmptyModel());
v = static_cast<Sequence *>(store.addEmptyModel());
store.removeModel(tempU);
v = store.modelAtIndex(0);
} else {
assert(store.numberOfFunctions() == 1);
v = store.addEmptyFunction();
assert(store.numberOfModels() == 1);
v = static_cast<Sequence *>(store.addEmptyModel());
}
v->setType(typeV);
v->setContent(definitionV);

View File

@@ -36,7 +36,7 @@ void ValuesController::willDisplayCellAtLocation(HighlightCell * cell, int i, in
}
I18n::Message ValuesController::emptyMessage() {
if (m_sequenceStore->numberOfDefinedFunctions() == 0) {
if (m_sequenceStore->numberOfDefinedModels() == 0) {
return I18n::Message::NoSequence;
}
return I18n::Message::NoActivatedSequence;

View File

@@ -11,6 +11,7 @@ DataTab = "Daten"
DefaultSetting = "Grundeinstellungen"
Deg = "gra"
DisplayValues = "Werte anzeigen"
Empty = "Leer"
ExitExamMode1 = "Wollen Sie den Testmodus "
ExitExamMode2 = "verlassen?"
ForbiddenValue = "Verbotener Wert"

View File

@@ -11,6 +11,7 @@ DataTab = "Data"
DefaultSetting = "Basic settings"
Deg = "deg"
DisplayValues = "Display values"
Empty = "Empty"
ExitExamMode1 = "Exit the exam "
ExitExamMode2 = "mode?"
ForbiddenValue = "Forbidden value"

View File

@@ -11,6 +11,7 @@ DataTab = "Datos"
DefaultSetting = "Ajustes basicos"
Deg = "gra"
DisplayValues = "Visualizar los valores"
Empty = "Vacio"
ExitExamMode1 = "Salir del modo "
ExitExamMode2 = "examen ?"
ForbiddenValue = "Valor prohibido"

View File

@@ -11,6 +11,7 @@ DataTab = "Donnees"
DefaultSetting = "Reglages de base"
Deg = "deg"
DisplayValues = "Afficher les valeurs"
Empty = "Vide"
ExitExamMode1 = "Voulez-vous sortir "
ExitExamMode2 = "du mode examen ?"
ForbiddenValue = "Valeur interdite"

View File

@@ -11,6 +11,7 @@ DataTab = "Dados"
DefaultSetting = "Configuracoes basicas"
Deg = "gra"
DisplayValues = "Exibir os valores"
Empty = "Vacuo"
ExitExamMode1 = "Voce quer sair do modo de "
ExitExamMode2 = "exame ?"
ForbiddenValue = "Valor proibida"

View File

@@ -19,6 +19,7 @@ DeterminantCommandWithArg = "det(M)"
DiffCommandWithArg = "diff(f(x),a)"
DimensionCommandWithArg = "dim(M)"
DiscreteLegend = "P(X="
DiscriminantFormulaDegree2 = "Δ=b^2-4ac"
Equal = "="
FactorCommandWithArg = "factor(n)"
FccId = "FCC ID"

View File

@@ -7,17 +7,21 @@ app_objs += $(addprefix apps/shared/,\
curve_view_range.o\
editable_cell_table_view_controller.o\
expression_field_delegate_app.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\
function_banner_delegate.o\
function_curve_parameter_controller.o\
function_expression_cell.o\
function_go_to_parameter_controller.o\
function_graph_view.o\
function_graph_controller.o\
function_list_controller.o\
function_store.o\
function_expression_cell.o\
function_title_cell.o\
go_to_parameter_controller.o\
initialisation_parameter_controller.o\
@@ -27,16 +31,16 @@ app_objs += $(addprefix apps/shared/,\
interval.o\
interval_parameter_controller.o\
language_controller.o\
list_controller.o\
list_parameter_controller.o\
memoized_curve_view_range.o\
message_view.o\
new_function_cell.o\
ok_view.o\
parameter_text_field_delegate.o\
range_parameter_controller.o\
regular_table_view_data_source.o\
round_cursor_view.o\
scrollable_exact_approximate_expressions_cell.o\
scrollable_exact_approximate_expressions_view.o\
simple_interactive_curve_view_controller.o\
expression_layout_field_delegate.o\
store_controller.o\

View File

@@ -0,0 +1,87 @@
#include "function.h"
#include <string.h>
#include <cmath>
#include <assert.h>
using namespace Poincare;
namespace Shared {
ExpressionModel::ExpressionModel() :
m_text{0},
m_expression(nullptr),
m_layout(nullptr)
{
}
ExpressionModel::~ExpressionModel() {
if (m_layout != nullptr) {
delete m_layout;
m_layout = nullptr;
}
if (m_expression != nullptr) {
delete m_expression;
m_expression = nullptr;
}
}
ExpressionModel& ExpressionModel::operator=(const ExpressionModel& other) {
// Self-assignment is benign
setContent(other.m_text);
return *this;
}
const char * ExpressionModel::text() const {
return m_text;
}
Poincare::Expression * ExpressionModel::expression(Poincare::Context * context) const {
if (m_expression == nullptr) {
m_expression = Expression::ParseAndSimplify(m_text, *context);
}
return m_expression;
}
Poincare::ExpressionLayout * ExpressionModel::layout() {
if (m_layout == nullptr) {
Expression * nonSimplifiedExpression = Expression::parse(m_text);
if (nonSimplifiedExpression != nullptr) {
m_layout = nonSimplifiedExpression->createLayout(PrintFloat::Mode::Decimal);
delete nonSimplifiedExpression;
}
}
return m_layout;
}
bool ExpressionModel::isDefined() {
return m_text[0] != 0;
}
bool ExpressionModel::isEmpty() {
return m_text[0] == 0;
}
void ExpressionModel::setContent(const char * c) {
strlcpy(m_text, c, sizeof(m_text));
if (m_layout != nullptr) {
delete m_layout;
m_layout = nullptr;
}
if (m_expression != nullptr) {
delete m_expression;
m_expression = nullptr;
}
}
void ExpressionModel::tidy() {
if (m_layout != nullptr) {
delete m_layout;
m_layout = nullptr;
}
if (m_expression != nullptr) {
delete m_expression;
m_expression = nullptr;
}
}
}

View File

@@ -0,0 +1,39 @@
#ifndef SHARED_EXPRESSION_MODEL_H
#define SHARED_EXPRESSION_MODEL_H
#include <poincare.h>
#include <kandinsky.h>
#include <escher.h>
namespace Shared {
class ExpressionModel {
public:
ExpressionModel();
virtual ~ExpressionModel(); // Delete expression and layout, if needed
ExpressionModel& operator=(const ExpressionModel& other);
ExpressionModel& operator=(ExpressionModel&& other) = delete;
ExpressionModel(const ExpressionModel& other) = delete;
ExpressionModel(ExpressionModel&& other) = delete;
const char * text() const;
Poincare::Expression * expression(Poincare::Context * context) const;
Poincare::ExpressionLayout * layout();
virtual bool isDefined();
virtual bool isEmpty();
virtual bool shouldBeClearedBeforeRemove() {
return !isEmpty();
}
virtual void setContent(const char * c);
virtual void tidy();
constexpr static int k_expressionBufferSize = TextField::maxBufferSize();
private:
constexpr static size_t k_dataLengthInBytes = (TextField::maxBufferSize())*sizeof(char);
static_assert((k_dataLengthInBytes & 0x3) == 0, "The expression model data size is not a multiple of 4 bytes (cannot compute crc)"); // Assert that dataLengthInBytes is a multiple of 4
char m_text[k_expressionBufferSize];
mutable Poincare::Expression * m_expression;
mutable Poincare::ExpressionLayout * m_layout;
};
}
#endif

View File

@@ -0,0 +1,133 @@
#include "expression_model_list_controller.h"
#include <assert.h>
namespace Shared {
ExpressionModelListController::ExpressionModelListController(Responder * parentResponder, I18n::Message text) :
DynamicViewController(parentResponder),
m_addNewMessage(text),
m_addNewModel(nullptr)
{
}
/* Table Data Source */
int ExpressionModelListController::numberOfExpressionRows() {
if (modelStore()->numberOfModels() == modelStore()->maxNumberOfModels()) {
return modelStore()->numberOfModels();
}
return 1 + modelStore()->numberOfModels();
}
KDCoordinate ExpressionModelListController::expressionRowHeight(int j) {
if (isAddEmptyRow(j)) {
return Metric::StoreRowHeight;
}
ExpressionModel * m = modelStore()->modelAtIndex(j);
if (m->layout() == nullptr) {
return Metric::StoreRowHeight;
}
KDCoordinate modelSize = m->layout()->size().height();
return modelSize + Metric::StoreRowHeight - KDText::charSize().height();
}
void ExpressionModelListController::willDisplayExpressionCellAtIndex(HighlightCell * cell, int j) {
EvenOddExpressionCell * myCell = (EvenOddExpressionCell *)cell;
ExpressionModel * m = modelStore()->modelAtIndex(j);
myCell->setExpressionLayout(m->layout());
}
/* Responder */
bool ExpressionModelListController::handleEventOnExpression(Ion::Events::Event event) {
if (selectedRow() < 0) {
return false;
}
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
if (isAddEmptyRow(selectedRow())) {
addEmptyModel();
return true;
}
ExpressionModel * model = modelStore()->modelAtIndex(modelIndexForRow(selectedRow()));
editExpression(model, event);
return true;
}
if (event == Ion::Events::Backspace && !isAddEmptyRow(selectedRow())) {
ExpressionModel * model = modelStore()->modelAtIndex(modelIndexForRow(selectedRow()));
if (model->shouldBeClearedBeforeRemove()) {
reinitExpression(model);
} else {
if (removeModelRow(model)) {
int newSelectedRow = selectedRow() >= numberOfExpressionRows() ? numberOfExpressionRows()-1 : selectedRow();
selectCellAtLocation(selectedColumn(), newSelectedRow);
selectableTableView()->reloadData();
}
}
return true;
}
if ((event.hasText() || event == Ion::Events::XNT || event == Ion::Events::Paste || event == Ion::Events::Toolbox || event == Ion::Events::Var)
&& !isAddEmptyRow(selectedRow())) {
ExpressionModel * model = modelStore()->modelAtIndex(modelIndexForRow(selectedRow()));
editExpression(model, event);
return true;
}
return false;
}
void ExpressionModelListController::addEmptyModel() {
ExpressionModel * e = modelStore()->addEmptyModel();
selectableTableView()->reloadData();
editExpression(e, Ion::Events::OK);
}
void ExpressionModelListController::reinitExpression(ExpressionModel * model) {
model->setContent("");
selectableTableView()->reloadData();
}
void ExpressionModelListController::editExpression(ExpressionModel * model, Ion::Events::Event event) {
char * initialText = nullptr;
char initialTextContent[TextField::maxBufferSize()];
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
strlcpy(initialTextContent, model->text(), sizeof(initialTextContent));
initialText = initialTextContent;
}
inputController()->edit(this, event, model, initialText,
[](void * context, void * sender){
ExpressionModel * myModel = static_cast<ExpressionModel *>(context);
InputViewController * myInputViewController = (InputViewController *)sender;
const char * textBody = myInputViewController->textBody();
myModel->setContent(textBody);
},
[](void * context, void * sender){
});
}
bool ExpressionModelListController::removeModelRow(ExpressionModel * model) {
modelStore()->removeModel(model);
return true;
}
int ExpressionModelListController::modelIndexForRow(int j) {
return j;
}
bool ExpressionModelListController::isAddEmptyRow(int j) {
return j == modelStore()->numberOfModels();
}
SelectableTableView * ExpressionModelListController::selectableTableView() {
return (SelectableTableView *)view();
}
void ExpressionModelListController::loadAddModelCell() {
m_addNewModel = new EvenOddMessageTextCell();
m_addNewModel->setMessage(m_addNewMessage);
}
void ExpressionModelListController::unloadAddModelCell() {
delete m_addNewModel;
m_addNewModel = nullptr;
}
}

View File

@@ -0,0 +1,39 @@
#ifndef SHARED_EXPRESSION_MODEL_LIST_CONTROLLER_H
#define SHARED_EXPRESSION_MODEL_LIST_CONTROLLER_H
#include <escher.h>
#include "expression_model_store.h"
#include "../i18n.h"
namespace Shared {
class ExpressionModelListController : public DynamicViewController, public SelectableTableViewDataSource {
public:
ExpressionModelListController(Responder * parentResponder, I18n::Message text);
protected:
static constexpr KDCoordinate k_expressionMargin = 5;
/* Table Data Source */
virtual int numberOfExpressionRows();
virtual KDCoordinate expressionRowHeight(int j);
virtual void willDisplayExpressionCellAtIndex(HighlightCell * cell, int j);
/* Responder */
bool handleEventOnExpression(Ion::Events::Event event);
virtual void addEmptyModel();
virtual void reinitExpression(ExpressionModel * model);
virtual void editExpression(ExpressionModel * model, Ion::Events::Event event);
virtual bool removeModelRow(ExpressionModel * function);
virtual int modelIndexForRow(int j);
virtual bool isAddEmptyRow(int j);
/* Dynamic View Controller */
virtual SelectableTableView * selectableTableView();
void loadAddModelCell();
void unloadAddModelCell();
virtual ExpressionModelStore * modelStore() = 0;
virtual InputViewController * inputController() = 0;
I18n::Message m_addNewMessage;
EvenOddMessageTextCell * m_addNewModel;
};
}
#endif

View File

@@ -0,0 +1,69 @@
#include "expression_model_store.h"
#include "function.h"
#include <assert.h>
namespace Shared {
ExpressionModelStore::ExpressionModelStore() :
m_numberOfModels(0)
{
}
ExpressionModel * ExpressionModelStore::addEmptyModel() {
assert(m_numberOfModels < maxNumberOfModels());
setModelAtIndex(emptyModel(), m_numberOfModels++);
return modelAtIndex(m_numberOfModels-1);
}
void ExpressionModelStore::removeModel(ExpressionModel * f) {
int i = 0;
while (modelAtIndex(i) != f && i < m_numberOfModels) {
i++;
}
assert(i>=0 && i<m_numberOfModels);
for (int j = i; j<m_numberOfModels-1; j++) {
setModelAtIndex(modelAtIndex(j+1), j);
}
setModelAtIndex(nullModel(), m_numberOfModels-1);
m_numberOfModels--;
}
void ExpressionModelStore::removeAll() {
for (int i = 0; i < m_numberOfModels; i++) {
setModelAtIndex(nullModel(), i);
}
m_numberOfModels = 0;
}
ExpressionModel * ExpressionModelStore::definedModelAtIndex(int i) {
assert(i>=0 && i<m_numberOfModels);
int index = 0;
for (int k = 0; k < m_numberOfModels; k++) {
if (modelAtIndex(k)->isDefined()) {
if (i == index) {
return modelAtIndex(k);
}
index++;
}
}
assert(false);
return nullptr;
}
int ExpressionModelStore::numberOfDefinedModels() {
int result = 0;
for (int i = 0; i < m_numberOfModels; i++) {
if (modelAtIndex(i)->isDefined()) {
result++;
}
}
return result;
}
void ExpressionModelStore::tidy() {
for (int i = 0; i < m_numberOfModels; i++) {
modelAtIndex(i)->tidy();
}
}
}

View File

@@ -0,0 +1,33 @@
#ifndef SHARED_EXPRESSION_MODEL_STORE_H
#define SHARED_EXPRESSION_MODEL_STORE_H
#include "expression_model.h"
#include <stdint.h>
namespace Shared {
/* ExpressionModelStore is a dumb class.
* Its only job is to store model */
class ExpressionModelStore {
public:
ExpressionModelStore();
virtual ExpressionModel * modelAtIndex(int i) = 0;
ExpressionModel * addEmptyModel();
void removeModel(ExpressionModel * f);
virtual void removeAll();
int numberOfModels() const { return m_numberOfModels; };
virtual ExpressionModel * definedModelAtIndex(int i);
int numberOfDefinedModels();
virtual int maxNumberOfModels() const = 0;
virtual void tidy();
protected:
virtual ExpressionModel * emptyModel() = 0;
virtual ExpressionModel * nullModel() = 0;
virtual void setModelAtIndex(ExpressionModel * f, int i) = 0;
int m_numberOfModels;
};
}
#endif

View File

@@ -8,89 +8,29 @@ using namespace Poincare;
namespace Shared {
Function::Function(const char * name, KDColor color) :
m_expression(nullptr),
m_text{0},
ExpressionModel(),
m_name(name),
m_color(color),
m_layout(nullptr),
m_active(true)
{
}
Function& Function::operator=(const Function& other) {
// Self-assignment is benign
m_color = other.m_color;
m_name = other.m_name;
m_active = other.m_active;
setContent(other.m_text);
return *this;
}
uint32_t Function::checksum() {
char data[k_dataLengthInBytes/sizeof(char)] = {};
strlcpy(data, m_text, TextField::maxBufferSize());
strlcpy(data, text(), TextField::maxBufferSize());
data[k_dataLengthInBytes-2] = m_name != nullptr ? m_name[0] : 0;
data[k_dataLengthInBytes-1] = m_active ? 1 : 0;
return Ion::crc32((uint32_t *)data, k_dataLengthInBytes/sizeof(uint32_t));
}
void Function::setContent(const char * c) {
strlcpy(m_text, c, sizeof(m_text));
if (m_layout != nullptr) {
delete m_layout;
m_layout = nullptr;
}
if (m_expression != nullptr) {
delete m_expression;
m_expression = nullptr;
}
}
void Function::setColor(KDColor color) {
m_color = color;
}
Function::~Function() {
if (m_layout != nullptr) {
delete m_layout;
m_layout = nullptr;
}
if (m_expression != nullptr) {
delete m_expression;
m_expression = nullptr;
}
}
const char * Function::text() const {
return m_text;
}
const char * Function::name() const {
return m_name;
}
Poincare::Expression * Function::expression(Poincare::Context * context) const {
if (m_expression == nullptr) {
m_expression = Expression::ParseAndSimplify(m_text, *context);
}
return m_expression;
}
Poincare::ExpressionLayout * Function::layout() {
if (m_layout == nullptr) {
Expression * nonSimplifiedExpression = Expression::parse(m_text);
if (nonSimplifiedExpression != nullptr) {
m_layout = nonSimplifiedExpression->createLayout(PrintFloat::Mode::Decimal);
delete nonSimplifiedExpression;
}
}
return m_layout;
}
bool Function::isDefined() {
return m_text[0] != 0;
}
bool Function::isActive() {
return m_active;
}
@@ -99,28 +39,9 @@ void Function::setActive(bool active) {
m_active = active;
}
bool Function::isEmpty() {
return m_text[0] == 0;
}
template<typename T>
T Function::templatedApproximateAtAbscissa(T x, Poincare::Context * context) const {
Poincare::VariableContext<T> variableContext = Poincare::VariableContext<T>(symbol(), context);
Poincare::Symbol xSymbol(symbol());
Poincare::Complex<T> e = Poincare::Complex<T>::Float(x);
variableContext.setExpressionForSymbolName(&e, &xSymbol, variableContext);
return expression(context)->approximateToScalar<T>(variableContext);
}
void Function::tidy() {
if (m_layout != nullptr) {
delete m_layout;
m_layout = nullptr;
}
if (m_expression != nullptr) {
delete m_expression;
m_expression = nullptr;
}
return expression(context)->approximateWithValueForSymbol(symbol(), x, *context);
}
}

View File

@@ -1,31 +1,18 @@
#ifndef SHARED_FUNCTION_H
#define SHARED_FUNCTION_H
#include <poincare.h>
#include <kandinsky.h>
#include <escher.h>
#include "expression_model.h"
namespace Shared {
class Function {
class Function : public ExpressionModel {
public:
Function(const char * name = nullptr, KDColor color = KDColorBlack);
virtual ~Function(); // Delete expression and layout, if needed
Function& operator=(const Function& other);
Function& operator=(Function&& other) = delete;
Function(const Function& other) = delete;
Function(Function&& other) = delete;
virtual uint32_t checksum();
const char * text() const;
const char * name() const;
KDColor color() const { return m_color; }
Poincare::Expression * expression(Poincare::Context * context) const;
Poincare::ExpressionLayout * layout();
virtual bool isDefined();
bool isActive();
void setActive(bool active);
virtual bool isEmpty();
virtual void setContent(const char * c);
void setColor(KDColor m_color);
virtual float evaluateAtAbscissa(float x, Poincare::Context * context) const {
return templatedApproximateAtAbscissa(x, context);
@@ -34,17 +21,13 @@ public:
return templatedApproximateAtAbscissa(x, context);
}
virtual double sumBetweenBounds(double start, double end, Poincare::Context * context) const = 0;
virtual void tidy();
private:
constexpr static size_t k_dataLengthInBytes = (TextField::maxBufferSize()+2)*sizeof(char)+2;
static_assert((k_dataLengthInBytes & 0x3) == 0, "The function data size is not a multiple of 4 bytes (cannot compute crc)"); // Assert that dataLengthInBytes is a multiple of 4
template<typename T> T templatedApproximateAtAbscissa(T x, Poincare::Context * context) const;
virtual char symbol() const = 0;
mutable Poincare::Expression * m_expression;
char m_text[TextField::maxBufferSize()];
const char * m_name;
KDColor m_color;
Poincare::ExpressionLayout * m_layout;
bool m_active;
};

View File

@@ -1,58 +1,28 @@
#include "function_expression_cell.h"
#include <assert.h>
using namespace Poincare;
namespace Shared {
FunctionExpressionCell::FunctionExpressionCell() :
EvenOddCell(),
m_expressionView()
EvenOddExpressionCell()
{
}
void FunctionExpressionCell::setExpressionLayout(ExpressionLayout * expressionLayout) {
m_expressionView.setExpressionLayout(expressionLayout);
}
void FunctionExpressionCell::setTextColor(KDColor textColor) {
m_expressionView.setTextColor(textColor);
}
void FunctionExpressionCell::setEven(bool even) {
EvenOddCell::setEven(even);
m_expressionView.setBackgroundColor(backgroundColor());
}
void FunctionExpressionCell::setHighlighted(bool highlight) {
if (highlight != EvenOddCell::isHighlighted()) {
EvenOddCell::setHighlighted(highlight);
m_expressionView.setBackgroundColor(backgroundColor());
}
}
int FunctionExpressionCell::numberOfSubviews() const {
return 1;
}
View * FunctionExpressionCell::subviewAtIndex(int index) {
assert(index == 0);
return &m_expressionView;
}
void FunctionExpressionCell::layoutSubviews() {
KDRect expressionFrame(k_separatorThickness+k_margin, 0, bounds().width() - k_separatorThickness-k_margin, bounds().height()-k_separatorThickness);
m_expressionView.setFrame(expressionFrame);
KDSize FunctionExpressionCell::minimalSizeForOptimalDisplay() const {
KDSize expressionSize = m_expressionView.minimalSizeForOptimalDisplay();
return KDSize(m_margin+expressionSize.width(), expressionSize.height()+k_separatorThickness);
}
void FunctionExpressionCell::drawRect(KDContext * ctx, KDRect rect) const {
KDColor separatorColor = m_even ? Palette::WallScreen : KDColorWhite;
// Color the vertical separator
ctx->fillRect(KDRect(0, 0, k_separatorThickness, bounds().height()), Palette::GreyBright);
// Color the horizontal separator
ctx->fillRect(KDRect(k_separatorThickness, bounds().height()-k_separatorThickness, bounds().width()-k_separatorThickness, k_separatorThickness), separatorColor);
ctx->fillRect(KDRect(0, bounds().height()-k_separatorThickness, bounds().width(), k_separatorThickness), separatorColor);
// Color the margin
ctx->fillRect(KDRect(k_separatorThickness, 0, k_margin, bounds().height()-k_separatorThickness), backgroundColor());
ctx->fillRect(KDRect(0, 0, m_margin, bounds().height()-k_separatorThickness), backgroundColor());
}
void FunctionExpressionCell::layoutSubviews() {
KDRect expressionFrame(m_margin, 0, bounds().width() - m_margin, bounds().height()-k_separatorThickness);
m_expressionView.setFrame(expressionFrame);
}
}

View File

@@ -2,25 +2,17 @@
#define SHARED_FUNCTION_EXPRESSION_CELL_H
#include <escher.h>
#include "function.h"
namespace Shared {
class FunctionExpressionCell : public EvenOddCell {
class FunctionExpressionCell : public EvenOddExpressionCell {
public:
FunctionExpressionCell();
void setExpressionLayout(Poincare::ExpressionLayout * expressionLayout);
void setTextColor(KDColor color);
void setEven(bool even) override;
void setHighlighted(bool highlight) override;
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
KDSize minimalSizeForOptimalDisplay() const override;
void drawRect(KDContext * ctx, KDRect rect) const override;
void layoutSubviews() override;
private:
constexpr static KDCoordinate k_separatorThickness = 1;
constexpr static KDCoordinate k_margin = 5;
ExpressionView m_expressionView;
};
}

View File

@@ -0,0 +1,232 @@
#include "function_list_controller.h"
#include <assert.h>
namespace Shared {
FunctionListController::FunctionListController(Responder * parentResponder, FunctionStore * functionStore, ButtonRowController * header, ButtonRowController * footer, I18n::Message text) :
ExpressionModelListController(parentResponder, text),
ButtonRowDelegate(header, footer),
m_functionStore(functionStore),
m_emptyCell(nullptr),
m_plotButton(this, I18n::Message::Plot, Invocation([](void * context, void * sender) {
FunctionListController * list = (FunctionListController *)context;
TabViewController * tabController = list->tabController();
tabController->setActiveTab(1);
}, this), KDText::FontSize::Small, Palette::PurpleBright),
m_valuesButton(this, I18n::Message::DisplayValues, Invocation([](void * context, void * sender) {
FunctionListController * list = (FunctionListController *)context;
TabViewController * tabController = list->tabController();
tabController->setActiveTab(2);
}, this), KDText::FontSize::Small, Palette::PurpleBright)
{
}
int FunctionListController::numberOfColumns() {
return 2;
};
KDCoordinate FunctionListController::columnWidth(int i) {
switch (i) {
case 0:
return k_functionNameWidth;
case 1:
return selectableTableView()->bounds().width()-k_functionNameWidth;
default:
assert(false);
return 0;
}
}
KDCoordinate FunctionListController::cumulatedWidthFromIndex(int i) {
switch (i) {
case 0:
return 0;
case 1:
return k_functionNameWidth;
case 2:
return selectableTableView()->bounds().width();
default:
assert(false);
return 0;
}
}
int FunctionListController::indexFromCumulatedWidth(KDCoordinate offsetX) {
if (offsetX <= k_functionNameWidth) {
return 0;
} else {
if (offsetX <= selectableTableView()->bounds().width())
return 1;
else {
return 2;
}
}
}
int FunctionListController::typeAtLocation(int i, int j) {
if (isAddEmptyRow(j)){
return i + 2;
}
return i;
}
HighlightCell * FunctionListController::reusableCell(int index, int type) {
assert(index >= 0);
assert(index < maxNumberOfRows());
switch (type) {
case 0:
return titleCells(index);
case 1:
return expressionCells(index);
case 2:
return m_emptyCell;
case 3:
return m_addNewModel;
default:
assert(false);
return nullptr;
}
}
int FunctionListController::reusableCellCount(int type) {
if (type > 1) {
return 1;
}
return maxNumberOfRows();
}
void FunctionListController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
if (!isAddEmptyRow(j)) {
if (i == 0) {
willDisplayTitleCellAtIndex(cell, j);
} else {
willDisplayExpressionCellAtIndex(cell, j);
}
}
EvenOddCell * myCell = (EvenOddCell *)cell;
myCell->setEven(j%2 == 0);
myCell->setHighlighted(i == selectedColumn() && j == selectedRow());
myCell->reloadCell();
}
int FunctionListController::numberOfButtons(ButtonRowController::Position position) const {
if (position == ButtonRowController::Position::Bottom) {
return 2;
}
return 0;
}
Button * FunctionListController::buttonAtIndex(int index, ButtonRowController::Position position) const {
if (position == ButtonRowController::Position::Top) {
return nullptr;
}
const Button * buttons[2] = {&m_plotButton, &m_valuesButton};
return (Button *)buttons[index];
}
void FunctionListController::didBecomeFirstResponder() {
if (selectedRow() == -1) {
selectCellAtLocation(1, 0);
} else {
selectCellAtLocation(selectedColumn(), selectedRow());
}
if (selectedRow() >= numberOfRows()) {
selectCellAtLocation(selectedColumn(), numberOfRows()-1);
}
footer()->setSelectedButton(-1);
app()->setFirstResponder(selectableTableView());
}
bool FunctionListController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::Up) {
if (selectedRow() == -1) {
footer()->setSelectedButton(-1);
selectableTableView()->selectCellAtLocation(1, numberOfRows()-1);
app()->setFirstResponder(selectableTableView());
return true;
}
selectableTableView()->deselectTable();
assert(selectedRow() == -1);
app()->setFirstResponder(tabController());
return true;
}
if (event == Ion::Events::Down) {
if (selectedRow() == -1) {
return false;
}
selectableTableView()->deselectTable();
footer()->setSelectedButton(0);
return true;
}
if (selectedRow() < 0) {
return false;
}
if (selectedColumn() == 1) {
return handleEventOnExpression(event);
}
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
assert(selectedColumn() == 0);
configureFunction(m_functionStore->modelAtIndex(modelIndexForRow(selectedRow())));
return true;
}
if (event == Ion::Events::Backspace) {
Shared::Function * function = m_functionStore->modelAtIndex(modelIndexForRow(selectedRow()));
if (removeModelRow(function)) {
int newSelectedRow = selectedRow() >= numberOfRows() ? numberOfRows()-1 : selectedRow();
selectCellAtLocation(selectedColumn(), newSelectedRow);
selectableTableView()->reloadData();
}
return true;
}
return false;
}
void FunctionListController::didEnterResponderChain(Responder * previousFirstResponder) {
selectableTableView()->reloadData();
}
void FunctionListController::willExitResponderChain(Responder * nextFirstResponder) {
if (nextFirstResponder == tabController()) {
selectableTableView()->deselectTable();
footer()->setSelectedButton(-1);
}
}
void FunctionListController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) {
if (isAddEmptyRow(selectedRow()) && selectedColumn() == 0) {
t->selectCellAtLocation(1, numberOfRows()-1);
}
}
StackViewController * FunctionListController::stackController() const{
return (StackViewController *)(parentResponder()->parentResponder()->parentResponder());
}
void FunctionListController::configureFunction(Shared::Function * function) {
StackViewController * stack = stackController();
parameterController()->setFunction(function);
stack->push(parameterController());
}
TabViewController * FunctionListController::tabController() const{
return (TabViewController *)(parentResponder()->parentResponder()->parentResponder()->parentResponder());
}
View * FunctionListController::loadView() {
loadAddModelCell();
m_emptyCell = new EvenOddCell();
SelectableTableView * selectableTableView = new SelectableTableView(this, this, this, this);
selectableTableView->setMargins(0);
selectableTableView->setVerticalCellOverlap(0);
selectableTableView->setShowsIndicators(false);
return selectableTableView;
}
void FunctionListController::unloadView(View * view) {
unloadAddModelCell();
delete m_emptyCell;
m_emptyCell = nullptr;
delete view;
}
}

View File

@@ -1,59 +1,64 @@
#ifndef SHARED_LIST_CONTROLLER_H
#define SHARED_LIST_CONTROLLER_H
#ifndef SHARED_FUNCTION_LIST_CONTROLLER_H
#define SHARED_FUNCTION_LIST_CONTROLLER_H
#include <escher.h>
#include "function_store.h"
#include "function_app.h"
#include "list_parameter_controller.h"
#include "new_function_cell.h"
#include "expression_model_list_controller.h"
#include "../i18n.h"
namespace Shared {
class ListController : public DynamicViewController, public ButtonRowDelegate, public TableViewDataSource, public SelectableTableViewDataSource, public SelectableTableViewDelegate {
class FunctionListController : public ExpressionModelListController, public ButtonRowDelegate, public TableViewDataSource, public SelectableTableViewDelegate {
public:
ListController(Responder * parentResponder, FunctionStore * functionStore, ButtonRowController * header, ButtonRowController * footer, I18n::Message text);
FunctionListController(Responder * parentResponder, FunctionStore * functionStore, ButtonRowController * header, ButtonRowController * footer, I18n::Message text);
/* TableViewDataSource */
int numberOfRows() override {
return numberOfExpressionRows();
}
int numberOfColumns() override;
KDCoordinate rowHeight(int j) override {
return expressionRowHeight(j);
}
KDCoordinate columnWidth(int i) override;
KDCoordinate cumulatedWidthFromIndex(int i) override;
KDCoordinate cumulatedHeightFromIndex(int j) override;
int indexFromCumulatedWidth(KDCoordinate offsetX) override;
int indexFromCumulatedHeight(KDCoordinate offsetY) override;
int typeAtLocation(int i, int j) override;
HighlightCell * reusableCell(int index, int type) override;
int reusableCellCount(int type) override;
void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override;
/* ButtonRowDelegate */
int numberOfButtons(ButtonRowController::Position position) const override;
Button * buttonAtIndex(int index, ButtonRowController::Position position) const override;
/* Responder */
void didBecomeFirstResponder() override;
bool handleEvent(Ion::Events::Event event) override;
void didEnterResponderChain(Responder * previousFirstResponder) override;
void willExitResponderChain(Responder * nextFirstResponder) override;
/* SelectableTableViewDelegate*/
void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) override;
protected:
StackViewController * stackController() const;
void configureFunction(Function * function);
virtual void reinitExpression(Function * function);
SelectableTableView * selectableTableView();
View * loadView() override;
void unloadView(View * view) override;
FunctionStore * m_functionStore;
private:
static constexpr KDCoordinate k_functionNameWidth = 65;
TabViewController * tabController() const;
virtual int functionIndexForRow(int j);
virtual const char * textForRow(int j);
virtual void addEmptyFunction();
virtual bool removeFunctionRow(Function * function) = 0;
virtual void editExpression(Function * function, Ion::Events::Event event) = 0;
ExpressionModelStore * modelStore() override { return m_functionStore; }
InputViewController * inputController() override {
FunctionApp * myApp = static_cast<FunctionApp *>(app());
return myApp->inputViewController();
}
virtual ListParameterController * parameterController() = 0;
virtual int maxNumberOfRows() = 0;
virtual HighlightCell * titleCells(int index) = 0;
virtual HighlightCell * expressionCells(int index) = 0;
virtual void willDisplayTitleCellAtIndex(HighlightCell * cell, int j) = 0;
virtual void willDisplayExpressionCellAtIndex(HighlightCell * cell, int j) = 0;
EvenOddCell * m_emptyCell;
I18n::Message m_addNewMessage;
NewFunctionCell * m_addNewFunction;
Button m_plotButton;
Button m_valuesButton;
};

View File

@@ -4,15 +4,15 @@
namespace Shared {
FunctionStore::FunctionStore() :
m_numberOfFunctions(0)
ExpressionModelStore()
{
}
Function * FunctionStore::activeFunctionAtIndex(int i) {
assert(i>=0 && i<m_numberOfFunctions);
assert(i>=0 && i<m_numberOfModels);
int index = 0;
for (int k = 0; k < m_numberOfFunctions; k++) {
Function * function = functionAtIndex(k);
for (int k = 0; k < m_numberOfModels; k++) {
Function * function = modelAtIndex(k);
if (function->isActive() && function->isDefined()) {
if (i == index) {
return function;
@@ -24,29 +24,10 @@ Function * FunctionStore::activeFunctionAtIndex(int i) {
return nullptr;
}
Function * FunctionStore::definedFunctionAtIndex(int i) {
assert(i>=0 && i<m_numberOfFunctions);
int index = 0;
for (int k = 0; k < m_numberOfFunctions; k++) {
if (functionAtIndex(k)->isDefined()) {
if (i == index) {
return functionAtIndex(k);
}
index++;
}
}
assert(false);
return nullptr;
}
int FunctionStore::numberOfFunctions() {
return m_numberOfFunctions;
}
int FunctionStore::numberOfActiveFunctions() {
int result = 0;
for (int i = 0; i < m_numberOfFunctions; i++) {
Function * function = functionAtIndex(i);
for (int i = 0; i < m_numberOfModels; i++) {
Function * function = modelAtIndex(i);
if (function->isDefined() && function->isActive()) {
result++;
}
@@ -54,20 +35,24 @@ int FunctionStore::numberOfActiveFunctions() {
return result;
}
int FunctionStore::numberOfDefinedFunctions() {
int result = 0;
for (int i = 0; i < m_numberOfFunctions; i++) {
if (functionAtIndex(i)->isDefined()) {
result++;
template<typename T>
T FunctionStore::firstAvailableAttribute(T attributes[], AttributeGetter<T> attribute) {
for (int k = 0; k < maxNumberOfModels(); k++) {
int j = 0;
while (j < m_numberOfModels) {
if (attribute(modelAtIndex(j)) == attributes[k]) {
break;
}
j++;
}
if (j == m_numberOfModels) {
return attributes[k];
}
}
return result;
}
void FunctionStore::tidy() {
for (int i = 0; i < m_numberOfFunctions; i++) {
functionAtIndex(i)->tidy();
}
return attributes[0];
}
}
template char const* const Shared::FunctionStore::firstAvailableAttribute<char const* const>(char const* const*, char const* const (*)(Shared::Function*));
template KDColor const Shared::FunctionStore::firstAvailableAttribute<KDColor const>(KDColor const*, KDColor const (*)(Shared::Function*));

View File

@@ -2,6 +2,7 @@
#define SHARED_FUNCTION_STORE_H
#include "function.h"
#include "expression_model_store.h"
#include <stdint.h>
namespace Shared {
@@ -9,26 +10,21 @@ namespace Shared {
/* FunctionStore is a dumb class.
* Its only job is to store functions and to give them a color. */
class FunctionStore {
class FunctionStore : public ExpressionModelStore {
public:
FunctionStore();
virtual uint32_t storeChecksum() = 0;
virtual Function * functionAtIndex(int i) = 0;
virtual Function * modelAtIndex(int i) override = 0;
virtual Function * activeFunctionAtIndex(int i);
virtual Function * definedFunctionAtIndex(int i);
virtual Function * addEmptyFunction() = 0;
virtual void removeFunction(Function * f) = 0;
virtual void removeAll() = 0;
int numberOfFunctions();
// Functions can be undefined when they have a color and a name but no content
int numberOfDefinedFunctions();
virtual Function * definedFunctionAtIndex(int i) { return static_cast<Function *>(definedModelAtIndex(i)); }
// An active function must be defined to be counted
int numberOfActiveFunctions();
virtual int maxNumberOfFunctions() = 0;
virtual char symbol() const = 0;
void tidy();
protected:
int m_numberOfFunctions;
static const char * const name(Shared::Function * f) { return f->name(); }
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);
private:
virtual const char * firstAvailableName() = 0;
virtual const KDColor firstAvailableColor() = 0;

View File

@@ -17,12 +17,14 @@ void FunctionTitleCell::setColor(KDColor color) {
void FunctionTitleCell::drawRect(KDContext * ctx, KDRect rect) const {
if (m_orientation == Orientation::VerticalIndicator){
ctx->fillRect(KDRect(0, 0, k_colorIndicatorThickness, bounds().height()), m_functionColor);
// Color the vertical separator
ctx->fillRect(KDRect(bounds().width()-k_separatorThickness, 0, k_separatorThickness, bounds().height()), Palette::GreyBright);
KDColor separatorColor = m_even ? Palette::WallScreen : KDColorWhite;
// Color the horizontal separator
ctx->fillRect(KDRect(k_colorIndicatorThickness, bounds().height()-k_separatorThickness, bounds().width()-k_colorIndicatorThickness-k_separatorThickness, k_separatorThickness), separatorColor);
} else {
ctx->fillRect(KDRect(0, 0, bounds().width(), k_colorIndicatorThickness), m_functionColor);
}
KDColor separatorColor = m_even ? Palette::WallScreen : KDColorWhite;
// Color the horizontal separator
ctx->fillRect(KDRect(k_colorIndicatorThickness, bounds().height()-k_separatorThickness, bounds().width()-k_colorIndicatorThickness, k_separatorThickness), separatorColor);
}
}

View File

@@ -1,5 +1,5 @@
#ifndef SEQUENCE_FUNCTION_TITLE_CELL_H
#define SEQUENCE_FUNCTION_TITLE_CELL_H
#ifndef SHARED_FUNCTION_TITLE_CELL_H
#define SHARED_FUNCTION_TITLE_CELL_H
#include <escher.h>

View File

@@ -1,313 +0,0 @@
#include "list_controller.h"
#include <assert.h>
namespace Shared {
ListController::ListController(Responder * parentResponder, FunctionStore * functionStore, ButtonRowController * header, ButtonRowController * footer, I18n::Message text) :
DynamicViewController(parentResponder),
ButtonRowDelegate(header, footer),
m_functionStore(functionStore),
m_emptyCell(nullptr),
m_addNewMessage(text),
m_addNewFunction(nullptr),
m_plotButton(this, I18n::Message::Plot, Invocation([](void * context, void * sender) {
ListController * list = (ListController *)context;
TabViewController * tabController = list->tabController();
tabController->setActiveTab(1);
}, this), KDText::FontSize::Small, Palette::PurpleBright),
m_valuesButton(this, I18n::Message::DisplayValues, Invocation([](void * context, void * sender) {
ListController * list = (ListController *)context;
TabViewController * tabController = list->tabController();
tabController->setActiveTab(2);
}, this), KDText::FontSize::Small, Palette::PurpleBright)
{
}
int ListController::numberOfColumns() {
return 2;
};
KDCoordinate ListController::columnWidth(int i) {
switch (i) {
case 0:
return k_functionNameWidth;
case 1:
return selectableTableView()->bounds().width()-k_functionNameWidth;
default:
assert(false);
return 0;
}
}
KDCoordinate ListController::cumulatedWidthFromIndex(int i) {
switch (i) {
case 0:
return 0;
case 1:
return k_functionNameWidth;
case 2:
return selectableTableView()->bounds().width();
default:
assert(false);
return 0;
}
}
KDCoordinate ListController::cumulatedHeightFromIndex(int j) {
int result = 0;
for (int k = 0; k < j; k++) {
result += rowHeight(k);
}
return result;
}
int ListController::indexFromCumulatedWidth(KDCoordinate offsetX) {
if (offsetX <= k_functionNameWidth) {
return 0;
} else {
if (offsetX <= selectableTableView()->bounds().width())
return 1;
else {
return 2;
}
}
}
int ListController::indexFromCumulatedHeight(KDCoordinate offsetY) {
int result = 0;
int j = 0;
while (result < offsetY && j < numberOfRows()) {
result += rowHeight(j++);
}
return (result < offsetY || offsetY == 0) ? j : j - 1;
}
int ListController::typeAtLocation(int i, int j) {
if (m_functionStore->numberOfFunctions() < m_functionStore->maxNumberOfFunctions()
&& j == numberOfRows() - 1) {
return i + 2;
}
return i;
}
HighlightCell * ListController::reusableCell(int index, int type) {
assert(index >= 0);
assert(index < maxNumberOfRows());
switch (type) {
case 0:
return titleCells(index);
case 1:
return expressionCells(index);
case 2:
return m_emptyCell;
case 3:
return m_addNewFunction;
default:
assert(false);
return nullptr;
}
}
int ListController::reusableCellCount(int type) {
if (type > 1) {
return 1;
}
return maxNumberOfRows();
}
void ListController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
if (j < numberOfRows() - 1 || m_functionStore->numberOfFunctions() == m_functionStore->maxNumberOfFunctions()) {
if (i == 0) {
willDisplayTitleCellAtIndex(cell, j);
} else {
willDisplayExpressionCellAtIndex(cell, j);
}
}
EvenOddCell * myCell = (EvenOddCell *)cell;
myCell->setEven(j%2 == 0);
myCell->setHighlighted(i == selectedColumn() && j == selectedRow());
myCell->reloadCell();
}
int ListController::numberOfButtons(ButtonRowController::Position position) const {
if (position == ButtonRowController::Position::Bottom) {
return 2;
}
return 0;
}
Button * ListController::buttonAtIndex(int index, ButtonRowController::Position position) const {
if (position == ButtonRowController::Position::Top) {
return nullptr;
}
const Button * buttons[2] = {&m_plotButton, &m_valuesButton};
return (Button *)buttons[index];
}
void ListController::didBecomeFirstResponder() {
if (selectedRow() == -1) {
selectCellAtLocation(1, 0);
} else {
selectCellAtLocation(selectedColumn(), selectedRow());
}
if (selectedRow() >= numberOfRows()) {
selectCellAtLocation(selectedColumn(), numberOfRows()-1);
}
footer()->setSelectedButton(-1);
app()->setFirstResponder(selectableTableView());
}
bool ListController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::Up) {
if (selectedRow() == -1) {
footer()->setSelectedButton(-1);
selectableTableView()->selectCellAtLocation(1, numberOfRows()-1);
app()->setFirstResponder(selectableTableView());
return true;
}
selectableTableView()->deselectTable();
assert(selectedRow() == -1);
app()->setFirstResponder(tabController());
return true;
}
if (event == Ion::Events::Down) {
if (selectedRow() == -1) {
return false;
}
selectableTableView()->deselectTable();
footer()->setSelectedButton(0);
return true;
}
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
switch (selectedColumn()) {
case 0:
{
if (m_functionStore->numberOfFunctions() < m_functionStore->maxNumberOfFunctions() &&
selectedRow() == numberOfRows() - 1) {
return true;
}
configureFunction(m_functionStore->functionAtIndex(functionIndexForRow(selectedRow())));
return true;
}
case 1:
{
if (m_functionStore->numberOfFunctions() < m_functionStore->maxNumberOfFunctions() &&
selectedRow() == numberOfRows() - 1) {
addEmptyFunction();
return true;
}
Shared::Function * function = m_functionStore->functionAtIndex(functionIndexForRow(selectedRow()));
editExpression(function, event);
return true;
}
default:
{
return false;
}
}
}
if (event == Ion::Events::Backspace && selectedRow() >= 0 &&
(selectedRow() < numberOfRows() - 1 || m_functionStore->numberOfFunctions() == m_functionStore->maxNumberOfFunctions())) {
Shared::Function * function = m_functionStore->functionAtIndex(functionIndexForRow(selectedRow()));
if (selectedColumn() == 1 && !function->isEmpty()) {
reinitExpression(function);
} else {
if (removeFunctionRow(function)) {
selectCellAtLocation(selectedColumn(), selectedRow());
if (selectedRow() >= numberOfRows()) {
selectCellAtLocation(selectedColumn(), numberOfRows()-1);
}
selectableTableView()->reloadData();
}
}
return true;
}
if ((event.hasText() || event == Ion::Events::XNT || event == Ion::Events::Paste || event == Ion::Events::Toolbox || event == Ion::Events::Var)
&& selectedColumn() == 1
&& (selectedRow() != numberOfRows() - 1
|| m_functionStore->numberOfFunctions() == m_functionStore->maxNumberOfFunctions())) {
Shared::Function * function = m_functionStore->functionAtIndex(functionIndexForRow(selectedRow()));
editExpression(function, event);
return true;
}
if (event == Ion::Events::Copy && selectedColumn() == 1 &&
(selectedRow() < numberOfRows() - 1 || m_functionStore->numberOfFunctions() == m_functionStore->maxNumberOfFunctions())) {
Clipboard::sharedClipboard()->store(textForRow(selectedRow()));
return true;
}
return false;
}
void ListController::didEnterResponderChain(Responder * previousFirstResponder) {
selectableTableView()->reloadData();
}
void ListController::willExitResponderChain(Responder * nextFirstResponder) {
if (nextFirstResponder == tabController()) {
selectableTableView()->deselectTable();
footer()->setSelectedButton(-1);
}
}
void ListController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) {
if (m_functionStore->numberOfFunctions() < m_functionStore->maxNumberOfFunctions() && selectedRow() == numberOfRows() - 1 && selectedColumn() == 0) {
t->selectCellAtLocation(1, numberOfRows()-1);
}
}
StackViewController * ListController::stackController() const{
return (StackViewController *)(parentResponder()->parentResponder()->parentResponder());
}
void ListController::configureFunction(Shared::Function * function) {
StackViewController * stack = stackController();
parameterController()->setFunction(function);
stack->push(parameterController());
}
void ListController::reinitExpression(Function * function) {
function->setContent("");
selectableTableView()->reloadData();
}
SelectableTableView * ListController::selectableTableView() {
return (SelectableTableView *)view();
}
TabViewController * ListController::tabController() const{
return (TabViewController *)(parentResponder()->parentResponder()->parentResponder()->parentResponder());
}
int ListController::functionIndexForRow(int j) {
return j;
}
const char * ListController::textForRow(int j) {
Shared::Function * function = m_functionStore->functionAtIndex(functionIndexForRow(j));
return function->text();
}
void ListController::addEmptyFunction() {
m_functionStore->addEmptyFunction();
selectableTableView()->reloadData();
}
View * ListController::loadView() {
m_emptyCell = new EvenOddCell();
m_addNewFunction = new NewFunctionCell(m_addNewMessage);
SelectableTableView * selectableTableView = new SelectableTableView(this, this, this, this);
selectableTableView->setMargins(0);
selectableTableView->setVerticalCellOverlap(0);
selectableTableView->setShowsIndicators(false);
return selectableTableView;
}
void ListController::unloadView(View * view) {
delete m_emptyCell;
m_emptyCell = nullptr;
delete m_addNewFunction;
m_addNewFunction = nullptr;
delete view;
}
}

View File

@@ -99,13 +99,13 @@ bool ListParameterController::handleEnterOnRow(int rowIndex) {
case 1:
#endif
{
if (m_functionStore->numberOfFunctions() > 1) {
m_functionStore->removeFunction(m_function);
if (m_functionStore->numberOfModels() > 1) {
m_functionStore->removeModel(m_function);
StackViewController * stack = (StackViewController *)(parentResponder());
stack->pop();
return true;
} else {
if (m_functionStore->numberOfDefinedFunctions() == 1) {
if (m_functionStore->numberOfDefinedModels() == 1) {
Function * f = m_functionStore->definedFunctionAtIndex(0);
f->setContent("");
StackViewController * stack = (StackViewController *)(parentResponder());

View File

@@ -1,35 +0,0 @@
#include "new_function_cell.h"
#include "assert.h"
namespace Shared {
NewFunctionCell::NewFunctionCell(I18n::Message text) :
EvenOddCell(),
m_messageTextView(KDText::FontSize::Large, text, 0.5f, 0.5f)
{
}
void NewFunctionCell::setEven(bool even) {
EvenOddCell::setEven(even);
m_messageTextView.setBackgroundColor(backgroundColor());
}
void NewFunctionCell::setHighlighted(bool highlight) {
EvenOddCell::setHighlighted(highlight);
m_messageTextView.setBackgroundColor(backgroundColor());
}
int NewFunctionCell::numberOfSubviews() const {
return 1;
}
View * NewFunctionCell::subviewAtIndex(int index) {
assert(index == 0);
return &m_messageTextView;
}
void NewFunctionCell::layoutSubviews() {
m_messageTextView.setFrame(bounds());
}
}

View File

@@ -1,22 +0,0 @@
#ifndef SHARED_NEW_FUNCTION_CELL_H
#define SHARED_NEW_FUNCTION_CELL_H
#include <escher.h>
namespace Shared {
class NewFunctionCell : public EvenOddCell {
public:
NewFunctionCell(I18n::Message text);
void setEven(bool even) override;
void setHighlighted(bool highlight) override;
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
private:
MessageTextView m_messageTextView;
};
}
#endif

View File

@@ -0,0 +1,47 @@
#include "scrollable_exact_approximate_expressions_cell.h"
#include <assert.h>
using namespace Poincare;
namespace Shared {
ScrollableExactApproximateExpressionsCell::ScrollableExactApproximateExpressionsCell(Responder * parentResponder) :
Responder(parentResponder),
m_view(this)
{
}
void ScrollableExactApproximateExpressionsCell::setHighlighted(bool highlight) {
m_view.evenOddCell()->setHighlighted(highlight);
reloadScroll();
}
void ScrollableExactApproximateExpressionsCell::setEven(bool even) {
EvenOddCell::setEven(even);
m_view.evenOddCell()->setEven(even);
}
void ScrollableExactApproximateExpressionsCell::reloadCell() {
m_view.evenOddCell()->reloadCell();
}
void ScrollableExactApproximateExpressionsCell::reloadScroll() {
m_view.reloadScroll();
}
void ScrollableExactApproximateExpressionsCell::didBecomeFirstResponder() {
app()->setFirstResponder(&m_view);
}
int ScrollableExactApproximateExpressionsCell::numberOfSubviews() const {
return 1;
}
View * ScrollableExactApproximateExpressionsCell::subviewAtIndex(int index) {
return &m_view;
}
void ScrollableExactApproximateExpressionsCell::layoutSubviews() {
m_view.setFrame(KDRect(k_margin,k_margin, bounds().width()-2*k_margin, bounds().height()-2*k_margin));
}
}

View File

@@ -0,0 +1,37 @@
#ifndef SHARED_SCROLLABLE_EXACT_APPROXIMATE_EXPRESSIONS_CELL_H
#define SHARED_SCROLLABLE_EXACT_APPROXIMATE_EXPRESSIONS_CELL_H
#include <escher.h>
#include "scrollable_exact_approximate_expressions_view.h"
namespace Shared {
class ScrollableExactApproximateExpressionsCell : public ::EvenOddCell, public Responder {
public:
ScrollableExactApproximateExpressionsCell(Responder * parentResponder = nullptr);
void setExpressions(Poincare::ExpressionLayout ** expressionsLayout) {
return m_view.setExpressions(expressionsLayout);
}
void setEqualMessage(I18n::Message equalSignMessage) {
return m_view.setEqualMessage(equalSignMessage);
}
void setHighlighted(bool highlight) override;
void setEven(bool even) override;
void reloadCell() override;
void reloadScroll();
Responder * responder() override {
return this;
}
Poincare::ExpressionLayout * expressionLayout() const override { return m_view.expressionLayout(); }
void didBecomeFirstResponder() override;
constexpr static KDCoordinate k_margin = 5;
private:
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
ScrollableExactApproximateExpressionsView m_view;
};
}
#endif

View File

@@ -1,31 +1,24 @@
#include "output_expressions_view.h"
#include "scrollable_output_expressions_view.h"
#include "scrollable_exact_approximate_expressions_view.h"
#include "../i18n.h"
#include <assert.h>
using namespace Poincare;
namespace Calculation {
namespace Shared {
OutputExpressionsView::OutputExpressionsView(Responder * parentResponder) :
Responder(parentResponder),
ScrollableExactApproximateExpressionsView::ContentCell::ContentCell() :
m_approximateExpressionView(),
m_approximateSign(KDText::FontSize::Large, I18n::Message::AlmostEqual, 0.5f, 0.5f, Palette::GreyVeryDark),
m_exactExpressionView(),
m_selectedSubviewType(OutputExpressionsView::SubviewType::ExactOutput)
m_selectedSubviewType((SubviewType)0)
{
}
void OutputExpressionsView::setExpressions(ExpressionLayout ** expressionsLayout) {
m_approximateExpressionView.setExpressionLayout(expressionsLayout[0]);
m_exactExpressionView.setExpressionLayout(expressionsLayout[1]);
layoutSubviews();
KDColor ScrollableExactApproximateExpressionsView::ContentCell::backgroundColor() const {
KDColor background = m_even ? KDColorWhite : Palette::WallScreen;
return background;
}
void OutputExpressionsView::setEqualMessage(I18n::Message equalSignMessage) {
m_approximateSign.setMessage(equalSignMessage);
}
void OutputExpressionsView::setHighlighted(bool highlight) {
void ScrollableExactApproximateExpressionsView::ContentCell::setHighlighted(bool highlight) {
// Do not call HighlightCell::setHighlighted to avoid marking all cell as dirty
m_highlighted = highlight;
m_exactExpressionView.setBackgroundColor(backgroundColor());
@@ -39,12 +32,7 @@ void OutputExpressionsView::setHighlighted(bool highlight) {
}
}
KDColor OutputExpressionsView::backgroundColor() const {
KDColor background = m_even ? Palette::WallScreen : KDColorWhite;
return background;
}
void OutputExpressionsView::reloadCell() {
void ScrollableExactApproximateExpressionsView::ContentCell::reloadCell() {
setHighlighted(isHighlighted());
m_approximateSign.setBackgroundColor(backgroundColor());
if (numberOfSubviews() == 1) {
@@ -55,7 +43,7 @@ void OutputExpressionsView::reloadCell() {
layoutSubviews();
}
KDSize OutputExpressionsView::minimalSizeForOptimalDisplay() const {
KDSize ScrollableExactApproximateExpressionsView::ContentCell::minimalSizeForOptimalDisplay() const {
KDSize approximateExpressionSize = m_approximateExpressionView.minimalSizeForOptimalDisplay();
if (numberOfSubviews() == 1) {
return approximateExpressionSize;
@@ -68,54 +56,32 @@ KDSize OutputExpressionsView::minimalSizeForOptimalDisplay() const {
return KDSize(exactExpressionSize.width()+approximateSignSize.width()+approximateExpressionSize.width()+2*k_digitHorizontalMargin, height);
}
void OutputExpressionsView::didBecomeFirstResponder() {
if (numberOfSubviews() == 1) {
setSelectedSubviewType(SubviewType::ApproximativeOutput);
} else {
setSelectedSubviewType(SubviewType::ExactOutput);
}
}
bool OutputExpressionsView::handleEvent(Ion::Events::Event event) {
if (numberOfSubviews() == 1) {
return false;
}
ScrollableOutputExpressionsView * scrollResponder = static_cast<ScrollableOutputExpressionsView *>(parentResponder());
KDCoordinate offset = scrollResponder->manualScrollingOffset().x();
bool rightExpressionIsVisible = minimalSizeForOptimalDisplay().width() - m_approximateExpressionView.minimalSizeForOptimalDisplay().width() - offset < scrollResponder->bounds().width()
;
bool leftExpressionIsVisible = m_exactExpressionView.minimalSizeForOptimalDisplay().width() - offset < scrollResponder->bounds().width();
if ((event == Ion::Events::Right && m_selectedSubviewType == SubviewType::ExactOutput && rightExpressionIsVisible) ||
(event == Ion::Events::Left && m_selectedSubviewType == SubviewType::ApproximativeOutput && leftExpressionIsVisible)) {
SubviewType otherSubviewType = m_selectedSubviewType == SubviewType::ExactOutput ? SubviewType::ApproximativeOutput : SubviewType::ExactOutput;
setSelectedSubviewType(otherSubviewType);
return true;
}
return false;
}
OutputExpressionsView::SubviewType OutputExpressionsView::selectedSubviewType() {
return m_selectedSubviewType;
}
void OutputExpressionsView::setSelectedSubviewType(OutputExpressionsView::SubviewType subviewType) {
void ScrollableExactApproximateExpressionsView::ContentCell::setSelectedSubviewType(ScrollableExactApproximateExpressionsView::SubviewType subviewType) {
m_selectedSubviewType = subviewType;
setHighlighted(isHighlighted());
}
int OutputExpressionsView::numberOfSubviews() const {
Poincare::ExpressionLayout * ScrollableExactApproximateExpressionsView::ContentCell::expressionLayout() const {
if (m_selectedSubviewType == SubviewType::ExactOutput) {
return m_exactExpressionView.expressionLayout();
} else {
return m_approximateExpressionView.expressionLayout();
}
}
int ScrollableExactApproximateExpressionsView::ContentCell::numberOfSubviews() const {
if (m_exactExpressionView.expressionLayout() != nullptr) {
return 3;
}
return 1;
}
View * OutputExpressionsView::subviewAtIndex(int index) {
View * ScrollableExactApproximateExpressionsView::ContentCell::subviewAtIndex(int index) {
View * views[3] = {&m_approximateExpressionView, &m_approximateSign, &m_exactExpressionView};
return views[index];
}
void OutputExpressionsView::layoutSubviews() {
void ScrollableExactApproximateExpressionsView::ContentCell::layoutSubviews() {
KDCoordinate height = bounds().height();
KDSize approximateExpressionSize = m_approximateExpressionView.minimalSizeForOptimalDisplay();
if (numberOfSubviews() == 1) {
@@ -132,4 +98,47 @@ void OutputExpressionsView::layoutSubviews() {
m_approximateSign.setFrame(KDRect(k_digitHorizontalMargin+exactExpressionSize.width(), baseline-approximateSignSize.height()/2, approximateSignSize));
}
ScrollableExactApproximateExpressionsView::ScrollableExactApproximateExpressionsView(Responder * parentResponder) :
ScrollableView(parentResponder, &m_contentCell, this),
m_contentCell()
{
}
void ScrollableExactApproximateExpressionsView::setExpressions(ExpressionLayout ** expressionsLayout) {
m_contentCell.approximateExpressionView()->setExpressionLayout(expressionsLayout[0]);
m_contentCell.exactExpressionView()->setExpressionLayout(expressionsLayout[1]);
m_contentCell.layoutSubviews();
}
void ScrollableExactApproximateExpressionsView::setEqualMessage(I18n::Message equalSignMessage) {
m_contentCell.approximateSign()->setMessage(equalSignMessage);
}
void ScrollableExactApproximateExpressionsView::didBecomeFirstResponder() {
if (m_contentCell.exactExpressionView()->expressionLayout() == nullptr) {
setSelectedSubviewType(SubviewType::ApproximativeOutput);
} else {
setSelectedSubviewType(SubviewType::ExactOutput);
}
}
bool ScrollableExactApproximateExpressionsView::handleEvent(Ion::Events::Event event) {
if (m_contentCell.exactExpressionView()->expressionLayout() == nullptr) {
return ScrollableView::handleEvent(event);
}
bool rightExpressionIsVisible = minimalSizeForOptimalDisplay().width() - m_contentCell.approximateExpressionView()->minimalSizeForOptimalDisplay().width() - m_manualScrollingOffset.x() < bounds().width()
;
bool leftExpressionIsVisible = m_contentCell.exactExpressionView()->minimalSizeForOptimalDisplay().width() - m_manualScrollingOffset.x() > 0;
if ((event == Ion::Events::Right && selectedSubviewType() == SubviewType::ExactOutput && rightExpressionIsVisible) ||
(event == Ion::Events::Left && selectedSubviewType() == SubviewType::ApproximativeOutput && leftExpressionIsVisible)) {
SubviewType otherSubviewType = selectedSubviewType() == SubviewType::ExactOutput ? SubviewType::ApproximativeOutput : SubviewType::ExactOutput;
setSelectedSubviewType(otherSubviewType);
return true;
}
return ScrollableView::handleEvent(event);
}
KDSize ScrollableExactApproximateExpressionsView::minimalSizeForOptimalDisplay() const {
return m_contentCell.minimalSizeForOptimalDisplay();
}
}

View File

@@ -0,0 +1,69 @@
#ifndef SHARED_SCROLLABLE_EXACT_APPROXIMATE_EXPRESSIONS_VIEW_H
#define SHARED_SCROLLABLE_EXACT_APPROXIMATE_EXPRESSIONS_VIEW_H
#include <escher.h>
namespace Shared {
class ScrollableExactApproximateExpressionsView : public ScrollableView, public ScrollViewDataSource {
public:
enum class SubviewType {
ExactOutput,
ApproximativeOutput
};
ScrollableExactApproximateExpressionsView(Responder * parentResponder);
::EvenOddCell * evenOddCell() {
return &m_contentCell;
}
void setExpressions(Poincare::ExpressionLayout ** expressionsLayout);
void setEqualMessage(I18n::Message equalSignMessage);
SubviewType selectedSubviewType() {
return m_contentCell.selectedSubviewType();
}
void setSelectedSubviewType(SubviewType subviewType) {
m_contentCell.setSelectedSubviewType(subviewType);
}
void didBecomeFirstResponder() override;
bool handleEvent(Ion::Events::Event event) override;
KDSize minimalSizeForOptimalDisplay() const override;
Poincare::ExpressionLayout * expressionLayout() const {
return m_contentCell.expressionLayout();
}
private:
class ContentCell : public ::EvenOddCell {
public:
ContentCell();
KDColor backgroundColor() const override;
void setHighlighted(bool highlight) override;
void reloadCell() override;
KDSize minimalSizeForOptimalDisplay() const override;
ExpressionView * approximateExpressionView() {
return &m_approximateExpressionView;
}
ExpressionView * exactExpressionView() {
return &m_exactExpressionView;
}
MessageTextView * approximateSign() {
return &m_approximateSign;
}
SubviewType selectedSubviewType() {
return m_selectedSubviewType;
}
void setSelectedSubviewType(SubviewType subviewType);
void layoutSubviews() override;
int numberOfSubviews() const override;
Poincare::ExpressionLayout * expressionLayout() const override;
private:
View * subviewAtIndex(int index) override;
constexpr static KDCoordinate k_digitHorizontalMargin = 10;
ExpressionView m_approximateExpressionView;
MessageTextView m_approximateSign;
ExpressionView m_exactExpressionView;
SubviewType m_selectedSubviewType;
};
ContentCell m_contentCell;
};
}
#endif

View File

@@ -85,11 +85,6 @@ bool ValuesController::handleEvent(Ion::Events::Event event) {
if (selectedRow() == -1) {
return header()->handleEvent(event);
}
if (event == Ion::Events::Copy && selectedRow() > 0 && selectedColumn() > 0) {
EvenOddBufferTextCell * cell = (EvenOddBufferTextCell *)selectableTableView()->selectedCell();
Clipboard::sharedClipboard()->store(cell->text());
return true;
}
return false;
}

29
apps/solver/Makefile Normal file
View File

@@ -0,0 +1,29 @@
snapshots += Solver::App::Snapshot
snapshot_headers += apps/solver/app.h
app_objs += $(addprefix apps/solver/,\
app.o\
equation_models_parameter_controller.o\
equation.o\
equation_list_view.o\
equation_store.o\
interval_controller.o\
list_controller.o\
solutions_controller.o\
)
i18n_files += $(addprefix apps/solver/,\
base.de.i18n\
base.en.i18n\
base.es.i18n\
base.fr.i18n\
base.pt.i18n\
)
tests += $(addprefix apps/solver/test/,\
equation_store.cpp\
)
test_objs += $(addprefix apps/solver/, equation.o equation_store.o)
test_objs += $(addprefix apps/shared/, expression_model.o expression_model_store.o)
app_images += apps/solver/solver_icon.png

71
apps/solver/app.cpp Normal file
View File

@@ -0,0 +1,71 @@
#include "app.h"
#include "../i18n.h"
#include "solver_icon.h"
using namespace Shared;
namespace Solver {
I18n::Message App::Descriptor::name() {
return I18n::Message::SolverApp;
}
I18n::Message App::Descriptor::upperName() {
return I18n::Message::SolverAppCapital;
}
const Image * App::Descriptor::icon() {
return ImageStore::SolverIcon;
}
App::Snapshot::Snapshot() :
m_equationStore()
{
}
App * App::Snapshot::unpack(Container * container) {
return new App(container, this);
}
App::Descriptor * App::Snapshot::descriptor() {
static Descriptor descriptor;
return &descriptor;
}
void App::Snapshot::reset() {
// Delete all equations
m_equationStore.removeAll();
}
void App::Snapshot::tidy() {
// Delete all expressions of equations
m_equationStore.tidy();
}
App::App(Container * container, Snapshot * snapshot) :
ExpressionFieldDelegateApp(container, snapshot, &m_inputViewController),
m_solutionsController(&m_alternateEmptyViewController, snapshot->equationStore()),
m_intervalController(nullptr, snapshot->equationStore()),
m_alternateEmptyViewController(nullptr, &m_solutionsController, &m_solutionsController),
m_listController(&m_listFooter, snapshot->equationStore(), &m_listFooter),
m_listFooter(&m_stackViewController, &m_listController, &m_listController, ButtonRowController::Position::Bottom, ButtonRowController::Style::EmbossedGrey, ButtonRowController::Size::Large),
m_stackViewController(&m_inputViewController, &m_listFooter),
m_inputViewController(&m_modalViewController, &m_stackViewController, &m_listController, &m_listController)
{
}
void App::willBecomeInactive() {
if (m_modalViewController.isDisplayingModal()) {
m_modalViewController.dismissModalViewController();
}
if (inputViewController()->isDisplayingModal()) {
inputViewController()->abortEditionAndDismiss();
}
::App::willBecomeInactive();
}
const char * App::XNT() {
return "x";
}
}

50
apps/solver/app.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef SOLVER_SOLVER_APP_H
#define SOLVER_SOLVER_APP_H
#include <escher.h>
#include "../shared/expression_field_delegate_app.h"
#include "list_controller.h"
#include "equation_store.h"
#include "interval_controller.h"
#include "solutions_controller.h"
namespace Solver {
class App : public Shared::ExpressionFieldDelegateApp {
public:
class Descriptor : public ::App::Descriptor {
public:
I18n::Message name() override;
I18n::Message upperName() override;
const Image * icon() override;
};
class Snapshot : public ::App::Snapshot {
public:
Snapshot();
App * unpack(Container * container) override;
Descriptor * descriptor() override;
void reset() override;
EquationStore * equationStore() { return &m_equationStore; }
private:
void tidy() override;
EquationStore m_equationStore;
};
InputViewController * inputViewController() { return &m_inputViewController; }
ViewController * solutionsControllerStack() { return &m_alternateEmptyViewController; }
ViewController * intervalController() { return &m_intervalController; }
void willBecomeInactive() override;
const char * XNT() override;
private:
App(Container * container, Snapshot * snapshot);
SolutionsController m_solutionsController;
IntervalController m_intervalController;
AlternateEmptyViewController m_alternateEmptyViewController;
ListController m_listController;
ButtonRowController m_listFooter;
StackViewController m_stackViewController;
InputViewController m_inputViewController;
};
}
#endif

21
apps/solver/base.de.i18n Normal file
View File

@@ -0,0 +1,21 @@
SolverApp = "Gleichungen"
SolverAppCapital = "GLEICHUNGEN"
AddEquation = "Gleichung hinzuzufügen"
ResolveEquation = "Lösen der Gleichung"
ResolveSystem = "Lösen des Gleichungssystems"
UseEquationModel = "Verwenden Sie einen Gleichungsmodell"
UndefinedEquation = "Undefinierte Gleichung"
TooManyVariables = "Es gibt zu viele Unbekannte"
NonLinearSystem = "Das System ist nicht linear"
Solution = "Lösung"
ApproximateSolution = "Ungefähre Lösung"
SearchInverval = "Lösungssuche Intervall"
NoSolutionSystem = "Das System hat keine Lösung"
NoSolutionEquation = "Die Gleichung hat keine Lösung"
NoSolutionInterval = "Keine Lösung im Intervall gefunden"
EnterEquation = "Geben Sie eine Gleichung ein"
InfiniteNumberOfSolutions = "Es gibt unendlich viele Lösungen"
ApproximateSolutionIntervalInstruction0= "Geben Sie das Intervall für die Suche"
ApproximateSolutionIntervalInstruction1= "nach einer ungefähren Lösung ein"
OnlyFirstSolutionsDisplayed0 = "Es werden nur die ersten"
OnlyFirstSolutionsDisplayed1 = "10 Lösungen angezeigt."

21
apps/solver/base.en.i18n Normal file
View File

@@ -0,0 +1,21 @@
SolverApp = "Equations"
SolverAppCapital = "EQUATIONS"
AddEquation = "Add equation"
ResolveEquation = "Solve the equation"
ResolveSystem = "Solve the system"
UseEquationModel = "Use an equation template"
UndefinedEquation = "Undefined equation"
TooManyVariables = "There are too many unknowns"
NonLinearSystem = "The system is not linear"
Solution = "Solution"
ApproximateSolution = "Approximate solution"
SearchInverval = "Search interval"
NoSolutionSystem = "The system has no solution"
NoSolutionEquation = "The equation has no solution"
NoSolutionInterval = "No solution found in the interval"
EnterEquation = "Enter an equation"
InfiniteNumberOfSolutions = "There are an infinite number of solutions"
ApproximateSolutionIntervalInstruction0= "Enter the interval to search"
ApproximateSolutionIntervalInstruction1= "for an approximate solution"
OnlyFirstSolutionsDisplayed0 = "Only the first 10 solutions"
OnlyFirstSolutionsDisplayed1 = "are displayed"

21
apps/solver/base.es.i18n Normal file
View File

@@ -0,0 +1,21 @@
SolverApp = "Ecuaciones"
SolverAppCapital = "ECUACIONES"
AddEquation = "Agregar una ecuación"
ResolveEquation = "Resolver la ecuación"
ResolveSystem = "Resolver el sistema"
UseEquationModel = "Usar un modelo de ecuación"
UndefinedEquation = "Ecuación indefinida"
TooManyVariables = "Hay demasiadas incógnitas"
NonLinearSystem = "El sistema no es lineal"
Solution = "Solucion"
ApproximateSolution = "Solución aproximada"
SearchInverval = "Intervalo de búsqueda"
NoSolutionSystem = "El sistema no tiene solución"
NoSolutionEquation = "La ecuación no tiene solución"
NoSolutionInterval = "Ninguna solución encontrada en el intervalo"
EnterEquation = "Escribe una ecuación"
InfiniteNumberOfSolutions = "Hay un número infinito de soluciones"
ApproximateSolutionIntervalInstruction0= "Introduzca el intervalo para"
ApproximateSolutionIntervalInstruction1= "buscar una solución aproximada"
OnlyFirstSolutionsDisplayed0 = "Sólo se muestran las"
OnlyFirstSolutionsDisplayed1 = "10 primeras soluciones"

21
apps/solver/base.fr.i18n Normal file
View File

@@ -0,0 +1,21 @@
SolverApp = "Equations"
SolverAppCapital = "EQUATIONS"
AddEquation = "Ajouter une équation"
ResolveEquation = "Résoudre l'équation"
ResolveSystem = "Résoudre le système"
UseEquationModel = "Utiliser un modèle d'équation"
UndefinedEquation = "Une equation est indéfinie"
TooManyVariables = "Le nombre d'inconnues est trop grand"
NonLinearSystem = "Le système n'est pas linéaire"
Solution = "Solution"
ApproximateSolution = "Solution approchée"
SearchInverval = "Intervalle de recherche"
NoSolutionSystem = "Le systeme n'admet aucune solution"
NoSolutionEquation = "L'équation n'admet aucune solution"
NoSolutionInterval = "Aucune solution trouvée dans cet intervalle"
EnterEquation = "Entrez une équation"
InfiniteNumberOfSolutions = "Le systeme admet une infinite de solutions"
ApproximateSolutionIntervalInstruction0= "Entrez l'intervalle dans lequel"
ApproximateSolutionIntervalInstruction1= "rechercher une solution approchée"
OnlyFirstSolutionsDisplayed0 = "Seulement les 10 premières"
OnlyFirstSolutionsDisplayed1 = "solutions sont affichées"

21
apps/solver/base.pt.i18n Normal file
View File

@@ -0,0 +1,21 @@
SolverApp = "Equações"
SolverAppCapital = "EQUACOES"
AddEquation = "Adicione uma equação"
ResolveEquation = "Resolver a equação"
ResolveSystem = "Resolver o sistema"
UseEquationModel = "Usar um modelo de equação"
UndefinedEquation = "Equação indefinida"
TooManyVariables = "Existem muitas incógnitas"
NonLinearSystem = "O sistema não é linear"
Solution = "Solução"
ApproximateSolution = "Solução aproximada"
SearchInverval = "Intervalo de busca"
NoSolutionSystem = "O sistema não tem solução"
NoSolutionEquation = "A equação não tem solução"
NoSolutionInterval = "Nenhuma solução encontrada em el intervalo"
EnterEquation = "Digite uma equação"
InfiniteNumberOfSolutions = "Existe uma infinidade de soluções"
ApproximateSolutionIntervalInstruction0= "Digite o intervalo para procurar"
ApproximateSolutionIntervalInstruction1= "uma solução aproximada"
OnlyFirstSolutionsDisplayed0 = "Somente as 10 primeiras"
OnlyFirstSolutionsDisplayed1 = "soluções são exibidas"

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