/******************************************************************************* * * * Société de Transports de Montréal. * * 2013 * * * * Projet Zones Tests * * * * * * * *******************************************************************************/ /* Description: Classe d'affichage d'un graphique analogique (vitesse du train, sondes lazer, etc.) La fonction SetData permet d'associer la liste de points à tracer. La fonction DisplayData() permet de fixer le timespan à afficher. La fonction détermine quels points sont dans le span et crée une liste de lignes qui sont ensuite affichés dans la fonction de rappel paint(). Utilisée dans la page de visualisation des passages de train. */ /* ************************************************************************** */ /* Revision: ### 20131024 JFM Verision d'origine. ### YYYYMMDD Description du besoin ou du bug Description du changement. */ /* ************************************************************************** */ #include "AnalogGraphItem.h" #include #include #include #include CAnalogGraphItem::CAnalogGraphItem(QGraphicsItem *Parent) { setParentItem(Parent); mDataSet = 0; mStartTime = mStopTime = 0; mDataValid = false; mLabel = new QGraphicsTextItem(this); mGraphPixmap = new QPixmap(boundingRect().width(),boundingRect().height()); mHorizLine = 0; mYOffset = 0; } CAnalogGraphItem::~CAnalogGraphItem() { delete mGraphPixmap; if(mHorizLine != 0) delete mHorizLine; } void CAnalogGraphItem::SetData(QList *DataList) { mDataSet = DataList; //Build the lines list } unsigned int CAnalogGraphItem::DisplayData(quint64 StartTime, quint64 StopTime) { mPainterPath = QPainterPath(); if(mDataSet == 0) return RET_OK; if(mDataSet->isEmpty()) return RET_ERROR; quint64 MinTime = mDataSet->first()->mTime; quint64 MaxTime = mDataSet->last()->mTime; if(StartTime >= StopTime) return RET_ERROR; if(StartTime < MinTime) return RET_ERROR; if(StopTime > MaxTime) return RET_ERROR; quint64 TimeSpan = StopTime - StartTime; mStartTime = StartTime; mStopTime = StopTime; //Find the index of the first and last point included in the timespan. int StartIndex = 0, StopIndex = 0; int i = 0; bool StartFound = false, StopFound = false; quint64 StartDrawTime = StartTime ,EndDrawTime = StopTime; if(StartTime < mAbsoluteStartTime) StartDrawTime = mAbsoluteStartTime; if(StopTime > mAbsoluteEndTime) EndDrawTime = mAbsoluteEndTime; while(StartFound == false || StopFound == false) { if(i >= mDataSet->size()) { qDebug("GraphItem::Array overflow while searching for time index"); return RET_ERROR; } quint64 CurTime = mDataSet->at(i)->mTime; if(CurTime <= StartDrawTime) { StartIndex = i; StartFound = true; } if(CurTime >= EndDrawTime && !StopFound) { StopIndex = i; StopFound = true; } i++; } //Find the Y scaling (vertical) qreal MaxValue = std::numeric_limits::min(); qreal MinValue = std::numeric_limits::max(); for(int i = 0; i < mDataSet->size(); i++) { qreal CurValue = mDataSet->at(i)->mValue; if(CurValue != -1 && CurValue != 0) //-1 is the value for "NO DATA" and 0 is not a valid value { if(MaxValue < CurValue) MaxValue = CurValue; if(MinValue > CurValue) MinValue = CurValue; } } if(MinValue < 0) { MinValue *= -1; MinValue += 1; mYOffset = MinValue; for(int i = 0; i < mDataSet->size(); i++) { mDataSet->at(i)->mValue += MinValue; } MaxValue += MinValue; MinValue = 1; } //Build the lines list mLinesList.clear(); qreal Width = boundingRect().width(); qreal Height = boundingRect().height(); qreal tick = (Width)/TimeSpan; //pixels per microsec qreal YFactor = (Height-2)/(MaxValue - MinValue); mYScaling = YFactor; mXScaling = tick; qreal X1,X2,Y1,Y2; //There is no quadratic interpolation, simply draw a straight //line between the points. When needed, execute a linear //interpolation when one of the two points creating a line is //outside the timespan (at both extremities of the view). // P2 P3 // P1 | *-------* | // * | | P4 // | | * // |<----timespan---->| // Start End // //Interpolation needed between Start->P2 and P3->End so there is no //"gap" in the graph between the first/last points and the view. for(int i = StartIndex; i < StopIndex; i++) { if(mDataSet->at(i)->mValue != -1) //-1 is the value for "NO DATA". { QLine NewLine; //Check if interpolation is needed at the left //of the span if(mDataSet->at(i)->mTime < StartTime) { //Interpolate Y value at StartTime qreal x1 = mDataSet->at(i)->mTime; qreal y1 = mDataSet->at(i)->mValue - MinValue; qreal x2 = mDataSet->at(i+1)->mTime; qreal y2 = mDataSet->at(i+1)->mValue - MinValue; // Y = mX + B qreal DeltaX = x2 - x1; qreal DeltaY = y2 - y1; qreal m = DeltaY/DeltaX; Y1 = (qreal)StartTime*m; Y1 += (y2 - m*x2); X1 = 0; } else { X1 = mDataSet->at(i)->mTime - StartTime; Y1 = mDataSet->at(i)->mValue - MinValue; } //Check if interpolation is needed at the right //of the span if(mDataSet->at(i+1)->mTime > StopTime) { //Interpolate Y value at StopTime qreal x1 = mDataSet->at(i)->mTime; qreal y1 = mDataSet->at(i)->mValue - MinValue; qreal x2 = mDataSet->at(i+1)->mTime; qreal y2 = mDataSet->at(i+1)->mValue - MinValue; qreal DeltaX = x2 - x1; qreal DeltaY = y2 - y1; qreal m = DeltaY/DeltaX; Y2 = (qreal)(StopTime)*m; Y2 += (y2 - m*x2); X2 = StopTime - StartTime; } else { X2 = mDataSet->at(i+1)->mTime - StartTime; Y2 = mDataSet->at(i+1)->mValue - MinValue; } Y1 = Height - Y1*YFactor; Y2 = Height - Y2*YFactor; NewLine.setLine(X1*tick,Y1-1,X2*tick,Y2-1); mLinesList.append(NewLine); // if(mPainterPath.isEmpty()) // mPainterPath.moveTo(X1*tick,Y1-1); // mPainterPath.lineTo(X2*tick,Y2-1); } } if(mHorizLine != 0) { Y1 = (mHorizLineYPos+mYOffset-MinValue); Y1 = Height - Y1*YFactor; mHorizLine->setP1(QPoint(0,Y1-1)); mHorizLine->setP2(QPoint((mDataSet->at(StopIndex)->mTime - StartTime)*tick,Y1-1)); } mDataValid = true; //#ifndef WINDOWS_OS mGraphPixmap->fill(QColor(245, 245, 255)); QPainter *painter = new QPainter(mGraphPixmap); painter->drawLines(mLinesList); delete painter; //#endif update(); return RET_OK; } qreal CAnalogGraphItem::GetValueForTime(quint64 time) { qreal value = 0.0; for(int i = 0; i < mDataSet->size()-1; i++) { if(time >= mDataSet->at(i)->mTime && time < mDataSet->at(i+1)->mTime) { value = mDataSet->at(i)->mValue - mYOffset; break; } } QString label = mLabelTitle; label += "\n"; label += QString().sprintf("%.2f",value); mLabel->setPlainText(label); mLabel->setPos(mLabel->pos().x(),boundingRect().height()/2 - mLabel->boundingRect().height()/2); update(); return value; } void CAnalogGraphItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option) Q_UNUSED(widget) if(mDataValid == false) return ; if(mDataSet == 0) return; if(mDataSet->size() == 0) return; // painter->setClipRect(option->exposedRect); // QPen pen; // pen.setWidth(1); //// pen.setColor(Qt::red); //// painter->setPen(pen); //// painter->drawRect(boundingRect()); // pen.setColor(Qt::black); // painter->setPen(pen); //#ifdef WINDOWS_OS // painter->drawLines(mLinesList); //#else painter->drawPixmap(0,0,*mGraphPixmap); if(mHorizLine != 0) { painter->setPen(QPen(Qt::red)); painter->drawLine(*mHorizLine); } //painter->drawPath(mPainterPath); //#endif // for(int i = 0; i < mLinesList.size(); i++) // { // painter->drawEllipse(mLinesList.at(i).p1(),2,2); // } } //void CGraphItem::UpdateDisplay() //{ // DisplayData(mStartIndex,mStopIndex); // update(); //} void CAnalogGraphItem::AddHorizontalLine(qreal YPosition) { if(mHorizLine == 0) mHorizLine = new QLine(); mHorizLineYPos = YPosition; } void CAnalogGraphItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { event->ignore(); } void CAnalogGraphItem::mouseReleaseEvent( QGraphicsSceneMouseEvent * event) { event->ignore(); } void CAnalogGraphItem::resizeEvent(QGraphicsSceneResizeEvent *event) { Q_UNUSED(event) //mBackgroundRect->setRect(boundingRect()); delete mGraphPixmap; mGraphPixmap = new QPixmap(boundingRect().width(),boundingRect().height()); DisplayData(mStartTime,mStopTime); } void CAnalogGraphItem::SetLabel(QString label,int Offset) { QFont font; font.setBold(true); mLabelTitle = label; mLabel->setFont(font); mLabel->setPlainText(label); mLabel->setPos(-Offset,boundingRect().height()/2 - mLabel->boundingRect().height()/2); } CGraphAnalogDataPair::CGraphAnalogDataPair(qreal Value, quint64 Time): mValue(Value), mTime(Time) { }