From c3ee6a71e7510c1a63ae65856315528914d97f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 3 Sep 2019 13:57:06 +0200 Subject: [PATCH] [apps/graph] Fix computeYRange for polar/cartesian functions Was very slow, compute only 1000 values to evaluate the yRange --- apps/graph/graph/graph_controller.cpp | 6 +++++- apps/shared/cartesian_function.h | 3 +++ apps/shared/function.h | 1 + apps/shared/function_graph_controller.cpp | 9 +++++---- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/apps/graph/graph/graph_controller.cpp b/apps/graph/graph/graph_controller.cpp index f9d57c416..da2b65023 100644 --- a/apps/graph/graph/graph_controller.cpp +++ b/apps/graph/graph/graph_controller.cpp @@ -75,12 +75,16 @@ void GraphController::interestingRanges(float * xm, float * xM, float * ym, floa double tMax = f->tMax(); assert(!std::isnan(tMin)); assert(!std::isnan(tMax)); - interestingFunctionRange(f, tMin, tMax, (tMax - tMin)/30, &resultxMin, &resultxMax, &resultyMin, &resultyMax); + assert(!std::isnan(f->rangeStep())); + interestingFunctionRange(f, tMin, tMax, f->rangeStep(), &resultxMin, &resultxMax, &resultyMin, &resultyMax); } } else { resultxMin = xMin; resultxMax = 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. */ const float step = const_cast(this)->curveView()->pixelWidth() / 2; for (int i = 0; i < functionStore()->numberOfActiveFunctions(); i++) { ExpiringPointer f = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i)); diff --git a/apps/shared/cartesian_function.h b/apps/shared/cartesian_function.h index 4991b5abb..a9f585e12 100644 --- a/apps/shared/cartesian_function.h +++ b/apps/shared/cartesian_function.h @@ -50,7 +50,10 @@ public: double tMax() const override; void setTMin(double tMin); void setTMax(double tMax); + float rangeStep() const override { return (tMax() - tMin())/k_polarParamRangeSearchNumberOfPoints; } + private: + constexpr static float k_polarParamRangeSearchNumberOfPoints = 100.0f; // This is ad hoc, no special justification template Poincare::Coordinate2D privateEvaluateXYAtParameter(T t, Poincare::Context * context) const; /* CartesianFunctionRecordDataBuffer is the layout of the data buffer of Record * representing a CartesianFunction. See comment on diff --git a/apps/shared/function.h b/apps/shared/function.h index 3ee7ceb7d..0193db9c3 100644 --- a/apps/shared/function.h +++ b/apps/shared/function.h @@ -37,6 +37,7 @@ public: virtual bool shouldClipTRangeToXRange() const { return true; } // Returns true if the function will not be displayed if t is outside x range. virtual double tMin() const { return NAN; } virtual double tMax() const { return NAN; } + virtual float rangeStep() const { return NAN; } // Name int nameWithArgument(char * buffer, size_t bufferSize); diff --git a/apps/shared/function_graph_controller.cpp b/apps/shared/function_graph_controller.cpp index 38a02e780..88b93f686 100644 --- a/apps/shared/function_graph_controller.cpp +++ b/apps/shared/function_graph_controller.cpp @@ -81,10 +81,6 @@ InteractiveCurveViewRangeDelegate::Range FunctionGraphController::computeYRange( float max = -FLT_MAX; float xMin = interactiveCurveViewRange->xMin(); float xMax = 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. */ - const float step = curveView()->pixelWidth() / 2; assert(functionStore()->numberOfActiveFunctions() > 0); for (int i = 0; i < functionStore()->numberOfActiveFunctions(); i++) { ExpiringPointer f = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i)); @@ -102,6 +98,11 @@ InteractiveCurveViewRangeDelegate::Range FunctionGraphController::computeYRange( } else if (f->shouldClipTRangeToXRange()) { tMax = minFloat(tMax, 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. */ + float rangeStep = f->rangeStep(); + const float step = std::isnan(rangeStep) ? curveView()->pixelWidth() / 2.0f : rangeStep; const int balancedBound = std::floor((tMax-tMin)/2/step); for (int j = -balancedBound; j <= balancedBound ; j++) { float t = (tMin+tMax)/2 + step * j;