Set interval for X and Y QwtPlotZoneItem - c++

I need to plot a region on a graph using the QwtPlotZoneItem class. I need to set different intervals for both X and Y axes, how can I do that?
The orientation of my QwtPlotZoneItem is vertical, so based on the documentation, if I set the interval it will by applied to the X axis only.
"For a horizontal zone the interval is related to the y axis, for a
vertical zone it is related to the x axis."
My constructor sets:
setOrientation( Qt::Vertical );
setInterval( initDate, endDate );
Basically, what I need is to create multiple rectangles on my graph that represents the regions, for example:
Qt 5.3.2
Qwt 6.1.0

I was trying to use an incorrect class for my purposes. I found a note in the documentation:
"For displaying an area that is bounded for x and y coordinates use
QwtPlotShapeItem"
The QwtPlotShapeItem class does exactly what I need.
I need basically to set the brush and create the rectangle, for example:
QwtPlotShapeItem *shapeItem = new QwtPlotShapeItem();
shapeItem->setBrush(QColor(255,255,255, 0));
shapeItem->setPen(QColor(Qt::transparent), 0.0, Qt::SolidLine);
// TopLeft - BottomRight
QRect myRect(QPoint(startDate, yUpperPos), QPoint(endDate, yLowerPos));
shapeItem->setRect( QRectF(myRect) );
shapeItem->attach(myQwtPlot);
myQwtPlot->replot();

Related

Getting the QTransform of a resizable selection area

