[apps/regression] Navigation between different series in graph

This commit is contained in:
Léa Saviot
2018-05-31 11:25:43 +02:00
parent 060d2a7c43
commit 0c2c998362
3 changed files with 140 additions and 53 deletions

View File

@@ -225,34 +225,79 @@ bool GraphController::moveCursorHorizontally(int direction) {
}
bool GraphController::moveCursorVertically(int direction) {
double yRegressionCurve = m_store->yValueForXValue(m_selectedSeries, m_cursor->x());
if (*m_selectedDotIndex >= 0) {
if ((yRegressionCurve - m_cursor->y() > 0) == (direction > 0)) {
selectRegressionCurve();
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 false;
}
volatile int closestRegressionSeries = -1;
int closestDotSeries = -1;
volatile int dotSelected = -1;
if (*m_selectedDotIndex == -1) {
// The current cursor is on a regression
// Check the closest regression
closestRegressionSeries = m_store->closestVerticalRegression(direction, m_cursor->x(), m_cursor->y(), m_selectedSeries);
// Check the closest dot
dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), direction > 0 ? -FLT_MAX : FLT_MAX, m_selectedSeries, *m_selectedDotIndex, &closestDotSeries);
} else {
int selectedSeries = -1;
int dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), &selectedSeries);
if (dotSelected >= 0 && dotSelected <= m_store->numberOfPairsOfSeries(selectedSeries)) {
m_view.setCursorView(&m_crossCursorView);
m_selectedSeries = selectedSeries;
*m_selectedDotIndex = dotSelected;
if (dotSelected == m_store->numberOfPairsOfSeries(m_selectedSeries)) {
m_cursor->moveTo(m_store->meanOfColumn(m_selectedSeries, 0), m_store->meanOfColumn(m_selectedSeries, 1));
m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
return true;
// The current cursor is on a dot
// Check the closest regression
closestRegressionSeries = m_store->closestVerticalRegression(direction, m_cursor->x(), m_cursor->y(), -1);
// Check the closest dot
dotSelected = m_store->closestVerticalDot(direction, m_cursor->x(), m_cursor->y(), m_selectedSeries, *m_selectedDotIndex, &closestDotSeries);
}
bool validRegression = closestRegressionSeries > -1;
bool validDot = dotSelected >= 0 && dotSelected <= m_store->numberOfPairsOfSeries(closestDotSeries);
if (validRegression && validDot) {
/* Compare the abscissa distances to select either the dot or the
* regression. If they are equal, compare the ordinate distances. */
double dotDistanceX = -1;
if (dotSelected == m_store->numberOfPairsOfSeries(closestDotSeries)) {
dotDistanceX = std::fabs(m_store->meanOfColumn(closestDotSeries, 0) - m_cursor->x());
} else {
dotDistanceX = std::fabs(m_store->get(closestDotSeries, 0, dotSelected) - m_cursor->x());
}
if (dotDistanceX != 0) {
/* The regression X distance to the point is 0, so it is closer than the
* dot. */
validDot = false;
} else {
// Compare the y distances
double regressionDistanceY = std::fabs(m_store->yValueForXValue(closestRegressionSeries, m_cursor->x()) - m_cursor->y());
double dotDistanceY = -1;
if (dotSelected == m_store->numberOfPairsOfSeries(closestDotSeries)) {
dotDistanceY = std::fabs(m_store->meanOfColumn(closestDotSeries, 1) - m_cursor->y());
} else {
dotDistanceY = std::fabs(m_store->get(closestDotSeries, 1, dotSelected) - m_cursor->y());
}
m_cursor->moveTo(m_store->get(m_selectedSeries, 0, *m_selectedDotIndex), m_store->get(m_selectedSeries, 1, *m_selectedDotIndex));
if (regressionDistanceY <= dotDistanceY) {
validDot = false;
} else {
validRegression = false;
}
}
}
if (!validDot && validRegression) {
// Select the regression
m_selectedSeries = closestRegressionSeries;
selectRegressionCurve();
m_cursor->moveTo(m_cursor->x(), m_store->yValueForXValue(m_selectedSeries, m_cursor->x()));
m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
return true;
}
if (validDot && !validRegression) {
m_view.setCursorView(&m_crossCursorView);
m_selectedSeries = closestDotSeries;
*m_selectedDotIndex = dotSelected;
if (dotSelected == m_store->numberOfPairsOfSeries(m_selectedSeries)) {
m_cursor->moveTo(m_store->meanOfColumn(m_selectedSeries, 0), m_store->meanOfColumn(m_selectedSeries, 1));
m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
return true;
}
return false;
m_cursor->moveTo(m_store->get(m_selectedSeries, 0, *m_selectedDotIndex), m_store->get(m_selectedSeries, 1, *m_selectedDotIndex));
m_store->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
return true;
}
return false;
}
uint32_t GraphController::modelVersion() {

View File

@@ -17,45 +17,80 @@ Store::Store() :
{
}
/* Regressions */
int Store::closestVerticalRegression(int direction, float x, float y, int currentRegressionSeries) {
int regressionSeries = -1;
float closestDistance = INFINITY;
/* The conditions to test on all the regressions are in this order:
* - the current regression is not the current regression
* - the next regression point should be within the window abscissa bounds
* - it is the closest one in abscissa to x
* - it is above y if direction > 0 and below otherwise */
for (int series = 0; series < k_numberOfSeries; series ++) {
if (!seriesIsEmpty(series) && series != currentRegressionSeries) {
double regressionY = yValueForXValue(series, x);
if ((m_yMin <= regressionY && regressionY <= m_yMax)
&& (std::fabs(regressionY - y) < closestDistance)
&& (regressionY - y > 0) == (direction > 0)) {
closestDistance = std::fabs(regressionY - y);
regressionSeries = series;
}
}
}
return regressionSeries;
}
/* Dots */
int Store::closestVerticalDot(int direction, float x, int * nextSeries) {
int Store::closestVerticalDot(int direction, float x, float y, int currentSeries, int currentDot, int * nextSeries) {
float nextX = INFINITY;
float nextY = INFINITY;
int selectedDot = -1;
/* The conditions to test on all dots are in this order:
* - the next dot should be within the window abscissa bounds
* - the next dot is the closest one in abscissa to x
* - the next dot is above the regression curve if direction == 1 and below
* otherwise */
* - if the currentDot is valid, the next series should not be the current series
* - the next dot should not be the current dot
* - the next dot should be within the window abscissa bounds
* - the next dot is the closest one in abscissa to x
* - the next dot is above the regression curve if direction == 1 and below
* otherwise
* - the next dot is above/under y
* - if the current dot is valid, do not select a dot of the same series */
for (int series = 0; series < k_numberOfSeries; series ++) {
if (!seriesIsEmpty(series)) {
if (!seriesIsEmpty(series) && (currentDot < 0 || currentSeries != series)) {
for (int index = 0; index < numberOfPairsOfSeries(series); index++) {
if ((m_xMin <= m_data[series][0][index] && m_data[series][0][index] <= m_xMax) &&
(std::fabs(m_data[series][0][index] - x) < std::fabs(nextX - x)) &&
((m_data[series][1][index] - yValueForXValue(series, m_data[series][0][index]) >= 0) == (direction > 0))) {
// Handle edge case: if 2 dots have the same abscissa but different ordinates
if (nextX != m_data[series][0][index] || ((nextY - m_data[series][1][index] >= 0) == (direction > 0))) {
nextX = m_data[series][0][index];
nextY = m_data[series][1][index];
selectedDot = index;
if ((currentSeries != series) || (index != currentDot)) {
double currentDataX = m_data[series][0][index];
double currentDataY = m_data[series][1][index];
if ((m_xMin <= currentDataX && currentDataX <= m_xMax) &&
(std::fabs(currentDataX - x) <= std::fabs(nextX - x)) &&
((currentDataY - yValueForXValue(currentSeries, currentDataX) >= 0) == (direction > 0)) &&
((currentDataY > y) == (direction > 0))) {
// Handle edge case: if 2 dots have the same abscissa but different ordinates
if (nextX != currentDataX || ((nextY - currentDataY >= 0) == (direction > 0))) {
nextX = currentDataX;
nextY = currentDataY;
selectedDot = index;
*nextSeries = series;
}
}
}
}
// Compare with the mean dot
if ((currentSeries != series) || (numberOfPairsOfSeries(series) != currentDot)) {
double meanX = meanOfColumn(series, 0);
double meanY = meanOfColumn(series, 1);
if (m_xMin <= meanX && meanX <= m_xMax &&
(std::fabs(meanX - x) <= std::fabs(nextX - x)) &&
((meanY - yValueForXValue(currentSeries, meanX) >= 0) == (direction > 0)) &&
((meanY > y) == (direction > 0))) {
if (nextX != meanX || ((nextY - meanY >= 0) == (direction > 0))) {
selectedDot = numberOfPairsOfSeries(series);
*nextSeries = series;
}
}
}
}
// Compare with the mean dot
double meanX = meanOfColumn(series, 0);
double meanY = meanOfColumn(series, 1);
if (m_xMin <= meanX && meanX <= m_xMax &&
(std::fabs(meanX - x) < std::fabs(nextX - x)) &&
((meanY - yValueForXValue(series, meanX) >= 0) == (direction > 0))) {
if (nextX != meanX || ((nextY - meanY >= 0) == (direction > 0))) {
selectedDot = numberOfPairsOfSeries(series);
*nextSeries = series;
}
}
}
return selectedDot;
}
@@ -88,8 +123,8 @@ int Store::nextDot(int series, int direction, int dot) {
}
// Compare with the mean dot
if (std::fabs(meanX - x) < std::fabs(nextX - x) &&
(numberOfPairsOfSeries(series) != dot) &&
(meanX >= x)) {
(numberOfPairsOfSeries(series) != dot) &&
(meanX >= x)) {
if (meanX != x || (numberOfPairsOfSeries(series) > dot)) {
selectedDot = numberOfPairsOfSeries(series);
}
@@ -97,8 +132,8 @@ int Store::nextDot(int series, int direction, int dot) {
} else {
// Compare with the mean dot
if (std::fabs(meanX - x) < std::fabs(nextX - x) &&
(numberOfPairsOfSeries(series) != dot) &&
(meanX <= x)) {
(numberOfPairsOfSeries(series) != dot) &&
(meanX <= x)) {
if ((meanX != x) || (numberOfPairsOfSeries(series) < dot)) {
nextX = meanX;
selectedDot = numberOfPairsOfSeries(series);

View File

@@ -3,6 +3,9 @@
#include "../shared/interactive_curve_view_range.h"
#include "../shared/float_pair_store.h"
extern "C" {
#include <float.h>
}
namespace Regression {
@@ -10,10 +13,14 @@ class Store : public Shared::InteractiveCurveViewRange, public Shared::FloatPair
public:
Store();
// Regression
/* Return the series index of the closest regression at abscissa x, above
* ordinate y if direction > 0, below otherwise */
int closestVerticalRegression(int direction, float x, float y, int currentRegressionSeries);
// Dots
/* Return the closest dot to abscissa x above the regression curve if
* direction > 0, below otherwise */
int closestVerticalDot(int direction, float x, int * nextSeries);
int closestVerticalDot(int direction, float x, float y, int currentSeries, int currentDot, int * nextSeries);
/* Return the closest dot to given dot, on the right if direction > 0,
* on the left otherwise */
int nextDot(int series, int direction, int dot);