Merge changes I6b4db914,Iec8031db,Id6e0aa7f,I7069b55c,I52f197f9, ...

* changes:
  [apps/regression] Clean
  [apps] Improvements of MVC structure regarding curve views and ranges
  [apps/regression] Change de default initialisation of regression graph
  [poincare] Create a class product
  [poincare] Create a product layout class
  [poincare] Create a class symbol layout to be used by sum and product layout
This commit is contained in:
Émilie Feral
2017-01-27 10:38:17 +01:00
committed by Gerrit
54 changed files with 618 additions and 380 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

@@ -27,7 +27,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

@@ -140,10 +140,16 @@ int Store::nextDot(int direction, int dot) {
/* Window */
void Store::setDefault() {
m_xMin = minValueOfColumn(0);
m_xMax = maxValueOfColumn(0);
m_yMin = minValueOfColumn(1);
m_yMax = maxValueOfColumn(1);
float min = minValueOfColumn(0);
float max = maxValueOfColumn(0);
float range = max - min;
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_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);

View File

@@ -29,6 +29,7 @@ objs += $(addprefix poincare/src/,\
opposite.o\
parenthesis.o\
power.o\
product.o\
sine.o\
subtraction.o\
sum.o\
@@ -46,9 +47,11 @@ objs += $(addprefix poincare/src/layout/,\
matrix_layout.o\
nth_root_layout.o\
parenthesis_layout.o\
product_layout.o\
string_layout.o\
sum_layout.o\
subscript_layout.o\
symbol_layout.o\
)
tests += $(addprefix poincare/test/,\

View File

@@ -24,6 +24,7 @@
#include <poincare/opposite.h>
#include <poincare/parenthesis.h>
#include <poincare/power.h>
#include <poincare/product.h>
#include <poincare/sine.h>
#include <poincare/subtraction.h>
#include <poincare/sum.h>

View File

@@ -25,6 +25,7 @@ class Expression {
Fraction,
Parenthesis,
Power,
Product,
Sine,
Sum,
Subtraction,

View File

@@ -0,0 +1,17 @@
#ifndef POINCARE_PRODUCT_H
#define POINCARE_PRODUCT_H
#include <poincare/function.h>
#include <poincare/n_context.h>
class Product : public Function {
public:
Product();
float approximate(Context & context) const override;
Type type() const override;
Expression * cloneWithDifferentOperands(Expression ** newOperands,
int numberOfOperands, bool cloneOperands = true) const override;
ExpressionLayout * createLayout() const override;
};
#endif

View File

@@ -90,6 +90,7 @@ tan { poincare_expression_yylval.expression = new Tangent(); return FUNCTION; }
log { poincare_expression_yylval.expression = new Logarithm(); return FUNCTION; }
root { poincare_expression_yylval.expression = new NthRoot(); return FUNCTION; }
sum { poincare_expression_yylval.expression = new Sum(); return FUNCTION; }
product { poincare_expression_yylval.expression = new Product(); return FUNCTION; }
\+ { return PLUS; }
\- { return MINUS; }
\* { return MULTIPLY; }

View File

@@ -0,0 +1,78 @@
#include "product_layout.h"
#include <string.h>
#include <assert.h>
ProductLayout::ProductLayout(ExpressionLayout * lowerBoundLayout, ExpressionLayout * upperBoundLayout, ExpressionLayout * argumentLayout) :
ExpressionLayout(),
m_lowerBoundLayout(lowerBoundLayout),
m_upperBoundLayout(upperBoundLayout),
m_argumentLayout(argumentLayout)
{
m_lowerBoundLayout->setParent(this);
m_upperBoundLayout->setParent(this);
m_argumentLayout->setParent(this);
m_baseline = max(m_upperBoundLayout->size().height()+k_boundHeightMargin+k_symbolHeight, m_argumentLayout->baseline());
}
ProductLayout::~ProductLayout() {
delete m_lowerBoundLayout;
delete m_upperBoundLayout;
delete m_argumentLayout;
}
void ProductLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) {
KDSize upperBoundSize = m_upperBoundLayout->size();
KDSize lowerBoundSize = m_lowerBoundLayout->size();
ctx->fillRect(KDRect(p.x() + max(max(0, (upperBoundSize.width()-k_symbolWidth)/2), (lowerBoundSize.width()-k_symbolWidth)/2),
p.y() + max(upperBoundSize.height()+k_boundHeightMargin, m_argumentLayout->baseline()-k_symbolHeight),
k_lineThickness, k_symbolHeight), expressionColor);
ctx->fillRect(KDRect(p.x() + max(max(0, (upperBoundSize.width()-k_symbolWidth)/2), (lowerBoundSize.width()-k_symbolWidth)/2),
p.y() + max(upperBoundSize.height()+k_boundHeightMargin, m_argumentLayout->baseline()-k_symbolHeight),
k_symbolWidth, k_lineThickness), expressionColor);
ctx->fillRect(KDRect(p.x() + max(max(0, (upperBoundSize.width()-k_symbolWidth)/2), (lowerBoundSize.width()-k_symbolWidth)/2)+k_symbolWidth,
p.y() + max(upperBoundSize.height()+k_boundHeightMargin, m_argumentLayout->baseline()-k_symbolHeight),
k_lineThickness, k_symbolHeight), expressionColor);
}
KDSize ProductLayout::computeSize() {
KDSize argumentSize = m_argumentLayout->size();
KDSize lowerBoundSize = m_lowerBoundLayout->size();
KDSize upperBoundSize = m_upperBoundLayout->size();
return KDSize(
max(max(k_symbolWidth, lowerBoundSize.width()), upperBoundSize.width())+k_argumentWidthMargin+argumentSize.width(),
m_baseline + max(k_boundHeightMargin+lowerBoundSize.height(), argumentSize.height() - m_argumentLayout->baseline())
);
}
ExpressionLayout * ProductLayout::child(uint16_t index) {
switch (index) {
case 0:
return m_upperBoundLayout;
case 1:
return m_lowerBoundLayout;
case 2:
return m_argumentLayout;
default:
return nullptr;
}
}
KDPoint ProductLayout::positionOfChild(ExpressionLayout * child) {
KDSize lowerBoundSize = m_lowerBoundLayout->size();
KDSize upperBoundSize = m_upperBoundLayout->size();
KDCoordinate x = 0;
KDCoordinate y = 0;
if (child == m_lowerBoundLayout) {
x = max(max(0, (k_symbolWidth-lowerBoundSize.width())/2), (upperBoundSize.width()-lowerBoundSize.width())/2);
y = m_baseline + k_boundHeightMargin;
} else if (child == m_upperBoundLayout) {
x = max(max(0, (k_symbolWidth-upperBoundSize.width())/2), (lowerBoundSize.width()-upperBoundSize.width())/2);
y = m_baseline - k_symbolHeight- k_boundHeightMargin-upperBoundSize.height();
} else if (child == m_argumentLayout) {
x = max(max(k_symbolWidth, lowerBoundSize.width()), upperBoundSize.width())+k_argumentWidthMargin;
y = m_baseline - m_argumentLayout->baseline();
} else {
assert(false);
}
return KDPoint(x,y);
}

View File

@@ -0,0 +1,27 @@
#ifndef POINCARE_PRODUCT_LAYOUT_H
#define POINCARE_PRODUCT_LAYOUT_H
#include <poincare/expression.h>
#include <poincare/expression_layout.h>
class ProductLayout : public ExpressionLayout {
public:
ProductLayout(ExpressionLayout * lowerBoundLayout, ExpressionLayout * upperBoundLayout, ExpressionLayout * argumentLayout);
~ProductLayout();
protected:
void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override;
KDSize computeSize() override;
ExpressionLayout * child(uint16_t index) override;
KDPoint positionOfChild(ExpressionLayout * child) override;
private:
constexpr static KDCoordinate k_symbolHeight = 15;
constexpr static KDCoordinate k_symbolWidth = 9;
constexpr static KDCoordinate k_boundHeightMargin = 2;
constexpr static KDCoordinate k_argumentWidthMargin = 2;
constexpr static KDCoordinate k_lineThickness = 1;
ExpressionLayout * m_lowerBoundLayout;
ExpressionLayout * m_upperBoundLayout;
ExpressionLayout * m_argumentLayout;
};
#endif

View File

@@ -20,24 +20,6 @@ const uint8_t symbolPixel[SumLayout::k_symbolHeight][SumLayout::k_symbolWidth] =
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
};
SumLayout::SumLayout(ExpressionLayout * lowerBoundLayout, ExpressionLayout * upperBoundLayout, ExpressionLayout * argumentLayout) :
ExpressionLayout(),
m_lowerBoundLayout(lowerBoundLayout),
m_upperBoundLayout(upperBoundLayout),
m_argumentLayout(argumentLayout)
{
m_lowerBoundLayout->setParent(this);
m_upperBoundLayout->setParent(this);
m_argumentLayout->setParent(this);
m_baseline = max(m_upperBoundLayout->size().height()+k_boundHeightMargin+k_symbolHeight, m_argumentLayout->baseline());
}
SumLayout::~SumLayout() {
delete m_lowerBoundLayout;
delete m_upperBoundLayout;
delete m_argumentLayout;
}
void SumLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) {
KDSize upperBoundSize = m_upperBoundLayout->size();
KDSize lowerBoundSize = m_lowerBoundLayout->size();
@@ -47,46 +29,3 @@ void SumLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDCo
k_symbolWidth, k_symbolHeight);
ctx->blendRectWithMask(symbolFrame, expressionColor, (const uint8_t *)symbolPixel, (KDColor *)workingBuffer);
}
KDSize SumLayout::computeSize() {
KDSize argumentSize = m_argumentLayout->size();
KDSize lowerBoundSize = m_lowerBoundLayout->size();
KDSize upperBoundSize = m_upperBoundLayout->size();
return KDSize(
max(max(k_symbolWidth, lowerBoundSize.width()), upperBoundSize.width())+k_argumentWidthMargin+argumentSize.width(),
m_baseline + max(k_boundHeightMargin+lowerBoundSize.height(), argumentSize.height() - m_argumentLayout->baseline())
);
}
ExpressionLayout * SumLayout::child(uint16_t index) {
switch (index) {
case 0:
return m_upperBoundLayout;
case 1:
return m_lowerBoundLayout;
case 2:
return m_argumentLayout;
default:
return nullptr;
}
}
KDPoint SumLayout::positionOfChild(ExpressionLayout * child) {
KDSize lowerBoundSize = m_lowerBoundLayout->size();
KDSize upperBoundSize = m_upperBoundLayout->size();
KDCoordinate x = 0;
KDCoordinate y = 0;
if (child == m_lowerBoundLayout) {
x = max(max(0, (k_symbolWidth-lowerBoundSize.width())/2), (upperBoundSize.width()-lowerBoundSize.width())/2);
y = m_baseline + k_boundHeightMargin;
} else if (child == m_upperBoundLayout) {
x = max(max(0, (k_symbolWidth-upperBoundSize.width())/2), (lowerBoundSize.width()-upperBoundSize.width())/2);
y = m_baseline - k_symbolHeight- k_boundHeightMargin-upperBoundSize.height();
} else if (child == m_argumentLayout) {
x = max(max(k_symbolWidth, lowerBoundSize.width()), upperBoundSize.width())+k_argumentWidthMargin;
y = m_baseline - m_argumentLayout->baseline();
} else {
assert(false);
}
return KDPoint(x,y);
}