I have built a small custom qml item that is used as a selection area (something like the QRubberBand component provided in Qt Widgets). The item also give the ability to user to resize the content of the selection, so by grabbing the bottom corner of the selection rectangle it is possible to drag to enlarge the content. After the user has done resizing I would like to compute the QTransform matrix of the transformation. QTransform provides a convenient QTransform::scale method to get a scale transformation matrix (which I can use by comparing the width and height ratio with the previous size of the selection). The problem is that QTransform::scale assumes that the center point of the transformation is the center of the object, but I would like my transformation origin to be the top left of the selection (since the user is dragging from the bottom-right).
So for example, if I have the following code:
QRectF selectionRect = QRectF(QPointF(10,10), QPointF(200,100));
// let's resize the rectangle by changing its bottom-right corner
auto newSelectionRect = selectionRect;
newSelectionRect.setBottomRight(QPointF(250, 120));
QTransform t;
t.scale(newSelectionRect.width()/selectionRect.width(), newSelectionRect.height()/selectionRect.height());
The problem here is that if I apply the transformation t to my original selectionRect I don't get my new rectangle newSelectionRect back, but I get the following:
QRectF selectionRect = QRectF(QPointF(10,10)*sx, QPointF(200,100)*sy);
where sx and sy are the scale factors of the transform. I would like a way to compute the QTransform of my transformation that gives back newSelectionRect when applied to selectionRect.
The problem lies in this assumption:
QTransform::scale assumes that the center point of the transformation is the center of the object
All transformations performed by QTransform are referred to the origin of the axis, is just an application of various tranformation matrixes (https://en.wikipedia.org/wiki/Transformation_matrix):
Also, QTransform::translate (https://doc.qt.io/qt-5/qtransform.html#translate) states:
Moves the coordinate system dx along the x axis and dy along the y axis, and returns a reference to the matrix.
Thereby, what you are looking for is:
QTransform t;
t.translate(+10, +10); // Move the origin to the top left corner of the rectangle
t.scale(newSelectionRect.width()/selectionRect.width(), newSelectionRect.height()/selectionRect.height()); // scale
t.translate(-10, -10); // move the origin back to where it was
QRectF resultRect = t.mapRect(selectionRect); // resultRect == newSelectionRect!

How to draw graph for a trajectory which goes left and right in x axis?

i want to draw a trajectory in x and y of a car in a parking lot.
the trajectory in x is not always in the same direction. sometime the car will go left.
the problem here is: sometime (not always!) the graph will no go left in x axis. You can see the two different result on the image https://imgur.com/Z53fNkt
any idea why?
the image at left is what i expect. at right is the same values , but i continue to plot data a little longer.
void TrackingResultsView::setupTrajectoryPlot()
{
QCustomPlot *customPlot = ui->qcp_trajectory;
customPlot->xAxis2->setVisible(true);
customPlot->xAxis2->setLabel("X-Position (pixel)");
customPlot->xAxis2->setRange(0, mModelPtr->frameSize().width());
customPlot->xAxis2->grid()->setVisible(true);
customPlot->xAxis->setRange(0, mModelPtr->frameSize().width());
customPlot->yAxis->setLabel("Y-Position (pixel)");
customPlot->yAxis->setRange(0, mModelPtr->frameSize().height());
customPlot->yAxis->setRangeReversed(true);
customPlot->yAxis2->setVisible(true);
customPlot->yAxis2->setRange(0, mModelPtr->frameSize().height());
customPlot->yAxis2->grid()->setVisible(true);
customPlot->yAxis2->setRangeReversed(true);
customPlot->addGraph(customPlot->xAxis2, customPlot->yAxis);
QVector<QVector<double>> data = createDataMap(mModelPtr->points());
customPlot->graph()->setData(data.at(0), data.at(1), true);
setTheme(customPlot, false);
}
thank you
(english is not my first langage)
The QCPGraph seems to be used for sorted data with only value per key. From QCustomPlot documentation, it looks like the QCPCurve would be a better match in order to plot a trajectory graph (multiple value for the same key).
From the QCPCurve description:
Unlike QCPGraph, plottables of this type may have multiple points with the same key coordinate, so their visual representation can have loops. This is realized by introducing a third coordinate t, which defines the order of the points described by the other two coordinates x and y.
here my new code with olivier help. its work!
QCustomPlot *customPlot = ui->qcp_trajectory;
customPlot->xAxis2->setVisible(true);
customPlot->xAxis2->setLabel("X-Position (pixel)");
customPlot->xAxis2->setRange(0, mModelPtr->frameSize().width());
customPlot->xAxis2->grid()->setVisible(true);
customPlot->xAxis->setRange(0, mModelPtr->frameSize().width());
customPlot->yAxis->setLabel("Y-Position (pixel)");
customPlot->yAxis->setRange(0, mModelPtr->frameSize().height());
customPlot->yAxis->setRangeReversed(true);
customPlot->yAxis2->setVisible(true);
customPlot->yAxis2->setRange(0, mModelPtr->frameSize().height());
customPlot->yAxis2->grid()->setVisible(true);
customPlot->yAxis2->setRangeReversed(true);
customPlot->addGraph(customPlot->xAxis2, customPlot->yAxis);
// create empty curve objects:
QCPCurve *trajectory = new QCPCurve(customPlot->xAxis2, customPlot->yAxis);
// generate the curve data points:
const int pointCount = mModelPtr->points().size();
QVector<QCPCurveData> datatrajectory(pointCount);
QVector<QVector<double>> data = createDataMap(mModelPtr->points());
for (int i = 0; i < pointCount ; ++i)
{
datatrajectory[i] = QCPCurveData(i, data.at(0).at(i), data.at(1).at(i));
}
trajectory->data()->set(datatrajectory, true);
setTheme(customPlot, false);

How to reset x and y axis in qcustomplot after replotting (Qt)

I am trying to crop my data, and replot only a selected part of the graph, but after the replot, the x axis still has the same values, and my graph starts at (0,0) instead of (cropped_starting_value_x, y).
How can I reset the values of the X axis? I've already tryed rescaleaxes, but it's not working.
Here I only have values from around 19 seconds.
QCPGraph *mainGraph = Customplott[0]->addGraph();
QCPGraph *SecondaryGraph = Customplott[0]->addGraph();
//QCPGraph *TertiaryGraph = Customplott[Number]->addGraph();
//mainGraph->setAdaptiveSampling(true);
mainGraph->setData(Time_temp, Measured_temp);
mainGraph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssNone, QPen(Qt::red), QBrush(Qt::white), 3));
mainGraph->setPen(QPen(Qt::blue)); // Magic only happens, when line width == 1
SecondaryGraph->setData(Time_temp, Output_temp);
SecondaryGraph->setPen(QPen(Qt::red));
Customplott[0]->setObjectName("YawRate");
Customplott[0]->xAxis->setLabel("Time [s]");
Customplott[0]->yAxis->setLabel("Yaw Rate");
/* rescale axes according to graph's data */
Customplott[0]->rescaleAxes();
mainGraph->rescaleAxes();
SecondaryGraph->rescaleAxes(true);
Customplott[0]->replot();
Every kind of help is highly appreciated!
You should use this method:
Customplott[0]->xAxis->setRange(minX, maxX);
(documentation here)

