From b8eba2c42aa35a779dbac2325cb0d5ec533ac2ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 28 Dec 2016 13:27:03 +0100 Subject: [PATCH] [apps/graph/graph] Make the cursor position belong to th emodel "GraphWindow" Change-Id: I8cdcda85f0e29e4b678829f39b8bd212cd8d1086 --- apps/graph/graph/banner_view.cpp | 54 ++--- apps/graph/graph/banner_view.h | 13 +- .../graph/curve_parameter_controller.cpp | 12 +- apps/graph/graph/curve_parameter_controller.h | 2 +- .../graph/graph/goto_parameter_controller.cpp | 8 +- apps/graph/graph/goto_parameter_controller.h | 4 +- apps/graph/graph/graph_controller.cpp | 55 ++--- apps/graph/graph/graph_view.cpp | 202 +++++------------- apps/graph/graph/graph_view.h | 17 +- apps/graph/graph/graph_window.cpp | 105 +++++++-- apps/graph/graph/graph_window.h | 19 +- .../initialisation_parameter_controller.cpp | 7 +- .../initialisation_parameter_controller.h | 3 +- .../graph/window_parameter_controller.cpp | 7 +- .../graph/graph/window_parameter_controller.h | 3 +- .../graph/graph/zoom_parameter_controller.cpp | 12 +- 16 files changed, 252 insertions(+), 271 deletions(-) diff --git a/apps/graph/graph/banner_view.cpp b/apps/graph/graph/banner_view.cpp index 306b9a2a3..727860523 100644 --- a/apps/graph/graph/banner_view.cpp +++ b/apps/graph/graph/banner_view.cpp @@ -1,52 +1,42 @@ #include "banner_view.h" #include +#include #include "../../constant.h" namespace Graph { -BannerView::BannerView() : - m_abscissa(0.0f), - m_function(nullptr), +BannerView::BannerView(GraphWindow * graphWindow) : m_abscissaView(0.0f, 0.5f), m_functionView(0.5f, 0.5f), m_derivativeView(1.0f, 0.5f), m_displayDerivative(false), - m_context(nullptr) + m_graphWindow(graphWindow) { } +void BannerView::reload() { + markRectAsDirty(bounds()); + char buffer[k_maxNumberOfCharacters+Constant::FloatBufferSizeInScientificMode] = {'x', ' ', '=', ' ',0}; + Float(m_graphWindow->xCursorPosition()).convertFloatToText(buffer+4, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode); + m_abscissaView.setText(buffer); + + strlcpy(buffer, "00(x) = ", k_maxNumberOfCharacters+1); + buffer[1] = m_graphWindow->functionSelectedByCursor()->name()[0]; + Float(m_graphWindow->yCursorPosition()).convertFloatToText(buffer+8, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode); + m_functionView.setText(buffer+1); + + if (m_displayDerivative) { + buffer[0] = m_graphWindow->functionSelectedByCursor()->name()[0]; + buffer[1] = '\''; + Float(m_graphWindow->derivativeAtCursorPosition()).convertFloatToText(buffer+8, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaForDerivativeNumberInScientificMode); + m_derivativeView.setText(buffer); + } +} + void BannerView::drawRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(bounds(), KDColorWhite); } -void BannerView::setContext(Context * context) { - m_context = context; -} - -void BannerView::setAbscissa(float x) { - markRectAsDirty(bounds()); - m_abscissa = x; - char buffer[4+Constant::FloatBufferSizeInScientificMode] = {'x', ' ', '=', ' ',0}; - Float(x).convertFloatToText(buffer+4, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode); - m_abscissaView.setText(buffer); -} - -void BannerView::setFunction(Function * f) { - markRectAsDirty(bounds()); - m_function = f; - float y = f->evaluateAtAbscissa(m_abscissa, m_context); - char buffer[8+Constant::FloatBufferSizeInScientificMode] = {0, 0, '(', 'x', ')', ' ', '=', ' ',0}; - buffer[1] = f->name()[0]; - Float(y).convertFloatToText(buffer+8, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode); - m_functionView.setText(buffer+1); - - y = f->approximateDerivative(m_abscissa, m_context); - buffer[0] = f->name()[0]; - buffer[1] = '\''; - Float(y).convertFloatToText(buffer+8, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaForDerivativeNumberInScientificMode); - m_derivativeView.setText(buffer); -} - void BannerView::setDisplayDerivative(bool displayDerivative) { m_displayDerivative = displayDerivative; } diff --git a/apps/graph/graph/banner_view.h b/apps/graph/graph/banner_view.h index e3770319b..c33e2c10b 100644 --- a/apps/graph/graph/banner_view.h +++ b/apps/graph/graph/banner_view.h @@ -2,30 +2,27 @@ #define GRAPH_BANNER_VIEW_H #include -#include "../function.h" +#include "graph_window.h" namespace Graph { class BannerView : public View { public: - BannerView(); + BannerView(GraphWindow * graphWindow); + void reload(); void drawRect(KDContext * ctx, KDRect rect) const override; - void setContext(Context * context); - void setAbscissa(float x); - void setFunction(Function * f); void setDisplayDerivative(bool displayDerivative); bool displayDerivative(); private: + constexpr static int k_maxNumberOfCharacters = 8; int numberOfSubviews() const override; View * subviewAtIndex(int index) override; void layoutSubviews() override; - float m_abscissa; - Function * m_function; BufferTextView m_abscissaView; BufferTextView m_functionView; BufferTextView m_derivativeView; bool m_displayDerivative; - Context * m_context; + GraphWindow * m_graphWindow; }; } diff --git a/apps/graph/graph/curve_parameter_controller.cpp b/apps/graph/graph/curve_parameter_controller.cpp index 49bd845e0..c11515ebb 100644 --- a/apps/graph/graph/curve_parameter_controller.cpp +++ b/apps/graph/graph/curve_parameter_controller.cpp @@ -3,7 +3,7 @@ namespace Graph { -CurveParameterController::CurveParameterController(GraphView * graphView) : +CurveParameterController::CurveParameterController(GraphView * graphView, GraphWindow * graphWindow) : ViewController(nullptr), m_graphView(graphView), m_function(nullptr), @@ -12,7 +12,7 @@ CurveParameterController::CurveParameterController(GraphView * graphView) : m_derivativeCell(SwitchMenuListCell((char*)"Nombre derivee")), m_selectableTableView(SelectableTableView(this, this, Metric::TopMargin, Metric::RightMargin, Metric::BottomMargin, Metric::LeftMargin)), - m_goToParameterController(GoToParameterController(this, m_graphView)) + m_goToParameterController(GoToParameterController(this, graphWindow)) { } @@ -32,7 +32,8 @@ void CurveParameterController::didBecomeFirstResponder() { void CurveParameterController::willDisplayCellForIndex(TableViewCell * cell, int index) { if (cell == &m_derivativeCell) { SwitchView * switchView = (SwitchView *)m_derivativeCell.accessoryView(); - switchView->setState(m_graphView->bannerView()->displayDerivative()); + BannerView * bannerView = (BannerView *)m_graphView->bannerView(); + switchView->setState(bannerView->displayDerivative()); } } @@ -49,9 +50,12 @@ bool CurveParameterController::handleEvent(Ion::Events::Event event) { return true; } case 2: - m_graphView->bannerView()->setDisplayDerivative(!m_graphView->bannerView()->displayDerivative()); + { + BannerView * bannerView = (BannerView *)m_graphView->bannerView(); + bannerView->setDisplayDerivative(!bannerView->displayDerivative()); m_selectableTableView.reloadData(); return true; + } default: return false; } diff --git a/apps/graph/graph/curve_parameter_controller.h b/apps/graph/graph/curve_parameter_controller.h index e89608968..a6c6bb8f5 100644 --- a/apps/graph/graph/curve_parameter_controller.h +++ b/apps/graph/graph/curve_parameter_controller.h @@ -9,7 +9,7 @@ namespace Graph { class CurveParameterController : public ViewController, public SimpleListViewDataSource { public: - CurveParameterController(GraphView * graphView); + CurveParameterController(GraphView * graphView, GraphWindow * graphWindow); View * view() override; const char * title() const override; diff --git a/apps/graph/graph/goto_parameter_controller.cpp b/apps/graph/graph/goto_parameter_controller.cpp index cf38a6931..d1392d88e 100644 --- a/apps/graph/graph/goto_parameter_controller.cpp +++ b/apps/graph/graph/goto_parameter_controller.cpp @@ -4,10 +4,10 @@ namespace Graph { -GoToParameterController::GoToParameterController(Responder * parentResponder, GraphView * graphView) : +GoToParameterController::GoToParameterController(Responder * parentResponder, GraphWindow * graphWindow) : FloatParameterController(parentResponder), m_abscisseCell(EditableTextMenuListCell(&m_selectableTableView, this, m_draftTextBuffer, (char*)"x")), - m_graphView(graphView), + m_graphWindow(graphWindow), m_function(nullptr) { } @@ -23,12 +23,12 @@ const char * GoToParameterController::title() const { float GoToParameterController::parameterAtIndex(int index) { assert(index == 0); - return m_graphView->xCursorPosition(); + return m_graphWindow->xCursorPosition(); } void GoToParameterController::setParameterAtIndex(int parameterIndex, float f) { assert(parameterIndex == 0); - m_graphView->goToAbscissaOnFunction(f, m_function); + m_graphWindow->setCursorPositionAtAbscissaWithFunction(f, m_function); } int GoToParameterController::numberOfRows() { diff --git a/apps/graph/graph/goto_parameter_controller.h b/apps/graph/graph/goto_parameter_controller.h index 1872e5b1a..a62bb99ea 100644 --- a/apps/graph/graph/goto_parameter_controller.h +++ b/apps/graph/graph/goto_parameter_controller.h @@ -8,7 +8,7 @@ namespace Graph { class GoToParameterController : public FloatParameterController { public: - GoToParameterController(Responder * parentResponder, GraphView * graphView); + GoToParameterController(Responder * parentResponder, GraphWindow * graphWindow); ExpressionTextFieldDelegate * textFieldDelegate() override; const char * title() const override; int numberOfRows() override; @@ -20,7 +20,7 @@ private: void setParameterAtIndex(int parameterIndex, float f) override; char m_draftTextBuffer[EditableTextMenuListCell::k_bufferLength]; EditableTextMenuListCell m_abscisseCell; - GraphView * m_graphView; + GraphWindow * m_graphWindow; Function * m_function; }; diff --git a/apps/graph/graph/graph_controller.cpp b/apps/graph/graph/graph_controller.cpp index a6e930b16..d4db9ead1 100644 --- a/apps/graph/graph/graph_controller.cpp +++ b/apps/graph/graph/graph_controller.cpp @@ -10,10 +10,10 @@ GraphController::GraphController(Responder * parentResponder, FunctionStore * fu HeaderViewDelegate(header), m_view(GraphView(functionStore, &m_graphWindow)), m_graphWindow(functionStore), - m_windowParameterController(WindowParameterController(this, &m_graphWindow, &m_view)), + m_windowParameterController(WindowParameterController(this, &m_graphWindow)), m_zoomParameterController(ZoomParameterController(this, &m_graphWindow, &m_view)), - m_initialisationParameterController(InitialisationParameterController(this, &m_graphWindow, &m_view)), - m_curveParameterController(CurveParameterController(&m_view)), + m_initialisationParameterController(InitialisationParameterController(this, &m_graphWindow)), + m_curveParameterController(CurveParameterController(&m_view, &m_graphWindow)), m_windowButton(this, "Axes", Invocation([](void * context, void * sender) { GraphController * graphController = (GraphController *) context; StackViewController * stack = graphController->stackController(); @@ -34,8 +34,8 @@ GraphController::GraphController(Responder * parentResponder, FunctionStore * fu } View * GraphController::view() { - if (m_view.xPixelCursorPosition() < 0.0f && !isEmpty()) { - m_view.initCursorPosition(); + if (isnan(m_graphWindow.xCursorPosition()) && !isEmpty()) { + m_graphWindow.initCursorPosition(); } return &m_view; } @@ -111,7 +111,7 @@ void GraphController::didBecomeFirstResponder() { } // if new functions were added to the store, the window parameters need to be refresh if (m_graphWindow.computeYaxes()) { - m_view.initCursorPosition(); + m_graphWindow.initCursorPosition(); } headerViewController()->setSelectedButton(-1); m_headerSelected = false; @@ -139,43 +139,48 @@ bool GraphController::handleEvent(Ion::Events::Event event) { } else { if (event == Ion::Events::Plus) { m_graphWindow.zoom(1.0f/3.0f); - m_view.initCursorPosition(); + m_graphWindow.initCursorPosition(); m_view.reload(); return true; } if (event == Ion::Events::Minus) { m_graphWindow.zoom(3.0f/4.0f); - m_view.initCursorPosition(); + m_graphWindow.initCursorPosition(); m_view.reload(); return true; } - if (event == Ion::Events::Left) { - m_view.moveCursorHorizontally(-2); + if (event == Ion::Events::Right || event == Ion::Events::Left) { + int direction = event == Ion::Events::Left ? -1 : 1; + m_view.reloadCursor(); + if (m_graphWindow.moveCursorHorizontally(direction)) { + m_view.reload(); + } else { + m_view.reloadCursor(); + } return true; } - if (event == Ion::Events::Right) { - m_view.moveCursorHorizontally(2); - return true; - } - if (event == Ion::Events::Up) { - Function * f = m_view.moveCursorVertically(1.0f); - if (f == nullptr) { - m_view.initCursorPosition(); + if (event == Ion::Events::Up || event == Ion::Events::Down) { + int direction = event == Ion::Events::Down ? -1 : 1; + int result = m_graphWindow.moveCursorVertically(direction); + if (result < 0) { + if (event == Ion::Events::Down) { + return false; + } + m_graphWindow.initCursorPosition(); m_view.setCursorVisible(false); headerViewController()->setSelectedButton(0); m_headerSelected = true; } - return true; - } - if (event == Ion::Events::Down) { - Function * f = m_view.moveCursorVertically(-1.0f); - if (f == nullptr) { - return false; + if (result == 0) { + m_view.reloadCursor(); + } + if (result == 1) { + m_view.reload(); } return true; } if (event == Ion::Events::OK) { - Function * f = m_functionStore->activeFunctionAtIndex(m_view.indexFunctionSelectedByCursor()); + Function * f = m_graphWindow.functionSelectedByCursor(); m_curveParameterController.setFunction(f); StackViewController * stack = stackController(); stack->push(&m_curveParameterController); diff --git a/apps/graph/graph/graph_view.cpp b/apps/graph/graph/graph_view.cpp index bb6220ad9..23babd1d6 100644 --- a/apps/graph/graph/graph_view.cpp +++ b/apps/graph/graph/graph_view.cpp @@ -8,10 +8,9 @@ namespace Graph { constexpr KDColor GraphView::k_gridColor; GraphView::GraphView(FunctionStore * functionStore, GraphWindow * graphWindow) : - CurveView(graphWindow), + CurveView(graphWindow, 0.0f, 0.0f, 0.2f, 0.0f), + m_bannerView(BannerView(graphWindow)), m_cursorView(CursorView()), - m_xCursorPosition(-1.0f), - m_yCursorPosition(-1.0f), m_visibleCursor(true), m_graphWindow(graphWindow), m_functionStore(functionStore), @@ -19,10 +18,65 @@ GraphView::GraphView(FunctionStore * functionStore, GraphWindow * graphWindow) : { } -BannerView * GraphView::bannerView() { +View * GraphView::bannerView() { return &m_bannerView; } +void GraphView::drawRect(KDContext * ctx, KDRect rect) const { + ctx->fillRect(rect, KDColorWhite); + drawGrid(ctx, rect); + drawAxes(ctx, rect, Axis::Horizontal); + drawAxes(ctx, rect, Axis::Vertical); + drawLabels(ctx, rect, Axis::Horizontal, true); + drawLabels(ctx, rect, Axis::Vertical, true); + for (int i = 0; i < m_functionStore->numberOfActiveFunctions(); i++) { + Function * f = m_functionStore->activeFunctionAtIndex(i); + drawCurve(ctx, rect, f, f->color()); + } +} + +void GraphView::setCursorVisible(bool visibleCursor) { + m_visibleCursor = visibleCursor; + reloadCursor(); +} + +void GraphView::setContext(Context * context) { + m_context = context; +} + +Context * GraphView::context() const { + return m_context; +} + +void GraphView::reload() { + markRectAsDirty(bounds()); + layoutSubviews(); + computeLabels(Axis::Horizontal); + computeLabels(Axis::Vertical); + m_bannerView.reload(); +} + +void GraphView::reloadCursor() { + KDCoordinate xCursorPixelPosition = roundf(floatToPixel(Axis::Horizontal, m_graphWindow->xCursorPosition())); + KDCoordinate yCursorPixelPosition = roundf(floatToPixel(Axis::Vertical, m_graphWindow->yCursorPosition())); + markRectAsDirty(KDRect(KDPoint(xCursorPixelPosition- k_cursorSize/2, yCursorPixelPosition - k_cursorSize/2), k_cursorSize, k_cursorSize)); + layoutSubviews(); + m_bannerView.reload(); +} + +void GraphView::layoutSubviews() { + KDCoordinate xCursorPixelPosition = roundf(floatToPixel(Axis::Horizontal, m_graphWindow->xCursorPosition())); + KDCoordinate yCursorPixelPosition = roundf(floatToPixel(Axis::Vertical, m_graphWindow->yCursorPosition())); + KDRect cursorFrame(xCursorPixelPosition - k_cursorSize/2, yCursorPixelPosition - k_cursorSize/2, k_cursorSize, k_cursorSize); + KDRect bannerFrame(KDRect(0, bounds().height()- k_bannerHeight, bounds().width(), k_bannerHeight)); + if (!m_visibleCursor) { + cursorFrame = KDRectZero; + bannerFrame = KDRectZero; + } + m_cursorView.setFrame(cursorFrame); + m_bannerView.setFrame(bannerFrame); +} + int GraphView::numberOfSubviews() const { return 2; }; @@ -35,145 +89,10 @@ View * GraphView::subviewAtIndex(int index) { return &m_bannerView; } -void GraphView::setContext(Context * context) { - m_context = context; - m_bannerView.setContext(context); -} - -Context * GraphView::context() const { - return m_context; -} - -int GraphView::indexFunctionSelectedByCursor() { - return m_indexFunctionSelectedByCursor; -} - -void GraphView::reload() { - markRectAsDirty(bounds()); - layoutSubviews(); - computeLabels(Axis::Horizontal); - computeLabels(Axis::Vertical); -} - -void GraphView::reloadCursor() { - markRectAsDirty(KDRect(KDPoint(roundf(m_xCursorPosition) - k_cursorSize/2, roundf(m_yCursorPosition)- k_cursorSize/2), k_cursorSize, k_cursorSize)); - layoutSubviews(); -} - char * GraphView::label(Axis axis, int index) const { return (axis == Axis::Horizontal ? (char *)m_xLabels[index] : (char *)m_yLabels[index]); } -float GraphView::xPixelCursorPosition() { - return m_xCursorPosition; -} - -float GraphView::xCursorPosition() { - return min(Axis::Horizontal) + m_xCursorPosition*(max(Axis::Horizontal)-min(Axis::Horizontal))/pixelLength(Axis::Horizontal); -} - -void GraphView::goToAbscissaOnFunction(float abscissa, Function * function) { - m_graphWindow->centerAxisAround(GraphWindow::Axis::X, abscissa); - m_xCursorPosition = floatToPixel(Axis::Horizontal, abscissa); - float ordinate = function->evaluateAtAbscissa(abscissa, m_context); - m_graphWindow->centerAxisAround(GraphWindow::Axis::Y, ordinate); - m_yCursorPosition = floatToPixel(Axis::Vertical, ordinate); - updateBannerView(function); - reload(); -} - -void GraphView::setCursorVisible(bool visibleCursor) { - m_visibleCursor = visibleCursor; - layoutSubviews(); -} - -void GraphView::initCursorPosition() { - float center = (min(Axis::Horizontal)+max(Axis::Horizontal))/2.0f; - m_indexFunctionSelectedByCursor = 0; - Function * firstFunction = m_functionStore->activeFunctionAtIndex(0); - float fCenter = firstFunction->evaluateAtAbscissa(center, m_context); - m_xCursorPosition = (bounds().width()-1.0f)/2.0f; - m_yCursorPosition = floatToPixel(Axis::Vertical, fCenter); - updateBannerView(firstFunction); -} - -void GraphView::moveCursorHorizontally(KDCoordinate xOffset) { - markRectAsDirty(KDRect(KDPoint(roundf(m_xCursorPosition) - k_cursorSize/2, roundf(m_yCursorPosition)- k_cursorSize/2), k_cursorSize, k_cursorSize)); - m_xCursorPosition = m_xCursorPosition + xOffset; - float x = pixelToFloat(Axis::Horizontal, m_xCursorPosition); - Function * f = m_functionStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor); - float y = f->evaluateAtAbscissa(x, m_context); - float xMargin = pixelToFloat(Axis::Horizontal, k_cursorMarginToBorder) - pixelToFloat(Axis::Horizontal, 0); - float yMargin = pixelToFloat(Axis::Vertical, 0) - pixelToFloat(Axis::Vertical, k_cursorMarginToBorder); - bool windowHasMoved = m_graphWindow->panToMakePointVisible(x, y, xMargin, yMargin); - m_xCursorPosition = floatToPixel(Axis::Horizontal, x); - m_yCursorPosition = floatToPixel(Axis::Vertical, y); - updateBannerView(f); - if (windowHasMoved) { - reload(); - } else { - reloadCursor(); - } -} - -Function * GraphView::moveCursorVertically(int direction) { - float x = pixelToFloat(Axis::Horizontal, m_xCursorPosition); - Function * actualFunction = m_functionStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor); - float y = actualFunction->evaluateAtAbscissa(x, m_context); - Function * nextFunction = actualFunction; - float nextY = direction > 0 ? FLT_MAX : -FLT_MAX; - for (int i = 0; i < m_functionStore->numberOfActiveFunctions(); i++) { - Function * f = m_functionStore->activeFunctionAtIndex(i); - float newY = f->evaluateAtAbscissa(x, m_context); - bool isNextFunction = direction > 0 ? (newY > y && newY < nextY) : (newY < y && newY > nextY); - if (isNextFunction) { - m_indexFunctionSelectedByCursor = i; - nextY = newY; - nextFunction = f; - } - } - if (nextFunction == actualFunction) { - return nullptr; - } - float xMargin = pixelToFloat(Axis::Horizontal, k_cursorMarginToBorder) - pixelToFloat(Axis::Horizontal, 0); - float yMargin = pixelToFloat(Axis::Vertical, 0) - pixelToFloat(Axis::Vertical, k_cursorMarginToBorder); - bool windowHasMoved = m_graphWindow->panToMakePointVisible(x, nextY, xMargin, yMargin); - markRectAsDirty(KDRect(KDPoint(roundf(m_xCursorPosition) - k_cursorSize/2, roundf(m_yCursorPosition)- k_cursorSize/2), k_cursorSize, k_cursorSize)); - m_xCursorPosition = floatToPixel(Axis::Horizontal, x); - m_yCursorPosition = floatToPixel(Axis::Vertical, nextY); - updateBannerView(nextFunction); - if (windowHasMoved) { - reload(); - } else { - reloadCursor(); - } - return nextFunction; -} - -void GraphView::layoutSubviews() { - KDRect cursorFrame(roundf(m_xCursorPosition) - k_cursorSize/2, roundf(m_yCursorPosition) - k_cursorSize/2, k_cursorSize, k_cursorSize); - KDRect bannerFrame(KDRect(0, bounds().height()- k_bannerHeight, bounds().width(), k_bannerHeight)); - if (!m_visibleCursor) { - cursorFrame = KDRectZero; - bannerFrame = KDRectZero; - } - m_cursorView.setFrame(cursorFrame); - m_bannerView.setFrame(bannerFrame); -} - -void GraphView::drawRect(KDContext * ctx, KDRect rect) const { - ctx->fillRect(rect, KDColorWhite); - drawGrid(ctx, rect); - drawAxes(ctx, rect, Axis::Horizontal); - drawAxes(ctx, rect, Axis::Vertical); - drawLabels(ctx, rect, Axis::Horizontal, true); - drawLabels(ctx, rect, Axis::Vertical, true); - for (int i = 0; i < m_functionStore->numberOfActiveFunctions(); i++) { - Function * f = m_functionStore->activeFunctionAtIndex(i); - drawCurve(ctx, rect, f, f->color()); - } -} - void GraphView::drawGridLines(KDContext * ctx, KDRect rect, Axis axis, float step, KDColor color) const { float rectMin = pixelToFloat(Axis::Horizontal, rect.left()); float rectMax = pixelToFloat(Axis::Horizontal, rect.right()); @@ -200,9 +119,4 @@ float GraphView::evaluateModelWithParameter(Model * curve, float abscissa) const return f->evaluateAtAbscissa(abscissa, m_context); } -void GraphView::updateBannerView(Function * function) { - m_bannerView.setAbscissa(xCursorPosition()); - m_bannerView.setFunction(function); -} - } diff --git a/apps/graph/graph/graph_view.h b/apps/graph/graph/graph_view.h index 0035be7a6..4d433205c 100644 --- a/apps/graph/graph/graph_view.h +++ b/apps/graph/graph/graph_view.h @@ -15,43 +15,30 @@ namespace Graph { class GraphView : public CurveView { public: GraphView(FunctionStore * functionStore, GraphWindow * graphWindow); - BannerView * bannerView(); + View * bannerView(); void drawRect(KDContext * ctx, KDRect rect) const override; - - float xPixelCursorPosition(); - float xCursorPosition(); - void goToAbscissaOnFunction(float abscissa, Function * function); void setCursorVisible(bool visibleCursor); - void initCursorPosition(); - void moveCursorHorizontally(KDCoordinate xOffset); - Function * moveCursorVertically(int direction); void setContext(Context * context); Context * context() const; - int indexFunctionSelectedByCursor(); void reload(); void reloadCursor(); + void layoutSubviews() override; private: constexpr static KDColor k_gridColor = KDColor::RGB24(0xEEEEEE); constexpr static KDCoordinate k_cursorSize = 9; - constexpr static float k_cursorMarginToBorder = 6.0f; constexpr static KDCoordinate k_bannerHeight = 30; int numberOfSubviews() const override; View * subviewAtIndex(int index) override; - void layoutSubviews() override; char * label(Axis axis, int index) const override; float evaluateModelWithParameter(Model * expression, float abscissa) const override; void drawGrid(KDContext * ctx, KDRect rect) const; void drawGridLines(KDContext * ctx, KDRect rect, Axis axis, float step, KDColor color) const; - void updateBannerView(Function * function); BannerView m_bannerView; CursorView m_cursorView; - float m_xCursorPosition; - float m_yCursorPosition; - int m_indexFunctionSelectedByCursor; bool m_visibleCursor; char m_xLabels[k_maxNumberOfXLabels][Constant::FloatBufferSizeInScientificMode]; diff --git a/apps/graph/graph/graph_window.cpp b/apps/graph/graph/graph_window.cpp index e6409cc30..5b46299a0 100644 --- a/apps/graph/graph/graph_window.cpp +++ b/apps/graph/graph/graph_window.cpp @@ -15,6 +15,9 @@ GraphWindow::GraphWindow(FunctionStore * functionStore) : m_yAuto(true), m_xGridUnit(2.0f), m_yGridUnit(2.0f), + m_xCursorPosition(NAN), + m_yCursorPosition(NAN), + m_indexFunctionSelectedByCursor(0), m_functionStore(functionStore), m_context(nullptr) { @@ -130,22 +133,6 @@ void GraphWindow::zoom(float ratio) { m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); } -void GraphWindow::centerAxisAround(Axis axis, float position) { - if (axis == Axis::X) { - float range = m_xMax - m_xMin; - m_xMin = position - range/2.0f; - m_xMax = position + range/2.0f; - computeYaxes(); - m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); - } else { - m_yAuto = false; - float range = m_yMax - m_yMin; - m_yMin = position - range/2.0f; - m_yMax = position + range/2.0f; - m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); - } -} - void GraphWindow::translateWindow(Direction direction) { m_yAuto = false; if (direction == Direction::Up) { @@ -212,6 +199,74 @@ void GraphWindow::setDefault() { setYAuto(true); } +float GraphWindow::xCursorPosition() { + return m_xCursorPosition; +} + +float GraphWindow::yCursorPosition() { + return m_yCursorPosition; +} + +float GraphWindow::derivativeAtCursorPosition() { + Function * f = m_functionStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor); + return f->approximateDerivative(m_xCursorPosition, m_context); +} + +Function * GraphWindow::functionSelectedByCursor() { + return m_functionStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor); +} + +void GraphWindow::setCursorPositionAtAbscissaWithFunction(float abscissa, Function * function) { + m_xCursorPosition = abscissa; + centerAxisAround(GraphWindow::Axis::X, m_xCursorPosition); + m_yCursorPosition = function->evaluateAtAbscissa(m_xCursorPosition, m_context); + centerAxisAround(GraphWindow::Axis::Y, m_yCursorPosition); +} + + +void GraphWindow::initCursorPosition() { + m_xCursorPosition = (m_xMin+m_xMax)/2.0f; + m_indexFunctionSelectedByCursor = 0; + Function * firstFunction = m_functionStore->activeFunctionAtIndex(0); + m_yCursorPosition = firstFunction->evaluateAtAbscissa(m_xCursorPosition, m_context); +} + +bool GraphWindow::moveCursorHorizontally(int direction) { + m_xCursorPosition = direction > 0 ? m_xCursorPosition + m_xGridUnit/k_numberOfCursorStepsInGradUnit : + m_xCursorPosition - m_xGridUnit/k_numberOfCursorStepsInGradUnit; + Function * f = m_functionStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor); + m_yCursorPosition = f->evaluateAtAbscissa(m_xCursorPosition, m_context); + float xMargin = k_cursorMarginFactorToBorder * (m_xMax - m_xMin); + float yMargin = k_cursorMarginFactorToBorder * (m_yMax - m_yMin); + bool windowHasMoved = panToMakePointVisible(m_xCursorPosition, m_yCursorPosition, xMargin, yMargin); + return windowHasMoved; +} + +int GraphWindow::moveCursorVertically(int direction) { + Function * actualFunction = m_functionStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor); + float y = actualFunction->evaluateAtAbscissa(m_xCursorPosition, m_context); + Function * nextFunction = actualFunction; + float nextY = direction > 0 ? FLT_MAX : -FLT_MAX; + for (int i = 0; i < m_functionStore->numberOfActiveFunctions(); i++) { + Function * f = m_functionStore->activeFunctionAtIndex(i); + float newY = f->evaluateAtAbscissa(m_xCursorPosition, m_context); + bool isNextFunction = direction > 0 ? (newY > y && newY < nextY) : (newY < y && newY > nextY); + if (isNextFunction) { + m_indexFunctionSelectedByCursor = i; + nextY = newY; + nextFunction = f; + } + } + if (nextFunction == actualFunction) { + return -1; + } + float xMargin = k_cursorMarginFactorToBorder * (m_xMax - m_xMin); + float yMargin = k_cursorMarginFactorToBorder * (m_yMax - m_yMin); + m_yCursorPosition = nextY; + bool windowHasMoved = panToMakePointVisible(m_xCursorPosition, m_yCursorPosition, xMargin, yMargin); + return windowHasMoved; +} + bool GraphWindow::panToMakePointVisible(float x, float y, float xMargin, float yMargin) { bool windowMoved = false; float xRange = m_xMax - m_xMin; @@ -234,15 +289,33 @@ bool GraphWindow::panToMakePointVisible(float x, float y, float xMargin, float y m_yMin = y - yMargin; m_yMax = m_yMin + yRange; m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); + computeYaxes(); windowMoved = true; } if (y > m_yMax - yMargin) { m_yMax = y + yMargin; m_yMin = m_yMax - yRange; m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); + computeYaxes(); windowMoved = true; } return windowMoved; } +void GraphWindow::centerAxisAround(Axis axis, float position) { + if (axis == Axis::X) { + float range = m_xMax - m_xMin; + m_xMin = position - range/2.0f; + m_xMax = position + range/2.0f; + computeYaxes(); + m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); + } else { + m_yAuto = false; + float range = m_yMax - m_yMin; + m_yMin = position - range/2.0f; + m_yMax = position + range/2.0f; + m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); + } +} + } diff --git a/apps/graph/graph/graph_window.h b/apps/graph/graph/graph_window.h index 5a94877f5..fa36cfdc7 100644 --- a/apps/graph/graph/graph_window.h +++ b/apps/graph/graph/graph_window.h @@ -33,14 +33,26 @@ public: Context * context(); void setContext(Context * context); void zoom(float ratio); - void centerAxisAround(Axis axis, float position); void translateWindow(Direction direction); void setTrigonometric(); void roundAbscissa(); void normalize(); void setDefault(); - bool panToMakePointVisible(float x, float y, float xMargin, float yMargin); + float xCursorPosition(); + float yCursorPosition(); + float derivativeAtCursorPosition(); + Function * functionSelectedByCursor(); + void setCursorPositionAtAbscissaWithFunction(float abscissa, Function * function); + void initCursorPosition(); + bool moveCursorHorizontally(int direction); + // the result of moveCursorVertically means: + // -1 -> no next function 0-> the window has not changed 1->the window changed + int moveCursorVertically(int direction); private: + constexpr static float k_cursorMarginFactorToBorder = 0.05f; + constexpr static float k_numberOfCursorStepsInGradUnit = 5.0f; + bool panToMakePointVisible(float x, float y, float xMargin, float yMargin); + void centerAxisAround(Axis axis, float position); float m_xMin; float m_xMax; float m_yMin; @@ -48,6 +60,9 @@ private: bool m_yAuto; float m_xGridUnit; float m_yGridUnit; + float m_xCursorPosition; + float m_yCursorPosition; + int m_indexFunctionSelectedByCursor; FunctionStore * m_functionStore; Context * m_context; }; diff --git a/apps/graph/graph/initialisation_parameter_controller.cpp b/apps/graph/graph/initialisation_parameter_controller.cpp index f0966b7de..ce4d2cef5 100644 --- a/apps/graph/graph/initialisation_parameter_controller.cpp +++ b/apps/graph/graph/initialisation_parameter_controller.cpp @@ -4,12 +4,11 @@ namespace Graph { -InitialisationParameterController::InitialisationParameterController(Responder * parentResponder, GraphWindow * graphWindow, GraphView * graphView) : +InitialisationParameterController::InitialisationParameterController(Responder * parentResponder, GraphWindow * graphWindow) : ViewController(parentResponder), m_selectableTableView(SelectableTableView(this, this, Metric::TopMargin, Metric::RightMargin, Metric::BottomMargin, Metric::LeftMargin)), - m_graphWindow(graphWindow), - m_graphView(graphView) + m_graphWindow(graphWindow) { m_cells[0].setText("Trigonometrique"); m_cells[1].setText("Abscisses entieres"); @@ -54,7 +53,7 @@ bool InitialisationParameterController::handleEvent(Ion::Events::Event event) { default: return false; } - m_graphView->initCursorPosition(); + m_graphWindow->initCursorPosition(); StackViewController * stack = (StackViewController *)parentResponder(); stack->pop(); return true; diff --git a/apps/graph/graph/initialisation_parameter_controller.h b/apps/graph/graph/initialisation_parameter_controller.h index 28f7b35f7..9eebe5ed4 100644 --- a/apps/graph/graph/initialisation_parameter_controller.h +++ b/apps/graph/graph/initialisation_parameter_controller.h @@ -9,7 +9,7 @@ namespace Graph { class InitialisationParameterController : public ViewController, public SimpleListViewDataSource { public: - InitialisationParameterController(Responder * parentResponder, GraphWindow * graphWindow, GraphView * graphView); + InitialisationParameterController(Responder * parentResponder, GraphWindow * graphWindow); View * view() override; const char * title() const override; bool handleEvent(Ion::Events::Event event) override; @@ -23,7 +23,6 @@ private: MenuListCell m_cells[k_totalNumberOfCells]; SelectableTableView m_selectableTableView; GraphWindow * m_graphWindow; - GraphView * m_graphView; }; } diff --git a/apps/graph/graph/window_parameter_controller.cpp b/apps/graph/graph/window_parameter_controller.cpp index ccdeb2e3b..65537e454 100644 --- a/apps/graph/graph/window_parameter_controller.cpp +++ b/apps/graph/graph/window_parameter_controller.cpp @@ -5,13 +5,12 @@ namespace Graph { -WindowParameterController::WindowParameterController(Responder * parentResponder, GraphWindow * graphWindow, GraphView * graphView) : +WindowParameterController::WindowParameterController(Responder * parentResponder, GraphWindow * graphWindow) : FloatParameterController(parentResponder), m_graphWindow(graphWindow), m_windowCells{EditableTextMenuListCell(&m_selectableTableView, this, m_draftTextBuffer, (char *)"Xmin"), EditableTextMenuListCell(&m_selectableTableView, this, m_draftTextBuffer, (char *)"Xmax"), EditableTextMenuListCell(&m_selectableTableView, this, m_draftTextBuffer, (char *)"Ymin"), EditableTextMenuListCell(&m_selectableTableView, this, m_draftTextBuffer, (char *)"Ymax")}, - m_yAutoCell(SwitchMenuListCell((char*)"Y auto")), - m_graphView(graphView) + m_yAutoCell(SwitchMenuListCell((char*)"Y auto")) { } @@ -66,7 +65,7 @@ void WindowParameterController::tableViewDidChangeSelection(SelectableTableView } bool WindowParameterController::handleEvent(Ion::Events::Event event) { - m_graphView->initCursorPosition(); + m_graphWindow->initCursorPosition(); if (activeCell() == 2) { if (event == Ion::Events::OK) { m_graphWindow->setYAuto(!m_graphWindow->yAuto()); diff --git a/apps/graph/graph/window_parameter_controller.h b/apps/graph/graph/window_parameter_controller.h index 979a26166..4313f5694 100644 --- a/apps/graph/graph/window_parameter_controller.h +++ b/apps/graph/graph/window_parameter_controller.h @@ -9,7 +9,7 @@ namespace Graph { class WindowParameterController : public FloatParameterController { public: - WindowParameterController(Responder * parentResponder, GraphWindow * graphWindow, GraphView * graphView); + WindowParameterController(Responder * parentResponder, GraphWindow * graphWindow); ExpressionTextFieldDelegate * textFieldDelegate() override; const char * title() const override; int numberOfRows() override; @@ -27,7 +27,6 @@ private: char m_draftTextBuffer[EditableTextMenuListCell::k_bufferLength]; EditableTextMenuListCell m_windowCells[k_numberOfTextCell]; SwitchMenuListCell m_yAutoCell; - GraphView * m_graphView; }; } diff --git a/apps/graph/graph/zoom_parameter_controller.cpp b/apps/graph/graph/zoom_parameter_controller.cpp index 77dc49a63..68306a038 100644 --- a/apps/graph/graph/zoom_parameter_controller.cpp +++ b/apps/graph/graph/zoom_parameter_controller.cpp @@ -26,37 +26,37 @@ View * ZoomParameterController::view() { bool ZoomParameterController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Plus) { m_graphWindow->zoom(1.0f/3.0f); - m_contentView.graphView()->initCursorPosition(); + m_graphWindow->initCursorPosition(); m_contentView.graphView()->reload(); return true; } if (event == Ion::Events::Minus) { m_graphWindow->zoom(3.0f/4.0f); - m_contentView.graphView()->initCursorPosition(); + m_graphWindow->initCursorPosition(); m_contentView.graphView()->reload(); return true; } if (event == Ion::Events::Up) { m_graphWindow->translateWindow(GraphWindow::Direction::Up); - m_contentView.graphView()->initCursorPosition(); + m_graphWindow->initCursorPosition(); m_contentView.graphView()->reload(); return true; } if (event == Ion::Events::Down) { m_graphWindow->translateWindow(GraphWindow::Direction::Down); - m_contentView.graphView()->initCursorPosition(); + m_graphWindow->initCursorPosition(); m_contentView.graphView()->reload(); return true; } if (event == Ion::Events::Left) { m_graphWindow->translateWindow(GraphWindow::Direction::Left); - m_contentView.graphView()->initCursorPosition(); + m_graphWindow->initCursorPosition(); m_contentView.graphView()->reload(); return true; } if (event == Ion::Events::Right) { m_graphWindow->translateWindow(GraphWindow::Direction::Right); - m_contentView.graphView()->initCursorPosition(); + m_graphWindow->initCursorPosition(); m_contentView.graphView()->reload(); return true; }