View File

@@ -1,26 +1,13 @@
#ifndef POINCARE_SUM_LAYOUT_H
#define POINCARE_SUM_LAYOUT_H
#include <poincare/expression.h>
#include <poincare/expression_layout.h>
#include "symbol_layout.h"
class SumLayout : public ExpressionLayout {
class SumLayout : public SymbolLayout {
public:
SumLayout(ExpressionLayout * lowerBoundLayout, ExpressionLayout * upperBoundLayout, ExpressionLayout * argumentLayout);
~SumLayout();
constexpr static KDCoordinate k_symbolHeight = 15;
constexpr static KDCoordinate k_symbolWidth = 9;
protected:
void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override;
KDSize computeSize() override;
ExpressionLayout * child(uint16_t index) override;
KDPoint positionOfChild(ExpressionLayout * child) override;
using SymbolLayout::SymbolLayout;
private:
constexpr static KDCoordinate k_boundHeightMargin = 2;
constexpr static KDCoordinate k_argumentWidthMargin = 2;
ExpressionLayout * m_lowerBoundLayout;
ExpressionLayout * m_upperBoundLayout;
ExpressionLayout * m_argumentLayout;
void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override;
};
#endif

View File

@@ -0,0 +1,64 @@
#include "symbol_layout.h"
#include <string.h>
#include <assert.h>
SymbolLayout::SymbolLayout(ExpressionLayout * lowerBoundLayout, ExpressionLayout * upperBoundLayout, ExpressionLayout * argumentLayout) :
ExpressionLayout(),
m_lowerBoundLayout(lowerBoundLayout),
m_upperBoundLayout(upperBoundLayout),
m_argumentLayout(argumentLayout)
{
m_lowerBoundLayout->setParent(this);
m_upperBoundLayout->setParent(this);
m_argumentLayout->setParent(this);
m_baseline = max(m_upperBoundLayout->size().height()+k_boundHeightMargin+k_symbolHeight, m_argumentLayout->baseline());
}
SymbolLayout::~SymbolLayout() {
delete m_lowerBoundLayout;
delete m_upperBoundLayout;
delete m_argumentLayout;
}
KDSize SymbolLayout::computeSize() {
KDSize argumentSize = m_argumentLayout->size();
KDSize lowerBoundSize = m_lowerBoundLayout->size();
KDSize upperBoundSize = m_upperBoundLayout->size();
return KDSize(
max(max(k_symbolWidth, lowerBoundSize.width()), upperBoundSize.width())+k_argumentWidthMargin+argumentSize.width(),
m_baseline + max(k_boundHeightMargin+lowerBoundSize.height(), argumentSize.height() - m_argumentLayout->baseline())
);
}
ExpressionLayout * SymbolLayout::child(uint16_t index) {
switch (index) {
case 0:
return m_upperBoundLayout;
case 1:
return m_lowerBoundLayout;
case 2:
return m_argumentLayout;
default:
return nullptr;
}
}
KDPoint SymbolLayout::positionOfChild(ExpressionLayout * child) {
KDSize lowerBoundSize = m_lowerBoundLayout->size();
KDSize upperBoundSize = m_upperBoundLayout->size();
KDCoordinate x = 0;
KDCoordinate y = 0;
if (child == m_lowerBoundLayout) {
x = max(max(0, (k_symbolWidth-lowerBoundSize.width())/2), (upperBoundSize.width()-lowerBoundSize.width())/2);
y = m_baseline + k_boundHeightMargin;
} else if (child == m_upperBoundLayout) {
x = max(max(0, (k_symbolWidth-upperBoundSize.width())/2), (lowerBoundSize.width()-upperBoundSize.width())/2);
y = m_baseline - k_symbolHeight- k_boundHeightMargin-upperBoundSize.height();
} else if (child == m_argumentLayout) {
x = max(max(k_symbolWidth, lowerBoundSize.width()), upperBoundSize.width())+k_argumentWidthMargin;
y = m_baseline - m_argumentLayout->baseline();
} else {
assert(false);
}
return KDPoint(x,y);
}

View File

@@ -0,0 +1,25 @@
#ifndef POINCARE_SYMBOL_LAYOUT_H
#define POINCARE_SYMBOL_LAYOUT_H
#include <poincare/expression.h>
#include <poincare/expression_layout.h>
class SymbolLayout : public ExpressionLayout {
public:
SymbolLayout(ExpressionLayout * lowerBoundLayout, ExpressionLayout * upperBoundLayout, ExpressionLayout * argumentLayout);
~SymbolLayout();
constexpr static KDCoordinate k_symbolHeight = 15;
constexpr static KDCoordinate k_symbolWidth = 9;
protected:
constexpr static KDCoordinate k_boundHeightMargin = 2;
ExpressionLayout * m_lowerBoundLayout;
ExpressionLayout * m_upperBoundLayout;
ExpressionLayout * m_argumentLayout;
private:
KDSize computeSize() override;
ExpressionLayout * child(uint16_t index) override;
KDPoint positionOfChild(ExpressionLayout * child) override;
constexpr static KDCoordinate k_argumentWidthMargin = 2;
};
#endif

49
poincare/src/product.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include <poincare/product.h>
#include <poincare/symbol.h>
#include <poincare/float.h>
#include "layout/string_layout.h"
#include "layout/horizontal_layout.h"
#include "layout/product_layout.h"
extern "C" {
#include <assert.h>
#include <stdlib.h>
}
Product::Product() :
Function("product")
{
}
Expression::Type Product::type() const {
return Type::Product;
}
Expression * Product::cloneWithDifferentOperands(Expression** newOperands,
int numberOfOperands, bool cloneOperands) const {
assert(numberOfOperands == 2);
assert(newOperands != nullptr);
Product * p = new Product();
p->setArgument(newOperands, numberOfOperands, cloneOperands);
return p;
}
float Product::approximate(Context& context) const {
NContext nContext = NContext(&context);
Symbol nSymbol = Symbol('n');
int start = m_args[1]->approximate(context);
int end = m_args[2]->approximate(context);
float result = 1.0f;
for (int i = start; i <= end; i++) {
Float iExpression = Float(i);
nContext.setExpressionForSymbolName(&iExpression, &nSymbol);
result = result*m_args[0]->approximate(nContext);
}
return result;
}
ExpressionLayout * Product::createLayout() const {
ExpressionLayout ** childrenLayouts = (ExpressionLayout **)malloc(2*sizeof(ExpressionLayout *));
childrenLayouts[0] = new StringLayout("n=", 2);
childrenLayouts[1] = m_args[1]->createLayout();
return new ProductLayout(new HorizontalLayout(childrenLayouts, 2), m_args[2]->createLayout(), m_args[0]->createLayout());
}