mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[apps/graph] Round x before evaluating graph cursor on scroll
Change-Id: I13500669963eb8130e188a898bed0bf63655add6
This commit is contained in:
committed by
Émilie Feral
parent
bd2609bcba
commit
9e12b61849
@@ -27,8 +27,11 @@ bool GraphControllerHelper::privateMoveCursorHorizontally(Shared::CurveViewCurso
|
||||
function = App::app()->functionStore()->modelForRecord(record); // Reload the expiring pointer
|
||||
double dir = (direction > 0 ? 1.0 : -1.0);
|
||||
double step = function->plotType() == ContinuousFunction::PlotType::Cartesian ? range->xGridUnit()/numberOfStepsInGradUnit : (tMax-tMin)/k_definitionDomainDivisor;
|
||||
step *= scrollSpeed;
|
||||
t += dir * step;
|
||||
t += dir * step * scrollSpeed;
|
||||
|
||||
// If possible, round t so that f(x) matches f evaluated at displayed x
|
||||
t = FunctionBannerDelegate::getValueDisplayedOnBanner(t, App::app()->localContext(), 0.05 * step, true);
|
||||
|
||||
t = std::max(tMin, std::min(tMax, t));
|
||||
Coordinate2D<double> xy = function->evaluateXYAtParameter(t, App::app()->localContext());
|
||||
cursor->moveTo(t, xy.x1(), xy.x2());
|
||||
|
||||
@@ -7,6 +7,12 @@ using namespace Poincare;
|
||||
|
||||
namespace Shared {
|
||||
|
||||
constexpr int k_precision = Preferences::MediumNumberOfSignificantDigits;
|
||||
|
||||
int convertDoubleToText(double t, char * buffer, int bufferSize) {
|
||||
return PoincareHelpers::ConvertFloatToText<double>(t, buffer, bufferSize, k_precision);
|
||||
}
|
||||
|
||||
void FunctionBannerDelegate::reloadBannerViewForCursorOnFunction(CurveViewCursor * cursor, Ion::Storage::Record record, FunctionStore * functionStore, Poincare::Context * context) {
|
||||
ExpiringPointer<Function> function = functionStore->modelForRecord(record);
|
||||
constexpr int bufferSize = k_maxNumberOfCharacters+PrintFloat::charSizeForFloatsWithPrecision(Preferences::LargeNumberOfSignificantDigits);
|
||||
@@ -18,9 +24,7 @@ void FunctionBannerDelegate::reloadBannerViewForCursorOnFunction(CurveViewCursor
|
||||
strlcpy(buffer + numberOfChar, "=", bufferSize - numberOfChar);
|
||||
bannerView()->abscissaSymbol()->setText(buffer);
|
||||
|
||||
constexpr int precision = Preferences::MediumNumberOfSignificantDigits;
|
||||
|
||||
numberOfChar = PoincareHelpers::ConvertFloatToText<double>(cursor->t(), buffer, bufferSize, precision);
|
||||
numberOfChar = convertDoubleToText(cursor->t(), buffer, bufferSize);
|
||||
assert(numberOfChar <= bufferSize);
|
||||
strlcpy(buffer+numberOfChar, space, bufferSize - numberOfChar);
|
||||
bannerView()->abscissaValue()->setText(buffer);
|
||||
@@ -28,7 +32,7 @@ void FunctionBannerDelegate::reloadBannerViewForCursorOnFunction(CurveViewCursor
|
||||
numberOfChar = function->nameWithArgument(buffer, bufferSize);
|
||||
assert(numberOfChar <= bufferSize);
|
||||
numberOfChar += strlcpy(buffer+numberOfChar, "=", bufferSize-numberOfChar);
|
||||
numberOfChar += function->printValue(cursor->t(), cursor->x(),cursor->y(), buffer+numberOfChar, bufferSize-numberOfChar, precision, context);
|
||||
numberOfChar += function->printValue(cursor->t(), cursor->x(),cursor->y(), buffer+numberOfChar, bufferSize-numberOfChar, k_precision, context);
|
||||
assert(numberOfChar <= bufferSize);
|
||||
strlcpy(buffer+numberOfChar, space, bufferSize-numberOfChar);
|
||||
bannerView()->ordinateView()->setText(buffer);
|
||||
@@ -36,4 +40,22 @@ void FunctionBannerDelegate::reloadBannerViewForCursorOnFunction(CurveViewCursor
|
||||
bannerView()->reload();
|
||||
}
|
||||
|
||||
double FunctionBannerDelegate::getValueDisplayedOnBanner(double t, Poincare::Context * context, double deltaThreshold, bool roundToZero) {
|
||||
if (roundToZero && std::fabs(t) < deltaThreshold) {
|
||||
// Round to 0 to avoid rounding to unnecessary low non-zero value.
|
||||
return 0.0;
|
||||
}
|
||||
// Convert float to text
|
||||
constexpr int bufferSize = k_maxNumberOfCharacters+PrintFloat::charSizeForFloatsWithPrecision(k_precision);
|
||||
char buffer[bufferSize];
|
||||
int numberOfChar = convertDoubleToText(t, buffer, bufferSize);
|
||||
assert(numberOfChar <= bufferSize);
|
||||
// Silence compiler warnings
|
||||
(void) numberOfChar;
|
||||
// Extract displayed value
|
||||
double displayedValue = PoincareHelpers::ApproximateToScalar<double>(buffer, context);
|
||||
// Return displayed value if difference from t is under deltaThreshold
|
||||
return std::fabs(displayedValue-t) < deltaThreshold ? displayedValue : t;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,14 @@ namespace Shared {
|
||||
class FunctionBannerDelegate {
|
||||
public:
|
||||
constexpr static int k_maxNumberOfCharacters = 50;
|
||||
/* getValueDisplayedOnBanner returns the value of t as displayed in the
|
||||
* banner, unless the difference from t exceeds deltaThreshold. If so,
|
||||
* return t. For instance, when a function is plotted between 1.000001 and
|
||||
* 1.000003, and the user goes to x = 1.000002, a small deltaThreshold
|
||||
* prevents him from being sent to x = 1
|
||||
* Note : Due to double encoding, not all values of t can be properly rounded.
|
||||
* For instance, x displayed as 0.01 can at best be encoded to x=0.010...02 */
|
||||
static double getValueDisplayedOnBanner(double t, Poincare::Context * context, double deltaThreshold, bool roundToZero = false);
|
||||
protected:
|
||||
void reloadBannerViewForCursorOnFunction(CurveViewCursor * cursor, Ion::Storage::Record record, FunctionStore * functionStore, Poincare::Context * context);
|
||||
virtual XYBannerView * bannerView() = 0;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "function_app.h"
|
||||
#include <assert.h>
|
||||
#include <cmath>
|
||||
#include <ion/display.h>
|
||||
|
||||
namespace Shared {
|
||||
|
||||
@@ -15,6 +16,10 @@ bool FunctionGoToParameterController::confirmParameterAtIndex(int parameterIndex
|
||||
assert(parameterIndex == 0);
|
||||
FunctionApp * myApp = FunctionApp::app();
|
||||
ExpiringPointer<Function> function = myApp->functionStore()->modelForRecord(m_record);
|
||||
// If possible, round f so that we go to the evaluation of the displayed f
|
||||
double pixelWidth = (m_graphRange->xMax() - m_graphRange->xMin()) / Ion::Display::Width;
|
||||
f = FunctionBannerDelegate::getValueDisplayedOnBanner(f, myApp->localContext(), pixelWidth, false);
|
||||
|
||||
Poincare::Coordinate2D<double> xy = function->evaluateXYAtParameter(f, myApp->localContext());
|
||||
m_cursor->moveTo(f, xy.x1(), xy.x2());
|
||||
m_graphRange->centerAxisAround(CurveViewRange::Axis::X, m_cursor->x());
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "function_banner_delegate.h"
|
||||
#include "interactive_curve_view_controller.h"
|
||||
#include <cmath>
|
||||
#include <float.h>
|
||||
@@ -219,6 +220,10 @@ bool InteractiveCurveViewController::textFieldDidFinishEditing(TextField * textF
|
||||
if (textFieldDelegateApp()->hasUndefinedValue(text, floatBody)) {
|
||||
return false;
|
||||
}
|
||||
/* If possible, round floatBody so that we go to the evaluation of the
|
||||
* displayed floatBody */
|
||||
floatBody = FunctionBannerDelegate::getValueDisplayedOnBanner(floatBody, textFieldDelegateApp()->localContext(), curveView()->pixelWidth(), false);
|
||||
|
||||
Coordinate2D<double> xy = xyValues(selectedCurveIndex(), floatBody, textFieldDelegateApp()->localContext());
|
||||
m_cursor->moveTo(floatBody, xy.x1(), xy.x2());
|
||||
interactiveCurveViewRange()->panToMakePointVisible(m_cursor->x(), m_cursor->y(), cursorTopMarginRatio(), cursorRightMarginRatio(), cursorBottomMarginRatio(), cursorLeftMarginRatio(), curveView()->pixelWidth());
|
||||
|
||||
@@ -229,7 +229,7 @@ PrintFloat::TextLengths PrintFloat::ConvertFloatToTextPrivate(T f, char * buffer
|
||||
* function. */
|
||||
if (std::isnan(mantissa) || std::isinf(mantissa)) {
|
||||
mantissa = std::round(std::pow(10, std::log10(std::fabs(f))+(T)(numberOfSignificantDigits -1 - exponentInBase10)));
|
||||
mantissa = std::copysign(mantissa, f);
|
||||
mantissa = std::copysign(mantissa, static_cast<double>(f));
|
||||
}
|
||||
/* We update the exponent in base 10 (if 0.99999999 was rounded to 1 for
|
||||
* instance)
|
||||
|
||||
Reference in New Issue
Block a user