mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[apps/shared] Change Zoom API
Moved some code around to decrease redundancy and put more of the logic into Poincare::Zoom Change-Id: I4804cf39493ac7f2f0b3c4eb554e5c15c3cef1c9
This commit is contained in:
committed by
Émilie Feral
parent
8572f4953c
commit
6be5e7d62c
@@ -139,31 +139,27 @@ int Store::nextDot(int series, int direction, int dot) {
|
||||
/* Window */
|
||||
|
||||
void Store::setDefault() {
|
||||
float minX = FLT_MAX;
|
||||
float maxX = -FLT_MAX;
|
||||
float min, max;
|
||||
float mins[k_numberOfSeries], maxs[k_numberOfSeries];
|
||||
for (int series = 0; series < k_numberOfSeries; series++) {
|
||||
if (!seriesIsEmpty(series)) {
|
||||
minX = std::min(minX, minValueOfColumn(series, 0));
|
||||
maxX = std::max(maxX, maxValueOfColumn(series, 0));
|
||||
}
|
||||
bool empty = seriesIsEmpty(series);
|
||||
mins[series] = empty ? NAN : minValueOfColumn(series, 0);
|
||||
maxs[series] = empty ? NAN : maxValueOfColumn(series, 0);
|
||||
}
|
||||
float range = maxX - minX;
|
||||
setXMin(minX - k_displayHorizontalMarginRatio * range);
|
||||
setXMax(maxX + k_displayHorizontalMarginRatio * range);
|
||||
Poincare::Zoom::CombineRanges(k_numberOfSeries, mins, maxs, &min, &max);
|
||||
float range = max - min;
|
||||
setXMin(min - k_displayHorizontalMarginRatio * range);
|
||||
setXMax(max + k_displayHorizontalMarginRatio * range);
|
||||
|
||||
float minY = FLT_MAX;
|
||||
float maxY = -FLT_MAX;
|
||||
for (int series = 0; series < k_numberOfSeries; series++) {
|
||||
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {
|
||||
if (xMin() <= get(series, 0, k) && get(series, 0, k) <= xMax()) {
|
||||
minY = std::min<float>(minY, get(series, 1, k));
|
||||
maxY = std::max<float>(maxY, get(series, 1, k));
|
||||
}
|
||||
}
|
||||
bool empty = seriesIsEmpty(series);
|
||||
mins[series] = empty ? NAN : minValueOfColumn(series, 1);
|
||||
maxs[series] = empty ? NAN : maxValueOfColumn(series, 1);
|
||||
}
|
||||
range = maxY - minY;
|
||||
setYMin(m_delegate->addMargin(minY, range, true, true));
|
||||
setYMax(m_delegate->addMargin(maxY, range, true, false));
|
||||
Poincare::Zoom::CombineRanges(k_numberOfSeries, mins, maxs, &min, &max);
|
||||
range = max - min;
|
||||
setYMin(m_delegate->addMargin(min, range, true, true));
|
||||
setYMax(m_delegate->addMargin(max, range, true, false));
|
||||
}
|
||||
|
||||
/* Series */
|
||||
|
||||
@@ -46,35 +46,6 @@ float GraphController::interestingXMin() const {
|
||||
return nmin;
|
||||
}
|
||||
|
||||
void GraphController::interestingRanges(InteractiveCurveViewRange * range) const {
|
||||
int nmin = INT_MAX;
|
||||
int nmax = 0;
|
||||
int nbOfActiveModels = functionStore()->numberOfActiveFunctions();
|
||||
for (int i = 0; i < nbOfActiveModels; i++) {
|
||||
Shared::Sequence * s = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i));
|
||||
int firstInterestingIndex = s->initialRank();
|
||||
nmin = std::min(nmin, firstInterestingIndex);
|
||||
nmax = std::max(nmax, firstInterestingIndex + static_cast<int>(k_defaultXHalfRange));
|
||||
}
|
||||
assert(nmax - nmin >= k_defaultXHalfRange);
|
||||
|
||||
range->setXMin(nmin);
|
||||
range->setXMax(nmax);
|
||||
|
||||
Context * context = textFieldDelegateApp()->localContext();
|
||||
float yMin = FLT_MAX, yMax = -FLT_MAX;
|
||||
for (int i = 0; i < nbOfActiveModels; i++) {
|
||||
Shared::Sequence * s = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i));
|
||||
Zoom::ValueAtAbscissa evaluation = [](float x, Context * context, const void * auxiliary) {
|
||||
return static_cast<const Shared::Sequence *>(auxiliary)->evaluateXYAtParameter(x, context).x2();
|
||||
};
|
||||
Zoom::RefinedYRangeForDisplay(evaluation, nmin, nmax, &yMin, &yMax, context, s);
|
||||
}
|
||||
|
||||
range->setYMin(yMin);
|
||||
range->setYMax(yMax);
|
||||
}
|
||||
|
||||
bool GraphController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) {
|
||||
Shared::TextFieldDelegateApp * myApp = textFieldDelegateApp();
|
||||
double floatBody;
|
||||
|
||||
@@ -20,7 +20,6 @@ public:
|
||||
TermSumController * termSumController() { return &m_termSumController; }
|
||||
// InteractiveCurveViewRangeDelegate
|
||||
float interestingXMin() const override;
|
||||
void interestingRanges(Shared::InteractiveCurveViewRange * range) const override;
|
||||
bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override;
|
||||
private:
|
||||
Shared::XYBannerView * bannerView() override { return &m_bannerView; }
|
||||
|
||||
@@ -262,38 +262,35 @@ void ContinuousFunction::setTMax(float tMax) {
|
||||
}
|
||||
|
||||
void ContinuousFunction::rangeForDisplay(float * xMin, float * xMax, float * yMin, float * yMax, Poincare::Context * context) const {
|
||||
if (plotType() == PlotType::Cartesian) {
|
||||
protectedRangeForDisplay(xMin, xMax, yMin, yMax, context, true);
|
||||
if (plotType() != PlotType::Cartesian) {
|
||||
assert(std::isfinite(tMin()) && std::isfinite(tMax()) && std::isfinite(rangeStep()) && rangeStep() > 0);
|
||||
protectedFullRangeForDisplay(tMin(), tMax(), rangeStep(), xMin, xMax, context, true);
|
||||
protectedFullRangeForDisplay(tMin(), tMax(), rangeStep(), yMin, yMax, context, false);
|
||||
return;
|
||||
}
|
||||
|
||||
Zoom::ValueAtAbscissa evaluation = [](float x, Context * context, const void * auxiliary) {
|
||||
/* When evaluating sin(x)/x close to zero using the standard sine function,
|
||||
* one can detect small variations, while the cardinal sine is supposed to be
|
||||
* locally monotonous. To smooth our such variations, we round the result of
|
||||
* the evaluations. As we are not interested in precise results but only in
|
||||
* ordering, this approximation is sufficient. */
|
||||
constexpr float precision = 1e-5;
|
||||
return precision * std::round(static_cast<const Function *>(auxiliary)->evaluateXYAtParameter(x, context).x2() / precision);
|
||||
};
|
||||
bool fullyComputed = Zoom::InterestingRangesForDisplay(evaluation, xMin, xMax, yMin, yMax, tMin(), tMax(), context, this);
|
||||
|
||||
evaluation = [](float x, Context * context, const void * auxiliary) {
|
||||
return static_cast<const Function *>(auxiliary)->evaluateXYAtParameter(x, context).x2();
|
||||
};
|
||||
|
||||
if (fullyComputed) {
|
||||
Zoom::RefinedYRangeForDisplay(evaluation, *xMin, *xMax, yMin, yMax, context, this);
|
||||
} else {
|
||||
fullXYRange(xMin, xMax, yMin, yMax, context);
|
||||
Zoom::RangeWithRatioForDisplay(evaluation, InteractiveCurveViewRange::NormalYXRatio(), xMin, xMax, yMin, yMax, context, this);
|
||||
}
|
||||
}
|
||||
|
||||
void ContinuousFunction::fullXYRange(float * xMin, float * xMax, float * yMin, float * yMax, Context * context) const {
|
||||
assert(yMin && yMax);
|
||||
assert(!(std::isinf(tMin()) || std::isinf(tMax()) || std::isnan(rangeStep())));
|
||||
|
||||
float resultXMin = FLT_MAX, resultXMax = - FLT_MAX, resultYMin = FLT_MAX, resultYMax = - FLT_MAX;
|
||||
for (float t = tMin(); t <= tMax(); t += rangeStep()) {
|
||||
Coordinate2D<float> xy = privateEvaluateXYAtParameter(t, context);
|
||||
if (!std::isfinite(xy.x1()) || !std::isfinite(xy.x2())) {
|
||||
continue;
|
||||
}
|
||||
resultXMin = std::min(xy.x1(), resultXMin);
|
||||
resultXMax = std::max(xy.x1(), resultXMax);
|
||||
resultYMin = std::min(xy.x2(), resultYMin);
|
||||
resultYMax = std::max(xy.x2(), resultYMax);
|
||||
}
|
||||
if (xMin) {
|
||||
*xMin = resultXMin;
|
||||
}
|
||||
if (xMax) {
|
||||
*xMax = resultXMax;
|
||||
}
|
||||
*yMin = resultYMin;
|
||||
*yMax = resultYMax;
|
||||
}
|
||||
|
||||
void * ContinuousFunction::Model::expressionAddress(const Ion::Storage::Record * record) const {
|
||||
return (char *)record->value().buffer+sizeof(RecordDataBuffer);
|
||||
}
|
||||
|
||||
@@ -80,26 +80,19 @@ Function::RecordDataBuffer * Function::recordData() const {
|
||||
return reinterpret_cast<RecordDataBuffer *>(const_cast<void *>(d.buffer));
|
||||
}
|
||||
|
||||
void Function::protectedRangeForDisplay(float * xMin, float * xMax, float * yMin, float * yMax, Poincare::Context * context, bool boundByMagnitude) const {
|
||||
Zoom::ValueAtAbscissa evaluation = [](float x, Context * context, const void * auxiliary) {
|
||||
/* When evaluating sin(x)/x close to zero using the standard sine function,
|
||||
* one can detect small variations, while the cardinal sine is supposed to be
|
||||
* locally monotonous. To smooth our such variations, we round the result of
|
||||
* the evaluations. As we are not interested in precise results but only in
|
||||
* ordering, this approximation is sufficient. */
|
||||
constexpr float precision = 1e-5;
|
||||
return precision * std::round(static_cast<const Function *>(auxiliary)->evaluateXYAtParameter(x, context).x2() / precision);
|
||||
};
|
||||
bool fullyComputed = Zoom::InterestingRangesForDisplay(evaluation, xMin, xMax, yMin, yMax, tMin(), tMax(), context, this);
|
||||
|
||||
evaluation = [](float x, Context * context, const void * auxiliary) {
|
||||
return static_cast<const Function *>(auxiliary)->evaluateXYAtParameter(x, context).x2();
|
||||
};
|
||||
|
||||
if (fullyComputed) {
|
||||
Zoom::RefinedYRangeForDisplay(evaluation, *xMin, *xMax, yMin, yMax, context, this, boundByMagnitude);
|
||||
void Function::protectedFullRangeForDisplay(float tMin, float tMax, float tStep, float * min, float * max, Poincare::Context * context, bool xRange) const {
|
||||
Poincare::Zoom::ValueAtAbscissa evaluation;
|
||||
if (xRange) {
|
||||
evaluation = [](float x, Poincare::Context * context, const void * auxiliary) {
|
||||
return static_cast<const Function *>(auxiliary)->evaluateXYAtParameter(x, context).x1();
|
||||
};
|
||||
} else {
|
||||
Zoom::RangeWithRatioForDisplay(evaluation, InteractiveCurveViewRange::NormalYXRatio(), xMin, xMax, yMin, yMax, context, this);
|
||||
evaluation = [](float x, Poincare::Context * context, const void * auxiliary) {
|
||||
return static_cast<const Function *>(auxiliary)->evaluateXYAtParameter(x, context).x2();
|
||||
};
|
||||
}
|
||||
|
||||
Poincare::Zoom::FullRange(evaluation, tMin, tMax, tStep, min, max, context, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ protected:
|
||||
bool m_active;
|
||||
};
|
||||
|
||||
void protectedRangeForDisplay(float * xMin, float * xMax, float * yMin, float * yMax, Poincare::Context * context, bool boundByMagnitude) const;
|
||||
void protectedFullRangeForDisplay(float tMin, float tMax, float tStep, float * min, float * max, Poincare::Context * context, bool xRange) const;
|
||||
|
||||
private:
|
||||
RecordDataBuffer * recordData() const;
|
||||
|
||||
@@ -138,22 +138,23 @@ int FunctionGraphController::numberOfCurves() const {
|
||||
|
||||
void FunctionGraphController::interestingRanges(InteractiveCurveViewRange * range) const {
|
||||
Poincare::Context * context = textFieldDelegateApp()->localContext();
|
||||
float resultXMin = FLT_MAX;
|
||||
float resultXMax = -FLT_MAX;
|
||||
float resultYMin = FLT_MAX;
|
||||
float resultYMax = -FLT_MAX;
|
||||
assert(functionStore()->numberOfActiveFunctions() > 0);
|
||||
int functionsCount = functionStore()->numberOfActiveFunctions();
|
||||
for (int i = 0; i < functionsCount; i++) {
|
||||
constexpr int maxLength = 10;
|
||||
float xMins[maxLength], xMaxs[maxLength], yMins[maxLength], yMaxs[maxLength];
|
||||
int length = functionStore()->numberOfActiveFunctions();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
ExpiringPointer<Function> f = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i));
|
||||
f->rangeForDisplay(&resultXMin, &resultXMax, &resultYMin, &resultYMax, context);
|
||||
f->rangeForDisplay(xMins + i, xMaxs + i, yMins + i, yMaxs + i, context);
|
||||
}
|
||||
|
||||
range->setXMin(resultXMin);
|
||||
range->setXMax(resultXMax);
|
||||
range->setYMin(resultYMin);
|
||||
range->setYMax(resultYMax);
|
||||
/* We can only call this method once the X range has been fully computed. */
|
||||
float xMin, xMax, yMin, yMax;
|
||||
Poincare::Zoom::CombineRanges(length, xMins, xMaxs, &xMin, &xMax);
|
||||
Poincare::Zoom::CombineRanges(length, yMins, yMaxs, &yMin, &yMax);
|
||||
range->setXMin(xMin);
|
||||
range->setXMax(xMax);
|
||||
range->setYMin(yMin);
|
||||
range->setYMax(yMax);
|
||||
|
||||
yRangeForCursorFirstMove(range);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <poincare/integer.h>
|
||||
#include <poincare/rational.h>
|
||||
#include <poincare/addition.h>
|
||||
#include <poincare/zoom.h>
|
||||
#include "../shared/poincare_helpers.h"
|
||||
#include <string.h>
|
||||
#include <apps/i18n.h>
|
||||
@@ -310,6 +311,15 @@ Expression Sequence::sumBetweenBounds(double start, double end, Poincare::Contex
|
||||
return Float<double>::Builder(result);
|
||||
}
|
||||
|
||||
void Sequence::rangeForDisplay(float * xMin, float * xMax, float * yMin, float * yMax, Poincare::Context * context) const {
|
||||
Poincare::Zoom::ValueAtAbscissa evaluation = [](float x, Poincare::Context * context, const void * auxiliary) {
|
||||
return static_cast<float>(static_cast<const Shared::Sequence *>(auxiliary)->initialRank());
|
||||
};
|
||||
Poincare::Zoom::FullRange(evaluation, 0, 1, 1, xMin, xMax, context, this);
|
||||
*xMax += Poincare::Zoom::k_defaultHalfRange;
|
||||
protectedFullRangeForDisplay(*xMin, *xMax, 1.f, yMin, yMax, context, false);
|
||||
}
|
||||
|
||||
Sequence::RecordDataBuffer * Sequence::recordData() const {
|
||||
assert(!isNull());
|
||||
Ion::Storage::Record::Data d = value();
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
constexpr static int k_initialRankNumberOfDigits = 3; // m_initialRank is capped by 999
|
||||
|
||||
//Range
|
||||
void rangeForDisplay(float * xMin, float * xMax, float * yMin, float * yMax, Poincare::Context * context) const override { protectedRangeForDisplay(xMin, xMax, yMin, yMax, context, false); };
|
||||
void rangeForDisplay(float * xMin, float * xMax, float * yMin, float * yMax, Poincare::Context * context) const override;
|
||||
|
||||
private:
|
||||
constexpr static const KDFont * k_layoutFont = KDFont::LargeFont;
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
/* Return false if the X range was given a default value because there were
|
||||
* no points of interest. */
|
||||
static bool InterestingRangesForDisplay(ValueAtAbscissa evaluation, float * xMin, float * xMax, float * yMin, float * yMax, float tMin, float tMax, Context * context, const void * auxiliary);
|
||||
static void RefinedYRangeForDisplay(ValueAtAbscissa evaluation, float xMin, float xMax, float * yMin, float * yMax, Context * context, const void * auxiliary, bool boundByMagnitude = false);
|
||||
static void RefinedYRangeForDisplay(ValueAtAbscissa evaluation, float xMin, float xMax, float * yMin, float * yMax, Context * context, const void * auxiliary);
|
||||
static void RangeWithRatioForDisplay(ValueAtAbscissa evaluation, float yxRatio, float * xMin, float * xMax, float * yMin, float * yMax, Context * context, const void * auxiliary);
|
||||
static void FullRange(ValueAtAbscissa evaluation, float tMin, float tMax, float tStep, float * fMin, float * fMax, Context * context, const void * auxiliary);
|
||||
|
||||
|
||||
@@ -147,16 +147,15 @@ bool Zoom::InterestingRangesForDisplay(ValueAtAbscissa evaluation, float * xMin,
|
||||
resultX[0] -= k_breathingRoom * xRange;
|
||||
resultX[1] += k_breathingRoom * xRange;
|
||||
}
|
||||
*xMin = std::min(resultX[0], *xMin);
|
||||
*xMax = std::max(resultX[1], *xMax);
|
||||
|
||||
*yMin = std::min(resultYMin, *yMin);
|
||||
*yMax = std::max(resultYMax, *yMax);
|
||||
*xMin = resultX[0];
|
||||
*xMax = resultX[1];
|
||||
*yMin = resultYMin;
|
||||
*yMax = resultYMax;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Zoom::RefinedYRangeForDisplay(ValueAtAbscissa evaluation, float xMin, float xMax, float * yMin, float * yMax, Context * context, const void * auxiliary, bool boundByMagnitude) {
|
||||
void Zoom::RefinedYRangeForDisplay(ValueAtAbscissa evaluation, float xMin, float xMax, float * yMin, float * yMax, Context * context, const void * auxiliary) {
|
||||
/* This methods computes the Y range that will be displayed for cartesian
|
||||
* functions and sequences, given an X range (xMin, xMax) and bounds yMin and
|
||||
* yMax that must be inside the Y range.*/
|
||||
@@ -184,13 +183,12 @@ void Zoom::RefinedYRangeForDisplay(ValueAtAbscissa evaluation, float xMin, float
|
||||
/* sum/pop is the log mean value of the function, which can be interpreted as
|
||||
* its average order of magnitude. Then, bound is the value for the next
|
||||
* order of magnitude and is used to cut the Y range. */
|
||||
if (boundByMagnitude) {
|
||||
float bound = (pop > 0) ? std::exp(sum / pop + 1.f) : FLT_MAX;
|
||||
sampleYMin = std::max(sampleYMin, - bound);
|
||||
sampleYMax = std::min(sampleYMax, bound);
|
||||
}
|
||||
*yMin = std::min(*yMin, sampleYMin);
|
||||
*yMax = std::max(*yMax, sampleYMax);
|
||||
|
||||
float bound = (pop > 0) ? std::exp(sum / pop + 1.f) : FLT_MAX;
|
||||
sampleYMin = std::max(sampleYMin, - bound);
|
||||
sampleYMax = std::min(sampleYMax, bound);
|
||||
*yMin = sampleYMin;
|
||||
*yMax = sampleYMax;
|
||||
if (*yMin == *yMax) {
|
||||
RangeFromSingleValue(*yMin, yMin, yMax);
|
||||
}
|
||||
@@ -257,7 +255,7 @@ void Zoom::RangeWithRatioForDisplay(ValueAtAbscissa evaluation, float yxRatio, f
|
||||
while (xMagnitude < k_maximalDistance) {
|
||||
for(const float unit : units) {
|
||||
const float xRange = unit * xMagnitude;
|
||||
RefinedYRangeForDisplay(evaluation, -xRange, xRange, &yMinRange, &yMaxRange, context, auxiliary, true);
|
||||
RefinedYRangeForDisplay(evaluation, -xRange, xRange, &yMinRange, &yMaxRange, context, auxiliary);
|
||||
float currentRatio = (yMaxRange - yMinRange) / (2 * xRange);
|
||||
float grade = std::fabs(std::log(currentRatio / yxRatio)) + std::fabs(std::log(xRange / 10.f)) * rangeMagnitudeWeight;
|
||||
if (std::fabs(std::log(currentRatio / yxRatio)) < maxMagnitudeDifference && grade < bestGrade) {
|
||||
@@ -274,7 +272,7 @@ void Zoom::RangeWithRatioForDisplay(ValueAtAbscissa evaluation, float yxRatio, f
|
||||
if (bestGrade == FLT_MAX) {
|
||||
*xMin = -k_defaultHalfRange;
|
||||
*xMax = k_defaultHalfRange;
|
||||
RefinedYRangeForDisplay(evaluation, *xMin, *xMax, yMin, yMax, context, auxiliary, true);
|
||||
RefinedYRangeForDisplay(evaluation, *xMin, *xMax, yMin, yMax, context, auxiliary);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user