From cf8c6f40a513ea9fc98a8501255bc83282f2eb2f Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Sat, 22 Oct 2011 12:03:15 +0100 Subject: [PATCH] PfPv plot adjust axis for track sprinters Higher values for AEPF/CPV have been observed with track sprinters. The fixed axis on the PfPv (QA) charts truncate the display. This patch adjusts the axes to suit the data and will also ensure the zone shading and labels are extended to the new maximums. The PfPv code also contained a lot of tab characters and the general style was quite dense, making it difficult to read and understand. I have adjusted to use a 4 space tab-stop and adjusted to match the general style of the rest of GC. Fixes #463. --- src/PfPvPlot.cpp | 471 ++++++++++++++++++++++++++--------------------- src/PfPvPlot.h | 50 ++--- 2 files changed, 283 insertions(+), 238 deletions(-) diff --git a/src/PfPvPlot.cpp b/src/PfPvPlot.cpp index 63217b8bf..4368ef0be 100644 --- a/src/PfPvPlot.cpp +++ b/src/PfPvPlot.cpp @@ -44,98 +44,81 @@ // at the center of the plot class PfPvPlotZoneLabel: public QwtPlotItem { - private: - PfPvPlot *parent; - int zone_number; - double watts; - QwtText text; - public: - PfPvPlotZoneLabel(PfPvPlot *_parent, int _zone_number) - { - parent = _parent; - zone_number = _zone_number; +private: - RideItem *rideItem = parent->rideItem; - const Zones *zones = rideItem->zones; - int zone_range = rideItem->zoneRange(); + PfPvPlot *parent; + int zone_number; + double watts; + QwtText text; - setZ(1.0 + zone_number / 100.0); +public: - // create new zone labels if we're shading - if (zone_range >= 0) { - QList zone_lows = zones->getZoneLows(zone_range); - QList zone_names = zones->getZoneNames(zone_range); - int num_zones = zone_lows.size(); - assert(zone_names.size() == num_zones); - if (zone_number < num_zones) { - watts = - ( - (zone_number + 1 < num_zones) ? - 0.5 * (zone_lows[zone_number] + zone_lows[zone_number + 1]) : - ( - (zone_number > 0) ? - (1.5 * zone_lows[zone_number] - 0.5 * zone_lows[zone_number - 1]) : - 2.0 * zone_lows[zone_number] - ) - ); + PfPvPlotZoneLabel(PfPvPlot *_parent, int _zone_number) + { + parent = _parent; + zone_number = _zone_number; - text = QwtText(zone_names[zone_number]); - text.setFont(QFont("Helvetica",24, QFont::Bold)); - QColor text_color = zoneColor(zone_number, num_zones); - text_color.setAlpha(64); - text.setColor(text_color); - } - } + RideItem *rideItem = parent->rideItem; + const Zones *zones = rideItem->zones; + int zone_range = rideItem->zoneRange(); - } + setZ(1.0 + zone_number / 100.0); - virtual int rtti() const - { - return QwtPlotItem::Rtti_PlotUserItem; - } + // create new zone labels if we're shading + if (zone_range >= 0) { - void draw(QPainter *painter, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - const QRect &rect) const - { - if (parent->shadeZones() && - (rect.width() > 0) && - (rect.height() > 0) - ) { - // draw the label along a plot diagonal: - // 1. x*y = watts * dx/dv * dy/df - // 2. x/width = y/height - // => - // 1. x^2 = width/height * watts - // 2. y^2 = height/width * watts + // retrieve zone setup + QList zone_lows = zones->getZoneLows(zone_range); + QList zone_names = zones->getZoneNames(zone_range); + int num_zones = zone_lows.size(); + assert(zone_names.size() == num_zones); - double xscale = fabs(xMap.transform(3) - xMap.transform(0)) / 3; - double yscale = fabs(yMap.transform(600) - yMap.transform(0)) / 600; - if ((xscale > 0) && (yscale > 0)) { - double w = watts * xscale * yscale; - int x = xMap.transform(sqrt(w * rect.width() / rect.height()) / xscale); - int y = yMap.transform(sqrt(w * rect.height() / rect.width()) / yscale); + if (zone_number < num_zones) { - // the following code based on source for QwtPlotMarker::draw() - QRect tr(QPoint(0, 0), text.textSize(painter->font())); - tr.moveCenter(QPoint(x, y)); - text.draw(painter, tr); - } - } - } + watts = ((zone_number + 1 < num_zones) ? 0.5 * (zone_lows[zone_number] + zone_lows[zone_number + 1]) : ( (zone_number > 0) ? (1.5 * zone_lows[zone_number] - 0.5 * zone_lows[zone_number - 1]) : 2.0 * zone_lows[zone_number])); + + text = QwtText(zone_names[zone_number]); + text.setFont(QFont("Helvetica",24, QFont::Bold)); + QColor text_color = zoneColor(zone_number, num_zones); + text_color.setAlpha(64); + text.setColor(text_color); + } + } + } + + virtual int rtti() const { + return QwtPlotItem::Rtti_PlotUserItem; + } + + void draw(QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRect &rect) const { + + if (parent->shadeZones() && (rect.width() > 0) && (rect.height() > 0)) { + // draw the label along a plot diagonal: + // 1. x*y = watts * dx/dv * dy/df + // 2. x/width = y/height + // => + // 1. x^2 = width/height * watts + // 2. y^2 = height/width * watts + double xscale = fabs(xMap.transform(parent->maxCPV) - xMap.transform(0)) / parent->maxCPV; + double yscale = fabs(yMap.transform(parent->maxAEPF) - yMap.transform(0)) / parent->maxAEPF; + if ((xscale > 0) && (yscale > 0)) { + double w = watts * xscale * yscale; + int x = xMap.transform(sqrt(w * rect.width() / rect.height()) / xscale); + int y = yMap.transform(sqrt(w * rect.height() / rect.width()) / yscale); + + // the following code based on source for QwtPlotMarker::draw() + QRect tr(QPoint(0, 0), text.textSize(painter->font())); + tr.moveCenter(QPoint(x, y)); + text.draw(painter, tr); + } + } + } }; -QwtArray PfPvPlot::contour_xvalues; - PfPvPlot::PfPvPlot(MainWindow *mainWindow) - : rideItem (NULL), - mainWindow(mainWindow), - cp_ (0), - cad_ (85), - cl_ (0.175), - shade_zones(true) + : rideItem (NULL), mainWindow(mainWindow), cp_ (0), cad_ (85), cl_ (0.175), shade_zones(true) { setInstanceName("PfPv Plot"); @@ -222,7 +205,6 @@ PfPvPlot::configChanged() mX->setLinePen(marker); mY->setLinePen(marker); cpCurve->setPen(cp); - } void @@ -244,104 +226,97 @@ PfPvPlot::refreshZoneItems() { // clear out any zone curves which are presently defined if (zoneCurves.size()) { - QListIterator i(zoneCurves); - while (i.hasNext()) { - QwtPlotCurve *curve = i.next(); - curve->detach(); - delete curve; - } + + QListIterator i(zoneCurves); + while (i.hasNext()) { + QwtPlotCurve *curve = i.next(); + curve->detach(); + delete curve; + } } zoneCurves.clear(); - // delete any existing power zone labels if (zoneLabels.size()) { - QListIterator i(zoneLabels); - while (i.hasNext()) { - PfPvPlotZoneLabel *label = i.next(); - label->detach(); - delete label; - } + + QListIterator i(zoneLabels); + while (i.hasNext()) { + PfPvPlotZoneLabel *label = i.next(); + label->detach(); + delete label; + } } zoneLabels.clear(); - if (! rideItem) - return; + // give up for a null ride + if (! rideItem) return; const Zones *zones = rideItem->zones; int zone_range = rideItem->zoneRange(); if (zone_range >= 0) { - setCP(zones->getCP(zone_range)); + setCP(zones->getCP(zone_range)); - // populate the zone curves - QList zone_power = zones->getZoneLows(zone_range); - QList zone_name = zones->getZoneNames(zone_range); - int num_zones = zone_power.size(); - assert(zone_name.size() == num_zones); - if (num_zones > 0) { - QPen *pen = new QPen(); - pen->setStyle(Qt::NoPen); + // populate the zone curves + QList zone_power = zones->getZoneLows(zone_range); + QList zone_name = zones->getZoneNames(zone_range); + int num_zones = zone_power.size(); + assert(zone_name.size() == num_zones); + if (num_zones > 0) { + QPen *pen = new QPen(); + pen->setStyle(Qt::NoPen); - QwtArray yvalues; + QwtArray yvalues; - // generate x values - for (int z = 0; z < num_zones; z ++) { - QwtPlotCurve *curve; - curve = new QwtPlotCurve(zone_name[z]); - curve->setPen(*pen); - QColor brush_color = zoneColor(z, num_zones); - brush_color.setHsv( - brush_color.hue(), - brush_color.saturation() / 4, - brush_color.value() - ); - curve->setBrush(brush_color); // fill below the line - curve->setZ(1 - 1e-6 * zone_power[z]); + // generate x values + for (int z = 0; z < num_zones; z ++) { - // generate data for curve - if (z < num_zones - 1) { - QwtArray contour_yvalues; - int watts = zone_power[z + 1]; - int dwatts = (double) watts; - for (int i = 0; i < contour_xvalues.size(); i ++) - contour_yvalues.append( - (1e6 * contour_xvalues[i] < watts) ? - 1e6 : - dwatts / contour_xvalues[i] - ); - curve->setData(contour_xvalues, contour_yvalues); - } - else { - // top zone has a curve at "infinite" power - QwtArray contour_x; - QwtArray contour_y; - contour_x.append(contour_xvalues[0]); - contour_x.append(contour_xvalues[contour_xvalues.size() - 1]); - contour_y.append(1e6); - contour_y.append(1e6); - curve->setData(contour_x, contour_y); - } - curve->setVisible(shade_zones); - curve->attach(this); - zoneCurves.append(curve); - } + QwtPlotCurve *curve = new QwtPlotCurve(zone_name[z]); - delete pen; + curve->setPen(*pen); + QColor brush_color = zoneColor(z, num_zones); + brush_color.setHsv(brush_color.hue(), brush_color.saturation() / 4, brush_color.value()); + curve->setBrush(brush_color); // fill below the line + curve->setZ(1 - 1e-6 * zone_power[z]); + // generate data for curve + if (z < num_zones - 1) { + QwtArray contour_yvalues; + int watts = zone_power[z + 1]; + int dwatts = (double) watts; + for (int i = 0; i < contour_xvalues.size(); i ++) { + contour_yvalues.append( (1e6 * contour_xvalues[i] < watts) ? 1e6 : dwatts / contour_xvalues[i]); + } + curve->setData(contour_xvalues, contour_yvalues); - // generate labels for existing zones - for (int z = 0; z < num_zones; z ++) { - PfPvPlotZoneLabel *label = new PfPvPlotZoneLabel(this, z); - label->setVisible(shade_zones); - label->attach(this); - zoneLabels.append(label); - } - // get the zones visible, even if data may take awhile - //replot(); + } else { - } + // top zone has a curve at "infinite" power + QwtArray contour_x; + QwtArray contour_y; + contour_x.append(contour_xvalues[0]); + contour_x.append(contour_xvalues[contour_xvalues.size() - 1]); + contour_y.append(1e6); + contour_y.append(1e6); + curve->setData(contour_x, contour_y); + } + + curve->setVisible(shade_zones); + curve->attach(this); + zoneCurves.append(curve); + } + + delete pen; + + // generate labels for existing zones + for (int z = 0; z < num_zones; z ++) { + PfPvPlotZoneLabel *label = new PfPvPlotZoneLabel(this, z); + label->setVisible(shade_zones); + label->attach(this); + zoneLabels.append(label); + } + } } } @@ -383,52 +358,53 @@ PfPvPlot::setData(RideItem *_rideItem) RideFile *ride = rideItem->ride(); if (ride) { - //setTitle(ride->startTime().toString(GC_DATETIME_FORMAT)); - // quickly erase old data - curve->setVisible(false); + recalc(); // labels etc may have changed with new ride - // handle zone stuff - refreshZoneItems(); + // quickly erase old data + curve->setVisible(false); - // due to the discrete power and cadence values returned by the - // power meter, there will very likely be many duplicate values. - // Rather than pass them all to the curve, use a set to strip - // out duplicates. - std::set > dataSet; - std::set > dataSetSelected; + // handle zone stuff + refreshZoneItems(); - long tot_cad = 0; - long tot_cad_points = 0; + // due to the discrete power and cadence values returned by the + // power meter, there will very likely be many duplicate values. + // Rather than pass them all to the curve, use a set to strip + // out duplicates. + std::set > dataSet; + std::set > dataSetSelected; + + long tot_cad = 0; + long tot_cad_points = 0; foreach(const RideFilePoint *p1, ride->dataPoints()) { - if (p1->watts != 0 && p1->cad != 0) { + if (p1->watts != 0 && p1->cad != 0) { - double aepf = (p1->watts * 60.0) / (p1->cad * cl_ * 2.0 * PI); - double cpv = (p1->cad * cl_ * 2.0 * PI) / 60.0; + double aepf = (p1->watts * 60.0) / (p1->cad * cl_ * 2.0 * PI); + double cpv = (p1->cad * cl_ * 2.0 * PI) / 60.0; dataSet.insert(std::make_pair(aepf, cpv)); - tot_cad += p1->cad; - tot_cad_points++; - } + tot_cad += p1->cad; + tot_cad_points++; + } } setCAD(tot_cad_points ? tot_cad / tot_cad_points : 0); - if (tot_cad_points == 0) { - //setTitle(tr("no cadence")); - refreshZoneItems(); - curve->setVisible(false); + if (tot_cad_points == 0) { + //setTitle(tr("no cadence")); + refreshZoneItems(); + curve->setVisible(false); - } else { - // Now that we have the set of points, transform them into the - // QwtArrays needed to set the curve's data. - QwtArray aepfArray; - QwtArray cpvArray; + } else { + // Now that we have the set of points, transform them into the + // QwtArrays needed to set the curve's data. + QwtArray aepfArray; + QwtArray cpvArray; - std::set >::const_iterator j(dataSet.begin()); - while (j != dataSet.end()) { + std::set >::const_iterator j(dataSet.begin()); + while (j != dataSet.end()) { const std::pair& dataPoint = *j; aepfArray.push_back(dataPoint.first); @@ -482,8 +458,8 @@ PfPvPlot::showIntervals(RideItem *_rideItem) int num_intervals=intervalCount(); if (mergeIntervals()) num_intervals = 1; - if (frameIntervals() || num_intervals==0) curve->setVisible(true); - if (frameIntervals()==false && num_intervals) curve->setVisible(false); + if (frameIntervals() || num_intervals==0) curve->setVisible(true); + if (frameIntervals()==false && num_intervals) curve->setVisible(false); QVector > > dataSetInterval(num_intervals); long tot_cad = 0; @@ -496,11 +472,12 @@ PfPvPlot::showIntervals(RideItem *_rideItem) double cpv = (p1->cad * cl_ * 2.0 * PI) / 60.0; for (int high=-1, t=0; tallIntervalItems()->childCount(); t++) { + IntervalItem *current = dynamic_cast(mainWindow->allIntervalItems()->child(t)); + if ((current != NULL) && current->isSelected()) { ++high; - if (p1->secs+ride->recIntSecs() > current->start - && p1->secs< current->stop) { + if (p1->secs+ride->recIntSecs() > current->start && p1->secs< current->stop) { if (mergeIntervals()) dataSetInterval[0].insert(std::make_pair(aepf, cpv)); else @@ -541,7 +518,9 @@ PfPvPlot::showIntervals(RideItem *_rideItem) int num_intervals_defined=0; QVector intervalmap; if (mainWindow->allIntervalItems() != NULL) { + num_intervals_defined = mainWindow->allIntervalItems()->childCount(); + for (int g=0; gallIntervalItems()->childCount(); g++) { IntervalItem *curr = dynamic_cast(mainWindow->allIntervalItems()->child(g)); if (curr->isSelected()) intervalmap.append(g); @@ -555,7 +534,9 @@ PfPvPlot::showIntervals(RideItem *_rideItem) if (mergeIntervals()) intervalOrder.insert(1,0); else { for (int i=0; iallIntervalItems()->childCount(); i++) { + IntervalItem *current = dynamic_cast(mainWindow->allIntervalItems()->child(i)); + if (current != NULL && current->isSelected() == true) { intervalOrder.insert(current->displaySequence, count++); } @@ -588,7 +569,7 @@ PfPvPlot::showIntervals(RideItem *_rideItem) intervalCurves.append(curve); } - } + } } replot(); } @@ -596,13 +577,65 @@ PfPvPlot::showIntervals(RideItem *_rideItem) void PfPvPlot::recalc() { - // initialize x values used for contours - if (contour_xvalues.isEmpty()) { - for (double x = 0; x <= 3.0; x += x / 20 + 0.02) - contour_xvalues.append(x); - contour_xvalues.append(3.0); + // adjust the scales if we have some big values + // this can happen with track sprinters who put + // out big numbers for power and cadence since + // hey have a fixed gear and big quads! + maxAEPF = 600; + maxCPV = 3; + + RideFile *ride; + if (rideItem && (ride=rideItem->ride())) { + + // calculate maximums + foreach(const RideFilePoint *p1, ride->dataPoints()) { + + if (p1->watts != 0 && p1->cad != 0) { + + double aepf = (p1->watts * 60.0) / (p1->cad * cl_ * 2.0 * PI); + double cpv = (p1->cad * cl_ * 2.0 * PI) / 60.0; + + if (aepf > maxAEPF) maxAEPF = aepf; + if (cpv > maxCPV) maxCPV = cpv; + } + } } + if (maxAEPF > 600) { + + setAxisScale(yLeft, 0, maxAEPF * 1.1); // a bit of headroom + tiqMarker[0]->setYValue(maxAEPF); + tiqMarker[1]->setYValue(maxAEPF); + + } else { + + maxAEPF = 600; // for background shading and CP curve + setAxisScale(yLeft, 0, 600); + tiqMarker[0]->setYValue(580); + tiqMarker[1]->setYValue(580); + } + + if (maxCPV > 3) { + + // round *UP* to next integer for axis to fill nicely + maxCPV = round(maxCPV + 0.5); + setAxisScale(xBottom, 0, maxCPV); + tiqMarker[0]->setXValue(maxCPV - 0.5); + tiqMarker[3]->setXValue(maxCPV - 0.5); + + } else { + + maxCPV = 3; // for background shading and CP curve + setAxisScale(xBottom, 0, 3); + tiqMarker[0]->setXValue(2.9); + tiqMarker[3]->setXValue(2.9); + } + + // initialize x values used for contours + contour_xvalues.clear(); + for (double x = 0; x <= maxCPV; x += x / 20 + 0.02) contour_xvalues.append(x); + contour_xvalues.append(maxCPV); + double cpv = (cad_ * cl_ * 2.0 * PI) / 60.0; mX->setXValue(cpv); @@ -610,7 +643,6 @@ PfPvPlot::recalc() mY->setYValue(aepf); // watch out for null rides - RideFile *ride; if (rideItem && (ride=rideItem->ride())) { timeInQuadrant[0]= @@ -635,39 +667,50 @@ PfPvPlot::recalc() double totaltime = timeInQuadrant[0] + timeInQuadrant[1] + timeInQuadrant[2] + timeInQuadrant[3] ; if (totaltime) { - tiqMarker[0]->setLabel(QwtText(QString("%1%").arg(timeInQuadrant[0] / totaltime * 100, 0, 'f', 1),QwtText::PlainText)); - tiqMarker[1]->setLabel(QwtText(QString("%1%").arg(timeInQuadrant[1] / totaltime * 100, 0, 'f', 1),QwtText::PlainText)); - tiqMarker[2]->setLabel(QwtText(QString("%1%").arg(timeInQuadrant[2] / totaltime * 100, 0, 'f', 1),QwtText::PlainText)); - tiqMarker[3]->setLabel(QwtText(QString("%1%").arg(timeInQuadrant[3] / totaltime * 100, 0, 'f', 1),QwtText::PlainText)); + + tiqMarker[0]->setLabel(QwtText(QString("%1%") + .arg(timeInQuadrant[0] / totaltime * 100, 0, 'f', 1),QwtText::PlainText)); + tiqMarker[1]->setLabel(QwtText(QString("%1%") + .arg(timeInQuadrant[1] / totaltime * 100, 0, 'f', 1),QwtText::PlainText)); + tiqMarker[2]->setLabel(QwtText(QString("%1%") + .arg(timeInQuadrant[2] / totaltime * 100, 0, 'f', 1),QwtText::PlainText)); + tiqMarker[3]->setLabel(QwtText(QString("%1%") + .arg(timeInQuadrant[3] / totaltime * 100, 0, 'f', 1),QwtText::PlainText)); + } else { + tiqMarker[0]->setLabel(QwtText("",QwtText::PlainText)); tiqMarker[1]->setLabel(QwtText("",QwtText::PlainText)); tiqMarker[2]->setLabel(QwtText("",QwtText::PlainText)); tiqMarker[3]->setLabel(QwtText("",QwtText::PlainText)); + } + } else { + tiqMarker[0]->setLabel(QwtText("",QwtText::PlainText)); tiqMarker[1]->setLabel(QwtText("",QwtText::PlainText)); tiqMarker[2]->setLabel(QwtText("",QwtText::PlainText)); tiqMarker[3]->setLabel(QwtText("",QwtText::PlainText)); + } QwtArray yvalues(contour_xvalues.size()); + if (cp_) { - for (int i = 0; i < contour_xvalues.size(); i ++) - yvalues[i] = - (cpv < cp_ / 1e6) ? - 1e6 : - cp_ / contour_xvalues[i]; - // generate curve at a given power - cpCurve->setData(contour_xvalues, yvalues); + // reinitialise array + for (int i = 0; i < contour_xvalues.size(); i ++) + yvalues[i] = (cpv < cp_ / 1e6) ? 1e6 : cp_ / contour_xvalues[i]; + + // generate curve at a given power + cpCurve->setData(contour_xvalues, yvalues); + + } else { + + // an empty curve if no power (or zero power) is specified + cpCurve->setData(QwtArray(), QwtArray()); } - else - // an empty curve if no power (or zero power) is specified - cpCurve->setData(QwtArray(), QwtArray()); - - //replot(); } int @@ -719,9 +762,9 @@ PfPvPlot::setShadeZones(bool value) // if there are defined zones and labels, set their visibility for (int i = 0; i < zoneCurves.size(); i ++) - zoneCurves[i]->setVisible(shade_zones); + zoneCurves[i]->setVisible(shade_zones); for (int i = 0; i < zoneLabels.size(); i ++) - zoneLabels[i]->setVisible(shade_zones); + zoneLabels[i]->setVisible(shade_zones); //replot(); } diff --git a/src/PfPvPlot.h b/src/PfPvPlot.h index cabd615af..cc0797257 100644 --- a/src/PfPvPlot.h +++ b/src/PfPvPlot.h @@ -41,19 +41,24 @@ class PfPvPlot : public QwtPlot public: PfPvPlot(MainWindow *mainWindow); - void refreshZoneItems(); + void refreshZoneItems(); void setData(RideItem *_rideItem); void showIntervals(RideItem *_rideItem); - int getCP(); - void setCP(int cp); - int getCAD(); - void setCAD(int cadence); - double getCL(); - void setCL(double cranklen); - void recalc(); + int getCP(); + void setCP(int cp); + int getCAD(); + void setCAD(int cadence); + double getCL(); + void setCL(double cranklen); + void recalc(); - RideItem *rideItem; + // zone shader uses this + double maxAEPF; + double maxCPV; + QwtArray contour_xvalues; + + RideItem *rideItem; bool shadeZones() const { return shade_zones; } void setShadeZones(bool value); @@ -76,25 +81,22 @@ class PfPvPlot : public QwtPlot int intervalCount() const; MainWindow *mainWindow; - QwtPlotCurve *curve; - QList intervalCurves; - QwtPlotCurve *cpCurve; - QList zoneCurves; - QList zoneLabels; - QwtPlotMarker *mX; - QwtPlotMarker *mY; + QwtPlotCurve *curve; + QList intervalCurves; + QwtPlotCurve *cpCurve; + QList zoneCurves; + QList zoneLabels; + QwtPlotMarker *mX; + QwtPlotMarker *mY; - static QwtArray contour_xvalues; // values used in CP and contour plots: djconnel - - int cp_; - int cad_; - double cl_; - bool shade_zones; // whether to shade zones, added 27Apr2009 djconnel - bool merge_intervals, frame_intervals; + int cp_; + int cad_; + double cl_; + bool shade_zones; // whether to shade zones, added 27Apr2009 djconnel + bool merge_intervals, frame_intervals; double timeInQuadrant[4]; // time in seconds spent in each quadrant QwtPlotMarker *tiqMarker[4]; // time in seconds spent in each quadrant }; #endif // _GC_QaPlot_h -