mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
[apps/regression] Navigation between different series in graph
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user