mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-18 21:30:38 +01:00
[apps] Factorize closestCurveIndexVertically from Regression and Graph
This commit is contained in:
@@ -256,43 +256,37 @@ bool GraphController::moveCursorHorizontally(int direction) {
|
||||
}
|
||||
double x = direction > 0 ? m_cursor->x() + m_store->xGridUnit()/k_numberOfCursorStepsInGradUnit :
|
||||
m_cursor->x() - m_store->xGridUnit()/k_numberOfCursorStepsInGradUnit;
|
||||
double y = m_store->yValueForXValue(*m_selectedSeriesIndex, x, globalContext());
|
||||
double y = yValue(*m_selectedSeriesIndex, x, globalContext());
|
||||
m_cursor->moveTo(x, y);
|
||||
m_store->panToMakePointVisible(x, y, cursorTopMarginRatio(), k_cursorRightMarginRatio, cursorBottomMarginRatio(), k_cursorLeftMarginRatio);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GraphController::moveCursorVertically(int direction) {
|
||||
int closestRegressionSeries = -1;
|
||||
Poincare::Context * context = globalContext();
|
||||
double x = m_cursor->x();
|
||||
double y = m_cursor->y();
|
||||
|
||||
// Find the closest regression
|
||||
int selectedRegressionIndex = *m_selectedDotIndex == -1 ? *m_selectedSeriesIndex : -1;
|
||||
int closestRegressionSeries = InteractiveCurveViewController::closestCurveIndexVertically(direction > 0, selectedRegressionIndex, context);
|
||||
|
||||
// Find the closest dot
|
||||
int closestDotSeries = -1;
|
||||
int dotSelected = -1;
|
||||
Poincare::Context * globContext = globalContext();
|
||||
|
||||
if (*m_selectedDotIndex == -1) {
|
||||
// The current cursor is on a regression
|
||||
// Check the closest regression
|
||||
closestRegressionSeries = m_store->closestVerticalRegression(direction, m_cursor->x(), m_cursor->y(), *m_selectedSeriesIndex, globContext);
|
||||
// Check the closest dot
|
||||
dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), direction > 0 ? -FLT_MAX : FLT_MAX, *m_selectedSeriesIndex, *m_selectedDotIndex, &closestDotSeries, globContext);
|
||||
} else {
|
||||
// The current cursor is on a dot
|
||||
// Check the closest regression
|
||||
closestRegressionSeries = m_store->closestVerticalRegression(direction, m_cursor->x(), m_cursor->y(), -1, globContext);
|
||||
// Check the closest dot
|
||||
dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), m_cursor->y(), *m_selectedSeriesIndex, *m_selectedDotIndex, &closestDotSeries, globContext);
|
||||
}
|
||||
double selectedDotY = *m_selectedDotIndex == -1 ? (direction > 0 ? -DBL_MAX : DBL_MAX) : y;
|
||||
int dotSelected = m_store->closestVerticalDot(direction, x, selectedDotY, *m_selectedSeriesIndex, *m_selectedDotIndex, &closestDotSeries, context);
|
||||
|
||||
// Choose between selecting the regression or the dot
|
||||
bool validRegression = closestRegressionSeries > -1;
|
||||
bool validDot = dotSelected >= 0 && dotSelected <= m_store->numberOfPairsOfSeries(closestDotSeries);
|
||||
|
||||
if (validRegression && validDot) {
|
||||
/* Compare the abscissa distances to select either the dot or the
|
||||
* regression. If they are equal, compare the ordinate distances. */
|
||||
double dotDistanceX = -1;
|
||||
if (dotSelected == m_store->numberOfPairsOfSeries(closestDotSeries)) {
|
||||
dotDistanceX = std::fabs(m_store->meanOfColumn(closestDotSeries, 0) - m_cursor->x());
|
||||
dotDistanceX = std::fabs(m_store->meanOfColumn(closestDotSeries, 0) - x);
|
||||
} else {
|
||||
dotDistanceX = std::fabs(m_store->get(closestDotSeries, 0, dotSelected) - m_cursor->x());
|
||||
dotDistanceX = std::fabs(m_store->get(closestDotSeries, 0, dotSelected) - x);
|
||||
}
|
||||
if (dotDistanceX != 0) {
|
||||
/* The regression X distance to the point is 0, so it is closer than the
|
||||
@@ -300,12 +294,12 @@ bool GraphController::moveCursorVertically(int direction) {
|
||||
validDot = false;
|
||||
} else {
|
||||
// Compare the y distances
|
||||
double regressionDistanceY = std::fabs(m_store->yValueForXValue(closestRegressionSeries, m_cursor->x(), globContext) - m_cursor->y());
|
||||
double regressionDistanceY = std::fabs(yValue(closestRegressionSeries, x, context) - y);
|
||||
double dotDistanceY = -1;
|
||||
if (dotSelected == m_store->numberOfPairsOfSeries(closestDotSeries)) {
|
||||
dotDistanceY = std::fabs(m_store->meanOfColumn(closestDotSeries, 1) - m_cursor->y());
|
||||
dotDistanceY = std::fabs(m_store->meanOfColumn(closestDotSeries, 1) - y);
|
||||
} else {
|
||||
dotDistanceY = std::fabs(m_store->get(closestDotSeries, 1, dotSelected) - m_cursor->y());
|
||||
dotDistanceY = std::fabs(m_store->get(closestDotSeries, 1, dotSelected) - y);
|
||||
}
|
||||
if (regressionDistanceY <= dotDistanceY) {
|
||||
validDot = false;
|
||||
@@ -314,28 +308,36 @@ bool GraphController::moveCursorVertically(int direction) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!validDot && validRegression) {
|
||||
|
||||
assert(!validDot || !validRegression);
|
||||
|
||||
if (validRegression) {
|
||||
// Select the regression
|
||||
*m_selectedSeriesIndex = closestRegressionSeries;
|
||||
selectRegressionCurve();
|
||||
m_cursor->moveTo(m_cursor->x(), m_store->yValueForXValue(*m_selectedSeriesIndex, m_cursor->x(), globContext));
|
||||
m_cursor->moveTo(x, yValue(*m_selectedSeriesIndex, x, context));
|
||||
m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), cursorTopMarginRatio(), k_cursorRightMarginRatio, cursorBottomMarginRatio(), k_cursorLeftMarginRatio);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (validDot && !validRegression) {
|
||||
if (validDot) {
|
||||
// Select the dot
|
||||
m_view.setCursorView(&m_crossCursorView);
|
||||
*m_selectedSeriesIndex = closestDotSeries;
|
||||
*m_selectedDotIndex = dotSelected;
|
||||
if (dotSelected == m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) {
|
||||
// Select the mean dot
|
||||
m_cursor->moveTo(m_store->meanOfColumn(*m_selectedSeriesIndex, 0), m_store->meanOfColumn(*m_selectedSeriesIndex, 1));
|
||||
m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), cursorTopMarginRatio(), k_cursorRightMarginRatio, cursorBottomMarginRatio(), k_cursorLeftMarginRatio);
|
||||
return true;
|
||||
} else {
|
||||
// Select a data point dot
|
||||
m_cursor->moveTo(m_store->get(*m_selectedSeriesIndex, 0, *m_selectedDotIndex), m_store->get(*m_selectedSeriesIndex, 1, *m_selectedDotIndex));
|
||||
m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), cursorTopMarginRatio(), k_cursorRightMarginRatio, cursorBottomMarginRatio(), k_cursorLeftMarginRatio);
|
||||
}
|
||||
m_cursor->moveTo(m_store->get(*m_selectedSeriesIndex, 0, *m_selectedDotIndex), m_store->get(*m_selectedSeriesIndex, 1, *m_selectedDotIndex));
|
||||
m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), cursorTopMarginRatio(), k_cursorRightMarginRatio, cursorBottomMarginRatio(), k_cursorLeftMarginRatio);
|
||||
return true;
|
||||
}
|
||||
|
||||
// There was no suitable selection
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -351,6 +353,22 @@ bool GraphController::isCursorVisible() {
|
||||
return interactiveCurveViewRange()->isCursorVisible(cursorTopMarginRatio(), k_cursorRightMarginRatio, cursorBottomMarginRatio(), k_cursorLeftMarginRatio);
|
||||
}
|
||||
|
||||
bool GraphController::closestCurveIndexIsSuitable(int newIndex, int currentIndex) const {
|
||||
return newIndex != currentIndex && !m_store->seriesIsEmpty(newIndex);
|
||||
}
|
||||
|
||||
double GraphController::yValue(int curveIndex, double x, Poincare::Context * context) const {
|
||||
return m_store->yValueForXValue(curveIndex, x, context);
|
||||
}
|
||||
|
||||
bool GraphController::suitableYValue(double y) const {
|
||||
return m_store->yMin() <= y && y <= m_store->yMax();
|
||||
}
|
||||
|
||||
int GraphController::numberOfCurves() const {
|
||||
return Store::k_numberOfSeries;
|
||||
}
|
||||
|
||||
float GraphController::cursorBottomMarginRatio() {
|
||||
float f = (m_view.cursorView()->minimalSizeForOptimalDisplay().height()/2 + 2 + estimatedBannerHeight())/k_viewHeight;
|
||||
return f;
|
||||
|
||||
@@ -28,29 +28,34 @@ private:
|
||||
constexpr static int k_maxLegendLength = 16;
|
||||
constexpr static int k_maxNumberOfCharacters = 50;
|
||||
constexpr static float k_viewHeight = 174.0f;
|
||||
|
||||
Poincare::Context * globalContext();
|
||||
Shared::CurveView * curveView() override;
|
||||
Shared::InteractiveCurveViewRange * interactiveCurveViewRange() override;
|
||||
bool handleEnter() override;
|
||||
void reloadBannerView() override;
|
||||
void initRangeParameters() override;
|
||||
void initCursorParameters() override;
|
||||
bool moveCursorHorizontally(int direction) override;
|
||||
bool moveCursorVertically(int direction) override;
|
||||
uint32_t modelVersion() override;
|
||||
uint32_t rangeVersion() override;
|
||||
bool isCursorVisible() override;
|
||||
|
||||
// InteractiveCurveViewController
|
||||
float displayTopMarginRatio() override;
|
||||
float displayBottomMarginRatio() override;
|
||||
|
||||
float cursorTopMarginRatio() { return 0.07f; } // (cursorHeight/2) / graphViewHeight
|
||||
float cursorBottomMarginRatio();
|
||||
float estimatedBannerHeight() const;
|
||||
|
||||
// SimpleInteractiveCurveViewController
|
||||
void reloadBannerView() override;
|
||||
bool moveCursorHorizontally(int direction) override;
|
||||
Shared::InteractiveCurveViewRange * interactiveCurveViewRange() override;
|
||||
Shared::CurveView * curveView() override;
|
||||
bool handleEnter() override;
|
||||
|
||||
// InteractiveCurveViewController
|
||||
void initRangeParameters() override;
|
||||
void initCursorParameters() override;
|
||||
bool moveCursorVertically(int direction) override;
|
||||
uint32_t modelVersion() override;
|
||||
uint32_t rangeVersion() override;
|
||||
bool isCursorVisible() override;
|
||||
bool closestCurveIndexIsSuitable(int newIndex, int currentIndex) const override;
|
||||
double yValue(int curveIndex, double x, Poincare::Context * context) const override;
|
||||
bool suitableYValue(double y) const override;
|
||||
int numberOfCurves() const override;
|
||||
|
||||
// InteractiveCurveViewRangeDelegate
|
||||
Shared::InteractiveCurveViewRangeDelegate::Range computeYRange(Shared::InteractiveCurveViewRange * interactiveCurveViewRange) override;
|
||||
float displayTopMarginRatio() override;
|
||||
float displayBottomMarginRatio() override; Shared::InteractiveCurveViewRangeDelegate::Range computeYRange(Shared::InteractiveCurveViewRange * interactiveCurveViewRange) override;
|
||||
|
||||
Shared::CursorView m_crossCursorView;
|
||||
Shared::RoundCursorView m_roundCursorView;
|
||||
|
||||
@@ -43,56 +43,6 @@ void Store::setSeriesRegressionType(int series, Model::Type type) {
|
||||
}
|
||||
}
|
||||
|
||||
int Store::closestVerticalRegression(int direction, double x, double y, int currentRegressionSeries, Poincare::Context * globalContext) {
|
||||
/* The conditions to test on all the regressions are in this order:
|
||||
* - the current regression is not the current regression
|
||||
* - the next regression point should be within the window abscissa bounds
|
||||
* - it is the closest one in abscissa to x
|
||||
* - it is above y if direction > 0 and below otherwise */
|
||||
|
||||
int nextRegressionSeries = -1;
|
||||
float nextY = direction > 0 ? FLT_MAX : -FLT_MAX;
|
||||
|
||||
for (int i = 0; i < k_numberOfSeries; i ++) {
|
||||
if (i == currentRegressionSeries || seriesIsEmpty(i)) {
|
||||
continue;
|
||||
}
|
||||
double newY = yValueForXValue(i, x, globalContext);
|
||||
if (newY < m_yMin || newY > m_yMax) {
|
||||
continue;
|
||||
}
|
||||
bool isNextRegression = false;
|
||||
/* Choosing the next regression to select quite complex because we need to
|
||||
* take care of regressions that have the same values at the current x. When
|
||||
* moving up, if several regressions have the same value, we select the
|
||||
* regression of higher index. When going down, we select the regression of
|
||||
* lower index. */
|
||||
if (direction > 0) {
|
||||
if (newY > y && newY < nextY) {
|
||||
isNextRegression = true;
|
||||
} else if (newY == nextY) {
|
||||
assert(i > nextRegressionSeries);
|
||||
isNextRegression = true;
|
||||
} else if (newY == y && i < currentRegressionSeries) {
|
||||
isNextRegression = true;
|
||||
}
|
||||
} else {
|
||||
if (newY < y && newY > nextY) {
|
||||
isNextRegression = true;
|
||||
} else if (newY == nextY) {
|
||||
assert(i > nextRegressionSeries);
|
||||
} else if (newY == y && i > currentRegressionSeries) {
|
||||
isNextRegression = true;
|
||||
}
|
||||
}
|
||||
if (isNextRegression) {
|
||||
nextY = newY;
|
||||
nextRegressionSeries = i;
|
||||
}
|
||||
}
|
||||
return nextRegressionSeries;
|
||||
}
|
||||
|
||||
/* Dots */
|
||||
|
||||
int Store::closestVerticalDot(int direction, double x, double y, int currentSeries, int currentDot, int * nextSeries, Poincare::Context * globalContext) {
|
||||
|
||||
@@ -35,9 +35,7 @@ public:
|
||||
assert((int)m_regressionTypes[series] >= 0 && (int)m_regressionTypes[series] < Model::k_numberOfModels);
|
||||
return regressionModel((int)m_regressionTypes[series]);
|
||||
}
|
||||
/* Return the series index of the closest regression at abscissa x, above
|
||||
* ordinate y if direction > 0, below otherwise */
|
||||
int closestVerticalRegression(int direction, double x, double y, int currentRegressionSeries, Poincare::Context * globalContext);
|
||||
|
||||
// Dots
|
||||
/* Return the closest dot to abscissa x above the regression curve if
|
||||
* direction > 0, below otherwise */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "interactive_curve_view_controller.h"
|
||||
#include "text_field_delegate_app.h"
|
||||
#include <cmath>
|
||||
#include <float.h>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace Poincare;
|
||||
@@ -161,4 +162,49 @@ StackViewController * InteractiveCurveViewController::stackController() const{
|
||||
return (StackViewController *)(parentResponder()->parentResponder()->parentResponder());
|
||||
}
|
||||
|
||||
int InteractiveCurveViewController::closestCurveIndexVertically(bool goingUp, int currentCurveIndex, Poincare::Context * context) const {
|
||||
double x = m_cursor->x();
|
||||
double y = m_cursor->y();
|
||||
double nextY = goingUp ? DBL_MAX : -DBL_MAX;
|
||||
int nextCurveIndex = -1;
|
||||
int curvesCount = numberOfCurves();
|
||||
for (int i = 0; i < curvesCount; i++) {
|
||||
if (!closestCurveIndexIsSuitable(i, currentCurveIndex)) {
|
||||
continue;
|
||||
}
|
||||
double newY = yValue(i, x, context);
|
||||
if (!suitableYValue(newY)) {
|
||||
continue;
|
||||
}
|
||||
bool isNextCurve = false;
|
||||
/* Choosing the closest vertical curve is quite complex because we need to
|
||||
* take care of curves that have the same values at the current x. When
|
||||
* moving up, if several curves have the same value, we choose the curve
|
||||
* of higher index. When going down, we select the curve of lower index. */
|
||||
if (goingUp) {
|
||||
if (newY > y && newY < nextY) {
|
||||
isNextCurve = true;
|
||||
} else if (newY == nextY) {
|
||||
assert(i > nextCurveIndex);
|
||||
isNextCurve = true;
|
||||
} else if (newY == y && i < currentCurveIndex) {
|
||||
isNextCurve = true;
|
||||
}
|
||||
} else {
|
||||
if (newY < y && newY > nextY) {
|
||||
isNextCurve = true;
|
||||
} else if (newY == nextY) {
|
||||
assert(i > nextCurveIndex);
|
||||
} else if (newY == y && i > currentCurveIndex) {
|
||||
isNextCurve = true;
|
||||
}
|
||||
}
|
||||
if (isNextCurve) {
|
||||
nextY = newY;
|
||||
nextCurveIndex = i;
|
||||
}
|
||||
}
|
||||
return nextCurveIndex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,6 +44,14 @@ protected:
|
||||
virtual uint32_t modelVersion() = 0;
|
||||
virtual uint32_t rangeVersion() = 0;
|
||||
virtual bool isCursorVisible() = 0;
|
||||
|
||||
// Closest vertical curve helper
|
||||
int closestCurveIndexVertically(bool goingUp, int currentSelectedCurve, Poincare::Context * context) const;
|
||||
virtual bool closestCurveIndexIsSuitable(int newIndex, int currentIndex) const { assert(false); return false; }
|
||||
virtual double yValue(int curveIndex, double x, Poincare::Context * context) const { assert(false); return 0; }
|
||||
virtual bool suitableYValue(double y) const { return true; }
|
||||
virtual int numberOfCurves() const { assert(false); return 0; }
|
||||
|
||||
OkView m_okView;
|
||||
private:
|
||||
uint32_t * m_modelVersion;
|
||||
|
||||
@@ -119,6 +119,11 @@ double StorageFunctionGraphController::defaultCursorAbscissa() {
|
||||
return (interactiveCurveViewRange()->xMin()+interactiveCurveViewRange()->xMax())/2.0f;
|
||||
}
|
||||
|
||||
StorageFunctionStore * StorageFunctionGraphController::functionStore() const {
|
||||
StorageFunctionApp * myApp = static_cast<StorageFunctionApp *>(app());
|
||||
return myApp->functionStore();
|
||||
}
|
||||
|
||||
void StorageFunctionGraphController::initCursorParameters() {
|
||||
double x = defaultCursorAbscissa();
|
||||
StorageFunctionApp * myApp = static_cast<StorageFunctionApp *>(app());
|
||||
@@ -136,50 +141,14 @@ void StorageFunctionGraphController::initCursorParameters() {
|
||||
|
||||
bool StorageFunctionGraphController::moveCursorVertically(int direction) {
|
||||
int currentActiveFunctionIndex = indexFunctionSelectedByCursor();
|
||||
StorageFunctionApp * myApp = static_cast<StorageFunctionApp *>(app());
|
||||
double y = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(currentActiveFunctionIndex))->evaluateAtAbscissa(m_cursor->x(), myApp->localContext());
|
||||
int nextActiveFunctionIndex = -1;
|
||||
double nextY = direction > 0 ? DBL_MAX : -DBL_MAX;
|
||||
int activeFunctionsCount = functionStore()->numberOfActiveFunctions();
|
||||
for (int i = 0; i < activeFunctionsCount; i++) {
|
||||
if (i == currentActiveFunctionIndex) {
|
||||
continue;
|
||||
}
|
||||
double newY = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i))->evaluateAtAbscissa(m_cursor->x(), myApp->localContext());
|
||||
bool isNextFunction = false;
|
||||
/* Choosing the next function to select quite complex because we need to
|
||||
* take care of functions that have the same values at the current x. When
|
||||
* moving up, if several functions have the same value, we select the
|
||||
* function of higher index. When going down, we select the function of
|
||||
* lower index. */
|
||||
if (direction > 0) {
|
||||
if (newY > y && newY < nextY) {
|
||||
isNextFunction = true;
|
||||
} else if (newY == nextY) {
|
||||
assert(i > nextActiveFunctionIndex);
|
||||
isNextFunction = true;
|
||||
} else if (newY == y && i < currentActiveFunctionIndex) {
|
||||
isNextFunction = true;
|
||||
}
|
||||
} else {
|
||||
if (newY < y && newY > nextY) {
|
||||
isNextFunction = true;
|
||||
} else if (newY == nextY) {
|
||||
assert(i > nextActiveFunctionIndex);
|
||||
} else if (newY == y && i > currentActiveFunctionIndex) {
|
||||
isNextFunction = true;
|
||||
}
|
||||
}
|
||||
if (isNextFunction) {
|
||||
nextY = newY;
|
||||
nextActiveFunctionIndex = i;
|
||||
}
|
||||
}
|
||||
Poincare::Context * context = static_cast<StorageFunctionApp *>(app())->localContext();
|
||||
|
||||
int nextActiveFunctionIndex = InteractiveCurveViewController::closestCurveIndexVertically(direction > 0, currentActiveFunctionIndex, context);
|
||||
if (nextActiveFunctionIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
selectFunctionWithCursor(nextActiveFunctionIndex);
|
||||
m_cursor->moveTo(m_cursor->x(), nextY);
|
||||
m_cursor->moveTo(m_cursor->x(), yValue(nextActiveFunctionIndex, m_cursor->x(), context));
|
||||
interactiveCurveViewRange()->panToMakePointVisible(m_cursor->x(), m_cursor->y(), cursorTopMarginRatio(), k_cursorRightMarginRatio, cursorBottomMarginRatio(), k_cursorLeftMarginRatio);
|
||||
return true;
|
||||
}
|
||||
@@ -200,9 +169,16 @@ bool StorageFunctionGraphController::isCursorVisible() {
|
||||
return interactiveCurveViewRange()->isCursorVisible(cursorTopMarginRatio(), k_cursorRightMarginRatio, cursorBottomMarginRatio(), k_cursorLeftMarginRatio);
|
||||
}
|
||||
|
||||
StorageFunctionStore * StorageFunctionGraphController::functionStore() const {
|
||||
StorageFunctionApp * myApp = static_cast<StorageFunctionApp *>(app());
|
||||
return myApp->functionStore();
|
||||
bool StorageFunctionGraphController::closestCurveIndexIsSuitable(int newIndex, int currentIndex) const {
|
||||
return newIndex != currentIndex;
|
||||
}
|
||||
|
||||
double StorageFunctionGraphController::yValue(int curveIndex, double x, Poincare::Context * context) const {
|
||||
return functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(curveIndex))->evaluateAtAbscissa(x, context);
|
||||
}
|
||||
|
||||
int StorageFunctionGraphController::numberOfCurves() const {
|
||||
return functionStore()->numberOfActiveFunctions();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,31 +19,34 @@ public:
|
||||
void viewWillAppear() override;
|
||||
|
||||
protected:
|
||||
float cursorTopMarginRatio() {
|
||||
return 0.068f; // (cursorHeight/2)/graphViewHeight;
|
||||
}
|
||||
float cursorTopMarginRatio() { return 0.068f; } // (cursorHeight/2)/graphViewHeight
|
||||
float cursorBottomMarginRatio();
|
||||
void reloadBannerView() override;
|
||||
bool handleEnter() override;
|
||||
int indexFunctionSelectedByCursor() const {
|
||||
return *m_indexFunctionSelectedByCursor;
|
||||
}
|
||||
int indexFunctionSelectedByCursor() const { return *m_indexFunctionSelectedByCursor; }
|
||||
virtual void selectFunctionWithCursor(int functionIndex);
|
||||
virtual double defaultCursorAbscissa();
|
||||
virtual StorageFunctionStore * functionStore() const;
|
||||
|
||||
private:
|
||||
constexpr static float k_viewHeight = 174.0f; // TODO Taken from Regresssion/graph_controller. Maybe we should compute and/or put in common ?
|
||||
|
||||
virtual StorageFunctionGraphView * functionGraphView() = 0;
|
||||
virtual View * cursorView() = 0;
|
||||
virtual StorageFunctionCurveParameterController * curveParameterController() = 0;
|
||||
|
||||
// InteractiveCurveViewController
|
||||
/* When y auto is ticked, we use a display margin to be ensure that the user
|
||||
* can move the cursor along the curve without panning the window */
|
||||
float displayTopMarginRatio() override {
|
||||
return 0.09f; // cursorHeight/graphViewHeight
|
||||
}
|
||||
float displayTopMarginRatio() override { return 0.09f; } // cursorHeight/graphViewHeight
|
||||
float displayBottomMarginRatio() override;
|
||||
|
||||
// InteractiveCurveViewRangeDelegate
|
||||
InteractiveCurveViewRangeDelegate::Range computeYRange(InteractiveCurveViewRange * interactiveCurveViewRange) override;
|
||||
float estimatedBannerHeight() const;
|
||||
virtual int estimatedBannerNumberOfLines() const { return 1; }
|
||||
|
||||
// InteractiveCurveViewController
|
||||
void initRangeParameters() override;
|
||||
void initCursorParameters() override;
|
||||
bool moveCursorVertically(int direction) override;
|
||||
@@ -51,9 +54,10 @@ private:
|
||||
uint32_t modelVersion() override;
|
||||
uint32_t rangeVersion() override;
|
||||
bool isCursorVisible() override;
|
||||
virtual StorageFunctionGraphView * functionGraphView() = 0;
|
||||
virtual View * cursorView() = 0;
|
||||
virtual StorageFunctionCurveParameterController * curveParameterController() = 0;
|
||||
bool closestCurveIndexIsSuitable(int newIndex, int currentIndex) const override;
|
||||
double yValue(int curveIndex, double x, Poincare::Context * context) const override;
|
||||
int numberOfCurves() const override;
|
||||
|
||||
InitialisationParameterController m_initialisationParameterController;
|
||||
Poincare::Preferences::AngleUnit * m_angleUnitVersion;
|
||||
int * m_indexFunctionSelectedByCursor;
|
||||
|
||||
Reference in New Issue
Block a user