mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-18 21:30:38 +01:00
[apps] Improvements of MVC structure regarding curve views and ranges
Change-Id: Iec8031dbf349c34c18694dffabd02ef9c88ebf2d
This commit is contained in:
@@ -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\
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
70
apps/memoized_curve_view_range.cpp
Normal file
70
apps/memoized_curve_view_range.cpp
Normal 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);
|
||||
}
|
||||
32
apps/memoized_curve_view_range.h
Normal file
32
apps/memoized_curve_view_range.h
Normal 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
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user