[poincare/zoom] Method ExpandSparseWindow

This method is used to remove extraneous empty sapce in the middle of
the window for functions that are discontinuous between their points of
interest.
This commit is contained in:
Gabriel Ozouf
2020-12-09 12:18:45 +01:00
committed by EmilieNumworks
parent 625a89e610
commit 35bfb8ec16
4 changed files with 39 additions and 0 deletions

View File

@@ -68,6 +68,10 @@ private:
* an asymptote, by recursively computing the slopes. In case of an extremum,
* the slope should taper off toward the center. */
static bool IsConvexAroundExtremum(ValueAtAbscissa evaluation, float x1, float x2, float x3, float y1, float y2, float y3, Context * context, const void * auxiliary, int iterations = 3);
/* If the function is discontinuous between its points of interest, there
* might be a lot of empty space in the middle of the screen. In that case,
* we want to zoom out to see more of the graph. */
static void ExpandSparseWindow(float * sample, int length, float * xMin, float * xMax, float * yMin, float * yMax);
};
}

View File

@@ -201,15 +201,19 @@ void Zoom::RefinedYRangeForDisplay(ValueAtAbscissa evaluation, float * xMin, flo
* yMax that must be inside the Y range.*/
assert(yMin && yMax);
float sample[k_sampleSize];
float sampleYMin = FLT_MAX, sampleYMax = -FLT_MAX;
const float step = (*xMax - *xMin) / (k_sampleSize - 1);
float x, y;
float sum = 0.f;
int pop = 0;
sample[0] = evaluation(*xMin, context, auxiliary);
sample[k_sampleSize - 1] = evaluation(*xMax, context, auxiliary);
for (int i = 1; i < k_sampleSize - 1; i++) {
x = *xMin + i * step;
y = evaluation(x, context, auxiliary);
sample[i] = y;
if (!std::isfinite(y)) {
continue;
}
@@ -244,6 +248,8 @@ void Zoom::RefinedYRangeForDisplay(ValueAtAbscissa evaluation, float * xMin, flo
} else if (*yMax < 0.f && *yMax / *yMin < k_forceXAxisThreshold) {
*yMax = 0.f;
}
ExpandSparseWindow(sample, k_sampleSize, xMin, xMax, yMin, yMax);
}
void Zoom::RangeWithRatioForDisplay(ValueAtAbscissa evaluation, float yxRatio, float * xMin, float * xMax, float * yMin, float * yMax, Context * context, const void * auxiliary) {
@@ -446,4 +452,31 @@ bool Zoom::IsConvexAroundExtremum(ValueAtAbscissa evaluation, float x1, float x2
return true;
}
void Zoom::ExpandSparseWindow(float * sample, int length, float * xMin, float * xMax, float * yMin, float * yMax) {
/* We compute the "empty center" of the window, i.e. the largest rectangle
* (with same center and shape as the window) that does not contain any
* point. If that rectangle is deemed too large, we consider that not enough
* of the curve shows up on screen and we zoom out. */
constexpr float emptyCenterMaxSize = 0.5f;
constexpr float ratioCorrection = 4.f/3.f;
float xCenter = (*xMax + *xMin) / 2.f;
float yCenter = (*yMax + *yMin) / 2.f;
float xRange = *xMax - *xMin;
float yRange = *yMax - *yMin;
float emptyCenter = FLT_MAX;
float step = xRange / (length - 1);
for (int i = 0; i < length; i++) {
float x = *xMin + i * step;
float y = sample[i];
float r = 2 * std::max(std::fabs(x - xCenter) / xRange, std::fabs(y - yCenter) / yRange);
emptyCenter = std::min(emptyCenter, r);
}
if (emptyCenter > emptyCenterMaxSize) {
SetZoom(ratioCorrection + emptyCenter, xCenter, yCenter, xMin, xMax, yMin ,yMax);
}
}
}

View File

@@ -128,6 +128,7 @@ QUIZ_CASE(poincare_zoom_refined_range) {
assert_refined_range_is("x×sin(x)", -14.4815292, 14.4815292, -7.37234354, 7.37234354);
assert_refined_range_is("x×ln(x)", -0.314885706, 1.36450469, -0.367870897, 0.396377981);
assert_refined_range_is("x!", -10, 10, NAN, NAN);
assert_refined_range_is("x^(1/x)", -1.3, 2.4, -0.564221799, 5.58451653);
}
void assert_orthonormal_range_is(const char * definition, float targetXMin, float targetXMax, float targetYMin, float targetYMax, Preferences::AngleUnit angleUnit = Radian, const char * symbol = "x") {