Skip to content

Commit

Permalink
replace JKQTPVectorFieldGraph::setAutoscaleLengthFactor() by an enum-…
Browse files Browse the repository at this point in the history
…property JKQTPVectorFieldGraph::setVectorLengthMode() and added a mode that ignores the length completely, the vectorfield example was also modified to better show this new property
  • Loading branch information
jkriege2 committed Feb 9, 2024
1 parent 1ff97e1 commit 627c329
Show file tree
Hide file tree
Showing 14 changed files with 133 additions and 48 deletions.
2 changes: 1 addition & 1 deletion doc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ if(JKQtPlotter_BUILD_EXAMPLES)
boxplot/JKQTPBoxplotVerticalGraph,JKQTPBoxplotHorizontalGraph/--iteratefunctorsteps--iteratefunctorsteps_suppressinitial--smallscreenshotplot
second_axis/JKQTBasePlotter_addSecondaryYAxis,JKQTBasePlotter_addSecondaryXAxis
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
)


Expand Down
Binary file modified doc/images/JKQTPVectorFieldGraph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/images/JKQTPVectorFieldGraphAnchorBottom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/images/JKQTPVectorFieldGraphAnchorMid.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/images/JKQTPVectorFieldGraphAnchorTip.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/JKQTPVectorFieldGraphIgnoreLength.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 24 additions & 4 deletions examples/vectorfield/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ Here is a short summary of the important parts of the code:
JKQTPDatastore* ds=plot.getDatastore();

// 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 columnDX=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return sin(y); });
const auto columnDY=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return cos(x); });
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)*sqrt(x/3.0); });

// 3. create JKQTPVectorFieldGraph to display the data:
JKQTPVectorFieldGraph* graph1=new JKQTPVectorFieldGraph(&plot);
graph1->setXYColumns(columnXY);
graph1->setDxColumn(columnDX);
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
plot.addGraph(graph1);
Expand All @@ -33,3 +33,23 @@ The result looks like this:
![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)
26 changes: 21 additions & 5 deletions examples/vectorfield/vectorfield.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,18 @@ int main(int argc, char* argv[])


// 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 columnDX=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return sin(y); });
const auto columnDY=ds->addCalculatedColumnFromColumn(columnXY.first, columnXY.second, [](double x,double y) { return cos(x); });
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)*sqrt(x/3.0); });


// 3. create JKQTPVectorFieldGraph to display the data:
JKQTPVectorFieldGraph* graph1=new JKQTPVectorFieldGraph(&plot);
graph1->setXYColumns(columnXY);
graph1->setDxColumn(columnDX);
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
plot.addGraph(graph1);
Expand Down Expand Up @@ -78,7 +78,23 @@ int main(int argc, char* argv[])

app.addExportStepFunctor([&](){
graph1->setAnchorPoint(JKQTPVectorFieldGraph::AnchorTip);
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();
Expand Down
43 changes: 29 additions & 14 deletions lib/jkqtplotter/graphs/jkqtpvectorfield.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <QDebug>
#include <QDateTime>
#include "jkqtcommon/jkqtpdrawingtools.h"
#include "jkqtmath/jkqtpstatisticstools.h"
#include "jkqtplotter/jkqtptools.h"

#define SmallestGreaterZeroCompare_xvsgz() if ((xvsgz>10.0*DBL_MIN)&&((smallestGreaterZero<10.0*DBL_MIN) || (xvsgz<smallestGreaterZero))) smallestGreaterZero=xvsgz;
Expand All @@ -33,9 +34,9 @@

JKQTPVectorFieldGraph::JKQTPVectorFieldGraph(JKQTBasePlotter *parent):
JKQTPXYAndVectorGraph(parent),
m_autoscaleLength(true),
m_autoscaleLengthFactor(0.9),
m_lengthScaleFactor(1),
m_vectorLengthMode(AutoscaleLength),
m_autoscaleLengthFactor(0.8),
m_lengthScaleFactor(1.0),
m_anchorPoint(AnchorBottom)
{
initDecoratedLineStyle(parent, parentPlotStyle, JKQTPPlotStyleType::Default);
Expand Down Expand Up @@ -72,17 +73,21 @@ void JKQTPVectorFieldGraph::draw(JKQTPEnhancedPainter &painter)
double scale=1;
if (getIndexRange(imin, imax)) {
// first determine (auto-scale) factor
if (m_autoscaleLength) {
if (m_vectorLengthMode==AutoscaleLength || m_vectorLengthMode==IgnoreLength) {
double avgVecLength=0;
double NDatapoints=0;
double xmin=0, xmax=0,ymin=0,ymax=0;
QVector<double> lengths;
lengths.reserve(imax-imin);
for (int iii=imin; iii<imax; iii++) {
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 yv=datastore->get(static_cast<size_t>(yColumn),static_cast<size_t>(i));
const QPointF vecv=getVectorDxDy(i);
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) {
xmin=xmax=xv;
ymin=ymax=yv;
Expand All @@ -95,10 +100,12 @@ void JKQTPVectorFieldGraph::draw(JKQTPEnhancedPainter &painter)
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 aproxNPerSide=sqrt(NDatapoints);
scale=plotsize/aproxNPerSide/avgVecLength*m_autoscaleLengthFactor;
const double aproxNPerSide=sqrt(NDatapoints);
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 {
scale=m_lengthScaleFactor;
}
Expand All @@ -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 x=transformX(xv);
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=[&]() {
switch (m_anchorPoint) {
case AnchorBottom: return QLineF(x,y,transformX(xv+scale*vecv.x()),transformY(yv+scale*vecv.y()));
Expand All @@ -119,7 +134,7 @@ void JKQTPVectorFieldGraph::draw(JKQTPEnhancedPainter &painter)
}
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()));
}
}
Expand All @@ -144,14 +159,14 @@ QColor JKQTPVectorFieldGraph::getKeyLabelColor() const
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
Expand Down
Loading

0 comments on commit 627c329

Please sign in to comment.