From c3008ca36038caa4464d1dd806ed48b97bd82a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 17 Jan 2017 17:42:30 +0100 Subject: [PATCH] [apps] Improvements of MVC structure regarding curve views and ranges Change-Id: Iec8031dbf349c34c18694dffabd02ef9c88ebf2d --- apps/Makefile | 1 + apps/banner_view.cpp | 7 +- apps/banner_view.h | 2 +- apps/curve_view.cpp | 71 +++++--------- apps/curve_view.h | 13 +-- apps/curve_view_range.cpp | 10 ++ apps/curve_view_range.h | 4 + .../graph/graph/goto_parameter_controller.cpp | 2 +- apps/graph/graph/graph_controller.cpp | 22 +++-- apps/graph/graph/graph_controller.h | 10 +- apps/graph/graph/graph_view.cpp | 2 +- .../initialisation_parameter_controller.cpp | 2 +- apps/interactive_curve_view_controller.cpp | 35 +++---- apps/interactive_curve_view_controller.h | 10 +- apps/interactive_curve_view_range.cpp | 94 +++---------------- apps/interactive_curve_view_range.h | 33 ++----- apps/memoized_curve_view_range.cpp | 70 ++++++++++++++ apps/memoized_curve_view_range.h | 32 +++++++ apps/probability/law/binomial_law.cpp | 21 +++-- apps/probability/law/exponential_law.cpp | 20 ++-- apps/probability/law/law.h | 4 + apps/probability/law/normal_law.cpp | 8 +- apps/probability/law/poisson_law.cpp | 12 +-- apps/probability/law/uniform_law.cpp | 8 +- apps/probability/law_curve_view.cpp | 2 +- .../regression/go_to_parameter_controller.cpp | 4 +- apps/regression/graph_controller.cpp | 46 +++++---- apps/regression/graph_controller.h | 9 +- apps/regression/graph_view.cpp | 2 +- .../initialisation_parameter_controller.cpp | 4 +- apps/regression/store.cpp | 8 +- apps/regression/store.h | 4 + apps/statistics/box_controller.cpp | 1 - apps/statistics/box_range.cpp | 17 ++-- apps/statistics/box_range.h | 4 + apps/statistics/box_view.cpp | 9 +- apps/statistics/box_view.h | 2 +- apps/statistics/histogram_controller.cpp | 11 +-- apps/statistics/histogram_view.cpp | 9 +- apps/statistics/histogram_view.h | 2 +- apps/statistics/store.cpp | 10 +- apps/statistics/store.h | 8 +- 42 files changed, 343 insertions(+), 302 deletions(-) create mode 100644 apps/memoized_curve_view_range.cpp create mode 100644 apps/memoized_curve_view_range.h diff --git a/apps/Makefile b/apps/Makefile index 65bcee208..270d8c47d 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -21,6 +21,7 @@ app_objs += $(addprefix apps/,\ interactive_curve_view_controller.o\ interactive_curve_view_range.o\ main.o\ + memoized_curve_view_range.o\ node.o\ node_list_view_controller.o\ node_navigation_controller.o\ diff --git a/apps/banner_view.cpp b/apps/banner_view.cpp index e359c3837..7202e62e1 100644 --- a/apps/banner_view.cpp +++ b/apps/banner_view.cpp @@ -7,8 +7,14 @@ void BannerView::drawRect(KDContext * ctx, KDRect rect) const { } void BannerView::setLegendAtIndex(char * text, int index) { + /* The layout of the banner's subviews depends on their content. + * Indeed, we're using a "centered text" algorithm to layout the subviews. + * So changing a legend implies two things + * - First, we need to update the textView to ensure it has the new content + * - Second, we need to relayout *all* of our subviews. */ TextView * textView = textViewAtIndex(index); textView->setText(text); + layoutSubviews(); } KDSize BannerView::minimalSizeForOptimalDisplay() { @@ -20,7 +26,6 @@ int BannerView::numberOfSubviews() const { } void BannerView::layoutSubviews() { - markRectAsDirty(bounds()); /* We iterate on subviews, adding their width until we exceed the view bound. * The last subview that exceeds the bound is recorded as the first subview of * the next line. For the current line, we scan again the subviews and frame diff --git a/apps/banner_view.h b/apps/banner_view.h index c857e12b4..8f3d89d13 100644 --- a/apps/banner_view.h +++ b/apps/banner_view.h @@ -8,10 +8,10 @@ public: void drawRect(KDContext * ctx, KDRect rect) const override; void setLegendAtIndex(char * text, int index); KDSize minimalSizeForOptimalDisplay() override; - void layoutSubviews() override; private: int numberOfSubviews() const override; View * subviewAtIndex(int index) override; + void layoutSubviews() override; int numberOfLines(); virtual TextView * textViewAtIndex(int i) = 0; }; diff --git a/apps/curve_view.cpp b/apps/curve_view.cpp index 45f09d7a0..9fc25662e 100644 --- a/apps/curve_view.cpp +++ b/apps/curve_view.cpp @@ -9,42 +9,30 @@ constexpr KDColor CurveView::k_axisColor; constexpr KDColor CurveView::k_gridColor; CurveView::CurveView(CurveViewRange * curveViewRange, CurveViewCursor * curveViewCursor, BannerView * bannerView, - View * cursorView, float topMarginFactor, float rightMarginFactor, float bottomMarginFactor, - float leftMarginFactor) : + View * cursorView) : View(), m_curveViewRange(curveViewRange), m_curveViewCursor(curveViewCursor), m_bannerView(bannerView), m_cursorView(cursorView), - m_topMarginFactor(topMarginFactor), - m_bottomMarginFactor(bottomMarginFactor), - m_leftMarginFactor(leftMarginFactor), - m_rightMarginFactor(rightMarginFactor) + m_drawnRangeVersion(0) { } void CurveView::reload() { - markRectAsDirty(bounds()); - if (label(Axis::Horizontal, 0) != nullptr) { - computeLabels(Axis::Horizontal); - } - if (label(Axis::Vertical, 0) != nullptr) { - computeLabels(Axis::Vertical); - } - layoutSubviews(); -} - -void CurveView::reloadSelection() { - if (m_curveViewCursor != nullptr) { - float pixelXSelection = roundf(floatToPixel(Axis::Horizontal, m_curveViewCursor->x())); - float pixelYSelection = roundf(floatToPixel(Axis::Vertical, m_curveViewCursor->y())); - KDRect dirtyZone(KDRect(pixelXSelection - k_cursorSize/2, pixelYSelection - k_cursorSize/2, k_cursorSize, k_cursorSize)); - markRectAsDirty(dirtyZone); - layoutCursor(); - if (m_bannerView != nullptr) { - m_bannerView->layoutSubviews(); + uint32_t rangeVersion = m_curveViewRange->rangeChecksum(); + if (m_drawnRangeVersion != rangeVersion) { + // FIXME: This should also be called if the *curve* changed + m_drawnRangeVersion = rangeVersion; + markRectAsDirty(bounds()); + if (label(Axis::Horizontal, 0) != nullptr) { + computeLabels(Axis::Horizontal); + } + if (label(Axis::Vertical, 0) != nullptr) { + computeLabels(Axis::Vertical); } } + layoutSubviews(); } bool CurveView::isMainViewSelected() const { @@ -54,7 +42,7 @@ bool CurveView::isMainViewSelected() const { void CurveView::selectMainView(bool mainViewSelected) { if (m_mainViewSelected != mainViewSelected) { m_mainViewSelected = mainViewSelected; - reloadSelection(); + reload(); layoutSubviews(); } } @@ -65,18 +53,12 @@ void CurveView::setCurveViewRange(CurveViewRange * curveViewRange) { float CurveView::min(Axis axis) const { assert(axis == Axis::Horizontal || axis == Axis::Vertical); - float range = axis == Axis::Horizontal ? m_curveViewRange->xMax() - m_curveViewRange->xMin() : m_curveViewRange->yMax() - m_curveViewRange->yMin(); - float absoluteMin = axis == Axis::Horizontal ? m_curveViewRange->xMin(): m_curveViewRange->yMin(); - float marginFactor = axis == Axis::Horizontal ? m_leftMarginFactor : m_bottomMarginFactor; - return absoluteMin - marginFactor*range; + return (axis == Axis::Horizontal ? m_curveViewRange->xMin(): m_curveViewRange->yMin()); } float CurveView::max(Axis axis) const { assert(axis == Axis::Horizontal || axis == Axis::Vertical); - float range = axis == Axis::Horizontal ? m_curveViewRange->xMax() - m_curveViewRange->xMin() : m_curveViewRange->yMax() - m_curveViewRange->yMin(); - float absoluteMax = (axis == Axis::Horizontal ? m_curveViewRange->xMax() : m_curveViewRange->yMax()); - float marginFactor = axis == Axis::Horizontal ? m_rightMarginFactor : m_topMarginFactor; - return absoluteMax + marginFactor*range; + return (axis == Axis::Horizontal ? m_curveViewRange->xMax() : m_curveViewRange->yMax()); } float CurveView::gridUnit(Axis axis) const { @@ -395,19 +377,6 @@ void CurveView::stampAtLocation(KDContext * ctx, KDRect rect, float pxf, float p } void CurveView::layoutSubviews() { - layoutCursor(); - if (m_bannerView != nullptr) { - m_bannerView->setFrame(bounds()); - KDCoordinate bannerHeight = m_bannerView->minimalSizeForOptimalDisplay().height(); - KDRect bannerFrame(KDRect(0, bounds().height()- bannerHeight, bounds().width(), bannerHeight)); - if (!m_mainViewSelected) { - bannerFrame = KDRectZero; - } - m_bannerView->setFrame(bannerFrame); - } -} - -void CurveView::layoutCursor() { if (m_curveViewCursor != nullptr && m_cursorView != nullptr) { KDCoordinate xCursorPixelPosition = roundf(floatToPixel(Axis::Horizontal, m_curveViewCursor->x())); KDCoordinate yCursorPixelPosition = roundf(floatToPixel(Axis::Vertical, m_curveViewCursor->y())); @@ -417,6 +386,14 @@ void CurveView::layoutCursor() { } m_cursorView->setFrame(cursorFrame); } + if (m_bannerView != nullptr) { + KDCoordinate bannerHeight = m_bannerView->minimalSizeForOptimalDisplay().height(); + KDRect bannerFrame(KDRect(0, bounds().height()- bannerHeight, bounds().width(), bannerHeight)); + if (!m_mainViewSelected) { + bannerFrame = KDRectZero; + } + m_bannerView->setFrame(bannerFrame); + } } int CurveView::numberOfSubviews() const { diff --git a/apps/curve_view.h b/apps/curve_view.h index 2705d6965..c6f499585 100644 --- a/apps/curve_view.h +++ b/apps/curve_view.h @@ -16,11 +16,8 @@ public: Vertical = 1 }; CurveView(CurveViewRange * curveViewRange = nullptr, CurveViewCursor * curveViewCursor = nullptr, - BannerView * bannerView = nullptr, View * cursorView = nullptr, float topMarginFactor = 0.0f, - float rightMarginFactor = 0.0f, float bottomMarginFactor = 0.0f, float leftMarginFactor = 0.0f); - // Reload methods - void reload(); - virtual void reloadSelection(); + BannerView * bannerView = nullptr, View * cursorView = nullptr); + virtual void reload(); // When the main view is selected, the banner view is visible bool isMainViewSelected() const; void selectMainView(bool mainViewSelected); @@ -72,7 +69,6 @@ private: * anti alising. */ void stampAtLocation(KDContext * ctx, KDRect rect, float pxf, float pyf, KDColor color) const; void layoutSubviews() override; - void layoutCursor(); int numberOfSubviews() const override; View * subviewAtIndex(int index) override; /* m_curveViewRange has to be non null but the cursor model, the banner and @@ -82,10 +78,7 @@ private: BannerView * m_bannerView; View * m_cursorView; bool m_mainViewSelected; - float m_topMarginFactor; - float m_bottomMarginFactor; - float m_leftMarginFactor; - float m_rightMarginFactor; + uint32_t m_drawnRangeVersion; }; #endif diff --git a/apps/curve_view_range.cpp b/apps/curve_view_range.cpp index e5d0fffe3..f116230c3 100644 --- a/apps/curve_view_range.cpp +++ b/apps/curve_view_range.cpp @@ -1,7 +1,17 @@ #include "curve_view_range.h" #include +#include +#include +#include #include +uint32_t CurveViewRange::rangeChecksum() { + float data[4] = {xMin(), xMax(), yMin(), yMax()}; + size_t dataLengthInBytes = 4*sizeof(float); + assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4 + return Ion::crc32((uint32_t *)data, dataLengthInBytes>>2); +} + float CurveViewRange::yGridUnit() { return 0.0f; } diff --git a/apps/curve_view_range.h b/apps/curve_view_range.h index 69d13e8b7..83a330491 100644 --- a/apps/curve_view_range.h +++ b/apps/curve_view_range.h @@ -1,12 +1,16 @@ #ifndef APPS_CURVE_VIEW_RANGE_H #define APPS_CURVE_VIEW_RANGE_H +#include + class CurveViewRange { public: enum class Axis { X, Y }; + virtual uint32_t rangeChecksum(); + virtual float xMin() = 0; virtual float xMax() = 0; virtual float yMin() = 0; diff --git a/apps/graph/graph/goto_parameter_controller.cpp b/apps/graph/graph/goto_parameter_controller.cpp index 87732df36..92f6a2f6d 100644 --- a/apps/graph/graph/goto_parameter_controller.cpp +++ b/apps/graph/graph/goto_parameter_controller.cpp @@ -28,7 +28,7 @@ void GoToParameterController::setParameterAtIndex(int parameterIndex, float f) { float y = m_function->evaluateAtAbscissa(f, graphApp->localContext()); m_graphRange->centerAxisAround(CurveViewRange::Axis::X, f); m_graphRange->centerAxisAround(CurveViewRange::Axis::Y, y); - m_graphRange->moveCursorTo(f, y); + m_cursor->moveTo(f, y); } int GoToParameterController::numberOfRows() { diff --git a/apps/graph/graph/graph_controller.cpp b/apps/graph/graph/graph_controller.cpp index 0ac43b81d..44d28d469 100644 --- a/apps/graph/graph/graph_controller.cpp +++ b/apps/graph/graph/graph_controller.cpp @@ -119,7 +119,6 @@ void GraphController::reloadBannerView() { Float(y).convertFloatToText(buffer + legendLength, Float::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Float::DisplayMode::Decimal); m_bannerView.setLegendAtIndex(buffer, 2); } - m_bannerView.layoutSubviews(); } void GraphController::initRangeParameters() { @@ -134,20 +133,23 @@ void GraphController::initCursorParameters() { Function * firstFunction = m_functionStore->activeFunctionAtIndex(0); App * graphApp = (Graph::App *)app(); float y = firstFunction->evaluateAtAbscissa(x, graphApp->localContext()); - m_graphRange.moveCursorTo(x, y); + m_cursor.moveTo(x, y); + m_graphRange.panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); } -int GraphController::moveCursorHorizontally(int direction) { +bool GraphController::moveCursorHorizontally(int direction) { float xCursorPosition = m_cursor.x(); - float x = direction > 0 ? xCursorPosition + m_graphRange.xGridUnit()/InteractiveCurveViewRange::k_numberOfCursorStepsInGradUnit : - xCursorPosition - m_graphRange.xGridUnit()/InteractiveCurveViewRange::k_numberOfCursorStepsInGradUnit; + float x = direction > 0 ? xCursorPosition + m_graphRange.xGridUnit()/k_numberOfCursorStepsInGradUnit : + xCursorPosition - m_graphRange.xGridUnit()/k_numberOfCursorStepsInGradUnit; Function * f = m_functionStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor); App * graphApp = (Graph::App *)app(); float y = f->evaluateAtAbscissa(x, graphApp->localContext()); - return m_graphRange.moveCursorTo(x, y);; + m_cursor.moveTo(x, y); + m_graphRange.panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); + return true; } -int GraphController::moveCursorVertically(int direction) { +bool GraphController::moveCursorVertically(int direction) { Function * actualFunction = m_functionStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor); App * graphApp = (Graph::App *)app(); float y = actualFunction->evaluateAtAbscissa(m_cursor.x(), graphApp->localContext()); @@ -164,9 +166,11 @@ int GraphController::moveCursorVertically(int direction) { } } if (nextFunction == actualFunction) { - return -1; + return false; } - return m_graphRange.moveCursorTo(m_cursor.x(), nextY); + m_cursor.moveTo(m_cursor.x(), nextY); + m_graphRange.panToMakePointVisible(m_cursor.x(), m_cursor.y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); + return true; } uint32_t GraphController::modelVersion() { diff --git a/apps/graph/graph/graph_controller.h b/apps/graph/graph/graph_controller.h index 7efd7d7c7..5613c10b0 100644 --- a/apps/graph/graph/graph_controller.h +++ b/apps/graph/graph/graph_controller.h @@ -21,16 +21,20 @@ public: const char * emptyMessage() override; bool didChangeRange(InteractiveCurveViewRange * interactiveCurveViewRange) override; - private: + constexpr static float k_cursorTopMarginRatio = 0.025f; + constexpr static float k_cursorRightMarginRatio = 0.015f; + constexpr static float k_cursorBottomMarginRatio = 0.08f; + constexpr static float k_cursorLeftMarginRatio = 0.015f; + constexpr static int k_maxNumberOfCharacters = 8; BannerView * bannerView() override; bool handleEnter() override; void reloadBannerView() override; void initRangeParameters() override; void initCursorParameters() override; - int moveCursorHorizontally(int direction) override; - int moveCursorVertically(int direction) override; + bool moveCursorHorizontally(int direction) override; + bool moveCursorVertically(int direction) override; uint32_t modelVersion() override; uint32_t rangeVersion() override; InteractiveCurveViewRange * interactiveCurveViewRange() override; diff --git a/apps/graph/graph/graph_view.cpp b/apps/graph/graph/graph_view.cpp index 1d8be6374..8d85f9d14 100644 --- a/apps/graph/graph/graph_view.cpp +++ b/apps/graph/graph/graph_view.cpp @@ -6,7 +6,7 @@ namespace Graph { GraphView::GraphView(FunctionStore * functionStore, InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor, ::BannerView * bannerView, View * cursorView) : - CurveView(graphRange, cursor, bannerView, cursorView, 0.0f, 0.0f, 0.1f, 0.0f), + CurveView(graphRange, cursor, bannerView, cursorView), m_functionStore(functionStore), m_context(nullptr) { diff --git a/apps/graph/graph/initialisation_parameter_controller.cpp b/apps/graph/graph/initialisation_parameter_controller.cpp index 395d8045d..acc689e0d 100644 --- a/apps/graph/graph/initialisation_parameter_controller.cpp +++ b/apps/graph/graph/initialisation_parameter_controller.cpp @@ -28,7 +28,7 @@ void InitialisationParameterController::didBecomeFirstResponder() { bool InitialisationParameterController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::OK) { RangeMethodPointer rangeMethods[k_totalNumberOfCells] = {&InteractiveCurveViewRange::setTrigonometric, - &InteractiveCurveViewRange::roundAbscissa, &InteractiveCurveViewRange::normalize, &InteractiveCurveViewRange::setDefault}; + &InteractiveCurveViewRange::roundAbscissa, &InteractiveCurveViewRange::normalize, &InteractiveCurveViewRange::setDefault}; (m_graphRange->*rangeMethods[m_selectableTableView.selectedRow()])(); StackViewController * stack = (StackViewController *)parentResponder(); stack->pop(); diff --git a/apps/interactive_curve_view_controller.cpp b/apps/interactive_curve_view_controller.cpp index 8f3cbccbf..55c1a482f 100644 --- a/apps/interactive_curve_view_controller.cpp +++ b/apps/interactive_curve_view_controller.cpp @@ -40,7 +40,7 @@ bool InteractiveCurveViewController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Down) { headerViewController()->setSelectedButton(-1); curveView()->selectMainView(true); - curveView()->reloadSelection(); + curveView()->reload(); reloadBannerView(); return true; } @@ -65,36 +65,25 @@ bool InteractiveCurveViewController::handleEvent(Ion::Events::Event event) { } if (event == Ion::Events::Left || event == Ion::Events::Right) { int direction = event == Ion::Events::Left ? -1 : 1; - curveView()->reloadSelection(); - int didMoveCursor = moveCursorHorizontally(direction); - if (didMoveCursor == 0) { - curveView()->reloadSelection(); - } - if (didMoveCursor == 1) { + if (moveCursorHorizontally(direction)) { curveView()->reload(); + reloadBannerView(); + return true; } - reloadBannerView(); - return (didMoveCursor >= 0); + return false; } if (event == Ion::Events::Down || event == Ion::Events::Up) { int direction = event == Ion::Events::Down ? -1 : 1; - curveView()->reloadSelection(); - int didMoveCursor = moveCursorVertically(direction); - if (didMoveCursor < 0) { - if (event == Ion::Events::Down) { - return false; - } - curveView()->selectMainView(false); - headerViewController()->setSelectedButton(0); + if (moveCursorVertically(direction)) { + curveView()->reload(); + reloadBannerView(); return true; } - if (didMoveCursor == 0) { - curveView()->reloadSelection(); + if (event == Ion::Events::Down) { + return false; } - if (didMoveCursor == 1) { - curveView()->reload(); - } - reloadBannerView(); + curveView()->selectMainView(false); + headerViewController()->setSelectedButton(0); return true; } if (event == Ion::Events::OK) { diff --git a/apps/interactive_curve_view_controller.h b/apps/interactive_curve_view_controller.h index 676439119..f01db42bd 100644 --- a/apps/interactive_curve_view_controller.h +++ b/apps/interactive_curve_view_controller.h @@ -26,6 +26,7 @@ public: Responder * defaultController() override; protected: + constexpr static float k_numberOfCursorStepsInGradUnit = 5.0f; virtual BannerView * bannerView() = 0; virtual bool handleEnter() = 0; Responder * tabController() const; @@ -34,11 +35,10 @@ protected: virtual void initRangeParameters() = 0; virtual void initCursorParameters() = 0; /* the result of moveCursorVertically/Horizontally means: - * -1 -> the cursor cannot move in this direction - * 0 -> the cursor moved but the window has not changed - * 1-> the cursor moved and the window changed */ - virtual int moveCursorHorizontally(int direction) = 0; - virtual int moveCursorVertically(int direction) = 0; + * false -> the cursor cannot move in this direction + * true -> the cursor moved */ + virtual bool moveCursorHorizontally(int direction) = 0; + virtual bool moveCursorVertically(int direction) = 0; virtual uint32_t modelVersion() = 0; virtual uint32_t rangeVersion() = 0; virtual InteractiveCurveViewRange * interactiveCurveViewRange() = 0; diff --git a/apps/interactive_curve_view_range.cpp b/apps/interactive_curve_view_range.cpp index a11759eb4..8f84eef53 100644 --- a/apps/interactive_curve_view_range.cpp +++ b/apps/interactive_curve_view_range.cpp @@ -5,13 +5,8 @@ #include InteractiveCurveViewRange::InteractiveCurveViewRange(CurveViewCursor * cursor, InteractiveCurveViewRangeDelegate * delegate) : - m_xMin(-10.0f), - m_xMax(10.0f), - m_yMin(-10.0f), - m_yMax(10.0f), + MemoizedCurveViewRange(), m_yAuto(true), - m_xGridUnit(2.0f), - m_yGridUnit(2.0f), m_cursor(cursor), m_delegate(delegate) { @@ -28,70 +23,22 @@ uint32_t InteractiveCurveViewRange::rangeChecksum() { return Ion::crc32((uint32_t *)data, dataLengthInBytes>>2); } -float InteractiveCurveViewRange::xMin() { - return m_xMin; -} - -float InteractiveCurveViewRange::xMax() { - return m_xMax; -} - -float InteractiveCurveViewRange::yMin() { - return m_yMin; -} - -float InteractiveCurveViewRange::yMax() { - return m_yMax; -} - bool InteractiveCurveViewRange::yAuto() { return m_yAuto; } -float InteractiveCurveViewRange::xGridUnit() { - return m_xGridUnit; -} - -float InteractiveCurveViewRange::yGridUnit() { - return m_yGridUnit; -} - void InteractiveCurveViewRange::setXMin(float xMin) { - m_xMin = xMin; - if (m_xMin >= m_xMax) { - m_xMax = xMin + 1.0f; - } + MemoizedCurveViewRange::setXMin(xMin); if (m_delegate) { m_delegate->didChangeRange(this); } - m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); } void InteractiveCurveViewRange::setXMax(float xMax) { - m_xMax = xMax; - if (m_xMin >= m_xMax) { - m_xMin = xMax - 1.0f; - } + MemoizedCurveViewRange::setXMax(xMax); if (m_delegate) { m_delegate->didChangeRange(this); } - m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); -} - -void InteractiveCurveViewRange::setYMin(float yMin) { - m_yMin = yMin; - if (m_yMin >= m_yMax) { - m_yMax = yMin + 1.0f; - } - m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); -} - -void InteractiveCurveViewRange::setYMax(float yMax) { - m_yMax = yMax; - if (m_yMin >= m_yMax) { - m_yMin = yMax - 1.0f; - } - m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); } void InteractiveCurveViewRange::setYAuto(bool yAuto) { @@ -183,48 +130,31 @@ void InteractiveCurveViewRange::centerAxisAround(Axis axis, float position) { } } -bool InteractiveCurveViewRange::moveCursorTo(float x, float y) { - if (m_cursor) { - m_cursor->moveTo(x, y); - float xMargin = k_cursorMarginFactorToBorder * (m_xMax - m_xMin); - float yMargin = k_cursorMarginFactorToBorder * (m_yMax - m_yMin); - bool windowHasMoved = panToMakePointVisible(x, y, xMargin, yMargin); - return windowHasMoved; - } - return false; -} - -bool InteractiveCurveViewRange::panToMakePointVisible(float x, float y, float xMargin, float yMargin) { - bool windowMoved = false; +void InteractiveCurveViewRange::panToMakePointVisible(float x, float y, float topMarginRatio, float rightMarginRatio, float bottomMarginRation, float leftMarginRation) { float xRange = m_xMax - m_xMin; float yRange = m_yMax - m_yMin; - if (x < m_xMin + xMargin) { - m_xMin = x - xMargin; + if (x < m_xMin + leftMarginRation*xRange) { + m_xMin = x - leftMarginRation*xRange; m_xMax = m_xMin + xRange; m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); m_yAuto = false; - windowMoved = true; } - if (x > m_xMax - xMargin) { - m_xMax = x + xMargin; + if (x > m_xMax - rightMarginRatio*xRange) { + m_xMax = x + rightMarginRatio*xRange; m_xMin = m_xMax - xRange; m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); m_yAuto = false; - windowMoved = true; } - if (y < m_yMin + yMargin) { - m_yMin = y - yMargin; + if (y < m_yMin + bottomMarginRation*yRange) { + m_yMin = y - bottomMarginRation*yRange; m_yMax = m_yMin + yRange; m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); m_yAuto = false; - windowMoved = true; } - if (y > m_yMax - yMargin) { - m_yMax = y + yMargin; + if (y > m_yMax - topMarginRatio*yRange) { + m_yMax = y + topMarginRatio*yRange; m_yMin = m_yMax - yRange; m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); m_yAuto = false; - windowMoved = true; } - return windowMoved; } diff --git a/apps/interactive_curve_view_range.h b/apps/interactive_curve_view_range.h index 42072b3ad..3fd49b074 100644 --- a/apps/interactive_curve_view_range.h +++ b/apps/interactive_curve_view_range.h @@ -2,28 +2,20 @@ #define APPS_INTERACTIVE_CURVE_VIEW_RANGE_H #include -#include "curve_view_range.h" +#include "memoized_curve_view_range.h" #include "curve_view_cursor.h" #include "interactive_curve_view_range_delegate.h" -class InteractiveCurveViewRange : public CurveViewRange { +class InteractiveCurveViewRange : public MemoizedCurveViewRange { public: InteractiveCurveViewRange(CurveViewCursor * cursor, InteractiveCurveViewRangeDelegate * delegate); void setCursor(CurveViewCursor * cursor); - uint32_t rangeChecksum(); + uint32_t rangeChecksum() override; //CurveViewWindow - float xMin() override; - float xMax() override; - float yMin() override; - float yMax() override; bool yAuto(); - float xGridUnit() override; - float yGridUnit() override; - void setXMin(float f); - void setXMax(float f); - void setYMin(float f); - void setYMax(float f); + void setXMin(float f) override; + void setXMax(float f) override; void setYAuto(bool yAuto); // Window @@ -34,23 +26,10 @@ public: void setTrigonometric(); virtual void setDefault(); void centerAxisAround(Axis axis, float position); - - //Cursor - // moveCursorTo returns true if the window has moved - bool moveCursorTo(float x, float y); - constexpr static float k_numberOfCursorStepsInGradUnit = 5.0f; + void panToMakePointVisible(float x, float y, float topMarginRatio, float rightMarginRatio, float bottomMarginRation, float leftMarginRation); protected: - constexpr static float k_cursorMarginFactorToBorder = 0.025f; - // Window bounds of the data - float m_xMin; - float m_xMax; - float m_yMin; - float m_yMax; bool m_yAuto; - float m_xGridUnit; - float m_yGridUnit; private: - bool panToMakePointVisible(float x, float y, float xMargin, float yMargin); CurveViewCursor * m_cursor; InteractiveCurveViewRangeDelegate * m_delegate; }; diff --git a/apps/memoized_curve_view_range.cpp b/apps/memoized_curve_view_range.cpp new file mode 100644 index 000000000..e201e59bd --- /dev/null +++ b/apps/memoized_curve_view_range.cpp @@ -0,0 +1,70 @@ +#include "memoized_curve_view_range.h" +#include +#include +#include + +MemoizedCurveViewRange::MemoizedCurveViewRange() : + m_xMin(-10.0f), + m_xMax(10.0f), + m_yMin(-10.0f), + m_yMax(10.0f), + m_xGridUnit(2.0f), + m_yGridUnit(2.0f) +{ +} + +float MemoizedCurveViewRange::xMin() { + return m_xMin; +} + +float MemoizedCurveViewRange::xMax() { + return m_xMax; +} + +float MemoizedCurveViewRange::yMin() { + return m_yMin; +} + +float MemoizedCurveViewRange::yMax() { + return m_yMax; +} + +float MemoizedCurveViewRange::xGridUnit() { + return m_xGridUnit; +} + +float MemoizedCurveViewRange::yGridUnit() { + return m_yGridUnit; +} + +void MemoizedCurveViewRange::setXMin(float xMin) { + m_xMin = xMin; + if (m_xMin >= m_xMax) { + m_xMax = xMin + 1.0f; + } + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); +} + +void MemoizedCurveViewRange::setXMax(float xMax) { + m_xMax = xMax; + if (m_xMin >= m_xMax) { + m_xMin = xMax - 1.0f; + } + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); +} + +void MemoizedCurveViewRange::setYMin(float yMin) { + m_yMin = yMin; + if (m_yMin >= m_yMax) { + m_yMax = yMin + 1.0f; + } + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); +} + +void MemoizedCurveViewRange::setYMax(float yMax) { + m_yMax = yMax; + if (m_yMin >= m_yMax) { + m_yMin = yMax - 1.0f; + } + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); +} diff --git a/apps/memoized_curve_view_range.h b/apps/memoized_curve_view_range.h new file mode 100644 index 000000000..fff68b2fd --- /dev/null +++ b/apps/memoized_curve_view_range.h @@ -0,0 +1,32 @@ +#ifndef APPS_MEMOIZED_CURVE_VIEW_RANGE_H +#define APPS_MEMOIZED_CURVE_VIEW_RANGE_H + +#include "curve_view_range.h" + + +class MemoizedCurveViewRange : public CurveViewRange { +public: + MemoizedCurveViewRange(); + //CurveViewWindow + float xMin() override; + float xMax() override; + float yMin() override; + float yMax() override; + float xGridUnit() override; + float yGridUnit() override; + virtual void setXMin(float f); + virtual void setXMax(float f); + void setYMin(float f); + void setYMax(float f); + +protected: + // Window bounds of the data + float m_xMin; + float m_xMax; + float m_yMin; + float m_yMax; + float m_xGridUnit; + float m_yGridUnit; +}; + +#endif diff --git a/apps/probability/law/binomial_law.cpp b/apps/probability/law/binomial_law.cpp index 26388de0a..54aefae5d 100644 --- a/apps/probability/law/binomial_law.cpp +++ b/apps/probability/law/binomial_law.cpp @@ -40,28 +40,31 @@ const char * BinomialLaw::parameterDefinitionAtIndex(int index) { } float BinomialLaw::xMin() { - return floorf(m_parameter1*m_parameter2-5.0f*sqrtf(m_parameter1*m_parameter2*(1-m_parameter2))); + float min = floorf(m_parameter1*m_parameter2-5.0f*sqrtf(m_parameter1*m_parameter2*(1-m_parameter2))); + float max = xMax(); + return min - k_displayLeftMarginRatio * (max - min); } float BinomialLaw::xMax() { - float result = ceilf(m_parameter1*m_parameter2+5.0f*sqrtf(m_parameter1*m_parameter2*(1-m_parameter2))); - if (result <= xMin()) { - result = xMin() + 1.0f; + float min = floorf(m_parameter1*m_parameter2-5.0f*sqrtf(m_parameter1*m_parameter2*(1-m_parameter2))); + float max = ceilf(m_parameter1*m_parameter2+5.0f*sqrtf(m_parameter1*m_parameter2*(1-m_parameter2))); + if (max <= min) { + max = min + 1.0f; } - return result; + return max + k_displayRightMarginRatio*(max - min); } float BinomialLaw::yMin() { - return 0.0f; + return -k_displayBottomMarginRatio*yMax(); } float BinomialLaw::yMax() { int maxAbscissa = m_parameter2 < 1.0f ? (m_parameter1+1)*m_parameter2 : m_parameter1; float result = evaluateAtAbscissa(maxAbscissa); - if (result <= yMin() || isnan(result)) { - result = yMin() + 1.0f; + if (result <= 0.0f || isnan(result)) { + result = 1.0f; } - return result; + return result*(1.0f+ k_displayTopMarginRatio); } float BinomialLaw::evaluateAtAbscissa(float x) const { diff --git a/apps/probability/law/exponential_law.cpp b/apps/probability/law/exponential_law.cpp index f9eb19096..1b79a8e0e 100644 --- a/apps/probability/law/exponential_law.cpp +++ b/apps/probability/law/exponential_law.cpp @@ -33,28 +33,32 @@ const char * ExponentialLaw::parameterDefinitionAtIndex(int index) { } float ExponentialLaw::xMin() { - return 0.0f; + float max = xMax(); + return - k_displayLeftMarginRatio * max; } float ExponentialLaw::xMax() { assert(m_parameter1 != 0.0f); float result = 5.0f/m_parameter1; - if (result <= xMin()) { - result = xMin() + 1.0f; + if (result <= 0.0f) { + result = 1.0f; } - return result; + return result*(1.0f+ k_displayRightMarginRatio); } float ExponentialLaw::yMin() { - return 0.0f; + return -k_displayBottomMarginRatio*yMax(); } float ExponentialLaw::yMax() { float result = m_parameter1; - if (result <= yMin()) { - result = yMin() + 1.0f; + if (result <= 0.0f || isnan(result)) { + result = 1.0f; } - return result; + if (result <= 0.0f) { + result = 1.0f; + } + return result*(1.0f+ k_displayTopMarginRatio); } float ExponentialLaw::evaluateAtAbscissa(float x) const { diff --git a/apps/probability/law/law.h b/apps/probability/law/law.h index 3449537c3..b1efbd4e1 100644 --- a/apps/probability/law/law.h +++ b/apps/probability/law/law.h @@ -34,6 +34,10 @@ public: virtual float rightIntegralInverseForProbability(float * probability); protected: constexpr static int k_maxNumberOfOperations = 1000000; + constexpr static float k_displayTopMarginRatio = 0.05f; + constexpr static float k_displayBottomMarginRatio = 0.2f; + constexpr static float k_displayLeftMarginRatio = 0.05f; + constexpr static float k_displayRightMarginRatio = 0.05f; }; } diff --git a/apps/probability/law/normal_law.cpp b/apps/probability/law/normal_law.cpp index 554ab8cc5..4597ce710 100644 --- a/apps/probability/law/normal_law.cpp +++ b/apps/probability/law/normal_law.cpp @@ -55,16 +55,16 @@ float NormalLaw::xMax() { } float NormalLaw::yMin() { - return 0.0f; + return -k_displayBottomMarginRatio*yMax(); } float NormalLaw::yMax() { float maxAbscissa = m_parameter1; float result = evaluateAtAbscissa(maxAbscissa); - if (isnan(result) || result == yMin()) { - result = yMin() + 1.0f; + if (isnan(result) || result <= 0.0f) { + result = 1.0f; } - return result; + return result*(1.0f+ k_displayTopMarginRatio); } float NormalLaw::evaluateAtAbscissa(float x) const { diff --git a/apps/probability/law/poisson_law.cpp b/apps/probability/law/poisson_law.cpp index fa611a1b9..6bd772702 100644 --- a/apps/probability/law/poisson_law.cpp +++ b/apps/probability/law/poisson_law.cpp @@ -32,26 +32,26 @@ const char * PoissonLaw::parameterDefinitionAtIndex(int index) { } float PoissonLaw::xMin() { - return 0.0f; + return -k_displayLeftMarginRatio*xMax(); } float PoissonLaw::xMax() { assert(m_parameter1 != 0); - return m_parameter1 + 5.0f*sqrtf(m_parameter1); + return (m_parameter1 + 5.0f*sqrtf(m_parameter1))*(1.0f+k_displayRightMarginRatio); } float PoissonLaw::yMin() { - return 0.0f; + return - k_displayBottomMarginRatio * yMax(); } float PoissonLaw::yMax() { int maxAbscissa = (int)m_parameter1; assert(maxAbscissa >= 0.0f); float result = evaluateAtAbscissa(maxAbscissa); - if (result <= yMin()) { - result = yMin() + 1.0f; + if (result <= 0.0f) { + result = 1.0f; } - return result; + return result*(1.0f+ k_displayTopMarginRatio); } float PoissonLaw::evaluateAtAbscissa(float x) const { diff --git a/apps/probability/law/uniform_law.cpp b/apps/probability/law/uniform_law.cpp index 88b6a2e09..e678e581e 100644 --- a/apps/probability/law/uniform_law.cpp +++ b/apps/probability/law/uniform_law.cpp @@ -54,15 +54,15 @@ float UniformLaw::xMax() { } float UniformLaw::yMin() { - return 0.0f; + return -k_displayBottomMarginRatio*yMax(); } float UniformLaw::yMax() { float result = m_parameter1 == m_parameter2 ? k_diracMaximum : 1.0f/(m_parameter2-m_parameter1); - if (result <= yMin()) { - result = yMin() + 1.0f; + if (result <= 0.0f) { + result = 1.0f; } - return result; + return result*(1.0f+ k_displayTopMarginRatio); } float UniformLaw::evaluateAtAbscissa(float t) const { diff --git a/apps/probability/law_curve_view.cpp b/apps/probability/law_curve_view.cpp index 618efe00a..bf199e452 100644 --- a/apps/probability/law_curve_view.cpp +++ b/apps/probability/law_curve_view.cpp @@ -5,7 +5,7 @@ namespace Probability { LawCurveView::LawCurveView() : - CurveView(nullptr, nullptr, nullptr, nullptr, 0.2f, 0.1f, 0.2f, 0.1f), + CurveView(nullptr, nullptr, nullptr, nullptr), m_law(nullptr), m_calculation(nullptr) { diff --git a/apps/regression/go_to_parameter_controller.cpp b/apps/regression/go_to_parameter_controller.cpp index f606fa70c..a4b367d75 100644 --- a/apps/regression/go_to_parameter_controller.cpp +++ b/apps/regression/go_to_parameter_controller.cpp @@ -37,12 +37,12 @@ void GoToParameterController::setParameterAtIndex(int parameterIndex, float f) { float y = m_store->yValueForXValue(f); m_store->centerAxisAround(CurveViewRange::Axis::X, f); m_store->centerAxisAround(CurveViewRange::Axis::Y, y); - m_store->moveCursorTo(f, y); + m_cursor->moveTo(f, y); } else { float x = m_store->xValueForYValue(f); m_store->centerAxisAround(CurveViewRange::Axis::X, x); m_store->centerAxisAround(CurveViewRange::Axis::Y, f); - m_store->moveCursorTo(x, f); + m_cursor->moveTo(x, f); } } diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index d18a3f4d2..646921774 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -91,8 +91,6 @@ void GraphController::reloadBannerView() { strlcpy(buffer, legend, legendLength+1); Float(y).convertFloatToText(buffer+legendLength, Float::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); m_bannerView.setLegendAtIndex(buffer, 4); - - m_bannerView.layoutSubviews(); } void GraphController::initRangeParameters() { @@ -102,50 +100,62 @@ void GraphController::initRangeParameters() { void GraphController::initCursorParameters() { float x = (m_store->xMin() + m_store->xMax())/2.0f; float y = m_store->yValueForXValue(x); - m_store->moveCursorTo(x, y); + m_cursor.moveTo(x, y); + m_store->panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); m_selectedDotIndex = -1; } -int GraphController::moveCursorHorizontally(int direction) { +bool GraphController::moveCursorHorizontally(int direction) { if (m_selectedDotIndex >= 0) { int dotSelected = m_store->nextDot(direction, m_selectedDotIndex); if (dotSelected >= 0 && dotSelected < m_store->numberOfPairs()) { m_selectedDotIndex = dotSelected; - return m_store->moveCursorTo(m_store->get(0, m_selectedDotIndex), m_store->get(1, m_selectedDotIndex)); + m_cursor.moveTo(m_store->get(0, m_selectedDotIndex), m_store->get(1, m_selectedDotIndex)); + m_store->panToMakePointVisible(m_cursor.x(), m_cursor.y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); + return true; } if (dotSelected == m_store->numberOfPairs()) { m_selectedDotIndex = dotSelected; - return m_store->moveCursorTo(m_store->meanOfColumn(0), m_store->meanOfColumn(1)); + m_cursor.moveTo(m_store->meanOfColumn(0), m_store->meanOfColumn(1)); + m_store->panToMakePointVisible(m_cursor.x(), m_cursor.y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); + return true; } - return -1; - } else { - float x = direction > 0 ? m_cursor.x() + m_store->xGridUnit()/InteractiveCurveViewRange::k_numberOfCursorStepsInGradUnit : - m_cursor.x() - m_store->xGridUnit()/InteractiveCurveViewRange::k_numberOfCursorStepsInGradUnit; - float y = m_store->yValueForXValue(x); - return m_store->moveCursorTo(x, y); + return false; } + float x = direction > 0 ? m_cursor.x() + m_store->xGridUnit()/k_numberOfCursorStepsInGradUnit : + m_cursor.x() - m_store->xGridUnit()/k_numberOfCursorStepsInGradUnit; + float y = m_store->yValueForXValue(x); + m_cursor.moveTo(x, y); + m_store->panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); + return true; } -int GraphController::moveCursorVertically(int direction) { +bool GraphController::moveCursorVertically(int direction) { float yRegressionCurve = m_store->yValueForXValue(m_cursor.x()); if (m_selectedDotIndex >= 0) { if ((yRegressionCurve - m_cursor.y() > 0) == (direction > 0)) { m_selectedDotIndex = -1; - return m_store->moveCursorTo(m_cursor.x(), yRegressionCurve); + m_cursor.moveTo(m_cursor.x(), yRegressionCurve); + m_store->panToMakePointVisible(m_cursor.x(), m_cursor.y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); + return true; } else { - return -1; + return false; } } else { int dotSelected = m_store->closestVerticalDot(direction, m_cursor.x()); if (dotSelected >= 0 && dotSelected < m_store->numberOfPairs()) { m_selectedDotIndex = dotSelected; - return m_store->moveCursorTo(m_store->get(0, m_selectedDotIndex), m_store->get(1, m_selectedDotIndex)); + m_cursor.moveTo(m_store->get(0, m_selectedDotIndex), m_store->get(1, m_selectedDotIndex)); + m_store->panToMakePointVisible(m_cursor.x(), m_cursor.y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); + return true; } if (dotSelected == m_store->numberOfPairs()) { m_selectedDotIndex = dotSelected; - return m_store->moveCursorTo(m_store->meanOfColumn(0), m_store->meanOfColumn(1)); + m_cursor.moveTo(m_store->meanOfColumn(0), m_store->meanOfColumn(1)); + m_store->panToMakePointVisible(m_cursor.x(), m_cursor.y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); + return true; } - return -1; + return false; } } diff --git a/apps/regression/graph_controller.h b/apps/regression/graph_controller.h index 6b6594af0..5d41c62ae 100644 --- a/apps/regression/graph_controller.h +++ b/apps/regression/graph_controller.h @@ -19,6 +19,11 @@ public: bool isEmpty() override; const char * emptyMessage() override; private: + constexpr static float k_cursorTopMarginRatio = 0.025f; + constexpr static float k_cursorRightMarginRatio = 0.015f; + constexpr static float k_cursorBottomMarginRatio = 0.16f; + constexpr static float k_cursorLeftMarginRatio = 0.015f; + constexpr static int k_maxNumberOfCharacters = 8; BannerView * bannerView() override; CurveView * curveView() override; @@ -27,8 +32,8 @@ private: void reloadBannerView() override; void initRangeParameters() override; void initCursorParameters() override; - int moveCursorHorizontally(int direction) override; - int moveCursorVertically(int direction) override; + bool moveCursorHorizontally(int direction) override; + bool moveCursorVertically(int direction) override; uint32_t modelVersion() override; uint32_t rangeVersion() override; BannerView m_bannerView; diff --git a/apps/regression/graph_view.cpp b/apps/regression/graph_view.cpp index 970708ae2..e4c18d764 100644 --- a/apps/regression/graph_view.cpp +++ b/apps/regression/graph_view.cpp @@ -5,7 +5,7 @@ namespace Regression { GraphView::GraphView(Store * store, CurveViewCursor * cursor, ::BannerView * bannerView, View * cursorView) : - CurveView(store, cursor, bannerView, cursorView, 0.05f, 0.05f, 0.25f, 0.05f), + CurveView(store, cursor, bannerView, cursorView), m_store(store) { } diff --git a/apps/regression/initialisation_parameter_controller.cpp b/apps/regression/initialisation_parameter_controller.cpp index dc5a3d9a9..95c9b8892 100644 --- a/apps/regression/initialisation_parameter_controller.cpp +++ b/apps/regression/initialisation_parameter_controller.cpp @@ -1,4 +1,5 @@ #include "initialisation_parameter_controller.h" +#include "graph_controller.h" #include #include @@ -27,7 +28,8 @@ void InitialisationParameterController::didBecomeFirstResponder() { bool InitialisationParameterController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::OK) { - RangeMethodPointer rangeMethods[k_totalNumberOfCells] = {&InteractiveCurveViewRange::roundAbscissa, &InteractiveCurveViewRange::normalize, &InteractiveCurveViewRange::setDefault}; + RangeMethodPointer rangeMethods[k_totalNumberOfCells] = {&InteractiveCurveViewRange::roundAbscissa, + &InteractiveCurveViewRange::normalize, &InteractiveCurveViewRange::setDefault}; (m_store->*rangeMethods[m_selectableTableView.selectedRow()])(); StackViewController * stack = (StackViewController *)parentResponder(); stack->pop(); diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index 70962c52c..7f0220b99 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -143,13 +143,13 @@ void Store::setDefault() { float min = minValueOfColumn(0); float max = maxValueOfColumn(0); float range = max - min; - m_xMin = min - k_cursorMarginFactorToBorder*range; - m_xMax = max + k_cursorMarginFactorToBorder*range; + m_xMin = min - k_displayLeftMarginRatio*range; + m_xMax = max + k_displayRightMarginRatio*range; min = minValueOfColumn(1); max = maxValueOfColumn(1); range = max - min; - m_yMin = min - k_cursorMarginFactorToBorder*range; - m_yMax = max + k_cursorMarginFactorToBorder*range; + m_yMin = min - k_displayBottomMarginRatio*range; + m_yMax = max + k_displayTopMarginRatio*range; if (m_xMin == m_xMax) {; m_xMin = m_xMin - 1.0f; m_xMax = m_xMax + 1.0f; diff --git a/apps/regression/store.h b/apps/regression/store.h index b663e7c0b..699152551 100644 --- a/apps/regression/store.h +++ b/apps/regression/store.h @@ -37,6 +37,10 @@ public: float correlationCoefficient(); float squaredCorrelationCoefficient(); private: + constexpr static float k_displayTopMarginRatio = 0.03f; + constexpr static float k_displayRightMarginRatio = 0.02f; + constexpr static float k_displayBottomMarginRatio = 0.2f; + constexpr static float k_displayLeftMarginRatio = 0.02f; float maxValueOfColumn(int i); float minValueOfColumn(int i); }; diff --git a/apps/statistics/box_controller.cpp b/apps/statistics/box_controller.cpp index 19eabdbf2..789d6797f 100644 --- a/apps/statistics/box_controller.cpp +++ b/apps/statistics/box_controller.cpp @@ -69,7 +69,6 @@ void BoxController::reloadBannerView() { float calculation = (m_store->*calculationMethods[(int)m_view.selectedQuantile()])(); Float(calculation).convertFloatToText(buffer, Float::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); m_boxBannerView.setLegendAtIndex(buffer, 1); - m_boxBannerView.layoutSubviews(); } } diff --git a/apps/statistics/box_range.cpp b/apps/statistics/box_range.cpp index f0f9e33cf..be087e243 100644 --- a/apps/statistics/box_range.cpp +++ b/apps/statistics/box_range.cpp @@ -8,22 +8,25 @@ BoxRange::BoxRange(Store * store) : } float BoxRange::xMin() { - return m_store->minValue(); + float min = m_store->minValue(); + float max = m_store->maxValue(); + max = min >= max ? min + 1 : max; + return min - k_displayLeftMarginRatio*(max-min); } float BoxRange::xMax() { - if (m_store->minValue() >= m_store->maxValue()) { - return m_store->minValue() + 1.0f; - } - return m_store->maxValue(); + float min = m_store->minValue(); + float max = m_store->maxValue(); + max = min >= max ? min + 1 : max; + return max + k_displayRightMarginRatio*(max - min); } float BoxRange::yMin() { - return 0.0f; + return -k_displayBottomMarginRatio; } float BoxRange::yMax() { - return 1.0f; + return 1.0f+k_displayTopMarginRatio; } float BoxRange::xGridUnit() { diff --git a/apps/statistics/box_range.h b/apps/statistics/box_range.h index 118572d73..a85da2b3e 100644 --- a/apps/statistics/box_range.h +++ b/apps/statistics/box_range.h @@ -15,6 +15,10 @@ public: float yMax() override; float xGridUnit() override; private: + constexpr static float k_displayTopMarginRatio = 0.0f; + constexpr static float k_displayRightMarginRatio = 0.2f; + constexpr static float k_displayBottomMarginRatio = 0.4f; + constexpr static float k_displayLeftMarginRatio = 0.2f; Store * m_store; }; diff --git a/apps/statistics/box_view.cpp b/apps/statistics/box_view.cpp index 96ee62007..7b50a8d78 100644 --- a/apps/statistics/box_view.cpp +++ b/apps/statistics/box_view.cpp @@ -5,14 +5,15 @@ namespace Statistics { BoxView::BoxView(Store * store, ::BannerView * bannerView) : - CurveView(&m_boxRange, nullptr, bannerView, nullptr, 0.0f, 0.2f, 0.4f, 0.2f), + CurveView(&m_boxRange, nullptr, bannerView, nullptr), m_store(store), m_boxRange(BoxRange(store)), m_selectedQuantile(Quantile::Min) { } -void BoxView::reloadSelection() { +void BoxView::reload() { + CurveView::reload(); CalculPointer calculationMethods[5] = {&Store::minValue, &Store::firstQuartile, &Store::median, &Store::thirdQuartile, &Store::maxValue}; float calculation = (m_store->*calculationMethods[(int)m_selectedQuantile])(); @@ -32,9 +33,9 @@ bool BoxView::selectQuantile(int selectedQuantile) { return false; } if ((int)m_selectedQuantile != selectedQuantile) { - reloadSelection(); + reload(); m_selectedQuantile = (Quantile)selectedQuantile; - reloadSelection(); + reload(); } return true; } diff --git a/apps/statistics/box_view.h b/apps/statistics/box_view.h index 431aeb82f..42208ebc8 100644 --- a/apps/statistics/box_view.h +++ b/apps/statistics/box_view.h @@ -20,7 +20,7 @@ public: Max = 4 }; BoxView(Store * store, ::BannerView * bannerView); - void reloadSelection() override; + void reload() override; Quantile selectedQuantile(); bool selectQuantile(int selectedQuantile); void drawRect(KDContext * ctx, KDRect rect) const override; diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 7b5e26d30..b9bbf60d3 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -43,7 +43,7 @@ bool HistogramController::handleEvent(Ion::Events::Event event) { if (!m_view.isMainViewSelected()) { headerViewController()->setSelectedButton(-1); m_view.selectMainView(true); - m_view.reloadSelection(); + m_view.reload(); reloadBannerView(); return true; } @@ -146,7 +146,6 @@ void HistogramController::reloadBannerView() { float frequency = size/m_store->sumOfColumn(1); Float(frequency).convertFloatToText(buffer+legendLength, Float::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); m_bannerView.setLegendAtIndex(buffer, 2); - m_bannerView.layoutSubviews(); } bool HistogramController::moveSelection(int deltaIndex) { @@ -182,9 +181,8 @@ void HistogramController::initRangeParameters() { if (xMin >= xMax) { xMax = xMin + 10.0f*barWidth; } - m_store->setXMin(xMin); - m_store->setXMax(xMax); - m_store->setYMin(0.0f); + m_store->setXMin(xMin - Store::k_displayLeftMarginRatio*(xMax-xMin)); + m_store->setXMax(xMax + Store::k_displayRightMarginRatio*(xMax-xMin)); float yMax = -FLT_MAX; for (int index = 0; index < m_store->numberOfBars(); index++) { float size = m_store->heightOfBarAtIndex(index); @@ -193,7 +191,8 @@ void HistogramController::initRangeParameters() { } } yMax = yMax/m_store->sumOfColumn(1); - m_store->setYMax(yMax); + m_store->setYMin(-Store::k_displayBottomMarginRatio*yMax); + m_store->setYMax(yMax*(1.0f+Store::k_displayTopMarginRatio)); } void HistogramController::initBarParameters() { diff --git a/apps/statistics/histogram_view.cpp b/apps/statistics/histogram_view.cpp index f5b78cd6e..68d1069b2 100644 --- a/apps/statistics/histogram_view.cpp +++ b/apps/statistics/histogram_view.cpp @@ -5,14 +5,15 @@ namespace Statistics { HistogramView::HistogramView(Store * store, ::BannerView * bannerView) : - CurveView(store, nullptr, bannerView, nullptr, 0.2f, 0.05f, 0.4f, 0.05f), + CurveView(store, nullptr, bannerView, nullptr), m_store(store), m_highlightedBarStart(NAN), m_highlightedBarEnd(NAN) { } -void HistogramView::reloadSelection() { +void HistogramView::reload() { + CurveView::reload(); float pixelLowerBound = floatToPixel(Axis::Horizontal, m_highlightedBarStart)-2; float pixelUpperBound = floatToPixel(Axis::Horizontal, m_highlightedBarEnd)+2; KDRect dirtyZone(KDRect(pixelLowerBound, 0, pixelUpperBound-pixelLowerBound, @@ -34,10 +35,10 @@ void HistogramView::drawRect(KDContext * ctx, KDRect rect) const { void HistogramView::setHighlight(float start, float end) { if (m_highlightedBarStart != start || m_highlightedBarEnd != end) { - reloadSelection(); + reload(); m_highlightedBarStart = start; m_highlightedBarEnd = end; - reloadSelection(); + reload(); } } diff --git a/apps/statistics/histogram_view.h b/apps/statistics/histogram_view.h index e6aedca20..0046e36e9 100644 --- a/apps/statistics/histogram_view.h +++ b/apps/statistics/histogram_view.h @@ -11,7 +11,7 @@ namespace Statistics { class HistogramView : public CurveView { public: HistogramView(Store * store, ::BannerView * bannerView); - void reloadSelection() override; + void reload() override; void drawRect(KDContext * ctx, KDRect rect) const override; void setHighlight(float start, float end); private: diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index 1ffe6d5cf..fab30ffb9 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -8,7 +8,7 @@ namespace Statistics { Store::Store() : - InteractiveCurveViewRange(nullptr, nullptr), + MemoizedCurveViewRange(), FloatPairStore(), m_barWidth(1.0f), m_firstDrawnBarAbscissa(0.0f) @@ -72,14 +72,14 @@ int Store::numberOfBars() { bool Store::scrollToSelectedBarIndex(int index) { float startSelectedBar = startOfBarAtIndex(index); float range = m_xMax - m_xMin; - if (m_xMin > startSelectedBar) { - m_xMin = startSelectedBar; + if (m_xMin + k_displayLeftMarginRatio*range > startSelectedBar) { + m_xMin = startSelectedBar - k_displayLeftMarginRatio*range; m_xMax = m_xMin + range; return true; } float endSelectedBar = endOfBarAtIndex(index); - if (endSelectedBar > m_xMax) { - m_xMax = endSelectedBar; + if (endSelectedBar > m_xMax - k_displayRightMarginRatio*range) { + m_xMax = endSelectedBar + k_displayRightMarginRatio*range; m_xMin = m_xMax - range; return true; } diff --git a/apps/statistics/store.h b/apps/statistics/store.h index 28e851f3a..aacb8c3d2 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -1,12 +1,12 @@ #ifndef STATISTICS_STORE_H #define STATISTICS_STORE_H -#include "../interactive_curve_view_range.h" +#include "../memoized_curve_view_range.h" #include "../float_pair_store.h" namespace Statistics { -class Store : public InteractiveCurveViewRange, public FloatPairStore { +class Store : public MemoizedCurveViewRange, public FloatPairStore { public: Store(); uint32_t barChecksum(); @@ -37,6 +37,10 @@ public: float median(); float sum(); float squaredValueSum(); + constexpr static float k_displayTopMarginRatio = 0.03f; + constexpr static float k_displayRightMarginRatio = 0.02f; + constexpr static float k_displayBottomMarginRatio = 0.3f; + constexpr static float k_displayLeftMarginRatio = 0.02f; private: float defaultValue(int i) override; float sumOfValuesBetween(float x1, float x2);