Display the plot values on mouse over. - Detect Scatter points

I am attempting to display the plot values of different points on my QCustomPlot in which I have a Line style of lsLine. I know i could set a mouse over signal on the QCustomPlot but that wont really help since I just need to be informed when the mouse is over my plotted line.My question is is there any way to find out if the mouse is over my scatter point. Is there a signal i could connect to that would tell me when the mouse is over a scatter point ?
Reimplement QCustomPlot::mouseMoveEvent or connect to QCustomPlot::mouseMove.
Then use axes' coordToPixel to translate (cursor) pixel coords to plot coords and search nearest points in your QCPDataMap with QMap::lowerBound(cursorX).
You can easily just connect a slot to the mouseMove signal that QCustomPlot emits. You can then use QCPAxis::pixelToCoord to find the coordinate :
connect(this, SIGNAL(mouseMove(QMouseEvent*)), this,SLOT(showPointToolTip(QMouseEvent*)));
void QCustomPlot::showPointToolTip(QMouseEvent *event)
{
int x = this->xAxis->pixelToCoord(event->pos().x());
int y = this->yAxis->pixelToCoord(event->pos().y());
setToolTip(QString("%1 , %2").arg(x).arg(y));
}
when You use datetime format (including more point per second) of X axis, then pixel to coord fails.
If you want to display coordinates between points, then this is the fastest way
maybe usefull (with connected signal QCustomplot::MouseMove)
void MainWindow::onMouseMoveGraph(QMouseEvent* evt)
{
int x = this->ui->customPlot->xAxis->pixelToCoord(evt->pos().x());
int y = this->ui->customPlot->yAxis->pixelToCoord(evt->pos().y());
qDebug()<<"pixelToCoord: "<<data.key<<data.value; //this is correct when step is greater 1 second
if (this->ui->customPlot->selectedGraphs().count()>0)
{
QCPGraph* graph = this->ui->customPlot->selectedGraphs().first();
QCPData data = graph->data()->lowerBound(x).value();
double dbottom = graph->valueAxis()->range().lower; //Yaxis bottom value
double dtop = graph->valueAxis()->range().upper; //Yaxis top value
long ptop = graph->valueAxis()->axisRect()->top(); //graph top margin
long pbottom = graph->valueAxis()->axisRect()->bottom(); //graph bottom position
// result for Y axis
double valueY = (evt->pos().y() - ptop) / (double)(pbottom - ptop)*(double)(dbottom - dtop) + dtop;
//or shortly for X-axis
double valueX = (evt->pos().x() - graph->keyAxis()->axisRect()->left()); //graph width in pixels
double ratio = (double)(graph->keyAxis()->axisRect()->right() - graph->keyAxis()->axisRect()->left()) / (double)(graph->keyAxis()->range().lower - graph->keyAxis()->range().upper); //ratio px->graph width
//and result for X-axis
valueX=-valueX / ratio + graph->keyAxis()->range().lower;
qDebug()<<"calculated:"<<valueX<<valueY;
}
}

3d model to fit in viewport

