mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[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:
committed by
EmilieNumworks
parent
625a89e610
commit
35bfb8ec16
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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") {
|
||||
|
||||
Reference in New Issue
Block a user