replace JKQTPVectorFieldGraph::setAutoscaleLengthFactor() by an enum-property JKQTPVectorFieldGraph::setVectorLengthMode() and added a mode that ignores the length completely, the vectorfield example was also modified to better show this new property
@ -274,7 +274,7 @@ if(JKQtPlotter_BUILD_EXAMPLES)
|
|||||||
boxplot/JKQTPBoxplotVerticalGraph,JKQTPBoxplotHorizontalGraph/--iteratefunctorsteps--iteratefunctorsteps_suppressinitial--smallscreenshotplot
|
boxplot/JKQTPBoxplotVerticalGraph,JKQTPBoxplotHorizontalGraph/--iteratefunctorsteps--iteratefunctorsteps_suppressinitial--smallscreenshotplot
|
||||||
second_axis/JKQTBasePlotter_addSecondaryYAxis,JKQTBasePlotter_addSecondaryXAxis
|
second_axis/JKQTBasePlotter_addSecondaryYAxis,JKQTBasePlotter_addSecondaryXAxis
|
||||||
graphlabels/JKQTPGLabelAwayFromXAxis,JKQTPGLabelAwayFromYAxis,JKQTPGLabelTowardsXAxis,JKQTPGLabelTowardsYAxis,JKQTPGLabelAboveData,JKQTPGLabelRightHandSide,JKQTPGLabelBelowData,JKQTPGLabelLeftHandSide,JKQTPGLSimpleBox,JKQTPGLSimpleBoxVertical,JKQTPGLSimpleBoxAndLine,JKQTPGLSimpleBoxAndLineVertical,JKQTPGLSimpleBoxAndLineONLYLABELS,JKQTPGLSimpleBoxAndLineONLYLABELSVertical/--iteratefunctorsteps--smallscreenshotplot
|
graphlabels/JKQTPGLabelAwayFromXAxis,JKQTPGLabelAwayFromYAxis,JKQTPGLabelTowardsXAxis,JKQTPGLabelTowardsYAxis,JKQTPGLabelAboveData,JKQTPGLabelRightHandSide,JKQTPGLabelBelowData,JKQTPGLabelLeftHandSide,JKQTPGLSimpleBox,JKQTPGLSimpleBoxVertical,JKQTPGLSimpleBoxAndLine,JKQTPGLSimpleBoxAndLineVertical,JKQTPGLSimpleBoxAndLineONLYLABELS,JKQTPGLSimpleBoxAndLineONLYLABELSVertical/--iteratefunctorsteps--smallscreenshotplot
|
||||||
vectorfield/JKQTPVectorFieldGraph,JKQTPVectorFieldGraphAnchorBottom,JKQTPVectorFieldGraphAnchorMid,JKQTPVectorFieldGraphAnchorTip/--iteratefunctorsteps
|
vectorfield/JKQTPVectorFieldGraph,JKQTPVectorFieldGraphAnchorBottom,JKQTPVectorFieldGraphAnchorMid,JKQTPVectorFieldGraphAnchorTip,JKQTPVectorFieldGraphAutoscaleLength,JKQTPVectorFieldGraphLengthFromData,JKQTPVectorFieldGraphIgnoreLength/--iteratefunctorsteps
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 52 KiB |
BIN
doc/images/JKQTPVectorFieldGraphAutoscaleLength.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
doc/images/JKQTPVectorFieldGraphIgnoreLength.png
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
doc/images/JKQTPVectorFieldGraphLengthFromData.png
Normal file
After Width: | Height: | Size: 70 KiB |
@ -11,17 +11,17 @@ Here is a short summary of the important parts of the code:
|
|||||||
JKQTPDatastore* ds=plot.getDatastore();
|
JKQTPDatastore* ds=plot.getDatastore();
|
||||||
|
|
||||||
// 2. make up some arbitrary data to be used for plotting
|
// 2. make up some arbitrary data to be used for plotting
|
||||||
// this generates a 2D grid of x/y-coordinates and then calculates dx=cos(y) and dy=sin(x)
|
// this generates a 2D grid of x/y-coordinates and then calculates dx=cos(y)*sqrt(x/3.0) and dy=sin(x)*sqrt(x/3.0)
|
||||||
const auto columnXY=ds->addLinearGridColumns(NX, 0, 6, NY, -3, 3,"x","y");
|
const auto columnXY=ds->addLinearGridColumns(NX, 0, 6, NY, -3, 3,"x","y");
|
||||||
const auto columnDX=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return sin(y); });
|
const auto columnDX=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return sin(y)*sqrt(x/3.0); });
|
||||||
const auto columnDY=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return cos(x); });
|
const auto columnDY=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return cos(x)*sqrt(x/3.0); });
|
||||||
|
|
||||||
// 3. create JKQTPVectorFieldGraph to display the data:
|
// 3. create JKQTPVectorFieldGraph to display the data:
|
||||||
JKQTPVectorFieldGraph* graph1=new JKQTPVectorFieldGraph(&plot);
|
JKQTPVectorFieldGraph* graph1=new JKQTPVectorFieldGraph(&plot);
|
||||||
graph1->setXYColumns(columnXY);
|
graph1->setXYColumns(columnXY);
|
||||||
graph1->setDxColumn(columnDX);
|
graph1->setDxColumn(columnDX);
|
||||||
graph1->setDyColumn(columnDY);
|
graph1->setDyColumn(columnDY);
|
||||||
graph1->setTitle(QObject::tr("$\\vec{f}(x,y)=\\bigl[\\sin(y), \\cos(x)\\bigr]^\\mathrm{T}$"));
|
graph1->setTitle(QObject::tr("$\\vec{f}(x,y)=\\bigl[\\sin(y)\\cdot\\sqrt{x/3}, \\cos(x)\\cdot\\sqrt{x/3}\\bigr]^\\mathrm{T}$"));
|
||||||
|
|
||||||
// 4. add the graphs to the plot, so it is actually displayed
|
// 4. add the graphs to the plot, so it is actually displayed
|
||||||
plot.addGraph(graph1);
|
plot.addGraph(graph1);
|
||||||
@ -33,3 +33,23 @@ The result looks like this:
|
|||||||
![vectorfield](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/vectorfield.png)
|
![vectorfield](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/vectorfield.png)
|
||||||
|
|
||||||
|
|
||||||
|
By default, the length of the drawn vector is determined from the actual length in the data via an autoscaling algorithm that is supposed to prevent the vectors from overlapping.
|
||||||
|
But you can modify this behaviour by adding a line
|
||||||
|
|
||||||
|
```.cpp
|
||||||
|
graph1->setVectorLengthMode(JKQTPVectorFieldGraph::LengthFromData);
|
||||||
|
```
|
||||||
|
|
||||||
|
which will use the given lengths directly (only scaled by an optional factor defined by JKQTPVectorFieldGraph::setLengthScaleFactor() ). The result then looks like this:
|
||||||
|
|
||||||
|
![vectorfield](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/doc/images/JKQTPVectorFieldGraphLengthFromData.png)
|
||||||
|
|
||||||
|
Alternatively you can also set
|
||||||
|
|
||||||
|
```.cpp
|
||||||
|
graph1->setVectorLengthMode(JKQTPVectorFieldGraph::IgnoreLength);
|
||||||
|
```
|
||||||
|
|
||||||
|
which will draw all vectors with the same length. You can scale this length by setting JKQTPVectorFieldGraph::setAutoscaleLengthFactor() ). The result then looks like this:
|
||||||
|
|
||||||
|
![vectorfield](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/doc/images/JKQTPVectorFieldGraphIgnoreLength.png)
|
||||||
|
@ -32,10 +32,10 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
|
|
||||||
// 2. make up some arbitrary data to be used for plotting
|
// 2. make up some arbitrary data to be used for plotting
|
||||||
// this generates a 2D grid of x/y-coordinates and then calculates dx=cos(y) and dy=sin(x)
|
// this generates a 2D grid of x/y-coordinates and then calculates dx=cos(y)*sqrt(x/3.0) and dy=sin(x)*sqrt(x/3.0)
|
||||||
const auto columnXY=ds->addLinearGridColumns(NX, 0, 6, NY, -3, 3,"x","y");
|
const auto columnXY=ds->addLinearGridColumns(NX, 0, 6, NY, -3, 3,"x","y");
|
||||||
const auto columnDX=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return sin(y); });
|
const auto columnDX=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return sin(y)*sqrt(x/3.0); });
|
||||||
const auto columnDY=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return cos(x); });
|
const auto columnDY=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return cos(x)*sqrt(x/3.0); });
|
||||||
|
|
||||||
|
|
||||||
// 3. create JKQTPVectorFieldGraph to display the data:
|
// 3. create JKQTPVectorFieldGraph to display the data:
|
||||||
@ -43,7 +43,7 @@ int main(int argc, char* argv[])
|
|||||||
graph1->setXYColumns(columnXY);
|
graph1->setXYColumns(columnXY);
|
||||||
graph1->setDxColumn(columnDX);
|
graph1->setDxColumn(columnDX);
|
||||||
graph1->setDyColumn(columnDY);
|
graph1->setDyColumn(columnDY);
|
||||||
graph1->setTitle(QObject::tr("$\\vec{f}(x,y)=\\bigl[\\sin(y), \\cos(x)\\bigr]^\\mathrm{T}$"));
|
graph1->setTitle(QObject::tr("$\\vec{f}(x,y)=\\bigl[\\sin(y)\\cdot\\sqrt{x/3}, \\cos(x)\\cdot\\sqrt{x/3}\\bigr]^\\mathrm{T}$"));
|
||||||
|
|
||||||
// 4. add the graphs to the plot, so it is actually displayed
|
// 4. add the graphs to the plot, so it is actually displayed
|
||||||
plot.addGraph(graph1);
|
plot.addGraph(graph1);
|
||||||
@ -81,5 +81,21 @@ int main(int argc, char* argv[])
|
|||||||
plot.redrawPlot();
|
plot.redrawPlot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.addExportStepFunctor([&](){
|
||||||
|
graph1->setAnchorPoint(JKQTPVectorFieldGraph::AnchorBottom);
|
||||||
|
graph1->setVectorLengthMode(JKQTPVectorFieldGraph::AutoscaleLength);
|
||||||
|
plot.redrawPlot();
|
||||||
|
});
|
||||||
|
app.addExportStepFunctor([&](){
|
||||||
|
graph1->setAnchorPoint(JKQTPVectorFieldGraph::AnchorBottom);
|
||||||
|
graph1->setVectorLengthMode(JKQTPVectorFieldGraph::LengthFromData);
|
||||||
|
plot.redrawPlot();
|
||||||
|
});
|
||||||
|
app.addExportStepFunctor([&](){
|
||||||
|
graph1->setAnchorPoint(JKQTPVectorFieldGraph::AnchorBottom);
|
||||||
|
graph1->setVectorLengthMode(JKQTPVectorFieldGraph::IgnoreLength);
|
||||||
|
plot.redrawPlot();
|
||||||
|
});
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include "jkqtcommon/jkqtpdrawingtools.h"
|
#include "jkqtcommon/jkqtpdrawingtools.h"
|
||||||
|
#include "jkqtmath/jkqtpstatisticstools.h"
|
||||||
#include "jkqtplotter/jkqtptools.h"
|
#include "jkqtplotter/jkqtptools.h"
|
||||||
|
|
||||||
#define SmallestGreaterZeroCompare_xvsgz() if ((xvsgz>10.0*DBL_MIN)&&((smallestGreaterZero<10.0*DBL_MIN) || (xvsgz<smallestGreaterZero))) smallestGreaterZero=xvsgz;
|
#define SmallestGreaterZeroCompare_xvsgz() if ((xvsgz>10.0*DBL_MIN)&&((smallestGreaterZero<10.0*DBL_MIN) || (xvsgz<smallestGreaterZero))) smallestGreaterZero=xvsgz;
|
||||||
@ -33,9 +34,9 @@
|
|||||||
|
|
||||||
JKQTPVectorFieldGraph::JKQTPVectorFieldGraph(JKQTBasePlotter *parent):
|
JKQTPVectorFieldGraph::JKQTPVectorFieldGraph(JKQTBasePlotter *parent):
|
||||||
JKQTPXYAndVectorGraph(parent),
|
JKQTPXYAndVectorGraph(parent),
|
||||||
m_autoscaleLength(true),
|
m_vectorLengthMode(AutoscaleLength),
|
||||||
m_autoscaleLengthFactor(0.9),
|
m_autoscaleLengthFactor(0.8),
|
||||||
m_lengthScaleFactor(1),
|
m_lengthScaleFactor(1.0),
|
||||||
m_anchorPoint(AnchorBottom)
|
m_anchorPoint(AnchorBottom)
|
||||||
{
|
{
|
||||||
initDecoratedLineStyle(parent, parentPlotStyle, JKQTPPlotStyleType::Default);
|
initDecoratedLineStyle(parent, parentPlotStyle, JKQTPPlotStyleType::Default);
|
||||||
@ -72,17 +73,21 @@ void JKQTPVectorFieldGraph::draw(JKQTPEnhancedPainter &painter)
|
|||||||
double scale=1;
|
double scale=1;
|
||||||
if (getIndexRange(imin, imax)) {
|
if (getIndexRange(imin, imax)) {
|
||||||
// first determine (auto-scale) factor
|
// first determine (auto-scale) factor
|
||||||
if (m_autoscaleLength) {
|
if (m_vectorLengthMode==AutoscaleLength || m_vectorLengthMode==IgnoreLength) {
|
||||||
double avgVecLength=0;
|
double avgVecLength=0;
|
||||||
double NDatapoints=0;
|
double NDatapoints=0;
|
||||||
double xmin=0, xmax=0,ymin=0,ymax=0;
|
double xmin=0, xmax=0,ymin=0,ymax=0;
|
||||||
|
QVector<double> lengths;
|
||||||
|
lengths.reserve(imax-imin);
|
||||||
for (int iii=imin; iii<imax; iii++) {
|
for (int iii=imin; iii<imax; iii++) {
|
||||||
const int i=qBound(imin, getDataIndex(iii), imax);
|
const int i=qBound(imin, getDataIndex(iii), imax);
|
||||||
const double xv=datastore->get(static_cast<size_t>(xColumn),static_cast<size_t>(i));
|
const double xv=datastore->get(static_cast<size_t>(xColumn),static_cast<size_t>(i));
|
||||||
const double yv=datastore->get(static_cast<size_t>(yColumn),static_cast<size_t>(i));
|
const double yv=datastore->get(static_cast<size_t>(yColumn),static_cast<size_t>(i));
|
||||||
const QPointF vecv=getVectorDxDy(i);
|
const QPointF vecv=getVectorDxDy(i);
|
||||||
if (JKQTPIsOKFloat(xv) && JKQTPIsOKFloat(yv) && JKQTPIsOKFloat(vecv)) {
|
if (JKQTPIsOKFloat(xv) && JKQTPIsOKFloat(yv) && JKQTPIsOKFloat(vecv)) {
|
||||||
avgVecLength+=sqrt(jkqtp_sqr(vecv.x())+jkqtp_sqr(vecv.y()));
|
const double l=sqrt(jkqtp_sqr(vecv.x())+jkqtp_sqr(vecv.y()));
|
||||||
|
lengths<<l;
|
||||||
|
avgVecLength+=l;
|
||||||
if (NDatapoints==0) {
|
if (NDatapoints==0) {
|
||||||
xmin=xmax=xv;
|
xmin=xmax=xv;
|
||||||
ymin=ymax=yv;
|
ymin=ymax=yv;
|
||||||
@ -95,10 +100,12 @@ void JKQTPVectorFieldGraph::draw(JKQTPEnhancedPainter &painter)
|
|||||||
NDatapoints++;
|
NDatapoints++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
avgVecLength/=NDatapoints;
|
//avgVecLength/=NDatapoints;
|
||||||
|
avgVecLength=jkqtpstatQuantile(lengths.begin(), lengths.end(), 0.9);
|
||||||
const double plotsize=qMax(fabs(xmax-xmin),fabs(ymax-ymin));
|
const double plotsize=qMax(fabs(xmax-xmin),fabs(ymax-ymin));
|
||||||
const double aproxNPerSide=sqrt(NDatapoints);
|
const double aproxNPerSide=sqrt(NDatapoints);
|
||||||
scale=plotsize/aproxNPerSide/avgVecLength*m_autoscaleLengthFactor;
|
if (m_vectorLengthMode==IgnoreLength) scale=plotsize/aproxNPerSide*m_autoscaleLengthFactor; // we can assume that the elngths of all vectors have been normalized beforehand
|
||||||
|
else scale=plotsize/aproxNPerSide/avgVecLength*m_autoscaleLengthFactor;
|
||||||
} else {
|
} else {
|
||||||
scale=m_lengthScaleFactor;
|
scale=m_lengthScaleFactor;
|
||||||
}
|
}
|
||||||
@ -110,7 +117,15 @@ void JKQTPVectorFieldGraph::draw(JKQTPEnhancedPainter &painter)
|
|||||||
const double yv=datastore->get(static_cast<size_t>(yColumn),static_cast<size_t>(i));
|
const double yv=datastore->get(static_cast<size_t>(yColumn),static_cast<size_t>(i));
|
||||||
const double x=transformX(xv);
|
const double x=transformX(xv);
|
||||||
const double y=transformY(yv);
|
const double y=transformY(yv);
|
||||||
const QPointF vecv=getVectorDxDy(i);
|
const QPointF vecv=[&]() {
|
||||||
|
QPointF vec=getVectorDxDy(i);
|
||||||
|
if (m_vectorLengthMode==IgnoreLength) {
|
||||||
|
const double veclen=sqrt(jkqtp_sqr(vec.x())+jkqtp_sqr(vec.y()));
|
||||||
|
if (qFuzzyIsNull(veclen)) vec=QPointF(0,0);
|
||||||
|
else vec/=veclen; // normalize vector
|
||||||
|
}
|
||||||
|
return vec;
|
||||||
|
}();
|
||||||
const QLineF l=[&]() {
|
const QLineF l=[&]() {
|
||||||
switch (m_anchorPoint) {
|
switch (m_anchorPoint) {
|
||||||
case AnchorBottom: return QLineF(x,y,transformX(xv+scale*vecv.x()),transformY(yv+scale*vecv.y()));
|
case AnchorBottom: return QLineF(x,y,transformX(xv+scale*vecv.x()),transformY(yv+scale*vecv.y()));
|
||||||
@ -119,7 +134,7 @@ void JKQTPVectorFieldGraph::draw(JKQTPEnhancedPainter &painter)
|
|||||||
}
|
}
|
||||||
return QLineF(JKQTP_NAN,JKQTP_NAN,JKQTP_NAN,JKQTP_NAN);
|
return QLineF(JKQTP_NAN,JKQTP_NAN,JKQTP_NAN,JKQTP_NAN);
|
||||||
}();
|
}();
|
||||||
if (JKQTPIsOKFloat(l)) {
|
if (JKQTPIsOKFloat(l) && l.length()>0) {
|
||||||
JKQTPPlotDecoratedLine(painter,l, getTailDecoratorStyle(), calcTailDecoratorSize(p.widthF()), getHeadDecoratorStyle(), calcHeadDecoratorSize(p.widthF()));
|
JKQTPPlotDecoratedLine(painter,l, getTailDecoratorStyle(), calcTailDecoratorSize(p.widthF()), getHeadDecoratorStyle(), calcHeadDecoratorSize(p.widthF()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,14 +159,14 @@ QColor JKQTPVectorFieldGraph::getKeyLabelColor() const
|
|||||||
return getLineColor();
|
return getLineColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JKQTPVectorFieldGraph::getAutoscaleLength() const
|
JKQTPVectorFieldGraph::VectorLengthMode JKQTPVectorFieldGraph::getVectorLengthMode() const
|
||||||
{
|
{
|
||||||
return m_autoscaleLength;
|
return m_vectorLengthMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JKQTPVectorFieldGraph::setAutoscaleLength(bool newAutoscaleLength)
|
void JKQTPVectorFieldGraph::setVectorLengthMode(VectorLengthMode newMode)
|
||||||
{
|
{
|
||||||
m_autoscaleLength = newAutoscaleLength;
|
m_vectorLengthMode = newMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
double JKQTPVectorFieldGraph::getAutoscaleLengthFactor() const
|
double JKQTPVectorFieldGraph::getAutoscaleLengthFactor() const
|
||||||
|
@ -43,7 +43,7 @@ class JKQTPDatastore;
|
|||||||
corresponding to their magnitude.
|
corresponding to their magnitude.
|
||||||
\ingroup jkqtplotter_vectorfieldgraphs
|
\ingroup jkqtplotter_vectorfieldgraphs
|
||||||
|
|
||||||
\note This type of plot is sometimes also refered to as quiver plot (e.g. in Matlab or matplotlib)
|
\note This type of plot is sometimes also refered to as <b>quiver plot</b> (e.g. in Matlab or matplotlib)
|
||||||
|
|
||||||
\image html JKQTPVectorFieldGraph.png
|
\image html JKQTPVectorFieldGraph.png
|
||||||
|
|
||||||
@ -54,35 +54,62 @@ class JKQTPDatastore;
|
|||||||
JKQTPDatastore* ds=plot.getDatastore();
|
JKQTPDatastore* ds=plot.getDatastore();
|
||||||
|
|
||||||
// 2. make up some arbitrary data to be used for plotting
|
// 2. make up some arbitrary data to be used for plotting
|
||||||
// this generates a 2D grid of x/y-coordinates and then calculates dx=cos(y) and dy=sin(x)
|
// this generates a 2D grid of x/y-coordinates and then calculates dx=cos(y)*sqrt(x/3.0) and dy=sin(x)*sqrt(x/3.0)
|
||||||
const auto columnXY=ds->addLinearGridColumns(NX, 0, 6, NY, -3, 3,"x","y");
|
const auto columnXY=ds->addLinearGridColumns(NX, 0, 6, NY, -3, 3,"x","y");
|
||||||
const auto columnDX=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return sin(y); });
|
const auto columnDX=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return sin(y)*sqrt(x/3.0); });
|
||||||
const auto columnDY=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return cos(x); });
|
const auto columnDY=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return cos(x)*sqrt(x/3.0); });
|
||||||
|
|
||||||
// 3. create JKQTPVectorFieldGraph to display the data:
|
// 3. create JKQTPVectorFieldGraph to display the data:
|
||||||
JKQTPVectorFieldGraph* graph1=new JKQTPVectorFieldGraph(&plot);
|
JKQTPVectorFieldGraph* graph1=new JKQTPVectorFieldGraph(&plot);
|
||||||
graph1->setXYColumns(columnXY);
|
graph1->setXYColumns(columnXY);
|
||||||
graph1->setDxColumn(columnDX);
|
graph1->setDxColumn(columnDX);
|
||||||
graph1->setDyColumn(columnDY);
|
graph1->setDyColumn(columnDY);
|
||||||
graph1->setTitle(QObject::tr("$\\vec{f}(x,y)=\\bigl[\\sin(y), \\cos(x)\\bigr]^\\mathrm{T}$"));
|
graph1->setTitle(QObject::tr("$\\vec{f}(x,y)=\\bigl[\\sin(y)\\cdot\\sqrt{x/3}, \\cos(x)\\cdot\\sqrt{x/3}\\bigr]^\\mathrm{T}$"));
|
||||||
|
|
||||||
// 4. add the graphs to the plot, so it is actually displayed
|
// 4. add the graphs to the plot, so it is actually displayed
|
||||||
plot.addGraph(graph1);
|
plot.addGraph(graph1);
|
||||||
\endcode
|
\endcode
|
||||||
|
|
||||||
|
You have several options to influence the way the vectors are drawn:
|
||||||
|
1. You can change the tip shape (and actually also the tail) of the vector by using the methods from
|
||||||
|
JKQTPGraphDecoratedLineStyleMixin, e.g. use JKQTPGraphDecoratedLineStyleMixin::setHeadDecoratorStyle()
|
||||||
|
to set another shape for the vector's tip, or modify JKQTPGraphDecoratedLineStyleMixin::setHeadDecoratorSizeFactor()
|
||||||
|
to modify the size of the vector's head. the vector line width, color etz. can be modified by the
|
||||||
|
methods from JKQTPGraphLineStyleMixin, like JKQTPGraphLineStyleMixin::setLineColor() or
|
||||||
|
JKQTPGraphLineStyleMixin::setLineWidth().
|
||||||
|
2. By default the length of the drawn vectors corresponds to the actual length of the vector data,
|
||||||
|
but is modified by an autoscaling algorithm that should prevent them from overlapping.
|
||||||
|
This behaviour can be changed by setVectorLengthMode() with the different options described in
|
||||||
|
VectorLengthMode.
|
||||||
|
3. By default, vector start at \c (x,y). But you can also make them be centered around or
|
||||||
|
point to \c (x,y) . This can be set by setAnchorPoint().
|
||||||
|
.
|
||||||
|
|
||||||
\see \ref JKQTPlotterVectorFieldExample , JKQTPGraphDecoratedLineStyleMixin , JKQTPXYAndVectorGraph
|
\see \ref JKQTPlotterVectorFieldExample , JKQTPGraphDecoratedLineStyleMixin , JKQTPXYAndVectorGraph
|
||||||
|
|
||||||
*/
|
*/
|
||||||
class JKQTPLOTTER_LIB_EXPORT JKQTPVectorFieldGraph: public JKQTPXYAndVectorGraph, public JKQTPGraphDecoratedLineStyleMixin {
|
class JKQTPLOTTER_LIB_EXPORT JKQTPVectorFieldGraph: public JKQTPXYAndVectorGraph, public JKQTPGraphDecoratedLineStyleMixin {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
/** \brief indicates the position of the point \c (x,y) relative to the vector */
|
||||||
enum VectorAnchorPoint {
|
enum VectorAnchorPoint {
|
||||||
AnchorBottom, //!< \brief this is the default: the vector starts at (x,y) \image html JKQTPVectorFieldGraphAnchorBottom.png
|
AnchorBottom, //!< \brief this is the default: the vector starts at \c (x,y) \image html JKQTPVectorFieldGraphAnchorBottom.png
|
||||||
AnchorMid, //!< \brief the vector's mid is at (x,y) \image html JKQTPVectorFieldGraphAnchorMid.png
|
AnchorMid, //!< \brief the vector's mid is at \c (x,y) \image html JKQTPVectorFieldGraphAnchorMid.png
|
||||||
AnchorTip //!< \brief the vector ends at (x,y) \image html JKQTPVectorFieldGraphAnchorTip.png
|
AnchorTip //!< \brief the vector ends at \c (x,y) \image html JKQTPVectorFieldGraphAnchorTip.png
|
||||||
};
|
};
|
||||||
Q_ENUM(VectorAnchorPoint)
|
Q_ENUM(VectorAnchorPoint)
|
||||||
|
|
||||||
|
/** \brief indicates how the drawn vector's length is calculated from the data
|
||||||
|
*
|
||||||
|
* \see documentation of m_vectorLengthMode for details
|
||||||
|
*/
|
||||||
|
enum VectorLengthMode {
|
||||||
|
AutoscaleLength, //!< \brief this is the default: vector lengths are autoscaled, so they don't overlap (in first approximation) \image html JKQTPVectorFieldGraphAnchorBottom.png
|
||||||
|
LengthFromData, //!< \brief the vector's length is determined by the data directly \image html JKQTPVectorFieldGraphAnchorMid.png
|
||||||
|
IgnoreLength //!< \brief all vectors have the same length \image html JKQTPVectorFieldGraphAnchorTip.png
|
||||||
|
};
|
||||||
|
Q_ENUM(VectorLengthMode)
|
||||||
|
|
||||||
/** \brief class constructor */
|
/** \brief class constructor */
|
||||||
explicit JKQTPVectorFieldGraph(JKQTBasePlotter* parent=nullptr);
|
explicit JKQTPVectorFieldGraph(JKQTBasePlotter* parent=nullptr);
|
||||||
/** \brief class constructor */
|
/** \brief class constructor */
|
||||||
@ -95,10 +122,10 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPVectorFieldGraph: public JKQTPXYAndVectorGraph
|
|||||||
/** \brief returns the color to be used for the key label */
|
/** \brief returns the color to be used for the key label */
|
||||||
virtual QColor getKeyLabelColor() const override;
|
virtual QColor getKeyLabelColor() const override;
|
||||||
|
|
||||||
/** \copydoc m_autoscaleLength */
|
/** \copydoc m_vectorLengthMode */
|
||||||
bool getAutoscaleLength() const;
|
VectorLengthMode getVectorLengthMode() const;
|
||||||
/** \copydoc m_autoscaleLength */
|
/** \copydoc m_vectorLengthMode */
|
||||||
void setAutoscaleLength(bool newAutoscaleLength);
|
void setVectorLengthMode(VectorLengthMode newMode);
|
||||||
|
|
||||||
/** \copydoc m_autoscaleLengthFactor */
|
/** \copydoc m_autoscaleLengthFactor */
|
||||||
double getAutoscaleLengthFactor() const;
|
double getAutoscaleLengthFactor() const;
|
||||||
@ -116,31 +143,38 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPVectorFieldGraph: public JKQTPXYAndVectorGraph
|
|||||||
/** \copydoc m_anchorPoint */
|
/** \copydoc m_anchorPoint */
|
||||||
void setAnchorPoint(VectorAnchorPoint newAnchorPoint);
|
void setAnchorPoint(VectorAnchorPoint newAnchorPoint);
|
||||||
|
|
||||||
Q_PROPERTY(bool autoscaleLength READ getAutoscaleLength WRITE setAutoscaleLength )
|
Q_PROPERTY(VectorLengthMode vectorLengthMode READ getVectorLengthMode WRITE setVectorLengthMode )
|
||||||
Q_PROPERTY(bool autoscaleLengthFactor READ getAutoscaleLengthFactor WRITE setAutoscaleLengthFactor )
|
Q_PROPERTY(bool autoscaleLengthFactor READ getAutoscaleLengthFactor WRITE setAutoscaleLengthFactor )
|
||||||
Q_PROPERTY(double lengthScaleFactor READ getLengthScaleFactor WRITE setLengthScaleFactor )
|
Q_PROPERTY(double lengthScaleFactor READ getLengthScaleFactor WRITE setLengthScaleFactor )
|
||||||
Q_PROPERTY(VectorAnchorPoint anchorPoint READ getAnchorPoint WRITE setAnchorPoint )
|
Q_PROPERTY(VectorAnchorPoint anchorPoint READ getAnchorPoint WRITE setAnchorPoint )
|
||||||
protected:
|
protected:
|
||||||
private:
|
private:
|
||||||
/** \brief enables or disables the autoscaling of vector lengths
|
/** \brief indicates how the length of the drawn vectors are determined from the data
|
||||||
*
|
*
|
||||||
* If disabled (\c false ) the vector is drawn from \c (x,y) to \c (x+dx,y+dy)*m_lengthScaleFactor ,
|
* Several modes are possible:
|
||||||
* otherweise to \c (x+dx,y+dy)*autoscale*m_autoscaleLengthFactor .
|
* - If \c == LengthFromData the vector is drawn from \c (x,y) to \c (x+dx,y+dy)*m_lengthScaleFactor.
|
||||||
* The autoscaled length is calculated by a siple algorithm that uses the average vector length in the data:
|
* \image html JKQTPVectorFieldGraphAutoscaleLength.png
|
||||||
* \c autoscale=plotwidth/VectorPerWidth/avgVectorLength .
|
* - If \c == AutoscaleLength the vector is drawn to \c (x+dx,y+dy)*autoscale*m_autoscaleLengthFactor .
|
||||||
|
* The autoscaled length is calculated by a simple algorithm that uses the 90% quantile of vector length in the data \c q90VectorLength :
|
||||||
|
* \c autoscale=plotwidth/VectorPerWidth/q90VectorLength .
|
||||||
|
* \image html JKQTPVectorFieldGraphLengthFromData.png
|
||||||
|
* - If \c == IgnoreLength all vectors are drawn with the same length, which is determined from \c autoscale*m_autoscaleLengthFactor
|
||||||
|
* where \c autoscale is defined as above.
|
||||||
|
* \image html JKQTPVectorFieldGraphIgnoreLength.png
|
||||||
|
* .
|
||||||
*
|
*
|
||||||
* \see m_autoscaleFactor, m_autoscaleLengthFactor, setAutoscaleLength(), getAutoscaleLength()
|
* \see VectorLengthMode, setVectorLengthMode(), getVectorLengthMode(), m_autoscaleFactor, m_autoscaleLengthFactor
|
||||||
*/
|
*/
|
||||||
bool m_autoscaleLength;
|
VectorLengthMode m_vectorLengthMode;
|
||||||
/** \brief a scaling factor that can be used to modify the result of the autoscaling algorithm (m_autoscaleLength \c ==true)
|
/** \brief a scaling factor that can be used to modify the result of the autoscaling algorithm (m_vectorLengthMode \c ==AutoscaleLength)
|
||||||
*
|
*
|
||||||
* The vector length is further scaled by this value.
|
* The vector length is further scaled by this value.
|
||||||
* \see m_autoscaleLength, setAutoscaleFactor(), getAutoscaleFactor()
|
* \see m_vectorLengthMode, setAutoscaleFactor(), getAutoscaleFactor()
|
||||||
*/
|
*/
|
||||||
double m_autoscaleLengthFactor;
|
double m_autoscaleLengthFactor;
|
||||||
/** \brief if m_autoscaleLength \c ==false, this is the scale-factor used to calculate the vector length
|
/** \brief if m_vectorLengthMode \c ==false, this is the scale-factor used to calculate the vector length
|
||||||
*
|
*
|
||||||
* \see setLengthScaleFactor(), getLengthScaleFactor(), m_autoscaleLength
|
* \see setLengthScaleFactor(), getLengthScaleFactor(), m_vectorLengthMode
|
||||||
*/
|
*/
|
||||||
double m_lengthScaleFactor;
|
double m_lengthScaleFactor;
|
||||||
/** \brief defines where the vector is anchored
|
/** \brief defines where the vector is anchored
|
||||||
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 15 KiB |