413 lines
10 KiB
C++
413 lines
10 KiB
C++
/*******************************************************************************
|
|
* *
|
|
* 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 <QPainter>
|
|
#include <QGraphicsSceneMouseEvent>
|
|
#include <limits>
|
|
#include <QStyleOptionGraphicsItem>
|
|
|
|
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<CGraphAnalogDataPair*> *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<float>::min();
|
|
qreal MinValue = std::numeric_limits<float>::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)
|
|
{
|
|
}
|