Files
Upsilon/apps/graph/test/caching.cpp
2020-12-07 15:58:56 +01:00

125 lines
5.1 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <quiz.h>
#include "helper.h"
#include <cmath>
using namespace Poincare;
using namespace Shared;
namespace Graph {
bool floatEquals(float a, float b, float tolerance = 1.f/static_cast<float>(Ion::Display::Height)) {
/* The default value for the tolerance is chosen so that the error introduced
* by caching would not typically be visible on screen. */
return (std::isnan(a) && std::isnan(b)) || IsApproximatelyEqual(a, b, tolerance, 0.);
}
void assert_check_cartesian_cache_against_function(ContinuousFunction * function, ContinuousFunctionCache * cache, Context * context, float tMin) {
/* We set the cache to nullptr to force the evaluation (otherwise we would be
* comparing the cache against itself). */
function->setCache(nullptr);
float t;
for (int i = 0; i < Ion::Display::Width; i++) {
t = tMin + i*cache->step();
Coordinate2D<float> cacheValues = cache->valueForParameter(function, context, t);
Coordinate2D<float> functionValues = function->evaluateXYAtParameter(t, context);
quiz_assert(floatEquals(t, cacheValues.x1()));
quiz_assert(floatEquals(t, functionValues.x1()));
quiz_assert(floatEquals(cacheValues.x2(), functionValues.x2()));
}
/* We set back the cache, so that it will not be invalidated in
* PrepareForCaching later. */
function->setCache(cache);
}
void assert_cartesian_cache_stays_valid_while_panning(ContinuousFunction * function, Context * context, InteractiveCurveViewRange * range, CurveViewCursor * cursor, ContinuousFunctionStore * store, float step) {
ContinuousFunctionCache * cache = store->cacheAtIndex(0);
assert(cache);
float tMin, tStep;
constexpr float margin = 0.04f;
constexpr int numberOfMoves = 30;
for (int i = 0; i < numberOfMoves; i++) {
cursor->moveTo(cursor->t() + step, cursor->x() + step, function->evaluateXYAtParameter(cursor->x() + step, context).x2());
range->panToMakePointVisible(cursor->x(), cursor->y(), margin, margin, margin, margin, (range->xMax() - range->xMin()) / (Ion::Display::Width - 1));
tMin = range->xMin();
tStep = (range->xMax() - range->xMin()) / (Ion::Display::Width - 1);
ContinuousFunctionCache::PrepareForCaching(function, cache, tMin, tStep);
assert_check_cartesian_cache_against_function(function, cache, context, tMin);
}
}
void assert_check_polar_cache_against_function(ContinuousFunction * function, Context * context, InteractiveCurveViewRange * range, ContinuousFunctionStore * store) {
ContinuousFunctionCache * cache = store->cacheAtIndex(0);
assert(cache);
float tMin = range->xMin();
float tMax = range->xMax();
float tStep, tCacheStep;
ContinuousFunctionCache::ComputeNonCartesianSteps(&tStep, &tCacheStep, tMax, tMin);
ContinuousFunctionCache::PrepareForCaching(function, cache, tMin, tCacheStep);
// Fill the cache
float t;
for (int i = 0; i < Ion::Display::Width / 2; i++) {
t = tMin + i*cache->step();
function->evaluateXYAtParameter(t, context);
}
function->setCache(nullptr);
for (int i = 0; i < Ion::Display::Width / 2; i++) {
t = tMin + i*cache->step();
Coordinate2D<float> cacheValues = cache->valueForParameter(function, context, t);
Coordinate2D<float> functionValues = function->evaluateXYAtParameter(t, context);
quiz_assert(floatEquals(cacheValues.x1(), functionValues.x1()));
quiz_assert(floatEquals(cacheValues.x2(), functionValues.x2()));
}
}
void assert_cache_stays_valid(ContinuousFunction::PlotType type, const char * definition, float rangeXMin = -5, float rangeXMax = 5) {
GlobalContext globalContext;
ContinuousFunctionStore functionStore;
InteractiveCurveViewRange graphRange;
graphRange.setXMin(rangeXMin);
graphRange.setXMax(rangeXMax);
graphRange.setYMin(-3.f);
graphRange.setYMax(3.f);
CurveViewCursor cursor;
ContinuousFunction * function = addFunction(definition, type, &functionStore, &globalContext);
Coordinate2D<float> origin = function->evaluateXYAtParameter(0.f, &globalContext);
cursor.moveTo(0.f, origin.x1(), origin.x2());
if (type == Cartesian) {
assert_cartesian_cache_stays_valid_while_panning(function, &globalContext, &graphRange, &cursor, &functionStore, 2.f);
assert_cartesian_cache_stays_valid_while_panning(function, &globalContext, &graphRange, &cursor, &functionStore, -0.4f);
} else {
assert_check_polar_cache_against_function(function, &globalContext, &graphRange, &functionStore);
}
functionStore.removeAll();
}
QUIZ_CASE(graph_caching) {
assert_cache_stays_valid(Cartesian, "x");
assert_cache_stays_valid(Cartesian, "x^2");
assert_cache_stays_valid(Cartesian, "sin(x)");
assert_cache_stays_valid(Cartesian, "sin(x)", -1e6f, 2e8f);
assert_cache_stays_valid(Cartesian, "sin(x^2)");
assert_cache_stays_valid(Cartesian, "1/x");
assert_cache_stays_valid(Cartesian, "1/x", -5e-5f, 5e-5f);
assert_cache_stays_valid(Cartesian, "-^x");
assert_cache_stays_valid(Polar, "1", 0.f, 360.f);
assert_cache_stays_valid(Polar, "θ", 0.f, 360.f);
assert_cache_stays_valid(Polar, "sin(θ)", 0.f, 360.f);
assert_cache_stays_valid(Polar, "sin(θ)", 2e-4f, 1e-3f);
assert_cache_stays_valid(Polar, "cos(5θ)", 0.f, 360.f);
assert_cache_stays_valid(Polar, "cos(5θ)", -1e8f, 1e8f);
}
}