Files
Upsilon/apps/graph/graph/graph_controller.cpp
Gabriel Ozouf 8970e294aa [apps] Remove modelVersion from curves apps
The graph range used to be reset to default whenever all functions were
modified. As we no longer want to reset the range without the user's
input, we do not need to track whether the functions changed at all.

/!\ As of this commit, there is no longer a way to restore the default
zoom, until a new automatic zoom button is added.

Change-Id: Ie74e8fd61e13055fa6ce2b2d1e883182d4ecffce
2020-11-04 15:58:09 +01:00

133 lines
5.4 KiB
C++

#include "graph_controller.h"
#include "../app.h"
#include <algorithm>
using namespace Poincare;
using namespace Shared;
namespace Graph {
GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, Shared::InteractiveCurveViewRange * curveViewRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, curveViewRange, &m_view, cursor, indexFunctionSelectedByCursor, rangeVersion, angleUnitVersion),
m_bannerView(this, inputEventHandlerDelegate, this),
m_view(curveViewRange, m_cursor, &m_bannerView, &m_cursorView),
m_graphRange(curveViewRange),
m_curveParameterController(inputEventHandlerDelegate, curveViewRange, &m_bannerView, m_cursor, &m_view, this),
m_displayDerivativeInBanner(false)
{
m_graphRange->setDelegate(this);
}
I18n::Message GraphController::emptyMessage() {
if (functionStore()->numberOfDefinedModels() == 0) {
return I18n::Message::NoFunction;
}
return I18n::Message::NoActivatedFunction;
}
void GraphController::viewWillAppear() {
m_view.drawTangent(false);
#ifdef GRAPH_CURSOR_SPEEDUP
m_cursorView.resetMemoization();
#endif
m_view.setCursorView(&m_cursorView);
FunctionGraphController::viewWillAppear();
selectFunctionWithCursor(indexFunctionSelectedByCursor());
}
bool GraphController::defaultRangeIsNormalized() const {
return functionStore()->displaysNonCartesianFunctions();
}
void GraphController::selectFunctionWithCursor(int functionIndex) {
FunctionGraphController::selectFunctionWithCursor(functionIndex);
ExpiringPointer<ContinuousFunction> f = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(functionIndex));
m_cursorView.setColor(f->color());
}
void GraphController::reloadBannerView() {
Ion::Storage::Record record = functionStore()->activeRecordAtIndex(indexFunctionSelectedByCursor());
bool displayDerivative = m_displayDerivativeInBanner &&
functionStore()->modelForRecord(record)->plotType() == ContinuousFunction::PlotType::Cartesian;
m_bannerView.setNumberOfSubviews(Shared::XYBannerView::k_numberOfSubviews + displayDerivative);
FunctionGraphController::reloadBannerView();
if (!displayDerivative) {
return;
}
reloadDerivativeInBannerViewForCursorOnFunction(m_cursor, record);
}
bool GraphController::moveCursorHorizontally(int direction, int scrollSpeed) {
Ion::Storage::Record record = functionStore()->activeRecordAtIndex(indexFunctionSelectedByCursor());
return privateMoveCursorHorizontally(m_cursor, direction, m_graphRange, k_numberOfCursorStepsInGradUnit, record, scrollSpeed);
}
int GraphController::nextCurveIndexVertically(bool goingUp, int currentSelectedCurve, Poincare::Context * context) const {
int nbOfActiveFunctions = 0;
if (!functionStore()->displaysNonCartesianFunctions(&nbOfActiveFunctions)) {
return FunctionGraphController::nextCurveIndexVertically(goingUp, currentSelectedCurve, context);
}
int nextActiveFunctionIndex = currentSelectedCurve + (goingUp ? -1 : 1);
return nextActiveFunctionIndex >= nbOfActiveFunctions ? -1 : nextActiveFunctionIndex;
}
double GraphController::defaultCursorT(Ion::Storage::Record record) {
ExpiringPointer<ContinuousFunction> function = functionStore()->modelForRecord(record);
if (function->plotType() == ContinuousFunction::PlotType::Cartesian) {
return FunctionGraphController::defaultCursorT(record);
}
return function->tMin();
}
void GraphController::jumpToLeftRightCurve(double t, int direction, int functionsCount, Ion::Storage::Record record) {
if (functionsCount == 1) {
return;
}
int nextCurveIndex = -1;
double xDelta = DBL_MAX;
double nextY = 0.0;
double nextT = 0.0;
for (int i = 0; i < functionsCount; i++) {
Ion::Storage::Record currentRecord = functionStore()->activeRecordAtIndex(i);
if (currentRecord == record) {
continue;
}
ExpiringPointer<ContinuousFunction> f = functionStore()->modelForRecord(currentRecord);
assert(f->plotType() == ContinuousFunction::PlotType::Cartesian);
/* Select the closest horizontal curve, then the closest vertically, then
* the lowest curve index. */
double currentTMin = f->tMin();
double currentTMax = f->tMax();
assert(!std::isnan(currentTMin));
assert(!std::isnan(currentTMax));
if ((direction > 0 && currentTMax > t)
||(direction < 0 && currentTMin < t))
{
double currentXDelta = direction > 0 ?
(t >= currentTMin ? 0.0 : currentTMin - t) :
(t <= currentTMax ? 0.0 : t - currentTMax);
assert(currentXDelta >= 0.0);
if (currentXDelta <= xDelta) {
double potentialNextTMin = f->tMin();
double potentialNextTMax = f->tMax();
double potentialNextT = std::max(potentialNextTMin, std::min(potentialNextTMax, t));
Coordinate2D<double> xy = f->evaluateXYAtParameter(potentialNextT, App::app()->localContext());
if (currentXDelta < xDelta || std::abs(xy.x2() - m_cursor->y()) < std::abs(nextY - m_cursor->y())) {
nextCurveIndex = i;
xDelta = currentXDelta;
nextY = xy.x2();
nextT = potentialNextT;
}
}
}
}
if (nextCurveIndex < 0) {
return;
}
m_cursor->moveTo(nextT, nextT, nextY);
selectFunctionWithCursor(nextCurveIndex);
return;
}
}