[apps/graph] xAuto and YAuto for polar/param

This commit is contained in:
Léa Saviot
2019-09-03 11:50:24 +02:00
parent ecfe626520
commit a4099c4c3e
8 changed files with 123 additions and 14 deletions

View File

@@ -1,10 +1,12 @@
#include "graph_controller.h"
#include "../app.h"
using namespace Poincare;
using namespace Shared;
namespace Graph {
static inline float minFloat(float x, float y) { return x < y ? x : y; }
static inline float maxFloat(float x, float y) { return x > y ? x : y; }
GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, CartesianFunctionStore * functionStore, Shared::InteractiveCurveViewRange * curveViewRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
@@ -33,6 +35,72 @@ void GraphController::viewWillAppear() {
selectFunctionWithCursor(indexFunctionSelectedByCursor()); // update the color of the cursor
}
void GraphController::interestingFunctionRange(ExpiringPointer<CartesianFunction> f, float tMin, float tMax, float step, float * xm, float * xM, float * ym, float * yM) const {
Poincare::Context * context = textFieldDelegateApp()->localContext();
const int balancedBound = std::floor((tMax-tMin)/2/step);
for (int j = -balancedBound; j <= balancedBound ; j++) {
float t = (tMin+tMax)/2 + step * j;
Coordinate2D<float> xy = f->evaluateXYAtParameter(t, context);
float x = xy.x1();
float y = xy.x2();
if (!std::isnan(x) && !std::isinf(x) && !std::isnan(y) && !std::isinf(y)) {
*xm = minFloat(*xm, x);
*xM = maxFloat(*xM, x);
*ym = minFloat(*ym, y);
*yM = maxFloat(*yM, y);
}
}
}
void GraphController::interestingRanges(float * xm, float * xM, float * ym, float * yM) const {
float resultxMin = FLT_MAX;
float resultxMax = -FLT_MAX;
float resultyMin = FLT_MAX;
float resultyMax = -FLT_MAX;
float xMin = const_cast<GraphController *>(this)->interactiveCurveViewRange()->xMin();
float xMax = const_cast<GraphController *>(this)->interactiveCurveViewRange()->xMax();
/* In practice, a step smaller than a pixel's width is needed for sampling
* the values of a function. Otherwise some relevant extremal values may be
* missed. */
assert(functionStore()->numberOfActiveFunctions() > 0);
if (displaysNonCartesianFunctions()) {
for (int i = 0; i < functionStore()->numberOfActiveFunctions(); i++) {
ExpiringPointer<CartesianFunction> f = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i));
if (f->plotType() == CartesianFunction::PlotType::Cartesian) {
continue;
}
/* Scan x-range from the middle to the extrema in order to get balanced
* y-range for even functions (y = 1/x). */
double tMin = f->tMin();
double tMax = f->tMax();
assert(!std::isnan(tMin));
assert(!std::isnan(tMax));
interestingFunctionRange(f, tMin, tMax, (tMax - tMin)/30, &resultxMin, &resultxMax, &resultyMin, &resultyMax);
}
} else {
resultxMin = xMin;
resultxMax = xMax;
}
const float step = const_cast<GraphController *>(this)->curveView()->pixelWidth() / 2;
for (int i = 0; i < functionStore()->numberOfActiveFunctions(); i++) {
ExpiringPointer<CartesianFunction> f = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i));
if (f->plotType() != CartesianFunction::PlotType::Cartesian) {
continue;
}
/* Scan x-range from the middle to the extrema in order to get balanced
* y-range for even functions (y = 1/x). */
assert(!std::isnan(f->tMin()));
assert(!std::isnan(f->tMax()));
double tMin = maxFloat(f->tMin(), xMin);
double tMax = minFloat(f->tMax(), xMax);
interestingFunctionRange(f, tMin, tMax, step, &resultxMin, &resultxMax, &resultyMin, &resultyMax);
}
*xm = resultxMin;
*xM = resultxMax;
*ym = resultyMin;
*yM = resultyMax;
}
float GraphController::interestingXHalfRange() const {
if (displaysNonCartesianFunctions())
{

View File

@@ -21,7 +21,7 @@ public:
bool displayDerivativeInBanner() const { return m_displayDerivativeInBanner; }
void setDisplayDerivativeInBanner(bool displayDerivative) { m_displayDerivativeInBanner = displayDerivative; }
float interestingXHalfRange() const override;
void interestingRanges(float * xm, float * xM, float * ym, float * yM) const override;
private:
int estimatedBannerNumberOfLines() const override { return 1 + m_displayDerivativeInBanner; }
void selectFunctionWithCursor(int functionIndex) override;
@@ -36,6 +36,7 @@ private:
CartesianFunctionStore * functionStore() const override { return static_cast<CartesianFunctionStore *>(Shared::FunctionGraphController::functionStore()); }
bool displaysNonCartesianFunctions() const;
bool defautRangeIsNormalized() const override { return displaysNonCartesianFunctions(); }
void interestingFunctionRange(Shared::ExpiringPointer<Shared::CartesianFunction> f, float tMin, float tMax, float step, float * xm, float * xM, float * ym, float * yM) const;
Shared::RoundCursorView m_cursorView;
BannerView m_bannerView;

View File

@@ -34,6 +34,7 @@ protected:
Poincare::Coordinate2D<double> xyValues(int curveIndex, double t, Poincare::Context * context) const override;
int numberOfCurves() const override;
void initCursorParameters() override;
CurveView * curveView() override;
private:
virtual FunctionGraphView * functionGraphView() = 0;
@@ -44,7 +45,6 @@ private:
// InteractiveCurveViewController
bool moveCursorVertically(int direction) override;
CurveView * curveView() override;
uint32_t modelVersion() override;
uint32_t rangeVersion() override;

View File

@@ -35,7 +35,7 @@ InteractiveCurveViewController::InteractiveCurveViewController(Responder * paren
{
}
float InteractiveCurveViewController::addMargin(float x, float range, bool isMin) {
float InteractiveCurveViewController::addMargin(float x, float range, bool isVertical, bool isMin) {
/* We are adding margins. Let's name:
* - The current range: rangeBefore
* - The next range: rangeAfter
@@ -60,8 +60,8 @@ float InteractiveCurveViewController::addMargin(float x, float range, bool isMin
* topRatioBefore = topRatioAfter, we would create too small margins and the
* controller might need to pan right after a Y auto calibration. */
float topMarginRatio = cursorTopMarginRatio();
float bottomMarginRatio = cursorBottomMarginRatio();
float topMarginRatio = isVertical ? cursorTopMarginRatio() : k_cursorRightMarginRatio;
float bottomMarginRatio = isVertical ? cursorBottomMarginRatio() : k_cursorLeftMarginRatio;
assert(topMarginRatio + bottomMarginRatio < 1); // Assertion so that the formula is correct
float ratioDenominator = 1 - bottomMarginRatio - topMarginRatio;
float ratio = isMin ? -bottomMarginRatio : topMarginRatio;

View File

@@ -62,7 +62,7 @@ private:
virtual int estimatedBannerNumberOfLines() const { return 1; }
// InteractiveCurveViewRangeDelegate
float addMargin(float x, float range, bool isMin) override;
float addMargin(float x, float range, bool isVertical, bool isMin) override;
uint32_t * m_modelVersion;
uint32_t * m_rangeVersion;

View File

@@ -146,16 +146,53 @@ void InteractiveCurveViewRange::setDefault() {
if (m_delegate == nullptr) {
return;
}
m_xMax = m_delegate->interestingXHalfRange();
setXMin(-m_xMax);
if (!m_delegate->defautRangeIsNormalized()) {
m_xMax = m_delegate->interestingXHalfRange();
setXMin(-m_xMax);
m_yAuto = true;
return;
}
#if 0
m_yAuto = false;
m_yMax = 3.0f;
setYMin(-m_yMax);
m_xMax = NormalizedXHalfRange();
setXMin(-NormalizedXHalfRange());
m_yMax = NormalizedYHalfRange();
setYMin(-NormalizedYHalfRange());
normalize();
#else
m_yAuto = false;
float a,b,c,d;
m_delegate->interestingRanges(&a, &b, &c, &d);
MemoizedCurveViewRange::setXMin(a);
MemoizedCurveViewRange::setXMax(b);
MemoizedCurveViewRange::setYMin(c);
MemoizedCurveViewRange::setYMax(d);
float xRange = m_xMax - m_xMin;
float yRange = m_yMax - m_yMin;
m_xMin = m_delegate->addMargin(m_xMin, xRange, false, true);
MemoizedCurveViewRange::setXMax(m_delegate->addMargin(m_xMax, xRange, false, false));
m_yMin = m_delegate->addMargin(m_yMin, yRange, true, true);
MemoizedCurveViewRange::setYMax(m_delegate->addMargin(m_yMax, yRange, true, false));
xRange = m_xMax - m_xMin;
yRange = m_yMax - m_yMin;
float xyRatio = xRange/yRange;
float normalizedXYRatio = NormalizedXHalfRange()/NormalizedYHalfRange();
if (xyRatio < normalizedXYRatio) {
float newXRange = normalizedXYRatio * yRange;
assert(newXRange > xRange);
float delta = (newXRange - xRange) / 2.0f;
m_xMin -= delta;
MemoizedCurveViewRange::setXMax(m_xMax+delta);
} else if (xyRatio > normalizedXYRatio) {
float newYRange = NormalizedYHalfRange()/NormalizedXHalfRange() * xRange;
assert(newYRange > yRange);
float delta = (newYRange - yRange) / 2.0f;
m_yMin -= delta;
MemoizedCurveViewRange::setYMax(m_yMax+delta);
}
#endif
}
void InteractiveCurveViewRange::centerAxisAround(Axis axis, float position) {

View File

@@ -19,7 +19,7 @@ bool InteractiveCurveViewRangeDelegate::didChangeRange(InteractiveCurveViewRange
if (max < min) {
range = 0.0f;
}
if (interactiveCurveViewRange->yMin() == addMargin(min, range, true) && interactiveCurveViewRange->yMax() == addMargin(max, range, false)) {
if (interactiveCurveViewRange->yMin() == addMargin(min, range, true, true) && interactiveCurveViewRange->yMax() == addMargin(max, range, true, false)) {
return false;
}
if (min == max) {
@@ -33,8 +33,8 @@ bool InteractiveCurveViewRangeDelegate::didChangeRange(InteractiveCurveViewRange
max = 1.0f;
}
range = max - min;
interactiveCurveViewRange->setYMin(addMargin(min, range, true));
interactiveCurveViewRange->setYMax(addMargin(max, range, false));
interactiveCurveViewRange->setYMin(addMargin(min, range, true, true));
interactiveCurveViewRange->setYMax(addMargin(max, range, true, false));
if (std::isinf(interactiveCurveViewRange->xMin())) {
interactiveCurveViewRange->setYMin(-FLT_MAX);
}

View File

@@ -1,6 +1,8 @@
#ifndef SHARED_INTERACTIVE_CURVE_VIEW_DELEGATE_H
#define SHARED_INTERACTIVE_CURVE_VIEW_DELEGATE_H
#include <assert.h>
namespace Shared {
class InteractiveCurveViewRange;
@@ -11,6 +13,8 @@ public:
virtual float interestingXMin() const { return -interestingXHalfRange(); }
virtual float interestingXHalfRange() const { return 10.0f; }
virtual bool defautRangeIsNormalized() const { return false; }
virtual void interestingRanges(float * xm, float * xM, float * ym, float * yM) const { assert(false); }
virtual float addMargin(float x, float range, bool isVertical, bool isMin) = 0;
protected:
struct Range {
float min;
@@ -18,7 +22,6 @@ protected:
};
private:
virtual Range computeYRange(InteractiveCurveViewRange * interactiveCurveViewRange) = 0;
virtual float addMargin(float x, float range, bool isMin) = 0;
};
}