diff --git a/apps/shared/interactive_curve_view_range.cpp b/apps/shared/interactive_curve_view_range.cpp index d1b6f6970..0f314b0c2 100644 --- a/apps/shared/interactive_curve_view_range.cpp +++ b/apps/shared/interactive_curve_view_range.cpp @@ -157,12 +157,8 @@ void InteractiveCurveViewRange::normalize(bool forceChangeY) { m_yRange.setMin(newYMin, k_lowerMaxFloat, k_upperMaxFloat); MemoizedCurveViewRange::protectedSetYMax(newYMax, k_lowerMaxFloat, k_upperMaxFloat); - /* When the coordinates reach 10^6, the float type is not precise enough to - * properly normalize. */ - // FIXME : Fine a more precise way to filter the edge cases - constexpr float limit = 1e6f; - assert(isOrthonormal() || xMin() < -limit || xMax() > limit || yMin() < -limit || yMax() > limit); - (void) limit; // Silence compilation warning about unused variable. + /* The range should be close to orthonormal, unless it has been clipped because the maximum bounds have been reached. */ + assert(isOrthonormal() || xMin() <= - k_lowerMaxFloat || xMax() >= k_lowerMaxFloat || yMin() <= - k_lowerMaxFloat || yMax() >= k_lowerMaxFloat); setZoomNormalize(isOrthonormal()); } @@ -281,8 +277,17 @@ void InteractiveCurveViewRange::panToMakePointVisible(float x, float y, float to } bool InteractiveCurveViewRange::isOrthonormal(float tolerance) const { + if (tolerance == 0.f) { + float xr = std::fabs(xMin()) > std::fabs(xMax()) ? xMax() / xMin() : xMin() / xMax(); + float yr = std::fabs(yMin()) > std::fabs(yMax()) ? yMax() / yMin() : yMin() / yMax(); + /* The subtraction x - y induces a loss of significance of -log2(1-x/y) + * bits. Since normalizing requires computing xMax - xMin and yMax - yMin, + * the ratio of the normalized range will deviate from the Normal ratio. We + * add an extra two lost bits to account for loss of precision from other + * sources. */ + tolerance = std::pow(2.f, - std::log2(std::min(1.f - xr, 1.f - yr)) - 23.f + 2.f); + } float ratio = (yMax() - yMin()) / (xMax() - xMin()); - float ratioDifference = std::fabs(std::log(ratio / NormalYXRatio())); - return ratioDifference <= tolerance; + return ratio <= NormalYXRatio() + tolerance && ratio >= NormalYXRatio() - tolerance; } } diff --git a/apps/shared/interactive_curve_view_range.h b/apps/shared/interactive_curve_view_range.h index dfde5c484..f7b860090 100644 --- a/apps/shared/interactive_curve_view_range.h +++ b/apps/shared/interactive_curve_view_range.h @@ -24,9 +24,9 @@ public: } static constexpr float NormalYXRatio() { return NormalizedYHalfRange(1.f) / NormalizedXHalfRange(1.f); } - /* A tolerance of 0.001 is necessary to cover the imprecision with the - * largest ranges, around 10^7 */ - bool isOrthonormal(float tolerance = 1e-3f) const; + /* If the tolerance is null, isOrthonormal will adapt the tolerance to take + * the loss of significance when changing the ratio into account. */ + bool isOrthonormal(float tolerance = 0.f) const; void setDelegate(InteractiveCurveViewRangeDelegate * delegate); uint32_t rangeChecksum() override; @@ -62,7 +62,7 @@ protected: constexpr static float k_lowerMaxFloat = 9E+7f; constexpr static float k_maxRatioPositionRange = 1E5f; /* The tolerance is chosen to normalize sqrt(x) */ - constexpr static float k_orthonormalTolerance = 0.7f; + constexpr static float k_orthonormalTolerance = 0.24f; static float clipped(float x, bool isMax) { return Range1D::clipped(x, isMax, k_lowerMaxFloat, k_upperMaxFloat); } /* In normalized settings, we put each axis so that 1cm = 2 units. For now, * the screen has size 43.2mm * 57.6mm.