Files
Upsilon/apps/graph/graph/graph_controller.cpp
Gabriel Ozouf a6db9688cd [apps/function] Factorize zoom in Function class
The new zoom implemented for ContinuousFunction is now factorized inside
Function to benefit the Sequence class. The same things is done to code
added to Graph::GraphController, which is moved into
FunctionGraphController.
This removes the reimplementation of several methods, most notably
computeYRange, as the implementation for function is general enough to
work on sequences.

Change-Id: I9b8211354064f46c3fa3dde3191dcb39d627a1d2
2020-11-04 15:30:53 +01:00

137 lines
5.6 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 * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, curveViewRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, previousModelsVersions, 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();
}
bool GraphController::shouldSetDefaultOnModelChange() const {
return functionStore()->displaysNonCartesianFunctions();
}
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;
}
}