[poincare/zoom] Better handling of single points of interest

Functions with a single point of inerest used to be drawn on a tiny
interval around this point.
Now, an orthonormal window is built around the point of interest, giving
a much nicer view.

Change-Id: I7793797aead2695532ddea44d93d62bb7ef870f4
This commit is contained in:
Gabriel Ozouf
2020-11-25 14:40:34 +01:00
committed by EmilieNumworks
parent 229865aff8
commit 4869383263

View File

@@ -49,7 +49,7 @@ bool Zoom::InterestingRangesForDisplay(ValueAtAbscissa evaluation, float * xMin,
float resultYMin = FLT_MAX, resultYMax = - FLT_MAX;
float asymptote[2] = {FLT_MAX, - FLT_MAX};
float explosion[2] = {FLT_MAX, - FLT_MAX};
int numberOfPoints;
int numberOfPoints, totalNumberOfPoints = 0;
float xFallback, yFallback[2] = {NAN, NAN};
float firstResult;
float dXOld, dXPrev, dXNext, yOld, yPrev, yNext;
@@ -63,6 +63,7 @@ bool Zoom::InterestingRangesForDisplay(ValueAtAbscissa evaluation, float * xMin,
ExtremumExistsOnInterval(ya, yb, yc) || ya == yc)
{
resultX[0] = resultX[1] = center;
totalNumberOfPoints++;
if (ExtremumExistsOnInterval(ya, yb, yc) && IsConvexAroundExtremum(evaluation, a, b, c, ya, yb, yc, context, auxiliary)) {
resultYMin = resultYMax = yb;
}
@@ -124,6 +125,7 @@ bool Zoom::InterestingRangesForDisplay(ValueAtAbscissa evaluation, float * xMin,
if (std::isnan(firstResult)) {
firstResult = dXNext;
}
totalNumberOfPoints++;
break;
default:
const float slopeNext = (yNext - yPrev) / (dXNext - dXPrev), slopePrev = (yPrev - yOld) / (dXPrev - dXOld);
@@ -151,23 +153,33 @@ bool Zoom::InterestingRangesForDisplay(ValueAtAbscissa evaluation, float * xMin,
}
}
if (totalNumberOfPoints == 1) {
float xM = (resultX[0] + resultX[1]) / 2;
resultX[0] = xM;
resultX[1] = xM;
}
/* Cut after horizontal asymptotes. */
resultX[0] = std::min(resultX[0], asymptote[0]);
resultX[1] = std::max(resultX[1], asymptote[1]);
/* Cut after explosions if it does not reduce precision */
float xMinWithExplosion = std::min(resultX[0], explosion[0]);
float xMaxWithExplosion = std::max(resultX[1], explosion[1]);
if (xMaxWithExplosion - xMinWithExplosion < k_maxRatioBetweenPointsOfInterest * (resultX[1] - resultX[0])) {
resultX[0] = xMinWithExplosion;
resultX[1] = xMaxWithExplosion;
}
if (resultX[0] >= resultX[1]) {
if (resultX[0] > resultX[1]) {
resultX[0] = NAN;
resultX[1] = NAN;
}
/* Fallback to default range. */
*xMin = NAN;
*xMax = NAN;
*xMin = resultX[0];
*xMax = resultX[1];
*yMin = NAN;
*yMax = NAN;
return false;
} else {
float xMinWithExplosion = std::min(resultX[0], explosion[0]);
float xMaxWithExplosion = std::max(resultX[1], explosion[1]);
if (xMaxWithExplosion - xMinWithExplosion < k_maxRatioBetweenPointsOfInterest * (resultX[1] - resultX[0])) {
resultX[0] = xMinWithExplosion;
resultX[1] = xMaxWithExplosion;
}
/* Add breathing room around points of interest. */
float xRange = resultX[1] - resultX[0];
resultX[0] -= k_breathingRoom * xRange;
@@ -247,9 +259,10 @@ void Zoom::RangeWithRatioForDisplay(ValueAtAbscissa evaluation, float yxRatio, f
float unit = k_smallUnitMantissa;
float xMagnitude = k_minimalDistance;
float yMinRange = FLT_MAX, yMaxRange = -FLT_MAX;
float center = *xMin == *xMax ? *xMin : 0.f;
while (xMagnitude < k_maximalDistance) {
const float xRange = unit * xMagnitude;
RefinedYRangeForDisplay(evaluation, -xRange, xRange, &yMinRange, &yMaxRange, context, auxiliary, sampleSize);
RefinedYRangeForDisplay(evaluation, center - xRange, center + xRange, &yMinRange, &yMaxRange, context, auxiliary, sampleSize);
float currentRatio = (yMaxRange - yMinRange) / (2 * xRange);
float grade = std::fabs(std::log(currentRatio / yxRatio));
/* When in doubt, favor ranges between [-1, 1] and [-10, 10] */
@@ -280,8 +293,8 @@ void Zoom::RangeWithRatioForDisplay(ValueAtAbscissa evaluation, float yxRatio, f
xRange = bestUnit * bestMagnitude;
}
xRange = bestUnit * smoothToPowerOfTen(bestMagnitude);
*xMin = -xRange;
*xMax = xRange;
*xMin = center - xRange;
*xMax = center + xRange;
SetToRatio(yxRatio, xMin, xMax, yMin, yMax);
}