mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-26 17:20:53 +01:00
[apps] Modular reimplementation of application models (stores and
ranges) and of curve views/curve view controllers. Change-Id: If4ca9bf1bec024917ef540a3fc7baefa8700f7ba
This commit is contained in:
@@ -8,23 +8,23 @@ include apps/statistics/Makefile
|
||||
|
||||
app_objs += $(addprefix apps/,\
|
||||
apps_container.o\
|
||||
constant.o\
|
||||
banner_view.o\
|
||||
constant.o\
|
||||
cursor_view.o\
|
||||
curve_view.o\
|
||||
curve_view_with_banner.o\
|
||||
curve_view_with_banner_and_cursor.o\
|
||||
curve_view_with_banner_and_cursor_controller.o\
|
||||
curve_view_window.o\
|
||||
curve_view_window_with_cursor.o\
|
||||
curve_view_cursor.o\
|
||||
curve_view_range.o\
|
||||
editable_cell_table_view_controller.o\
|
||||
expression_text_field_delegate.o\
|
||||
float_pair_store.o\
|
||||
float_parameter_controller.o\
|
||||
interactive_curve_view_controller.o\
|
||||
interactive_curve_view_range.o\
|
||||
main.o\
|
||||
node.o\
|
||||
node_list_view_controller.o\
|
||||
node_navigation_controller.o\
|
||||
range_parameter_controller.o\
|
||||
store_controller.o\
|
||||
store_parameter_controller.o\
|
||||
text_field_delegate_app.o\
|
||||
@@ -33,7 +33,6 @@ app_objs += $(addprefix apps/,\
|
||||
toolbox_node.o\
|
||||
variable_box_controller.o\
|
||||
variable_box_leaf_cell.o\
|
||||
window_parameter_controller.o\
|
||||
zoom_parameter_controller.o\
|
||||
)
|
||||
|
||||
|
||||
@@ -6,11 +6,16 @@
|
||||
#include <string.h>
|
||||
|
||||
constexpr KDColor CurveView::k_axisColor;
|
||||
constexpr KDColor CurveView::k_gridColor;
|
||||
|
||||
CurveView::CurveView(CurveViewWindow * curveViewWindow, float topMarginFactor,
|
||||
float rightMarginFactor, float bottomMarginFactor, float leftMarginFactor) :
|
||||
CurveView::CurveView(CurveViewRange * curveViewRange, CurveViewCursor * curveViewCursor, View * bannerView,
|
||||
View * cursorView, float topMarginFactor, float rightMarginFactor, float bottomMarginFactor,
|
||||
float leftMarginFactor) :
|
||||
View(),
|
||||
m_curveViewWindow(curveViewWindow),
|
||||
m_curveViewRange(curveViewRange),
|
||||
m_curveViewCursor(curveViewCursor),
|
||||
m_bannerView(bannerView),
|
||||
m_cursorView(cursorView),
|
||||
m_topMarginFactor(topMarginFactor),
|
||||
m_bottomMarginFactor(bottomMarginFactor),
|
||||
m_leftMarginFactor(leftMarginFactor),
|
||||
@@ -18,32 +23,61 @@ CurveView::CurveView(CurveViewWindow * curveViewWindow, float topMarginFactor,
|
||||
{
|
||||
}
|
||||
|
||||
void CurveView::setCurveViewWindow(CurveViewWindow * curveViewWindow) {
|
||||
m_curveViewWindow = curveViewWindow;
|
||||
}
|
||||
|
||||
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);
|
||||
layoutSubviews();
|
||||
}
|
||||
}
|
||||
|
||||
bool CurveView::isMainViewSelected() const {
|
||||
return m_mainViewSelected;
|
||||
}
|
||||
|
||||
void CurveView::selectMainView(bool mainViewSelected) {
|
||||
if (m_mainViewSelected != mainViewSelected) {
|
||||
m_mainViewSelected = mainViewSelected;
|
||||
reloadSelection();
|
||||
layoutSubviews();
|
||||
}
|
||||
}
|
||||
|
||||
void CurveView::setCurveViewRange(CurveViewRange * curveViewRange) {
|
||||
m_curveViewRange = curveViewRange;
|
||||
}
|
||||
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
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;
|
||||
}
|
||||
|
||||
float CurveView::gridUnit(Axis axis) const {
|
||||
return (axis == Axis::Horizontal ? m_curveViewWindow->xGridUnit() : m_curveViewWindow->yGridUnit());
|
||||
return (axis == Axis::Horizontal ? m_curveViewRange->xGridUnit() : m_curveViewRange->yGridUnit());
|
||||
}
|
||||
|
||||
KDCoordinate CurveView::pixelLength(Axis axis) const {
|
||||
@@ -62,14 +96,6 @@ float CurveView::floatToPixel(Axis axis, float f) const {
|
||||
return pixelLength(axis)*fraction;
|
||||
}
|
||||
|
||||
int CurveView::numberOfLabels(Axis axis) const {
|
||||
Axis otherAxis = axis == Axis::Horizontal ? Axis::Vertical : Axis::Horizontal;
|
||||
if (min(otherAxis) > 0.0f || max(otherAxis) < 0.0f) {
|
||||
return 0;
|
||||
}
|
||||
return ceilf((max(axis) - min(axis))/(2*gridUnit(axis)));
|
||||
}
|
||||
|
||||
void CurveView::computeLabels(Axis axis) {
|
||||
char buffer[Constant::FloatBufferSizeInDecimalMode];
|
||||
float step = gridUnit(axis);
|
||||
@@ -173,6 +199,11 @@ void CurveView::drawGridLines(KDContext * ctx, KDRect rect, Axis axis, float ste
|
||||
}
|
||||
}
|
||||
|
||||
void CurveView::drawGrid(KDContext * ctx, KDRect rect) const {
|
||||
drawGridLines(ctx, rect, Axis::Horizontal, m_curveViewRange->xGridUnit(), k_gridColor);
|
||||
drawGridLines(ctx, rect, Axis::Vertical, m_curveViewRange->yGridUnit(), k_gridColor);
|
||||
}
|
||||
|
||||
void CurveView::drawAxes(KDContext * ctx, KDRect rect, Axis axis) const {
|
||||
drawLine(ctx, rect, axis, 0.0f, k_axisColor, 2);
|
||||
}
|
||||
@@ -271,31 +302,12 @@ void CurveView::drawHistogram(KDContext * ctx, KDRect rect, Model * model, float
|
||||
}
|
||||
}
|
||||
|
||||
void CurveView::stampAtLocation(KDContext * ctx, KDRect rect, float pxf, float pyf, KDColor color) const {
|
||||
// We avoid drawing when no part of the stamp is visible
|
||||
if (pyf < -stampSize || pyf > pixelLength(Axis::Vertical)+stampSize) {
|
||||
return;
|
||||
int CurveView::numberOfLabels(Axis axis) const {
|
||||
Axis otherAxis = axis == Axis::Horizontal ? Axis::Vertical : Axis::Horizontal;
|
||||
if (min(otherAxis) > 0.0f || max(otherAxis) < 0.0f) {
|
||||
return 0;
|
||||
}
|
||||
KDCoordinate px = pxf;
|
||||
KDCoordinate py = pyf;
|
||||
KDRect stampRect(px-circleDiameter/2, py-circleDiameter/2, stampSize, stampSize);
|
||||
if (!rect.intersects(stampRect)) {
|
||||
return;
|
||||
}
|
||||
uint8_t shiftedMask[stampSize][stampSize];
|
||||
KDColor workingBuffer[stampSize*stampSize];
|
||||
float dx = pxf - floorf(pxf);
|
||||
float dy = pyf - floorf(pyf);
|
||||
/* TODO: this could be optimized by precomputing 10 or 100 shifted masks. The
|
||||
* dx and dy would be rounded to one tenth or one hundredth to choose the
|
||||
* right shifted mask. */
|
||||
for (int i=0; i<stampSize; i++) {
|
||||
for (int j=0; j<stampSize; j++) {
|
||||
shiftedMask[i][j] = dx * (stampMask[i][j]*dy+stampMask[i+1][j]*(1.0f-dy))
|
||||
+ (1.0f-dx) * (stampMask[i][j+1]*dy + stampMask[i+1][j+1]*(1.0f-dy));
|
||||
}
|
||||
}
|
||||
ctx->blendRectWithMask(stampRect, color, (const uint8_t *)shiftedMask, workingBuffer);
|
||||
return ceilf((max(axis) - min(axis))/(2*gridUnit(axis)));
|
||||
}
|
||||
|
||||
float CurveView::evaluateModelWithParameter(Model * curve, float t) const {
|
||||
@@ -350,3 +362,63 @@ void CurveView::straightJoinDots(KDContext * ctx, KDRect rect, float pxf, float
|
||||
}
|
||||
straightJoinDots(ctx, rect, puf, pvf, pxf, pyf, color);
|
||||
}
|
||||
|
||||
void CurveView::stampAtLocation(KDContext * ctx, KDRect rect, float pxf, float pyf, KDColor color) const {
|
||||
// We avoid drawing when no part of the stamp is visible
|
||||
if (pyf < -stampSize || pyf > pixelLength(Axis::Vertical)+stampSize) {
|
||||
return;
|
||||
}
|
||||
KDCoordinate px = pxf;
|
||||
KDCoordinate py = pyf;
|
||||
KDRect stampRect(px-circleDiameter/2, py-circleDiameter/2, stampSize, stampSize);
|
||||
if (!rect.intersects(stampRect)) {
|
||||
return;
|
||||
}
|
||||
uint8_t shiftedMask[stampSize][stampSize];
|
||||
KDColor workingBuffer[stampSize*stampSize];
|
||||
float dx = pxf - floorf(pxf);
|
||||
float dy = pyf - floorf(pyf);
|
||||
/* TODO: this could be optimized by precomputing 10 or 100 shifted masks. The
|
||||
* dx and dy would be rounded to one tenth or one hundredth to choose the
|
||||
* right shifted mask. */
|
||||
for (int i=0; i<stampSize; i++) {
|
||||
for (int j=0; j<stampSize; j++) {
|
||||
shiftedMask[i][j] = dx * (stampMask[i][j]*dy+stampMask[i+1][j]*(1.0f-dy))
|
||||
+ (1.0f-dx) * (stampMask[i][j+1]*dy + stampMask[i+1][j+1]*(1.0f-dy));
|
||||
}
|
||||
}
|
||||
ctx->blendRectWithMask(stampRect, color, (const uint8_t *)shiftedMask, workingBuffer);
|
||||
}
|
||||
|
||||
void CurveView::layoutSubviews() {
|
||||
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()));
|
||||
KDRect cursorFrame(xCursorPixelPosition - k_cursorSize/2, yCursorPixelPosition - k_cursorSize/2, k_cursorSize, k_cursorSize);
|
||||
if (!m_mainViewSelected) {
|
||||
cursorFrame = KDRectZero;
|
||||
}
|
||||
m_cursorView->setFrame(cursorFrame);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int CurveView::numberOfSubviews() const {
|
||||
return (m_bannerView != nullptr) + (m_cursorView != nullptr);
|
||||
};
|
||||
|
||||
View * CurveView::subviewAtIndex(int index) {
|
||||
assert(index >= 0 && index < 2);
|
||||
if (index == 0) {
|
||||
return m_bannerView;
|
||||
}
|
||||
return m_cursorView;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
#include <escher.h>
|
||||
#include <poincare.h>
|
||||
#include "curve_view_window.h"
|
||||
#include "curve_view_range.h"
|
||||
#include "curve_view_cursor.h"
|
||||
#include <math.h>
|
||||
|
||||
class CurveView : public View {
|
||||
@@ -13,15 +14,26 @@ public:
|
||||
Horizontal = 0,
|
||||
Vertical = 1
|
||||
};
|
||||
CurveView(CurveViewWindow * curveViewWindow = nullptr, float topMarginFactor = 0.0f,
|
||||
CurveView(CurveViewRange * curveViewRange = nullptr, CurveViewCursor * curveViewCursor = nullptr,
|
||||
View * bannerView = nullptr, View * cursorView = nullptr, float topMarginFactor = 0.0f,
|
||||
float rightMarginFactor = 0.0f, float bottomMarginFactor = 0.0f, float leftMarginFactor = 0.0f);
|
||||
virtual void reload();
|
||||
// Reload methods
|
||||
void reload();
|
||||
virtual void reloadSelection();
|
||||
// When the main view is selected, the banner view is visible
|
||||
bool isMainViewSelected() const;
|
||||
void selectMainView(bool mainViewSelected);
|
||||
|
||||
protected:
|
||||
void setCurveViewRange(CurveViewRange * curveViewRange);
|
||||
|
||||
// Drawing methods
|
||||
constexpr static KDColor k_axisColor = KDColor::RGB24(0x000000);
|
||||
constexpr static KDColor k_gridColor = KDColor::RGB24(0xEEEEEE);
|
||||
constexpr static KDCoordinate k_labelMargin = 4;
|
||||
constexpr static int k_maxNumberOfXLabels = 18;
|
||||
constexpr static int k_maxNumberOfYLabels = 13;
|
||||
void setCurveViewWindow(CurveViewWindow * curveViewWindow);
|
||||
constexpr static KDCoordinate k_cursorSize = 9;
|
||||
/* 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;
|
||||
@@ -38,12 +50,14 @@ protected:
|
||||
KDColor color, KDCoordinate thickness = 1) const;
|
||||
void drawDot(KDContext * ctx, KDRect rect, float x, float y, KDColor color, KDSize size) const;
|
||||
void drawGridLines(KDContext * ctx, KDRect rect, Axis axis, float step, KDColor color) const;
|
||||
void drawGrid(KDContext * ctx, KDRect rect) const;
|
||||
void drawAxes(KDContext * ctx, KDRect rect, Axis axis) const;
|
||||
void drawCurve(KDContext * ctx, KDRect rect, Model * curve, KDColor color, bool colorUnderCurve = false, float colorLowerBound = 0.0f, float colorUpperBound = 0.0f, bool continuously = false) const;
|
||||
void drawHistogram(KDContext * ctx, KDRect rect, Model * model, float firstBarAbscissa, float barWidth,
|
||||
bool fillBar, KDColor defaultColor, KDColor highlightColor, float highlightLowerBound = INFINITY, float highlightUpperBound = -INFINITY) const;
|
||||
void computeLabels(Axis axis);
|
||||
void drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin) const;
|
||||
|
||||
private:
|
||||
constexpr static int k_externRectMargin = 1;
|
||||
int numberOfLabels(Axis axis) const;
|
||||
@@ -57,7 +71,16 @@ private:
|
||||
* function shifts the stamp (by blending adjacent pixel colors) to draw with
|
||||
* anti alising. */
|
||||
void stampAtLocation(KDContext * ctx, KDRect rect, float pxf, float pyf, KDColor color) const;
|
||||
CurveViewWindow * m_curveViewWindow;
|
||||
void layoutSubviews() override;
|
||||
int numberOfSubviews() const override;
|
||||
View * subviewAtIndex(int index) override;
|
||||
/* m_curveViewRange has to be non null but the cursor model, the banner and
|
||||
* cursor views may be nullptr if not needed. */
|
||||
CurveViewRange * m_curveViewRange;
|
||||
CurveViewCursor * m_curveViewCursor;
|
||||
View * m_bannerView;
|
||||
View * m_cursorView;
|
||||
bool m_mainViewSelected;
|
||||
float m_topMarginFactor;
|
||||
float m_bottomMarginFactor;
|
||||
float m_leftMarginFactor;
|
||||
|
||||
21
apps/curve_view_cursor.cpp
Normal file
21
apps/curve_view_cursor.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "curve_view_cursor.h"
|
||||
#include <math.h>
|
||||
|
||||
CurveViewCursor::CurveViewCursor() :
|
||||
m_x(NAN),
|
||||
m_y(NAN)
|
||||
{
|
||||
}
|
||||
|
||||
float CurveViewCursor::x() {
|
||||
return m_x;
|
||||
}
|
||||
|
||||
float CurveViewCursor::y() {
|
||||
return m_y;
|
||||
}
|
||||
|
||||
void CurveViewCursor::moveTo(float x, float y) {
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
}
|
||||
16
apps/curve_view_cursor.h
Normal file
16
apps/curve_view_cursor.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef APPS_CURVE_VIEW_CURSOR_H
|
||||
#define APPS_CURVE_VIEW_CURSOR_H
|
||||
|
||||
|
||||
class CurveViewCursor {
|
||||
public:
|
||||
CurveViewCursor();
|
||||
float x();
|
||||
float y();
|
||||
void moveTo(float x, float y);
|
||||
protected:
|
||||
float m_x;
|
||||
float m_y;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "curve_view_window.h"
|
||||
#include "curve_view_range.h"
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
float CurveViewWindow::yGridUnit() {
|
||||
float CurveViewRange::yGridUnit() {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float CurveViewWindow::computeGridUnit(Axis axis, float min, float max) {
|
||||
float CurveViewRange::computeGridUnit(Axis axis, float min, float max) {
|
||||
int a = 0;
|
||||
int b = 0;
|
||||
float d = max - min;
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef APPS_CURVE_VIEW_WINDOW_H
|
||||
#define APPS_CURVE_VIEW_WINDOW_H
|
||||
#ifndef APPS_CURVE_VIEW_RANGE_H
|
||||
#define APPS_CURVE_VIEW_RANGE_H
|
||||
|
||||
class CurveViewWindow {
|
||||
class CurveViewRange {
|
||||
public:
|
||||
enum class Axis {
|
||||
X,
|
||||
@@ -13,6 +13,7 @@ public:
|
||||
virtual float yMax() = 0;
|
||||
virtual float xGridUnit() = 0;
|
||||
virtual float yGridUnit();
|
||||
float computeGridUnit(Axis axis, float min, float max);
|
||||
protected:
|
||||
constexpr static float k_minNumberOfXGridUnits = 7.0f;
|
||||
constexpr static float k_maxNumberOfXGridUnits = 18.0f;
|
||||
@@ -21,7 +22,6 @@ protected:
|
||||
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, float min, float max);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,197 +0,0 @@
|
||||
#include "curve_view_window_with_cursor.h"
|
||||
#include <math.h>
|
||||
|
||||
CurveViewWindowWithCursor::CurveViewWindowWithCursor() :
|
||||
m_xCursorPosition(NAN),
|
||||
m_yCursorPosition(NAN),
|
||||
m_xMin(-10.0f),
|
||||
m_xMax(10.0f),
|
||||
m_yMin(-10.0f),
|
||||
m_yMax(10.0f),
|
||||
m_yAuto(true),
|
||||
m_xGridUnit(2.0f),
|
||||
m_yGridUnit(2.0f)
|
||||
{
|
||||
}
|
||||
|
||||
float CurveViewWindowWithCursor::xCursorPosition() {
|
||||
return m_xCursorPosition;
|
||||
}
|
||||
|
||||
float CurveViewWindowWithCursor::yCursorPosition() {
|
||||
return m_yCursorPosition;
|
||||
}
|
||||
|
||||
float CurveViewWindowWithCursor::xMin() {
|
||||
return m_xMin;
|
||||
}
|
||||
|
||||
float CurveViewWindowWithCursor::xMax() {
|
||||
return m_xMax;
|
||||
}
|
||||
|
||||
float CurveViewWindowWithCursor::yMin() {
|
||||
return m_yMin;
|
||||
}
|
||||
|
||||
float CurveViewWindowWithCursor::yMax() {
|
||||
return m_yMax;
|
||||
}
|
||||
|
||||
bool CurveViewWindowWithCursor::yAuto() {
|
||||
return m_yAuto;
|
||||
}
|
||||
|
||||
float CurveViewWindowWithCursor::xGridUnit() {
|
||||
return m_xGridUnit;
|
||||
}
|
||||
|
||||
float CurveViewWindowWithCursor::yGridUnit() {
|
||||
return m_yGridUnit;
|
||||
}
|
||||
|
||||
void CurveViewWindowWithCursor::setXMin(float xMin) {
|
||||
m_xMin = xMin;
|
||||
computeYaxis();
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
initCursorPosition();
|
||||
}
|
||||
|
||||
void CurveViewWindowWithCursor::setXMax(float xMax) {
|
||||
m_xMax = xMax;
|
||||
computeYaxis();
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
initCursorPosition();
|
||||
}
|
||||
|
||||
void CurveViewWindowWithCursor::setYMin(float yMin) {
|
||||
m_yMin = yMin;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
initCursorPosition();
|
||||
}
|
||||
|
||||
void CurveViewWindowWithCursor::setYMax(float yMax) {
|
||||
m_yMax = yMax;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
initCursorPosition();
|
||||
}
|
||||
|
||||
void CurveViewWindowWithCursor::setYAuto(bool yAuto) {
|
||||
m_yAuto = yAuto;
|
||||
computeYaxis();
|
||||
}
|
||||
|
||||
void CurveViewWindowWithCursor::zoom(float ratio) {
|
||||
float xMin = m_xMin;
|
||||
float xMax = m_xMax;
|
||||
float yMin = m_yMin;
|
||||
float yMax = m_yMax;
|
||||
m_xMin = (xMax+xMin)/2.0f - ratio*fabsf(xMax-xMin);
|
||||
m_xMax = (xMax+xMin)/2.0f + ratio*fabsf(xMax-xMin);
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
m_yAuto = false;
|
||||
m_yMin = (yMax+yMin)/2.0f - ratio*fabsf(yMax-yMin);
|
||||
m_yMax = (yMax+yMin)/2.0f + ratio*fabsf(yMax-yMin);
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
initCursorPosition();
|
||||
}
|
||||
|
||||
void CurveViewWindowWithCursor::translateWindow(Direction direction) {
|
||||
m_yAuto = false;
|
||||
if (direction == Direction::Up) {
|
||||
m_yMin = m_yMin + m_yGridUnit;
|
||||
m_yMax = m_yMax + m_yGridUnit;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
}
|
||||
if (direction == Direction::Down) {
|
||||
m_yMin = m_yMin - m_yGridUnit;
|
||||
m_yMax = m_yMax - m_yGridUnit;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
}
|
||||
if (direction == Direction::Left) {
|
||||
m_xMin = m_xMin - m_xGridUnit;
|
||||
m_xMax = m_xMax - m_xGridUnit;
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
}
|
||||
if (direction == Direction::Right) {
|
||||
m_xMin = m_xMin + m_xGridUnit;
|
||||
m_xMax = m_xMax + m_xGridUnit;
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
}
|
||||
initCursorPosition();
|
||||
}
|
||||
|
||||
void CurveViewWindowWithCursor::roundAbscissa() {
|
||||
float xMin = m_xMin;
|
||||
float xMax = m_xMax;
|
||||
m_xMin = roundf((xMin+xMax)/2) - 160.0f;
|
||||
m_xMax = roundf((xMin+xMax)/2) + 159.0f;
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
computeYaxis();
|
||||
initCursorPosition();
|
||||
}
|
||||
|
||||
void CurveViewWindowWithCursor::normalize() {
|
||||
float xMin = m_xMin;
|
||||
float xMax = m_xMax;
|
||||
float yMin = m_yMin;
|
||||
float yMax = m_yMax;
|
||||
m_xMin = (xMin+xMax)/2 - 5.3f;
|
||||
m_xMax = (xMin+xMax)/2 + 5.3f;
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
m_yAuto = false;
|
||||
m_yMin = (yMin+yMax)/2 - 3.1f;
|
||||
m_yMax = (yMin+yMax)/2 + 3.1f;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
initCursorPosition();
|
||||
}
|
||||
|
||||
bool CurveViewWindowWithCursor::panToMakePointVisible(float x, float y, float xMargin, float yMargin) {
|
||||
bool windowMoved = false;
|
||||
float xRange = m_xMax - m_xMin;
|
||||
float yRange = m_yMax - m_yMin;
|
||||
if (x < m_xMin + xMargin) {
|
||||
m_xMin = x - xMargin;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
m_yMin = m_yMax - yRange;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
m_yAuto = false;
|
||||
windowMoved = true;
|
||||
}
|
||||
return windowMoved;
|
||||
}
|
||||
|
||||
void CurveViewWindowWithCursor::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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#include "curve_view_with_banner.h"
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
CurveViewWithBanner::CurveViewWithBanner(CurveViewWindow * curveViewWindow, BannerView * bannerView,
|
||||
float topMarginFactor, float rightMarginFactor, float bottomMarginFactor, float leftMarginFactor) :
|
||||
CurveView(curveViewWindow, topMarginFactor, rightMarginFactor, bottomMarginFactor, leftMarginFactor),
|
||||
m_mainViewSelected(true),
|
||||
m_bannerView(bannerView)
|
||||
{
|
||||
}
|
||||
|
||||
void CurveViewWithBanner::reload() {
|
||||
markRectAsDirty(bounds());
|
||||
computeLabels(Axis::Horizontal);
|
||||
}
|
||||
|
||||
bool CurveViewWithBanner::isMainViewSelected() {
|
||||
return m_mainViewSelected;
|
||||
}
|
||||
|
||||
void CurveViewWithBanner::selectMainView(bool mainViewSelected) {
|
||||
if (m_mainViewSelected != mainViewSelected) {
|
||||
m_mainViewSelected = mainViewSelected;
|
||||
reloadSelection();
|
||||
layoutSubviews();
|
||||
}
|
||||
}
|
||||
|
||||
int CurveViewWithBanner::numberOfSubviews() const {
|
||||
return 1;
|
||||
};
|
||||
|
||||
View * CurveViewWithBanner::subviewAtIndex(int index) {
|
||||
assert(index == 0);
|
||||
return m_bannerView;
|
||||
}
|
||||
|
||||
void CurveViewWithBanner::layoutSubviews() {
|
||||
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);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#ifndef APPS_CURVE_VIEW_WITH_BANNER_H
|
||||
#define APPS_CURVE_VIEW_WITH_BANNER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "curve_view.h"
|
||||
#include "banner_view.h"
|
||||
|
||||
class CurveViewWithBanner : public CurveView {
|
||||
public:
|
||||
CurveViewWithBanner(CurveViewWindow * curveViewWindow = nullptr, BannerView * bannerView = nullptr,
|
||||
float topMarginFactor = 0.0f, float rightMarginFactor = 0.0f, float bottomMarginFactor = 0.0f, float leftMarginFactor = 0.0f);
|
||||
virtual void reloadSelection() = 0;
|
||||
void reload() override;
|
||||
bool isMainViewSelected();
|
||||
void selectMainView(bool mainViewSelected);
|
||||
protected:
|
||||
void layoutSubviews() override;
|
||||
bool m_mainViewSelected;
|
||||
BannerView * m_bannerView;
|
||||
private:
|
||||
int numberOfSubviews() const override;
|
||||
View * subviewAtIndex(int index) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,55 +0,0 @@
|
||||
#include "curve_view_with_banner_and_cursor.h"
|
||||
#include <assert.h>
|
||||
|
||||
constexpr KDColor CurveViewWithBannerAndCursor::k_gridColor;
|
||||
|
||||
CurveViewWithBannerAndCursor::CurveViewWithBannerAndCursor(CurveViewWindowWithCursor * curveViewWindowWithCursor,
|
||||
BannerView * bannerView, CursorView * cursorView, float topMarginFactor, float rightMarginFactor, float bottomMarginFactor, float leftMarginFactor) :
|
||||
CurveViewWithBanner(curveViewWindowWithCursor, bannerView, topMarginFactor, rightMarginFactor, bottomMarginFactor, leftMarginFactor),
|
||||
m_cursorView(cursorView),
|
||||
m_curveViewWindowWithCursor(curveViewWindowWithCursor)
|
||||
{
|
||||
}
|
||||
|
||||
void CurveViewWithBannerAndCursor::reload() {
|
||||
markRectAsDirty(bounds());
|
||||
computeLabels(Axis::Horizontal);
|
||||
computeLabels(Axis::Vertical);
|
||||
layoutSubviews();
|
||||
}
|
||||
|
||||
void CurveViewWithBannerAndCursor::reloadSelection() {
|
||||
float pixelXSelection = roundf(floatToPixel(Axis::Horizontal, m_curveViewWindowWithCursor->xCursorPosition()));
|
||||
float pixelYSelection = roundf(floatToPixel(Axis::Vertical, m_curveViewWindowWithCursor->yCursorPosition()));
|
||||
KDRect dirtyZone(KDRect(pixelXSelection - k_cursorSize/2, pixelYSelection - k_cursorSize/2, k_cursorSize, k_cursorSize));
|
||||
markRectAsDirty(dirtyZone);
|
||||
layoutSubviews();
|
||||
}
|
||||
|
||||
void CurveViewWithBannerAndCursor::drawGrid(KDContext * ctx, KDRect rect) const {
|
||||
drawGridLines(ctx, rect, Axis::Horizontal, m_curveViewWindowWithCursor->xGridUnit(), k_gridColor);
|
||||
drawGridLines(ctx, rect, Axis::Vertical, m_curveViewWindowWithCursor->yGridUnit(), k_gridColor);
|
||||
}
|
||||
|
||||
void CurveViewWithBannerAndCursor::layoutSubviews() {
|
||||
KDCoordinate xCursorPixelPosition = roundf(floatToPixel(Axis::Horizontal, m_curveViewWindowWithCursor->xCursorPosition()));
|
||||
KDCoordinate yCursorPixelPosition = roundf(floatToPixel(Axis::Vertical, m_curveViewWindowWithCursor->yCursorPosition()));
|
||||
KDRect cursorFrame(xCursorPixelPosition - k_cursorSize/2, yCursorPixelPosition - k_cursorSize/2, k_cursorSize, k_cursorSize);
|
||||
if (!m_mainViewSelected) {
|
||||
cursorFrame = KDRectZero;
|
||||
}
|
||||
m_cursorView->setFrame(cursorFrame);
|
||||
CurveViewWithBanner::layoutSubviews();
|
||||
}
|
||||
|
||||
int CurveViewWithBannerAndCursor::numberOfSubviews() const {
|
||||
return 2;
|
||||
};
|
||||
|
||||
View * CurveViewWithBannerAndCursor::subviewAtIndex(int index) {
|
||||
assert(index >= 0 && index < 2);
|
||||
if (index == 0) {
|
||||
return m_cursorView;
|
||||
}
|
||||
return m_bannerView;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef APPS_CURVE_VIEW_WITH_BANNER_AND_CURSOR_H
|
||||
#define APPS_CURVE_VIEW_WITH_BANNER_AND_CURSOR_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "curve_view_window_with_cursor.h"
|
||||
#include "curve_view_with_banner.h"
|
||||
#include "cursor_view.h"
|
||||
|
||||
class CurveViewWithBannerAndCursor : public CurveViewWithBanner {
|
||||
public:
|
||||
CurveViewWithBannerAndCursor(CurveViewWindowWithCursor * curveViewWindowWithCursor = nullptr, BannerView * bannerView = nullptr,
|
||||
CursorView * cursorView = nullptr, float topMarginFactor = 0.0f, float rightMarginFactor = 0.0f, float bottomMarginFactor = 0.0f, float leftMarginFactor = 0.0f);
|
||||
void reload() override;
|
||||
void reloadSelection() override;
|
||||
void drawGrid(KDContext * ctx, KDRect rect) const;
|
||||
protected:
|
||||
constexpr static KDCoordinate k_cursorSize = 9;
|
||||
constexpr static KDColor k_gridColor = KDColor::RGB24(0xEEEEEE);
|
||||
void layoutSubviews() override;
|
||||
int numberOfSubviews() const override;
|
||||
View * subviewAtIndex(int index) override;
|
||||
|
||||
CursorView * m_cursorView;
|
||||
CurveViewWindowWithCursor * m_curveViewWindowWithCursor;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,153 +0,0 @@
|
||||
#include "curve_view_with_banner_and_cursor_controller.h"
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
CurveViewWindowWithBannerAndCursorController::CurveViewWindowWithBannerAndCursorController(Responder * parentResponder,
|
||||
HeaderViewController * header, CurveViewWindowWithCursor * graphWindow, CurveViewWithBannerAndCursor * graphView) :
|
||||
ViewController(parentResponder),
|
||||
HeaderViewDelegate(header),
|
||||
m_graphWindow(graphWindow),
|
||||
m_graphView(graphView),
|
||||
m_windowParameterController(WindowParameterController(this, m_graphWindow)),
|
||||
m_zoomParameterController(ZoomParameterController(this, m_graphWindow, m_graphView)),
|
||||
m_windowButton(this, "Axes", Invocation([](void * context, void * sender) {
|
||||
CurveViewWindowWithBannerAndCursorController * graphController = (CurveViewWindowWithBannerAndCursorController *) context;
|
||||
StackViewController * stack = graphController->stackController();
|
||||
stack->push(graphController->windowParameterController());
|
||||
}, this)),
|
||||
m_zoomButton(this, "Zoom", Invocation([](void * context, void * sender) {
|
||||
CurveViewWindowWithBannerAndCursorController * graphController = (CurveViewWindowWithBannerAndCursorController *) context;
|
||||
StackViewController * stack = graphController->stackController();
|
||||
stack->push(graphController->zoomParameterController());
|
||||
}, this)),
|
||||
m_defaultInitialisationButton(this, "Initialisation", Invocation([](void * context, void * sender) {
|
||||
CurveViewWindowWithBannerAndCursorController * graphController = (CurveViewWindowWithBannerAndCursorController *) context;
|
||||
StackViewController * stack = graphController->stackController();
|
||||
stack->push(graphController->initialisationParameterController());
|
||||
}, this)),
|
||||
m_cursorView(CursorView())
|
||||
{
|
||||
}
|
||||
|
||||
const char * CurveViewWindowWithBannerAndCursorController::title() const {
|
||||
return "Graphique";
|
||||
}
|
||||
|
||||
View * CurveViewWindowWithBannerAndCursorController::view() {
|
||||
return m_graphView;
|
||||
}
|
||||
|
||||
bool CurveViewWindowWithBannerAndCursorController::handleEvent(Ion::Events::Event event) {
|
||||
if (!m_graphView->isMainViewSelected()) {
|
||||
if (event == Ion::Events::Down) {
|
||||
headerViewController()->setSelectedButton(-1);
|
||||
m_graphView->selectMainView(true);
|
||||
m_graphView->reloadSelection();
|
||||
reloadBannerView();
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::Up) {
|
||||
headerViewController()->setSelectedButton(-1);
|
||||
app()->setFirstResponder(tabController());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (event == Ion::Events::Plus) {
|
||||
m_graphWindow->zoom(1.0f/3.0f);
|
||||
m_graphView->reload();
|
||||
reloadBannerView();
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::Minus) {
|
||||
m_graphWindow->zoom(3.0f/4.0f);
|
||||
m_graphView->reload();
|
||||
reloadBannerView();
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::Left || event == Ion::Events::Right) {
|
||||
int direction = event == Ion::Events::Left ? -1 : 1;
|
||||
m_graphView->reloadSelection();
|
||||
int didMoveCursor = m_graphWindow->moveCursorHorizontally(direction);
|
||||
if (didMoveCursor == 0) {
|
||||
m_graphView->reloadSelection();
|
||||
} else {
|
||||
m_graphView->reload();
|
||||
}
|
||||
reloadBannerView();
|
||||
return (didMoveCursor >= 0);
|
||||
}
|
||||
if (event == Ion::Events::Down || event == Ion::Events::Up) {
|
||||
int direction = event == Ion::Events::Down ? -1 : 1;
|
||||
m_graphView->reloadSelection();
|
||||
int didMoveCursor = m_graphWindow->moveCursorVertically(direction);
|
||||
if (didMoveCursor < 0) {
|
||||
if (event == Ion::Events::Down) {
|
||||
return false;
|
||||
}
|
||||
m_graphView->selectMainView(false);
|
||||
headerViewController()->setSelectedButton(0);
|
||||
return true;
|
||||
}
|
||||
if (didMoveCursor == 0) {
|
||||
m_graphView->reloadSelection();
|
||||
}
|
||||
if (didMoveCursor == 1) {
|
||||
m_graphView->reload();
|
||||
}
|
||||
reloadBannerView();
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::OK) {
|
||||
return handleEnter();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CurveViewWindowWithBannerAndCursorController::didBecomeFirstResponder() {
|
||||
headerViewController()->setSelectedButton(-1);
|
||||
m_graphView->selectMainView(true);
|
||||
// Layout view whe the graph view that might have been modified by the zoom page
|
||||
headerViewController()->layoutView();
|
||||
// Reload graph view
|
||||
m_graphView->reload();
|
||||
reloadBannerView();
|
||||
}
|
||||
|
||||
ViewController * CurveViewWindowWithBannerAndCursorController::windowParameterController() {
|
||||
return &m_windowParameterController;
|
||||
}
|
||||
|
||||
ViewController * CurveViewWindowWithBannerAndCursorController::zoomParameterController() {
|
||||
return &m_zoomParameterController;
|
||||
}
|
||||
|
||||
int CurveViewWindowWithBannerAndCursorController::numberOfButtons() const {
|
||||
return 3;
|
||||
}
|
||||
|
||||
Button * CurveViewWindowWithBannerAndCursorController::buttonAtIndex(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return &m_windowButton;
|
||||
case 1:
|
||||
return &m_zoomButton;
|
||||
case 2:
|
||||
return &m_defaultInitialisationButton;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Responder * CurveViewWindowWithBannerAndCursorController::defaultController() {
|
||||
return tabController();
|
||||
}
|
||||
|
||||
Responder * CurveViewWindowWithBannerAndCursorController::tabController() const{
|
||||
return (parentResponder()->parentResponder()->parentResponder()->parentResponder());
|
||||
}
|
||||
|
||||
StackViewController * CurveViewWindowWithBannerAndCursorController::stackController() const{
|
||||
return (StackViewController *)(parentResponder()->parentResponder()->parentResponder());
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
#ifndef APPS_CURVE_VIEW_WINDOW_WITH_BANNER_AND_CURSOR_CONTROLLER_H
|
||||
#define APPS_CURVE_VIEW_WINDOW_WITH_BANNER_AND_CURSOR_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "curve_view_window_with_cursor.h"
|
||||
#include "curve_view_with_banner_and_cursor.h"
|
||||
#include "window_parameter_controller.h"
|
||||
#include "zoom_parameter_controller.h"
|
||||
|
||||
class CurveViewWindowWithBannerAndCursorController : public ViewController, public HeaderViewDelegate, public AlternateEmptyViewDelegate {
|
||||
public:
|
||||
CurveViewWindowWithBannerAndCursorController(Responder * parentResponder, HeaderViewController * header, CurveViewWindowWithCursor * graphWindow, CurveViewWithBannerAndCursor * graphView);
|
||||
View * view() override;
|
||||
const char * title() const override;
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
void didBecomeFirstResponder() override;
|
||||
|
||||
ViewController * windowParameterController();
|
||||
ViewController * zoomParameterController();
|
||||
virtual ViewController * initialisationParameterController() = 0;
|
||||
|
||||
int numberOfButtons() const override;
|
||||
Button * buttonAtIndex(int index) override;
|
||||
|
||||
Responder * defaultController() override;
|
||||
protected:
|
||||
virtual bool handleEnter() = 0;
|
||||
Responder * tabController() const;
|
||||
StackViewController * stackController() const;
|
||||
virtual void reloadBannerView() = 0;
|
||||
CurveViewWindowWithCursor * m_graphWindow;
|
||||
CurveViewWithBannerAndCursor * m_graphView;
|
||||
WindowParameterController m_windowParameterController;
|
||||
ZoomParameterController m_zoomParameterController;
|
||||
Button m_windowButton;
|
||||
Button m_zoomButton;
|
||||
Button m_defaultInitialisationButton;
|
||||
CursorView m_cursorView;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -55,7 +55,7 @@ float FloatPairStore::sumOfColumn(int i) {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FloatPairStore::checksum() {
|
||||
uint32_t FloatPairStore::storeChecksum() {
|
||||
size_t dataLengthInBytes = m_numberOfPairs*2*sizeof(float);
|
||||
assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4
|
||||
//return Ion::crc32((uint32_t *)m_data, dataLengthInBytes>>2);
|
||||
|
||||
@@ -15,7 +15,7 @@ public:
|
||||
void deleteAllPairs();
|
||||
void resetColumn(int i);
|
||||
float sumOfColumn(int i);
|
||||
uint32_t checksum();
|
||||
uint32_t storeChecksum();
|
||||
constexpr static int k_maxNumberOfPairs = 500;
|
||||
protected:
|
||||
virtual float defaultValue(int ii);
|
||||
|
||||
@@ -4,7 +4,6 @@ app_objs += $(addprefix apps/graph/,\
|
||||
function_store.o\
|
||||
function_title_cell.o\
|
||||
graph/banner_view.o\
|
||||
graph/graph_window.o\
|
||||
graph/curve_parameter_controller.o\
|
||||
graph/goto_parameter_controller.o\
|
||||
graph/graph_controller.o\
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
extern "C" {
|
||||
#include <assert.h>
|
||||
}
|
||||
#include <stddef.h>
|
||||
|
||||
namespace Graph {
|
||||
|
||||
@@ -15,6 +16,13 @@ FunctionStore::FunctionStore() :
|
||||
addEmptyFunction();
|
||||
}
|
||||
|
||||
uint32_t FunctionStore::storeChecksum() {
|
||||
size_t dataLengthInBytes = m_numberOfFunctions*sizeof(Function);
|
||||
assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4
|
||||
//return Ion::crc32((uint32_t *)m_functions, dataLengthInBytes>>2);
|
||||
return m_numberOfFunctions;
|
||||
}
|
||||
|
||||
Function * FunctionStore::functionAtIndex(int i) {
|
||||
assert(i>=0 && i<m_numberOfFunctions);
|
||||
return &m_functions[i];
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define GRAPH_FUNCTION_STORE_H
|
||||
|
||||
#include "function.h"
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Graph {
|
||||
/* FunctionStore is a dumb class.
|
||||
@@ -9,6 +10,7 @@ namespace Graph {
|
||||
class FunctionStore {
|
||||
public:
|
||||
FunctionStore();
|
||||
uint32_t storeChecksum();
|
||||
Function * functionAtIndex(int i);
|
||||
Function * activeFunctionAtIndex(int i);
|
||||
Function * definedFunctionAtIndex(int i);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace Graph {
|
||||
|
||||
CurveParameterController::CurveParameterController(GraphWindow * graphWindow, BannerView * bannerView) :
|
||||
CurveParameterController::CurveParameterController(InteractiveCurveViewRange * graphRange, BannerView * bannerView, CurveViewCursor * cursor) :
|
||||
ViewController(nullptr),
|
||||
m_bannerView(bannerView),
|
||||
m_function(nullptr),
|
||||
@@ -12,7 +12,7 @@ CurveParameterController::CurveParameterController(GraphWindow * graphWindow, Ba
|
||||
m_derivativeCell(SwitchMenuListCell((char*)"Nombre derivee")),
|
||||
m_selectableTableView(SelectableTableView(this, this, Metric::TopMargin, Metric::RightMargin,
|
||||
Metric::BottomMargin, Metric::LeftMargin)),
|
||||
m_goToParameterController(GoToParameterController(this, graphWindow))
|
||||
m_goToParameterController(GoToParameterController(this, graphRange, cursor))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
#include "goto_parameter_controller.h"
|
||||
#include "banner_view.h"
|
||||
#include "../function.h"
|
||||
#include "../../curve_view_cursor.h"
|
||||
#include "../../interactive_curve_view_range.h"
|
||||
|
||||
namespace Graph {
|
||||
|
||||
class CurveParameterController : public ViewController, public SimpleListViewDataSource {
|
||||
public:
|
||||
CurveParameterController(GraphWindow * graphWindow, BannerView * bannerView);
|
||||
CurveParameterController(InteractiveCurveViewRange * graphRange, BannerView * bannerView, CurveViewCursor * cursor);
|
||||
|
||||
View * view() override;
|
||||
const char * title() const override;
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
|
||||
namespace Graph {
|
||||
|
||||
GoToParameterController::GoToParameterController(Responder * parentResponder, GraphWindow * graphWindow) :
|
||||
GoToParameterController::GoToParameterController(Responder * parentResponder, InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor) :
|
||||
FloatParameterController(parentResponder),
|
||||
m_abscisseCell(EditableTextMenuListCell(&m_selectableTableView, this, m_draftTextBuffer, (char*)"x")),
|
||||
m_graphWindow(graphWindow),
|
||||
m_graphRange(graphRange),
|
||||
m_cursor(cursor),
|
||||
m_function(nullptr)
|
||||
{
|
||||
}
|
||||
@@ -18,12 +19,16 @@ const char * GoToParameterController::title() const {
|
||||
|
||||
float GoToParameterController::parameterAtIndex(int index) {
|
||||
assert(index == 0);
|
||||
return m_graphWindow->xCursorPosition();
|
||||
return m_cursor->x();
|
||||
}
|
||||
|
||||
void GoToParameterController::setParameterAtIndex(int parameterIndex, float f) {
|
||||
assert(parameterIndex == 0);
|
||||
m_graphWindow->setCursorPositionAtAbscissaWithFunction(f, m_function);
|
||||
App * graphApp = (Graph::App *)app();
|
||||
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);
|
||||
}
|
||||
|
||||
int GoToParameterController::numberOfRows() {
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
#include <escher.h>
|
||||
#include "graph_view.h"
|
||||
#include "../../float_parameter_controller.h"
|
||||
#include "../../curve_view_cursor.h"
|
||||
#include "../../interactive_curve_view_range.h"
|
||||
|
||||
namespace Graph {
|
||||
class GoToParameterController : public FloatParameterController {
|
||||
public:
|
||||
GoToParameterController(Responder * parentResponder, GraphWindow * graphWindow);
|
||||
GoToParameterController(Responder * parentResponder, InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor);
|
||||
const char * title() const override;
|
||||
int numberOfRows() override;
|
||||
TableViewCell * reusableCell(int index) override;
|
||||
@@ -19,7 +21,8 @@ private:
|
||||
void setParameterAtIndex(int parameterIndex, float f) override;
|
||||
char m_draftTextBuffer[EditableTextMenuListCell::k_bufferLength];
|
||||
EditableTextMenuListCell m_abscisseCell;
|
||||
GraphWindow * m_graphWindow;
|
||||
InteractiveCurveViewRange * m_graphRange;
|
||||
CurveViewCursor * m_cursor;
|
||||
Function * m_function;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,24 +2,23 @@
|
||||
#include "../app.h"
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
namespace Graph {
|
||||
|
||||
GraphController::GraphController(Responder * parentResponder, FunctionStore * functionStore, HeaderViewController * header) :
|
||||
CurveViewWindowWithBannerAndCursorController(parentResponder, header, &m_graphWindow, &m_view),
|
||||
InteractiveCurveViewController(parentResponder, header, &m_graphRange, &m_view),
|
||||
m_bannerView(BannerView()),
|
||||
m_view(GraphView(functionStore, &m_graphWindow, &m_bannerView, &m_cursorView)),
|
||||
m_graphWindow(functionStore),
|
||||
m_initialisationParameterController(InitialisationParameterController(this, &m_graphWindow)),
|
||||
m_curveParameterController(CurveParameterController(&m_graphWindow, &m_bannerView)),
|
||||
m_functionStore(functionStore)
|
||||
m_view(GraphView(functionStore, &m_graphRange, &m_cursor, &m_bannerView, &m_cursorView)),
|
||||
m_graphRange(InteractiveCurveViewRange(&m_cursor, this)),
|
||||
m_initialisationParameterController(InitialisationParameterController(this, &m_graphRange)),
|
||||
m_curveParameterController(CurveParameterController(&m_graphRange, &m_bannerView, &m_cursor)),
|
||||
m_functionStore(functionStore),
|
||||
m_indexFunctionSelectedByCursor(0)
|
||||
{
|
||||
}
|
||||
|
||||
View * GraphController::view() {
|
||||
if (isnan(m_graphWindow.xCursorPosition()) && !isEmpty()) {
|
||||
m_graphWindow.initCursorPosition();
|
||||
}
|
||||
return &m_view;
|
||||
}
|
||||
|
||||
@@ -46,19 +45,50 @@ void GraphController::didBecomeFirstResponder() {
|
||||
App * graphApp = (Graph::App *)app();
|
||||
m_view.setContext(graphApp->localContext());
|
||||
}
|
||||
if (m_graphWindow.context() == nullptr) {
|
||||
App * graphApp = (Graph::App *)app();
|
||||
m_graphWindow.setContext(graphApp->localContext());
|
||||
InteractiveCurveViewController::didBecomeFirstResponder();
|
||||
}
|
||||
|
||||
bool GraphController::didChangeRange(InteractiveCurveViewRange * interactiveCurveViewRange) {
|
||||
if (!m_graphRange.yAuto()) {
|
||||
return false;
|
||||
}
|
||||
// if new functions were added to the store, the window parameters need to be refresh
|
||||
if (m_graphWindow.computeYaxis()) {
|
||||
m_graphWindow.initCursorPosition();
|
||||
App * graphApp = (Graph::App *)app();
|
||||
if (m_functionStore->numberOfActiveFunctions() > 0) {
|
||||
float min = FLT_MAX;
|
||||
float max = -FLT_MAX;
|
||||
float xMin = m_graphRange.xMin();
|
||||
float xMax = m_graphRange.xMax();
|
||||
float step = (xMax - xMin)/Ion::Display::Width;
|
||||
for (int i=0; i<m_functionStore->numberOfActiveFunctions(); i++) {
|
||||
Function * f = m_functionStore->activeFunctionAtIndex(i);
|
||||
float y = 0.0f;
|
||||
for (float x = xMin; x <= xMax; x += step) {
|
||||
y = f->evaluateAtAbscissa(x, graphApp->localContext());
|
||||
if (!isnan(y) && !isinf(y)) {
|
||||
min = min < y ? min : y;
|
||||
max = max > y ? max : y;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_graphRange.yMin() == min && m_graphRange.yMax() == max) {
|
||||
return false;
|
||||
}
|
||||
if (min == max) {
|
||||
min = min - 1;
|
||||
max = max + 1;
|
||||
}
|
||||
m_graphRange.setYMin(min);
|
||||
m_graphRange.setYMax(max);
|
||||
}
|
||||
CurveViewWindowWithBannerAndCursorController::didBecomeFirstResponder();
|
||||
return true;
|
||||
}
|
||||
|
||||
BannerView * GraphController::bannerView() {
|
||||
return &m_bannerView;
|
||||
}
|
||||
|
||||
bool GraphController::handleEnter() {
|
||||
Function * f = m_graphWindow.functionSelectedByCursor();
|
||||
Function * f = m_functionStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor);
|
||||
m_curveParameterController.setFunction(f);
|
||||
StackViewController * stack = stackController();
|
||||
stack->push(&m_curveParameterController);
|
||||
@@ -67,21 +97,87 @@ bool GraphController::handleEnter() {
|
||||
|
||||
void GraphController::reloadBannerView() {
|
||||
char buffer[k_maxNumberOfCharacters+Constant::FloatBufferSizeInScientificMode] = {'x', ' ', '=', ' ',0};
|
||||
Float(m_graphWindow.xCursorPosition()).convertFloatToText(buffer+4, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode);
|
||||
Float(m_cursor.x()).convertFloatToText(buffer+4, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode);
|
||||
m_bannerView.setLegendAtIndex(buffer, 0);
|
||||
|
||||
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);
|
||||
Function * f = m_functionStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor);
|
||||
buffer[1] = f->name()[0];
|
||||
Float(m_cursor.y()).convertFloatToText(buffer+8, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode);
|
||||
m_bannerView.setLegendAtIndex(buffer+1, 1);
|
||||
|
||||
if (m_bannerView.displayDerivative()) {
|
||||
buffer[0] = m_graphWindow.functionSelectedByCursor()->name()[0];
|
||||
buffer[0] = f->name()[0];
|
||||
buffer[1] = '\'';
|
||||
Float(m_graphWindow.derivativeAtCursorPosition()).convertFloatToText(buffer+8, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaForDerivativeNumberInScientificMode);
|
||||
App * graphApp = (Graph::App *)app();
|
||||
float y = f->approximateDerivative(m_cursor.x(), graphApp->localContext());
|
||||
Float(y).convertFloatToText(buffer+8, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaForDerivativeNumberInScientificMode);
|
||||
m_bannerView.setLegendAtIndex(buffer, 2);
|
||||
}
|
||||
m_bannerView.layoutSubviews();
|
||||
}
|
||||
|
||||
void GraphController::initRangeParameters() {
|
||||
if (didChangeRange(&m_graphRange)) {
|
||||
initCursorParameters();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphController::initCursorParameters() {
|
||||
float x = (m_graphRange.xMin()+m_graphRange.xMax())/2.0f;
|
||||
m_indexFunctionSelectedByCursor = 0;
|
||||
Function * firstFunction = m_functionStore->activeFunctionAtIndex(0);
|
||||
App * graphApp = (Graph::App *)app();
|
||||
float y = firstFunction->evaluateAtAbscissa(x, graphApp->localContext());
|
||||
m_graphRange.moveCursorTo(x, y);
|
||||
}
|
||||
|
||||
int 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;
|
||||
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);;
|
||||
}
|
||||
|
||||
int 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());
|
||||
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_cursor.x(), graphApp->localContext());
|
||||
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;
|
||||
}
|
||||
return m_graphRange.moveCursorTo(m_cursor.x(), nextY);
|
||||
}
|
||||
|
||||
uint32_t GraphController::modelVersion() {
|
||||
return m_functionStore->storeChecksum();
|
||||
}
|
||||
|
||||
uint32_t GraphController::rangeVersion() {
|
||||
return m_graphRange.rangeChecksum();
|
||||
}
|
||||
|
||||
InteractiveCurveViewRange * GraphController::interactiveCurveViewRange() {
|
||||
return &m_graphRange;
|
||||
}
|
||||
|
||||
CurveView * GraphController::curveView() {
|
||||
return &m_view;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
|
||||
#include <escher.h>
|
||||
#include "graph_view.h"
|
||||
#include "graph_window.h"
|
||||
#include "banner_view.h"
|
||||
#include "curve_parameter_controller.h"
|
||||
#include "initialisation_parameter_controller.h"
|
||||
#include "../../curve_view_with_banner_and_cursor_controller.h"
|
||||
#include "../../interactive_curve_view_controller.h"
|
||||
#include "../function_store.h"
|
||||
|
||||
namespace Graph {
|
||||
class GraphController : public CurveViewWindowWithBannerAndCursorController {
|
||||
class GraphController : public InteractiveCurveViewController, public InteractiveCurveViewRangeDelegate {
|
||||
public:
|
||||
GraphController(Responder * parentResponder, FunctionStore * functionStore, HeaderViewController * header);
|
||||
View * view() override;
|
||||
@@ -21,16 +20,28 @@ public:
|
||||
bool isEmpty() override;
|
||||
const char * emptyMessage() override;
|
||||
|
||||
bool didChangeRange(InteractiveCurveViewRange * interactiveCurveViewRange) override;
|
||||
|
||||
private:
|
||||
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;
|
||||
uint32_t modelVersion() override;
|
||||
uint32_t rangeVersion() override;
|
||||
InteractiveCurveViewRange * interactiveCurveViewRange() override;
|
||||
CurveView * curveView() override;
|
||||
BannerView m_bannerView;
|
||||
GraphView m_view;
|
||||
GraphWindow m_graphWindow;
|
||||
InteractiveCurveViewRange m_graphRange;
|
||||
InitialisationParameterController m_initialisationParameterController;
|
||||
CurveParameterController m_curveParameterController;
|
||||
FunctionStore * m_functionStore;
|
||||
int m_indexFunctionSelectedByCursor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
namespace Graph {
|
||||
|
||||
GraphView::GraphView(FunctionStore * functionStore, GraphWindow * graphWindow, BannerView * bannerView, CursorView * cursorView) :
|
||||
CurveViewWithBannerAndCursor(graphWindow, bannerView, cursorView, 0.0f, 0.0f, 0.2f, 0.0f),
|
||||
GraphView::GraphView(FunctionStore * functionStore, InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor, View * bannerView, View * cursorView) :
|
||||
CurveView(graphRange, cursor, bannerView, cursorView, 0.0f, 0.0f, 0.2f, 0.0f),
|
||||
m_functionStore(functionStore),
|
||||
m_context(nullptr)
|
||||
{
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
#define GRAPH_GRAPH_VIEW_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "graph_window.h"
|
||||
#include "../../curve_view_with_banner_and_cursor.h"
|
||||
#include "../../curve_view.h"
|
||||
#include "../../constant.h"
|
||||
#include "../function_store.h"
|
||||
#include "../local_context.h"
|
||||
#include "../../interactive_curve_view_range.h"
|
||||
|
||||
namespace Graph {
|
||||
|
||||
class GraphView : public CurveViewWithBannerAndCursor {
|
||||
class GraphView : public CurveView {
|
||||
public:
|
||||
GraphView(FunctionStore * functionStore, GraphWindow * graphWindow, BannerView * bannerView, CursorView * cursorView);
|
||||
GraphView(FunctionStore * functionStore, InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor, View * bannerView, View * cursorView);
|
||||
void drawRect(KDContext * ctx, KDRect rect) const override;
|
||||
void setContext(Context * context);
|
||||
Context * context() const;
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
#include "graph_window.h"
|
||||
#include "../../constant.h"
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <ion.h>
|
||||
|
||||
namespace Graph {
|
||||
|
||||
GraphWindow::GraphWindow(FunctionStore * functionStore) :
|
||||
CurveViewWindowWithCursor(),
|
||||
m_indexFunctionSelectedByCursor(0),
|
||||
m_functionStore(functionStore),
|
||||
m_context(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
bool GraphWindow::computeYaxis() {
|
||||
if (!m_yAuto) {
|
||||
return false;
|
||||
}
|
||||
if (m_functionStore->numberOfActiveFunctions() > 0) {
|
||||
float min = FLT_MAX;
|
||||
float max = -FLT_MAX;
|
||||
float step = (m_xMax - m_xMin)/Ion::Display::Width;
|
||||
for (int i=0; i<m_functionStore->numberOfActiveFunctions(); i++) {
|
||||
Function * f = m_functionStore->activeFunctionAtIndex(i);
|
||||
float y = 0.0f;
|
||||
for (float x = m_xMin; x <= m_xMax; x += step) {
|
||||
y = f->evaluateAtAbscissa(x, m_context);
|
||||
if (!isnan(y) && !isinf(y)) {
|
||||
min = min < y ? min : y;
|
||||
max = max > y ? max : y;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_yMin == min && m_yMax == max) {
|
||||
return false;
|
||||
}
|
||||
m_yMin = min;
|
||||
m_yMax = max;
|
||||
if (m_yMin == m_yMax) {
|
||||
m_yMin = min - 1;
|
||||
m_yMax = max + 1;
|
||||
}
|
||||
}
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
return true;
|
||||
}
|
||||
|
||||
Context * GraphWindow::context() {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
void GraphWindow::setContext(Context * context) {
|
||||
m_context = context;
|
||||
}
|
||||
|
||||
void GraphWindow::setTrigonometric() {
|
||||
m_xMin = -10.5f;
|
||||
m_xMax = 10.5f;
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
m_yAuto = false;
|
||||
m_yMin = -1.6f;
|
||||
m_yMax = 1.6f;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
initCursorPosition();
|
||||
}
|
||||
|
||||
void GraphWindow::setDefault() {
|
||||
m_xMin = -10.0f;
|
||||
m_xMax = 10.0f;
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
setYAuto(true);
|
||||
initCursorPosition();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int GraphWindow::moveCursorHorizontally(int direction) {
|
||||
m_xCursorPosition = direction > 0 ? m_xCursorPosition + m_xGridUnit/CurveViewWindowWithCursor::k_numberOfCursorStepsInGradUnit :
|
||||
m_xCursorPosition - m_xGridUnit/CurveViewWindowWithCursor::k_numberOfCursorStepsInGradUnit;
|
||||
Function * f = m_functionStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor);
|
||||
m_yCursorPosition = f->evaluateAtAbscissa(m_xCursorPosition, m_context);
|
||||
float xMargin = CurveViewWindowWithCursor::k_cursorMarginFactorToBorder * (m_xMax - m_xMin);
|
||||
float yMargin = CurveViewWindowWithCursor::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 = CurveViewWindowWithCursor::k_cursorMarginFactorToBorder * (m_xMax - m_xMin);
|
||||
float yMargin = CurveViewWindowWithCursor::k_cursorMarginFactorToBorder * (m_yMax - m_yMin);
|
||||
m_yCursorPosition = nextY;
|
||||
bool windowHasMoved = panToMakePointVisible(m_xCursorPosition, m_yCursorPosition, xMargin, yMargin);
|
||||
return windowHasMoved;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef GRAPH_GRAPH_AXIS_INTERVAL_H
|
||||
#define GRAPH_GRAPH_AXIS_INTERVAL_H
|
||||
|
||||
#include "../function_store.h"
|
||||
#include "../../curve_view_window_with_cursor.h"
|
||||
|
||||
namespace Graph {
|
||||
|
||||
class GraphWindow : public CurveViewWindowWithCursor {
|
||||
public:
|
||||
GraphWindow(FunctionStore * functionStore);
|
||||
/* computeYAxis needs to be public to be called when a function is added to
|
||||
* the function store. */
|
||||
bool computeYaxis() override;
|
||||
Context * context();
|
||||
void setContext(Context * context);
|
||||
void setTrigonometric();
|
||||
void setDefault();
|
||||
float derivativeAtCursorPosition();
|
||||
Function * functionSelectedByCursor();
|
||||
void setCursorPositionAtAbscissaWithFunction(float abscissa, Function * function);
|
||||
void initCursorPosition() override;
|
||||
int moveCursorHorizontally(int direction) override;
|
||||
// the result of moveCursorVertically means:
|
||||
// -1 -> no next function 0-> the window has not changed 1->the window changed
|
||||
int moveCursorVertically(int direction) override;
|
||||
private:
|
||||
int m_indexFunctionSelectedByCursor;
|
||||
FunctionStore * m_functionStore;
|
||||
Context * m_context;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
namespace Graph {
|
||||
|
||||
InitialisationParameterController::InitialisationParameterController(Responder * parentResponder, GraphWindow * graphWindow) :
|
||||
InitialisationParameterController::InitialisationParameterController(Responder * parentResponder, InteractiveCurveViewRange * graphRange) :
|
||||
ViewController(parentResponder),
|
||||
m_selectableTableView(SelectableTableView(this, this, Metric::TopMargin, Metric::RightMargin,
|
||||
Metric::BottomMargin, Metric::LeftMargin)),
|
||||
m_graphWindow(graphWindow)
|
||||
m_graphRange(graphRange)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -30,20 +30,20 @@ bool InitialisationParameterController::handleEvent(Ion::Events::Event event) {
|
||||
switch (m_selectableTableView.selectedRow()) {
|
||||
case 0:
|
||||
// TODO: if mode == degree, xmin = -600, xmax = 600
|
||||
m_graphWindow->setTrigonometric();
|
||||
m_graphRange->setTrigonometric();
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
m_graphWindow->roundAbscissa();
|
||||
m_graphRange->roundAbscissa();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
m_graphWindow->normalize();
|
||||
m_graphRange->normalize();
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
m_graphWindow->setDefault();
|
||||
m_graphRange->setDefault();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
#define GRAPH_GRAPH_INITIALISATION_PARAMETER_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "graph_window.h"
|
||||
#include "../../interactive_curve_view_range.h"
|
||||
|
||||
namespace Graph {
|
||||
|
||||
class InitialisationParameterController : public ViewController, public SimpleListViewDataSource {
|
||||
public:
|
||||
InitialisationParameterController(Responder * parentResponder, GraphWindow * graphWindow);
|
||||
InitialisationParameterController(Responder * parentResponder, InteractiveCurveViewRange * graphRange);
|
||||
View * view() override;
|
||||
const char * title() const override;
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
@@ -22,7 +22,7 @@ private:
|
||||
constexpr static int k_totalNumberOfCells = 4;
|
||||
MenuListCell m_cells[k_totalNumberOfCells];
|
||||
SelectableTableView m_selectableTableView;
|
||||
GraphWindow * m_graphWindow;
|
||||
InteractiveCurveViewRange * m_graphRange;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
162
apps/interactive_curve_view_controller.cpp
Normal file
162
apps/interactive_curve_view_controller.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
#include "interactive_curve_view_controller.h"
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
InteractiveCurveViewController::InteractiveCurveViewController(Responder * parentResponder, HeaderViewController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView) :
|
||||
ViewController(parentResponder),
|
||||
HeaderViewDelegate(header),
|
||||
m_cursor(),
|
||||
m_cursorView(CursorView()),
|
||||
m_rangeParameterController(RangeParameterController(this, interactiveRange)),
|
||||
m_zoomParameterController(ZoomParameterController(this, interactiveRange, curveView)),
|
||||
m_rangeButton(this, "Axes", Invocation([](void * context, void * sender) {
|
||||
InteractiveCurveViewController * graphController = (InteractiveCurveViewController *) context;
|
||||
StackViewController * stack = graphController->stackController();
|
||||
stack->push(graphController->rangeParameterController());
|
||||
}, this)),
|
||||
m_zoomButton(this, "Zoom", Invocation([](void * context, void * sender) {
|
||||
InteractiveCurveViewController * graphController = (InteractiveCurveViewController *) context;
|
||||
StackViewController * stack = graphController->stackController();
|
||||
stack->push(graphController->zoomParameterController());
|
||||
}, this)),
|
||||
m_defaultInitialisationButton(this, "Initialisation", Invocation([](void * context, void * sender) {
|
||||
InteractiveCurveViewController * graphController = (InteractiveCurveViewController *) context;
|
||||
StackViewController * stack = graphController->stackController();
|
||||
stack->push(graphController->initialisationParameterController());
|
||||
}, this))
|
||||
{
|
||||
}
|
||||
|
||||
const char * InteractiveCurveViewController::title() const {
|
||||
return "Graphique";
|
||||
}
|
||||
|
||||
View * InteractiveCurveViewController::view() {
|
||||
return curveView();
|
||||
}
|
||||
|
||||
bool InteractiveCurveViewController::handleEvent(Ion::Events::Event event) {
|
||||
if (!curveView()->isMainViewSelected()) {
|
||||
if (event == Ion::Events::Down) {
|
||||
headerViewController()->setSelectedButton(-1);
|
||||
curveView()->selectMainView(true);
|
||||
curveView()->reloadSelection();
|
||||
reloadBannerView();
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::Up) {
|
||||
headerViewController()->setSelectedButton(-1);
|
||||
app()->setFirstResponder(tabController());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (event == Ion::Events::Plus) {
|
||||
interactiveCurveViewRange()->zoom(1.0f/3.0f);
|
||||
curveView()->reload();
|
||||
reloadBannerView();
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::Minus) {
|
||||
interactiveCurveViewRange()->zoom(3.0f/4.0f);
|
||||
curveView()->reload();
|
||||
reloadBannerView();
|
||||
return true;
|
||||
}
|
||||
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) {
|
||||
curveView()->reload();
|
||||
}
|
||||
reloadBannerView();
|
||||
return (didMoveCursor >= 0);
|
||||
}
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
if (didMoveCursor == 0) {
|
||||
curveView()->reloadSelection();
|
||||
}
|
||||
if (didMoveCursor == 1) {
|
||||
curveView()->reload();
|
||||
}
|
||||
reloadBannerView();
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::OK) {
|
||||
return handleEnter();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void InteractiveCurveViewController::didBecomeFirstResponder() {
|
||||
uint32_t newModelVersion = modelVersion();
|
||||
if (m_modelVersion != newModelVersion) {
|
||||
m_modelVersion = newModelVersion;
|
||||
initRangeParameters();
|
||||
}
|
||||
uint32_t newRangeVersion = rangeVersion();
|
||||
if (m_rangeVersion != newRangeVersion) {
|
||||
m_rangeVersion = newRangeVersion;
|
||||
initCursorParameters();
|
||||
}
|
||||
headerViewController()->setSelectedButton(-1);
|
||||
curveView()->selectMainView(true);
|
||||
// Layout view whe the graph view that might have been modified by the zoom page
|
||||
headerViewController()->layoutView();
|
||||
// Reload graph view
|
||||
curveView()->reload();
|
||||
reloadBannerView();
|
||||
}
|
||||
|
||||
ViewController * InteractiveCurveViewController::rangeParameterController() {
|
||||
return &m_rangeParameterController;
|
||||
}
|
||||
|
||||
ViewController * InteractiveCurveViewController::zoomParameterController() {
|
||||
return &m_zoomParameterController;
|
||||
}
|
||||
|
||||
int InteractiveCurveViewController::numberOfButtons() const {
|
||||
return 3;
|
||||
}
|
||||
|
||||
Button * InteractiveCurveViewController::buttonAtIndex(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return &m_rangeButton;
|
||||
case 1:
|
||||
return &m_zoomButton;
|
||||
case 2:
|
||||
return &m_defaultInitialisationButton;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Responder * InteractiveCurveViewController::defaultController() {
|
||||
return tabController();
|
||||
}
|
||||
|
||||
Responder * InteractiveCurveViewController::tabController() const{
|
||||
return (parentResponder()->parentResponder()->parentResponder()->parentResponder());
|
||||
}
|
||||
|
||||
StackViewController * InteractiveCurveViewController::stackController() const{
|
||||
return (StackViewController *)(parentResponder()->parentResponder()->parentResponder());
|
||||
}
|
||||
58
apps/interactive_curve_view_controller.h
Normal file
58
apps/interactive_curve_view_controller.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef APPS_INTERACTIVE_CURVE_VIEW_CONTROLLER_H
|
||||
#define APPS_INTERACTIVE_CURVE_VIEW_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "interactive_curve_view_range.h"
|
||||
#include "curve_view.h"
|
||||
#include "cursor_view.h"
|
||||
#include "banner_view.h"
|
||||
#include "range_parameter_controller.h"
|
||||
#include "zoom_parameter_controller.h"
|
||||
|
||||
class InteractiveCurveViewController : public ViewController, public HeaderViewDelegate, public AlternateEmptyViewDelegate {
|
||||
public:
|
||||
InteractiveCurveViewController(Responder * parentResponder, HeaderViewController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView);
|
||||
View * view() override;
|
||||
const char * title() const override;
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
void didBecomeFirstResponder() override;
|
||||
|
||||
ViewController * rangeParameterController();
|
||||
ViewController * zoomParameterController();
|
||||
virtual ViewController * initialisationParameterController() = 0;
|
||||
|
||||
int numberOfButtons() const override;
|
||||
Button * buttonAtIndex(int index) override;
|
||||
|
||||
Responder * defaultController() override;
|
||||
protected:
|
||||
virtual BannerView * bannerView() = 0;
|
||||
virtual bool handleEnter() = 0;
|
||||
Responder * tabController() const;
|
||||
StackViewController * stackController() const;
|
||||
virtual void reloadBannerView() = 0;
|
||||
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;
|
||||
virtual uint32_t modelVersion() = 0;
|
||||
virtual uint32_t rangeVersion() = 0;
|
||||
virtual InteractiveCurveViewRange * interactiveCurveViewRange() = 0;
|
||||
virtual CurveView * curveView() = 0;
|
||||
CurveViewCursor m_cursor;
|
||||
CursorView m_cursorView;
|
||||
private:
|
||||
uint32_t m_modelVersion;
|
||||
uint32_t m_rangeVersion;
|
||||
RangeParameterController m_rangeParameterController;
|
||||
ZoomParameterController m_zoomParameterController;
|
||||
Button m_rangeButton;
|
||||
Button m_zoomButton;
|
||||
Button m_defaultInitialisationButton;
|
||||
};
|
||||
|
||||
#endif
|
||||
218
apps/interactive_curve_view_range.cpp
Normal file
218
apps/interactive_curve_view_range.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
#include "interactive_curve_view_range.h"
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <ion.h>
|
||||
|
||||
InteractiveCurveViewRange::InteractiveCurveViewRange(CurveViewCursor * cursor, InteractiveCurveViewRangeDelegate * delegate) :
|
||||
m_xMin(-10.0f),
|
||||
m_xMax(10.0f),
|
||||
m_yMin(-10.0f),
|
||||
m_yMax(10.0f),
|
||||
m_yAuto(true),
|
||||
m_xGridUnit(2.0f),
|
||||
m_yGridUnit(2.0f),
|
||||
m_cursor(cursor),
|
||||
m_delegate(delegate)
|
||||
{
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::setCursor(CurveViewCursor * cursor) {
|
||||
m_cursor = cursor;
|
||||
}
|
||||
|
||||
uint32_t InteractiveCurveViewRange::rangeChecksum() {
|
||||
float data[5] = {m_xMin, m_xMax, m_yMin, m_yMax, m_yAuto ? 1.0f : 0.0f};
|
||||
size_t dataLengthInBytes = 5*sizeof(float);
|
||||
assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4
|
||||
//return Ion::crc32((uint32_t *)data, dataLengthInBytes>>2);
|
||||
return m_xMin+ m_xMax+ m_yMin+ m_yMax+ m_yAuto;
|
||||
}
|
||||
|
||||
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_delegate) {
|
||||
m_delegate->didChangeRange(this);
|
||||
}
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::setXMax(float xMax) {
|
||||
m_xMax = 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;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::setYMax(float yMax) {
|
||||
m_yMax = yMax;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::setYAuto(bool yAuto) {
|
||||
m_yAuto = yAuto;
|
||||
if (m_delegate) {
|
||||
m_delegate->didChangeRange(this);
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::zoom(float ratio) {
|
||||
float xMin = m_xMin;
|
||||
float xMax = m_xMax;
|
||||
float yMin = m_yMin;
|
||||
float yMax = m_yMax;
|
||||
m_xMin = (xMax+xMin)/2.0f - ratio*fabsf(xMax-xMin);
|
||||
m_xMax = (xMax+xMin)/2.0f + ratio*fabsf(xMax-xMin);
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
m_yAuto = false;
|
||||
m_yMin = (yMax+yMin)/2.0f - ratio*fabsf(yMax-yMin);
|
||||
m_yMax = (yMax+yMin)/2.0f + ratio*fabsf(yMax-yMin);
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::panWithVector(float x, float y) {
|
||||
m_yAuto = false;
|
||||
m_xMin = m_xMin + x;
|
||||
m_xMax = m_xMax + x;
|
||||
m_yMin = m_yMin + y;
|
||||
m_yMax = m_yMax + y;
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::roundAbscissa() {
|
||||
float xMin = m_xMin;
|
||||
float xMax = m_xMax;
|
||||
m_xMin = roundf((xMin+xMax)/2) - (float)Ion::Display::Width/2.0f;
|
||||
m_xMax = roundf((xMin+xMax)/2) + (float)Ion::Display::Width/2.0f-1.0f;
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
if (m_delegate) {
|
||||
m_delegate->didChangeRange(this);
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::normalize() {
|
||||
float xMin = m_xMin;
|
||||
float xMax = m_xMax;
|
||||
float yMin = m_yMin;
|
||||
float yMax = m_yMax;
|
||||
m_xMin = (xMin+xMax)/2 - 5.3f;
|
||||
m_xMax = (xMin+xMax)/2 + 5.3f;
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
m_yAuto = false;
|
||||
m_yMin = (yMin+yMax)/2 - 3.1f;
|
||||
m_yMax = (yMin+yMax)/2 + 3.1f;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::setTrigonometric() {
|
||||
m_xMin = -10.5f;
|
||||
m_xMax = 10.5f;
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
m_yAuto = false;
|
||||
m_yMin = -1.6f;
|
||||
m_yMax = 1.6f;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::setDefault() {
|
||||
m_xMin = -10.0f;
|
||||
m_xMax = 10.0f;
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
setYAuto(true);
|
||||
}
|
||||
|
||||
void InteractiveCurveViewRange::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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bool InteractiveCurveViewRange::moveCursorTo(float x, float y) {
|
||||
if (m_cursor) {
|
||||
m_cursor->moveTo(x, y);
|
||||
float xMargin = InteractiveCurveViewRange::k_cursorMarginFactorToBorder * (m_xMax - m_xMin);
|
||||
float yMargin = InteractiveCurveViewRange::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;
|
||||
float xRange = m_xMax - m_xMin;
|
||||
float yRange = m_yMax - m_yMin;
|
||||
if (x < m_xMin + xMargin) {
|
||||
m_xMin = x - xMargin;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
m_yMin = m_yMax - yRange;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
m_yAuto = false;
|
||||
windowMoved = true;
|
||||
}
|
||||
return windowMoved;
|
||||
}
|
||||
@@ -1,22 +1,16 @@
|
||||
#ifndef APPS_CURVE_VIEW_WINDOW_WITH_CURSOR_H
|
||||
#define APPS_CURVE_VIEW_WINDOW_WITH_CURSOR_H
|
||||
#ifndef APPS_INTERACTIVE_CURVE_VIEW_RANGE_H
|
||||
#define APPS_INTERACTIVE_CURVE_VIEW_RANGE_H
|
||||
|
||||
#include "curve_view_window.h"
|
||||
#include <stdint.h>
|
||||
#include "curve_view_range.h"
|
||||
#include "curve_view_cursor.h"
|
||||
#include "interactive_curve_view_range_delegate.h"
|
||||
|
||||
class CurveViewWindowWithCursor : public CurveViewWindow {
|
||||
class InteractiveCurveViewRange : public CurveViewRange {
|
||||
public:
|
||||
enum class Direction {
|
||||
Up,
|
||||
Left,
|
||||
Down,
|
||||
Right
|
||||
};
|
||||
CurveViewWindowWithCursor();
|
||||
// Cursor
|
||||
float xCursorPosition();
|
||||
float yCursorPosition();
|
||||
virtual int moveCursorHorizontally(int direction) = 0;
|
||||
virtual int moveCursorVertically(int direction) = 0;
|
||||
InteractiveCurveViewRange(CurveViewCursor * cursor, InteractiveCurveViewRangeDelegate * delegate);
|
||||
void setCursor(CurveViewCursor * cursor);
|
||||
uint32_t rangeChecksum();
|
||||
|
||||
//CurveViewWindow
|
||||
float xMin() override;
|
||||
@@ -34,21 +28,20 @@ public:
|
||||
|
||||
// Window
|
||||
void zoom(float ratio);
|
||||
void translateWindow(Direction direction);
|
||||
void panWithVector(float x, float y);
|
||||
void roundAbscissa();
|
||||
void normalize();
|
||||
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;
|
||||
protected:
|
||||
constexpr static float k_cursorMarginFactorToBorder = 0.025f;
|
||||
constexpr static float k_numberOfCursorStepsInGradUnit = 5.0f;
|
||||
bool panToMakePointVisible(float x, float y, float xMargin, float yMargin);
|
||||
void centerAxisAround(Axis axis, float position);
|
||||
virtual void initCursorPosition() = 0;
|
||||
virtual bool computeYaxis() = 0;
|
||||
|
||||
// Cursor
|
||||
float m_xCursorPosition;
|
||||
float m_yCursorPosition;
|
||||
|
||||
// Window bounds of the data
|
||||
float m_xMin;
|
||||
float m_xMax;
|
||||
@@ -57,6 +50,8 @@ protected:
|
||||
bool m_yAuto;
|
||||
float m_xGridUnit;
|
||||
float m_yGridUnit;
|
||||
CurveViewCursor * m_cursor;
|
||||
InteractiveCurveViewRangeDelegate * m_delegate;
|
||||
};
|
||||
|
||||
#endif
|
||||
11
apps/interactive_curve_view_range_delegate.h
Normal file
11
apps/interactive_curve_view_range_delegate.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef APPS_INTERACTIVE_CURVE_VIEW_DELEGATE_H
|
||||
#define APPS_INTERACTIVE_CURVE_VIEW_DELEGATE_H
|
||||
|
||||
class InteractiveCurveViewRange;
|
||||
|
||||
class InteractiveCurveViewRangeDelegate {
|
||||
public:
|
||||
virtual bool didChangeRange(InteractiveCurveViewRange * interactiveCurveViewRange) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -2,11 +2,11 @@
|
||||
#define PROBABILITE_LAW_H
|
||||
|
||||
#include <poincare.h>
|
||||
#include "../../curve_view_window.h"
|
||||
#include "../../curve_view_range.h"
|
||||
|
||||
namespace Probability {
|
||||
|
||||
class Law : public CurveViewWindow {
|
||||
class Law : public CurveViewRange {
|
||||
public:
|
||||
enum class Type : uint8_t{
|
||||
Binomial,
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
namespace Probability {
|
||||
|
||||
LawCurveView::LawCurveView() :
|
||||
CurveView(nullptr, 0.2f, 0.1f, 0.2f, 0.1f),
|
||||
CurveView(nullptr, nullptr, nullptr, nullptr, 0.2f, 0.1f, 0.2f, 0.1f),
|
||||
m_law(nullptr),
|
||||
m_calculation(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void LawCurveView::setLaw(Law * law) {
|
||||
setCurveViewWindow(law);
|
||||
setCurveViewRange(law);
|
||||
m_law = law;
|
||||
}
|
||||
|
||||
@@ -20,11 +20,6 @@ void LawCurveView::setCalculation(Calculation * calculation) {
|
||||
m_calculation = calculation;
|
||||
}
|
||||
|
||||
void LawCurveView::reload() {
|
||||
markRectAsDirty(bounds());
|
||||
computeLabels(Axis::Horizontal);
|
||||
}
|
||||
|
||||
void LawCurveView::drawRect(KDContext * ctx, KDRect rect) const {
|
||||
float lowerBound = m_calculation->lowerBound();
|
||||
float upperBound = m_calculation->upperBound();
|
||||
@@ -39,7 +34,9 @@ void LawCurveView::drawRect(KDContext * ctx, KDRect rect) const {
|
||||
}
|
||||
|
||||
char * LawCurveView::label(Axis axis, int index) const {
|
||||
assert(axis == Axis::Horizontal);
|
||||
if (axis == Axis::Vertical) {
|
||||
return nullptr;
|
||||
}
|
||||
return (char *)m_labels[index];
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ public:
|
||||
LawCurveView();
|
||||
void setLaw(Law * law);
|
||||
void setCalculation(Calculation * calculation);
|
||||
void reload() override;
|
||||
void drawRect(KDContext * ctx, KDRect rect) const override;
|
||||
protected:
|
||||
char * label(Axis axis, int index) const override;
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
#include "window_parameter_controller.h"
|
||||
#include "range_parameter_controller.h"
|
||||
#include "apps_container.h"
|
||||
#include <assert.h>
|
||||
|
||||
WindowParameterController::WindowParameterController(Responder * parentResponder, CurveViewWindowWithCursor * graphWindow) :
|
||||
RangeParameterController::RangeParameterController(Responder * parentResponder, InteractiveCurveViewRange * interactiveRange) :
|
||||
FloatParameterController(parentResponder),
|
||||
m_graphWindow(graphWindow),
|
||||
m_windowCells{EditableTextMenuListCell(&m_selectableTableView, this, m_draftTextBuffer, (char *)"Xmin"), EditableTextMenuListCell(&m_selectableTableView, this, m_draftTextBuffer, (char *)"Xmax"),
|
||||
m_interactiveRange(interactiveRange),
|
||||
m_rangeCells{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"))
|
||||
{
|
||||
}
|
||||
|
||||
const char * WindowParameterController::title() const {
|
||||
const char * RangeParameterController::title() const {
|
||||
return "Axes";
|
||||
}
|
||||
|
||||
void WindowParameterController::willDisplayCellForIndex(TableViewCell * cell, int index) {
|
||||
void RangeParameterController::willDisplayCellForIndex(TableViewCell * cell, int index) {
|
||||
if (index == 2) {
|
||||
SwitchView * switchView = (SwitchView *)m_yAutoCell.accessoryView();
|
||||
switchView->setState(m_graphWindow->yAuto());
|
||||
switchView->setState(m_interactiveRange->yAuto());
|
||||
return;
|
||||
}
|
||||
if (index == 3 || index == 4) {
|
||||
if (m_graphWindow->yAuto()) {
|
||||
m_windowCells[index-1].setTextColor(Palette::DesactiveTextColor);
|
||||
if (m_interactiveRange->yAuto()) {
|
||||
m_rangeCells[index-1].setTextColor(Palette::DesactiveTextColor);
|
||||
} else {
|
||||
m_windowCells[index-1].setTextColor(KDColorBlack);
|
||||
m_rangeCells[index-1].setTextColor(KDColorBlack);
|
||||
}
|
||||
}
|
||||
FloatParameterController::willDisplayCellForIndex(cell, index);
|
||||
}
|
||||
|
||||
bool WindowParameterController::textFieldDidFinishEditing(TextField * textField, const char * text) {
|
||||
bool RangeParameterController::textFieldDidFinishEditing(TextField * textField, const char * text) {
|
||||
AppsContainer * appsContainer = (AppsContainer *)app()->container();
|
||||
Context * globalContext = appsContainer->globalContext();
|
||||
float floatBody = Expression::parse(text)->approximate(*globalContext);
|
||||
@@ -42,7 +42,7 @@ bool WindowParameterController::textFieldDidFinishEditing(TextField * textField,
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowParameterController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) {
|
||||
void RangeParameterController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) {
|
||||
if (previousSelectedCellX == 0 && previousSelectedCellY >= 0 && previousSelectedCellY !=2) {
|
||||
EditableTextMenuListCell * myCell = (EditableTextMenuListCell *)t->cellAtLocation(previousSelectedCellX, previousSelectedCellY);
|
||||
myCell->setEditing(false);
|
||||
@@ -50,76 +50,76 @@ void WindowParameterController::tableViewDidChangeSelection(SelectableTableView
|
||||
}
|
||||
if (t->selectedColumn() == 0 && t->selectedRow() >= 0 && t->selectedRow() !=2) {
|
||||
EditableTextMenuListCell * myNewCell = (EditableTextMenuListCell *)t->cellAtLocation(t->selectedColumn(), t->selectedRow());
|
||||
if ((t->selectedRow() == 0 || t->selectedRow() == 1) || !m_graphWindow->yAuto()) {
|
||||
if ((t->selectedRow() == 0 || t->selectedRow() == 1) || !m_interactiveRange->yAuto()) {
|
||||
app()->setFirstResponder(myNewCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowParameterController::handleEvent(Ion::Events::Event event) {
|
||||
bool RangeParameterController::handleEvent(Ion::Events::Event event) {
|
||||
if (activeCell() == 2) {
|
||||
if (event == Ion::Events::OK) {
|
||||
m_graphWindow->setYAuto(!m_graphWindow->yAuto());
|
||||
m_interactiveRange->setYAuto(!m_interactiveRange->yAuto());
|
||||
m_selectableTableView.reloadData();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (m_graphWindow->yAuto() && (activeCell() == 3 || activeCell() == 4)) {
|
||||
if (m_interactiveRange->yAuto() && (activeCell() == 3 || activeCell() == 4)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float WindowParameterController::parameterAtIndex(int index) {
|
||||
float RangeParameterController::parameterAtIndex(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return m_graphWindow->xMin();
|
||||
return m_interactiveRange->xMin();
|
||||
case 1:
|
||||
return m_graphWindow->xMax();
|
||||
return m_interactiveRange->xMax();
|
||||
case 3:
|
||||
return m_graphWindow->yMin();
|
||||
return m_interactiveRange->yMin();
|
||||
case 4:
|
||||
return m_graphWindow->yMax();
|
||||
return m_interactiveRange->yMax();
|
||||
default:
|
||||
assert(false);
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void WindowParameterController::setParameterAtIndex(int parameterIndex, float f) {
|
||||
void RangeParameterController::setParameterAtIndex(int parameterIndex, float f) {
|
||||
switch(parameterIndex) {
|
||||
case 0:
|
||||
m_graphWindow->setXMin(f);
|
||||
m_interactiveRange->setXMin(f);
|
||||
break;
|
||||
case 1:
|
||||
m_graphWindow->setXMax(f);
|
||||
m_interactiveRange->setXMax(f);
|
||||
break;
|
||||
case 3:
|
||||
m_graphWindow->setYMin(f);
|
||||
m_interactiveRange->setYMin(f);
|
||||
break;
|
||||
case 4:
|
||||
m_graphWindow->setYMax(f);
|
||||
m_interactiveRange->setYMax(f);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
int WindowParameterController::numberOfRows() {
|
||||
int RangeParameterController::numberOfRows() {
|
||||
return k_numberOfTextCell+1;
|
||||
};
|
||||
|
||||
TableViewCell * WindowParameterController::reusableCell(int index) {
|
||||
TableViewCell * RangeParameterController::reusableCell(int index) {
|
||||
if (index == 2) {
|
||||
return &m_yAutoCell;
|
||||
}
|
||||
int cellIndex = index > 2 ? index - 1 : index;
|
||||
assert(cellIndex >= 0);
|
||||
assert(cellIndex < k_numberOfTextCell);
|
||||
return &m_windowCells[cellIndex];
|
||||
return &m_rangeCells[cellIndex];
|
||||
}
|
||||
|
||||
int WindowParameterController::reusableCellCount() {
|
||||
int RangeParameterController::reusableCellCount() {
|
||||
return k_numberOfTextCell+1;
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
#ifndef APPS_WINDOW_PARAMETER_CONTROLLER_H
|
||||
#define APPS_WINDOW_PARAMETER_CONTROLLER_H
|
||||
#ifndef APPS_RANGE_PARAMETER_CONTROLLER_H
|
||||
#define APPS_RANGE_PARAMETER_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "curve_view_window_with_cursor.h"
|
||||
#include "interactive_curve_view_range.h"
|
||||
#include "float_parameter_controller.h"
|
||||
|
||||
class WindowParameterController : public FloatParameterController {
|
||||
class RangeParameterController : public FloatParameterController {
|
||||
public:
|
||||
WindowParameterController(Responder * parentResponder, CurveViewWindowWithCursor * graphWindow);
|
||||
RangeParameterController(Responder * parentResponder, InteractiveCurveViewRange * interactiveCurveViewRange);
|
||||
const char * title() const override;
|
||||
int numberOfRows() override;
|
||||
TableViewCell * reusableCell(int index) override;
|
||||
@@ -20,9 +20,9 @@ private:
|
||||
float parameterAtIndex(int index) override;
|
||||
void setParameterAtIndex(int parameterIndex, float f) override;
|
||||
constexpr static int k_numberOfTextCell = 4;
|
||||
CurveViewWindowWithCursor * m_graphWindow;
|
||||
InteractiveCurveViewRange * m_interactiveRange;
|
||||
char m_draftTextBuffer[EditableTextMenuListCell::k_bufferLength];
|
||||
EditableTextMenuListCell m_windowCells[k_numberOfTextCell];
|
||||
EditableTextMenuListCell m_rangeCells[k_numberOfTextCell];
|
||||
SwitchMenuListCell m_yAutoCell;
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ private:
|
||||
constexpr static int k_totalNumberOfColumns = 2;
|
||||
constexpr static int k_maxNumberOfDisplayableRows = 10;
|
||||
static constexpr KDCoordinate k_cellHeight = 25;
|
||||
static constexpr KDCoordinate k_cellWidth = 320/2 - Metric::RightMargin/2 - Metric::LeftMargin/2;
|
||||
static constexpr KDCoordinate k_cellWidth = Ion::Display::Width/2 - Metric::RightMargin/2 - Metric::LeftMargin/2;
|
||||
EvenOddPointerTextCell m_titleCells[k_maxNumberOfDisplayableRows];
|
||||
EvenOddDoubleBufferTextCell m_columnTitleCell;
|
||||
EvenOddDoubleBufferTextCell m_doubleCalculationCells[k_maxNumberOfDisplayableRows/2];
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
|
||||
namespace Regression {
|
||||
|
||||
GoToParameterController::GoToParameterController(Responder * parentResponder, Store * store) :
|
||||
GoToParameterController::GoToParameterController(Responder * parentResponder, Store * store, CurveViewCursor * cursor) :
|
||||
FloatParameterController(parentResponder),
|
||||
m_abscisseCell(EditableTextMenuListCell(&m_selectableTableView, this, m_draftTextBuffer)),
|
||||
m_store(store),
|
||||
m_cursor(cursor),
|
||||
m_xPrediction(true)
|
||||
{
|
||||
}
|
||||
@@ -25,17 +26,23 @@ const char * GoToParameterController::title() const {
|
||||
float GoToParameterController::parameterAtIndex(int index) {
|
||||
assert(index == 0);
|
||||
if (m_xPrediction) {
|
||||
return m_store->xCursorPosition();
|
||||
return m_cursor->x();
|
||||
}
|
||||
return m_store->yCursorPosition();
|
||||
return m_cursor->y();
|
||||
}
|
||||
|
||||
void GoToParameterController::setParameterAtIndex(int parameterIndex, float f) {
|
||||
assert(parameterIndex == 0);
|
||||
if (m_xPrediction) {
|
||||
m_store->setCursorPositionAtAbscissa(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);
|
||||
} else {
|
||||
m_store->setCursorPositionAtOrdinate(f);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
#include <escher.h>
|
||||
#include "../float_parameter_controller.h"
|
||||
#include "../curve_view_cursor.h"
|
||||
#include "store.h"
|
||||
|
||||
namespace Regression {
|
||||
|
||||
class GoToParameterController : public FloatParameterController {
|
||||
public:
|
||||
GoToParameterController(Responder * parentResponder, Store * store);
|
||||
GoToParameterController(Responder * parentResponder, Store * store, CurveViewCursor * cursor);
|
||||
void setXPrediction(bool xPrediction);
|
||||
const char * title() const override;
|
||||
int numberOfRows() override;
|
||||
@@ -22,6 +23,7 @@ private:
|
||||
char m_draftTextBuffer[EditableTextMenuListCell::k_bufferLength];
|
||||
EditableTextMenuListCell m_abscisseCell;
|
||||
Store * m_store;
|
||||
CurveViewCursor * m_cursor;
|
||||
bool m_xPrediction;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,29 +3,21 @@
|
||||
namespace Regression {
|
||||
|
||||
GraphController::GraphController(Responder * parentResponder, HeaderViewController * headerViewController, Store * store) :
|
||||
CurveViewWindowWithBannerAndCursorController(parentResponder, headerViewController, store, &m_view),
|
||||
m_view(GraphView(store, &m_bannerView, &m_cursorView)),
|
||||
InteractiveCurveViewController(parentResponder, headerViewController, store, &m_view),
|
||||
m_bannerView(BannerView()),
|
||||
m_view(GraphView(store, &m_cursor, &m_bannerView, &m_cursorView)),
|
||||
m_store(store),
|
||||
m_initialisationParameterController(InitialisationParameterController(this, m_store)),
|
||||
m_predictionParameterController(PredictionParameterController(this, m_store))
|
||||
m_predictionParameterController(PredictionParameterController(this, m_store, &m_cursor)),
|
||||
m_selectedDotIndex(-1)
|
||||
{
|
||||
m_store->setCursor(&m_cursor);
|
||||
}
|
||||
|
||||
ViewController * GraphController::initialisationParameterController() {
|
||||
return &m_initialisationParameterController;
|
||||
}
|
||||
|
||||
void GraphController::didBecomeFirstResponder() {
|
||||
uint32_t storeChecksum = m_store->checksum();
|
||||
if (m_storeVersion != storeChecksum) {
|
||||
m_storeVersion = storeChecksum;
|
||||
m_store->initWindowParameters();
|
||||
m_store->initCursorPosition();
|
||||
}
|
||||
CurveViewWindowWithBannerAndCursorController::didBecomeFirstResponder();
|
||||
}
|
||||
|
||||
|
||||
bool GraphController::isEmpty() {
|
||||
if (m_store->numberOfPairs() < 2 || isinf(m_store->slope()) || isnan(m_store->slope())) {
|
||||
return true;
|
||||
@@ -40,8 +32,20 @@ const char * GraphController::emptyMessage() {
|
||||
return "Pas assez de donnees pour un regression";
|
||||
}
|
||||
|
||||
BannerView * GraphController::bannerView() {
|
||||
return &m_bannerView;
|
||||
}
|
||||
|
||||
CurveView * GraphController::curveView() {
|
||||
return &m_view;
|
||||
}
|
||||
|
||||
InteractiveCurveViewRange * GraphController::interactiveCurveViewRange() {
|
||||
return m_store;
|
||||
}
|
||||
|
||||
bool GraphController::handleEnter() {
|
||||
if (m_store->selectedDotIndex() == -1) {
|
||||
if (m_selectedDotIndex == -1) {
|
||||
stackController()->push(&m_predictionParameterController);
|
||||
return true;
|
||||
}
|
||||
@@ -66,9 +70,9 @@ void GraphController::reloadBannerView() {
|
||||
m_bannerView.setLegendAtIndex(buffer, 2);
|
||||
|
||||
legend = "x = ";
|
||||
float x = m_store->xCursorPosition();
|
||||
float x = m_cursor.x();
|
||||
// Display a specific legend if the mean dot is selected
|
||||
if (m_store->selectedDotIndex() == m_store->numberOfPairs()) {
|
||||
if (m_selectedDotIndex == m_store->numberOfPairs()) {
|
||||
legend = "x^ = ";
|
||||
x = m_store->meanOfColumn(0);
|
||||
}
|
||||
@@ -78,8 +82,8 @@ void GraphController::reloadBannerView() {
|
||||
m_bannerView.setLegendAtIndex(buffer, 3);
|
||||
|
||||
legend = "y = ";
|
||||
float y = m_store->yCursorPosition();
|
||||
if (m_store->selectedDotIndex() == m_store->numberOfPairs()) {
|
||||
float y = m_cursor.y();
|
||||
if (m_selectedDotIndex == m_store->numberOfPairs()) {
|
||||
legend = "y^ = ";
|
||||
y = m_store->meanOfColumn(1);
|
||||
}
|
||||
@@ -91,4 +95,66 @@ void GraphController::reloadBannerView() {
|
||||
m_bannerView.layoutSubviews();
|
||||
}
|
||||
|
||||
void GraphController::initRangeParameters() {
|
||||
m_store->setDefault();
|
||||
}
|
||||
|
||||
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_selectedDotIndex = -1;
|
||||
}
|
||||
|
||||
int 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));
|
||||
}
|
||||
if (dotSelected == m_store->numberOfPairs()) {
|
||||
m_selectedDotIndex = dotSelected;
|
||||
return m_store->moveCursorTo(m_store->meanOfColumn(0), m_store->meanOfColumn(1));
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int 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);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} 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));
|
||||
}
|
||||
if (dotSelected == m_store->numberOfPairs()) {
|
||||
m_selectedDotIndex = dotSelected;
|
||||
return m_store->moveCursorTo(m_store->meanOfColumn(0), m_store->meanOfColumn(1));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GraphController::modelVersion() {
|
||||
return m_store->storeChecksum();
|
||||
}
|
||||
|
||||
uint32_t GraphController::rangeVersion() {
|
||||
return m_store->rangeChecksum();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,28 +7,38 @@
|
||||
#include "graph_view.h"
|
||||
#include "initialisation_parameter_controller.h"
|
||||
#include "prediction_parameter_controller.h"
|
||||
#include "../curve_view_with_banner_and_cursor_controller.h"
|
||||
#include "../interactive_curve_view_controller.h"
|
||||
|
||||
namespace Regression {
|
||||
|
||||
class GraphController : public CurveViewWindowWithBannerAndCursorController {
|
||||
class GraphController : public InteractiveCurveViewController {
|
||||
|
||||
public:
|
||||
GraphController(Responder * parentResponder, HeaderViewController * headerViewController, Store * store);
|
||||
ViewController * initialisationParameterController() override;
|
||||
void didBecomeFirstResponder() override;
|
||||
bool isEmpty() override;
|
||||
const char * emptyMessage() override;
|
||||
private:
|
||||
constexpr static int k_maxNumberOfCharacters = 8;
|
||||
BannerView * bannerView() override;
|
||||
CurveView * curveView() override;
|
||||
InteractiveCurveViewRange * interactiveCurveViewRange() override;
|
||||
bool handleEnter() override;
|
||||
void reloadBannerView() override;
|
||||
void initRangeParameters() override;
|
||||
void initCursorParameters() override;
|
||||
int moveCursorHorizontally(int direction) override;
|
||||
int moveCursorVertically(int direction) override;
|
||||
uint32_t modelVersion() override;
|
||||
uint32_t rangeVersion() override;
|
||||
BannerView m_bannerView;
|
||||
GraphView m_view;
|
||||
Store * m_store;
|
||||
uint32_t m_storeVersion;
|
||||
InitialisationParameterController m_initialisationParameterController;
|
||||
PredictionParameterController m_predictionParameterController;
|
||||
/* The selectedDotIndex is -1 when no dot is selected, m_numberOfPairs when
|
||||
* the mean dot is selected and the dot index otherwise */
|
||||
int m_selectedDotIndex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
namespace Regression {
|
||||
|
||||
GraphView::GraphView(Store * store, BannerView * bannerView, CursorView * cursorView) :
|
||||
CurveViewWithBannerAndCursor(store, bannerView, cursorView, 0.05f, 0.05f, 0.25f, 0.05f),
|
||||
GraphView::GraphView(Store * store, CurveViewCursor * cursor, View * bannerView, View * cursorView) :
|
||||
CurveView(store, cursor, bannerView, cursorView, 0.05f, 0.05f, 0.25f, 0.05f),
|
||||
m_store(store)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
#include <escher.h>
|
||||
#include "store.h"
|
||||
#include "../constant.h"
|
||||
#include "../cursor_view.h"
|
||||
#include "../curve_view_with_banner_and_cursor.h"
|
||||
#include "../curve_view.h"
|
||||
|
||||
namespace Regression {
|
||||
|
||||
class GraphView : public CurveViewWithBannerAndCursor {
|
||||
class GraphView : public CurveView {
|
||||
public:
|
||||
GraphView(Store * store, ::BannerView * bannerView, CursorView * cursorView);
|
||||
GraphView(Store * store, CurveViewCursor * cursor, View * bannerView, View * cursorView);
|
||||
void drawRect(KDContext * ctx, KDRect rect) const override;
|
||||
private:
|
||||
constexpr static KDCoordinate k_dotSize = 5;
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
namespace Regression {
|
||||
|
||||
PredictionParameterController::PredictionParameterController(Responder * parentResponder, Store * store) :
|
||||
PredictionParameterController::PredictionParameterController(Responder * parentResponder, Store * store, CurveViewCursor * cursor) :
|
||||
ViewController(parentResponder),
|
||||
m_selectableTableView(SelectableTableView(this, this, Metric::TopMargin, Metric::RightMargin,
|
||||
Metric::BottomMargin, Metric::LeftMargin)),
|
||||
m_goToParameterController(GoToParameterController(this, store))
|
||||
m_goToParameterController(GoToParameterController(this, store, cursor))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
#include <escher.h>
|
||||
#include "store.h"
|
||||
#include "../curve_view_cursor.h"
|
||||
#include "go_to_parameter_controller.h"
|
||||
|
||||
namespace Regression {
|
||||
|
||||
class PredictionParameterController : public ViewController, public SimpleListViewDataSource {
|
||||
public:
|
||||
PredictionParameterController(Responder * parentResponder, Store * store);
|
||||
PredictionParameterController(Responder * parentResponder, Store * store, CurveViewCursor * cursor);
|
||||
View * view() override;
|
||||
const char * title() const override;
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
|
||||
@@ -7,79 +7,139 @@
|
||||
namespace Regression {
|
||||
|
||||
Store::Store() :
|
||||
CurveViewWindowWithCursor(),
|
||||
FloatPairStore(),
|
||||
m_selectedDotIndex(-1)
|
||||
InteractiveCurveViewRange(nullptr, this),
|
||||
FloatPairStore()
|
||||
{
|
||||
}
|
||||
|
||||
/* Cursor */
|
||||
|
||||
void Store::initCursorPosition() {
|
||||
m_xCursorPosition = (m_xMin+m_xMax)/2.0f;
|
||||
m_yCursorPosition = yValueForXValue(m_xCursorPosition);
|
||||
m_selectedDotIndex = -1;
|
||||
}
|
||||
|
||||
int Store::moveCursorVertically(int direction) {
|
||||
float yRegressionCurve = yValueForXValue(m_xCursorPosition);
|
||||
if (m_selectedDotIndex >= 0) {
|
||||
if ((yRegressionCurve - m_yCursorPosition > 0) == (direction > 0)) {
|
||||
m_selectedDotIndex = -1;
|
||||
m_yCursorPosition = yRegressionCurve;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
int dotSelected = selectClosestDotRelativelyToCurve(direction);
|
||||
if (dotSelected >= 0) {
|
||||
m_selectedDotIndex = dotSelected;
|
||||
} else {
|
||||
return -1;
|
||||
bool Store::didChangeRange(InteractiveCurveViewRange * interactiveCurveViewRange) {
|
||||
if (!m_yAuto) {
|
||||
return false;
|
||||
}
|
||||
float min = m_yMin;
|
||||
float max = m_yMax;
|
||||
for (int k = 0; k < m_numberOfPairs; k++) {
|
||||
if (m_xMin <= m_data[0][k] && m_data[0][k] <= m_xMax) {
|
||||
if (m_data[1][k] < min) {
|
||||
min = m_data[1][k];
|
||||
}
|
||||
if (m_data[1][k] > max) {
|
||||
max = m_data[1][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
bool windowHasMoved = panToMakePointVisible(m_xCursorPosition, m_yCursorPosition, 0.0f, 0.0f);
|
||||
return windowHasMoved;
|
||||
if (min == m_yMin && max == m_yMax) {
|
||||
return false;
|
||||
}
|
||||
m_yMin = min;
|
||||
m_yMax = max;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
return true;
|
||||
}
|
||||
|
||||
int Store::moveCursorHorizontally(int direction) {
|
||||
if (m_selectedDotIndex >= 0) {
|
||||
int dotSelected = selectNextDot(direction);
|
||||
if (dotSelected >= 0) {
|
||||
m_selectedDotIndex = dotSelected;
|
||||
} else {
|
||||
return -1;
|
||||
/* Dots */
|
||||
|
||||
int Store::closestVerticalDot(int direction, float x) {
|
||||
float nextX = INFINITY;
|
||||
float nextY = INFINITY;
|
||||
int selectedDot = -1;
|
||||
/* The conditions to test on all dots are in this order:
|
||||
* - the next dot should be within the window abscissa bounds
|
||||
* - the next dot is the closest one in abscissa to x
|
||||
* - the next dot is above the regression curve if direction == 1 and below
|
||||
* otherwise */
|
||||
for (int index = 0; index < m_numberOfPairs; index++) {
|
||||
if ((m_xMin <= m_data[0][index] && m_data[0][index] <= m_xMax) &&
|
||||
(fabsf(m_data[0][index] - x) < fabsf(nextX - x)) &&
|
||||
((m_data[1][index] - yValueForXValue(m_data[0][index]) >= 0) == (direction > 0))) {
|
||||
// Handle edge case: if 2 dots have the same abscissa but different ordinates
|
||||
if (nextX != m_data[0][index] || ((nextY - m_data[1][index] >= 0) == (direction > 0))) {
|
||||
nextX = m_data[0][index];
|
||||
nextY = m_data[1][index];
|
||||
selectedDot = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Compare with the mean dot
|
||||
if (m_xMin <= meanOfColumn(0) && meanOfColumn(0) <= m_xMax &&
|
||||
(fabsf(meanOfColumn(0) - x) < fabsf(nextX - x)) &&
|
||||
((meanOfColumn(1) - yValueForXValue(meanOfColumn(0)) >= 0) == (direction > 0))) {
|
||||
if (nextX != meanOfColumn(0) || ((nextY - meanOfColumn(1) >= 0) == (direction > 0))) {
|
||||
nextX = meanOfColumn(0);
|
||||
nextY = meanOfColumn(1);
|
||||
selectedDot = m_numberOfPairs;
|
||||
}
|
||||
}
|
||||
return selectedDot;
|
||||
}
|
||||
|
||||
int Store::nextDot(int direction, int dot) {
|
||||
float nextX = INFINITY;
|
||||
float nextY = INFINITY;
|
||||
int selectedDot = -1;
|
||||
float x = meanOfColumn(0);
|
||||
if (dot >= 0 && dot < m_numberOfPairs) {
|
||||
x = get(0, dot);
|
||||
}
|
||||
/* We have to scan the Store in opposite ways for the 2 directions to ensure to
|
||||
* select all dots (even with equal abscissa) */
|
||||
if (direction > 0) {
|
||||
for (int index = 0; index < m_numberOfPairs; index++) {
|
||||
/* The conditions to test are in this order:
|
||||
* - the next dot is the closest one in abscissa to x
|
||||
* - the next dot is not the same as the selected one
|
||||
* - the next dot is at the right of the selected one */
|
||||
if (fabsf(m_data[0][index] - x) < fabsf(nextX - x) &&
|
||||
(index != dot) &&
|
||||
(m_data[0][index] >= x)) {
|
||||
// Handle edge case: 2 dots have same abscissa
|
||||
if (m_data[0][index] != x || (index > dot)) {
|
||||
nextX = m_data[0][index];
|
||||
nextY = m_data[1][index];
|
||||
selectedDot = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Compare with the mean dot
|
||||
if (fabsf(meanOfColumn(0) - x) < fabsf(nextX - x) &&
|
||||
(m_numberOfPairs != dot) &&
|
||||
(meanOfColumn(0) >= x)) {
|
||||
if (meanOfColumn(0) != x || (x > dot)) {
|
||||
nextX = meanOfColumn(0);
|
||||
nextY = meanOfColumn(1);
|
||||
selectedDot = m_numberOfPairs;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_xCursorPosition = direction > 0 ? m_xCursorPosition + m_xGridUnit/CurveViewWindowWithCursor::k_numberOfCursorStepsInGradUnit :
|
||||
m_xCursorPosition - m_xGridUnit/CurveViewWindowWithCursor::k_numberOfCursorStepsInGradUnit;
|
||||
m_yCursorPosition = yValueForXValue(m_xCursorPosition);
|
||||
// Compare with the mean dot
|
||||
if (fabsf(meanOfColumn(0) - x) < fabsf(nextX - x) &&
|
||||
(m_numberOfPairs != dot) &&
|
||||
(meanOfColumn(0) <= x)) {
|
||||
if (meanOfColumn(0) != x || (m_numberOfPairs < dot)) {
|
||||
nextX = meanOfColumn(0);
|
||||
nextY = meanOfColumn(1);
|
||||
selectedDot = m_numberOfPairs;
|
||||
}
|
||||
}
|
||||
for (int index = m_numberOfPairs-1; index >= 0; index--) {
|
||||
if (fabsf(m_data[0][index] - x) < fabsf(nextX - x) &&
|
||||
(index != dot) &&
|
||||
(m_data[0][index] <= x)) {
|
||||
// Handle edge case: 2 dots have same abscissa
|
||||
if (m_data[0][index] != x || (index < dot)) {
|
||||
nextX = m_data[0][index];
|
||||
nextY = m_data[1][index];
|
||||
selectedDot = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool windowHasMoved = panToMakePointVisible(m_xCursorPosition, m_yCursorPosition, 0.0f, 0.0f);
|
||||
return windowHasMoved;
|
||||
}
|
||||
|
||||
int Store::selectedDotIndex() {
|
||||
return m_selectedDotIndex;
|
||||
}
|
||||
|
||||
void Store::setCursorPositionAtAbscissa(float abscissa) {
|
||||
m_xCursorPosition = abscissa;
|
||||
centerAxisAround(CurveViewWindow::Axis::X, m_xCursorPosition);
|
||||
m_yCursorPosition = yValueForXValue(m_xCursorPosition);
|
||||
centerAxisAround(CurveViewWindow::Axis::Y, m_yCursorPosition);
|
||||
}
|
||||
|
||||
void Store::setCursorPositionAtOrdinate(float ordinate) {
|
||||
m_yCursorPosition = ordinate;
|
||||
centerAxisAround(CurveViewWindow::Axis::Y, m_yCursorPosition);
|
||||
m_xCursorPosition = xValueForYValue(m_yCursorPosition);
|
||||
centerAxisAround(CurveViewWindow::Axis::X, m_xCursorPosition);
|
||||
return selectedDot;
|
||||
}
|
||||
|
||||
/* Window */
|
||||
|
||||
void Store::initWindowParameters() {
|
||||
void Store::setDefault() {
|
||||
m_xMin = minValueOfColumn(0);
|
||||
m_xMax = maxValueOfColumn(0);
|
||||
m_yMin = minValueOfColumn(1);
|
||||
@@ -96,11 +156,27 @@ void Store::initWindowParameters() {
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
}
|
||||
|
||||
void Store::setDefault() {
|
||||
initWindowParameters();
|
||||
/* Calculations */
|
||||
|
||||
float Store::maxValueOfColumn(int i) {
|
||||
float max = -FLT_MAX;
|
||||
for (int k = 0; k < m_numberOfPairs; k++) {
|
||||
if (m_data[i][k] > max) {
|
||||
max = m_data[i][k];
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/* Calculations */
|
||||
float Store::minValueOfColumn(int i) {
|
||||
float min = FLT_MAX;
|
||||
for (int k = 0; k < m_numberOfPairs; k++) {
|
||||
if (m_data[i][k] < min) {
|
||||
min = m_data[i][k];
|
||||
}
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
float Store::squaredValueSumOfColumn(int i) {
|
||||
float result = 0;
|
||||
@@ -160,152 +236,4 @@ float Store::squaredCorrelationCoefficient() {
|
||||
return cov*cov/(varianceOfColumn(0)*varianceOfColumn(1));
|
||||
}
|
||||
|
||||
/* private methods */
|
||||
|
||||
float Store::maxValueOfColumn(int i) {
|
||||
float max = -FLT_MAX;
|
||||
for (int k = 0; k < m_numberOfPairs; k++) {
|
||||
if (m_data[i][k] > max) {
|
||||
max = m_data[i][k];
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
float Store::minValueOfColumn(int i) {
|
||||
float min = FLT_MAX;
|
||||
for (int k = 0; k < m_numberOfPairs; k++) {
|
||||
if (m_data[i][k] < min) {
|
||||
min = m_data[i][k];
|
||||
}
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
bool Store::computeYaxis() {
|
||||
float min = m_yMin;
|
||||
float max = m_yMax;
|
||||
for (int k = 0; k < m_numberOfPairs; k++) {
|
||||
if (m_xMin <= m_data[0][k] && m_data[0][k] <= m_xMax) {
|
||||
if (m_data[1][k] < min) {
|
||||
min = m_data[1][k];
|
||||
}
|
||||
if (m_data[1][k] > max) {
|
||||
max = m_data[1][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (min == m_yMin && max == m_yMax) {
|
||||
return false;
|
||||
}
|
||||
m_yMin = min;
|
||||
m_yMax = max;
|
||||
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
|
||||
return true;
|
||||
}
|
||||
|
||||
int Store::selectClosestDotRelativelyToCurve(int direction) {
|
||||
float nextX = INFINITY;
|
||||
float nextY = INFINITY;
|
||||
int selectedDot = -1;
|
||||
/* The conditions to test on all dots are in this order:
|
||||
* - the next dot should be within the window abscissa bounds
|
||||
* - the next dot is the closest one in abscissa
|
||||
* - the next dot is above the selected one if direction == 1 and below
|
||||
* otherwise */
|
||||
for (int index = 0; index < m_numberOfPairs; index++) {
|
||||
if ((m_xMin <= m_data[0][index] && m_data[0][index] <= m_xMax) &&
|
||||
(fabsf(m_data[0][index] - m_xCursorPosition) < fabsf(nextX - m_xCursorPosition)) &&
|
||||
((m_data[1][index] - yValueForXValue(m_data[0][index]) >= 0) == (direction > 0))) {
|
||||
// Handle edge case: if 2 dots have the same abscissa but different ordinates
|
||||
if (nextX != m_data[0][index] || ((nextY - m_data[1][index] >= 0) == (direction > 0))) {
|
||||
nextX = m_data[0][index];
|
||||
nextY = m_data[1][index];
|
||||
selectedDot = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Compare with the mean dot
|
||||
if (m_xMin <= meanOfColumn(0) && meanOfColumn(0) <= m_xMax &&
|
||||
(fabsf(meanOfColumn(0) - m_xCursorPosition) < fabsf(nextX - m_xCursorPosition)) &&
|
||||
((meanOfColumn(1) - yValueForXValue(meanOfColumn(0)) >= 0) == (direction > 0))) {
|
||||
if (nextX != meanOfColumn(0) || ((nextY - meanOfColumn(1) >= 0) == (direction > 0))) {
|
||||
nextX = meanOfColumn(0);
|
||||
nextY = meanOfColumn(1);
|
||||
selectedDot = m_numberOfPairs;
|
||||
}
|
||||
}
|
||||
if (!isinf(nextX) && !isinf(nextY)) {
|
||||
m_xCursorPosition = nextX;
|
||||
m_yCursorPosition = nextY;
|
||||
return selectedDot;
|
||||
}
|
||||
return selectedDot;
|
||||
}
|
||||
|
||||
int Store::selectNextDot(int direction) {
|
||||
float nextX = INFINITY;
|
||||
float nextY = INFINITY;
|
||||
int selectedDot = -1;
|
||||
/* We have to scan the Store in opposite ways for the 2 directions to ensure to
|
||||
* select all dots (even with equal abscissa) */
|
||||
if (direction > 0) {
|
||||
for (int index = 0; index < m_numberOfPairs; index++) {
|
||||
/* The conditions to test are in this order:
|
||||
* - the next dot is the closest one in abscissa
|
||||
* - the next dot is not the same as the selected one
|
||||
* - the next dot is at the right of the selected one */
|
||||
if (fabsf(m_data[0][index] - m_xCursorPosition) < fabsf(nextX - m_xCursorPosition) &&
|
||||
(index != m_selectedDotIndex) &&
|
||||
(m_data[0][index] >= m_xCursorPosition)) {
|
||||
// Handle edge case: 2 dots have same abscissa
|
||||
if (m_data[0][index] != m_xCursorPosition || (index > m_selectedDotIndex)) {
|
||||
nextX = m_data[0][index];
|
||||
nextY = m_data[1][index];
|
||||
selectedDot = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Compare with the mean dot
|
||||
if (fabsf(meanOfColumn(0) - m_xCursorPosition) < fabsf(nextX - m_xCursorPosition) &&
|
||||
(m_numberOfPairs != m_selectedDotIndex) &&
|
||||
(meanOfColumn(0) >= m_xCursorPosition)) {
|
||||
if (meanOfColumn(0) != m_xCursorPosition || (m_numberOfPairs > m_selectedDotIndex)) {
|
||||
nextX = meanOfColumn(0);
|
||||
nextY = meanOfColumn(1);
|
||||
selectedDot = m_numberOfPairs;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Compare with the mean dot
|
||||
if (fabsf(meanOfColumn(0) - m_xCursorPosition) < fabsf(nextX - m_xCursorPosition) &&
|
||||
(m_numberOfPairs != m_selectedDotIndex) &&
|
||||
(meanOfColumn(0) <= m_xCursorPosition)) {
|
||||
if (meanOfColumn(0) != m_xCursorPosition || (m_numberOfPairs < m_selectedDotIndex)) {
|
||||
nextX = meanOfColumn(0);
|
||||
nextY = meanOfColumn(1);
|
||||
selectedDot = m_numberOfPairs;
|
||||
}
|
||||
}
|
||||
for (int index = m_numberOfPairs-1; index >= 0; index--) {
|
||||
if (fabsf(m_data[0][index] - m_xCursorPosition) < fabsf(nextX - m_xCursorPosition) &&
|
||||
(index != m_selectedDotIndex) &&
|
||||
(m_data[0][index] <= m_xCursorPosition)) {
|
||||
// Handle edge case: 2 dots have same abscissa
|
||||
if (m_data[0][index] != m_xCursorPosition || (index < m_selectedDotIndex)) {
|
||||
nextX = m_data[0][index];
|
||||
nextY = m_data[1][index];
|
||||
selectedDot = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isinf(nextX) && !isinf(nextY)) {
|
||||
m_xCursorPosition = nextX;
|
||||
m_yCursorPosition = nextY;
|
||||
return selectedDot;
|
||||
}
|
||||
return selectedDot;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
#ifndef REGRESSION_STORE_H
|
||||
#define REGRESSION_STORE_H
|
||||
|
||||
#include "../curve_view_window_with_cursor.h"
|
||||
#include "../interactive_curve_view_range.h"
|
||||
#include "../float_pair_store.h"
|
||||
|
||||
namespace Regression {
|
||||
|
||||
class Store : public CurveViewWindowWithCursor, public FloatPairStore {
|
||||
class Store : public InteractiveCurveViewRange, public FloatPairStore, public InteractiveCurveViewRangeDelegate {
|
||||
public:
|
||||
Store();
|
||||
// Cursor
|
||||
void initCursorPosition() override;
|
||||
// the result of moveCursorVertically means:
|
||||
// 0-> the window has not changed 1->the window changed
|
||||
int moveCursorVertically(int direction) override;
|
||||
int moveCursorHorizontally(int direction) override;
|
||||
/* The selectedDotIndex is -1 when no dot is selected, m_numberOfPairs when
|
||||
* the mean dot is selected and the dot index otherwise */
|
||||
int selectedDotIndex();
|
||||
void setCursorPositionAtAbscissa(float abscissa);
|
||||
void setCursorPositionAtOrdinate(float ordinate);
|
||||
bool didChangeRange(InteractiveCurveViewRange * interactiveCurveViewRange) override;
|
||||
|
||||
// Dots
|
||||
/* Return the closest dot to x above the regression curve if direction > 0,
|
||||
* below otherwise*/
|
||||
int closestVerticalDot(int direction, float x);
|
||||
/* Return the closest dot to dot given on the right if direction > 0,
|
||||
* on the left otherwise*/
|
||||
int nextDot(int direction, int dot);
|
||||
|
||||
// Window
|
||||
void initWindowParameters();
|
||||
void setDefault();
|
||||
void setDefault() override;
|
||||
|
||||
// Calculation
|
||||
float squaredValueSumOfColumn(int i);
|
||||
@@ -41,13 +38,6 @@ public:
|
||||
private:
|
||||
float maxValueOfColumn(int i);
|
||||
float minValueOfColumn(int i);
|
||||
|
||||
bool computeYaxis() override;
|
||||
int selectClosestDotRelativelyToCurve(int direction);
|
||||
int selectNextDot(int direction);
|
||||
|
||||
// Cursor
|
||||
int m_selectedDotIndex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ app_objs += $(addprefix apps/statistics/,\
|
||||
app.o\
|
||||
box_banner_view.o\
|
||||
box_controller.o\
|
||||
box_range.o\
|
||||
box_view.o\
|
||||
box_window.o\
|
||||
calculation_controller.o\
|
||||
histogram_banner_view.o\
|
||||
histogram_controller.o\
|
||||
|
||||
@@ -37,12 +37,6 @@ bool BoxController::handleEvent(Ion::Events::Event event) {
|
||||
}
|
||||
|
||||
void BoxController::didBecomeFirstResponder() {
|
||||
uint32_t storeChecksum = m_store->checksum();
|
||||
if (m_storeVersion != storeChecksum) {
|
||||
m_storeVersion = storeChecksum;
|
||||
m_store->initWindowParameters();
|
||||
m_store->initBarParameters();
|
||||
}
|
||||
m_view.selectMainView(true);
|
||||
m_view.reload();
|
||||
reloadBannerView();
|
||||
|
||||
@@ -25,7 +25,6 @@ private:
|
||||
BoxBannerView m_boxBannerView;
|
||||
BoxView m_view;
|
||||
Store * m_store;
|
||||
uint32_t m_storeVersion;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
#include "box_window.h"
|
||||
#include "box_range.h"
|
||||
|
||||
namespace Statistics {
|
||||
|
||||
BoxWindow::BoxWindow(Store * store) :
|
||||
BoxRange::BoxRange(Store * store) :
|
||||
m_store(store)
|
||||
{
|
||||
}
|
||||
|
||||
float BoxWindow::xMin() {
|
||||
float BoxRange::xMin() {
|
||||
return m_store->minValue();
|
||||
}
|
||||
|
||||
float BoxWindow::xMax() {
|
||||
float BoxRange::xMax() {
|
||||
if (m_store->minValue() >= m_store->maxValue()) {
|
||||
return m_store->minValue() + 1.0f;
|
||||
}
|
||||
return m_store->maxValue();
|
||||
}
|
||||
|
||||
float BoxWindow::yMin() {
|
||||
float BoxRange::yMin() {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float BoxWindow::yMax() {
|
||||
float BoxRange::yMax() {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float BoxWindow::xGridUnit() {
|
||||
float BoxRange::xGridUnit() {
|
||||
return computeGridUnit(Axis::X, xMin(), xMax());
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#ifndef STATISTICS_BOX_WINDOW_H
|
||||
#define STATISTICS_BOX_WINDOW_H
|
||||
#ifndef STATISTICS_BOX_RANGE_H
|
||||
#define STATISTICS_BOX_RANGE_H
|
||||
|
||||
#include "../curve_view_window.h"
|
||||
#include "../curve_view_range.h"
|
||||
#include "store.h"
|
||||
|
||||
namespace Statistics {
|
||||
|
||||
class BoxWindow : public CurveViewWindow {
|
||||
class BoxRange : public CurveViewRange {
|
||||
public:
|
||||
BoxWindow(Store * store);
|
||||
BoxRange(Store * store);
|
||||
float xMin() override;
|
||||
// if the range of value is to wide compared to the bar width, value max is capped
|
||||
float xMax() override;
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
namespace Statistics {
|
||||
|
||||
BoxView::BoxView(Store * store, BannerView * bannerView) :
|
||||
CurveViewWithBanner(&m_boxWindow, bannerView, 0.0f, 0.2f, 0.4f, 0.2f),
|
||||
BoxView::BoxView(Store * store, View * bannerView) :
|
||||
CurveView(&m_boxRange, nullptr, bannerView, nullptr, 0.0f, 0.2f, 0.4f, 0.2f),
|
||||
m_store(store),
|
||||
m_boxWindow(BoxWindow(store)),
|
||||
m_boxRange(BoxRange(store)),
|
||||
m_selectedQuantile(0)
|
||||
{
|
||||
}
|
||||
@@ -47,7 +47,7 @@ void BoxView::drawRect(KDContext * ctx, KDRect rect) const {
|
||||
for (int k = 0; k < 5; k++) {
|
||||
drawSegment(ctx, rect, Axis::Vertical, calculations[k], lowBounds[k], upBounds[k], KDColorBlack);
|
||||
}
|
||||
if (m_mainViewSelected) {
|
||||
if (isMainViewSelected()) {
|
||||
drawSegment(ctx, rect, Axis::Vertical, calculations[m_selectedQuantile], lowBounds[m_selectedQuantile], upBounds[m_selectedQuantile], KDColorRed);
|
||||
}
|
||||
drawSegment(ctx, rect, Axis::Horizontal, 0.5f, m_store->minValue(), m_store->firstQuartile(), KDColorBlack);
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
|
||||
#include <escher.h>
|
||||
#include "store.h"
|
||||
#include "box_window.h"
|
||||
#include "box_range.h"
|
||||
#include "../constant.h"
|
||||
#include "../curve_view_with_banner.h"
|
||||
#include "../curve_view.h"
|
||||
|
||||
namespace Statistics {
|
||||
|
||||
class BoxView : public CurveViewWithBanner {
|
||||
class BoxView : public CurveView {
|
||||
public:
|
||||
BoxView(Store * store, BannerView * bannerView);
|
||||
BoxView(Store * store, View * bannerView);
|
||||
void reloadSelection() override;
|
||||
int selectedQuantile();
|
||||
bool selectQuantile(int selectedQuantile);
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
private:
|
||||
char * label(Axis axis, int index) const override;
|
||||
Store * m_store;
|
||||
BoxWindow m_boxWindow;
|
||||
BoxRange m_boxRange;
|
||||
char m_labels[k_maxNumberOfXLabels][Constant::FloatBufferSizeInScientificMode];
|
||||
// -1->Unselect 0->min 1->first quartile 2->median 3->third quartile 4->max
|
||||
int m_selectedQuantile;
|
||||
|
||||
@@ -36,7 +36,7 @@ private:
|
||||
constexpr static int k_totalNumberOfRows = 13;
|
||||
constexpr static int k_maxNumberOfDisplayableRows = 10;
|
||||
static constexpr KDCoordinate k_cellHeight = 25;
|
||||
static constexpr KDCoordinate k_cellWidth = 320/2 - Metric::RightMargin/2 - Metric::LeftMargin/2;
|
||||
static constexpr KDCoordinate k_cellWidth = Ion::Display::Width/2 - Metric::RightMargin/2 - Metric::LeftMargin/2;
|
||||
EvenOddPointerTextCell m_titleCells[k_maxNumberOfDisplayableRows];
|
||||
EvenOddBufferTextCell m_calculationCells[k_maxNumberOfDisplayableRows];
|
||||
SelectableTableView m_selectableTableView;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "histogram_controller.h"
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
namespace Statistics {
|
||||
|
||||
@@ -15,6 +16,7 @@ HistogramController::HistogramController(Responder * parentResponder, HeaderView
|
||||
stack->push(histogramController->histogramParameterController());
|
||||
}, this))),
|
||||
m_store(store),
|
||||
m_cursor(CurveViewCursor()),
|
||||
m_histogramParameterController(nullptr, store)
|
||||
{
|
||||
}
|
||||
@@ -59,11 +61,8 @@ bool HistogramController::handleEvent(Ion::Events::Event event) {
|
||||
}
|
||||
if (m_view.isMainViewSelected() && (event == Ion::Events::Left || event == Ion::Events::Right)) {
|
||||
int direction = event == Ion::Events::Left ? -1 : 1;
|
||||
m_view.reloadSelection();
|
||||
if (m_store->selectNextBarToward(direction)) {
|
||||
if (moveSelection(direction)) {
|
||||
m_view.reload();
|
||||
} else {
|
||||
m_view.reloadSelection();
|
||||
}
|
||||
reloadBannerView();
|
||||
return true;
|
||||
@@ -72,12 +71,22 @@ bool HistogramController::handleEvent(Ion::Events::Event event) {
|
||||
}
|
||||
|
||||
void HistogramController::didBecomeFirstResponder() {
|
||||
uint32_t storeChecksum = m_store->checksum();
|
||||
uint32_t storeChecksum = m_store->storeChecksum();
|
||||
if (m_storeVersion != storeChecksum) {
|
||||
m_storeVersion = storeChecksum;
|
||||
m_store->initBarParameters();
|
||||
m_store->initWindowParameters();
|
||||
initBarParameters();
|
||||
}
|
||||
uint32_t barChecksum = m_store->barChecksum();
|
||||
if (m_barVersion != barChecksum) {
|
||||
m_barVersion = barChecksum;
|
||||
initRangeParameters();
|
||||
}
|
||||
uint32_t rangeChecksum = m_store->rangeChecksum();
|
||||
if (m_rangeVersion != rangeChecksum) {
|
||||
m_rangeVersion = rangeChecksum;
|
||||
initBarSelection();
|
||||
}
|
||||
m_view.setHighlight(m_store->startOfBarAtIndex(m_selectedBarIndex), m_store->endOfBarAtIndex(m_selectedBarIndex));
|
||||
headerViewController()->setSelectedButton(-1);
|
||||
m_view.selectMainView(true);
|
||||
m_view.reload();
|
||||
@@ -115,10 +124,10 @@ void HistogramController::reloadBannerView() {
|
||||
const char * legend = "Interval [";
|
||||
int legendLength = strlen(legend);
|
||||
strlcpy(buffer, legend, legendLength+1);
|
||||
float lowerBound = m_store->selectedBar() - m_store->barWidth()/2;
|
||||
float lowerBound = m_store->startOfBarAtIndex(m_selectedBarIndex);
|
||||
int lowerBoundNumberOfChar = Float(lowerBound).convertFloatToText(buffer+legendLength, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode);
|
||||
buffer[legendLength+lowerBoundNumberOfChar] = ';';
|
||||
float upperBound = m_store->selectedBar() + m_store->barWidth()/2;
|
||||
float upperBound = m_store->endOfBarAtIndex(m_selectedBarIndex);
|
||||
int upperBoundNumberOfChar = Float(upperBound).convertFloatToText(buffer+legendLength+lowerBoundNumberOfChar+1, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode);
|
||||
buffer[legendLength+lowerBoundNumberOfChar+upperBoundNumberOfChar+1] = '[';
|
||||
buffer[legendLength+lowerBoundNumberOfChar+upperBoundNumberOfChar+2] = 0;
|
||||
@@ -127,7 +136,7 @@ void HistogramController::reloadBannerView() {
|
||||
legend = "Effectif: ";
|
||||
legendLength = strlen(legend);
|
||||
strlcpy(buffer, legend, legendLength+1);
|
||||
float size = m_store->heightForBarAtValue(m_store->selectedBar());
|
||||
float size = m_store->heightOfBarAtIndex(m_selectedBarIndex);
|
||||
Float(size).convertFloatToText(buffer+legendLength, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode);
|
||||
m_bannerView.setLegendAtIndex(buffer, 1);
|
||||
|
||||
@@ -140,4 +149,79 @@ void HistogramController::reloadBannerView() {
|
||||
m_bannerView.layoutSubviews();
|
||||
}
|
||||
|
||||
bool HistogramController::moveSelection(int deltaIndex) {
|
||||
int newSelectedBarIndex = m_selectedBarIndex;
|
||||
if (deltaIndex > 0) {
|
||||
do {
|
||||
newSelectedBarIndex++;
|
||||
} while (m_store->heightOfBarAtIndex(newSelectedBarIndex) == 0 && newSelectedBarIndex < m_store->numberOfBars());
|
||||
} else {
|
||||
do {
|
||||
newSelectedBarIndex--;
|
||||
} while (m_store->heightOfBarAtIndex(newSelectedBarIndex) == 0 && newSelectedBarIndex >= 0);
|
||||
}
|
||||
if (newSelectedBarIndex >= 0 && newSelectedBarIndex < m_store->numberOfBars()) {
|
||||
m_selectedBarIndex = newSelectedBarIndex;
|
||||
m_view.setHighlight(m_store->startOfBarAtIndex(m_selectedBarIndex), m_store->endOfBarAtIndex(m_selectedBarIndex));
|
||||
return m_store->scrollToSelectedBarIndex(m_selectedBarIndex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void HistogramController::initRangeParameters() {
|
||||
float min = m_store->firstDrawnBarAbscissa();
|
||||
float max = m_store->maxValue();
|
||||
float barWidth = m_store->barWidth();
|
||||
float xMin = min;
|
||||
float xMax = max + barWidth;
|
||||
/* if a bar is represented by less than one pixel, we cap xMax */
|
||||
if ((xMax - xMin)/barWidth > k_maxNumberOfBarsPerWindow) {
|
||||
xMax = xMin + k_maxNumberOfBarsPerWindow*barWidth;
|
||||
}
|
||||
/* Edge case */
|
||||
if (xMin >= xMax) {
|
||||
xMax = xMin + 10.0f*barWidth;
|
||||
}
|
||||
m_store->setXMin(xMin);
|
||||
m_store->setXMax(xMax);
|
||||
m_store->setYMin(0.0f);
|
||||
float yMax = -FLT_MAX;
|
||||
for (int index = 0; index < m_store->numberOfBars(); index++) {
|
||||
float size = m_store->heightOfBarAtIndex(index);
|
||||
if (size > yMax) {
|
||||
yMax = size;
|
||||
}
|
||||
}
|
||||
yMax = yMax/m_store->sumOfColumn(1);
|
||||
m_store->setYMax(yMax);
|
||||
}
|
||||
|
||||
void HistogramController::initBarParameters() {
|
||||
float min = m_store->minValue();
|
||||
float max = m_store->maxValue();
|
||||
m_store->setFirstDrawnBarAbscissa(min);
|
||||
float barWidth = m_store->computeGridUnit(CurveViewRange::Axis::X, min, max);
|
||||
if (barWidth <= 0.0f) {
|
||||
barWidth = 1.0f;
|
||||
}
|
||||
m_store->setBarWidth(barWidth);
|
||||
}
|
||||
|
||||
void HistogramController::initBarSelection() {
|
||||
m_selectedBarIndex = 0;
|
||||
while ((m_store->heightOfBarAtIndex(m_selectedBarIndex) == 0 &&
|
||||
m_selectedBarIndex < m_store->numberOfBars()) ||
|
||||
m_store->startOfBarAtIndex(m_selectedBarIndex) < m_store->firstDrawnBarAbscissa()) {
|
||||
m_selectedBarIndex++;
|
||||
}
|
||||
if (m_selectedBarIndex >= m_store->numberOfBars()) {
|
||||
/* No bar is after m_firstDrawnBarAbscissa, so we select the first bar */
|
||||
m_selectedBarIndex = 0;
|
||||
while (m_store->heightOfBarAtIndex(m_selectedBarIndex) == 0 && m_selectedBarIndex < m_store->numberOfBars()) {
|
||||
m_selectedBarIndex++;
|
||||
}
|
||||
}
|
||||
m_store->scrollToSelectedBarIndex(m_selectedBarIndex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,14 +28,24 @@ public:
|
||||
const char * emptyMessage() override;
|
||||
Responder * defaultController() override;
|
||||
private:
|
||||
constexpr static int k_maxNumberOfBarsPerWindow = 280;
|
||||
constexpr static int k_maxNumberOfCharacters = 12;
|
||||
Responder * tabController() const;
|
||||
void reloadBannerView();
|
||||
void initRangeParameters();
|
||||
void initBarParameters();
|
||||
void initBarSelection();
|
||||
// return true if the window has scrolled
|
||||
bool moveSelection(int deltaIndex);
|
||||
HistogramBannerView m_bannerView;
|
||||
HistogramView m_view;
|
||||
Button m_settingButton;
|
||||
Store * m_store;
|
||||
CurveViewCursor m_cursor;
|
||||
uint32_t m_storeVersion;
|
||||
uint32_t m_barVersion;
|
||||
uint32_t m_rangeVersion;
|
||||
int m_selectedBarIndex;
|
||||
HistogramParameterController m_histogramParameterController;
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ float HistogramParameterController::parameterAtIndex(int index) {
|
||||
if (index == 0) {
|
||||
return m_store->barWidth();
|
||||
}
|
||||
return m_store->firsBarAbscissa();
|
||||
return m_store->firstDrawnBarAbscissa();
|
||||
}
|
||||
|
||||
void HistogramParameterController::setParameterAtIndex(int parameterIndex, float f) {
|
||||
@@ -33,7 +33,7 @@ void HistogramParameterController::setParameterAtIndex(int parameterIndex, float
|
||||
}
|
||||
m_store->setBarWidth(f);
|
||||
} else {
|
||||
m_store->setFirsBarAbscissa(f);
|
||||
m_store->setFirstDrawnBarAbscissa(f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,19 +4,19 @@
|
||||
|
||||
namespace Statistics {
|
||||
|
||||
HistogramView::HistogramView(Store * store, BannerView * bannerView) :
|
||||
CurveViewWithBanner(store, bannerView, 0.2f, 0.1f, 0.4f, 0.1f),
|
||||
m_store(store)
|
||||
HistogramView::HistogramView(Store * store, View * bannerView) :
|
||||
CurveView(store, nullptr, bannerView, nullptr, 0.2f, 0.1f, 0.4f, 0.1f),
|
||||
m_store(store),
|
||||
m_highlightedBarStart(NAN),
|
||||
m_highlightedBarEnd(NAN)
|
||||
{
|
||||
}
|
||||
|
||||
void HistogramView::reloadSelection() {
|
||||
float pixelLowerBound = floatToPixel(Axis::Horizontal, m_store->selectedBar() - m_store->barWidth())-1;
|
||||
float pixelUpperBound = floatToPixel(Axis::Horizontal, m_store->selectedBar() + m_store->barWidth())+1;
|
||||
float selectedValueInPixels = floatToPixel(Axis::Vertical, (float)m_store->heightForBarAtValue(m_store->selectedBar())/m_store->sumOfColumn(1))-1;
|
||||
float horizontalAxisInPixels = floatToPixel(Axis::Vertical, 0.0f)+1;
|
||||
KDRect dirtyZone(KDRect(pixelLowerBound, selectedValueInPixels, pixelUpperBound-pixelLowerBound,
|
||||
horizontalAxisInPixels - selectedValueInPixels));
|
||||
float pixelLowerBound = floatToPixel(Axis::Horizontal, m_highlightedBarStart)-2;
|
||||
float pixelUpperBound = floatToPixel(Axis::Horizontal, m_highlightedBarEnd)+2;
|
||||
KDRect dirtyZone(KDRect(pixelLowerBound, 0, pixelUpperBound-pixelLowerBound,
|
||||
bounds().height()));
|
||||
markRectAsDirty(dirtyZone);
|
||||
}
|
||||
|
||||
@@ -24,11 +24,20 @@ void HistogramView::drawRect(KDContext * ctx, KDRect rect) const {
|
||||
ctx->fillRect(rect, KDColorWhite);
|
||||
drawAxes(ctx, rect, Axis::Horizontal);
|
||||
drawLabels(ctx, rect, Axis::Horizontal, false);
|
||||
if (m_mainViewSelected) {
|
||||
drawHistogram(ctx, rect, nullptr, m_store->firsBarAbscissa(), m_store->barWidth(), true, KDColorBlack, KDColorRed,
|
||||
m_store->selectedBar() - m_store->barWidth()/2, m_store->selectedBar() + m_store->barWidth()/2);
|
||||
if (isMainViewSelected()) {
|
||||
drawHistogram(ctx, rect, nullptr, m_store->firstDrawnBarAbscissa(), m_store->barWidth(), true, KDColorBlack, KDColorRed,
|
||||
m_highlightedBarStart, m_highlightedBarEnd);
|
||||
} else {
|
||||
drawHistogram(ctx, rect, nullptr, m_store->firsBarAbscissa(), m_store->barWidth(), true, KDColorBlack, KDColorRed);
|
||||
drawHistogram(ctx, rect, nullptr, m_store->firstDrawnBarAbscissa(), m_store->barWidth(), true, KDColorBlack, KDColorRed);
|
||||
}
|
||||
}
|
||||
|
||||
void HistogramView::setHighlight(float start, float end) {
|
||||
if (m_highlightedBarStart != start || m_highlightedBarEnd != end) {
|
||||
reloadSelection();
|
||||
m_highlightedBarStart = start;
|
||||
m_highlightedBarEnd = end;
|
||||
reloadSelection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +49,7 @@ char * HistogramView::label(Axis axis, int index) const {
|
||||
}
|
||||
|
||||
float HistogramView::evaluateModelWithParameter(Model * curve, float t) const {
|
||||
return (float)m_store->heightForBarAtValue(t)/m_store->sumOfColumn(1);
|
||||
return m_store->heightOfBarAtValue(t)/m_store->sumOfColumn(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,20 +4,23 @@
|
||||
#include <escher.h>
|
||||
#include "store.h"
|
||||
#include "../constant.h"
|
||||
#include "../curve_view_with_banner.h"
|
||||
#include "../curve_view.h"
|
||||
|
||||
namespace Statistics {
|
||||
|
||||
class HistogramView : public CurveViewWithBanner {
|
||||
class HistogramView : public CurveView {
|
||||
public:
|
||||
HistogramView(Store * store, BannerView * bannerView);
|
||||
HistogramView(Store * store, View * bannerView);
|
||||
void reloadSelection() override;
|
||||
void drawRect(KDContext * ctx, KDRect rect) const override;
|
||||
void setHighlight(float start, float end);
|
||||
private:
|
||||
char * label(Axis axis, int index) const override;
|
||||
float evaluateModelWithParameter(Model * curve, float t) const override;
|
||||
Store * m_store;
|
||||
char m_labels[k_maxNumberOfXLabels][Constant::FloatBufferSizeInScientificMode];
|
||||
float m_highlightedBarStart;
|
||||
float m_highlightedBarEnd;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -7,29 +7,23 @@
|
||||
namespace Statistics {
|
||||
|
||||
Store::Store() :
|
||||
InteractiveCurveViewRange(nullptr, nullptr),
|
||||
FloatPairStore(),
|
||||
m_barWidth(1.0f),
|
||||
m_selectedBar(0.0f),
|
||||
m_firstBarAbscissa(0.0f),
|
||||
m_xMin(0.0f),
|
||||
m_xMax(10.0f),
|
||||
m_yMax(1.0f),
|
||||
m_xGridUnit(1.0f)
|
||||
m_firstDrawnBarAbscissa(0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
/* Histogram bars */
|
||||
|
||||
void Store::initBarParameters() {
|
||||
float min = minValue();
|
||||
float max = maxValue();
|
||||
m_firstBarAbscissa = min;
|
||||
m_barWidth = computeGridUnit(Axis::X, min, max);
|
||||
if (m_barWidth <= 0.0f) {
|
||||
m_barWidth = 1.0f;
|
||||
}
|
||||
uint32_t Store::barChecksum() {
|
||||
float data[2] = {m_barWidth, m_firstDrawnBarAbscissa};
|
||||
size_t dataLengthInBytes = 2*sizeof(float);
|
||||
assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4
|
||||
//return Ion::crc32((uint32_t *)data, dataLengthInBytes>>2);
|
||||
return m_barWidth + m_firstDrawnBarAbscissa;
|
||||
}
|
||||
|
||||
/* Histogram bars */
|
||||
|
||||
float Store::barWidth() {
|
||||
return m_barWidth;
|
||||
}
|
||||
@@ -41,108 +35,57 @@ void Store::setBarWidth(float barWidth) {
|
||||
m_barWidth = barWidth;
|
||||
}
|
||||
|
||||
float Store::firsBarAbscissa() {
|
||||
return m_firstBarAbscissa;
|
||||
float Store::firstDrawnBarAbscissa() {
|
||||
return m_firstDrawnBarAbscissa;
|
||||
}
|
||||
|
||||
void Store::setFirsBarAbscissa(float firsBarAbscissa) {
|
||||
m_firstBarAbscissa = firsBarAbscissa;
|
||||
void Store::setFirstDrawnBarAbscissa(float firstBarAbscissa) {
|
||||
m_firstDrawnBarAbscissa = firstBarAbscissa;
|
||||
}
|
||||
|
||||
int Store::heightForBarAtValue(float value) {
|
||||
float Store::heightOfBarAtIndex(int index) {
|
||||
return sumOfValuesBetween(startOfBarAtIndex(index), endOfBarAtIndex(index));
|
||||
}
|
||||
|
||||
float Store::heightOfBarAtValue(float value) {
|
||||
float width = barWidth();
|
||||
int barNumber = floorf((value - m_firstBarAbscissa)/width);
|
||||
float lowerBound = m_firstBarAbscissa + barNumber*width;
|
||||
float upperBound = m_firstBarAbscissa + (barNumber+1)*width;
|
||||
int barNumber = floorf((value - m_firstDrawnBarAbscissa)/width);
|
||||
float lowerBound = m_firstDrawnBarAbscissa + barNumber*width;
|
||||
float upperBound = m_firstDrawnBarAbscissa + (barNumber+1)*width;
|
||||
return sumOfValuesBetween(lowerBound, upperBound);
|
||||
}
|
||||
|
||||
float Store::selectedBar() {
|
||||
return m_selectedBar;
|
||||
float Store::startOfBarAtIndex(int index) {
|
||||
float firstBarAbscissa = m_firstDrawnBarAbscissa + m_barWidth*floorf((minValue()- m_firstDrawnBarAbscissa)/m_barWidth);
|
||||
return firstBarAbscissa + index * m_barWidth;
|
||||
}
|
||||
|
||||
bool Store::selectNextBarToward(int direction) {
|
||||
float newSelectedBar = m_selectedBar;
|
||||
float max = maxValue();
|
||||
float min = minValue();
|
||||
if (direction > 0.0f) {
|
||||
do {
|
||||
newSelectedBar += m_barWidth;
|
||||
newSelectedBar = closestMiddleBarTo(newSelectedBar);
|
||||
} while (heightForBarAtValue(newSelectedBar) == 0 && newSelectedBar < max + m_barWidth);
|
||||
float Store::endOfBarAtIndex(int index) {
|
||||
return startOfBarAtIndex(index) + m_barWidth;
|
||||
}
|
||||
|
||||
int Store::numberOfBars() {
|
||||
float firstBarAbscissa = m_firstDrawnBarAbscissa + m_barWidth*floorf((minValue()- m_firstDrawnBarAbscissa)/m_barWidth);
|
||||
return ceilf((maxValue() - firstBarAbscissa)/m_barWidth)+1;
|
||||
}
|
||||
|
||||
bool Store::scrollToSelectedBarIndex(int index) {
|
||||
float startSelectedBar = startOfBarAtIndex(index);
|
||||
float range = m_xMax - m_xMin;
|
||||
if (m_xMin > startSelectedBar) {
|
||||
m_xMin = startSelectedBar;
|
||||
m_xMax = m_xMin + range;
|
||||
return true;
|
||||
}
|
||||
if (direction < 0.0f) {
|
||||
do {
|
||||
newSelectedBar -= m_barWidth;
|
||||
newSelectedBar = closestMiddleBarTo(newSelectedBar);
|
||||
} while (heightForBarAtValue(newSelectedBar) == 0 && newSelectedBar > min - m_barWidth);
|
||||
}
|
||||
if (newSelectedBar > min - m_barWidth && newSelectedBar < max + m_barWidth) {
|
||||
m_selectedBar = newSelectedBar;
|
||||
return scrollToSelectedBar();
|
||||
float endSelectedBar = endOfBarAtIndex(index);
|
||||
if (endSelectedBar > m_xMax) {
|
||||
m_xMax = endSelectedBar;
|
||||
m_xMin = m_xMax - range;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* CurveViewWindow */
|
||||
|
||||
void Store::initWindowParameters() {
|
||||
float min = minValue();
|
||||
float max = maxValue();
|
||||
m_xMin = m_firstBarAbscissa;
|
||||
m_xMax = max + m_barWidth;
|
||||
if ((m_xMax - m_xMin)/m_barWidth > k_maxNumberOfBarsPerWindow) {
|
||||
m_xMax = m_xMin + k_maxNumberOfBarsPerWindow*m_barWidth;
|
||||
}
|
||||
if (m_xMin >= m_xMax) {
|
||||
m_xMax = m_xMin + 10.0f*m_barWidth;
|
||||
}
|
||||
|
||||
m_yMax = -FLT_MAX;
|
||||
for (float x = min+m_barWidth/2.0f; x < max+m_barWidth; x += m_barWidth) {
|
||||
float size = heightForBarAtValue(x);
|
||||
if (size > m_yMax) {
|
||||
m_yMax = size;
|
||||
}
|
||||
}
|
||||
m_yMax = m_yMax/sumOfColumn(1);
|
||||
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
|
||||
|
||||
m_selectedBar = m_firstBarAbscissa + m_barWidth/2.0f;
|
||||
while (heightForBarAtValue(m_selectedBar) == 0 && m_selectedBar < max + m_barWidth) {
|
||||
m_selectedBar += m_barWidth;
|
||||
m_selectedBar = closestMiddleBarTo(m_selectedBar);
|
||||
}
|
||||
if (m_selectedBar > max + m_barWidth) {
|
||||
/* No bar is after m_firstBarAbscissa */
|
||||
m_selectedBar = m_firstBarAbscissa + m_barWidth/2.0f;
|
||||
while (heightForBarAtValue(m_selectedBar) == 0 && m_selectedBar > min - m_barWidth) {
|
||||
m_selectedBar -= m_barWidth;
|
||||
m_selectedBar = closestMiddleBarTo(m_selectedBar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float Store::xMin() {
|
||||
return m_xMin;
|
||||
}
|
||||
|
||||
float Store::xMax() {
|
||||
return m_xMax;
|
||||
}
|
||||
|
||||
float Store::yMin() {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float Store::yMax() {
|
||||
return m_yMax;
|
||||
}
|
||||
|
||||
float Store::xGridUnit() {
|
||||
return m_xGridUnit;
|
||||
}
|
||||
|
||||
/* Calculation */
|
||||
|
||||
float Store::maxValue() {
|
||||
@@ -244,21 +187,6 @@ float Store::sumOfValuesBetween(float x1, float x2) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Store::scrollToSelectedBar() {
|
||||
float range = m_xMax - m_xMin;
|
||||
if (m_xMin > m_selectedBar) {
|
||||
m_xMin = m_selectedBar - m_barWidth/2;
|
||||
m_xMax = m_xMin + range;
|
||||
return true;
|
||||
}
|
||||
if (m_selectedBar > m_xMax) {
|
||||
m_xMax = m_selectedBar + m_barWidth/2;
|
||||
m_xMin = m_xMax - range;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float Store::sortedElementNumber(int k) {
|
||||
// TODO: use an other algorithm (ex quickselect) to avoid quadratic complexity
|
||||
float bufferValues[m_numberOfPairs];
|
||||
@@ -283,8 +211,4 @@ int Store::minIndex(float * bufferValues, int bufferLength) {
|
||||
return index;
|
||||
}
|
||||
|
||||
float Store::closestMiddleBarTo(float f) {
|
||||
return m_firstBarAbscissa + roundf((f-m_firstBarAbscissa - m_barWidth/2.0f) / m_barWidth) * m_barWidth + m_barWidth/2.0f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,33 +1,27 @@
|
||||
#ifndef STATISTICS_STORE_H
|
||||
#define STATISTICS_STORE_H
|
||||
|
||||
#include "../curve_view_window.h"
|
||||
#include "../interactive_curve_view_range.h"
|
||||
#include "../float_pair_store.h"
|
||||
|
||||
namespace Statistics {
|
||||
|
||||
class Store : public CurveViewWindow, public FloatPairStore {
|
||||
class Store : public InteractiveCurveViewRange, public FloatPairStore {
|
||||
public:
|
||||
Store();
|
||||
|
||||
uint32_t barChecksum();
|
||||
// Histogram bars
|
||||
void initBarParameters();
|
||||
float barWidth();
|
||||
void setBarWidth(float barWidth);
|
||||
float firsBarAbscissa();
|
||||
void setFirsBarAbscissa(float firsBarAbscissa);
|
||||
int heightForBarAtValue(float value);
|
||||
float selectedBar();
|
||||
bool selectNextBarToward(int direction);
|
||||
|
||||
//CurveViewWindow
|
||||
void initWindowParameters();
|
||||
float xMin() override;
|
||||
// if the range of value is to wide compared to the bar width, value max is capped
|
||||
float xMax() override;
|
||||
float yMin() override;
|
||||
float yMax() override;
|
||||
float xGridUnit() override;
|
||||
float firstDrawnBarAbscissa();
|
||||
void setFirstDrawnBarAbscissa(float firstDrawnBarAbscissa);
|
||||
float heightOfBarAtIndex(int index);
|
||||
float heightOfBarAtValue(float value);
|
||||
float startOfBarAtIndex(int index);
|
||||
float endOfBarAtIndex(int index);
|
||||
int numberOfBars();
|
||||
// return true if the window has scrolled
|
||||
bool scrollToSelectedBarIndex(int index);
|
||||
|
||||
// Calculation
|
||||
float maxValue();
|
||||
@@ -43,22 +37,13 @@ public:
|
||||
float sum();
|
||||
float squaredValueSum();
|
||||
private:
|
||||
constexpr static int k_maxNumberOfBarsPerWindow = 300;
|
||||
float defaultValue(int i) override;
|
||||
float sumOfValuesBetween(float x1, float x2);
|
||||
bool scrollToSelectedBar();
|
||||
float sortedElementNumber(int k);
|
||||
int minIndex(float * bufferValues, int bufferLength);
|
||||
float closestMiddleBarTo(float f);
|
||||
// Histogram bars
|
||||
float m_barWidth;
|
||||
float m_selectedBar;
|
||||
float m_firstBarAbscissa;
|
||||
// Window bounds of the data
|
||||
float m_xMin;
|
||||
float m_xMax;
|
||||
float m_yMax;
|
||||
float m_xGridUnit;
|
||||
float m_firstDrawnBarAbscissa;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ void StoreParameterController::selectXColumn(bool xColumnSelected) {
|
||||
m_xColumnSelected = xColumnSelected;
|
||||
}
|
||||
|
||||
|
||||
const char * StoreParameterController::title() const {
|
||||
return "Option de la colonne";
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ constexpr KDColor ZoomParameterController::ContentView::LegendView::k_legendBack
|
||||
|
||||
/* Zoom Parameter Controller */
|
||||
|
||||
ZoomParameterController::ZoomParameterController(Responder * parentResponder, CurveViewWindowWithCursor * graphWindow, CurveViewWithBannerAndCursor * graphView) :
|
||||
ZoomParameterController::ZoomParameterController(Responder * parentResponder, InteractiveCurveViewRange * interactiveRange, CurveView * curveView) :
|
||||
ViewController(parentResponder),
|
||||
m_contentView(ContentView(graphView)),
|
||||
m_graphWindow(graphWindow)
|
||||
m_contentView(ContentView(curveView)),
|
||||
m_interactiveRange(interactiveRange)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -23,33 +23,33 @@ 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()->reload();
|
||||
m_interactiveRange->zoom(1.0f/3.0f);
|
||||
m_contentView.curveView()->reload();
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::Minus) {
|
||||
m_graphWindow->zoom(3.0f/4.0f);
|
||||
m_contentView.graphView()->reload();
|
||||
m_interactiveRange->zoom(3.0f/4.0f);
|
||||
m_contentView.curveView()->reload();
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::Up) {
|
||||
m_graphWindow->translateWindow(CurveViewWindowWithCursor::Direction::Up);
|
||||
m_contentView.graphView()->reload();
|
||||
m_interactiveRange->panWithVector(0.0f, m_interactiveRange->yGridUnit());
|
||||
m_contentView.curveView()->reload();
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::Down) {
|
||||
m_graphWindow->translateWindow(CurveViewWindowWithCursor::Direction::Down);
|
||||
m_contentView.graphView()->reload();
|
||||
m_interactiveRange->panWithVector(0.0f, -m_interactiveRange->yGridUnit());
|
||||
m_contentView.curveView()->reload();
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::Left) {
|
||||
m_graphWindow->translateWindow(CurveViewWindowWithCursor::Direction::Left);
|
||||
m_contentView.graphView()->reload();
|
||||
m_interactiveRange->panWithVector(-m_interactiveRange->xGridUnit(), 0.0f);
|
||||
m_contentView.curveView()->reload();
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::Right) {
|
||||
m_graphWindow->translateWindow(CurveViewWindowWithCursor::Direction::Right);
|
||||
m_contentView.graphView()->reload();
|
||||
m_interactiveRange->panWithVector(m_interactiveRange->xGridUnit(), 0.0f);
|
||||
m_contentView.curveView()->reload();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ void ZoomParameterController::didBecomeFirstResponder() {
|
||||
|
||||
/* Content View */
|
||||
|
||||
ZoomParameterController::ContentView::ContentView(CurveViewWithBannerAndCursor * graphView) :
|
||||
m_graphView(graphView)
|
||||
ZoomParameterController::ContentView::ContentView(CurveView * curveView) :
|
||||
m_curveView(curveView)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -74,18 +74,18 @@ int ZoomParameterController::ContentView::numberOfSubviews() const {
|
||||
View * ZoomParameterController::ContentView::subviewAtIndex(int index) {
|
||||
assert(index >= 0 && index < 2);
|
||||
if (index == 0) {
|
||||
return m_graphView;
|
||||
return m_curveView;
|
||||
}
|
||||
return &m_legendView;
|
||||
}
|
||||
|
||||
void ZoomParameterController::ContentView::layoutSubviews() {
|
||||
m_graphView->setFrame(bounds());
|
||||
m_curveView->setFrame(bounds());
|
||||
m_legendView.setFrame(KDRect(0, bounds().height() - k_legendHeight, bounds().width(), k_legendHeight));
|
||||
}
|
||||
|
||||
CurveViewWithBannerAndCursor * ZoomParameterController::ContentView::graphView() {
|
||||
return m_graphView;
|
||||
CurveView * ZoomParameterController::ContentView::curveView() {
|
||||
return m_curveView;
|
||||
}
|
||||
|
||||
/* Legend View */
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
#define APPS_ZOOM_PARAMETER_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "curve_view_window_with_cursor.h"
|
||||
#include "curve_view_with_banner_and_cursor.h"
|
||||
#include "interactive_curve_view_range.h"
|
||||
#include "curve_view.h"
|
||||
|
||||
class ZoomParameterController : public ViewController {
|
||||
public:
|
||||
ZoomParameterController(Responder * parentResponder, CurveViewWindowWithCursor * graphWindow, CurveViewWithBannerAndCursor * graphView);
|
||||
ZoomParameterController(Responder * parentResponder, InteractiveCurveViewRange * interactiveCurveViewRange, CurveView * curveView);
|
||||
const char * title() const override;
|
||||
View * view() override;
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
@@ -15,9 +15,9 @@ public:
|
||||
private:
|
||||
class ContentView : public View {
|
||||
public:
|
||||
ContentView(CurveViewWithBannerAndCursor * graphView);
|
||||
ContentView(CurveView * curveView);
|
||||
void layoutSubviews() override;
|
||||
CurveViewWithBannerAndCursor * graphView();
|
||||
CurveView * curveView();
|
||||
private:
|
||||
class LegendView : public View {
|
||||
public:
|
||||
@@ -34,12 +34,12 @@ private:
|
||||
};
|
||||
int numberOfSubviews() const override;
|
||||
View * subviewAtIndex(int index) override;
|
||||
CurveViewWithBannerAndCursor * m_graphView;
|
||||
CurveView * m_curveView;
|
||||
LegendView m_legendView;
|
||||
constexpr static KDCoordinate k_legendHeight = 50;
|
||||
};
|
||||
ContentView m_contentView;
|
||||
CurveViewWindowWithCursor * m_graphWindow;
|
||||
InteractiveCurveViewRange * m_interactiveRange;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user