diff --git a/apps/Makefile b/apps/Makefile index 61d173693..3aae45d86 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -9,6 +9,7 @@ app_objs += $(addprefix apps/,\ apps_container.o\ constant.o\ curve_view.o\ + curve_view_window.o\ editable_cell_table_view_controller.o\ expression_text_field_delegate.o\ float_parameter_controller.o\ diff --git a/apps/curve_view.cpp b/apps/curve_view.cpp index 76ada169e..42dd57378 100644 --- a/apps/curve_view.cpp +++ b/apps/curve_view.cpp @@ -7,15 +7,38 @@ constexpr KDColor CurveView::k_axisColor; -CurveView::CurveView() : - View() +CurveView::CurveView(CurveViewWindow * curveViewWindow) : + View(), + m_curveViewWindow(curveViewWindow) { } +void CurveView::setCurveViewWindow(CurveViewWindow * curveViewWindow) { + m_curveViewWindow = curveViewWindow; +} + void CurveView::reload() { markRectAsDirty(bounds()); } +float CurveView::min(Axis axis) const { + assert(axis == Axis::Horizontal || axis == Axis::Vertical); + float range = axis == Axis::Horizontal ? m_curveViewWindow->xMax() - m_curveViewWindow->xMin() : m_curveViewWindow->yMax() - m_curveViewWindow->yMin(); + float absoluteMin = (axis == Axis::Horizontal ? m_curveViewWindow->xMin(): m_curveViewWindow->yMin()); + return absoluteMin - k_marginFactor*range; +} + +float CurveView::max(Axis axis) const { + assert(axis == Axis::Horizontal || axis == Axis::Vertical); + float range = axis == Axis::Horizontal ? m_curveViewWindow->xMax() - m_curveViewWindow->xMin() : m_curveViewWindow->yMax() - m_curveViewWindow->yMin(); + float absoluteMax = (axis == Axis::Horizontal ? m_curveViewWindow->xMax() : m_curveViewWindow->yMax()); + return absoluteMax + k_marginFactor*range; +} + +float CurveView::gridUnit(Axis axis) const { + return (axis == Axis::Horizontal ? m_curveViewWindow->xGridUnit() : m_curveViewWindow->yGridUnit()); +} + KDCoordinate CurveView::pixelLength(Axis axis) const { assert(axis == Axis::Horizontal || axis == Axis::Vertical); return (axis == Axis::Horizontal ? m_frame.width() : m_frame.height()); @@ -131,13 +154,14 @@ const uint8_t stampMask[stampSize+1][stampSize+1] = { constexpr static int k_maxNumberOfIterations = 10; constexpr static int k_resolution = 320.0f; +constexpr static int k_externRectMargin = 1; void CurveView::drawCurve(void * curve, KDColor color, KDContext * ctx, KDRect rect, bool colorUnderCurve, float colorLowerBound, float colorUpperBound, bool continuously) const { float xMin = min(Axis::Horizontal); float xMax = max(Axis::Horizontal); float xStep = (xMax-xMin)/k_resolution; - float rectMin = pixelToFloat(Axis::Horizontal, rect.left()); - float rectMax = pixelToFloat(Axis::Horizontal, rect.right()); + float rectMin = pixelToFloat(Axis::Horizontal, rect.left() - k_externRectMargin); + float rectMax = pixelToFloat(Axis::Horizontal, rect.right() + k_externRectMargin); for (float x = rectMin; x < rectMax; x += xStep) { float y = evaluateCurveAtAbscissa(curve, x); float pxf = floatToPixel(Axis::Horizontal, x); diff --git a/apps/curve_view.h b/apps/curve_view.h index 3aae1b06a..ee37fe522 100644 --- a/apps/curve_view.h +++ b/apps/curve_view.h @@ -3,6 +3,7 @@ #include #include +#include "curve_view_window.h" class CurveView : public View { public: @@ -10,16 +11,19 @@ public: Horizontal = 0, Vertical = 1 }; - CurveView(); + CurveView(CurveViewWindow * curveViewWindow = nullptr); void reload(); protected: constexpr static KDColor k_axisColor = KDColor::RGB24(0x000000); constexpr static KDCoordinate k_labelMargin = 4; constexpr static int k_maxNumberOfXLabels = 18; constexpr static int k_maxNumberOfYLabels = 13; - virtual float min(Axis axis) const = 0; - virtual float max(Axis axis) const = 0; - virtual float gridUnit(Axis axis) const = 0; + void setCurveViewWindow(CurveViewWindow * curveViewWindow); + /* The window bounds are deduced from the model bounds but also take into + account a margin (computed with k_marginFactor) */ + float min(Axis axis) const; + float max(Axis axis) const; + float gridUnit(Axis axis) const; virtual char * label(Axis axis, int index) const = 0; KDCoordinate pixelLength(Axis axis) const; float pixelToFloat(Axis axis, KDCoordinate p) const; @@ -32,6 +36,7 @@ protected: void computeLabels(Axis axis); void drawLabels(Axis axis, bool shiftOrigin, KDContext * ctx, KDRect rect) const; private: + constexpr static float k_marginFactor = 0.2f; int numberOfLabels(Axis axis) const; virtual float evaluateCurveAtAbscissa(void * curve, float t) const = 0; /* Recursively join two dots (dichotomy). The method stops when the @@ -43,6 +48,7 @@ private: * function shifts the stamp (by blending adjacent pixel colors) to draw with * anti alising. */ void stampAtLocation(float pxf, float pyf, KDColor color, KDContext * ctx, KDRect rect) const; + CurveViewWindow * m_curveViewWindow; }; #endif diff --git a/apps/curve_view_window.cpp b/apps/curve_view_window.cpp new file mode 100644 index 000000000..682b8918e --- /dev/null +++ b/apps/curve_view_window.cpp @@ -0,0 +1,29 @@ +#include "curve_view_window.h" +#include +#include + +float CurveViewWindow::yGridUnit() { + return 0.0f; +} + +float CurveViewWindow::computeGridUnit(Axis axis) { + int a = 0; + int b = 0; + float d = xMax() - xMin(); + float maxNumberOfUnits = k_maxNumberOfXGridUnits; + float minNumberOfUnits = k_minNumberOfXGridUnits; + if (axis == Axis::Y) { + d = yMax() - yMin(); + maxNumberOfUnits = k_maxNumberOfYGridUnits; + minNumberOfUnits = k_minNumberOfYGridUnits; + } + float units[3] = {k_oneUnit, k_twoUnit, k_fiveUnit}; + for (int k = 0; k < 3; k++) { + float unit = units[k]; + if (floorf(log10f(d/(unit*maxNumberOfUnits))) != floorf(log10f(d/(unit*minNumberOfUnits)))) { + b = floorf(log10f(d/(unit*minNumberOfUnits))); + a = unit; + } + } + return a*powf(10,b); +} diff --git a/apps/curve_view_window.h b/apps/curve_view_window.h new file mode 100644 index 000000000..48412cf71 --- /dev/null +++ b/apps/curve_view_window.h @@ -0,0 +1,27 @@ +#ifndef APPS_CURVE_VIEW_WINDOW_H +#define APPS_CURVE_VIEW_WINDOW_H + +class CurveViewWindow { +public: + enum class Axis { + X, + Y + }; + virtual float xMin() = 0; + virtual float xMax() = 0; + virtual float yMin() = 0; + virtual float yMax() = 0; + virtual float xGridUnit() = 0; + virtual float yGridUnit(); +protected: + constexpr static float k_minNumberOfXGridUnits = 7.0f; + constexpr static float k_maxNumberOfXGridUnits = 18.0f; + constexpr static float k_minNumberOfYGridUnits = 5.0f; + constexpr static float k_maxNumberOfYGridUnits = 13.0f; + constexpr static float k_oneUnit = 1.0f; + constexpr static float k_twoUnit = 2.0f; + constexpr static float k_fiveUnit = 5.0f; + float computeGridUnit(Axis axis); +}; + +#endif diff --git a/apps/graph/Makefile b/apps/graph/Makefile index 01bb69cda..afd4276c9 100644 --- a/apps/graph/Makefile +++ b/apps/graph/Makefile @@ -3,6 +3,7 @@ app_objs += $(addprefix apps/graph/,\ function.o\ function_store.o\ function_title_cell.o\ + graph/banner_view.o\ graph/graph_window.o\ graph/window_parameter_controller.o\ graph/cursor_view.o\ diff --git a/apps/graph/graph/banner_view.cpp b/apps/graph/graph/banner_view.cpp new file mode 100644 index 000000000..306b9a2a3 --- /dev/null +++ b/apps/graph/graph/banner_view.cpp @@ -0,0 +1,82 @@ +#include "banner_view.h" +#include +#include "../../constant.h" + +namespace Graph { + +BannerView::BannerView() : + m_abscissa(0.0f), + m_function(nullptr), + m_abscissaView(0.0f, 0.5f), + m_functionView(0.5f, 0.5f), + m_derivativeView(1.0f, 0.5f), + m_displayDerivative(false), + m_context(nullptr) +{ +} + +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; +} + +bool BannerView::displayDerivative() { + return m_displayDerivative; +} + +int BannerView::numberOfSubviews() const { + if (m_displayDerivative) { + return 3; + } + return 2; +} + +View * BannerView::subviewAtIndex(int index) { + assert(index >= 0); + if (index == 0) { + return &m_abscissaView; + } + if (index == 1) { + return &m_functionView; + } + return &m_derivativeView; +} + +void BannerView::layoutSubviews() { + m_abscissaView.setFrame(bounds()); + m_functionView.setFrame(bounds()); + m_derivativeView.setFrame(bounds()); +} + +} diff --git a/apps/graph/graph/banner_view.h b/apps/graph/graph/banner_view.h new file mode 100644 index 000000000..e3770319b --- /dev/null +++ b/apps/graph/graph/banner_view.h @@ -0,0 +1,33 @@ +#ifndef GRAPH_BANNER_VIEW_H +#define GRAPH_BANNER_VIEW_H + +#include +#include "../function.h" + +namespace Graph { + +class BannerView : public View { +public: + BannerView(); + 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: + 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; +}; + +} + +#endif diff --git a/apps/graph/graph/curve_parameter_controller.cpp b/apps/graph/graph/curve_parameter_controller.cpp index 54fb977b8..49bd845e0 100644 --- a/apps/graph/graph/curve_parameter_controller.cpp +++ b/apps/graph/graph/curve_parameter_controller.cpp @@ -6,7 +6,6 @@ namespace Graph { CurveParameterController::CurveParameterController(GraphView * graphView) : ViewController(nullptr), m_graphView(graphView), - m_displayDerivative(false), m_function(nullptr), m_calculationCell(ChevronMenuListCell((char*)"Calculer")), m_goToCell(ChevronMenuListCell((char*)"Aller a")), @@ -33,7 +32,7 @@ void CurveParameterController::didBecomeFirstResponder() { void CurveParameterController::willDisplayCellForIndex(TableViewCell * cell, int index) { if (cell == &m_derivativeCell) { SwitchView * switchView = (SwitchView *)m_derivativeCell.accessoryView(); - switchView->setState(m_displayDerivative); + switchView->setState(m_graphView->bannerView()->displayDerivative()); } } @@ -50,7 +49,7 @@ bool CurveParameterController::handleEvent(Ion::Events::Event event) { return true; } case 2: - m_displayDerivative = !m_displayDerivative; + m_graphView->bannerView()->setDisplayDerivative(!m_graphView->bannerView()->displayDerivative()); m_selectableTableView.reloadData(); return true; default: @@ -79,10 +78,6 @@ KDCoordinate CurveParameterController::cellHeight() { return 35; } -bool CurveParameterController::displayDerivative() const { - return m_displayDerivative; -} - void CurveParameterController::setFunction(Function * function) { m_function = function; } diff --git a/apps/graph/graph/curve_parameter_controller.h b/apps/graph/graph/curve_parameter_controller.h index fd2e49f11..e89608968 100644 --- a/apps/graph/graph/curve_parameter_controller.h +++ b/apps/graph/graph/curve_parameter_controller.h @@ -20,11 +20,9 @@ public: TableViewCell * reusableCell(int index) override; int reusableCellCount() override; void willDisplayCellForIndex(TableViewCell * cell, int index) override; - bool displayDerivative() const; void setFunction(Function * function); private: GraphView * m_graphView; - bool m_displayDerivative; Function * m_function; constexpr static int k_totalNumberOfCells = 3; ChevronMenuListCell m_calculationCell; diff --git a/apps/graph/graph/graph_view.cpp b/apps/graph/graph/graph_view.cpp index 409927a45..92de61c06 100644 --- a/apps/graph/graph/graph_view.cpp +++ b/apps/graph/graph/graph_view.cpp @@ -8,7 +8,7 @@ namespace Graph { constexpr KDColor GraphView::k_gridColor; GraphView::GraphView(FunctionStore * functionStore, GraphWindow * graphWindow) : - CurveView(), + CurveView(graphWindow), m_cursorView(CursorView()), m_xCursorPosition(-1.0f), m_yCursorPosition(-1.0f), @@ -19,17 +19,25 @@ GraphView::GraphView(FunctionStore * functionStore, GraphWindow * graphWindow) : { } +BannerView * GraphView::bannerView() { + return &m_bannerView; +} + int GraphView::numberOfSubviews() const { - return 1; + return 2; }; View * GraphView::subviewAtIndex(int index) { - assert(index == 0); - return &m_cursorView; + assert(index >= 0 && index < 2); + if (index == 0) { + return &m_cursorView; + } + return &m_bannerView; } void GraphView::setContext(Context * context) { m_context = context; + m_bannerView.setContext(context); } Context * GraphView::context() const { @@ -52,10 +60,6 @@ void GraphView::reloadCursor() { layoutSubviews(); } -float GraphView::gridUnit(Axis axis) const { - return (axis == Axis::Horizontal ? m_graphWindow->xGridUnit() : m_graphWindow->yGridUnit()); -} - char * GraphView::label(Axis axis, int index) const { return (axis == Axis::Horizontal ? (char *)m_xLabels[index] : (char *)m_yLabels[index]); } @@ -74,6 +78,7 @@ void GraphView::goToAbscissaOnFunction(float abscissa, Function * function) { float ordinate = function->evaluateAtAbscissa(abscissa, m_context); m_graphWindow->centerAxisAround(GraphWindow::Axis::Y, ordinate); m_yCursorPosition = floatToPixel(Axis::Vertical, ordinate); + updateBannerView(function); reload(); } @@ -89,6 +94,7 @@ void GraphView::initCursorPosition() { 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) { @@ -102,6 +108,7 @@ void GraphView::moveCursorHorizontally(KDCoordinate xOffset) { 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 { @@ -134,6 +141,7 @@ Function * GraphView::moveCursorVertically(int direction) { 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 { @@ -144,10 +152,13 @@ Function * GraphView::moveCursorVertically(int direction) { 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 { @@ -184,19 +195,14 @@ void GraphView::drawGrid(KDContext * ctx, KDRect rect) const { drawGridLines(ctx, rect, Axis::Vertical, m_graphWindow->yGridUnit(), k_gridColor); } -float GraphView::min(Axis axis) const { - assert(axis == Axis::Horizontal || axis == Axis::Vertical); - return (axis == Axis::Horizontal ? m_graphWindow->xMin() : m_graphWindow->yMin()); -} - -float GraphView::max(Axis axis) const { - assert(axis == Axis::Horizontal || axis == Axis::Vertical); - return (axis == Axis::Horizontal ? m_graphWindow->xMax() : m_graphWindow->yMax()); -} - float GraphView::evaluateCurveAtAbscissa(void * curve, float abscissa) const { Function * f = (Function *)curve; 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 7bcfb4b29..0723a7607 100644 --- a/apps/graph/graph/graph_view.h +++ b/apps/graph/graph/graph_view.h @@ -2,6 +2,7 @@ #define GRAPH_GRAPH_VIEW_H #include +#include "banner_view.h" #include "cursor_view.h" #include "graph_window.h" #include "../../curve_view.h" @@ -14,6 +15,7 @@ namespace Graph { class GraphView : public CurveView { public: GraphView(FunctionStore * functionStore, GraphWindow * graphWindow); + BannerView * bannerView(); void drawRect(KDContext * ctx, KDRect rect) const override; float xPixelCursorPosition(); @@ -32,18 +34,19 @@ 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; - float min(Axis axis) const override; - float max(Axis axis) const override; - float gridUnit(Axis axis) const override; char * label(Axis axis, int index) const override; float evaluateCurveAtAbscissa(void * 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; diff --git a/apps/graph/graph/graph_window.cpp b/apps/graph/graph/graph_window.cpp index 72d74d185..555f6db7c 100644 --- a/apps/graph/graph/graph_window.cpp +++ b/apps/graph/graph/graph_window.cpp @@ -51,23 +51,23 @@ float GraphWindow::yGridUnit() { void GraphWindow::setXMin(float xMin) { m_xMin = xMin; computeYaxes(); - computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X); } void GraphWindow::setXMax(float xMax) { m_xMax = xMax; computeYaxes(); - computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X); } void GraphWindow::setYMin(float yMin) { m_yMin = yMin; - computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y); } void GraphWindow::setYMax(float yMax) { m_yMax = yMax; - computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y); } void GraphWindow::setYAuto(bool yAuto) { @@ -104,7 +104,7 @@ bool GraphWindow::computeYaxes() { m_yMax = max + 1; } } - computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y); return true; } @@ -123,11 +123,11 @@ void GraphWindow::zoom(float ratio) { float yMax = m_yMax; m_xMin = (xMax+xMin)/2.0f - ratio*fabsf(xMax-xMin); m_xMax = (xMax+xMin)/2.0f + ratio*fabsf(xMax-xMin); - computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X); m_yAuto = false; m_yMin = (yMax+yMin)/2.0f - ratio*fabsf(yMax-yMin); m_yMax = (yMax+yMin)/2.0f + ratio*fabsf(yMax-yMin); - computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y); } void GraphWindow::centerAxisAround(Axis axis, float position) { @@ -136,13 +136,13 @@ void GraphWindow::centerAxisAround(Axis axis, float position) { m_xMin = position - range/2.0f; m_xMax = position + range/2.0f; computeYaxes(); - computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X); } else { m_yAuto = false; float range = m_yMax - m_yMin; m_yMin = position - range/2.0f; m_yMax = position + range/2.0f; - computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y); } } @@ -151,23 +151,23 @@ void GraphWindow::translateWindow(Direction direction) { if (direction == Direction::Up) { m_yMin = m_yMin + m_yGridUnit; m_yMax = m_yMax + m_yGridUnit; - computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y); } if (direction == Direction::Down) { m_yMin = m_yMin - m_yGridUnit; m_yMax = m_yMax - m_yGridUnit; - computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y); } if (direction == Direction::Left) { m_xMin = m_xMin - m_xGridUnit; m_xMax = m_xMax - m_xGridUnit; - computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X); computeYaxes(); } if (direction == Direction::Right) { m_xMin = m_xMin + m_xGridUnit; m_xMax = m_xMax + m_xGridUnit; - computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X); computeYaxes(); } } @@ -175,11 +175,11 @@ void GraphWindow::translateWindow(Direction direction) { void GraphWindow::setTrigonometric() { m_xMin = -10.5f; m_xMax = 10.5f; - computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X); m_yAuto = false; m_yMin = -1.6f; m_yMax = 1.6f; - computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y); } void GraphWindow::roundAbscissa() { @@ -187,7 +187,7 @@ void GraphWindow::roundAbscissa() { float xMax = m_xMax; m_xMin = roundf((xMin+xMax)/2) - 160.0f; m_xMax = roundf((xMin+xMax)/2) + 159.0f; - computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X); computeYaxes(); } @@ -198,17 +198,17 @@ void GraphWindow::normalize() { float yMax = m_yMax; m_xMin = (xMin+xMax)/2 - 5.3f; m_xMax = (xMin+xMax)/2 + 5.3f; - computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X); m_yAuto = false; m_yMin = (yMin+yMax)/2 - 3.1f; m_yMax = (yMin+yMax)/2 + 3.1f; - computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y); } void GraphWindow::setDefault() { m_xMin = -10.0f; m_xMax = 10.0f; - computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X); setYAuto(true); } @@ -219,56 +219,30 @@ bool GraphWindow::panToMakePointVisible(float x, float y, float xMargin, float y if (x < m_xMin + xMargin) { m_xMin = x - xMargin; m_xMax = m_xMin + xRange; - computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X); computeYaxes(); windowMoved = true; } if (x > m_xMax - xMargin) { m_xMax = x + xMargin; m_xMin = m_xMax - xRange; - computeGridUnit(Axis::X); + m_xGridUnit = computeGridUnit(Axis::X); computeYaxes(); windowMoved = true; } if (y < m_yMin + yMargin) { m_yMin = y - yMargin; m_yMax = m_yMin + yRange; - computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y); windowMoved = true; } if (y > m_yMax - yMargin) { m_yMax = y + yMargin; m_yMin = m_yMax - yRange; - computeGridUnit(Axis::Y); + m_yGridUnit = computeGridUnit(Axis::Y); windowMoved = true; } return windowMoved; } -void GraphWindow::computeGridUnit(Axis axis) { - int a = 0; - int b = 0; - float d = m_xMax - m_xMin; - float maxNumberOfUnits = k_maxNumberOfXGridUnits; - float minNumberOfUnits = k_minNumberOfXGridUnits; - if (axis == Axis::Y) { - d = m_yMax - m_yMin; - maxNumberOfUnits = k_maxNumberOfYGridUnits; - minNumberOfUnits = k_minNumberOfYGridUnits; - } - float units[3] = {k_oneUnit, k_twoUnit, k_fiveUnit}; - for (int k = 0; k < 3; k++) { - float unit = units[k]; - if (floorf(log10f(d/(unit*maxNumberOfUnits))) != floorf(log10f(d/(unit*minNumberOfUnits)))) { - b = floorf(log10f(d/(unit*minNumberOfUnits))); - a = unit; - } - } - if (axis == Axis::X) { - m_xGridUnit = a*powf(10,b); - } else { - m_yGridUnit = a*powf(10,b); - } -} - } diff --git a/apps/graph/graph/graph_window.h b/apps/graph/graph/graph_window.h index eb1b94835..5a94877f5 100644 --- a/apps/graph/graph/graph_window.h +++ b/apps/graph/graph/graph_window.h @@ -2,15 +2,12 @@ #define GRAPH_GRAPH_AXIS_INTERVAL_H #include "../function_store.h" +#include "../../curve_view_window.h" namespace Graph { -class GraphWindow { +class GraphWindow : public CurveViewWindow { public: - enum class Axis { - X, - Y - }; enum class Direction { Up, Left, @@ -18,13 +15,13 @@ public: Right }; GraphWindow(FunctionStore * functionStore); - float xMin(); - float xMax(); - float yMin(); - float yMax(); + float xMin() override; + float xMax() override; + float yMin() override; + float yMax() override; bool yAuto(); - float xGridUnit(); - float yGridUnit(); + float xGridUnit() override; + float yGridUnit() override; void setXMin(float f); void setXMax(float f); void setYMin(float f); @@ -44,14 +41,6 @@ public: void setDefault(); bool panToMakePointVisible(float x, float y, float xMargin, float yMargin); private: - constexpr static float k_minNumberOfXGridUnits = 7.0f; - constexpr static float k_maxNumberOfXGridUnits = 18.0f; - constexpr static float k_minNumberOfYGridUnits = 5.0f; - constexpr static float k_maxNumberOfYGridUnits = 13.0f; - constexpr static float k_oneUnit = 1.0f; - constexpr static float k_twoUnit = 2.0f; - constexpr static float k_fiveUnit = 5.0f; - void computeGridUnit(Axis axis); float m_xMin; float m_xMax; float m_yMin; diff --git a/apps/probability/law/binomial_law.cpp b/apps/probability/law/binomial_law.cpp index 4bfc8f0be..e5c494a6e 100644 --- a/apps/probability/law/binomial_law.cpp +++ b/apps/probability/law/binomial_law.cpp @@ -48,17 +48,12 @@ float BinomialLaw::xMax() { } float BinomialLaw::yMin() { - int maxAbscissa = m_parameter2 < 1.0f ? (m_parameter1+1)*m_parameter2 : m_parameter1; - float result = k_minMarginFactor*evaluateAtAbscissa(maxAbscissa); - if (result >= 0.0f || isnan(result)) { - result = k_minMarginFactor; - } - return result; + return 0.0f; } float BinomialLaw::yMax() { int maxAbscissa = m_parameter2 < 1.0f ? (m_parameter1+1)*m_parameter2 : m_parameter1; - float result = k_maxMarginFactor*evaluateAtAbscissa(maxAbscissa); + float result = evaluateAtAbscissa(maxAbscissa); if (result <= 0.0f || result == yMin() || isnan(result)) { result = yMin() + 1.0f; } diff --git a/apps/probability/law/exponential_law.cpp b/apps/probability/law/exponential_law.cpp index dd197ff23..c34eba7bf 100644 --- a/apps/probability/law/exponential_law.cpp +++ b/apps/probability/law/exponential_law.cpp @@ -42,11 +42,11 @@ float ExponentialLaw::xMax() { } float ExponentialLaw::yMin() { - return k_minMarginFactor*m_parameter1; + return 0.0f; } float ExponentialLaw::yMax() { - return k_maxMarginFactor*m_parameter1; + return m_parameter1; } float ExponentialLaw::evaluateAtAbscissa(float x) const { diff --git a/apps/probability/law/law.cpp b/apps/probability/law/law.cpp index 243057ffe..b05cdb0db 100644 --- a/apps/probability/law/law.cpp +++ b/apps/probability/law/law.cpp @@ -4,19 +4,8 @@ namespace Probability { -float Law::gridUnit() { - int a = 0; - int b = 0; - float d = xMax() - xMin(); - float units[3] = {k_oneUnit, k_twoUnit, k_fiveUnit}; - for (int k = 0; k < 3; k++) { - float unit = units[k]; - if (floorf(log10f(d/(unit*k_maxNumberOfXGridUnits))) != floorf(log10f(d/(unit*k_minNumberOfXGridUnits)))) { - b = floorf(log10f(d/(unit*k_minNumberOfXGridUnits))); - a = unit; - } - } - return a*powf(10,b); +float Law::xGridUnit() { + return computeGridUnit(Axis::X); } float Law::cumulativeDistributiveFunctionAtAbscissa(float x) const { diff --git a/apps/probability/law/law.h b/apps/probability/law/law.h index c7ef88b4e..662ee83d5 100644 --- a/apps/probability/law/law.h +++ b/apps/probability/law/law.h @@ -2,10 +2,11 @@ #define PROBABILITE_LAW_H #include +#include "../../curve_view_window.h" namespace Probability { -class Law { +class Law : public CurveViewWindow { public: enum class Type : uint8_t{ Binomial, @@ -18,11 +19,7 @@ public: virtual const char * title() = 0; virtual Type type() const = 0; virtual bool isContinuous() const = 0; - virtual float xMin() = 0; - virtual float yMin() = 0; - virtual float xMax() = 0; - virtual float yMax() = 0; - float gridUnit(); + float xGridUnit() override; virtual int numberOfParameter() = 0; virtual float parameterValueAtIndex(int index) = 0; virtual const char * parameterNameAtIndex(int index) = 0; @@ -36,13 +33,6 @@ public: virtual float cumulativeDistributiveInverseForProbability(float * probability); virtual float rightIntegralInverseForProbability(float * probability); protected: - constexpr static float k_minNumberOfXGridUnits = 7.0f; - constexpr static float k_maxNumberOfXGridUnits = 18.0f; - constexpr static float k_oneUnit = 1.0f; - constexpr static float k_twoUnit = 2.0f; - constexpr static float k_fiveUnit = 5.0f; - constexpr static float k_minMarginFactor = -0.2f; - constexpr static float k_maxMarginFactor = 1.2f; constexpr static int k_maxNumberOfOperations = 1000000; }; diff --git a/apps/probability/law/normal_law.cpp b/apps/probability/law/normal_law.cpp index 20eba841e..6103ba48d 100644 --- a/apps/probability/law/normal_law.cpp +++ b/apps/probability/law/normal_law.cpp @@ -55,17 +55,12 @@ float NormalLaw::xMax() { } float NormalLaw::yMin() { - float maxAbscissa = m_parameter1; - float result = k_minMarginFactor*evaluateAtAbscissa(maxAbscissa); - if (result >= 0.0f) { - result = k_minMarginFactor; - } - return result; + return 0.0f; } float NormalLaw::yMax() { float maxAbscissa = m_parameter1; - float result = k_maxMarginFactor*evaluateAtAbscissa(maxAbscissa); + float result = evaluateAtAbscissa(maxAbscissa); if (result <= 0.0f || result == yMin()) { result = yMin() + 1.0f; } diff --git a/apps/probability/law/poisson_law.cpp b/apps/probability/law/poisson_law.cpp index 19be3dc9d..17483d2b9 100644 --- a/apps/probability/law/poisson_law.cpp +++ b/apps/probability/law/poisson_law.cpp @@ -32,7 +32,7 @@ const char * PoissonLaw::parameterDefinitionAtIndex(int index) { } float PoissonLaw::xMin() { - return -1.0f; + return 0.0f; } float PoissonLaw::xMax() { @@ -41,13 +41,12 @@ float PoissonLaw::xMax() { } float PoissonLaw::yMin() { - int maxAbscissa = (int)m_parameter1; - return k_minMarginFactor*evaluateAtAbscissa(maxAbscissa); + return 0.0f; } float PoissonLaw::yMax() { int maxAbscissa = (int)m_parameter1; - return k_maxMarginFactor*evaluateAtAbscissa(maxAbscissa); + return evaluateAtAbscissa(maxAbscissa); } float PoissonLaw::evaluateAtAbscissa(float x) const { diff --git a/apps/probability/law/uniform_law.cpp b/apps/probability/law/uniform_law.cpp index d6069eff2..a80df4c81 100644 --- a/apps/probability/law/uniform_law.cpp +++ b/apps/probability/law/uniform_law.cpp @@ -48,11 +48,11 @@ float UniformLaw::xMax() { } float UniformLaw::yMin() { - return k_minMarginFactor*(1.0f/(m_parameter2-m_parameter1)); + return 0.0f; } float UniformLaw::yMax() { - return k_maxMarginFactor*(1.0f/(m_parameter2-m_parameter1)); + return (1.0f/(m_parameter2-m_parameter1)); } float UniformLaw::evaluateAtAbscissa(float t) const { diff --git a/apps/probability/law_curve_view.cpp b/apps/probability/law_curve_view.cpp index a86cd134b..7e8752d3a 100644 --- a/apps/probability/law_curve_view.cpp +++ b/apps/probability/law_curve_view.cpp @@ -12,6 +12,7 @@ LawCurveView::LawCurveView() : } void LawCurveView::setLaw(Law * law) { + setCurveViewWindow(law); m_law = law; } @@ -45,21 +46,6 @@ void LawCurveView::drawRect(KDContext * ctx, KDRect rect) const { } } -float LawCurveView::min(Axis axis) const { - assert(axis == Axis::Horizontal || axis == Axis::Vertical); - return (axis == Axis::Horizontal ? m_law->xMin() : m_law->yMin()); -} - -float LawCurveView::max(Axis axis) const { - assert(axis == Axis::Horizontal || axis == Axis::Vertical); - return (axis == Axis::Horizontal ? m_law->xMax() : m_law->yMax()); -} - -float LawCurveView::gridUnit(Axis axis) const { - assert(axis == Axis::Horizontal); - return m_law->gridUnit(); -} - char * LawCurveView::label(Axis axis, int index) const { assert(axis == Axis::Horizontal); return (char *)m_labels[index]; diff --git a/apps/probability/law_curve_view.h b/apps/probability/law_curve_view.h index 23ba954f8..9def316dd 100644 --- a/apps/probability/law_curve_view.h +++ b/apps/probability/law_curve_view.h @@ -18,9 +18,6 @@ public: void reload(); void drawRect(KDContext * ctx, KDRect rect) const override; protected: - float min(Axis axis) const override; - float max(Axis axis) const override; - float gridUnit(Axis axis) const override; char * label(Axis axis, int index) const override; private: char m_labels[k_maxNumberOfXLabels][Constant::FloatBufferSizeInScientificMode]; diff --git a/apps/statistics/app.cpp b/apps/statistics/app.cpp index 75b547900..f96d28ec8 100644 --- a/apps/statistics/app.cpp +++ b/apps/statistics/app.cpp @@ -12,7 +12,7 @@ App::App(Container * container) : m_boxController(BoxController(&m_boxAlternateEmptyViewController)), m_boxAlternateEmptyViewController(AlternateEmptyViewController(nullptr, &m_boxController, &m_boxController)), m_boxStackViewController(StackViewController(&m_tabViewController, &m_boxAlternateEmptyViewController)), - m_histogramController(HistogramController(&m_histogramHeader, &m_histogramHeader)), + m_histogramController(HistogramController(&m_histogramHeader, &m_histogramHeader, &m_data)), m_histogramHeader(HeaderViewController(&m_histogramAlternateEmptyViewController, &m_histogramController, &m_histogramController)), m_histogramAlternateEmptyViewController(AlternateEmptyViewController(nullptr, &m_histogramHeader, &m_histogramController)), m_histogramStackViewController(StackViewController(&m_tabViewController, &m_histogramAlternateEmptyViewController)), diff --git a/apps/statistics/data.cpp b/apps/statistics/data.cpp index 38820b5c6..db1c5667b 100644 --- a/apps/statistics/data.cpp +++ b/apps/statistics/data.cpp @@ -1,4 +1,5 @@ #include "data.h" +#include namespace Statistics { @@ -22,7 +23,7 @@ int Data::sizeAtIndex(int index) { void Data::setValueAtIndex(float value, int index) { m_values[index] = value; if (index >= m_numberOfPairs) { - m_sizes[index] = 0; + m_sizes[index] = 1; m_numberOfPairs++; } } @@ -35,4 +36,66 @@ void Data::setSizeAtIndex(int size, int index) { } } +void Data::deletePairAtIndex(int index) { + m_numberOfPairs--; + for (int k = index; k < m_numberOfPairs; k++) { + m_values[k] = m_values[k+1]; + m_sizes[k] = m_sizes[k+1]; + } + m_values[m_numberOfPairs] = 0.0f; + m_sizes[m_numberOfPairs] = 1; +} + +int Data::sizeOfValuesBetweenBounds(float lowerBound, float upperBound) const { + int result = 0; + for (int k = 0; k < m_numberOfPairs; k++) { + if (m_values[k] < upperBound && lowerBound <= m_values[k]) { + result += m_sizes[k]; + } + } + return result; +} + +float Data::xMin() { + float valueMin = FLT_MAX; + for (int k = 0; k < m_numberOfPairs; k++) { + if (m_values[k] < valueMin) { + valueMin = m_values[k]; + } + } + return valueMin; +} + +float Data::xMax() { + float valueMax = -FLT_MAX; + for (int k = 0; k < m_numberOfPairs; k++) { + if (m_values[k] > valueMax) { + valueMax = m_values[k]; + } + } + float valueMin = xMin(); + if (valueMax - valueMin > k_maxRangeValue) { + valueMax = valueMin + 10.0f; + } + return valueMax; +} + +float Data::yMin() { + return 0.0f; +} + +float Data::yMax() { + float sizeMax = -FLT_MAX; + for (int k = 0; k < m_numberOfPairs; k++) { + if (m_sizes[k] > sizeMax) { + sizeMax = m_sizes[k]; + } + } + return sizeMax; +} + +float Data::xGridUnit() { + return computeGridUnit(Axis::X); +} + } diff --git a/apps/statistics/data.h b/apps/statistics/data.h index 668729ab7..61b6bdc40 100644 --- a/apps/statistics/data.h +++ b/apps/statistics/data.h @@ -1,9 +1,11 @@ #ifndef STATISTICS_DATA_H #define STATISTICS_DATA_H +#include "../curve_view_window.h" + namespace Statistics { -class Data { +class Data : public CurveViewWindow { public: Data(); // Delete the implicit copy constructor: the object is heavy @@ -13,9 +15,18 @@ public: int sizeAtIndex(int index); void setValueAtIndex(float value, int index); void setSizeAtIndex(int size, int index); + void deletePairAtIndex(int index); + int sizeOfValuesBetweenBounds(float lowerBound, float upperBound) const; + float xMin() override; + // if the range of value is to wide, value max returns valueMin + 10 + float xMax() override; + float yMin() override; + float yMax() override; + float xGridUnit() override; // TODO: decide the max number of elements after optimization constexpr static int k_maxNumberOfPairs = 500; private: + constexpr static int k_maxRangeValue = 320; int m_sizes[k_maxNumberOfPairs]; float m_values[k_maxNumberOfPairs]; int m_numberOfPairs; diff --git a/apps/statistics/data_controller.cpp b/apps/statistics/data_controller.cpp index 98f888d1b..f14ff2da6 100644 --- a/apps/statistics/data_controller.cpp +++ b/apps/statistics/data_controller.cpp @@ -79,6 +79,18 @@ bool DataController::handleEvent(Ion::Events::Event event) { app()->setFirstResponder(tabController()); return true; } + if (event == Ion::Events::Clear) { + if (m_selectableTableView.selectedColumn() == 0) { + m_data->deletePairAtIndex(m_selectableTableView.selectedRow()-1); + m_selectableTableView.reloadData(); + } else { + m_data->setSizeAtIndex(1, m_selectableTableView.selectedRow()-1); + EvenOddEditableTextCell * myCell = (EvenOddEditableTextCell *)m_selectableTableView.cellAtLocation(m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow()); + willDisplayCellAtLocation(myCell, m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow()); + myCell->editableTextCell()->textField()->handleEvent(Ion::Events::OK); + } + return true; + } return false; } diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 0e9dcf5e3..9f0f1ace4 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -2,12 +2,13 @@ namespace Statistics { -HistogramController::HistogramController(Responder * parentResponder, HeaderViewController * headerViewController) : +HistogramController::HistogramController(Responder * parentResponder, HeaderViewController * headerViewController, Data * data) : ViewController(parentResponder), HeaderViewDelegate(headerViewController), m_view(SolidColorView(KDColorGreen)), m_settingButton(Button(this, "Reglages de l'histogramme",Invocation([](void * context, void * sender) {}, this))), - m_selectedBin(0) + m_selectedBin(0), + m_data(data) { } @@ -49,11 +50,14 @@ Button * HistogramController::buttonAtIndex(int index) { } bool HistogramController::isEmpty() { + if (m_data->numberOfPairs() == 0) { + return true; + } return false; } const char * HistogramController::emptyMessage() { - return "Aucune donnée à tracer"; + return "Aucune donnee à tracer"; } Responder * HistogramController::defaultController() { diff --git a/apps/statistics/histogram_controller.h b/apps/statistics/histogram_controller.h index c0fed2498..0f7726ad8 100644 --- a/apps/statistics/histogram_controller.h +++ b/apps/statistics/histogram_controller.h @@ -2,13 +2,14 @@ #define STATISTICS_HISTOGRAM_CONTROLLER_H #include +#include "data.h" namespace Statistics { class HistogramController : public ViewController, public HeaderViewDelegate, public AlternateEmptyViewDelegate { public: - HistogramController(Responder * parentResponder, HeaderViewController * headerViewController); + HistogramController(Responder * parentResponder, HeaderViewController * headerViewController, Data * m_data); const char * title() const override; View * view() override; bool handleEvent(Ion::Events::Event event) override; @@ -24,6 +25,7 @@ private: SolidColorView m_view; Button m_settingButton; int m_selectedBin; + Data * m_data; }; } diff --git a/escher/include/escher/editable_text_cell.h b/escher/include/escher/editable_text_cell.h index f4005ad9f..6550f3944 100644 --- a/escher/include/escher/editable_text_cell.h +++ b/escher/include/escher/editable_text_cell.h @@ -10,7 +10,7 @@ class EditableTextCell : public TableViewCell, public Responder { public: EditableTextCell(Responder * parentResponder, TextFieldDelegate * delegate, char * draftTextBuffer, float horizontalAlignment = 0.0f, float verticalAlignment = 0.5f, KDColor textColor = KDColorBlack, KDColor = KDColorWhite); - TextField * textfield(); + TextField * textField(); void reloadCell() override; const char * text() const; void setText(const char * textContent); diff --git a/escher/include/escher/even_odd_editable_text_cell.h b/escher/include/escher/even_odd_editable_text_cell.h index 421256de0..e63331567 100644 --- a/escher/include/escher/even_odd_editable_text_cell.h +++ b/escher/include/escher/even_odd_editable_text_cell.h @@ -8,6 +8,7 @@ class EvenOddEditableTextCell : public EvenOddCell, public Responder { public: EvenOddEditableTextCell(Responder * parentResponder, TextFieldDelegate * delegate, char * draftTextBuffer); + EditableTextCell * editableTextCell(); void reloadCell() override; const char * text() const; void setText(const char * textContent); diff --git a/escher/src/editable_text_cell.cpp b/escher/src/editable_text_cell.cpp index 5e4bf5fd0..b9bcdd3f7 100644 --- a/escher/src/editable_text_cell.cpp +++ b/escher/src/editable_text_cell.cpp @@ -11,7 +11,7 @@ EditableTextCell::EditableTextCell(Responder * parentResponder, TextFieldDelegat { } -TextField * EditableTextCell::textfield() { +TextField * EditableTextCell::textField() { return &m_textField; } diff --git a/escher/src/even_odd_editable_text_cell.cpp b/escher/src/even_odd_editable_text_cell.cpp index 4a75b3e27..bb7c28637 100644 --- a/escher/src/even_odd_editable_text_cell.cpp +++ b/escher/src/even_odd_editable_text_cell.cpp @@ -9,9 +9,13 @@ EvenOddEditableTextCell::EvenOddEditableTextCell(Responder * parentResponder, Te { } +EditableTextCell * EvenOddEditableTextCell::editableTextCell() { + return &m_editableCell; +} + void EvenOddEditableTextCell::reloadCell() { EvenOddCell::reloadCell(); - m_editableCell.textfield()->setBackgroundColor(backgroundColor()); + m_editableCell.textField()->setBackgroundColor(backgroundColor()); } const char * EvenOddEditableTextCell::text() const {