[apps] Improvements of MVC structure regarding curve views and ranges

Change-Id: Iec8031dbf349c34c18694dffabd02ef9c88ebf2d
This commit is contained in:
Émilie Feral
2017-01-17 17:42:30 +01:00
parent 72de9990e9
commit c3008ca360
42 changed files with 343 additions and 302 deletions

View File

@@ -21,6 +21,7 @@ app_objs += $(addprefix apps/,\
interactive_curve_view_controller.o\
interactive_curve_view_range.o\
main.o\
memoized_curve_view_range.o\
node.o\
node_list_view_controller.o\
node_navigation_controller.o\

View File

@@ -7,8 +7,14 @@ void BannerView::drawRect(KDContext * ctx, KDRect rect) const {
}
void BannerView::setLegendAtIndex(char * text, int index) {
/* The layout of the banner's subviews depends on their content.
* Indeed, we're using a "centered text" algorithm to layout the subviews.
* So changing a legend implies two things
* - First, we need to update the textView to ensure it has the new content
* - Second, we need to relayout *all* of our subviews. */
TextView * textView = textViewAtIndex(index);
textView->setText(text);
layoutSubviews();
}
KDSize BannerView::minimalSizeForOptimalDisplay() {
@@ -20,7 +26,6 @@ int BannerView::numberOfSubviews() const {
}
void BannerView::layoutSubviews() {
markRectAsDirty(bounds());
/* We iterate on subviews, adding their width until we exceed the view bound.
* The last subview that exceeds the bound is recorded as the first subview of
* the next line. For the current line, we scan again the subviews and frame

View File

@@ -8,10 +8,10 @@ public:
void drawRect(KDContext * ctx, KDRect rect) const override;
void setLegendAtIndex(char * text, int index);
KDSize minimalSizeForOptimalDisplay() override;
void layoutSubviews() override;
private:
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
void layoutSubviews() override;
int numberOfLines();
virtual TextView * textViewAtIndex(int i) = 0;
};

View File

@@ -9,42 +9,30 @@ constexpr KDColor CurveView::k_axisColor;
constexpr KDColor CurveView::k_gridColor;
CurveView::CurveView(CurveViewRange * curveViewRange, CurveViewCursor * curveViewCursor, BannerView * bannerView,
View * cursorView, float topMarginFactor, float rightMarginFactor, float bottomMarginFactor,
float leftMarginFactor) :
View * cursorView) :
View(),
m_curveViewRange(curveViewRange),
m_curveViewCursor(curveViewCursor),
m_bannerView(bannerView),
m_cursorView(cursorView),
m_topMarginFactor(topMarginFactor),
m_bottomMarginFactor(bottomMarginFactor),
m_leftMarginFactor(leftMarginFactor),
m_rightMarginFactor(rightMarginFactor)
m_drawnRangeVersion(0)
{
}
void CurveView::reload() {
markRectAsDirty(bounds());
if (label(Axis::Horizontal, 0) != nullptr) {
computeLabels(Axis::Horizontal);
}
if (label(Axis::Vertical, 0) != nullptr) {
computeLabels(Axis::Vertical);
}
layoutSubviews();
}
void CurveView::reloadSelection() {
if (m_curveViewCursor != nullptr) {
float pixelXSelection = roundf(floatToPixel(Axis::Horizontal, m_curveViewCursor->x()));
float pixelYSelection = roundf(floatToPixel(Axis::Vertical, m_curveViewCursor->y()));
KDRect dirtyZone(KDRect(pixelXSelection - k_cursorSize/2, pixelYSelection - k_cursorSize/2, k_cursorSize, k_cursorSize));
markRectAsDirty(dirtyZone);
layoutCursor();
if (m_bannerView != nullptr) {
m_bannerView->layoutSubviews();
uint32_t rangeVersion = m_curveViewRange->rangeChecksum();
if (m_drawnRangeVersion != rangeVersion) {
// FIXME: This should also be called if the *curve* changed
m_drawnRangeVersion = rangeVersion;
markRectAsDirty(bounds());
if (label(Axis::Horizontal, 0) != nullptr) {
computeLabels(Axis::Horizontal);
}
if (label(Axis::Vertical, 0) != nullptr) {
computeLabels(Axis::Vertical);
}
}
layoutSubviews();
}
bool CurveView::isMainViewSelected() const {
@@ -54,7 +42,7 @@ bool CurveView::isMainViewSelected() const {
void CurveView::selectMainView(bool mainViewSelected) {
if (m_mainViewSelected != mainViewSelected) {
m_mainViewSelected = mainViewSelected;
reloadSelection();
reload();
layoutSubviews();
}
}
@@ -65,18 +53,12 @@ void CurveView::setCurveViewRange(CurveViewRange * curveViewRange) {
float CurveView::min(Axis axis) const {
assert(axis == Axis::Horizontal || axis == Axis::Vertical);
float range = axis == Axis::Horizontal ? m_curveViewRange->xMax() - m_curveViewRange->xMin() : m_curveViewRange->yMax() - m_curveViewRange->yMin();
float absoluteMin = axis == Axis::Horizontal ? m_curveViewRange->xMin(): m_curveViewRange->yMin();
float marginFactor = axis == Axis::Horizontal ? m_leftMarginFactor : m_bottomMarginFactor;
return absoluteMin - marginFactor*range;
return (axis == Axis::Horizontal ? m_curveViewRange->xMin(): m_curveViewRange->yMin());
}
float CurveView::max(Axis axis) const {
assert(axis == Axis::Horizontal || axis == Axis::Vertical);
float range = axis == Axis::Horizontal ? m_curveViewRange->xMax() - m_curveViewRange->xMin() : m_curveViewRange->yMax() - m_curveViewRange->yMin();
float absoluteMax = (axis == Axis::Horizontal ? m_curveViewRange->xMax() : m_curveViewRange->yMax());
float marginFactor = axis == Axis::Horizontal ? m_rightMarginFactor : m_topMarginFactor;
return absoluteMax + marginFactor*range;
return (axis == Axis::Horizontal ? m_curveViewRange->xMax() : m_curveViewRange->yMax());
}
float CurveView::gridUnit(Axis axis) const {
@@ -395,19 +377,6 @@ void CurveView::stampAtLocation(KDContext * ctx, KDRect rect, float pxf, float p
}
void CurveView::layoutSubviews() {
layoutCursor();
if (m_bannerView != nullptr) {
m_bannerView->setFrame(bounds());
KDCoordinate bannerHeight = m_bannerView->minimalSizeForOptimalDisplay().height();
KDRect bannerFrame(KDRect(0, bounds().height()- bannerHeight, bounds().width(), bannerHeight));
if (!m_mainViewSelected) {
bannerFrame = KDRectZero;
}
m_bannerView->setFrame(bannerFrame);
}
}
void CurveView::layoutCursor() {
if (m_curveViewCursor != nullptr && m_cursorView != nullptr) {
KDCoordinate xCursorPixelPosition = roundf(floatToPixel(Axis::Horizontal, m_curveViewCursor->x()));
KDCoordinate yCursorPixelPosition = roundf(floatToPixel(Axis::Vertical, m_curveViewCursor->y()));
@@ -417,6 +386,14 @@ void CurveView::layoutCursor() {
}
m_cursorView->setFrame(cursorFrame);
}
if (m_bannerView != nullptr) {
KDCoordinate bannerHeight = m_bannerView->minimalSizeForOptimalDisplay().height();
KDRect bannerFrame(KDRect(0, bounds().height()- bannerHeight, bounds().width(), bannerHeight));
if (!m_mainViewSelected) {
bannerFrame = KDRectZero;
}
m_bannerView->setFrame(bannerFrame);
}
}
int CurveView::numberOfSubviews() const {

View File

@@ -16,11 +16,8 @@ public:
Vertical = 1
};
CurveView(CurveViewRange * curveViewRange = nullptr, CurveViewCursor * curveViewCursor = nullptr,
BannerView * bannerView = nullptr, View * cursorView = nullptr, float topMarginFactor = 0.0f,
float rightMarginFactor = 0.0f, float bottomMarginFactor = 0.0f, float leftMarginFactor = 0.0f);
// Reload methods
void reload();
virtual void reloadSelection();
BannerView * bannerView = nullptr, View * cursorView = nullptr);
virtual void reload();
// When the main view is selected, the banner view is visible
bool isMainViewSelected() const;
void selectMainView(bool mainViewSelected);
@@ -72,7 +69,6 @@ private:
* anti alising. */
void stampAtLocation(KDContext * ctx, KDRect rect, float pxf, float pyf, KDColor color) const;
void layoutSubviews() override;
void layoutCursor();
int numberOfSubviews() const override;
View * subviewAtIndex(int index) override;
/* m_curveViewRange has to be non null but the cursor model, the banner and
@@ -82,10 +78,7 @@ private:
BannerView * m_bannerView;
View * m_cursorView;
bool m_mainViewSelected;
float m_topMarginFactor;
float m_bottomMarginFactor;
float m_leftMarginFactor;
float m_rightMarginFactor;
uint32_t m_drawnRangeVersion;
};
#endif

View File

@@ -1,7 +1,17 @@
#include "curve_view_range.h"
#include <math.h>
#include <ion.h>
#include <assert.h>
#include <stddef.h>
#include <float.h>
uint32_t CurveViewRange::rangeChecksum() {
float data[4] = {xMin(), xMax(), yMin(), yMax()};
size_t dataLengthInBytes = 4*sizeof(float);
assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4
return Ion::crc32((uint32_t *)data, dataLengthInBytes>>2);
}
float CurveViewRange::yGridUnit() {
return 0.0f;
}

View File

@@ -1,12 +1,16 @@
#ifndef APPS_CURVE_VIEW_RANGE_H
#define APPS_CURVE_VIEW_RANGE_H
#include <stdint.h>
class CurveViewRange {
public:
enum class Axis {
X,
Y
};
virtual uint32_t rangeChecksum();
virtual float xMin() = 0;
virtual float xMax() = 0;
virtual float yMin() = 0;

View File

@@ -28,7 +28,7 @@ void GoToParameterController::setParameterAtIndex(int parameterIndex, float f) {
float y = m_function->evaluateAtAbscissa(f, graphApp->localContext());
m_graphRange->centerAxisAround(CurveViewRange::Axis::X, f);
m_graphRange->centerAxisAround(CurveViewRange::Axis::Y, y);
m_graphRange->moveCursorTo(f, y);
m_cursor->moveTo(f, y);
}
int GoToParameterController::numberOfRows() {

View File

@@ -119,7 +119,6 @@ void GraphController::reloadBannerView() {
Float(y).convertFloatToText(buffer + legendLength, Float::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Float::DisplayMode::Decimal);
m_bannerView.setLegendAtIndex(buffer, 2);
}
m_bannerView.layoutSubviews();
}
void GraphController::initRangeParameters() {
@@ -134,20 +133,23 @@ void GraphController::initCursorParameters() {
Function * firstFunction = m_functionStore->activeFunctionAtIndex(0);
App * graphApp = (Graph::App *)app();
float y = firstFunction->evaluateAtAbscissa(x, graphApp->localContext());
m_graphRange.moveCursorTo(x, y);
m_cursor.moveTo(x, y);
m_graphRange.panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
}
int GraphController::moveCursorHorizontally(int direction) {
bool GraphController::moveCursorHorizontally(int direction) {
float xCursorPosition = m_cursor.x();
float x = direction > 0 ? xCursorPosition + m_graphRange.xGridUnit()/InteractiveCurveViewRange::k_numberOfCursorStepsInGradUnit :
xCursorPosition - m_graphRange.xGridUnit()/InteractiveCurveViewRange::k_numberOfCursorStepsInGradUnit;
float x = direction > 0 ? xCursorPosition + m_graphRange.xGridUnit()/k_numberOfCursorStepsInGradUnit :
xCursorPosition - m_graphRange.xGridUnit()/k_numberOfCursorStepsInGradUnit;
Function * f = m_functionStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor);
App * graphApp = (Graph::App *)app();
float y = f->evaluateAtAbscissa(x, graphApp->localContext());
return m_graphRange.moveCursorTo(x, y);;
m_cursor.moveTo(x, y);
m_graphRange.panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
return true;
}
int GraphController::moveCursorVertically(int direction) {
bool GraphController::moveCursorVertically(int direction) {
Function * actualFunction = m_functionStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor);
App * graphApp = (Graph::App *)app();
float y = actualFunction->evaluateAtAbscissa(m_cursor.x(), graphApp->localContext());
@@ -164,9 +166,11 @@ int GraphController::moveCursorVertically(int direction) {
}
}
if (nextFunction == actualFunction) {
return -1;
return false;
}
return m_graphRange.moveCursorTo(m_cursor.x(), nextY);
m_cursor.moveTo(m_cursor.x(), nextY);
m_graphRange.panToMakePointVisible(m_cursor.x(), m_cursor.y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
return true;
}
uint32_t GraphController::modelVersion() {

View File

@@ -21,16 +21,20 @@ public:
const char * emptyMessage() override;
bool didChangeRange(InteractiveCurveViewRange * interactiveCurveViewRange) override;
private:
constexpr static float k_cursorTopMarginRatio = 0.025f;
constexpr static float k_cursorRightMarginRatio = 0.015f;
constexpr static float k_cursorBottomMarginRatio = 0.08f;
constexpr static float k_cursorLeftMarginRatio = 0.015f;
constexpr static int k_maxNumberOfCharacters = 8;
BannerView * bannerView() override;
bool handleEnter() override;
void reloadBannerView() override;
void initRangeParameters() override;
void initCursorParameters() override;
int moveCursorHorizontally(int direction) override;
int moveCursorVertically(int direction) override;
bool moveCursorHorizontally(int direction) override;
bool moveCursorVertically(int direction) override;
uint32_t modelVersion() override;
uint32_t rangeVersion() override;
InteractiveCurveViewRange * interactiveCurveViewRange() override;

View File

@@ -6,7 +6,7 @@
namespace Graph {
GraphView::GraphView(FunctionStore * functionStore, InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor, ::BannerView * bannerView, View * cursorView) :
CurveView(graphRange, cursor, bannerView, cursorView, 0.0f, 0.0f, 0.1f, 0.0f),
CurveView(graphRange, cursor, bannerView, cursorView),
m_functionStore(functionStore),
m_context(nullptr)
{

View File

@@ -28,7 +28,7 @@ void InitialisationParameterController::didBecomeFirstResponder() {
bool InitialisationParameterController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK) {
RangeMethodPointer rangeMethods[k_totalNumberOfCells] = {&InteractiveCurveViewRange::setTrigonometric,
&InteractiveCurveViewRange::roundAbscissa, &InteractiveCurveViewRange::normalize, &InteractiveCurveViewRange::setDefault};
&InteractiveCurveViewRange::roundAbscissa, &InteractiveCurveViewRange::normalize, &InteractiveCurveViewRange::setDefault};
(m_graphRange->*rangeMethods[m_selectableTableView.selectedRow()])();
StackViewController * stack = (StackViewController *)parentResponder();
stack->pop();

View File

@@ -40,7 +40,7 @@ bool InteractiveCurveViewController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::Down) {
headerViewController()->setSelectedButton(-1);
curveView()->selectMainView(true);
curveView()->reloadSelection();
curveView()->reload();
reloadBannerView();
return true;
}
@@ -65,36 +65,25 @@ bool InteractiveCurveViewController::handleEvent(Ion::Events::Event event) {
}
if (event == Ion::Events::Left || event == Ion::Events::Right) {
int direction = event == Ion::Events::Left ? -1 : 1;
curveView()->reloadSelection();
int didMoveCursor = moveCursorHorizontally(direction);
if (didMoveCursor == 0) {
curveView()->reloadSelection();
}
if (didMoveCursor == 1) {
if (moveCursorHorizontally(direction)) {
curveView()->reload();
reloadBannerView();
return true;
}
reloadBannerView();
return (didMoveCursor >= 0);
return false;
}
if (event == Ion::Events::Down || event == Ion::Events::Up) {
int direction = event == Ion::Events::Down ? -1 : 1;
curveView()->reloadSelection();
int didMoveCursor = moveCursorVertically(direction);
if (didMoveCursor < 0) {
if (event == Ion::Events::Down) {
return false;
}
curveView()->selectMainView(false);
headerViewController()->setSelectedButton(0);
if (moveCursorVertically(direction)) {
curveView()->reload();
reloadBannerView();
return true;
}
if (didMoveCursor == 0) {
curveView()->reloadSelection();
if (event == Ion::Events::Down) {
return false;
}
if (didMoveCursor == 1) {
curveView()->reload();
}
reloadBannerView();
curveView()->selectMainView(false);
headerViewController()->setSelectedButton(0);
return true;
}
if (event == Ion::Events::OK) {

View File

@@ -26,6 +26,7 @@ public:
Responder * defaultController() override;
protected:
constexpr static float k_numberOfCursorStepsInGradUnit = 5.0f;
virtual BannerView * bannerView() = 0;
virtual bool handleEnter() = 0;
Responder * tabController() const;
@@ -34,11 +35,10 @@ protected:
virtual void initRangeParameters() = 0;
virtual void initCursorParameters() = 0;
/* the result of moveCursorVertically/Horizontally means:
* -1 -> the cursor cannot move in this direction
* 0 -> the cursor moved but the window has not changed
* 1-> the cursor moved and the window changed */
virtual int moveCursorHorizontally(int direction) = 0;
virtual int moveCursorVertically(int direction) = 0;
* false -> the cursor cannot move in this direction
* true -> the cursor moved */
virtual bool moveCursorHorizontally(int direction) = 0;
virtual bool moveCursorVertically(int direction) = 0;
virtual uint32_t modelVersion() = 0;
virtual uint32_t rangeVersion() = 0;
virtual InteractiveCurveViewRange * interactiveCurveViewRange() = 0;

View File

@@ -5,13 +5,8 @@
#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),
MemoizedCurveViewRange(),
m_yAuto(true),
m_xGridUnit(2.0f),
m_yGridUnit(2.0f),
m_cursor(cursor),
m_delegate(delegate)
{
@@ -28,70 +23,22 @@ uint32_t InteractiveCurveViewRange::rangeChecksum() {
return Ion::crc32((uint32_t *)data, dataLengthInBytes>>2);
}
float InteractiveCurveViewRange::xMin() {
return m_xMin;
}
float InteractiveCurveViewRange::xMax() {
return m_xMax;
}
float InteractiveCurveViewRange::yMin() {
return m_yMin;
}
float InteractiveCurveViewRange::yMax() {
return m_yMax;
}
bool InteractiveCurveViewRange::yAuto() {
return m_yAuto;
}
float InteractiveCurveViewRange::xGridUnit() {
return m_xGridUnit;
}
float InteractiveCurveViewRange::yGridUnit() {
return m_yGridUnit;
}
void InteractiveCurveViewRange::setXMin(float xMin) {
m_xMin = xMin;
if (m_xMin >= m_xMax) {
m_xMax = xMin + 1.0f;
}
MemoizedCurveViewRange::setXMin(xMin);
if (m_delegate) {
m_delegate->didChangeRange(this);
}
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
}
void InteractiveCurveViewRange::setXMax(float xMax) {
m_xMax = xMax;
if (m_xMin >= m_xMax) {
m_xMin = xMax - 1.0f;
}
MemoizedCurveViewRange::setXMax(xMax);
if (m_delegate) {
m_delegate->didChangeRange(this);
}
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
}
void InteractiveCurveViewRange::setYMin(float yMin) {
m_yMin = yMin;
if (m_yMin >= m_yMax) {
m_yMax = yMin + 1.0f;
}
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
}
void InteractiveCurveViewRange::setYMax(float yMax) {
m_yMax = yMax;
if (m_yMin >= m_yMax) {
m_yMin = yMax - 1.0f;
}
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
}
void InteractiveCurveViewRange::setYAuto(bool yAuto) {
@@ -183,48 +130,31 @@ void InteractiveCurveViewRange::centerAxisAround(Axis axis, float position) {
}
}
bool InteractiveCurveViewRange::moveCursorTo(float x, float y) {
if (m_cursor) {
m_cursor->moveTo(x, y);
float xMargin = k_cursorMarginFactorToBorder * (m_xMax - m_xMin);
float yMargin = k_cursorMarginFactorToBorder * (m_yMax - m_yMin);
bool windowHasMoved = panToMakePointVisible(x, y, xMargin, yMargin);
return windowHasMoved;
}
return false;
}
bool InteractiveCurveViewRange::panToMakePointVisible(float x, float y, float xMargin, float yMargin) {
bool windowMoved = false;
void InteractiveCurveViewRange::panToMakePointVisible(float x, float y, float topMarginRatio, float rightMarginRatio, float bottomMarginRation, float leftMarginRation) {
float xRange = m_xMax - m_xMin;
float yRange = m_yMax - m_yMin;
if (x < m_xMin + xMargin) {
m_xMin = x - xMargin;
if (x < m_xMin + leftMarginRation*xRange) {
m_xMin = x - leftMarginRation*xRange;
m_xMax = m_xMin + xRange;
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
m_yAuto = false;
windowMoved = true;
}
if (x > m_xMax - xMargin) {
m_xMax = x + xMargin;
if (x > m_xMax - rightMarginRatio*xRange) {
m_xMax = x + rightMarginRatio*xRange;
m_xMin = m_xMax - xRange;
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
m_yAuto = false;
windowMoved = true;
}
if (y < m_yMin + yMargin) {
m_yMin = y - yMargin;
if (y < m_yMin + bottomMarginRation*yRange) {
m_yMin = y - bottomMarginRation*yRange;
m_yMax = m_yMin + yRange;
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
m_yAuto = false;
windowMoved = true;
}
if (y > m_yMax - yMargin) {
m_yMax = y + yMargin;
if (y > m_yMax - topMarginRatio*yRange) {
m_yMax = y + topMarginRatio*yRange;
m_yMin = m_yMax - yRange;
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
m_yAuto = false;
windowMoved = true;
}
return windowMoved;
}

View File

@@ -2,28 +2,20 @@
#define APPS_INTERACTIVE_CURVE_VIEW_RANGE_H
#include <stdint.h>
#include "curve_view_range.h"
#include "memoized_curve_view_range.h"
#include "curve_view_cursor.h"
#include "interactive_curve_view_range_delegate.h"
class InteractiveCurveViewRange : public CurveViewRange {
class InteractiveCurveViewRange : public MemoizedCurveViewRange {
public:
InteractiveCurveViewRange(CurveViewCursor * cursor, InteractiveCurveViewRangeDelegate * delegate);
void setCursor(CurveViewCursor * cursor);
uint32_t rangeChecksum();
uint32_t rangeChecksum() override;
//CurveViewWindow
float xMin() override;
float xMax() override;
float yMin() override;
float yMax() override;
bool yAuto();
float xGridUnit() override;
float yGridUnit() override;
void setXMin(float f);
void setXMax(float f);
void setYMin(float f);
void setYMax(float f);
void setXMin(float f) override;
void setXMax(float f) override;
void setYAuto(bool yAuto);
// Window
@@ -34,23 +26,10 @@ public:
void setTrigonometric();
virtual void setDefault();
void centerAxisAround(Axis axis, float position);
//Cursor
// moveCursorTo returns true if the window has moved
bool moveCursorTo(float x, float y);
constexpr static float k_numberOfCursorStepsInGradUnit = 5.0f;
void panToMakePointVisible(float x, float y, float topMarginRatio, float rightMarginRatio, float bottomMarginRation, float leftMarginRation);
protected:
constexpr static float k_cursorMarginFactorToBorder = 0.025f;
// Window bounds of the data
float m_xMin;
float m_xMax;
float m_yMin;
float m_yMax;
bool m_yAuto;
float m_xGridUnit;
float m_yGridUnit;
private:
bool panToMakePointVisible(float x, float y, float xMargin, float yMargin);
CurveViewCursor * m_cursor;
InteractiveCurveViewRangeDelegate * m_delegate;
};

View File

@@ -0,0 +1,70 @@
#include "memoized_curve_view_range.h"
#include <math.h>
#include <assert.h>
#include <ion.h>
MemoizedCurveViewRange::MemoizedCurveViewRange() :
m_xMin(-10.0f),
m_xMax(10.0f),
m_yMin(-10.0f),
m_yMax(10.0f),
m_xGridUnit(2.0f),
m_yGridUnit(2.0f)
{
}
float MemoizedCurveViewRange::xMin() {
return m_xMin;
}
float MemoizedCurveViewRange::xMax() {
return m_xMax;
}
float MemoizedCurveViewRange::yMin() {
return m_yMin;
}
float MemoizedCurveViewRange::yMax() {
return m_yMax;
}
float MemoizedCurveViewRange::xGridUnit() {
return m_xGridUnit;
}
float MemoizedCurveViewRange::yGridUnit() {
return m_yGridUnit;
}
void MemoizedCurveViewRange::setXMin(float xMin) {
m_xMin = xMin;
if (m_xMin >= m_xMax) {
m_xMax = xMin + 1.0f;
}
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
}
void MemoizedCurveViewRange::setXMax(float xMax) {
m_xMax = xMax;
if (m_xMin >= m_xMax) {
m_xMin = xMax - 1.0f;
}
m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax);
}
void MemoizedCurveViewRange::setYMin(float yMin) {
m_yMin = yMin;
if (m_yMin >= m_yMax) {
m_yMax = yMin + 1.0f;
}
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
}
void MemoizedCurveViewRange::setYMax(float yMax) {
m_yMax = yMax;
if (m_yMin >= m_yMax) {
m_yMin = yMax - 1.0f;
}
m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax);
}

View File

@@ -0,0 +1,32 @@
#ifndef APPS_MEMOIZED_CURVE_VIEW_RANGE_H
#define APPS_MEMOIZED_CURVE_VIEW_RANGE_H
#include "curve_view_range.h"
class MemoizedCurveViewRange : public CurveViewRange {
public:
MemoizedCurveViewRange();
//CurveViewWindow
float xMin() override;
float xMax() override;
float yMin() override;
float yMax() override;
float xGridUnit() override;
float yGridUnit() override;
virtual void setXMin(float f);
virtual void setXMax(float f);
void setYMin(float f);
void setYMax(float f);
protected:
// Window bounds of the data
float m_xMin;
float m_xMax;
float m_yMin;
float m_yMax;
float m_xGridUnit;
float m_yGridUnit;
};
#endif

View File

@@ -40,28 +40,31 @@ const char * BinomialLaw::parameterDefinitionAtIndex(int index) {
}
float BinomialLaw::xMin() {
return floorf(m_parameter1*m_parameter2-5.0f*sqrtf(m_parameter1*m_parameter2*(1-m_parameter2)));
float min = floorf(m_parameter1*m_parameter2-5.0f*sqrtf(m_parameter1*m_parameter2*(1-m_parameter2)));
float max = xMax();
return min - k_displayLeftMarginRatio * (max - min);
}
float BinomialLaw::xMax() {
float result = ceilf(m_parameter1*m_parameter2+5.0f*sqrtf(m_parameter1*m_parameter2*(1-m_parameter2)));
if (result <= xMin()) {
result = xMin() + 1.0f;
float min = floorf(m_parameter1*m_parameter2-5.0f*sqrtf(m_parameter1*m_parameter2*(1-m_parameter2)));
float max = ceilf(m_parameter1*m_parameter2+5.0f*sqrtf(m_parameter1*m_parameter2*(1-m_parameter2)));
if (max <= min) {
max = min + 1.0f;
}
return result;
return max + k_displayRightMarginRatio*(max - min);
}
float BinomialLaw::yMin() {
return 0.0f;
return -k_displayBottomMarginRatio*yMax();
}
float BinomialLaw::yMax() {
int maxAbscissa = m_parameter2 < 1.0f ? (m_parameter1+1)*m_parameter2 : m_parameter1;
float result = evaluateAtAbscissa(maxAbscissa);
if (result <= yMin() || isnan(result)) {
result = yMin() + 1.0f;
if (result <= 0.0f || isnan(result)) {
result = 1.0f;
}
return result;
return result*(1.0f+ k_displayTopMarginRatio);
}
float BinomialLaw::evaluateAtAbscissa(float x) const {

View File

@@ -33,28 +33,32 @@ const char * ExponentialLaw::parameterDefinitionAtIndex(int index) {
}
float ExponentialLaw::xMin() {
return 0.0f;
float max = xMax();
return - k_displayLeftMarginRatio * max;
}
float ExponentialLaw::xMax() {
assert(m_parameter1 != 0.0f);
float result = 5.0f/m_parameter1;
if (result <= xMin()) {
result = xMin() + 1.0f;
if (result <= 0.0f) {
result = 1.0f;
}
return result;
return result*(1.0f+ k_displayRightMarginRatio);
}
float ExponentialLaw::yMin() {
return 0.0f;
return -k_displayBottomMarginRatio*yMax();
}
float ExponentialLaw::yMax() {
float result = m_parameter1;
if (result <= yMin()) {
result = yMin() + 1.0f;
if (result <= 0.0f || isnan(result)) {
result = 1.0f;
}
return result;
if (result <= 0.0f) {
result = 1.0f;
}
return result*(1.0f+ k_displayTopMarginRatio);
}
float ExponentialLaw::evaluateAtAbscissa(float x) const {

View File

@@ -34,6 +34,10 @@ public:
virtual float rightIntegralInverseForProbability(float * probability);
protected:
constexpr static int k_maxNumberOfOperations = 1000000;
constexpr static float k_displayTopMarginRatio = 0.05f;
constexpr static float k_displayBottomMarginRatio = 0.2f;
constexpr static float k_displayLeftMarginRatio = 0.05f;
constexpr static float k_displayRightMarginRatio = 0.05f;
};
}

View File

@@ -55,16 +55,16 @@ float NormalLaw::xMax() {
}
float NormalLaw::yMin() {
return 0.0f;
return -k_displayBottomMarginRatio*yMax();
}
float NormalLaw::yMax() {
float maxAbscissa = m_parameter1;
float result = evaluateAtAbscissa(maxAbscissa);
if (isnan(result) || result == yMin()) {
result = yMin() + 1.0f;
if (isnan(result) || result <= 0.0f) {
result = 1.0f;
}
return result;
return result*(1.0f+ k_displayTopMarginRatio);
}
float NormalLaw::evaluateAtAbscissa(float x) const {

View File

@@ -32,26 +32,26 @@ const char * PoissonLaw::parameterDefinitionAtIndex(int index) {
}
float PoissonLaw::xMin() {
return 0.0f;
return -k_displayLeftMarginRatio*xMax();
}
float PoissonLaw::xMax() {
assert(m_parameter1 != 0);
return m_parameter1 + 5.0f*sqrtf(m_parameter1);
return (m_parameter1 + 5.0f*sqrtf(m_parameter1))*(1.0f+k_displayRightMarginRatio);
}
float PoissonLaw::yMin() {
return 0.0f;
return - k_displayBottomMarginRatio * yMax();
}
float PoissonLaw::yMax() {
int maxAbscissa = (int)m_parameter1;
assert(maxAbscissa >= 0.0f);
float result = evaluateAtAbscissa(maxAbscissa);
if (result <= yMin()) {
result = yMin() + 1.0f;
if (result <= 0.0f) {
result = 1.0f;
}
return result;
return result*(1.0f+ k_displayTopMarginRatio);
}
float PoissonLaw::evaluateAtAbscissa(float x) const {

View File

@@ -54,15 +54,15 @@ float UniformLaw::xMax() {
}
float UniformLaw::yMin() {
return 0.0f;
return -k_displayBottomMarginRatio*yMax();
}
float UniformLaw::yMax() {
float result = m_parameter1 == m_parameter2 ? k_diracMaximum : 1.0f/(m_parameter2-m_parameter1);
if (result <= yMin()) {
result = yMin() + 1.0f;
if (result <= 0.0f) {
result = 1.0f;
}
return result;
return result*(1.0f+ k_displayTopMarginRatio);
}
float UniformLaw::evaluateAtAbscissa(float t) const {

View File

@@ -5,7 +5,7 @@
namespace Probability {
LawCurveView::LawCurveView() :
CurveView(nullptr, nullptr, nullptr, nullptr, 0.2f, 0.1f, 0.2f, 0.1f),
CurveView(nullptr, nullptr, nullptr, nullptr),
m_law(nullptr),
m_calculation(nullptr)
{

View File

@@ -37,12 +37,12 @@ void GoToParameterController::setParameterAtIndex(int parameterIndex, float f) {
float y = m_store->yValueForXValue(f);
m_store->centerAxisAround(CurveViewRange::Axis::X, f);
m_store->centerAxisAround(CurveViewRange::Axis::Y, y);
m_store->moveCursorTo(f, y);
m_cursor->moveTo(f, y);
} else {
float x = m_store->xValueForYValue(f);
m_store->centerAxisAround(CurveViewRange::Axis::X, x);
m_store->centerAxisAround(CurveViewRange::Axis::Y, f);
m_store->moveCursorTo(x, f);
m_cursor->moveTo(x, f);
}
}

View File

@@ -91,8 +91,6 @@ void GraphController::reloadBannerView() {
strlcpy(buffer, legend, legendLength+1);
Float(y).convertFloatToText(buffer+legendLength, Float::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits);
m_bannerView.setLegendAtIndex(buffer, 4);
m_bannerView.layoutSubviews();
}
void GraphController::initRangeParameters() {
@@ -102,50 +100,62 @@ void GraphController::initRangeParameters() {
void GraphController::initCursorParameters() {
float x = (m_store->xMin() + m_store->xMax())/2.0f;
float y = m_store->yValueForXValue(x);
m_store->moveCursorTo(x, y);
m_cursor.moveTo(x, y);
m_store->panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
m_selectedDotIndex = -1;
}
int GraphController::moveCursorHorizontally(int direction) {
bool GraphController::moveCursorHorizontally(int direction) {
if (m_selectedDotIndex >= 0) {
int dotSelected = m_store->nextDot(direction, m_selectedDotIndex);
if (dotSelected >= 0 && dotSelected < m_store->numberOfPairs()) {
m_selectedDotIndex = dotSelected;
return m_store->moveCursorTo(m_store->get(0, m_selectedDotIndex), m_store->get(1, m_selectedDotIndex));
m_cursor.moveTo(m_store->get(0, m_selectedDotIndex), m_store->get(1, m_selectedDotIndex));
m_store->panToMakePointVisible(m_cursor.x(), m_cursor.y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
return true;
}
if (dotSelected == m_store->numberOfPairs()) {
m_selectedDotIndex = dotSelected;
return m_store->moveCursorTo(m_store->meanOfColumn(0), m_store->meanOfColumn(1));
m_cursor.moveTo(m_store->meanOfColumn(0), m_store->meanOfColumn(1));
m_store->panToMakePointVisible(m_cursor.x(), m_cursor.y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
return true;
}
return -1;
} else {
float x = direction > 0 ? m_cursor.x() + m_store->xGridUnit()/InteractiveCurveViewRange::k_numberOfCursorStepsInGradUnit :
m_cursor.x() - m_store->xGridUnit()/InteractiveCurveViewRange::k_numberOfCursorStepsInGradUnit;
float y = m_store->yValueForXValue(x);
return m_store->moveCursorTo(x, y);
return false;
}
float x = direction > 0 ? m_cursor.x() + m_store->xGridUnit()/k_numberOfCursorStepsInGradUnit :
m_cursor.x() - m_store->xGridUnit()/k_numberOfCursorStepsInGradUnit;
float y = m_store->yValueForXValue(x);
m_cursor.moveTo(x, y);
m_store->panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
return true;
}
int GraphController::moveCursorVertically(int direction) {
bool GraphController::moveCursorVertically(int direction) {
float yRegressionCurve = m_store->yValueForXValue(m_cursor.x());
if (m_selectedDotIndex >= 0) {
if ((yRegressionCurve - m_cursor.y() > 0) == (direction > 0)) {
m_selectedDotIndex = -1;
return m_store->moveCursorTo(m_cursor.x(), yRegressionCurve);
m_cursor.moveTo(m_cursor.x(), yRegressionCurve);
m_store->panToMakePointVisible(m_cursor.x(), m_cursor.y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
return true;
} else {
return -1;
return false;
}
} else {
int dotSelected = m_store->closestVerticalDot(direction, m_cursor.x());
if (dotSelected >= 0 && dotSelected < m_store->numberOfPairs()) {
m_selectedDotIndex = dotSelected;
return m_store->moveCursorTo(m_store->get(0, m_selectedDotIndex), m_store->get(1, m_selectedDotIndex));
m_cursor.moveTo(m_store->get(0, m_selectedDotIndex), m_store->get(1, m_selectedDotIndex));
m_store->panToMakePointVisible(m_cursor.x(), m_cursor.y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
return true;
}
if (dotSelected == m_store->numberOfPairs()) {
m_selectedDotIndex = dotSelected;
return m_store->moveCursorTo(m_store->meanOfColumn(0), m_store->meanOfColumn(1));
m_cursor.moveTo(m_store->meanOfColumn(0), m_store->meanOfColumn(1));
m_store->panToMakePointVisible(m_cursor.x(), m_cursor.y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
return true;
}
return -1;
return false;
}
}

View File

@@ -19,6 +19,11 @@ public:
bool isEmpty() override;
const char * emptyMessage() override;
private:
constexpr static float k_cursorTopMarginRatio = 0.025f;
constexpr static float k_cursorRightMarginRatio = 0.015f;
constexpr static float k_cursorBottomMarginRatio = 0.16f;
constexpr static float k_cursorLeftMarginRatio = 0.015f;
constexpr static int k_maxNumberOfCharacters = 8;
BannerView * bannerView() override;
CurveView * curveView() override;
@@ -27,8 +32,8 @@ private:
void reloadBannerView() override;
void initRangeParameters() override;
void initCursorParameters() override;
int moveCursorHorizontally(int direction) override;
int moveCursorVertically(int direction) override;
bool moveCursorHorizontally(int direction) override;
bool moveCursorVertically(int direction) override;
uint32_t modelVersion() override;
uint32_t rangeVersion() override;
BannerView m_bannerView;

View File

@@ -5,7 +5,7 @@
namespace Regression {
GraphView::GraphView(Store * store, CurveViewCursor * cursor, ::BannerView * bannerView, View * cursorView) :
CurveView(store, cursor, bannerView, cursorView, 0.05f, 0.05f, 0.25f, 0.05f),
CurveView(store, cursor, bannerView, cursorView),
m_store(store)
{
}

View File

@@ -1,4 +1,5 @@
#include "initialisation_parameter_controller.h"
#include "graph_controller.h"
#include <assert.h>
#include <math.h>
@@ -27,7 +28,8 @@ void InitialisationParameterController::didBecomeFirstResponder() {
bool InitialisationParameterController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK) {
RangeMethodPointer rangeMethods[k_totalNumberOfCells] = {&InteractiveCurveViewRange::roundAbscissa, &InteractiveCurveViewRange::normalize, &InteractiveCurveViewRange::setDefault};
RangeMethodPointer rangeMethods[k_totalNumberOfCells] = {&InteractiveCurveViewRange::roundAbscissa,
&InteractiveCurveViewRange::normalize, &InteractiveCurveViewRange::setDefault};
(m_store->*rangeMethods[m_selectableTableView.selectedRow()])();
StackViewController * stack = (StackViewController *)parentResponder();
stack->pop();

View File

@@ -143,13 +143,13 @@ void Store::setDefault() {
float min = minValueOfColumn(0);
float max = maxValueOfColumn(0);
float range = max - min;
m_xMin = min - k_cursorMarginFactorToBorder*range;
m_xMax = max + k_cursorMarginFactorToBorder*range;
m_xMin = min - k_displayLeftMarginRatio*range;
m_xMax = max + k_displayRightMarginRatio*range;
min = minValueOfColumn(1);
max = maxValueOfColumn(1);
range = max - min;
m_yMin = min - k_cursorMarginFactorToBorder*range;
m_yMax = max + k_cursorMarginFactorToBorder*range;
m_yMin = min - k_displayBottomMarginRatio*range;
m_yMax = max + k_displayTopMarginRatio*range;
if (m_xMin == m_xMax) {;
m_xMin = m_xMin - 1.0f;
m_xMax = m_xMax + 1.0f;

View File

@@ -37,6 +37,10 @@ public:
float correlationCoefficient();
float squaredCorrelationCoefficient();
private:
constexpr static float k_displayTopMarginRatio = 0.03f;
constexpr static float k_displayRightMarginRatio = 0.02f;
constexpr static float k_displayBottomMarginRatio = 0.2f;
constexpr static float k_displayLeftMarginRatio = 0.02f;
float maxValueOfColumn(int i);
float minValueOfColumn(int i);
};

View File

@@ -69,7 +69,6 @@ void BoxController::reloadBannerView() {
float calculation = (m_store->*calculationMethods[(int)m_view.selectedQuantile()])();
Float(calculation).convertFloatToText(buffer, Float::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits);
m_boxBannerView.setLegendAtIndex(buffer, 1);
m_boxBannerView.layoutSubviews();
}
}

View File

@@ -8,22 +8,25 @@ BoxRange::BoxRange(Store * store) :
}
float BoxRange::xMin() {
return m_store->minValue();
float min = m_store->minValue();
float max = m_store->maxValue();
max = min >= max ? min + 1 : max;
return min - k_displayLeftMarginRatio*(max-min);
}
float BoxRange::xMax() {
if (m_store->minValue() >= m_store->maxValue()) {
return m_store->minValue() + 1.0f;
}
return m_store->maxValue();
float min = m_store->minValue();
float max = m_store->maxValue();
max = min >= max ? min + 1 : max;
return max + k_displayRightMarginRatio*(max - min);
}
float BoxRange::yMin() {
return 0.0f;
return -k_displayBottomMarginRatio;
}
float BoxRange::yMax() {
return 1.0f;
return 1.0f+k_displayTopMarginRatio;
}
float BoxRange::xGridUnit() {

View File

@@ -15,6 +15,10 @@ public:
float yMax() override;
float xGridUnit() override;
private:
constexpr static float k_displayTopMarginRatio = 0.0f;
constexpr static float k_displayRightMarginRatio = 0.2f;
constexpr static float k_displayBottomMarginRatio = 0.4f;
constexpr static float k_displayLeftMarginRatio = 0.2f;
Store * m_store;
};

View File

@@ -5,14 +5,15 @@
namespace Statistics {
BoxView::BoxView(Store * store, ::BannerView * bannerView) :
CurveView(&m_boxRange, nullptr, bannerView, nullptr, 0.0f, 0.2f, 0.4f, 0.2f),
CurveView(&m_boxRange, nullptr, bannerView, nullptr),
m_store(store),
m_boxRange(BoxRange(store)),
m_selectedQuantile(Quantile::Min)
{
}
void BoxView::reloadSelection() {
void BoxView::reload() {
CurveView::reload();
CalculPointer calculationMethods[5] = {&Store::minValue, &Store::firstQuartile, &Store::median, &Store::thirdQuartile,
&Store::maxValue};
float calculation = (m_store->*calculationMethods[(int)m_selectedQuantile])();
@@ -32,9 +33,9 @@ bool BoxView::selectQuantile(int selectedQuantile) {
return false;
}
if ((int)m_selectedQuantile != selectedQuantile) {
reloadSelection();
reload();
m_selectedQuantile = (Quantile)selectedQuantile;
reloadSelection();
reload();
}
return true;
}

View File

@@ -20,7 +20,7 @@ public:
Max = 4
};
BoxView(Store * store, ::BannerView * bannerView);
void reloadSelection() override;
void reload() override;
Quantile selectedQuantile();
bool selectQuantile(int selectedQuantile);
void drawRect(KDContext * ctx, KDRect rect) const override;

View File

@@ -43,7 +43,7 @@ bool HistogramController::handleEvent(Ion::Events::Event event) {
if (!m_view.isMainViewSelected()) {
headerViewController()->setSelectedButton(-1);
m_view.selectMainView(true);
m_view.reloadSelection();
m_view.reload();
reloadBannerView();
return true;
}
@@ -146,7 +146,6 @@ void HistogramController::reloadBannerView() {
float frequency = size/m_store->sumOfColumn(1);
Float(frequency).convertFloatToText(buffer+legendLength, Float::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits);
m_bannerView.setLegendAtIndex(buffer, 2);
m_bannerView.layoutSubviews();
}
bool HistogramController::moveSelection(int deltaIndex) {
@@ -182,9 +181,8 @@ void HistogramController::initRangeParameters() {
if (xMin >= xMax) {
xMax = xMin + 10.0f*barWidth;
}
m_store->setXMin(xMin);
m_store->setXMax(xMax);
m_store->setYMin(0.0f);
m_store->setXMin(xMin - Store::k_displayLeftMarginRatio*(xMax-xMin));
m_store->setXMax(xMax + Store::k_displayRightMarginRatio*(xMax-xMin));
float yMax = -FLT_MAX;
for (int index = 0; index < m_store->numberOfBars(); index++) {
float size = m_store->heightOfBarAtIndex(index);
@@ -193,7 +191,8 @@ void HistogramController::initRangeParameters() {
}
}
yMax = yMax/m_store->sumOfColumn(1);
m_store->setYMax(yMax);
m_store->setYMin(-Store::k_displayBottomMarginRatio*yMax);
m_store->setYMax(yMax*(1.0f+Store::k_displayTopMarginRatio));
}
void HistogramController::initBarParameters() {

View File

@@ -5,14 +5,15 @@
namespace Statistics {
HistogramView::HistogramView(Store * store, ::BannerView * bannerView) :
CurveView(store, nullptr, bannerView, nullptr, 0.2f, 0.05f, 0.4f, 0.05f),
CurveView(store, nullptr, bannerView, nullptr),
m_store(store),
m_highlightedBarStart(NAN),
m_highlightedBarEnd(NAN)
{
}
void HistogramView::reloadSelection() {
void HistogramView::reload() {
CurveView::reload();
float pixelLowerBound = floatToPixel(Axis::Horizontal, m_highlightedBarStart)-2;
float pixelUpperBound = floatToPixel(Axis::Horizontal, m_highlightedBarEnd)+2;
KDRect dirtyZone(KDRect(pixelLowerBound, 0, pixelUpperBound-pixelLowerBound,
@@ -34,10 +35,10 @@ void HistogramView::drawRect(KDContext * ctx, KDRect rect) const {
void HistogramView::setHighlight(float start, float end) {
if (m_highlightedBarStart != start || m_highlightedBarEnd != end) {
reloadSelection();
reload();
m_highlightedBarStart = start;
m_highlightedBarEnd = end;
reloadSelection();
reload();
}
}

View File

@@ -11,7 +11,7 @@ namespace Statistics {
class HistogramView : public CurveView {
public:
HistogramView(Store * store, ::BannerView * bannerView);
void reloadSelection() override;
void reload() override;
void drawRect(KDContext * ctx, KDRect rect) const override;
void setHighlight(float start, float end);
private:

View File

@@ -8,7 +8,7 @@
namespace Statistics {
Store::Store() :
InteractiveCurveViewRange(nullptr, nullptr),
MemoizedCurveViewRange(),
FloatPairStore(),
m_barWidth(1.0f),
m_firstDrawnBarAbscissa(0.0f)
@@ -72,14 +72,14 @@ int Store::numberOfBars() {
bool Store::scrollToSelectedBarIndex(int index) {
float startSelectedBar = startOfBarAtIndex(index);
float range = m_xMax - m_xMin;
if (m_xMin > startSelectedBar) {
m_xMin = startSelectedBar;
if (m_xMin + k_displayLeftMarginRatio*range > startSelectedBar) {
m_xMin = startSelectedBar - k_displayLeftMarginRatio*range;
m_xMax = m_xMin + range;
return true;
}
float endSelectedBar = endOfBarAtIndex(index);
if (endSelectedBar > m_xMax) {
m_xMax = endSelectedBar;
if (endSelectedBar > m_xMax - k_displayRightMarginRatio*range) {
m_xMax = endSelectedBar + k_displayRightMarginRatio*range;
m_xMin = m_xMax - range;
return true;
}

View File

@@ -1,12 +1,12 @@
#ifndef STATISTICS_STORE_H
#define STATISTICS_STORE_H
#include "../interactive_curve_view_range.h"
#include "../memoized_curve_view_range.h"
#include "../float_pair_store.h"
namespace Statistics {
class Store : public InteractiveCurveViewRange, public FloatPairStore {
class Store : public MemoizedCurveViewRange, public FloatPairStore {
public:
Store();
uint32_t barChecksum();
@@ -37,6 +37,10 @@ public:
float median();
float sum();
float squaredValueSum();
constexpr static float k_displayTopMarginRatio = 0.03f;
constexpr static float k_displayRightMarginRatio = 0.02f;
constexpr static float k_displayBottomMarginRatio = 0.3f;
constexpr static float k_displayLeftMarginRatio = 0.02f;
private:
float defaultValue(int i) override;
float sumOfValuesBetween(float x1, float x2);