[apps/solver] Display user variables in the solutions

This commit is contained in:
Léa Saviot
2020-01-27 17:58:26 +01:00
parent afbca672fd
commit db0e7f0d8b
15 changed files with 198 additions and 70 deletions

View File

@@ -8,6 +8,7 @@
#include <poincare/horizontal_layout.h>
#include <poincare/preferences.h>
#include <poincare/symbol_abstract.h>
#include <poincare/symbol.h>
#include <poincare/vertical_offset_layout.h>
using namespace Poincare;
@@ -17,19 +18,21 @@ namespace Solver {
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
constexpr KDColor SolutionsController::ContentView::k_backgroundColor;
SolutionsController::ContentView::ContentView(SolutionsController * controller) :
m_warningMessageView0(KDFont::SmallFont, I18n::Message::Default, 0.5f, 0.5f, KDColorBlack, Palette::WallScreenDark),
m_warningMessageView1(KDFont::SmallFont, I18n::Message::Default, 0.5f, 0.5f, KDColorBlack, Palette::WallScreenDark),
m_warningMessageView0(KDFont::SmallFont, I18n::Message::Default, 0.5f, 0.5f, KDColorBlack, k_backgroundColor),
m_warningMessageView1(KDFont::SmallFont, I18n::Message::Default, 0.5f, 0.5f, KDColorBlack, k_backgroundColor),
m_selectableTableView(controller),
m_displayWarningMoreSolutions(false)
{
m_selectableTableView.setBackgroundColor(Palette::WallScreenDark);
m_selectableTableView.setBackgroundColor(k_backgroundColor);
m_selectableTableView.setVerticalCellOverlap(0);
}
void SolutionsController::ContentView::drawRect(KDContext * ctx, KDRect rect) const {
if (m_displayWarningMoreSolutions) {
ctx->fillRect(KDRect(0, 0, bounds().width(), k_topMargin), Palette::WallScreenDark);
ctx->fillRect(KDRect(0, 0, bounds().width(), k_topMargin), k_backgroundColor);
}
}
@@ -80,7 +83,7 @@ SolutionsController::SolutionsController(Responder * parentResponder, EquationSt
m_deltaCell(0.5f, 0.5f),
m_delta2Layout(),
m_contentView(this),
m_shouldReplaceFuncionsButNotSymbols(false)
m_shouldReplaceFunctionsButNotSymbols(false)
{
m_delta2Layout = HorizontalLayout::Builder(VerticalOffsetLayout::Builder(CodePointLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Position::Superscript), LayoutHelper::String("-4ac", 4, KDFont::SmallFont));
const char * deltaB = "Δ=b";
@@ -93,6 +96,7 @@ SolutionsController::SolutionsController(Responder * parentResponder, EquationSt
}
for (int i = 0; i < k_numberOfSymbolCells; i++) {
m_symbolCells[i].setAlignment(0.5f, 0.5f);
m_symbolCells[i].setFont(KDFont::LargeFont);
}
}
@@ -109,7 +113,7 @@ void SolutionsController::viewWillAppear() {
bool requireWarning = false;
if (m_equationStore->type() == EquationStore::Type::Monovariable) {
m_contentView.setWarningMessages(I18n::Message::OnlyFirstSolutionsDisplayed0, I18n::Message::OnlyFirstSolutionsDisplayed1);
requireWarning = m_equationStore->haveMoreApproximationSolutions(App::app()->localContext(), m_shouldReplaceFuncionsButNotSymbols);
requireWarning = m_equationStore->haveMoreApproximationSolutions(App::app()->localContext(), m_shouldReplaceFunctionsButNotSymbols);
} else if (m_equationStore->type() == EquationStore::Type::PolynomialMonovariable && m_equationStore->numberOfSolutions() == 1) {
assert(Preferences::sharedPreferences()->complexFormat() == Preferences::ComplexFormat::Real);
m_contentView.setWarningMessages(I18n::Message::PolynomeHasNoRealSolution0, I18n::Message::PolynomeHasNoRealSolution1);
@@ -158,52 +162,76 @@ Responder * SolutionsController::defaultController() {
/* TableViewDataSource */
int SolutionsController::numberOfRows() const {
return m_equationStore->numberOfSolutions();
return m_equationStore->numberOfSolutions() + (m_equationStore->numberOfUserVariables() > 0 ? 1 + m_equationStore->numberOfUserVariables() : 0);
}
void SolutionsController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
const int rowOfUserVariablesMessage = userVariablesMessageRow();
if (j == rowOfUserVariablesMessage) {
// Predefined varaible used/ignored message
assert(i >= 0);
MessageCell * messageCell = static_cast<MessageCell *>(cell);
messageCell->setHorizontalAlignment(i == 0 ? 1.0f : 0.0f);
if (usedUserVariables()) {
messageCell->setMessage(i == 0 ? I18n::Message::PredefinedVariablesUsedLeft : I18n::Message::PredefinedVariablesUsedRight);
} else {
messageCell->setMessage(i == 0 ? I18n::Message::PredefinedVariablesIgnoredLeft : I18n::Message::PredefinedVariablesIgnoredRight);
}
return;
}
if (i == 0) {
// Name of the variable or discriminant
if (m_equationStore->type() == EquationStore::Type::PolynomialMonovariable && j == m_equationStore->numberOfSolutions()-1) {
// Discriminant
// Formula of the discriminant
EvenOddExpressionCell * deltaCell = static_cast<EvenOddExpressionCell *>(cell);
deltaCell->setLayout(m_delta2Layout);
} else {
EvenOddBufferTextCell * symbolCell = static_cast<EvenOddBufferTextCell *>(cell);
symbolCell->setFont(KDFont::LargeFont);
char bufferSymbol[Poincare::SymbolAbstract::k_maxNameSize+1]; // Hold at maximum Delta = b^2-4ac or the variable name + a digit
switch (m_equationStore->type()) {
case EquationStore::Type::LinearSystem:
char bufferSymbol[Poincare::SymbolAbstract::k_maxNameSize+1]; // Holds at maximum the variable name + a digit
if (rowOfUserVariablesMessage < 0 || j < rowOfUserVariablesMessage) {
// Solution symbol name
if (m_equationStore->type() == EquationStore::Type::LinearSystem) {
/* The system has more than one variable: the cell text is the
* variable name */
strlcpy(bufferSymbol, m_equationStore->variableAtIndex(j), Poincare::SymbolAbstract::k_maxNameSize);
break;
default:
} else {
/* The system has one variable but might have many solutions: the cell
* text is variableX, with X the row index + 1 (e.g. x1, x2,...) */
int length = strlcpy(bufferSymbol, m_equationStore->variableAtIndex(0), Poincare::SymbolAbstract::k_maxNameSize);
bufferSymbol[length++] = j+'1';
bufferSymbol[length++] = j+'1'; // TODO LEA change for 10
bufferSymbol[length] = 0;
break;
}
} else {
// User variable name
assert(rowOfUserVariablesMessage > 0);
strlcpy(bufferSymbol, m_equationStore->userVariableAtIndex(j - rowOfUserVariablesMessage - 1), Poincare::SymbolAbstract::k_maxNameSize);
}
symbolCell->setText(bufferSymbol);
}
} else {
// Value of the variable or discriminant
if (m_equationStore->type() == EquationStore::Type::Monovariable) {
EvenOddBufferTextCell * valueCell = static_cast<EvenOddBufferTextCell *>(cell);
constexpr int precision = Preferences::LargeNumberOfSignificantDigits;
constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(precision);
char bufferValue[bufferSize];
PoincareHelpers::ConvertFloatToText<double>(m_equationStore->approximateSolutionAtIndex(j), bufferValue, bufferSize, precision);
valueCell->setText(bufferValue);
} else {
ScrollableTwoExpressionsCell * valueCell = static_cast<ScrollableTwoExpressionsCell *>(cell);
Poincare::Layout exactLayout = m_equationStore->exactSolutionLayoutsAtIndexAreIdentical(j) ? Poincare::Layout() : m_equationStore->exactSolutionLayoutAtIndex(j, true);
valueCell->setLayouts(exactLayout, m_equationStore->exactSolutionLayoutAtIndex(j, false));
if (!exactLayout.isUninitialized()) {
valueCell->setEqualMessage(m_equationStore->exactSolutionLayoutsAtIndexAreEqual(j) ? I18n::Message::Equal : I18n::Message::AlmostEqual);
if (rowOfUserVariablesMessage < 0 || j < rowOfUserVariablesMessage) {
if (m_equationStore->type() == EquationStore::Type::Monovariable) {
// Values of the solutions
EvenOddBufferTextCell * valueCell = static_cast<EvenOddBufferTextCell *>(cell);
constexpr int precision = Preferences::LargeNumberOfSignificantDigits;
constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(precision);
char bufferValue[bufferSize];
PoincareHelpers::ConvertFloatToText<double>(m_equationStore->approximateSolutionAtIndex(j), bufferValue, bufferSize, precision);
valueCell->setText(bufferValue);
} else {
// Values of the solutions or discriminant
ScrollableTwoExpressionsCell * valueCell = static_cast<ScrollableTwoExpressionsCell *>(cell);
Poincare::Layout exactLayout = m_equationStore->exactSolutionLayoutsAtIndexAreIdentical(j) ? Poincare::Layout() : m_equationStore->exactSolutionLayoutAtIndex(j, true);
valueCell->setLayouts(exactLayout, m_equationStore->exactSolutionLayoutAtIndex(j, false));
if (!exactLayout.isUninitialized()) {
valueCell->setEqualMessage(m_equationStore->exactSolutionLayoutsAtIndexAreEqual(j) ? I18n::Message::Equal : I18n::Message::AlmostEqual);
}
}
} else {
// Values of the solutions or discriminant
ScrollableTwoExpressionsCell * valueCell = static_cast<ScrollableTwoExpressionsCell *>(cell);
const char * symbol = m_equationStore->userVariableAtIndex(j - rowOfUserVariablesMessage - 1);
Poincare::Layout layout = PoincareHelpers::CreateLayout(App::app()->localContext()->expressionForSymbolAbstract(Poincare::Symbol::Builder(symbol, strlen(symbol)), false));
valueCell->setLayouts(Poincare::Layout(), layout);
}
}
EvenOddCell * evenOddCell = static_cast<EvenOddCell *>(cell);
@@ -218,12 +246,22 @@ KDCoordinate SolutionsController::rowHeight(int j) {
if (m_equationStore->type() == EquationStore::Type::Monovariable) {
return k_defaultCellHeight;
}
Poincare::Layout exactLayout = m_equationStore->exactSolutionLayoutAtIndex(j, true);
Poincare::Layout approximateLayout = m_equationStore->exactSolutionLayoutAtIndex(j, false);
KDCoordinate exactLayoutHeight = exactLayout.layoutSize().height();
KDCoordinate approximateLayoutHeight = approximateLayout.layoutSize().height();
KDCoordinate layoutHeight = maxCoordinate(exactLayout.baseline(), approximateLayout.baseline()) + maxCoordinate(exactLayoutHeight-exactLayout.baseline(), approximateLayoutHeight-approximateLayout.baseline());
return layoutHeight + 2 * Metric::CommonSmallMargin;
const int rowOfUserVariablesMessage = userVariablesMessageRow();
if (rowOfUserVariablesMessage < 0 || j < rowOfUserVariablesMessage) {
Poincare::Layout exactLayout = m_equationStore->exactSolutionLayoutAtIndex(j, true);
Poincare::Layout approximateLayout = m_equationStore->exactSolutionLayoutAtIndex(j, false);
KDCoordinate exactLayoutHeight = exactLayout.layoutSize().height();
KDCoordinate approximateLayoutHeight = approximateLayout.layoutSize().height();
KDCoordinate layoutHeight = maxCoordinate(exactLayout.baseline(), approximateLayout.baseline()) + maxCoordinate(exactLayoutHeight-exactLayout.baseline(), approximateLayoutHeight-approximateLayout.baseline());
return layoutHeight + 2 * Metric::CommonSmallMargin;
}
if (j == rowOfUserVariablesMessage) {
return Metric::CommonTopMargin + k_defaultCellHeight + Metric::CommonBottomMargin;
}
// TODO: memoize user symbols if too slow
const char * symbol = m_equationStore->userVariableAtIndex(j - rowOfUserVariablesMessage - 1);
Poincare::Layout layout = PoincareHelpers::CreateLayout(App::app()->localContext()->expressionForSymbolAbstract(Poincare::Symbol::Builder(symbol, strlen(symbol)), false));
return layout.layoutSize().height() + 2 * Metric::CommonSmallMargin;
}
KDCoordinate SolutionsController::cumulatedWidthFromIndex(int i) {
@@ -232,11 +270,9 @@ KDCoordinate SolutionsController::cumulatedWidthFromIndex(int i) {
return 0;
case 1:
return k_symbolCellWidth;
case 2:
return k_symbolCellWidth+k_valueCellWidth;
default:
assert(false);
return 0;
assert(i == 2);
return k_symbolCellWidth+k_valueCellWidth;
}
}
@@ -260,6 +296,8 @@ HighlightCell * SolutionsController::reusableCell(int index, int type) {
return &m_deltaCell;
case k_exactValueCellType:
return &m_exactValueCells[index];
case k_messageCellType:
return &m_messageCells[index];
default:
assert(type == k_approximateValueCellType);
return &m_approximateValueCells[index];
@@ -269,29 +307,43 @@ HighlightCell * SolutionsController::reusableCell(int index, int type) {
int SolutionsController::reusableCellCount(int type) {
switch (type) {
case k_symbolCellType:
return EquationStore::k_maxNumberOfSolutions;
return k_numberOfSymbolCells;
case k_deltaCellType:
return 1;
case k_exactValueCellType:
return EquationStore::k_maxNumberOfExactSolutions;
return k_numberOfExactValueCells;
case k_messageCellType:
return k_numberOfMessageCells;
default:
assert(type == k_approximateValueCellType);
return EquationStore::k_maxNumberOfApproximateSolutions;
return k_numberOfApproximateValueCells;
}
}
int SolutionsController::typeAtLocation(int i, int j) {
const int rowOfUserVariableMessage = userVariablesMessageRow();
if (j == rowOfUserVariableMessage) {
return k_messageCellType;
}
if (i == 0) {
if (m_equationStore->type() == EquationStore::Type::PolynomialMonovariable && j == m_equationStore->numberOfSolutions()-1) {
return k_deltaCellType;
}
return k_symbolCellType;
}
return m_equationStore->type() == EquationStore::Type::Monovariable ? k_approximateValueCellType : k_exactValueCellType;
if ((rowOfUserVariableMessage < 0 || j < rowOfUserVariableMessage) && m_equationStore->type() == EquationStore::Type::Monovariable) {
return k_approximateValueCellType;
}
return k_exactValueCellType;
}
void SolutionsController::didBecomeFirstResponder() {
Container::activeApp()->setFirstResponder(m_contentView.selectableTableView());
}
int SolutionsController::userVariablesMessageRow() const {
assert(m_equationStore->numberOfUserVariables() >= 0);
return m_equationStore->numberOfUserVariables() == 0 ? -1 : m_equationStore->numberOfSolutions();
}
}