diff --git a/apps/solver/Makefile b/apps/solver/Makefile index e6996c14a..de0f341b5 100644 --- a/apps/solver/Makefile +++ b/apps/solver/Makefile @@ -7,6 +7,7 @@ app_objs += $(addprefix apps/solver/,\ equation.o\ equation_store.o\ list_controller.o\ + solutions_controller.o\ ) i18n_files += $(addprefix apps/solver/,\ diff --git a/apps/solver/solutions_controller.cpp b/apps/solver/solutions_controller.cpp new file mode 100644 index 000000000..023d6c04b --- /dev/null +++ b/apps/solver/solutions_controller.cpp @@ -0,0 +1,213 @@ +#include "solutions_controller.h" +#include "app.h" +#include "../constant.h" +#include +#include + +using namespace Poincare; +using namespace Shared; + +namespace Solver { + +SolutionsController::SolutionsController(Responder * parentResponder, EquationStore * equationStore) : + ViewController(parentResponder), + m_equationStore(equationStore), + m_selectableTableView(this) +{ + m_selectableTableView.setBackgroundColor(Palette::WallScreenDark); + m_selectableTableView.setVerticalCellOverlap(0); + for (int i = 0; i < EquationStore::k_maxNumberOfExactSolutions; i++) { + m_exactValueCells[i].setParentResponder(&m_selectableTableView); + } + for (int i = 0; i < EquationStore::k_maxNumberOfSolutions; i++) { + m_symbolCells[i].setAlignment(0.5f, 0.5f); + } +} + +/* ViewController */ +const char * SolutionsController::title() { + if (m_equationStore->type() == EquationStore::Type::Monovariable) { + return I18n::translate(I18n::Message::ApproximateSolution); + } + return I18n::translate(I18n::Message::Solution); +} + +View * SolutionsController::view() { + return &m_selectableTableView; +} + +void SolutionsController::viewWillAppear() { + ViewController::viewWillAppear(); + m_selectableTableView.reloadData(); + if (selectedRow() < 0) { + selectCellAtLocation(0, 0); + } +} + +/* AlternateEmptyRowDelegate */ + +bool SolutionsController::isEmpty() const { + if (m_equationStore->numberOfSolutions() == 0 || m_equationStore->numberOfSolutions() == INT_MAX) { + return true; + } + return false; +} + +I18n::Message SolutionsController::emptyMessage() { + if (m_equationStore->numberOfSolutions() == INT_MAX) { + return I18n::Message::InfiniteNumberOfSolutions; + } + return I18n::Message::NoSolution; +} + +Responder * SolutionsController::defaultController() { + return parentResponder(); +} + +/* TableViewDataSource */ + +int SolutionsController::numberOfRows() { + if (m_equationStore->type() == EquationStore::Type::PolynomialMonovariable) { + return m_equationStore->numberOfSolutions() + 1; // add the delta row + } + return m_equationStore->numberOfSolutions(); +} + +int SolutionsController::numberOfColumns() { + return 2; +} + +void SolutionsController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { + EvenOddCell * evenOddCell = static_cast(cell); + evenOddCell->setEven(j%2 == 0); + if (i == 0) { + EvenOddBufferTextCell * symbolCell = static_cast(cell); + symbolCell->setFontSize(KDText::FontSize::Large); + char bufferSymbol[10]; // hold at maximum Delta = b^2-4ac + switch (m_equationStore->type()) { + case EquationStore::Type::LinearSystem: + bufferSymbol[0] = m_equationStore->variableAtIndex(j); + bufferSymbol[1] = 0; + break; + case EquationStore::Type::PolynomialMonovariable: + if (j == m_equationStore->numberOfSolutions()) { + symbolCell->setFontSize(KDText::FontSize::Small); + strlcpy(bufferSymbol, I18n::translate(I18n::Message::DiscriminantFormulaDegree2), 10); + break; + } + default: + bufferSymbol[0] = m_equationStore->variableAtIndex(0); + bufferSymbol[1] = j+'0'; + bufferSymbol[2] = 0; + break; + } + symbolCell->setText(bufferSymbol); + return; + } + if (m_equationStore->type() == EquationStore::Type::Monovariable) { + EvenOddBufferTextCell * valueCell = static_cast(cell); + char bufferValue[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; + PrintFloat::convertFloatToText(m_equationStore->approximateSolutionAtIndex(j), bufferValue, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + valueCell->setText(bufferValue); + } else { + Shared::ScrollableExactApproximateExpressionsCell * valueCell = static_cast(cell); + Poincare::ExpressionLayout * exactSolutionLayouts[2] = {m_equationStore->exactSolutionLayoutAtIndex(j, false), m_equationStore->exactSolutionLayoutAtIndex(j, true)}; + valueCell->setExpressions(exactSolutionLayouts); + valueCell->setEqualMessage(I18n::Message::AlmostEqual);// TODO: true equal when possible? + } +} + +KDCoordinate SolutionsController::columnWidth(int i) { + if (i == 0) { + return k_symbolCellWidth; + } + return k_valueCellWidth; +} + +KDCoordinate SolutionsController::rowHeight(int j) { + if (m_equationStore->type() == EquationStore::Type::Monovariable) { + return k_defaultCellHeight; + } + Poincare::ExpressionLayout * exactLayout = m_equationStore->exactSolutionLayoutAtIndex(j, true); + Poincare::ExpressionLayout * approximateLayout = m_equationStore->exactSolutionLayoutAtIndex(j, false); + KDCoordinate exactLayoutHeight = exactLayout->size().height(); + KDCoordinate approximateLayoutHeight = approximateLayout->size().height(); + KDCoordinate layoutHeight = max(exactLayout->baseline(), approximateLayout->baseline()) + max(exactLayoutHeight-exactLayout->baseline(), approximateLayoutHeight-approximateLayout->baseline()); + return layoutHeight+k_defaultCellHeight; +} + +KDCoordinate SolutionsController::cumulatedHeightFromIndex(int j) { + int result = 0; + for (int k = 0; k < j; k++) { + result += rowHeight(k); + } + return result; +} + +int SolutionsController::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; +} + +KDCoordinate SolutionsController::cumulatedWidthFromIndex(int i) { + switch (i) { + case 0: + return 0; + case 1: + return k_symbolCellWidth; + case 2: + return k_symbolCellWidth+k_valueCellWidth; + default: + assert(false); + return 0; + } +} + +int SolutionsController::indexFromCumulatedWidth(KDCoordinate offsetX) { + if (offsetX <= k_symbolCellWidth) { + return 0; + } else { + if (offsetX <= k_symbolCellWidth+k_valueCellWidth) + return 1; + else { + return 2; + } + } +} + +HighlightCell * SolutionsController::reusableCell(int index, int type) { + if (type == 0) { + return &m_symbolCells[index]; + } else if (type == 1) { + return &m_exactValueCells[index]; + } + return &m_approximateValueCells[index]; +} + +int SolutionsController::reusableCellCount(int type) { + switch (type) { + case 0: + return EquationStore::k_maxNumberOfSolutions; + case 1: + return EquationStore::k_maxNumberOfExactSolutions; + default: + return EquationStore::k_maxNumberOfApproximateSolutions; + } +} + +int SolutionsController::typeAtLocation(int i, int j) { + if (i == 0) { + return 0; + } + return m_equationStore->type() == EquationStore::Type::Monovariable ? 2 : 1; +} + +void SolutionsController::didBecomeFirstResponder() { + app()->setFirstResponder(&m_selectableTableView); +} + +} diff --git a/apps/solver/solutions_controller.h b/apps/solver/solutions_controller.h new file mode 100644 index 000000000..a1365d052 --- /dev/null +++ b/apps/solver/solutions_controller.h @@ -0,0 +1,51 @@ +#ifndef SOLVER_SOLUTIONS_CONTROLLER_H +#define SOLVER_SOLUTIONS_CONTROLLER_H + +#include +#include "equation_store.h" +#include "../shared/scrollable_exact_approximate_expressions_cell.h" +#include "../i18n.h" + +namespace Solver { + +class SolutionsController : public ViewController, public AlternateEmptyViewDelegate, public SelectableTableViewDataSource, public TableViewDataSource { +public: + SolutionsController(Responder * parentResponder, EquationStore * equationStore); + /* ViewController */ + const char * title() override; + View * view() override; + void viewWillAppear() override; + /* AlternateEmptyViewDelegate */ + bool isEmpty() const override; + virtual I18n::Message emptyMessage() override; + virtual Responder * defaultController() override; + /* TableViewDataSource */ + int numberOfRows() override; + int numberOfColumns() override; + void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) 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; + int typeAtLocation(int i, int j) override; + /* Responder */ + void didBecomeFirstResponder() override; +private: + constexpr static int k_symbolCellWidth = 100; + constexpr static int k_valueCellWidth = 180; + constexpr static KDCoordinate k_defaultCellHeight = 20; + EquationStore * m_equationStore; + EvenOddBufferTextCell m_symbolCells[EquationStore::k_maxNumberOfSolutions]; + Shared::ScrollableExactApproximateExpressionsCell m_exactValueCells[EquationStore::k_maxNumberOfExactSolutions]; + EvenOddBufferTextCell m_approximateValueCells[EquationStore::k_maxNumberOfApproximateSolutions]; + SelectableTableView m_selectableTableView; +}; + +} + +#endif +