Skip to content
Permalink
Browse files

Datafilter vectors - curve()

.. curve(series, x|y|z|d|t) - retrieve the data from another curve so
   we don't have to recompute it when working with user charts.

   if the datafilter is not on a user chart it will return 0. User
   needs to ensure the series are ordered correctly for this to work.
  • Loading branch information
liversedge committed Mar 25, 2020
1 parent b24869e commit bda728696f61ef877511c95129fedd1df2ea3f77
@@ -133,24 +133,29 @@ UserChart::setRide(RideItem *item)
chart->initialiseChart(chartinfo.title, chartinfo.type, chartinfo.animate, chartinfo.legendpos, chartinfo.stack, chartinfo.orientation);

// now generate the series data
foreach (GenericSeriesInfo series, seriesinfo) {
for (int ii=0; ii<seriesinfo.count(); ii++) {

// get a reference to it as we update it
GenericSeriesInfo &series = seriesinfo[ii];

// clear old annotations for this series
annotations.clear();

// create program
if (series.user1 == NULL) {

series.user1 = new UserChartData(context, series.string1);
series.user1 = new UserChartData(context, this, series.string1);
connect(static_cast<UserChartData*>(series.user1)->program, SIGNAL(annotateLabel(QStringList&)), this, SLOT(annotateLabel(QStringList&)));
}

// cast so we can work with it
UserChartData *ucd = static_cast<UserChartData*>(series.user1);
ucd->compute(ride, Specification(), dr);
series.xseries = ucd->x.vector;
series.yseries = ucd->y.vector;

// data now generated so can add curve
chart->addCurve(series.name, ucd->x.vector, ucd->y.vector, series.xname, series.yname,
chart->addCurve(series.name, series.xseries, series.yseries, series.xname, series.yname,
series.labels, series.colors,
series.line, series.symbol, series.size, series.color, series.opacity, series.opengl, series.legend, series.datalabels);

@@ -287,7 +292,11 @@ UserChart::applySettings(QString x)
chartinfo.stack = obj["stack"].toBool();
chartinfo.orientation = obj["orientation"].toInt();

// array of series
// array of series, but userchartdata needs to be deleted
foreach(GenericSeriesInfo series, seriesinfo)
if (series.user1 != NULL)
delete static_cast<UserChartData*>(series.user1);

seriesinfo.clear();
foreach(QJsonValue it, obj["SERIES"].toArray()) {

@@ -31,6 +31,7 @@
#include "GenericChart.h"
#include "GenericPlot.h"
#include "ColorButton.h"
#include "DataFilter.h"

// the chart
class UserChartSettings;
@@ -43,6 +44,8 @@ class UserChart : public GcChartWindow {
// we can rely on JSON support since we also need Qt Charts which is qt5 also
Q_PROPERTY(QString settings READ settings WRITE applySettings USER true)

friend class ::Leaf; // data filter eval accessing our curve data

public:

UserChart(Context *context, bool rangemode);
@@ -20,12 +20,13 @@
#include "UserChartData.h"
#include "DataFilter.h"

UserChartData::UserChartData(Context *context, QString script) : context(context), script(script)
UserChartData::UserChartData(Context *context, UserChart *parent, QString script) : context(context), script(script)
{
// compile the program - built in a context that can close.
program = new DataFilter(NULL, context, script);
root = program->root();
rt = &program->rt;
rt->chart = parent;

// lookup functions we need
finit = rt->functions.contains("init") ? rt->functions.value("init") : NULL;
@@ -23,14 +23,15 @@
#include "DataFilter.h"
#include "Specification.h"

class UserChart;
class UserChartData : public QObject {

Q_OBJECT

public:

// new program
UserChartData(Context *context, QString script);
UserChartData(Context *context, UserChart *parent, QString script);
~UserChartData();

// Compute from samples
@@ -52,6 +53,9 @@ class UserChartData : public QObject {
// our runtime
DataFilterRuntime *rt;

// our owner
UserChart *parent;

};

#endif // _GC_UserChartData_h
@@ -44,6 +44,7 @@ QMutex pythonMutex;
#include "Zones.h"
#include "PaceZones.h"
#include "HrZones.h"
#include "UserChart.h"

#include "DataFilter_yacc.h"

@@ -204,6 +205,10 @@ static struct {
{ "variance", 1 }, // variance(v) - calculates the variance for the elements in the vector.
{ "stddev", 1 }, // stddev(v) - calculates the standard deviation for elements in the vector.

{ "curve", 2 }, // curve(series, x|y|z|d|t) - fetch the computed values for another curve
// will need to be on a user chart, and the series will need to have already
// been computed.


// add new ones above this line
{ "", -1 }
@@ -344,6 +349,14 @@ DataFilter::builtins()

returning << "arguniq(list)";

} else if (i == 68) {

returning << "uniq(list [,list n])";

} else if (i == 71) {

returning << "curve(series, x|y|z|d|t)";

} else {

QString function;
@@ -1319,6 +1332,7 @@ void Leaf::validateFilter(Context *context, DataFilterRuntime *df, Leaf *leaf)
QRegExp pmcValidSymbols("^(stress|lts|sts|sb|rr|date)$", Qt::CaseInsensitive);
QRegExp smoothAlgos("^(sma|ewma)$", Qt::CaseInsensitive);
QRegExp annotateTypes("^(label)$", Qt::CaseInsensitive);
QRegExp curveData("^(x|y|z|d|t)$", Qt::CaseInsensitive);

if (leaf->series) { // old way of hand crafting each function in the lexer including support for literal parameter e.g. (power, 1)

@@ -1590,6 +1604,22 @@ void Leaf::validateFilter(Context *context, DataFilterRuntime *df, Leaf *leaf)
}
}

} else if (leaf->function == "curve") {

// on a user chart we can access computed values
// for other series, reduces overhead etc
if (leaf->fparms.count() != 2 || leaf->fparms[0]->type != Leaf::Symbol || leaf->fparms[1]->type != Leaf::Symbol) {
leaf->inerror = true;
DataFiltererrors << QString(tr("curve(seriesname, x|y|z|d|t), need to specify series name and data."));
} else {
// check series data
QString symbol = *(leaf->fparms[1]->lvalue.n);
if (!curveData.exactMatch(symbol)) {
DataFiltererrors << QString(tr("'%1' is not a valid, x, y, z, d or t expected").arg(symbol));
leaf->inerror = true;
}
}

} else if (leaf->function == "meanmax") {

// is the param 1 a valid data series?
@@ -2083,6 +2113,7 @@ DataFilter::DataFilter(QObject *parent, Context *context) : QObject(parent), con
{
// let folks know who owns this rumtime for signalling
rt.owner = this;
rt.chart = NULL;

// be sure not to enable this by accident!
rt.isdynamic = false;
@@ -2103,6 +2134,7 @@ DataFilter::DataFilter(QObject *parent, Context *context, QString formula) : QOb
{
// let folks know who owns this rumtime for signalling
rt.owner = this;
rt.chart = NULL;

// be sure not to enable this by accident!
rt.isdynamic = false;
@@ -3016,6 +3048,28 @@ Result Leaf::eval(DataFilterRuntime *df, Leaf *leaf, float x, long it, RideItem
return Result(count);
}

// access user chart curve data, if it's there
if (leaf->function == "curve") {

// not on a chart m8
if (df->chart == NULL) return Result(0);

Result returning(0);

// lets see if we can find the series
QString symbol = *(leaf->fparms[0]->lvalue.n);
for(int i=0; i<df->chart->seriesinfo.count(); i++) {
if (df->chart->seriesinfo[i].name == symbol) {
// woop!
QString data=*(leaf->fparms[1]->lvalue.n);
if (data == "x") returning.vector = df->chart->seriesinfo[i].xseries;
if (data == "y") returning.vector = df->chart->seriesinfo[i].yseries;
// z d t still todo XXX
}
}
return returning;
}

// sort
if (leaf->function == "sort") {

@@ -112,6 +112,7 @@ class Leaf {
RideFile::XDataJoin xjoin; // how to join xdata with main
};

class UserChart;
class DataFilterRuntime {

// allocated for each thread to avoid race
@@ -126,6 +127,10 @@ class DataFilterRuntime {
// needs to be reapplied as the ride selection changes
bool isdynamic;

// the user chart if were used for that
// enables use of cache() command
UserChart *chart;

// Lookup tables
QMap<QString,QString> lookupMap;
QMap<QString,bool> lookupType; // true if a number, false if a string

0 comments on commit bda7286

Please sign in to comment.
You can’t perform that action at this time.