//----------------------------------------------------------------------------- // $Id: Q3GUI/SlotCurve.cpp 2017/03/27 16:50:21 philw $ //----------------------------------------------------------------------------- // // class SlotCurve : public QwtPlotCurve // class SeriesSlotCurve : public SlotCurve // class ScalarSlotCurve : public SeriesSlotCurve // class PeriodicSlotCurve : public SeriesSlotCurve // class TableSlotCurve : public SlotCurve // class TableSlotContourCurve : public TableSlotCurve // class ParametricSlotCurve : public SlotCurve // // class SlotCurve::WeedFitter : public QwtWeedingCurveFitter // class SlotCurve::SplineFitter : public QwtSplineCurveFitter // //--- #include "SlotCurve.hpp" // this module #include "SlotPlot.hpp" // #include "PlotEditorDlg.hpp" #include "PlotDlgSettings.hpp" #include #include #include #include #include #include #include // End of Qt declarations #undef slots #include "Account.hpp" #include "Date_Time.hpp" #include "PeriodicSlot.hpp" #include "PlotInfo.hpp" #include "ScalarSlot.hpp" #include "SeriesSlot.hpp" #include "SimObj.hpp" #include "SlotGUIUtils.hpp" #include "TableSeriesSlot.hpp" #include "UnitMgr.hpp" #include "cwfmtstream.hpp" #include "cwsstream.hpp" #include "rwError.hpp" #include "rwStr.hpp" //---------------------------------------------------------------------------- //+public // // METHOD: // SlotCurve::SlotCurve() // // PURPOSE: // Constructor for the abstract base class SlotCurve. // // PARAMETERS: // (I) parent - The parent plot object // //---------------------------------------------------------------------------- SlotCurve::SlotCurve (SlotPlot* parent) : QwtPlotCurve(), _isCurveValid(true), _plot(parent), _initFinished (false), _rwCurveStyle (RwLines), _sourcePoints(), // QPolygonF _precomputedStepPoints(), // QPolygonF _legendOrd (-1), _deletedEmitted (false), _minXValid(false), _maxXValid(false), _minYValid(false), _maxYValid(false), _cachedMinX(0), _cachedMaxX(0), _cachedMinY(0), _cachedMaxY(0) { setYAxis(QwtPlot::yLeft); // Initialize _rwCurveStyle from QwtPlotCurve base class default. // Note that RwSpline is not natively supported by Qwt, as a curve // style as such. Spline curves are shown with the use of the // QwtSplineCurveFitter class (a QwtCurveFitter subclass). QwtPlotCurve::CurveStyle qwtCurveStyle = style(); switch (qwtCurveStyle) { case QwtPlotCurve::NoCurve: _rwCurveStyle = RwLines; break; case QwtPlotCurve::Lines: _rwCurveStyle = RwLines; break; case QwtPlotCurve::Sticks: _rwCurveStyle = RwLines; break; case QwtPlotCurve::Steps: _rwCurveStyle = RwSteps; break; case QwtPlotCurve::Dots: _rwCurveStyle = RwDots; break; case QwtPlotCurve::UserCurve: _rwCurveStyle = RwLines; break; } // Initial defaults for slot curves const CurveInfo& infoRef = PlotDlgSettings::instance()->getDefaultCurveSettings(); loadCurveInfo (&infoRef); // Set attributes used by QwtPlotCurve::legendIcon(). setLegendAttribute (QwtPlotCurve::LegendShowLine, true); setLegendAttribute (QwtPlotCurve::LegendShowSymbol, true); setLegendAttribute (QwtPlotCurve::LegendShowBrush, true); // Set item attributes setItemAttribute (QwtPlotItem::Margins, true); // Set render hint, anti-aliasing setRenderHint (QwtPlotItem::RenderAntialiased, true); // Ensure a minimum legend icon width of 32 pixels. Note that this must // be done after the calls to QwtPlotCurve::setLegendAttribute() because // that method has a side-effect of re-initializing the legend icon size. reapplyLegendLabelMinimumWidth(); // See additional comments in method. } SlotCurve::~SlotCurve() { // This is called from all SlotCurve subclass destructors, so that // the slotCurveDeleted signal is emitted from the most dervied class. emitDeleted(); } // called from SlotPlot after SlotCurve creation void SlotCurve::finishInit (bool force /*=false*/) { if (_initFinished && !force) return; //----->> if (_plot) { QwtLegendLabel* legendLab = _plot->legendLabelOfPlotItem (this); if (legendLab) { // *** Mark "Initialization Finished" *** _initFinished = true; legendLab->setContextMenuPolicy (Qt::CustomContextMenu); QObject::disconnect ( legendLab, SIGNAL (customContextMenuRequested (const QPoint&)), _plot, SLOT (curve_customContextMenuRequested (const QPoint&))); QObject::connect ( legendLab, SIGNAL (customContextMenuRequested (const QPoint&)), _plot, SLOT (curve_customContextMenuRequested (const QPoint&))); // Set Legend Label's Tooltip legendLab->setToolTip ("Click to hide"); } } } // virtual from QwtPlotItem void SlotCurve::updateLegend(const QwtPlotItem* itemParam, const QList& legendDataList) { static const char* mname ("SlotCurve::updateLegend"); static int callCnt (0); ++callCnt; const int legendDataCnt = legendDataList.count(); legendDataCnt; // (avoid compilation warning) itemParam; // (avoid compilation warning) // std::cout << mname << " [#" << callCnt << "]" // << " this " << std::hex << (long) this << std::dec // << ", param " << std::hex << (long) itemParam << std::dec // << std::endl; // Call base class QwtPlotCurve::updateLegend (this, legendData()); } // virtual from QwtPlotItem void SlotCurve::itemChanged() { static const char* mname ("SlotCurve::itemChanged"); static int callCnt (0); ++callCnt; // std::cout << mname << " [#" << callCnt << "]" // << " " << std::hex << (long) this << std::dec // << std::endl; // call base class method QwtPlotCurve::itemChanged(); } void SlotCurve::legendChanged() { static const char* mname ("SlotCurve::legendChanged"); static int callCnt (0); ++callCnt; // std::cout << mname << " [#" << callCnt << "]" // << " " << std::hex << (long) this << std::dec // << std::endl; // call base class method QwtPlotCurve::legendChanged(); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SlotCurve::minXValue(), maxXValue(), minYValue(), maxYValue() // // PURPOSE: // Overrides the QwtPlotCurve virtual methods to cache the min/max // values for the current curve data. // //---------------------------------------------------------------------------- double SlotCurve::minXValue() const { if (!_minXValid) { const QRectF bound = boundingRect(); // [QwtPlotSeriesItem] _cachedMinX = bound.left(); _minXValid = bound.isValid(); } return _cachedMinX; } double SlotCurve::maxXValue() const { if (!_maxXValid) { const QRectF bound = boundingRect(); // [QwtPlotSeriesItem] _cachedMaxX = bound.right(); _maxXValid = bound.isValid(); } return _cachedMaxX; } double SlotCurve::minYValue() const { if (!_minYValid) { const QRectF bound = boundingRect(); // [QwtPlotSeriesItem] _cachedMinY = bound.top(); _minYValid = bound.isValid(); } return _cachedMinY; } double SlotCurve::maxYValue() const { if (!_maxYValid) { const QRectF bound = boundingRect(); // [QwtPlotSeriesItem] _cachedMaxY = bound.bottom(); _maxYValid = bound.isValid(); } return _cachedMaxY; } //---------------------------------------------------------------------------- //+public - virtual from QwtPlotCurve // // METHOD: // SlotCurve::boundingRect() // // PURPOSE: // Override the QwtPlotCurve method to allow handling of invalid values. // Bounding rectangle coordinates will be calculated ignoring NaNs // //---------------------------------------------------------------------------- // virtual from QwtPlotCurve QRectF SlotCurve::boundingRect() const { int validCnt (0); QRectF rect = boundingRect_CountValid (validCnt); return rect; } QRectF SlotCurve::boundingRect_CountValid (int& validPointCntRet) const { validPointCntRet = 0; const int sz = (int) dataSize(); if ( sz <= 0 ) { return QRectF(1.0, 1.0, -2.0, -2.0); // invalid } double minX(INVALIDVALUE); double maxX(INVALIDVALUE); double minY(INVALIDVALUE); double maxY(INVALIDVALUE); bool valid(false); for ( int i = 0; i < sz; i++ ) { if (isValid(sample(i).x()) && isValid(sample(i).y())) { minX = maxX = sample(i).x(); minY = maxY = sample(i).y(); valid = true; break; } } if (!valid) { // Data is all invalid return QRectF(1.0, 1.0, -2.0, -2.0); // invalid } for ( int i = 0; i < sz; ++i ) { const double xv = sample(i).x(); const double yv = sample(i).y(); if (isValid(xv) && isValid(yv)) { ++validPointCntRet; if ( xv < minX ) minX = xv; if ( xv > maxX ) maxX = xv; if ( yv < minY ) minY = yv; if ( yv > maxY ) maxY = yv; } } return QRectF(minX, minY, maxX - minX, maxY - minY); } //---------------------------------------------------------------------------- //+protectedl // // METHOD: // SlotCurve::invalidateCachedValues() // // PURPOSE: // The internal data has changed, so invalidate all the cached values. // //---------------------------------------------------------------------------- void SlotCurve::invalidateCachedValues() { _minXValid = _maxXValid = _minYValid = _maxYValid = false; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- void SlotCurve::emitDeleted() { // Request parent slotPlot to emit a 'slotCurveDeleted' signal. // This is called from all SlotCurve subclass destructors, so that // the slotCurveDeleted signal is emitted from the most dervied class. if (!_deletedEmitted) { if (rwAssert (_plot != NULL)) { _plot->emitSlotCurveDeleted (this); } _deletedEmitted = true; } } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- void SlotCurve::precomputeStepPoints(bool applyToo /*=false*/) { static const char* mname ("SlotCurve::precomputeStepPoints"); static int callCnt (0); ++callCnt; //-------------------------------------------- // Input: QPolygonF _sourcePoints; // Computed: QPolygonF _precomputedStepPoints; //-------------------------------------------- const int sourceCnt = _sourcePoints.count(); std::cout << mname << " [#" << callCnt << "] " << sourceCnt << std::endl; if (sourceCnt < 1) { _precomputedStepPoints.clear(); } else { _precomputedStepPoints.resize ((sourceCnt * 2) - 1); _precomputedStepPoints [0] = _sourcePoints [0]; double priorX = _precomputedStepPoints [0].x(); int targCrs (1); for (int srcInx = 1; srcInx < sourceCnt; ++srcInx) { _precomputedStepPoints [targCrs++] = QPointF (priorX, _sourcePoints [srcInx].y()); _precomputedStepPoints [targCrs++] = _sourcePoints [srcInx]; priorX = _sourcePoints [srcInx].x(); } } if (applyToo) { setSamples (_precomputedStepPoints); } } //---------------------------------------------------------------------------- //+protected - virtual from QwtPlotCurve // // METHOD: // SlotCurve::drawCurve // // PURPOSE: // Override the QwtPlotCurve method to allow handling of invalid values. // Lines will be broken at invalid values, so this code finds and sends the // resulting segments off to be drawn separately. // //---------------------------------------------------------------------------- void SlotCurve::drawCurve(QPainter* painter, int style, const QwtScaleMap& xMap, const QwtScaleMap& yMap, const QRectF& canvasRect, int from, int to) const { int startInx(-1); int endInx(-1); bool valid(false); // Step through the data and break lines at invalid values for (int i = from; i <= to; i++) { valid = isValid(sample(i).x()) && isValid(sample(i).y()); if (valid) { // Data point is valid if (startInx == -1) { startInx = i; } else { endInx = i; } } if (!valid || i == to) { // Draw the segment, if appropriate if (startInx == -1) { // There is no valid segment to draw continue; } if (endInx == -1) { // Means there is a single point in the segment // send it off anyway because if data markers are shown // the point will need to be drawn endInx = startInx; } switch (style) { case Lines: drawLines(painter, xMap, yMap, canvasRect, startInx, endInx); break; case Sticks: drawSticks(painter, xMap, yMap, canvasRect, startInx, endInx); break; case Steps: drawSteps(painter, xMap, yMap, canvasRect, startInx, endInx); break; case Dots: drawDots(painter, xMap, yMap, canvasRect, startInx, endInx); break; case NoCurve: default: break; } startInx = -1; endInx = -1; } } } //---------------------------------------------------------------------------- //+protected - virtual from QwtPlotCurve // // METHOD: // SlotCurve::drawSymbols // // PURPOSE: // Override the QwtPlotCurve method to allow handling of invalid values. // Lines will be broken at invalid values and symbols will not be drawn there. // This code finds and sends the valid segments off to be drawn individually. // //---------------------------------------------------------------------------- void SlotCurve::drawSymbols(QPainter *painter, const QwtSymbol &symbol, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF& canvasRect, int from, int to) const { int startInx(-1); int endInx(-1); bool valid(false); // Step through the data and break data at invalid values for (int i = from; i <= to; i++) { valid = isValid(sample(i).x()) && isValid(sample(i).y()); if (valid) { // Data point is valid if (startInx == -1) { startInx = i; } else { endInx = i; } } if (!valid || i == to) { // Draw the valid segment, if appropriate if (startInx == -1) { // There is no valid segment to draw symbols for continue; } if (endInx == -1) { // Means there is a single point in the segment // send it off anyway because symbol at that // point will need to be drawn endInx = startInx; } // Call base class method to draw symbols for segment QwtPlotCurve::drawSymbols(painter, symbol, xMap, yMap, canvasRect, startInx, endInx); startInx = -1; endInx = -1; } } } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // virtual from QwtPlotCurve QwtGraphic SlotCurve::legendIcon (int index, // (ignore, only one) const QSizeF& iconSize) const { // call base class method //-- QwtGraphic graphic = QwtPlotCurve::legendIcon (index, iconSize); // Note [Phil, 3-2017, RW 7.1, Qwt 6.1.3, Qt 5.5.1, Gnats 5864]: // The following code is adapted from QwtPlotCurve::legendIcon(). // See the fix for Gnats 5864, below ("Legend and plot line look // different if thickness is greater than one"). // // A question about this fix approach was posted on the QtCentre/Qwt // forum: http://www.qtcentre.org/threads/68045 -- content also here: // http://cadswes2.colorado.edu/~philw/pub/5864/LegendProb.html Q_UNUSED( index ); if ( iconSize.isEmpty() ) return QwtGraphic(); QwtGraphic graphic; graphic.setDefaultSize( iconSize ); graphic.setRenderHint( QwtGraphic::RenderPensUnscaled, true ); QPainter painter( &graphic ); painter.setRenderHint( QPainter::Antialiasing, testRenderHint( QwtPlotItem::RenderAntialiased ) ); bool doShowLine = testLegendAttribute (QwtPlotCurve::LegendShowLine); bool doShowSymb = testLegendAttribute (QwtPlotCurve::LegendShowSymbol); bool doShowBrush = testLegendAttribute (QwtPlotCurve::LegendShowBrush); bool noAttribs = !(doShowLine || doShowSymb || doShowBrush); const QPen curvePen = pen(); const QwtSymbol* curveSymb = symbol(); if ( noAttribs || doShowBrush) { QBrush curveBrush = brush(); if ( curveBrush.style() == Qt::NoBrush && noAttribs ) { if ( style() != QwtPlotCurve::NoCurve ) { curveBrush = QBrush( curvePen.color() ); } else if ( curveSymb && (curveSymb->style() != QwtSymbol::NoSymbol) ) { curveBrush = QBrush( curveSymb->pen().color() ); } } if ( curveBrush.style() != Qt::NoBrush ) { QRectF r( 0, 0, iconSize.width(), iconSize.height() ); painter.fillRect( r, curveBrush ); } } if ( doShowLine ) { if ( curvePen != Qt::NoPen ) { // CADSWES FIX, Gnats 5864 ("Legend and plot line look different if // thickness is greater than one"). DON'T set the pen Cap Style. // //-- QPen pn = curvePen; //-- pn.setCapStyle( Qt::FlatCap ); //-- painter.setPen( pn ); painter.setPen( curvePen ); const double y = 0.5 * iconSize.height(); QwtPainter::drawLine( &painter, 0.0, y, iconSize.width(), y ); } } if ( doShowSymb ) { if ( curveSymb ) { QRectF r( 0, 0, iconSize.width(), iconSize.height() ); curveSymb->drawSymbol( &painter, r ); } } return graphic; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // virtual from QwtPlotCurve void SlotCurve::drawSteps (QPainter* painter, const QwtScaleMap& xMap, const QwtScaleMap& yMap, const QRectF& canvasRect, int from, int to) const { //--------------------------------------------------------------------- // Fixup to QwtPlotCurve bug which will be fixed after Qwt 6.1.3. // See QtCentre/Qwt Thread: http://www.qtcentre.org/threads/66464 // "QwtPlotCurve Step CurveStyle incorrect when extending beyond // QwtPlot edge". [July 2016]. // // If required, adjust the canvasRect to be larger, on all four sides, // by the thickness of the drawn curve (i.e. the QPainter QPen width). //--------------------------------------------------------------------- // Note: QWT_VERSION is (major << 16) + (minor << 8) + patch. const bool versNeedsCanvasRectExpand = (QWT_VERSION <= 0x060103); const bool clipOn = testPaintAttribute (QwtPlotCurve::ClipPolygons); QRectF adjustedCanvasRect (canvasRect); if (versNeedsCanvasRectExpand && clipOn) { qreal pw = qMax (qreal(1.0), painter->pen().widthF()); adjustedCanvasRect = canvasRect.adjusted (-pw, -pw, pw, pw); } // Call the Qwt base class method QwtPlotCurve::drawSteps (painter, xMap, yMap, adjustedCanvasRect, from, to); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // virtual from QwtPlotItem void SlotCurve::getCanvasMarginHint (const QwtScaleMap& xMap, const QwtScaleMap& yMap, const QRectF& canvasRect, double& lftMargin, double& topMargin, double& rgtMargin, double& botMargin) const { xMap; // (avoid compilation warning) yMap; // (avoid compilation warning) canvasRect; // (avoid compilation warning) lftMargin = 6; topMargin = 6; rgtMargin = 6; botMargin = 6; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SlotCurve::saveCurveInfo() const // // PURPOSE: // Save the current curve info the given CurveInfo struct. // // PARAMETERS: // (O) info - curve info struct to be saved to // //---------------------------------------------------------------------------- void SlotCurve::saveCurveInfo(CurveInfo *info) const { info->setLegendOrd (_legendOrd); info->_title = title().text().trimmed(); info->_curveStyle = styleToString(rwCurveStyle()); saveLineInfo(info->_lineInfo, pen()); const QwtSymbol* sym = symbol(); if (sym) saveSymbolInfo(info->_symbolInfo, *sym); info->_hidden = !(isVisible()); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SlotCurve::loadCurveInfo() // // PURPOSE: // Populate the current curve info from the given CurveInfo struct. // // PARAMETERS: // (I) info - curve info struct to be loaded // //---------------------------------------------------------------------------- void SlotCurve::loadCurveInfo(const CurveInfo *info) { PlotDlgSettings* settings = PlotDlgSettings::instance(); const QFont legendFont = settings->getLegendFont(); QwtText titleText (info->_title); titleText.setFont (legendFont); setTitle (titleText); _legendOrd = info->legendOrd(); setRwCurveStyle(stringToStyle(info->_curveStyle)); QPen pen; loadLineInfo(info->_lineInfo, pen); setPen(pen); const QwtSymbol* origSym = symbol(); QwtSymbol* symbol = origSym ? new QwtSymbol (origSym->style(), origSym->brush(), origSym->pen(), origSym->size()) : new QwtSymbol(); loadSymbolInfo(info->_symbolInfo, *symbol); setSymbol(symbol); reapplyLegendLabelMinimumWidth(); // must be called after setSymbol() } // Returns a RiverWare style identifier from the curves Qwt properties SlotCurve::RwCurveStyle SlotCurve::rwCurveStyle() const { // Note [Phil, RW 7.1, 4-12-2017]: With the new use of a QwtCurveFitter // to fix Gnats XXXX, we can no longer rely on the "Fitted" curve // attribute to distinguish a QwtPlotCurve having an RwSpline style from // an RwLines style. So, we've added a field to SlotCurve for this // style property. // //-- switch (style()) //-- { //-- case Lines: //-- { //-- if (testCurveAttribute(Fitted)) //-- return RwSpline; //-- else //-- return RwLines; //-- } //-- case Dots: return RwDots; //-- case Steps: return RwSteps; //-- } //-- //-- return RwNoCurve; return _rwCurveStyle; } // Sets the curves Qwt properties from a RiverWare curve style identifier void SlotCurve::setRwCurveStyle(RwCurveStyle rwStyle) { static const char* mname ("SlotCurve::setRwCurveStyle"); static int callCnt (0); ++callCnt; const bool wasSteps (_rwCurveStyle == RwSteps); _rwCurveStyle = rwStyle; std::cout << mname << " [#" << callCnt << "]" << " " << std::hex << (long) this << std::dec << " " << qPrintable (styleToString (rwStyle)) << std::endl; bool useSplineFitter (false); bool useWeedFitter (false); switch (rwStyle) { case RwLines: { setStyle(Lines); useWeedFitter = true; break; } case RwSpline: { setStyle(Lines); useSplineFitter = true; break; } case RwDots: { setStyle(Dots); break; } case RwSteps: { // Note [Phil, RW 7.1, 4-12-2017, Gnats XXXX]: We are no longer // using Qwt's built in "Steps" curve style because that doesn't // engage the QwtPlotCurve's QwtCurveFitter. //-- setStyle(Steps); setStyle(Lines); useWeedFitter = true; break; } default: { setStyle(NoCurve); } } // Insure that the QwtSlotCurve "Inverted" attribute is off. setCurveAttribute(Inverted, false); const bool isSteps (_rwCurveStyle == RwSteps); if (wasSteps != isSteps) { if (isSteps) precomputeStepPoints (true); // applyToo. else setSamples (_sourcePoints); } bool doCreateSplitFitter (false); bool doCreateWeedFitter (false); const QwtCurveFitter* oldFitter = curveFitter(); if (useSplineFitter) { doCreateSplitFitter = (dynamic_cast (oldFitter) == NULL); setCurveAttribute(Fitted, true); } else if (useWeedFitter) { doCreateWeedFitter = (dynamic_cast (oldFitter) == NULL); setCurveAttribute(Fitted, true); } else { setCurveFitter(NULL); setCurveAttribute(Fitted, false); } if (doCreateSplitFitter) { SplineFitter* splineFitter = new SplineFitter (this); // A spline size of three times the number of data points seems to // generate a reasonable curve close to the actual data points splineFitter->setSplineSize(3 * (int) dataSize()); // Gnats 4911 setCurveFitter(splineFitter); } else if (doCreateWeedFitter) { WeedFitter* weedFitter = new WeedFitter (this); setCurveFitter(weedFitter); } } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- void SlotCurve::reapplyLegendLabelMinimumWidth() { // Ensure a minimum legend icon width of 32 pixels. // Note [Phil, 10-2015, RW 6.8, Qwt 5.2.3 to 6.1.2 port]: The effect // of calling QwtPlotCurve::setLegendIconSize() is blown away by certain // other QwtPlotCurve setter methods. These call this internal function: // static void qwtUpdateLegendIconSize (QwtPlotCurve*). We need to keep // on doing this after calls to: // // (1) void QwtPlotCurve::setLegendAttribute (LegendAttribute, bool on); // (2) void QwtPlotCurve::setSymbol (QwtSymbol* symbol); const QSize origSize = legendIconSize(); if (origSize.width() < 32) setLegendIconSize (QSize (32, origSize.height())); } //---------------------------------------------------------------------------- //+public static // // METHOD: // SlotCurve::getFullSlotName() // // PURPOSE: // Return the name of a Slot, as a Qt character string. // // PARAMETERS: // (I) slotPtr // // RETURN VALUES: // QString // // DESIGN DETAILS: // After revsion 1.25 (1-13-2004), the custom code in this method // was replaced with a call to Slot::getCompleteName(). For // information about our naming convention and supporting methods, // see: http://cadswes.colorado.edu/internal/RiverWare/Code/naming.html // //---------------------------------------------------------------------------- QString SlotCurve::getFullSlotName(Slot *slotPtr) { if (!slotPtr) return QString(); const QString completeName (slotPtr->getCompleteName()); QString fullSlotName (completeName); // Account MultiSlot subslots have "__dot__" before // the linked supply name (which is always "supply"). // fullSlotName.replace ("__dot__", "."); return fullSlotName; } //---------------------------------------------------------------------------- //+public // // METHOD: // SlotCurve::isSameUnits() // // PURPOSE: // Determines whether the units of slotPtr are compatible with the units // of the current object. // // PARAMETERS: // (I) slotCurve // // RETURN VALUES: // bool // //---------------------------------------------------------------------------- bool SlotCurve::isSameUnits(SlotCurve *slotCurve) const { if (!rwAssert (slotCurve != NULL)) return (false); //----->> const ScaledUnitPtr xSuPtr1 = getXUnits(); const ScaledUnitPtr ySuPtr1 = getYUnits(); const ScaledUnitPtr xSuPtr2 = slotCurve->getXUnits(); const ScaledUnitPtr ySuPtr2 = slotCurve->getYUnits(); // const bool eq = (xSuPtr1 == xSuPtr2) && (ySuPtr1 == ySuPtr2); // (global function defined in Units/ScaledUnit.cpp) const bool eq = equivalent (xSuPtr1, xSuPtr2) && equivalent (ySuPtr1, ySuPtr2); return eq; } //---------------------------------------------------------------------------- //+public static // // METHOD: // SlotCurve::createSlotCurve() // // PURPOSE: // Factory method to create a SlotCurve object. This version of this // overloaded method, takes a single slotPtr and 2 column values for // specifying the columns of the slot to plot on the x and y axes. // // PARAMETERS: // (I) parent // (I) slotPtr - slot to create curve of // (I) xColumn - column to plot on X-axis // (I) yColumn - column to plot on Y-axis // (I) zColumn - column for the contour // (I) contourValue // // RETURN VALUES: // SlotCurve* // //---------------------------------------------------------------------------- SlotCurve* SlotCurve::createSlotCurve(SlotPlot* parent, Slot* slotPtr, int xColumn, int yColumn, int zColumn, double contourValue) { SlotCurve* slotCurve (NULL); if (slotPtr->isA(Slot::SeriesSlotBit) || slotPtr->isA(Slot::TableSeriesSlotBit)) { // Make sure we have a valid column for TableSeriesSlots. if (yColumn == -1) yColumn = 0; slotCurve = new SeriesSlotCurve (parent, slotPtr, yColumn); } else if (slotPtr->isA(Slot::ScalarSlotBit)) { ScalarSlot* scalarSlotPtr = dynamic_cast(slotPtr); rwAssert(scalarSlotPtr != NULL); slotCurve = new ScalarSlotCurve (parent, scalarSlotPtr); } else if (slotPtr->isA(Slot::PeriodicSlotBit)) { PeriodicSlot* periodicSlotPtr = dynamic_cast(slotPtr); rwAssert(periodicSlotPtr != NULL); if (yColumn == -1) yColumn = 0; slotCurve = new PeriodicSlotCurve (parent, periodicSlotPtr, yColumn); } else if (slotPtr->isA(Slot::TableSlotBit)) { TableSlot* tableSlotPtr = dynamic_cast(slotPtr); rwAssert(tableSlotPtr != NULL); if (zColumn == -1) { const bool col0MaybeIsVertDist = SlotGUIUtils::slotColIsProbablyVerticalDistance (tableSlotPtr, 0); const bool col1MaybeIsVertDist = SlotGUIUtils::slotColIsProbablyVerticalDistance (tableSlotPtr, 1); // Default is to plot column 0 on x-axis, and column 1 on y-axis const bool overrideAxisDefault = rwWorkspace->overrideDefaultPlotAxesForVertDist(); if (overrideAxisDefault && col0MaybeIsVertDist && !col1MaybeIsVertDist) { if (xColumn == -1) xColumn = 1; if (yColumn == -1) yColumn = 0; } else { if (xColumn == -1) xColumn = 0; // default if (yColumn == -1) yColumn = 1; // default } slotCurve = new TableSlotCurve(parent, tableSlotPtr, xColumn, yColumn); } else { slotCurve = new TableSlotContourCurve(parent, tableSlotPtr, xColumn, yColumn, zColumn, contourValue); } } return slotCurve; } //---------------------------------------------------------------------------- //+public static // // METHOD: // SlotCurve::createSlotCurve() // // PURPOSE: // Factory method to create a SlotCurve object. This version of this // overloaded method, takes 2 Slot objects which will be plotted against // each other. // // PARAMETERS: // (I) parent // (I) slotPtrX - slot to plot on X-axis // (I) slotPtrY - slot to plot on Y-axis // // RETURN VALUES: // SlotCurve* // //---------------------------------------------------------------------------- SlotCurve* SlotCurve::createSlotCurve(SlotPlot* parent, Slot* slotPtrX, Slot* slotPtrY) { SeriesSlot* seriesSlotPtrX = dynamic_cast(slotPtrX); SeriesSlot* seriesSlotPtrY = dynamic_cast(slotPtrY); rwAssert(seriesSlotPtrX && seriesSlotPtrY); SlotCurve* newCurve = new ParametricSlotCurve(parent, seriesSlotPtrX, seriesSlotPtrY); return newCurve; } //---------------------------------------------------------------------------- // public static // // METHOD: // static void saveLineInfo(LineInfo &info, const QPen &pen) // // PURPOSE: // Convenience function used to save a QPen to LineInfo structure. // //---------------------------------------------------------------------------- void SlotCurve::saveLineInfo(LineInfo &info, const QPen &pen) { info._style = lineStyleToString(pen.style()); info._width = pen.width(); info._colorRGB = pen.color().rgb(); } //---------------------------------------------------------------------------- // public static // // METHOD: // static void saveSymbolInfo(SymbolInfo &info, const QwtSymbol &sym) // // PURPOSE: // Convenience function used to save a QSymbol to SymbolInfo structure. // //---------------------------------------------------------------------------- void SlotCurve::saveSymbolInfo(SymbolInfo &info, const QwtSymbol &sym) { info._style = symbolStyleToString(sym.style()); info._size = sym.size().width(); info._colorRGB = sym.pen().color().rgb(); } //---------------------------------------------------------------------------- // public static // // METHOD: // static void loadLineInfo(LineInfo &info, QPen &pen) // // PURPOSE: // Convenience function used to load a LineInfo structure into a QPen. // //---------------------------------------------------------------------------- void SlotCurve::loadLineInfo(const LineInfo &info, QPen &pen) { pen.setStyle(stringToLineStyle(info._style)); pen.setWidth(info._width); QColor color; color.setRgb(info._colorRGB); pen.setColor(color); } //---------------------------------------------------------------------------- // public static // // METHOD: // static void loadSymbolInfo(SymbolInfo &info, QwtSymbol &symbol) // // PURPOSE: // Convenience function used to load a SymbolInfo structure into a QwtSymbol // //---------------------------------------------------------------------------- void SlotCurve::loadSymbolInfo(const SymbolInfo &info, QwtSymbol &symbol) { static const char* mname ("SlotCurve::loadSymbolInfo"); const QString origStyleStr = info._style; QwtSymbol::Style useStyle = stringToSymbolStyle (origStyleStr); if (!SymbolInfo::EllipseStyleEnabled) { // See Gnats 4837. There is a problem with the Ellipse symbol style // in Qwt versions 5.0.2 and 5.2.1 [September 2010; RiverWare 5.3]. if (useStyle == QwtSymbol::Ellipse) { // std::cout << mname << " Translating Ellipse to Diamond" // << std::endl; useStyle = QwtSymbol::Diamond; } } symbol.setStyle(useStyle); symbol.setSize(QSize(info._size, info._size)); QPen pen = symbol.pen(); QColor color = pen.color(); color.setRgb(info._colorRGB); pen.setColor(color); symbol.setPen(pen); } //---------------------------------------------------------------------------- // public static // // METHOD: // static QString lineStyleToString(Qt::PenStyle style) // // PURPOSE: // Convenience function used to convert a Qt::PenStyle to a string. // //---------------------------------------------------------------------------- QString SlotCurve::lineStyleToString(Qt::PenStyle style) { switch (style) { case Qt::NoPen: return "NoPen"; case Qt::SolidLine: return "Solid"; case Qt::DashLine: return "Dash"; case Qt::DotLine: return "Dot"; case Qt::DashDotLine: return "DashDot"; case Qt::DashDotDotLine: return "DashDotDot"; } rwAssert(false); return QString(); } //---------------------------------------------------------------------------- // public static // // METHOD: // static Qt::PenStyle stringToLineStyle(const QString &style) // // PURPOSE: // Convenience function used to convert a string to a Qt::PenStyle // //---------------------------------------------------------------------------- Qt::PenStyle SlotCurve::stringToLineStyle(const QString &style) { if (style == "NoPen") return Qt::NoPen; if (style == "Solid") return Qt::SolidLine; if (style == "Dash") return Qt::DashLine; if (style == "Dot") return Qt::DotLine; if (style == "DashDot") return Qt::DashDotLine; if (style == "DashDotDot") return Qt::DashDotDotLine; rwAssert(false); return Qt::NoPen; } //---------------------------------------------------------------------------- // public static // // METHOD: // static QString symbolStyleToString(QwtSymbol::Style style) // // PURPOSE: // Convenience function used to convert a Qwt::SymbolStyle to a string. // //---------------------------------------------------------------------------- QString SlotCurve::symbolStyleToString(QwtSymbol::Style style) { QString retStyleStr ("None"); switch (style) { case QwtSymbol::NoSymbol: retStyleStr = "None"; break; case QwtSymbol::Ellipse: retStyleStr = "Ellipse"; break; case QwtSymbol::Rect: retStyleStr = "Rect"; break; case QwtSymbol::Diamond: retStyleStr = "Diamond"; break; case QwtSymbol::Triangle: retStyleStr = "Triangle"; break; case QwtSymbol::Cross: retStyleStr = "Cross"; break; case QwtSymbol::XCross: retStyleStr = "XCross"; break; default: rwAssert (("SlotCurve::symbolStyleToString unsupported style", 0)); } return retStyleStr; } //---------------------------------------------------------------------------- // public static // // METHOD: // static QwtSymbol::Style stringToSymbolStyle(const QString &style) // // PURPOSE: // Convenience function used to convert a string to Qwt::SymbolStyle. // //---------------------------------------------------------------------------- QwtSymbol::Style SlotCurve::stringToSymbolStyle(const QString &style) { QwtSymbol::Style retStyle (QwtSymbol::NoSymbol); if (style == "None") retStyle = QwtSymbol::NoSymbol; else if (style == "Ellipse") retStyle = QwtSymbol::Ellipse; else if (style == "Rect") retStyle = QwtSymbol::Rect; else if (style == "Diamond") retStyle = QwtSymbol::Diamond; else if (style == "Triangle") retStyle = QwtSymbol::Triangle; else if (style == "Cross") retStyle = QwtSymbol::Cross; else if (style == "XCross") retStyle = QwtSymbol::XCross; else { rwAssert (("SlotCurve::stringToSymbolStyle unsupported style", 0)); } return retStyle; } //---------------------------------------------------------------------------- // public static // // METHOD: // static QString styleToString(RwCurveStyle style) // // PURPOSE: // Convenience function used to convert a RwCurveStyle to a string. // //---------------------------------------------------------------------------- QString SlotCurve::styleToString(RwCurveStyle style) { switch (style) { case RwNoCurve: return "NoCurve"; case RwLines: return "Lines"; case RwSpline: return "Spline"; case RwDots: return "Dots"; case RwSteps: return "Steps"; } rwAssert(false); return QString(); } //---------------------------------------------------------------------------- // public static // // METHOD: // static SlotCurve::RwCurveStyle stringToStyle(const QString &style) // // PURPOSE: // Convenience function used to convert a string to a Qwt::CurveStyle. // //---------------------------------------------------------------------------- SlotCurve::RwCurveStyle SlotCurve::stringToStyle(const QString &style) { if (style == "NoCurve") { return RwNoCurve; } if (style == "Lines") { return RwLines; } if (style == "Steps") { return RwSteps; } if (style == "Dots") { return RwDots; } if (style == "Spline") { return RwSpline; } rwAssert(false); return RwNoCurve; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // static double SlotCurve::julianDaysFromDoubleSecs(double secsDouble) { if (isValid (secsDouble) && (secsDouble >= 0.0)) { const seconds_t valSecs = seconds_t (secsDouble); const Date_Time valDate (valSecs); const double valJulDate = valDate.julian(); return valJulDate; } return INVALIDVALUE; } //---------------------------------------------------------------------------- //+protected static // // METHOD: // SlotCurve::getSlotUnitsString() // // PURPOSE: // Get the units for this curve. // // RETURN VALUES: // QString - axis label string // //---------------------------------------------------------------------------- QString SlotCurve::getSlotUnitsString(Slot *slotPtr, int colNum) { if (slotPtr == NULL) { // Means curve is a placeholder curve that slots can be added // to later. Return a units string of blank return QString(); } QString usrUnitStr (unitMgr->getUnitType (NOUNITS)); double slotScale (1.0); if (slotPtr->isA(Slot::TableSeriesSlotBit)) { TableSeriesSlot* tsslot = dynamic_cast(slotPtr); rwAssert(tsslot != NULL); if (colNum >= 0) { slotScale = tsslot->getUsrScale(colNum); usrUnitStr = tsslot->getUsrUnits(colNum); } } else if (slotPtr->isA(Slot::SeriesSlotBit)) { SeriesSlot* sslot = dynamic_cast(slotPtr); if (rwAssert(sslot != NULL)) { const SeriesSlot (*unitRefSlot) (sslot->getUnitRefSeriesSlot()); if (rwAssert(unitRefSlot != NULL)) { slotScale = unitRefSlot->getUsrScale(); usrUnitStr = unitRefSlot->getUsrUnits(); } } } else if (slotPtr->isA(Slot::ScalarSlotBit)) { ScalarSlot* scSlot = dynamic_cast(slotPtr); if (rwAssert(scSlot != NULL)) { slotScale = scSlot->getUsrScale(); usrUnitStr = scSlot->getUsrUnits(); } } else if (slotPtr->isA(Slot::TableSlotBit)) { TableSlot* tslot = dynamic_cast(slotPtr); rwAssert(tslot != NULL); if (colNum >= 0) { slotScale = tslot->getUsrScale(colNum); usrUnitStr = tslot->getUsrUnits(colNum); } } else { rwAssert(("Unsupported Slot Type", false)); } // Display the scale value on the unitsString if it is not // the default value of 1.0 if (slotScale == 1.0) { return (usrUnitStr); //---------------->> } QString scaleQStr; if (slotScale > 1.0) scaleQStr = QString::number (slotScale) + ' '; else // (slotScale < 1.0) scaleQStr = QString::number (slotScale, 'e', 2) + ' '; // Add the units to the string QString unitString (scaleQStr); unitString += usrUnitStr; return unitString; } //---------------------------------------------------------------------------- //+protected statis // // METHOD: // SlotCurve::getSlotUnitType() // // PURPOSE: // Get the unit type for the slot on this curve. // // RETURN VALUES: // unit_type // //---------------------------------------------------------------------------- unit_type SlotCurve::getSlotUnitType(Slot *slotPtr, int colNum) { if (slotPtr->isA(Slot::SeriesSlotBit)) { SeriesSlot* sslot = dynamic_cast(slotPtr); if (sslot) { return sslot->getUnitType(); } } else if (slotPtr->isA(Slot::TableSlotBit)) { TableSlot* tslot = dynamic_cast(slotPtr); if (tslot) { if (colNum >= 0) { return tslot->getUnitType (colNum); } } } else if (slotPtr->isA(Slot::ScalarSlotBit)) { ScalarSlot* scSlot = dynamic_cast(slotPtr); if (scSlot) { return scSlot->getUnitType(); } } return NOUNITS; } //---------------------------------------------------------------------------- //+public // // METHOD: // SeriesSlotCurve::SeriesSlotCurve(parent, slotPtr); // // PURPOSE: // Constructor. // // PARAMETERS: // (I) parent - The parent plot object // (I) slotPtr - The slot to plot // //---------------------------------------------------------------------------- SeriesSlotCurve::SeriesSlotCurve(SlotPlot* parent, Slot* slotPtr, int colNum) : SlotCurve(parent), _slotPtr(slotPtr), _colNum(colNum), _cbId(NULL) { if (!validate()) { return; } // Note: It's now too early to update the underlying QwtPlotCurve's data. // Display units now come from the axes to which this curve has yet to // be placed. //-- updateCurveData(); // // If this is flow data or some other type of average data, // then display this as a step curve. Average data should not be // displayed as lines between dots, because it really represents data // over a period of time. // const unit_type utyp = getSlotUnitType(_slotPtr, _colNum); if (utyp == FLOW) setRwCurveStyle(RwSteps); PlotDlgSettings* settings = PlotDlgSettings::instance(); const QFont legendFont = settings->getLegendFont(); QwtText titleText (getFullTitle()); titleText.setFont (legendFont); setTitle (titleText); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SeriesSlotCurve::~SeriesSlotCurve() // // PURPOSE: // Destructor. // //---------------------------------------------------------------------------- SeriesSlotCurve::~SeriesSlotCurve() { // This is called from all SlotCurve subclass destructors, so that // the slotCurveDeleted signal is emitted from the most dervied class. emitDeleted(); if (_slotPtr && _cbId) { _slotPtr->deleteCallback(_cbId); _cbId = NULL; } } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SeriesSlotCurve::saveCurveInfo(info) const // // PURPOSE: // Save the current curve object into the CurveInfo struct. // // PARAMETERS: // (O) info - CurveInfo struct to save to // //---------------------------------------------------------------------------- void SeriesSlotCurve::saveCurveInfo(CurveInfo *info) const { SlotCurve::saveCurveInfo(info); info->_type = CurveInfo::SERIES; info->_ySlot = _slotPtr; info->_yColumn = _colNum; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SeriesSlotCurve::updateCurveData() // // PURPOSE: // Updates the curve with the current data from the Slot in the Riverware // model. // // PARAMETERS: // None // //---------------------------------------------------------------------------- void SeriesSlotCurve::updateCurveData() { // TableSeriesSlot derives from TableSlot, not SeriesSlot. So only // one of these cast slot pointers will be non-NULL. SeriesSlot* sslot = dynamic_cast(_slotPtr); TableSeriesSlot* tsslot = dynamic_cast(_slotPtr); const bool intIndexed = sslot && sslot->usersIndexByInt(); // Get Y-axis units from the SlotPlot's Y-axis assigned to this SlotCurve const int curveYAxis = yAxis(); ScaledUnitPtr yAxisSuPtr = _plot ? _plot->axisUnits (curveYAxis) : NULL; if ((yAxisSuPtr == NULL) || ((sslot == NULL) && (tsslot == NULL))) { _sourcePoints.clear(); _precomputedStepPoints.clear(); setSamples (_sourcePoints); invalidateCachedValues(); return; //---->> } const unit_type yUnitType = yAxisSuPtr->getType(); const bool yIsTime (yUnitType == DATETIME); const double yScale = yAxisSuPtr->getScale(); const QString yUnitStr = yAxisSuPtr->getUsrUnit(); // Get the number of values in this series int numValues(0); if (sslot) numValues = sslot->numValues(); else if (tsslot) numValues = tsslot->numTimeSteps(); static const QPointF NanPoint (INVALIDVALUE, INVALIDVALUE); QVector pointVector (numValues, NanPoint); // ************************* // *** (1) Series Slot *** // ************************* if (sslot) { // Get all the values on the series double* valueList = sslot->getValues(numValues, yScale, yUnitStr); Date_Time currentDate; for (int i = 0; i < numValues; ++i) { double xVal (INVALIDVALUE); double yVal (INVALIDVALUE); // Note: Include NaN value points. Lines are drawn with breaks // at NaNs values. [Gnats 4904]. if (intIndexed) { xVal = i+1; } else { sslot->getDate(i, ¤tDate); const double currJulDate = currentDate.julian(); xVal = currJulDate; } const double val = valueList[i]; yVal = (yIsTime ? julianDaysFromDoubleSecs (val) : val); pointVector [i] = QPointF (xVal, yVal); } delete [] valueList; } // ******************************* // *** (2) Table Series Slot *** // ******************************* else if (tsslot) { Date_Time currentDate; for (int i = 0; i < numValues; ++i) { // Note: Include NaN value points. Lines are drawn with breaks // at NaNs values. [Gnats 4904]. tsslot->getDate(i, ¤tDate); double currJulDate = currentDate.julian(); double val (INVALIDVALUE); tsslot->getValueByIndex(i, _colNum, val, yScale, yUnitStr); double yVal = (yIsTime ? julianDaysFromDoubleSecs (val) : val); pointVector [i] = QPointF (currJulDate, yVal); } } // ***************************************************** // *** Set Curve Data from Computed QPointF Vector *** // ***************************************************** // Set the data for the curve _sourcePoints = pointVector; if (_rwCurveStyle == RwSteps) precomputeStepPoints (true); // applyToo else setSamples (_sourcePoints); // If the curve type is spline, adjust the spline size of the curve // fitter taking into account the number of data points. // A spline size of three times the number of data points seems to // generate a reasonable curve close to the actual data points. if (rwCurveStyle() == RwSpline) { QwtSplineCurveFitter* fitter = dynamic_cast (curveFitter()); if (fitter) { fitter->setSplineSize(numValues * 3); } } invalidateCachedValues(); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SeriesSlotCurve::getFullTitle() const // // PURPOSE: // Get the official title for this curve. // // PARAMETERS: // None // // RETURN VALUES: // QString - curve title // //---------------------------------------------------------------------------- QString SeriesSlotCurve::getFullTitle() const { QString title = getFullSlotName(_slotPtr); // // For TableSeriesSlots and PeriodicSlots add the column label // TableSlot* tslot = dynamic_cast(_slotPtr); if (tslot && (_colNum >= 0)) { title += " - "; title += tslot->getColLabelOrValueString(_colNum); } return title; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SeriesSlotCurve::setupAxis() // // PURPOSE: // Setup the label and scale for an axis, based on this curve. // // PARAMETERS: // (I) axisId - the axis to initialize // //---------------------------------------------------------------------------- void SeriesSlotCurve::setupAxis(int axisId) { switch (axisId) { case QwtPlot::yLeft: case QwtPlot::yRight: { // For a single slot plot, the y-axis label is the units of the // slot. _plot->setAxisTitle(axisId, getSlotUnitsString(_slotPtr, std::max (0, _colNum))); _plot->enableAxis(axisId); setYAxis(axisId); break; } case QwtPlot::xBottom: case QwtPlot::xTop: { // X-Axis - on a SeriesSlot we know the x-axis is the date, so // don't clutter things up with a label. _plot->setAxisTitle(axisId, QString()); _plot->setAxisLabelFormat( axisId, 'D', TimeScaleDraw::FORMAT_MMDDYY, false); _plot->enableAxis(axisId); setXAxis(axisId); break; } } } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SeriesSlotCurve::getXUnits() const // // PURPOSE: // Get the scaled unit pointer for the X-axis of this curve // // RETURN VALUES: // QString - units string // //---------------------------------------------------------------------------- ScaledUnitPtr SeriesSlotCurve::getXUnits() const { // If this is an integer indexed SeriesSlot, the x axis has no units. SeriesSlot* seriesSlot = dynamic_cast(_slotPtr); if (seriesSlot && seriesSlot->usersIndexByInt()) { ScaledUnitPtr noUnitsSuPtr = UnitMgr::instance()->getScaledUnit(NOUNITS); return noUnitsSuPtr; } ScaledUnitPtr dtSuPtr = UnitMgr::instance()->getScaledUnit(DATETIME); return dtSuPtr; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SeriesSlotCurve::getYUnits() const // // PURPOSE: // Get the scaled unit pointer for the Y-axis of this curve // // RETURN VALUES: // QString - units string // //---------------------------------------------------------------------------- ScaledUnitPtr SeriesSlotCurve::getYUnits() const { if (_slotPtr == NULL) return NULL; //----->> const NumDisplayAttribs& dispAttbs = _slotPtr->activeNumDisplayAttribs_prim (std::max (0, _colNum)); const ScaledUnitPtr suPtr = dispAttbs.scaledUnit(); return suPtr; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SeriesSlotCurve::getXAxisDescription() const // // PURPOSE: // Get the label for the X-axis of this curve // // RETURN VALUES: // QString - axis label string // //---------------------------------------------------------------------------- QString SeriesSlotCurve::getXAxisDescription() const { // If this is an integer indexed SeriesSlot, the x axis has no units. SeriesSlot* seriesSlot = dynamic_cast(_slotPtr); if (seriesSlot && seriesSlot->usersIndexByInt()) { return UnitMgr::instance()->getUnitType(NOUNITS); } static const QString TimeUnitTypeName = UnitMgr::instance()->getUnitType(DATETIME); return TimeUnitTypeName; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SeriesSlotCurve::getYAxisDescription() const // // PURPOSE: // Get the label for the Y-axis of this curve // // RETURN VALUES: // QString - axis label string // //---------------------------------------------------------------------------- QString SeriesSlotCurve::getYAxisDescription() const { // The Y-Axis is the slot, so use its full title. return getFullTitle(); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- unit_type SeriesSlotCurve::slotUnitType() const { const unit_type utyp = getSlotUnitType (_slotPtr, _colNum); return utyp; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SeriesSlotCurve::isSameCurve() const // // PURPOSE: // Tests whether the given curve represents the same slots as the current // curve, independent of the labels, style, etc. // // PARAMETERS: // (I) slotCurve // // RETURN VALUES: // bool // //---------------------------------------------------------------------------- bool SeriesSlotCurve::isSameCurve(SlotCurve *slotCurve) const { static const char* mname ("SeriesSlotCurve::isSameCurve"); static int callCnt (0); ++callCnt; SeriesSlotCurve* sscurve = dynamic_cast(slotCurve); if (sscurve == NULL) return (false); //-------------------------------->> const bool isFirstCol1 = (sscurve->_colNum < 1); // negative or 0 const bool isFirstCol2 = (_colNum < 1); // negative or 0 const bool bothFirstCol = (isFirstCol1 && isFirstCol2); const bool isSame = (sscurve->_slotPtr == _slotPtr) && (bothFirstCol || (sscurve->_colNum == _colNum)); // std::cout << mname << " [#" << callCnt << "]" // << " " << std::hex << (long) sscurve->_slotPtr << std::dec // << " [" << sscurve->_colNum << "]," // << " " << std::hex << (long) _slotPtr << std::dec // << " [" << _colNum << "]" // << " " << (isSame ? "SAME" : "DIFF") // << std::endl; return (isSame); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SeriesSlotCurve::hasSlot() const // // PURPOSE: // Determine whether this curve is plotting the given slotPtr. // // PARAMETERS: // (I) slotPtr // // RETURN VALUES: // bool // //---------------------------------------------------------------------------- bool SeriesSlotCurve::hasSlot(Slot *slotPtr) const { return (_slotPtr == slotPtr); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // public virtual QList SeriesSlotCurve::getSlots() const { QList retList; if (_slotPtr) retList.append (_slotPtr); return (retList); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // public virtual bool SeriesSlotCurve::slotsInSet (const QSet& slotSet) const { const bool cont = _slotPtr && slotSet.contains (_slotPtr); return (cont); } // public virtual SlotColRef SeriesSlotCurve::xSlotCol() const { return SlotColRef ((Slot*) NULL, 0); } // public virtual SlotColRef SeriesSlotCurve::ySlotCol() const { return SlotColRef (_slotPtr, _colNum); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SeriesSlotCurve::validate() // // PURPOSE: // Determine whether this curve can be plotted. // // PARAMETERS: // None // // RETURN VALUES: // bool // //---------------------------------------------------------------------------- bool SeriesSlotCurve::validate() { if (_slotPtr == NULL) { _isCurveValid = false; return false; } _isCurveValid = true; return true; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // SeriesSlotCurve::registerCallbacks() // // PURPOSE: // Register this curve to receive callbacks from its slot. // //---------------------------------------------------------------------------- void SeriesSlotCurve::registerCallbacks() { if (_slotPtr && (_cbId == NULL)) { _cbId = _slotPtr->addCallback(SlotPlot__callbackNoteProc, _plot); // See SlotPlot::callbackHandler(). } } //---------------------------------------------------------------------------- //+public // // METHOD: // SeriesSlotCurve::changeSlotInfo() // // PURPOSE: // Change the slot and column info for the sloCurve // //---------------------------------------------------------------------------- bool SeriesSlotCurve::changeSlotInfo(Slot* slotPtr, int colNum) { if (slotPtr != _slotPtr || colNum != _colNum) { // Try to reset the slot info and see if the plot can support it Slot* origSlotPtr = _slotPtr; int origColNum = _colNum; _slotPtr = slotPtr; _colNum = colNum; if (_plot->changeSlotCurve(this)) { // Change was successfully supported in the containing plot updateCurveData(); // Adjust callbacks for the new slot configuration if (origSlotPtr && _cbId) { origSlotPtr->deleteCallback(_cbId); _cbId = 0; } registerCallbacks(); // If this is flow data or some other type of average data, // then display this as a step curve. Average data should not be // displayed as lines between dots, because it really represents data // over a period of time. // if (getSlotUnitType(_slotPtr, _colNum) == FLOW) { setRwCurveStyle(RwSteps); } else { setRwCurveStyle(RwLines); } } else { // Change was not successfully supported // Set the slot info back to original _slotPtr = origSlotPtr; _colNum = origColNum; return false; } } return true; } //---------------------------------------------------------------------------- //+public // // METHOD: // TableSlotCurve::TableSlotCurve() // // PURPOSE: // Constructor. // // PARAMETERS: // (I) parent - The parent plot object // (I) slotPtr - The slot to plot // (I) xColumnNum - column for x axis // (I) yColumnNum - column for y axis // //---------------------------------------------------------------------------- TableSlotCurve::TableSlotCurve(SlotPlot* parent, TableSlot* slotPtr, int xColumnNum, int yColumnNum) : SlotCurve(parent), _slotPtr(slotPtr), _xColumnNum(xColumnNum), _yColumnNum(yColumnNum), _cbId(0) { if (!validate()) { return; } // Note: It's now too early to update the underlying QwtPlotCurve's data. // Display units now come from the axes to which this curve has yet to // be placed. //-- updateCurveData(); PlotDlgSettings* settings = PlotDlgSettings::instance(); const QFont legendFont = settings->getLegendFont(); QwtText titleText (getFullTitle()); titleText.setFont (legendFont); setTitle (titleText); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotCurve::~TableSlotCurve() // // PURPOSE: // Destructor. // //---------------------------------------------------------------------------- TableSlotCurve::~TableSlotCurve() { // This is called from all SlotCurve subclass destructors, so that // the slotCurveDeleted signal is emitted from the most dervied class. emitDeleted(); if (_slotPtr && _cbId) { _slotPtr->deleteCallback(_cbId); } } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotCurve::saveCurveInfo(info) const // // PURPOSE: // Save the current curve object into the CurveInfo struct. // // PARAMETERS: // (O) info - CurveInfo struct to save to // //---------------------------------------------------------------------------- void TableSlotCurve::saveCurveInfo(CurveInfo *info) const { SlotCurve::saveCurveInfo(info); info->_type = CurveInfo::TABLE; info->_xSlot = _slotPtr; info->_ySlot = _slotPtr; info->_xColumn = _xColumnNum; info->_yColumn = _yColumnNum; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotCurve::updateCurveData() // // PURPOSE: // Updates the curve with the current data from the Slot in the Riverware // model. // // PARAMETERS: // None // //---------------------------------------------------------------------------- void TableSlotCurve::updateCurveData() { // Get X-axis units from the SlotPlot's X-axis assigned to this SlotCurve // Get Y-axis units from the SlotPlot's Y-axis assigned to this SlotCurve const int curveXAxis = xAxis(); const int curveYAxis = yAxis(); ScaledUnitPtr xAxisSuPtr = _plot ? _plot->axisUnits (curveXAxis) : NULL; ScaledUnitPtr yAxisSuPtr = _plot ? _plot->axisUnits (curveYAxis) : NULL; if ((xAxisSuPtr == NULL) || (yAxisSuPtr == NULL) || (_slotPtr == NULL)) { _sourcePoints.clear(); _precomputedStepPoints.clear(); setSamples (_sourcePoints); invalidateCachedValues(); return; //---->> } const unit_type xUnitType = xAxisSuPtr->getType(); const unit_type yUnitType = yAxisSuPtr->getType(); const bool xIsTime (xUnitType == DATETIME); const bool yIsTime (yUnitType == DATETIME); const double xScale = xAxisSuPtr->getScale(); const double yScale = yAxisSuPtr->getScale(); const QString xUnitStr = xAxisSuPtr->getUsrUnit(); const QString yUnitStr = yAxisSuPtr->getUsrUnit(); int numRows (0); int numCols (0); cwArray2 *valueList; valueList = _slotPtr->getValues(numRows, numCols); static const QPointF NanPoint (INVALIDVALUE, INVALIDVALUE); QVector pointVector (numRows, NanPoint); for (int i = 0; i < numRows; i++) { const double valX = _slotPtr->getValue(i, _xColumnNum, xScale, xUnitStr); const double valY = _slotPtr->getValue(i, _yColumnNum, yScale, yUnitStr); // Note: Include NaN value points. Lines are drawn with breaks // at NaNs values. [Gnats 4904]. const double x = xIsTime ? julianDaysFromDoubleSecs (valX) : valX; const double y = yIsTime ? julianDaysFromDoubleSecs (valY) : valY; pointVector [i] = QPointF (x, y); } // Set the data for the curve _sourcePoints = pointVector; if (_rwCurveStyle == RwSteps) precomputeStepPoints (true); // applyToo else setSamples (_sourcePoints); // If the curve type is spline, adjust the spline size of the curve // fitter taking into account the number of data points. // A spline size of three times the number of data points seems to // generate a reasonable curve close to the actual data points. if (rwCurveStyle() == RwSpline) { QwtSplineCurveFitter* fitter = dynamic_cast (curveFitter()); if (fitter) { fitter->setSplineSize(numRows * 3); } } delete valueList; invalidateCachedValues(); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotCurve::getFullTitle() const // // PURPOSE: // Get the official title for this curve. // // PARAMETERS: // None // // RETURN VALUES: // QString - curve title // //---------------------------------------------------------------------------- QString TableSlotCurve::getFullTitle() const { QString title; title = getFullSlotName(_slotPtr); if (_slotPtr) { title += " ("; title += _slotPtr->getColLabelOrValueString(_xColumnNum); title += " x "; title += _slotPtr->getColLabelOrValueString(_yColumnNum); title += ")"; } return title; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotCurve::setupAxis() // // PURPOSE: // Setup the label and scale for an axis, based on this curve. // // PARAMETERS: // (I) axisId - the axis to initialize // //---------------------------------------------------------------------------- void TableSlotCurve::setupAxis(int axisId) { switch (axisId) { case QwtPlot::yLeft: case QwtPlot::yRight: { _plot->setAxisTitle(axisId, getSlotUnitsString(_slotPtr, _yColumnNum)); setYAxis(axisId); break; } case QwtPlot::xBottom: case QwtPlot::xTop: { _plot->setAxisTitle(axisId, getSlotUnitsString(_slotPtr, _xColumnNum)); setXAxis(axisId); break; } } } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotCurve::getXUnits() const // // PURPOSE: // Get the scaled unit pointer for the X-axis of this curve // // RETURN VALUES: // QString - units string // //---------------------------------------------------------------------------- ScaledUnitPtr TableSlotCurve::getXUnits() const { if (_slotPtr == NULL) return NULL; //----->> const NumDisplayAttribs& dispAttbs = _slotPtr->activeNumDisplayAttribs_prim (std::max (0, _xColumnNum)); const ScaledUnitPtr suPtr = dispAttbs.scaledUnit(); return suPtr; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotCurve::getYUnits() const // // PURPOSE: // Get the scaled unit pointer for the Y-axis of this curve // // RETURN VALUES: // QString - units string // //---------------------------------------------------------------------------- ScaledUnitPtr TableSlotCurve::getYUnits() const { if (_slotPtr == NULL) return NULL; //----->> const NumDisplayAttribs& dispAttbs = _slotPtr->activeNumDisplayAttribs_prim (std::max (0, _yColumnNum)); const ScaledUnitPtr suPtr = dispAttbs.scaledUnit(); return suPtr; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotCurve::getXAxisDescription() const // // PURPOSE: // Get the label for the X-axis of this curve // // RETURN VALUES: // QString - axis label string // //---------------------------------------------------------------------------- QString TableSlotCurve::getXAxisDescription() const { QString descr; descr = getFullSlotName(_slotPtr); if (_slotPtr) { descr += " - "; descr += _slotPtr->getColLabelOrValueString(_xColumnNum); } return descr; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotCurve::getYAxisDescription() const // // PURPOSE: // Get the label for the Y-axis of this curve // // RETURN VALUES: // QString - axis label string // //---------------------------------------------------------------------------- QString TableSlotCurve::getYAxisDescription() const { QString descr; descr = getFullSlotName(_slotPtr); if (_slotPtr) { descr += " - "; descr += _slotPtr->getColLabelOrValueString(_yColumnNum); } return descr; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotCurve::isSameCurve() const // // PURPOSE: // Tests whether the given curve represents the same slots as the current // curve, independent of the labels, style, etc. // // PARAMETERS: // (I) slotCurve // // RETURN VALUES: // bool // //---------------------------------------------------------------------------- bool TableSlotCurve::isSameCurve(SlotCurve *slotCurve) const { TableSlotCurve* tscurve = dynamic_cast(slotCurve); return (tscurve && (tscurve->_slotPtr == _slotPtr) && (tscurve->_xColumnNum == _xColumnNum) && (tscurve->_yColumnNum == _yColumnNum)); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotCurve::hasSlot() const // // PURPOSE: // Determine whether this curve is plotting the given slotPtr. // // PARAMETERS: // (I) slotPtr // // RETURN VALUES: // bool // //---------------------------------------------------------------------------- bool TableSlotCurve::hasSlot(Slot *slotPtr) const { return (_slotPtr == slotPtr); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // public virtual QList TableSlotCurve::getSlots() const { QList retList; if (_slotPtr) retList.append (_slotPtr); return (retList); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // public virtual bool TableSlotCurve::slotsInSet (const QSet& slotSet) const { const bool cont = _slotPtr && slotSet.contains (_slotPtr); return (cont); } // public virtual SlotColRef TableSlotCurve::xSlotCol() const { return SlotColRef (_slotPtr, _xColumnNum); } // public virtual SlotColRef TableSlotCurve::ySlotCol() const { return SlotColRef (_slotPtr, _yColumnNum); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotCurve::validate() // // PURPOSE: // Determine whether this curve can be plotted. // // PARAMETERS: // None // // RETURN VALUES: // bool // //---------------------------------------------------------------------------- bool TableSlotCurve::validate() { if (_slotPtr == NULL) { _isCurveValid = false; return false; } if ((_slotPtr->getNumColumns() <= _xColumnNum) || (_slotPtr->getNumColumns() <= _yColumnNum)) { QString warningString = "Unable to plot "; warningString += getFullSlotName(_slotPtr) + ".\n"; warningString += QString("Invalid columns selected for plotting."); QMessageBox::warning(_plot, QString("Plot Error"), warningString); _isCurveValid = false; return false; } _isCurveValid = true; return true; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotCurve::registerCallbacks() // // PURPOSE: // Register this curve to receive callbacks from its slot. // //---------------------------------------------------------------------------- void TableSlotCurve::registerCallbacks() { if (_slotPtr) { _cbId = _slotPtr->addCallback(SlotPlot__callbackNoteProc, _plot); // See SlotPlot::callbackHandler(). } } //---------------------------------------------------------------------------- //+public // // METHOD: // TableSlotCurve::changeSlotInfo() // // PURPOSE: // Change the slot and column info for the slotCurve // //---------------------------------------------------------------------------- bool TableSlotCurve::changeSlotInfo(TableSlot* slotPtr, int xColumnNum, int yColumnNum) { if (slotPtr != _slotPtr || xColumnNum != _xColumnNum || yColumnNum != _yColumnNum) { // Try to reset the slot info and see if the plot can support it TableSlot* origSlotPtr = _slotPtr; int origXColNum = _xColumnNum; int origYColNum = _yColumnNum; _slotPtr = slotPtr; _xColumnNum = xColumnNum; _yColumnNum = yColumnNum; if (_plot->changeSlotCurve(this)) { // Change was successfully supported in the containing plot updateCurveData(); // Adjust callbacks for the new slot configuration if (origSlotPtr && _cbId) { origSlotPtr->deleteCallback(_cbId); _cbId = 0; } registerCallbacks(); } else { // Change was not successfully supported // Set the slot info back to original _slotPtr = origSlotPtr; _xColumnNum = origXColNum; _yColumnNum = origYColNum; return false; } } return true; } //---------------------------------------------------------------------------- //+public // // METHOD: // TableSlotContourCurve::TableSlotContourCurve() // // PURPOSE: // Constructor. // // PARAMETERS: // (I) parent - The parent plot object // (I) slotPtr - The slot to plot // (I) xColumnNum - column for x axis // (I) yColumnNum - column for y axis // (I) zColumnNum - column for z axis // (I) contourValue - float value for this contour // //---------------------------------------------------------------------------- TableSlotContourCurve::TableSlotContourCurve( SlotPlot* parent, TableSlot* slotPtr, int xColumnNum, int yColumnNum, int zColumnNum, double contourValue) : TableSlotCurve(parent, slotPtr, xColumnNum, yColumnNum), _zColumnNum(zColumnNum), _contourValue(contourValue) { if (!validate()) { return; } // Note: It's now too early to update the underlying QwtPlotCurve's data. // Display units now come from the axes to which this curve has yet to // be placed. //-- updateCurveData(); // Always set the default curve style to lines for contour curves. setRwCurveStyle(RwLines); PlotDlgSettings* settings = PlotDlgSettings::instance(); const QFont legendFont = settings->getLegendFont(); QwtText titleText (getFullTitle()); titleText.setFont (legendFont); setTitle (titleText); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotContourCurve::saveCurveInfo(info) const // // PURPOSE: // Save the current curve object into the CurveInfo struct. // // PARAMETERS: // (O) info - CurveInfo struct to save to // //---------------------------------------------------------------------------- void TableSlotContourCurve::saveCurveInfo(CurveInfo *info) const { TableSlotCurve::saveCurveInfo(info); info->_type = CurveInfo::TABLECONTOUR; info->_xSlot = _slotPtr; info->_ySlot = _slotPtr; info->_xColumn = _xColumnNum; info->_yColumn = _yColumnNum; info->_zColumn = _zColumnNum; info->_contourValue = _contourValue; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotContourCurve::updateCurveData() // // PURPOSE: // Updates the curve with the current data from the Slot in the Riverware // model. // // PARAMETERS: // None // //---------------------------------------------------------------------------- void TableSlotContourCurve::updateCurveData() { if (_slotPtr == NULL) { _sourcePoints.clear(); _precomputedStepPoints.clear(); setSamples (_sourcePoints); invalidateCachedValues(); return; //---->> } int numRows (0); int numCols (0); cwArray2 *valueList; valueList = _slotPtr->getValues(numRows, numCols); // Get X-axis units from the SlotPlot's X-axis assigned to this SlotCurve // Get Y-axis units from the SlotPlot's Y-axis assigned to this SlotCurve const int curveXAxis = xAxis(); const int curveYAxis = yAxis(); ScaledUnitPtr xAxisSuPtr = _plot ? _plot->axisUnits (curveXAxis) : NULL; ScaledUnitPtr yAxisSuPtr = _plot ? _plot->axisUnits (curveYAxis) : NULL; if ((xAxisSuPtr == NULL) || (yAxisSuPtr == NULL) || (_slotPtr == NULL)) { _sourcePoints.clear(); _precomputedStepPoints.clear(); setSamples (_sourcePoints); invalidateCachedValues(); return; //---->> } const unit_type xUnitType = xAxisSuPtr->getType(); const unit_type yUnitType = yAxisSuPtr->getType(); const bool xIsTime (xUnitType == DATETIME); const bool yIsTime (yUnitType == DATETIME); const double xScale = xAxisSuPtr->getScale(); const double yScale = yAxisSuPtr->getScale(); const QString xUnitStr = xAxisSuPtr->getUsrUnit(); const QString yUnitStr = yAxisSuPtr->getUsrUnit(); const double zScale = _slotPtr->getUsrScale (_zColumnNum); const QString zUnitStr = _slotPtr->getUsrUnits (_zColumnNum); QList pointList; pointList.reserve (numRows); for (int row = 0; row < numRows; row++) { // Only add points with this contour value (out to precision 12) const double zVal = _slotPtr->getValue(row, _zColumnNum, zScale, zUnitStr); if (fabs(zVal - _contourValue) < 0.000000001) { const double x = _slotPtr->getValue(row, _xColumnNum, xScale, xUnitStr); const double y = _slotPtr->getValue(row, _yColumnNum, yScale, yUnitStr); // Note: Include NaN value points. Lines are drawn with breaks // at NaNs values. [Gnats 4904]. const QPointF pnt ( (xIsTime ? julianDaysFromDoubleSecs (x) : x) , (yIsTime ? julianDaysFromDoubleSecs (y) : y) ); pointList.append (pnt); } } // Set the data for the curve _sourcePoints = pointList.toVector(); if (_rwCurveStyle == RwSteps) precomputeStepPoints (true); // applyToo else setSamples (_sourcePoints); // If the curve type is spline, adjust the spline size of the curve // fitter taking into account the number of data points. // A spline size of three times the number of data points seems to // generate a reasonable curve close to the actual data points. if (rwCurveStyle() == RwSpline) { QwtSplineCurveFitter* fitter = dynamic_cast (curveFitter()); if (fitter) { fitter->setSplineSize(numRows * 3); } } delete valueList; invalidateCachedValues(); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotContourCurve::getFullTitle() const // // PURPOSE: // Get the official title for this curve. // // PARAMETERS: // None // // RETURN VALUES: // QString - curve title // //---------------------------------------------------------------------------- QString TableSlotContourCurve::getFullTitle() const { QString title; title = getFullSlotName(_slotPtr); if(_slotPtr) { title += " ("; title += _slotPtr->getColumnLabel(_xColumnNum); title += " x "; title += _slotPtr->getColumnLabel(_yColumnNum); title += " x "; QString contourString; contourString.setNum(_contourValue); title += contourString; title += ")"; } return title; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotContourCurve::isSameCurve() const // // PURPOSE: // Tests whether the given curve represents the same slots as the current // curve, independent of the labels, style, etc. // // PARAMETERS: // (I) slotCurve // // RETURN VALUES: // bool // //---------------------------------------------------------------------------- bool TableSlotContourCurve::isSameCurve(SlotCurve *slotCurve) const { TableSlotContourCurve* tsccurve = dynamic_cast(slotCurve); return (tsccurve && (tsccurve->_slotPtr == _slotPtr) && (tsccurve->_contourValue == _contourValue)); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // TableSlotContourCurve::validate() // // PURPOSE: // Determine whether this curve can be plotted. // // PARAMETERS: // None // // RETURN VALUES: // bool // //---------------------------------------------------------------------------- bool TableSlotContourCurve::validate() { if (_slotPtr == NULL) { _isCurveValid = false; return false; } if ((_slotPtr->getNumColumns() <= _xColumnNum) || (_slotPtr->getNumColumns() <= _yColumnNum) || (_slotPtr->getNumColumns() <= _zColumnNum)) { QString warningString = "Unable to plot "; warningString += getFullSlotName(_slotPtr) + ".\n"; warningString += QString("Invalid columns selected for plotting."); QMessageBox::warning(_plot, QString("Plot Error"), warningString); _isCurveValid = false; return false; } _isCurveValid = true; return true; } //---------------------------------------------------------------------------- //+public // // METHOD: // ScalarSlotCurve::ScalarSlotCurve() // // PURPOSE: // Constructor. // // PARAMETERS: // (I) parent - The parent plot object // (I) slotPtr - The slot to plot // //---------------------------------------------------------------------------- ScalarSlotCurve::ScalarSlotCurve(SlotPlot* parent, ScalarSlot* slotPtr) : SeriesSlotCurve(parent, slotPtr, 0), _computedStartDate(), _computedEndDate() { static const char* mname ("ScalarSlotCurve ctor"); // const QString slotName = // _slotPtr ? _slotPtr->getCompleteName() : QString ("NULL"); // std::cout << mname << qPrintable (slotName) << std::endl; RunInfo* rinfo = rwWorkspace->getRunInfo(); if (rwAssert (rinfo != NULL)) { _computedStartDate = *(rinfo->getInitDate()); _computedEndDate = *(rinfo->getEndDate()); } // resolve computed fields for debug _computedStartDate.debugResolve(); _computedEndDate.debugResolve(); setRwCurveStyle(RwSteps); // Note: It's now too early to update the underlying QwtPlotCurve's data. // Display units now come from the axes to which this curve has yet to // be placed. //-- updateCurveData(); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ScalarSlotCurve::~ScalarSlotCurve() // // PURPOSE: // Destructor. // //---------------------------------------------------------------------------- ScalarSlotCurve::~ScalarSlotCurve() { // This is called from all SlotCurve subclass destructors, so that // the slotCurveDeleted signal is emitted from the most dervied class. emitDeleted(); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ScalarSlotCurve::saveCurveInfo() const // // PURPOSE: // Save the current curve info the given CurveInfo struct. // // PARAMETERS: // (O) info - curve info struct to be saved to // //---------------------------------------------------------------------------- void ScalarSlotCurve::saveCurveInfo(CurveInfo *info) const { SeriesSlotCurve::saveCurveInfo(info); info->_type = CurveInfo::SCALAR; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ScalarSlotCurve::updateCurveData() // // PURPOSE: // Updates the curve with the current data from the Slot in the Riverware // model. // // PARAMETERS: // None // // INPUTS: // Date_Time _computedStartDate; // ScalarSlotCurve // Date_Time _computedEndDate; // ScalarSlotCurve // Slot* _slotPtr; // SeriesSlotCurve // //---------------------------------------------------------------------------- void ScalarSlotCurve::updateCurveData() { static const char* mname ("ScalarSlotCurve::updateCurveData"); static int callCnt (0); ++callCnt; ScalarSlot* scSlot = dynamic_cast(_slotPtr); if ( (_plot == NULL) || (_slotPtr == NULL) || (!rwAssert (scSlot != NULL)) ) { _sourcePoints.clear(); _precomputedStepPoints.clear(); setSamples (_sourcePoints); invalidateCachedValues(); return; //---->> } const bool isFullDateTimeVal = isDateTimeScalarSlotCurve(); bool valueAxisIsY = true; int valueAxis = yAxis(); int otherAxis = xAxis(); if (isFullDateTimeVal) { valueAxisIsY = false; valueAxis = xAxis(); otherAxis = yAxis(); } const ScaledUnitPtr valueSuPtr = _plot->axisUnits (valueAxis); if (!rwAssert (valueSuPtr != NULL)) { _sourcePoints.clear(); _precomputedStepPoints.clear(); setSamples (_sourcePoints); invalidateCachedValues(); return; //---->> } const bool valueSuPtrIsDt = (valueSuPtr->getType() == DATETIME); const bool valueIsPartialDt = valueSuPtrIsDt && (valueSuPtr->getUsrUnit() != PartialDateTime_AbsDateTimeUnitName); // Plotting of Partial Date/Times is not currently supported. if (valueIsPartialDt) { _sourcePoints.clear(); _precomputedStepPoints.clear(); setSamples (_sourcePoints); invalidateCachedValues(); return; //---->> } const double yScale = valueSuPtr->getScale(); const QString yUnitStr = valueSuPtr->getUsrUnit(); const double val = scSlot->getValue (yScale, yUnitStr); const double plotVal = valueSuPtrIsDt ? julianDaysFromDoubleSecs (floor (val)) : val; // Note: Were having difficulty with DateTime values which had been // converted to a Partial DateTime, and then back into a Full DateTime. // (That process corrupts the original Full DateTime, changing it to // a DateTime within the First Leap Year of our epoch range). Let's // not generate points for such strange Full DateTimes. (Add two days // of seconds to the limit, just for safety). // static seconds_t firstLeapYearEndSecsPlus (0); if (firstLeapYearEndSecsPlus == 0) { Date_Time firstLeapYearEnd; firstLeapYearEnd.setTime (Date_Time::getFirstLeapYear(), 12, 31); firstLeapYearEndSecsPlus = firstLeapYearEnd.getAbsSeconds() + 172800; } if (valueSuPtrIsDt && (floor (val) <= firstLeapYearEndSecsPlus)) { _sourcePoints.clear(); _precomputedStepPoints.clear(); setSamples (_sourcePoints); invalidateCachedValues(); return; //---->> [SEE NOTE ABOVE]. } const QwtInterval otherInterval = _plot->axisInterval (otherAxis); const double otherAxisMin = otherInterval.minValue(); const double otherAxisMax = otherInterval.maxValue(); QPointF pnt1 (INVALIDVALUE, INVALIDVALUE); QPointF pnt2 (INVALIDVALUE, INVALIDVALUE); if (valueAxisIsY) { pnt1 = QPointF (otherAxisMin, plotVal); pnt2 = QPointF (otherAxisMax, plotVal); } else { pnt1 = QPointF (plotVal, otherAxisMin); pnt2 = QPointF (plotVal, otherAxisMax); } QVector pointVec (2); pointVec [0] = pnt1; pointVec [1] = pnt2; // if (isDt) // { // std::cout << mname << " [" << "#" << callCnt << "]" // << std::fixed // << " X (" << pnt1.x() << " --> " << pnt2.x() << ")" // << std::defaultfloat // << " Y (" << pnt1.y() << " --> " << pnt2.y() << ")" // << std::scientific // << " " << qPrintable (getFullTitle()) // << std::endl; // } _sourcePoints = pointVec; setSamples (_sourcePoints); if (_rwCurveStyle == RwSteps) precomputeStepPoints (true); // applyToo else setSamples (_sourcePoints); invalidateCachedValues(); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // virtual from SlotCurve bool ScalarSlotCurve::isDateTimeScalarSlotCurve() const { ScalarSlot* scSlot = dynamic_cast(_slotPtr); ScaledUnitPtr suPtr = scSlot ? scSlot->getScaledUnitPtr() : NULL; const bool isFullDateTimeVal = suPtr && (suPtr->getType() == DATETIME) && (suPtr->getUsrUnit() == PartialDateTime_AbsDateTimeUnitName); return isFullDateTimeVal; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // virtual from SlotCurve ScaledUnitPtr ScalarSlotCurve::getXUnits() const { const bool isFullDateTimeVal = isDateTimeScalarSlotCurve(); ScaledUnitPtr xSuPtr (NULL); if (isFullDateTimeVal) { ScalarSlot* scSlot = dynamic_cast(_slotPtr); ScaledUnitPtr slotSuPtr = scSlot ? scSlot->getScaledUnitPtr() : NULL; xSuPtr = slotSuPtr ? slotSuPtr : unitMgr->getScaledUnit (NOUNITS); } else { xSuPtr = UnitMgr::instance()->getScaledUnit(DATETIME); } return xSuPtr; } // virtual from SlotCurve ScaledUnitPtr ScalarSlotCurve::getYUnits() const { const bool isFullDateTimeVal = isDateTimeScalarSlotCurve(); ScaledUnitPtr ySuPtr (NULL); if (isFullDateTimeVal) { // Use units of the left axis ySuPtr = _plot ? _plot->axisUnits (QwtPlot::yLeft) : unitMgr->getScaledUnit (NOUNITS); } else { // The Y-Axis is the slot, so use the slot's units ScalarSlot* scSlot = dynamic_cast(_slotPtr); ScaledUnitPtr slotSuPtr = scSlot ? scSlot->getScaledUnitPtr() : NULL; ySuPtr = slotSuPtr ? slotSuPtr : unitMgr->getScaledUnit (NOUNITS); } return ySuPtr; } // virtual from SlotCurve QString ScalarSlotCurve::getXAxisDescription() const { QString desc; const bool isFullDateTimeVal = isDateTimeScalarSlotCurve(); if (isFullDateTimeVal) { // The X-Axis is the slot, so use its full title. desc = getFullTitle(); } return desc; } // virtual from SlotCurve QString ScalarSlotCurve::getYAxisDescription() const { QString desc; const bool isFullDateTimeVal = isDateTimeScalarSlotCurve(); if (!isFullDateTimeVal) { // The Y-Axis is the slot, so use its full title. desc = getFullTitle(); } return desc; } //---------------------------------------------------------------------------- //+public // // METHOD: // ScalarSlotCurve::setComputedDates(double dt1, double dt2) // // PURPOSE: // Set the periodic curve's start and end dates for CURVE DISPLAY from // the provided julian representations of dates. This involves // recalculating the data points containted in the ScalarSlotCurves' // QwtPlotCurve (base class) based on the new time range. // //---------------------------------------------------------------------------- void ScalarSlotCurve::setComputedDates(double dt1, double dt2) { _computedStartDate.setJulianDate (std::min (dt1, dt2)); _computedEndDate.setJulianDate (std::max (dt1, dt2)); // resolve computed fields for debug _computedStartDate.debugResolve(); _computedEndDate.debugResolve(); updateCurveData(); } double ScalarSlotCurve::computedStartDate() const { return _computedStartDate.julian(); } double ScalarSlotCurve::computedEndDate() const { return _computedEndDate.julian(); } //---------------------------------------------------------------------------- //+public // // METHOD: // PeriodicSlotCurve::PeriodicSlotCurve() // // PURPOSE: // Constructor. // // PARAMETERS: // (I) parent - The parent plot object // (I) slotPtr - The slot to plot // (I) yColumnNum - column for y axis // //---------------------------------------------------------------------------- PeriodicSlotCurve::PeriodicSlotCurve(SlotPlot* parent, PeriodicSlot* slotPtr, int yColumnNum) : SeriesSlotCurve(parent, slotPtr, yColumnNum), _computedStartDate(), _computedEndDate() { static const char* mname ("PeriodicSlotCurve ctor"); // const QString slotName = // _slotPtr ? _slotPtr->getCompleteName() : QString ("NULL"); // std::cout << mname << qPrintable (slotName) << std::endl; RunInfo* rinfo = rwWorkspace->getRunInfo(); if (rwAssert (rinfo != NULL)) { _computedStartDate = *(rinfo->getInitDate()); _computedEndDate = *(rinfo->getEndDate()); } // resolve computed fields for debug _computedStartDate.debugResolve(); _computedEndDate.debugResolve(); // // Set CurveStyle based on access method for this periodic slot // if (slotPtr && slotPtr->defaultAccessMethodIsLookup()) setRwCurveStyle(RwSteps); else setRwCurveStyle(RwLines); // Note: It's now too early to update the underlying QwtPlotCurve's data. // Display units now come from the axes to which this curve has yet to // be placed. //-- updateCurveData(); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // PeriodicSlotCurve::~PeriodicSlotCurve() // // PURPOSE: // Destructor. // //---------------------------------------------------------------------------- PeriodicSlotCurve::~PeriodicSlotCurve() { // This is called from all SlotCurve subclass destructors, so that // the slotCurveDeleted signal is emitted from the most dervied class. emitDeleted(); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // PeriodicSlotCurve::saveCurveInfo() const // // PURPOSE: // Save the current curve info the given CurveInfo struct. // // PARAMETERS: // (O) info - curve info struct to be saved to // //---------------------------------------------------------------------------- void PeriodicSlotCurve::saveCurveInfo(CurveInfo *info) const { SeriesSlotCurve::saveCurveInfo(info); info->_type = CurveInfo::PERIODIC; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // PeriodicSlotCurve::updateCurveData() // // PURPOSE: // Updates the curve with the current data from the Slot in the Riverware // model. // // PARAMETERS: // None // // INPUTS: // Date_Time _computedStartDate; // PeriodicSlotCurve // Date_Time _computedEndDate; // PeriodicSlotCurve // Slot* _slotPtr; // SeriesSlotCurve // int _colNum; // SeriesSlotCurve // //---------------------------------------------------------------------------- void PeriodicSlotCurve::updateCurveData() { const PeriodicSlot* pslot = dynamic_cast(_slotPtr); // Get Y-axis units from the SlotPlot's Y-axis assigned to this SlotCurve const int curveYAxis = yAxis(); ScaledUnitPtr yAxisSuPtr = _plot ? _plot->axisUnits (curveYAxis) : NULL; if ( (yAxisSuPtr == NULL) || (_slotPtr == NULL) || !rwAssert (pslot != NULL) ) { _sourcePoints.clear(); _precomputedStepPoints.clear(); setSamples (_sourcePoints); invalidateCachedValues(); return; //---->> } const unit_type yUnitType = yAxisSuPtr->getType(); const bool yIsTime (yUnitType == DATETIME); const double yScale = yAxisSuPtr->getScale(); const QString yUnitStr = yAxisSuPtr->getUsrUnit(); // // Set CurveStyle based on access method for this periodic slot // if (pslot->defaultAccessMethodIsLookup()) setRwCurveStyle(RwSteps); else setRwCurveStyle(RwLines); // Compute the number of data points between start and end dates const TimeLine *rowMap = pslot->getRowMap(); const seconds_t periodSec = rowMap->getPeriod().approxSeconds(); const seconds_t startEndSec = _computedEndDate - _computedStartDate; const int numPeriods = startEndSec / periodSec + 3; // Add two spots for the actual start and end dates const int numPoints = (numPeriods * pslot->getNumRows()) + 2; QList pointList; const int allocCnt = std::max (2, numPoints); pointList.reserve (allocCnt); // *************************************************** // *** (1) Periodic Value at Computed Start Date *** // *************************************************** TimeLine::Period period(rowMap->getPeriod()); QString explanation; bool success(true); // Add a point for the start date - this ensures the periodic curve will // extend the whole way to the left on the graph and also ensures that // the periodic line will appear if the start-end interval is too short // to encompass a point defined on the periodic plot. double val (INVALIDVALUE); success = pslot->getValue(_computedStartDate, _colNum, val, explanation, yScale, yUnitStr); if (success) { if (isValid (val)) { pointList.append ( QPointF (_computedStartDate.julian(), yIsTime ? julianDaysFromDoubleSecs (val) : val)); } } // ******************************************************* // *** (2) Periodic Values over Displayed Time Range *** // ******************************************************* // Iterate over each period contained inside the start/end interval Date_Time refDate = _computedStartDate; while (refDate < (_computedEndDate + DeltaTime((long) period.quantity(), period.units()))) { // Get all the dates for this period std::list periodDates; rowMap->getDatesInPeriod(refDate, periodDates); for (std::list::iterator dateIter = periodDates.begin(); (dateIter != periodDates.end()) && (*dateIter <= _computedEndDate); ++dateIter) { const Date_Time currDate = *dateIter; if (currDate < _computedStartDate) continue; //---------- val = INVALIDVALUE; success = pslot->getValue(currDate, _colNum, val, explanation, yScale, yUnitStr); // Note: Include NaN value points. Lines are drawn with breaks // at NaNs values. [Gnats 4904]. pointList.append ( QPointF (currDate.julian(), yIsTime ? julianDaysFromDoubleSecs (val) : val)); } // Next period period = rowMap->getPeriod(); refDate += DeltaTime ((long) period.quantity(), period.units()); } // ************************************************** // *** (3) Periodic Values at Computed End Date *** // ************************************************** // Add a point for the end date - this ensures the periodic curve will // extend the whole way to the right on the graph and also ensures that // the periodic line will appear if the start-end interval is too short // to encompass a point defined on the periodic plot. val = INVALIDVALUE; success = pslot->getValue(_computedEndDate, _colNum, val, explanation, yScale, yUnitStr); if (success) { if (isValid (val)) { pointList.append ( QPointF (_computedEndDate.julian(), yIsTime ? julianDaysFromDoubleSecs (val) : val)); } } // **************************************** // *** (5) Set the data for the curve *** // **************************************** _sourcePoints = pointList.toVector(); if (_rwCurveStyle == RwSteps) precomputeStepPoints (true); // applyToo else setSamples (_sourcePoints); // If the curve type is spline, adjust the spline size of the curve // fitter taking into account the number of data points. // A spline size of three times the number of data points seems to // generate a reasonable curve close to the actual data points. if (rwCurveStyle() == RwSpline) { QwtSplineCurveFitter* fitter = dynamic_cast (curveFitter()); if (fitter) { fitter->setSplineSize(numValues * 3); } } invalidateCachedValues(); } //---------------------------------------------------------------------------- //+public // // METHOD: // PeriodicSlotCurve::setComputedDates(double dt1, double dt2) // // PURPOSE: // Set the periodic curve's start and end dates for CURVE DISPLAY from // the provided julian representations of dates. This involves // recalculating the data points containted in the PeriodicSlotCurves' // QwtPlotCurve (base class) based on the new time range. // //---------------------------------------------------------------------------- void PeriodicSlotCurve::setComputedDates(double dt1, double dt2) { static const char* mname ("PeriodicSlotCurve::setComputedDates"); static int callCnt (0); ++callCnt; // Gnats 5832: Adding an empty Periodic Slot as the first slot in // an empty plot fails assertion (related to bad Date_Time value). // const bool skip ((dt1 <= 0.0) || (dt2 <= 0.0)); if (!skip) { _computedStartDate.setJulianDate (std::min (dt1, dt2)); _computedEndDate.setJulianDate (std::max (dt1, dt2)); } // resolve computed fields for debug _computedStartDate.debugResolve(); _computedEndDate.debugResolve(); // const QString str1 (_computedStartDate.ascii_date_time()); // const QString str2 (_computedEndDate.ascii_date_time()); // std::cout << mname << " [#" << callCnt << "]" // << " (" << dt1 << ", " << dt2 << ")" // << (skip ? " SKIP" : "") // << " Start " << qPrintable (str1) // << ", End " << qPrintable (str2) // << std::endl; updateCurveData(); } double PeriodicSlotCurve::computedStartDate() const { return _computedStartDate.julian(); } double PeriodicSlotCurve::computedEndDate() const { return _computedEndDate.julian(); } //---------------------------------------------------------------------------- //+public // // METHOD: // ParametricSlotCurve::ParametricSlotCurve() // // PURPOSE: // Constructor. // // PARAMETERS: // (I) parent - The parent plot object // (I) xSlotPtr - The slot to plot on the X-Axis // (I) xSlotPtr - The slot to plot on the Y-Axis // //---------------------------------------------------------------------------- ParametricSlotCurve::ParametricSlotCurve(SlotPlot* parent, SeriesSlot* xSlotPtr, SeriesSlot* ySlotPtr) : SlotCurve(parent) { _slotPtr[X_SLOT] = xSlotPtr; _slotPtr[Y_SLOT] = ySlotPtr; _cbId[X_SLOT] = 0; _cbId[Y_SLOT] = 0; if (!validate()) { return; } // Note: It's now too early to update the underlying QwtPlotCurve's data. // Display units now come from the axes to which this curve has yet to // be placed. //-- updateCurveData(); PlotDlgSettings* settings = PlotDlgSettings::instance(); const QFont legendFont = settings->getLegendFont(); QwtText titleText (getFullTitle()); titleText.setFont (legendFont); setTitle (titleText); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::~ParametricSlotCurve() // // PURPOSE: // Destructor. // //---------------------------------------------------------------------------- ParametricSlotCurve::~ParametricSlotCurve() { // This is called from all SlotCurve subclass destructors, so that // the slotCurveDeleted signal is emitted from the most dervied class. emitDeleted(); if (_slotPtr[X_SLOT] && _cbId[X_SLOT]) { _slotPtr[X_SLOT]->deleteCallback(_cbId[X_SLOT]); } if (_slotPtr[Y_SLOT] && _cbId[Y_SLOT]) { _slotPtr[Y_SLOT]->deleteCallback(_cbId[Y_SLOT]); } } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::saveCurveInfo(info) const // // PURPOSE: // Save the current curve object into the CurveInfo struct. // // PARAMETERS: // (O) info - CurveInfo struct to save to // //---------------------------------------------------------------------------- void ParametricSlotCurve::saveCurveInfo(CurveInfo *info) const { SlotCurve::saveCurveInfo(info); info->_type = CurveInfo::PARAMETRIC; info->_xSlot = _slotPtr[X_SLOT]; info->_ySlot = _slotPtr[Y_SLOT]; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::updateCurveData() // // PURPOSE: // Updates the curve with the current data from the Slot in the Riverware // model. // // PARAMETERS: // None // //---------------------------------------------------------------------------- void ParametricSlotCurve::updateCurveData() { if ((_slotPtr[X_SLOT] == NULL) || (_slotPtr[Y_SLOT] == NULL)) { _sourcePoints.clear(); _precomputedStepPoints.clear(); setSamples (_sourcePoints); invalidateCachedValues(); return; //---->> } // These slots need to line-up properly, so that they both start // on the same timestep. // const Date_Time* xStart = _slotPtr[X_SLOT]->getStartDate(); const Date_Time* xEnd = _slotPtr[X_SLOT]->getEndDate(); const Date_Time* yStart = _slotPtr[Y_SLOT]->getStartDate(); const Date_Time* yEnd = _slotPtr[Y_SLOT]->getEndDate(); // Find the area of intersection between the 2 timeseries const Date_Time *start = (*xStart > *yStart) ? xStart : yStart; const Date_Time *end = (*xEnd < *yEnd) ? xEnd : yEnd; // Get X-axis units from the SlotPlot's X-axis assigned to this SlotCurve // Get Y-axis units from the SlotPlot's Y-axis assigned to this SlotCurve const int curveXAxis = xAxis(); const int curveYAxis = yAxis(); ScaledUnitPtr xAxisSuPtr = _plot ? _plot->axisUnits (curveXAxis) : NULL; ScaledUnitPtr yAxisSuPtr = _plot ? _plot->axisUnits (curveYAxis) : NULL; if ((xAxisSuPtr == NULL) || (yAxisSuPtr == NULL)) { _sourcePoints.clear(); _precomputedStepPoints.clear(); setSamples (_sourcePoints); invalidateCachedValues(); return; //---->> } const unit_type xUnitType = xAxisSuPtr->getType(); const unit_type yUnitType = yAxisSuPtr->getType(); const bool xIsTime (xUnitType == DATETIME); const bool yIsTime (yUnitType == DATETIME); const double xScale = xAxisSuPtr->getScale(); const double yScale = yAxisSuPtr->getScale(); const QString xUnitStr = xAxisSuPtr->getUsrUnit(); const QString yUnitStr = yAxisSuPtr->getUsrUnit(); int numXValues (0); int numYValues (0); double *valueListX (NULL); double *valueListY (NULL); _slotPtr[X_SLOT]->getValues(start, end, valueListX, numXValues, xScale, xUnitStr); _slotPtr[Y_SLOT]->getValues(start, end, valueListY, numYValues, yScale, yUnitStr); // The number of x and y values should be the same, but just in case int numValues = (numXValues < numYValues) ? numXValues : numYValues; const int allocCnt = std::max (2, numValues); QList pointList; pointList.reserve (allocCnt); for (int i = 0; i < numValues; i++) { const double valX = valueListX[i]; const double valY = valueListY[i]; const double x = xIsTime ? julianDaysFromDoubleSecs (valX) : valX; const double y = yIsTime ? julianDaysFromDoubleSecs (valY) : valY; // If either of the values is NaN, skip this point if (isValid(x) && isValid(y)) { pointList.append (QPointF (x, y)); } } // Set the data in the curve _sourcePoints = pointList.toVector(); if (_rwCurveStyle == RwSteps) precomputeStepPoints (true); // applyToo else setSamples (_sourcePoints); // If the curve type is spline, adjust the spline size of the curve // fitter taking into account the number of data points. // A spline size of three times the number of data points seems to // generate a reasonable curve close to the actual data points. if (rwCurveStyle() == RwSpline) { QwtSplineCurveFitter* fitter = dynamic_cast (curveFitter()); if (fitter) { fitter->setSplineSize(numRows * 3); } } delete [] valueListX; delete [] valueListY; invalidateCachedValues(); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::getFullTitle() const // // PURPOSE: // Get the official title for this curve of the form: // "Object1::Slot1 X Object2::Slot2" // // PARAMETERS: // None // // RETURN VALUES: // QString - curve title // //---------------------------------------------------------------------------- QString ParametricSlotCurve::getFullTitle() const { // "Object1::Slot1 X Object2::Slot2" QString fullTitle; fullTitle = getFullSlotName(_slotPtr[X_SLOT]); fullTitle += " X "; fullTitle += getFullSlotName(_slotPtr[Y_SLOT]); return fullTitle; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::setupAxis() // // PURPOSE: // Setup the label and scale for an axis, based on this curve. // // PARAMETERS: // (I) axisId - the axis to initialize // //---------------------------------------------------------------------------- void ParametricSlotCurve::setupAxis(int axisId) { switch (axisId) { case QwtPlot::yLeft: case QwtPlot::yRight: { _plot->setAxisTitle(axisId, getSlotUnitsString(_slotPtr[Y_SLOT])); setYAxis(axisId); break; } case QwtPlot::xBottom: case QwtPlot::xTop: { _plot->setAxisTitle(axisId, getSlotUnitsString(_slotPtr[X_SLOT])); setXAxis(axisId); break; } } } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::getXUnits() const // // PURPOSE: // Get the scaled unit pointer for the X-axis of this curve // // RETURN VALUES: // QString - units string // //---------------------------------------------------------------------------- ScaledUnitPtr ParametricSlotCurve::getXUnits() const { if (_slotPtr[X_SLOT] == NULL) return NULL; //----->> const NumDisplayAttribs& dispAttbs = _slotPtr[X_SLOT]->activeNumDisplayAttribs_prim (0); const ScaledUnitPtr suPtr = dispAttbs.scaledUnit(); return suPtr; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::getYUnits() const // // PURPOSE: // Get the scaled unit pointer for the X-axis of this curve // // RETURN VALUES: // QString - units string // //---------------------------------------------------------------------------- ScaledUnitPtr ParametricSlotCurve::getYUnits() const { if (_slotPtr[Y_SLOT] == NULL) return NULL; //----->> const NumDisplayAttribs& dispAttbs = _slotPtr[Y_SLOT]->activeNumDisplayAttribs_prim (0); const ScaledUnitPtr suPtr = dispAttbs.scaledUnit(); return suPtr; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::getXAxisDescription() const // // PURPOSE: // Get the label for the X-axis of this curve // // RETURN VALUES: // QString - axis label string // //---------------------------------------------------------------------------- QString ParametricSlotCurve::getXAxisDescription() const { return getFullSlotName(_slotPtr[X_SLOT]); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::getYAxisDescription() const // // PURPOSE: // Get the label for the X-axis of this curve // // RETURN VALUES: // QString - axis label string // //---------------------------------------------------------------------------- QString ParametricSlotCurve::getYAxisDescription() const { return getFullSlotName(_slotPtr[Y_SLOT]); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::isSameCurve() const // // PURPOSE: // Tests whether the given curve represents the same slots as the current // curve, independent of the labels, style, etc. // // PARAMETERS: // (I) slotCurve // // RETURN VALUES: // bool // //---------------------------------------------------------------------------- bool ParametricSlotCurve::isSameCurve(SlotCurve *slotCurve) const { ParametricSlotCurve* pscurve = dynamic_cast(slotCurve); return (pscurve && (pscurve->_slotPtr[X_SLOT] == _slotPtr[X_SLOT]) && (pscurve->_slotPtr[Y_SLOT] == _slotPtr[Y_SLOT])); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::hasSlot() const // // PURPOSE: // Determine whether this curve is plotting the given slotPtr. // // PARAMETERS: // (I) slotPtr // // RETURN VALUES: // bool // //---------------------------------------------------------------------------- bool ParametricSlotCurve::hasSlot(Slot *slotPtr) const { return ((_slotPtr[X_SLOT] == slotPtr) || (_slotPtr[Y_SLOT] == slotPtr)); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::hasSlots() const // // PURPOSE: // Determine whether this curve is plotting the both of the given slotPtrs. // // PARAMETERS: // (I) slotPtrX // (I) slotPtrY // // RETURN VALUES: // bool // //---------------------------------------------------------------------------- bool ParametricSlotCurve::hasSlots(Slot *slotPtrX, Slot *slotPtrY) const { return ((_slotPtr[X_SLOT] == slotPtrX) && (_slotPtr[Y_SLOT] == slotPtrY)); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // public virtual QList ParametricSlotCurve::getSlots() const { QList retList; if (_slotPtr[X_SLOT]) retList.append (_slotPtr[X_SLOT]); if (_slotPtr[Y_SLOT]) retList.append (_slotPtr[Y_SLOT]); return (retList); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // public virtual bool ParametricSlotCurve::slotsInSet (const QSet& slotSet) const { const bool cont = (_slotPtr[X_SLOT] && slotSet.contains (_slotPtr[X_SLOT])) || (_slotPtr[Y_SLOT] && slotSet.contains (_slotPtr[Y_SLOT])) ; return (cont); } // public virtual SlotColRef ParametricSlotCurve::xSlotCol() const { return SlotColRef (_slotPtr[X_SLOT], 0); } // public virtual SlotColRef ParametricSlotCurve::ySlotCol() const { return SlotColRef (_slotPtr[Y_SLOT], 0); } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::validate() // // PURPOSE: // Determine whether this curve can be plotted. // // PARAMETERS: // None // // RETURN VALUES: // bool // //---------------------------------------------------------------------------- bool ParametricSlotCurve::validate() { if (_slotPtr[X_SLOT] == NULL || _slotPtr[Y_SLOT] == NULL) { _isCurveValid = false; return false; } _isCurveValid = true; return true; } //---------------------------------------------------------------------------- //+public virtual // // METHOD: // ParametricSlotCurve::registerCallbacks() // // PURPOSE: // Register this curve to receive callbacks from its slot. // //---------------------------------------------------------------------------- void ParametricSlotCurve::registerCallbacks() { if (_slotPtr[X_SLOT]) { _cbId[X_SLOT] = _slotPtr[X_SLOT]->addCallback(SlotPlot__callbackNoteProc, _plot); } if (_slotPtr[Y_SLOT]) { _cbId[Y_SLOT] = _slotPtr[Y_SLOT]->addCallback(SlotPlot__callbackNoteProc, _plot); } // See SlotPlot::callbackHandler(). } //---------------------------------------------------------------------------- //+public // // METHOD: // ParametricSlotCurve::changeSlotInfo() // // PURPOSE: // Change the slot info for the slotCurve // //---------------------------------------------------------------------------- bool ParametricSlotCurve::changeSlotInfo(SeriesSlot* xSlotPtr, SeriesSlot* ySlotPtr) { if (xSlotPtr != _slotPtr[X_SLOT] || ySlotPtr != _slotPtr[Y_SLOT]) { // Try to reset the slot info and see if the plot can support it SeriesSlot* origXSlotPtr = _slotPtr[X_SLOT]; SeriesSlot* origYSlotPtr = _slotPtr[Y_SLOT]; _slotPtr[X_SLOT] = xSlotPtr; _slotPtr[Y_SLOT] = ySlotPtr; if (_plot->changeSlotCurve(this)) { // Change was successfully supported in the containing plot updateCurveData(); // Adjust callbacks for the new slot configuration if (origXSlotPtr && _cbId[X_SLOT]) { origXSlotPtr->deleteCallback(_cbId[X_SLOT]); _cbId[X_SLOT] = 0; } if (origYSlotPtr && _cbId[Y_SLOT]) { origYSlotPtr->deleteCallback(_cbId[Y_SLOT]); _cbId[Y_SLOT] = 0; } registerCallbacks(); } else { // Change was not successfully supported // Set the slot info back to original _slotPtr[X_SLOT] = origXSlotPtr; _slotPtr[Y_SLOT] = origYSlotPtr; return false; } } return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int SlotCurve::WeedFitter::_instanceCnt (0); SlotCurve::WeedFitter::WeedFitter (SlotCurve* slotCurve) : QwtWeedingCurveFitter (1.0), // tolerance _instanceNum (++_instanceCnt), _clientSlotCurve (slotCurve), _cachedInpPoints(), _cachedFiltPoints() { static const char* mname ("SlotCurve::WeedFitter ctor"); std::cout << mname << " [Inst " << _instanceNum << "]" << std::endl; } SlotCurve::WeedFitter::~WeedFitter() { static const char* mname ("SlotCurve::WeedFitter DTOR"); std::cout << mname << " [Inst " << _instanceNum << "]" << std::endl; } // virtual from QwtWeedingCurveFitter QPolygonF SlotCurve::WeedFitter::fitCurve (const QPolygonF& inPoints) const { static const char* mname ("SlotCurve::WeedFitter::fitCurve"); // ********************************** // *** Solid Line: Don't Filter *** // ********************************** if (_clientSlotCurve) { const QPen curvePen = _clientSlotCurve->pen(); const Qt::PenStyle pStyle = curvePen.style(); const bool isPatt = (pStyle != Qt::SolidLine) && (pStyle != Qt::NoPen); if (!isPatt) { // The curve line is solid (or strangely, "NoPen"). // Just return the input point array. std::cout << mname << " [Inst " << _instanceNum << "] SOLID-SKIP " << inPoints.count() << std::endl; return inPoints; //----->> } } const bool pointsChange (inPoints != _cachedInpPoints); if (pointsChange) { _cachedInpPoints = inPoints; // Call base class method _cachedFiltPoints = QwtWeedingCurveFitter::fitCurve (_cachedInpPoints); std::cout << mname << " [Inst " << _instanceNum << "]" << " FILTER " << _cachedInpPoints.count() << " --> " << _cachedFiltPoints.count() << std::endl; } else { std::cout << mname << " [Inst " << _instanceNum << "] CACHE-HIT " << _cachedFiltPoints.count() << std::endl; } return _cachedFiltPoints; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int SlotCurve::SplineFitter::_instanceCnt (0); SlotCurve::SplineFitter::SplineFitter (SlotCurve* slotCurve) : QwtSplineCurveFitter(), _instanceNum (++_instanceCnt), _clientSlotCurve (slotCurve), _weedFilter (NULL) { static const char* mname ("SlotCurve::SplineFitter ctor"); std::cout << mname << " [Inst " << _instanceNum << "]" << std::endl; setFitMode (QwtSplineCurveFitter::Spline); // [QwtSplineCurveFitter] _weedFilter = new QwtWeedingCurveFitter (1.0); // tolerance } SlotCurve::SplineFitter::~SplineFitter() { static const char* mname ("SlotCurve::SplineFitter DTOR"); std::cout << mname << " [Inst " << _instanceNum << "]" << std::endl; if (_weedFilter) { delete _weedFilter; _weedFilter = NULL; } } // virtual from QwtSplineCurveFitter QPolygonF SlotCurve::SplineFitter::fitCurve (const QPolygonF& inPoints) const { static const char* mname ("SlotCurve::SplineFitter::fitCurve"); const int inPointsCnt = inPoints.count(); if (inPointsCnt < 1) return QPolygonF(); //---->> // It seemed ideal to set the spline size with the exact number of // points being processed known. (This method is called independently // for each contiguous piece of the curve). But it seems that it is too // late to do that (at least without some other processing which I // haven't yet figured out). So, we won't do this here now. // //-- // A spline size of three times the number of data points seems to //-- // generate a reasonable curve close to the actual data points. //-- // See Gnats 4911 [Sep 2010, RiverWare 5.2.6]. //-- //-- SplineFitter* mutableThis = const_cast (this); //-- const int newSplinePointCnt (3 * inPointsCnt); //-- mutableThis->setSplineSize (newSplinePointCnt); // Call base class method. const QPolygonF splinePoly = QwtSplineCurveFitter::fitCurve (inPoints); bool isPatternLine (false); if (_clientSlotCurve) { const QPen curvePen = _clientSlotCurve->pen(); const Qt::PenStyle pStyle = curvePen.style(); isPatternLine = (pStyle != Qt::SolidLine) && (pStyle != Qt::NoPen); } if (!isPatternLine || (_weedFilter == NULL)) { // Solid Line: Just return the spline result. std::cout << mname << " [Inst " << _instanceNum << "]" << " FILTER " << inPoints.count() << " --> " << splinePoly.count() << std::endl; return splinePoly; //----->> } const QPolygonF weedPoly = _weedFilter->fitCurve (splinePoly); std::cout << mname << " [Inst " << _instanceNum << "]" << " FILTER " << inPoints.count() << " --> " << splinePoly.count() << " --> " << weedPoly.count() << std::endl; return weedPoly; } //--- (end SlotCurve.cpp) ---