OutilZT/sources/GuiElements/AnalogGraphItem.cpp
2017-07-20 11:11:06 -04:00

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)
{
}