How does a 3D model handled unit wise ?
When i have a random model that i want to fit in my view port i dunno if it is too big or not, if i need to translate it to be in the middle...
I think a 3d object might have it's own origine.
You need to find a bounding volume, a shape that encloses all the object's vertices, for your object that is easier to work with than the object itself. Spheres are often used for this. Either the artist can define the sphere as part of the model information or you can work it out at run time. Calculating the optimal sphere is very hard, but you can get a good approximation using the following:
determine the min and max value of each point's x, y and z
for each vertex
min_x = min (min_x, vertex.x)
max_x = max (max_x, vertex.x)
min_y = min (min_y, vertex.y)
max_y = max (max_y, vertex.y)
min_z = min (min_z, vertex.z)
max_z = max (max_z, vertex.z)
sphere centre = (max_x + min_x) / 2, (max_y + min_y) / 2, (max_z + min_z) / 2
sphere radius = distance from centre to (max_x, max_y, max_z)
Using this sphere, determine the a world position that allows the sphere to be viewed in full - simple geometry will determine this.
Sorry, your question is very unclear. I suppose you want to center a 3D model to a viewport. You can achieve this by calculating the model's bounding box. To do this, traverse all polygons and get the minimum/maximum X/Y/Z coordinates. The bounding box given by the points (min_x,min_y,min_z) and (max_x,max_y,max_z) will contain the whole model. Now you can center the model by looking at the center of this box. With some further calculations (depending on your FOV) you can also get the left/right/upper/lower borders inside your viewport.
"so i tried to scale it down"
The best thing to do in this situation is not to transform your model at all! Leave it be. What you want to change is your camera.
First calculate the bounding box of your model somewhere in 3D space.
Next calculate the radius of it by taking the max( aabb.max.x-aabb.min.x, aabb.max.y-aabb.min.y, aabb.max.z-aabb.min.z ). It's crude but it gets the job done.
To center the object in the viewport place the camera at the object position. If Y is your forward axis subtract the radius from Y. If Z is the forward axis then subtract radius from it instead. Subtract a fudge factor to get you past the pesky near plane so your model doesn't clip out. I use quaternions in my engine with a nice lookat() method. So call lookat() and pass in the center of the bounding box. Voila! You're object is centered in the viewport regardless of where it is in the world.
This always places the camera axis aligned so you might want to get fancy and transform the camera into model space instead, subtract off the radius, then lookat() the center again. Then you're always looking at the back of the model. The key is always the lookat().
Here's some example code from my engine. It checks to see if we're trying to frame a chunk of static terrain, if so look down from a height, or a light or a static mesh. A visual is anything that draws in the scene and there are dozens of different types. A Visual::Instance is a copy of the visual, or where to draw it.
void EnvironmentView::frameSelected(){
if( m_tSelection.toInstance() ){
Visual::Instance& I = m_tSelection.toInstance().cast();
Visual* pVisual = I.toVisual();
if( pVisual->isa( StaticTerrain::classid )){
toEditorCamera().toL2W().setPosition( pt3( 0, 0, 50000 ));
toEditorCamera().lookat( pt3( 0 ));
}else if( I.toFlags()->bIsLight ){
Visual::LightInstance& L = static_cast<Visual::LightInstance&>( I );
qst3& L2W = L.toL2W();
const sphere s( L2W.toPosition(), L2W.toScale() );
const f32 y =-(s.toCenter()+s.toRadius()).y();
const f32 z = (s.toCenter()+s.toRadius()).y();
qst3& camL2W = toEditorCamera().toL2W();
camL2W.setPosition(s.toCenter()+pt3( 0, y, z ));//45 deg above
toEditorCamera().lookat( s.toCenter() );
}else{
Mesh::handle hMesh = pVisual->getMesh();
if( hMesh ){
qst3& L2W = m_tSelection.toInstance()->toL2W();
vec4x4 M;
L2W.getMatrix( M );
aabb3 b0 = hMesh->toBounds();
b0.min = M * b0.min;
b0.max = M * b0.max;
aabb3 b1;
b1 += b0.min;
b1 += b0.max;
const sphere s( b1.toSphere() );
const f32 y =-(s.toCenter()+s.toRadius()*2.5f).y();
const f32 z = (s.toCenter()+s.toRadius()*2.5f).y();
qst3& camL2W = toEditorCamera().toL2W();
camL2W.setPosition( L2W.toPosition()+pt3( 0, y, z ));//45 deg above
toEditorCamera().lookat( b1.toOrigin() );
}
}
}
}