mirror of
https://github.com/jkriege2/JKQtPlotter.git
synced 2024-12-25 01:51:49 +08:00
NEW: Base class JKQTPXYAndVectorGraph for graphs like vector fields, e.g. (x,y,dx,dy) or (x,y,angle,length)
NEW: ... and an actual implementation JKQTPVectorFieldGraph (+example)
This commit is contained in:
parent
a178a1f6d3
commit
e2057ae27a
@ -245,6 +245,7 @@ if(JKQtPlotter_BUILD_EXAMPLES)
|
||||
multithreaded/multithreaded/--mdfile=${CMAKE_CURRENT_LIST_DIR}/../examples/multithreaded/README.md
|
||||
#"multithreaded_complex[multithreaded]/multithreaded_complex/--mdfile=${CMAKE_CURRENT_LIST_DIR}/../examples/multithreaded/README.md --complexlabel"
|
||||
graphlabels/graphlabels,graphlabels_hor
|
||||
vectorfield
|
||||
)
|
||||
|
||||
|
||||
@ -273,6 +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
|
||||
)
|
||||
|
||||
|
||||
|
@ -94,6 +94,9 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
|
||||
<tr><td> \image html violinplot_small.png
|
||||
<td> \subpage JKQTPlotterViolinplotGraphs
|
||||
<td> `JKQTPViolinplotVerticalElement`, `JKQTPViolinplotHorizontalElement`, ...
|
||||
<tr><td> \image html vectorfield_small.png
|
||||
<td> \subpage JKQTPlotterVectorFieldExample
|
||||
<td> `JKQTPVectorFieldGraph`
|
||||
</table>
|
||||
|
||||
|
||||
@ -114,7 +117,7 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
|
||||
<td> \subpage JKQTPlotterGeometricCoordinateAxis0
|
||||
<td> `JKQTPCoordinateAxisStyle::drawMode0`, `JKQTPGeoPolygon`, `JKQTPGeoEllipse`
|
||||
<tr><td> \image html JKQTPGLSimpleBoxAndLineONLYLABELS_small.png
|
||||
<td> \subpage JKQTPXYGraphLabels
|
||||
<td> \subpage JKQTPlotterGraphLabelsExample
|
||||
<td> `JKQTPXYGraphLabels`
|
||||
</table>
|
||||
|
||||
|
@ -75,13 +75,17 @@
|
||||
rank=same;
|
||||
|
||||
JKQTPXYYGraph [URL="\ref JKQTPXYYGraph"]
|
||||
noteJKQTPXYYGraph [shape="note", color="lightyellow",style=filled,fontsize=8,label="base class for all\ngraphs based on\n (x, y1, y2) data tripels"];
|
||||
noteJKQTPXYYGraph [shape="note", color="lightyellow",style=filled,fontsize=8,label="base class for all\ngraphs based on\n (x, y1, y2) data triples"];
|
||||
JKQTPXYYGraph -> noteJKQTPXYYGraph [style=dashed,arrowhead=none];
|
||||
|
||||
JKQTPXXYGraph [URL="\ref JKQTPXXYGraph"]
|
||||
noteJKQTPXXYGraph [shape="note", color="lightyellow",style=filled,fontsize=8,label="base class for all\ngraphs based on\n (x1, x2, y) data tripels"];
|
||||
noteJKQTPXXYGraph [shape="note", color="lightyellow",style=filled,fontsize=8,label="base class for all\ngraphs based on\n (x1, x2, y) data triples"];
|
||||
JKQTPXXYGraph -> noteJKQTPXXYGraph [style=dashed,arrowhead=none];
|
||||
|
||||
JKQTPXYAndVectorGraph [URL="\ref JKQTPXYAndVectorGraph"]
|
||||
noteJJKQTPXYAndVectorGraph [shape="note", color="lightyellow",style=filled,fontsize=8,label="base class for all\nvector field graphs based on\n (x, y, dx, dy) data quadruples"];
|
||||
JKQTPXYAndVectorGraph -> noteJJKQTPXYAndVectorGraph [style=dashed,arrowhead=none];
|
||||
|
||||
JKQTPXYBaselineGraph [URL="\ref JKQTPXYBaselineGraph"]
|
||||
noteJJKQTPXYBaselineGraph [shape="note", color="lightyellow",style=filled,fontsize=8,label="base class for all\ngraphs based on\n (x,y) data pairs and a baseline"];
|
||||
JKQTPXYBaselineGraph -> noteJJKQTPXYBaselineGraph [style=dashed,arrowhead=none];
|
||||
|
@ -285,6 +285,21 @@ Examples:
|
||||
<td> JKQTPPeakStreamGraph
|
||||
</table>
|
||||
|
||||
|
||||
\defgroup jkqtplotter_vectorfieldgraphs Vector Fields
|
||||
\ingroup jkqtplotter_concretegraphs
|
||||
|
||||
This group assembles graphs that represent vector fields (i.e. sets of quadruples (x,y,dx,dy) or (x,y,angle,lengthh) in a plot:
|
||||
<table>
|
||||
<tr>
|
||||
<th> Screenshot
|
||||
<th> Classes
|
||||
<tr>
|
||||
<td>\image html vectorfield_small.png
|
||||
<td> JKQTPVectorFieldGraph
|
||||
</table>
|
||||
|
||||
|
||||
\defgroup jkqtplotter_imagelots Matrix/Image Plotting
|
||||
\ingroup jkqtplotter_concretegraphs
|
||||
|
||||
|
@ -20,7 +20,6 @@ This page lists several todos and wishes for future version of JKQTPlotter
|
||||
<li>graphs: gant-chart as simplified vector field with (x,y1,y2) or (x1,x2,y), or (x,y,dx), (x,y,dy) ... different head/tail style</li>
|
||||
<li>graphs: matrix plots with boxes, labels, ...</li>
|
||||
<li>graphs: matrix plots with symbols: symbol-type, color, size should be parametric, cf. scatter plots</li>
|
||||
<li>graphs: vector field graph (arrows), variant (x,y,dx,dy), (x,y,alpha,length), (x1,y1,x2,y2) ... different head/tail styles</li>
|
||||
<li>graphs: waterfall charts (see https://en.wikipedia.org/wiki/Waterfall_chart)</li>
|
||||
<li>graphs: parametrized variant of JKQTPSingleColumnSymbolsGraph (beeswarm plots etz.) where each symbol may have a color given by a second column</li>
|
||||
<li>graphs: option for JKQTPSingleColumnSymbolsGraph (beeswarm plots etz.) to have a distribution only on one side of the center (left or right)</li>
|
||||
|
@ -128,6 +128,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
|
||||
<li>NEW: allow linear-gradient(), currentcolor, ... in brush definitions of style.ini-file ... and using it is cyberpunk and dark styles</li>
|
||||
<li>NEW: style simple_noaxes.ini</li>
|
||||
<li>NEW: JKQTPXYGraphLabels which can draw a label next to each datapoint in the given x/y-dataset. The labels can be x-/y- or x&y-coordinates or custom, then defined by a user-supplied functor (+example \ref JKQTPlotterGraphLabelsExample)</li>
|
||||
<li>NEW: Base class JKQTPXYAndVectorGraph for graphs like vector fields, e.g. (x,y,dx,dy) or (x,y,angle,length) and an actual implementation JKQTPVectorFieldGraph (+example \ref JKQTPlotterVectorFieldExample)</li>
|
||||
</ul></li>
|
||||
|
||||
<li>JKQTMathText:<ul>
|
||||
|
BIN
doc/images/JKQTPVectorFieldGraph.png
Normal file
BIN
doc/images/JKQTPVectorFieldGraph.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
BIN
doc/images/JKQTPVectorFieldGraphAnchorBottom.png
Normal file
BIN
doc/images/JKQTPVectorFieldGraphAnchorBottom.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 67 KiB |
BIN
doc/images/JKQTPVectorFieldGraphAnchorMid.png
Normal file
BIN
doc/images/JKQTPVectorFieldGraphAnchorMid.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 67 KiB |
BIN
doc/images/JKQTPVectorFieldGraphAnchorTip.png
Normal file
BIN
doc/images/JKQTPVectorFieldGraphAnchorTip.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 67 KiB |
@ -107,6 +107,7 @@ if (JKQtPlotter_BUILD_LIB_JKQTPLOTTER)
|
||||
add_subdirectory(symbols_and_styles)
|
||||
add_subdirectory(ui)
|
||||
add_subdirectory(user_interaction)
|
||||
add_subdirectory(vectorfield)
|
||||
add_subdirectory(violinplot)
|
||||
add_subdirectory(wiggleplots)
|
||||
endif()
|
||||
|
25
examples/vectorfield/CMakeLists.txt
Normal file
25
examples/vectorfield/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.23)
|
||||
|
||||
set(EXAMPLE_NAME vectorfield)
|
||||
set(EXENAME jkqtptest_${EXAMPLE_NAME})
|
||||
|
||||
message( STATUS ".. Building Example ${EXAMPLE_NAME}" )
|
||||
|
||||
|
||||
|
||||
add_executable(${EXENAME} WIN32 ${EXAMPLE_NAME}.cpp)
|
||||
target_link_libraries(${EXENAME} JKQTPExampleToolsLib)
|
||||
target_include_directories(${EXENAME} PRIVATE ../../lib)
|
||||
target_link_libraries(${EXENAME} ${jkqtplotter_namespace}JKQTPlotter${jkqtplotter_LIBNAME_VERSION_PART})
|
||||
|
||||
# precomiled headers to speed up compilation
|
||||
if (JKQtPlotter_BUILD_WITH_PRECOMPILED_HEADERS)
|
||||
target_precompile_headers(${EXENAME} REUSE_FROM jkqtptest_simpletest)
|
||||
endif (JKQtPlotter_BUILD_WITH_PRECOMPILED_HEADERS)
|
||||
|
||||
|
||||
# Installation
|
||||
install(TARGETS ${EXENAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
#Installation of Qt DLLs on Windows
|
||||
jkqtplotter_deployqt(${EXENAME})
|
35
examples/vectorfield/README.md
Normal file
35
examples/vectorfield/README.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Example (JKQTPlotter): Vector Field Plot Example {#JKQTPlotterVectorFieldExample}
|
||||
This project (see [`vectorfield`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/vectorfield) demonstrates the use of JKQTPXYvectorfield to add labels to the datapoints of a graph.
|
||||
|
||||
The source code of the main application is (see [`vectorfield.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/vectorfield/vectorfield.cpp).
|
||||
|
||||
Here is a short summary of the important parts of the code:
|
||||
|
||||
```.cpp
|
||||
// 1. setup a plotter window and get a pointer to the internal datastore (for convenience)
|
||||
JKQTPlotter plot;
|
||||
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)
|
||||
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); });
|
||||
|
||||
// 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}$"));
|
||||
|
||||
// 4. add the graphs to the plot, so it is actually displayed
|
||||
plot.addGraph(graph1);
|
||||
```
|
||||
|
||||
|
||||
The result looks like this:
|
||||
|
||||
![vectorfield](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/vectorfield.png)
|
||||
|
||||
|
85
examples/vectorfield/vectorfield.cpp
Normal file
85
examples/vectorfield/vectorfield.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/** \example vectorfield.cpp
|
||||
* Display a vector field
|
||||
*
|
||||
* \ref JKQTPlotterVectorFieldExample
|
||||
*/
|
||||
|
||||
#include "jkqtpexampleapplication.h"
|
||||
#include <QApplication>
|
||||
#include "jkqtplotter/jkqtplotter.h"
|
||||
#include "jkqtplotter/graphs/jkqtpvectorfield.h"
|
||||
#include "jkqtplotter/graphs/jkqtpscatter.h"
|
||||
#include "jkqtpexampleapplication.h"
|
||||
|
||||
|
||||
#define NX 9
|
||||
#define NY 9
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
JKQTPAppSettingController highDPIController(argc,argv);
|
||||
JKQTPExampleApplication app(argc, argv);
|
||||
|
||||
|
||||
// 1. setup a plotter window and get a pointer to the internal datastore (for convenience)
|
||||
JKQTPlotter plot;
|
||||
plot.getPlotter()->setUseAntiAliasingForGraphs(true); // nicer (but slower) plotting
|
||||
plot.getPlotter()->setUseAntiAliasingForSystem(true); // nicer (but slower) plotting
|
||||
plot.getPlotter()->setUseAntiAliasingForText(true); // nicer (but slower) text rendering
|
||||
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)
|
||||
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); });
|
||||
|
||||
|
||||
// 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}$"));
|
||||
|
||||
// 4. add the graphs to the plot, so it is actually displayed
|
||||
plot.addGraph(graph1);
|
||||
|
||||
// 5. scale the plot so the graph is contained
|
||||
plot.getPlotter()->setAxisAspectRatio(1);
|
||||
plot.getPlotter()->setAspectRatio(1);
|
||||
plot.getPlotter()->setMaintainAxisAspectRatio(true);
|
||||
plot.getPlotter()->setMaintainAspectRatio(true);
|
||||
plot.zoomToFit();
|
||||
|
||||
// show plotter and make it a decent size
|
||||
plot.setWindowTitle("JKQTPVectorFieldGraph example");
|
||||
plot.show();
|
||||
plot.resize(400/plot.devicePixelRatioF(),430/plot.devicePixelRatioF());
|
||||
|
||||
|
||||
|
||||
app.addExportStepFunctor([&](){
|
||||
JKQTPXYScatterGraph* g2=new JKQTPXYScatterGraph(&plot);
|
||||
g2->setXYColumns(columnXY);
|
||||
g2->setTitle("anchor points");
|
||||
g2->setSymbolSize(5);
|
||||
g2->setSymbolType(JKQTPFilledCircle);
|
||||
plot.addGraph(g2);
|
||||
plot.redrawPlot();
|
||||
});
|
||||
app.addExportStepFunctor([&](){
|
||||
graph1->setAnchorPoint(JKQTPVectorFieldGraph::AnchorMid);
|
||||
plot.redrawPlot();
|
||||
});
|
||||
|
||||
app.addExportStepFunctor([&](){
|
||||
graph1->setAnchorPoint(JKQTPVectorFieldGraph::AnchorTip);
|
||||
plot.redrawPlot();
|
||||
});
|
||||
|
||||
return app.exec();
|
||||
}
|
@ -27,6 +27,8 @@
|
||||
#include <limits>
|
||||
#include <QPoint>
|
||||
#include <QPointF>
|
||||
#include <QLineF>
|
||||
#include <QRectF>
|
||||
#include <vector>
|
||||
#include <QString>
|
||||
#include <functional>
|
||||
@ -485,6 +487,18 @@ inline bool JKQTPIsOKFloat(T v) {
|
||||
return std::isfinite(v)&&(!std::isinf(v))&&(!std::isnan(v));
|
||||
}
|
||||
|
||||
inline bool JKQTPIsOKFloat(const QPointF& v) {
|
||||
return JKQTPIsOKFloat<qreal>(v.x()) && JKQTPIsOKFloat<qreal>(v.y());
|
||||
}
|
||||
|
||||
inline bool JKQTPIsOKFloat(const QLineF& v) {
|
||||
return JKQTPIsOKFloat<qreal>(v.x1()) && JKQTPIsOKFloat<qreal>(v.x2()) && JKQTPIsOKFloat<qreal>(v.y1()) && JKQTPIsOKFloat<qreal>(v.y2());
|
||||
}
|
||||
|
||||
inline bool JKQTPIsOKFloat(const QRectF& v) {
|
||||
return JKQTPIsOKFloat<qreal>(v.x()) && JKQTPIsOKFloat<qreal>(v.x()) && JKQTPIsOKFloat<qreal>(v.width()) && JKQTPIsOKFloat<qreal>(v.height());
|
||||
}
|
||||
|
||||
/** \brief evaluates a gaussian propability density function
|
||||
* \ingroup jkqtptools_math_basic
|
||||
*
|
||||
|
@ -59,11 +59,12 @@ isEmpty(JKQTP_PLOTTER_PRI_INCLUDED) {
|
||||
$$PWD/jkqtplotter/graphs/jkqtplines.h \
|
||||
$$PWD/jkqtplotter/graphs/jkqtpgraphlabelstylemixin.h \
|
||||
$$PWD/jkqtplotter/graphs/jkqtpgraphlabels.h \
|
||||
$$PWD/jkqtplotter/graphs/jkqtpvectorfield.h \
|
||||
$$PWD/jkqtplotter/gui/jkqtpcomboboxes.h \
|
||||
$$PWD/jkqtplotter/gui/jkqtpenhancedspinboxes.h \
|
||||
$$PWD/jkqtplotter/gui/jkqtpenhancedtableview.h \
|
||||
$$PWD/jkqtplotter/gui/jkqtpgraphsmodel.h \
|
||||
$$PWD/jkqtplotter/gui/jkvanishqtoolbar.h
|
||||
$$PWD/jkqtplotter/gui/jkvanishqtoolbar.h
|
||||
|
||||
|
||||
|
||||
@ -115,6 +116,7 @@ isEmpty(JKQTP_PLOTTER_PRI_INCLUDED) {
|
||||
$$PWD/jkqtplotter/graphs/jkqtplines.cpp \
|
||||
$$PWD/jkqtplotter/graphs/jkqtpgraphlabelstylemixin.cpp \
|
||||
$$PWD/jkqtplotter/graphs/jkqtpgraphlabels.cpp \
|
||||
$$PWD/jkqtplotter/graphs/jkqtpvectorfield.cpp \
|
||||
$$PWD/jkqtplotter/gui/jkqtpcomboboxes.cpp \
|
||||
$$PWD/jkqtplotter/gui/jkqtpenhancedspinboxes.cpp \
|
||||
$$PWD/jkqtplotter/gui/jkqtpenhancedtableview.cpp \
|
||||
|
@ -33,6 +33,7 @@ target_sources(${lib_name} PRIVATE
|
||||
jkqtplines.cpp
|
||||
jkqtpgraphlabelstylemixin.cpp
|
||||
jkqtpgraphlabels.cpp
|
||||
jkqtpvectorfield.cpp
|
||||
)
|
||||
# ... and add headers
|
||||
target_sources(${lib_name} PUBLIC FILE_SET HEADERS TYPE HEADERS
|
||||
@ -68,4 +69,5 @@ target_sources(${lib_name} PUBLIC FILE_SET HEADERS TYPE HEADERS
|
||||
jkqtplines.h
|
||||
jkqtpgraphlabelstylemixin.h
|
||||
jkqtpgraphlabels.h
|
||||
jkqtpvectorfield.h
|
||||
)
|
||||
|
174
lib/jkqtplotter/graphs/jkqtpvectorfield.cpp
Normal file
174
lib/jkqtplotter/graphs/jkqtpvectorfield.cpp
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
Copyright (c) 2008-2024 Jan W. Krieger (<jan@jkrieger.de>)
|
||||
|
||||
|
||||
|
||||
This software is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License (LGPL) as published by
|
||||
the Free Software Foundation, either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License (LGPL) for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License (LGPL)
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "jkqtplotter/graphs/jkqtpvectorfield.h"
|
||||
#include "jkqtplotter/jkqtpbaseplotter.h"
|
||||
#include "jkqtplotter/jkqtplotter.h"
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
#include "jkqtcommon/jkqtpdrawingtools.h"
|
||||
#include "jkqtplotter/jkqtptools.h"
|
||||
|
||||
#define SmallestGreaterZeroCompare_xvsgz() if ((xvsgz>10.0*DBL_MIN)&&((smallestGreaterZero<10.0*DBL_MIN) || (xvsgz<smallestGreaterZero))) smallestGreaterZero=xvsgz;
|
||||
|
||||
|
||||
|
||||
JKQTPVectorFieldGraph::JKQTPVectorFieldGraph(JKQTBasePlotter *parent):
|
||||
JKQTPXYAndVectorGraph(parent),
|
||||
m_autoscaleLength(true),
|
||||
m_lengthScaleFactor(1),
|
||||
m_anchorPoint(AnchorBottom)
|
||||
{
|
||||
initDecoratedLineStyle(parent, parentPlotStyle, JKQTPPlotStyleType::Default);
|
||||
setTailDecoratorStyle(JKQTPNoDecorator);
|
||||
setHeadDecoratorStyle(JKQTPFilledArrow);
|
||||
setHeadDecoratorSizeFactor(getHeadDecoratorSizeFactor()/1.6);
|
||||
setTailDecoratorSizeFactor(getTailDecoratorSizeFactor()/1.6);
|
||||
}
|
||||
|
||||
JKQTPVectorFieldGraph::JKQTPVectorFieldGraph(JKQTPlotter *parent):
|
||||
JKQTPVectorFieldGraph(parent->getPlotter())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void JKQTPVectorFieldGraph::draw(JKQTPEnhancedPainter &painter)
|
||||
{
|
||||
#ifdef JKQTBP_AUTOTIMER
|
||||
JKQTPAutoOutputTimer jkaaot("JKQTPVectorFieldGraph::draw");
|
||||
#endif
|
||||
if (parent==nullptr) return;
|
||||
const JKQTPDatastore* datastore=parent->getDatastore();
|
||||
if (datastore==nullptr) return;
|
||||
|
||||
drawErrorsBefore(painter);
|
||||
{
|
||||
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
|
||||
const QPen p=getLinePen(painter, parent);
|
||||
painter.setPen(p);
|
||||
painter.setBrush(p.color());
|
||||
|
||||
int imax=0;
|
||||
int imin=0;
|
||||
double scale=1;
|
||||
if (getIndexRange(imin, imax)) {
|
||||
// first determine (auto-scale) factor
|
||||
if (m_autoscaleLength) {
|
||||
double avgVecLength=0;
|
||||
double NDatapoints=0;
|
||||
double xmin=0, xmax=0,ymin=0,ymax=0;
|
||||
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()));
|
||||
if (NDatapoints==0) {
|
||||
xmin=xmax=xv;
|
||||
ymin=ymax=yv;
|
||||
} else {
|
||||
xmin=qMin(xmin,xv);
|
||||
xmax=qMax(xmax,xv);
|
||||
ymin=qMin(ymin,yv);
|
||||
ymax=qMax(ymax,yv);
|
||||
}
|
||||
NDatapoints++;
|
||||
}
|
||||
}
|
||||
avgVecLength/=NDatapoints;
|
||||
const double plotsize=qMax(fabs(xmax-xmin),fabs(ymax-ymin));
|
||||
const double aproxNPerSide=sqrt(NDatapoints);
|
||||
scale=plotsize/aproxNPerSide/avgVecLength;
|
||||
} else {
|
||||
scale=m_lengthScaleFactor;
|
||||
}
|
||||
|
||||
// now draw
|
||||
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 double x=transformX(xv);
|
||||
const double y=transformY(yv);
|
||||
const QPointF vecv=getVectorDxDy(i);
|
||||
const QLineF l=[&]() {
|
||||
switch (m_anchorPoint) {
|
||||
case AnchorBottom: return QLineF(x,y,transformX(xv+scale*vecv.x()),transformY(yv+scale*vecv.y()));
|
||||
case AnchorMid: return QLineF(transformX(xv-0.5*scale*vecv.x()),transformY(yv-0.5*scale*vecv.y()),transformX(xv+0.5*scale*vecv.x()),transformY(yv+0.5*scale*vecv.y()));
|
||||
case AnchorTip: return QLineF(transformX(xv-scale*vecv.x()),transformY(yv-scale*vecv.y()), x,y);
|
||||
}
|
||||
return QLineF(JKQTP_NAN,JKQTP_NAN,JKQTP_NAN,JKQTP_NAN);
|
||||
}();
|
||||
if (JKQTPIsOKFloat(l)) {
|
||||
JKQTPPlotDecoratedLine(painter,l, getTailDecoratorStyle(), calcTailDecoratorSize(p.widthF()), getHeadDecoratorStyle(), calcHeadDecoratorSize(p.widthF()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
drawErrorsAfter(painter);
|
||||
}
|
||||
|
||||
void JKQTPVectorFieldGraph::drawKeyMarker(JKQTPEnhancedPainter &painter, const QRectF &rect)
|
||||
{
|
||||
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
|
||||
const QPen p=getKeyLinePen(painter, rect, parent);
|
||||
painter.setPen(p);
|
||||
painter.setBrush(p.color());
|
||||
const QLineF l(rect.left(), rect.bottom(), rect.right(), rect.top());
|
||||
JKQTPPlotDecoratedLine(painter,l, getTailDecoratorStyle(), calcTailDecoratorSize(p.widthF()), getHeadDecoratorStyle(), calcHeadDecoratorSize(p.widthF()));
|
||||
}
|
||||
|
||||
QColor JKQTPVectorFieldGraph::getKeyLabelColor() const
|
||||
{
|
||||
return getLineColor();
|
||||
}
|
||||
|
||||
bool JKQTPVectorFieldGraph::getAutoscaleLength() const
|
||||
{
|
||||
return m_autoscaleLength;
|
||||
}
|
||||
|
||||
void JKQTPVectorFieldGraph::setAutoscaleLength(bool newAutoscaleLength)
|
||||
{
|
||||
m_autoscaleLength = newAutoscaleLength;
|
||||
}
|
||||
|
||||
double JKQTPVectorFieldGraph::getLengthScaleFactor() const
|
||||
{
|
||||
return m_lengthScaleFactor;
|
||||
}
|
||||
|
||||
void JKQTPVectorFieldGraph::setLengthScaleFactor(double newLengthScaleFactor)
|
||||
{
|
||||
m_lengthScaleFactor = newLengthScaleFactor;
|
||||
}
|
||||
|
||||
JKQTPVectorFieldGraph::VectorAnchorPoint JKQTPVectorFieldGraph::getAnchorPoint() const
|
||||
{
|
||||
return m_anchorPoint;
|
||||
}
|
||||
|
||||
void JKQTPVectorFieldGraph::setAnchorPoint(VectorAnchorPoint newAnchorPoint)
|
||||
{
|
||||
m_anchorPoint = newAnchorPoint;
|
||||
}
|
133
lib/jkqtplotter/graphs/jkqtpvectorfield.h
Normal file
133
lib/jkqtplotter/graphs/jkqtpvectorfield.h
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
Copyright (c) 2008-2024 Jan W. Krieger (<jan@jkrieger.de>)
|
||||
|
||||
|
||||
|
||||
This software is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License (LGPL) as published by
|
||||
the Free Software Foundation, either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License (LGPL) for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License (LGPL)
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef jkqtpvectorfield_H
|
||||
#define jkqtpvectorfield_H
|
||||
|
||||
|
||||
#include <QString>
|
||||
#include <QPainter>
|
||||
#include <QPair>
|
||||
#include <functional>
|
||||
#include "jkqtplotter/jkqtptools.h"
|
||||
#include "jkqtplotter/jkqtplotter_imexport.h"
|
||||
#include "jkqtplotter/jkqtpgraphsbase.h"
|
||||
#include "jkqtplotter/jkqtpgraphsbasestylingmixins.h"
|
||||
|
||||
// forward declarations
|
||||
class JKQTBasePlotter;
|
||||
class JKQTPlotter;
|
||||
class JKQTPCoordinateAxis;
|
||||
class JKQTPDatastore;
|
||||
|
||||
|
||||
|
||||
/*! \brief This graph plots a vector field, i.e. a set of vectors (dx,dy) or (angle,length) at positions (x,y).
|
||||
This type of plot is sometimes also refered to as quicver plot (e.g. in Matlab or matplotlib)
|
||||
\ingroup jkqtplotter_vectorfieldgraphs
|
||||
|
||||
|
||||
\image html JKQTPVectorFieldGraph.png
|
||||
|
||||
To achieve this, use code like this:
|
||||
\code
|
||||
// 1. setup a plotter window and get a pointer to the internal datastore (for convenience)
|
||||
JKQTPlotter plot;
|
||||
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)
|
||||
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); });
|
||||
|
||||
// 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}$"));
|
||||
|
||||
// 4. add the graphs to the plot, so it is actually displayed
|
||||
plot.addGraph(graph1);
|
||||
\endcode
|
||||
|
||||
\see \ref JKQTPlotterVectorFieldExample , JKQTPGraphDecoratedLineStyleMixin , JKQTPXYAndVectorGraph
|
||||
|
||||
*/
|
||||
class JKQTPLOTTER_LIB_EXPORT JKQTPVectorFieldGraph: public JKQTPXYAndVectorGraph, public JKQTPGraphDecoratedLineStyleMixin {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum VectorAnchorPoint {
|
||||
AnchorBottom, //!< \brief this is the default: the vector starts at (x,y) \image html JKQTPVectorFieldGraphAnchorBottom.png
|
||||
AnchorMid, //!< \brief the vector's mid is at (x,y) \image html JKQTPVectorFieldGraphAnchorMid.png
|
||||
AnchorTip //!< \brief the vector ends at (x,y) \image html JKQTPVectorFieldGraphAnchorTip.png
|
||||
};
|
||||
Q_ENUM(VectorAnchorPoint)
|
||||
|
||||
/** \brief class constructor */
|
||||
explicit JKQTPVectorFieldGraph(JKQTBasePlotter* parent=nullptr);
|
||||
/** \brief class constructor */
|
||||
JKQTPVectorFieldGraph(JKQTPlotter* parent);
|
||||
|
||||
/** \brief plots the graph to the plotter object specified as parent */
|
||||
virtual void draw(JKQTPEnhancedPainter& painter) override;
|
||||
/** \brief plots a key marker inside the specified rectangle \a rect */
|
||||
virtual void drawKeyMarker(JKQTPEnhancedPainter& painter, const QRectF& rect) override;
|
||||
/** \brief returns the color to be used for the key label */
|
||||
virtual QColor getKeyLabelColor() const override;
|
||||
|
||||
/** \copydoc m_autoscaleLength */
|
||||
bool getAutoscaleLength() const;
|
||||
/** \copydoc m_autoscaleLength */
|
||||
void setAutoscaleLength(bool newAutoscaleLength);
|
||||
|
||||
/** \copydoc m_lengthScaleFactor */
|
||||
double getLengthScaleFactor() const;
|
||||
/** \copydoc m_lengthScaleFactor */
|
||||
void setLengthScaleFactor(double newLengthScaleFactor);
|
||||
|
||||
|
||||
/** \copydoc m_anchorPoint */
|
||||
VectorAnchorPoint getAnchorPoint() const;
|
||||
/** \copydoc m_anchorPoint */
|
||||
void setAnchorPoint(VectorAnchorPoint newAnchorPoint);
|
||||
|
||||
Q_PROPERTY(bool autoscaleLength READ getAutoscaleLength WRITE setAutoscaleLength )
|
||||
Q_PROPERTY(double lengthScaleFactor READ getLengthScaleFactor WRITE setLengthScaleFactor )
|
||||
Q_PROPERTY(VectorAnchorPoint anchorPoint READ getAnchorPoint WRITE setAnchorPoint )
|
||||
protected:
|
||||
private:
|
||||
/** \brief enables or disables the autoscaling of vector lengths
|
||||
*
|
||||
* If disabled (\c false ) the vector is drawn from (x,y) to (x+dx*m_lengthScaleFactor,y+dy*m_lengthScaleFactor),
|
||||
* otherweise to (x+dx*autoscale,y+dy*autoscale)
|
||||
*/
|
||||
bool m_autoscaleLength;
|
||||
/** \brief if m_autoscaleLength \c ==false, this is the scale-factor used to calculate the vector length */
|
||||
double m_lengthScaleFactor;
|
||||
/** \brief defines where the vector is anchored */
|
||||
VectorAnchorPoint m_anchorPoint;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // jkqtpvectorfield_H
|
@ -1330,6 +1330,9 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPDatastore{
|
||||
inline size_t addColumnCalculatedFromColumn(size_t otherColumnX, size_t otherColumnY, const std::function<double(double,double)>& f, const QString& name=QString("")) {
|
||||
return addCalculatedColumnFromColumn(otherColumnX, otherColumnY, f, name);
|
||||
}
|
||||
inline size_t addCalculatedColumnFromColumn(const std::pair<size_t, size_t>& otherColumn, const std::function<double(double,double)>& f, const QString& name=QString("")) {
|
||||
return addCalculatedColumnFromColumn(otherColumn.first, otherColumn.second, f, name);
|
||||
}
|
||||
|
||||
/** \brief returns the number of (logical) columns currently managed by the datastore */
|
||||
inline size_t getColumnCount() const { return static_cast<size_t>(columns.size()); }
|
||||
|
@ -1164,3 +1164,145 @@ JKQTPPlotAnnotationElement::~JKQTPPlotAnnotationElement()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
JKQTPXYAndVectorGraph::JKQTPXYAndVectorGraph(JKQTBasePlotter *parent):
|
||||
JKQTPXYGraph(parent),
|
||||
vectorDataLayout(DefaultVectorDataLayout),
|
||||
dxColumn(-1), dyColumn(-1),
|
||||
angleColumn(-1), lengthColumn(-1)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool JKQTPXYAndVectorGraph::getXMinMax(double &minx, double &maxx, double &smallestGreaterZero)
|
||||
{
|
||||
return JKQTPXYGraph::getXMinMax(minx,maxx,smallestGreaterZero);
|
||||
}
|
||||
|
||||
bool JKQTPXYAndVectorGraph::getYMinMax(double &miny, double &maxy, double &smallestGreaterZero)
|
||||
{
|
||||
return JKQTPXYGraph::getYMinMax(miny,maxy,smallestGreaterZero);
|
||||
}
|
||||
|
||||
bool JKQTPXYAndVectorGraph::usesColumn(int column) const
|
||||
{
|
||||
if (vectorDataLayout==DeltaXDeltaYLayout) {
|
||||
return (column==dxColumn) || (column==dyColumn) || JKQTPXYGraph::usesColumn(column);
|
||||
} else {
|
||||
return (column==angleColumn) || (column==lengthColumn) || JKQTPXYGraph::usesColumn(column);
|
||||
}
|
||||
}
|
||||
|
||||
int JKQTPXYAndVectorGraph::getDxColumn() const
|
||||
{
|
||||
return dxColumn;
|
||||
}
|
||||
|
||||
int JKQTPXYAndVectorGraph::getDyColumn() const
|
||||
{
|
||||
return dyColumn;
|
||||
}
|
||||
|
||||
int JKQTPXYAndVectorGraph::getAngleColumn() const
|
||||
{
|
||||
return angleColumn;
|
||||
}
|
||||
|
||||
int JKQTPXYAndVectorGraph::getLengthColumn() const
|
||||
{
|
||||
return lengthColumn;
|
||||
}
|
||||
|
||||
JKQTPXYAndVectorGraph::VectorDataLayout JKQTPXYAndVectorGraph::getVectorDataLayout() const
|
||||
{
|
||||
return vectorDataLayout;
|
||||
}
|
||||
|
||||
double JKQTPXYAndVectorGraph::hitTest(const QPointF &posSystem, QPointF *closestSpotSystem, QString *label, HitTestMode mode) const
|
||||
{
|
||||
return JKQTPXYGraph::hitTest(posSystem, closestSpotSystem, label, mode);
|
||||
}
|
||||
|
||||
void JKQTPXYAndVectorGraph::setDxColumn(int col)
|
||||
{
|
||||
dxColumn=col;
|
||||
vectorDataLayout=DeltaXDeltaYLayout;
|
||||
}
|
||||
|
||||
void JKQTPXYAndVectorGraph::setDyColumn(int col)
|
||||
{
|
||||
dyColumn=col;
|
||||
vectorDataLayout=DeltaXDeltaYLayout;
|
||||
}
|
||||
|
||||
void JKQTPXYAndVectorGraph::setDxDyColumn(int colDx, int colDy)
|
||||
{
|
||||
dxColumn=colDx;
|
||||
dyColumn=colDy;
|
||||
vectorDataLayout=DeltaXDeltaYLayout;
|
||||
|
||||
}
|
||||
|
||||
void JKQTPXYAndVectorGraph::setAngleColumn(int col)
|
||||
{
|
||||
angleColumn=col;
|
||||
vectorDataLayout=AngleAndLengthLayout;
|
||||
}
|
||||
|
||||
void JKQTPXYAndVectorGraph::setAngleAndLengthColumn(int colAngle, int colLength)
|
||||
{
|
||||
angleColumn=colAngle;
|
||||
lengthColumn=colLength;
|
||||
vectorDataLayout=AngleAndLengthLayout;
|
||||
}
|
||||
|
||||
void JKQTPXYAndVectorGraph::setLengthColumn(int col)
|
||||
{
|
||||
lengthColumn=col;
|
||||
vectorDataLayout=AngleAndLengthLayout;
|
||||
}
|
||||
|
||||
QPointF JKQTPXYAndVectorGraph::getVectorDxDy(int i) const
|
||||
{
|
||||
|
||||
const JKQTPDatastore* datastore=parent->getDatastore();
|
||||
if (datastore) {
|
||||
switch(vectorDataLayout) {
|
||||
case DeltaXDeltaYLayout: {
|
||||
const double dx=datastore->get(static_cast<size_t>(dxColumn),static_cast<size_t>(i));
|
||||
const double dy=datastore->get(static_cast<size_t>(dyColumn),static_cast<size_t>(i));
|
||||
return QPointF(dx,dy);
|
||||
} break;
|
||||
case AngleAndLengthLayout: {
|
||||
const double a=datastore->get(static_cast<size_t>(angleColumn),static_cast<size_t>(i));
|
||||
const double l=(lengthColumn<0) ? 1.0 : datastore->get(static_cast<size_t>(lengthColumn),static_cast<size_t>(i));
|
||||
return QPointF(l*cos(a),l*sin(a));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
return QPointF(JKQTP_NAN, JKQTP_NAN);
|
||||
}
|
||||
|
||||
bool JKQTPXYAndVectorGraph::getIndexRange(int &imin, int &imax) const
|
||||
{
|
||||
bool ok=JKQTPXYGraph::getIndexRange(imin, imax);
|
||||
if (ok) {
|
||||
if (vectorDataLayout==DeltaXDeltaYLayout) {
|
||||
if (parent==nullptr) return false;
|
||||
if (dxColumn<0||dyColumn<0) return false;
|
||||
const JKQTPDatastore* datastore=parent->getDatastore();
|
||||
const int rowsDX=static_cast<int>(datastore->getRows(static_cast<size_t>(dxColumn)));
|
||||
const int rowsDY=static_cast<int>(datastore->getRows(static_cast<size_t>(dyColumn)));
|
||||
imax=qMin<int>(imax, qMin<int>(rowsDX,rowsDY));
|
||||
} else if (vectorDataLayout==AngleAndLengthLayout) {
|
||||
if (parent==nullptr) return false;
|
||||
if (angleColumn<0 && lengthColumn<0) return false;
|
||||
if (angleColumn<0) return false;
|
||||
const JKQTPDatastore* datastore=parent->getDatastore();
|
||||
const int rowsA=static_cast<int>(datastore->getRows(static_cast<size_t>(angleColumn)));
|
||||
const int rowsL=(lengthColumn>=0)?static_cast<int>(datastore->getRows(static_cast<size_t>(lengthColumn))):0;
|
||||
imax=qMin<int>(imax, qMin<int>(rowsA,rowsL));
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
@ -957,5 +957,123 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPSingleColumnGraph: public JKQTPGraph {
|
||||
|
||||
|
||||
|
||||
/** \brief This virtual JKQTPGraph descendent extends JKQTPXYGraph with two additional columns that encode for a vector starting at (x,y), i.e. either two distances along the x- and y-axis (\f$ \Delta x, \Delta y \f$), or a rotation angle \f$ \alpha \f$ and a vector length \f$ \l \f$ .
|
||||
* \ingroup jkqtplotter_basegraphs
|
||||
*
|
||||
* \see
|
||||
*/
|
||||
class JKQTPLOTTER_LIB_EXPORT JKQTPXYAndVectorGraph: public JKQTPXYGraph {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/** \brief values from this enum indicates how to interpret the data columns provided to this graph */
|
||||
enum VectorDataLayout {
|
||||
DeltaXDeltaYLayout,
|
||||
AngleAndLengthLayout,
|
||||
|
||||
DefaultVectorDataLayout=DeltaXDeltaYLayout,
|
||||
};
|
||||
Q_ENUM(VectorDataLayout)
|
||||
|
||||
/** \brief class constructor */
|
||||
JKQTPXYAndVectorGraph(JKQTBasePlotter* parent=nullptr);
|
||||
|
||||
|
||||
/** \copydoc JKQTPPlotElement::getXMinMax() */
|
||||
virtual bool getXMinMax(double& minx, double& maxx, double& smallestGreaterZero) override;
|
||||
/** \copydoc JKQTPPlotElement::getYMinMax() */
|
||||
virtual bool getYMinMax(double& miny, double& maxy, double& smallestGreaterZero) override;
|
||||
|
||||
/** \copydoc JKQTPGraph::usesColumn() */
|
||||
virtual bool usesColumn(int column) const override;
|
||||
|
||||
/** \copydoc dxColumn */
|
||||
int getDxColumn() const;
|
||||
/** \copydoc dyColumn */
|
||||
int getDyColumn() const;
|
||||
/** \copydoc angleColumn */
|
||||
int getAngleColumn() const;
|
||||
/** \copydoc lengthColumn */
|
||||
int getLengthColumn() const;
|
||||
/** \copydoc vectorDataLayout */
|
||||
VectorDataLayout getVectorDataLayout() const;
|
||||
|
||||
/** \copydoc JKQTPXYGraph::hitTest() */
|
||||
virtual double hitTest(const QPointF &posSystem, QPointF* closestSpotSystem=nullptr, QString* label=nullptr, HitTestMode mode=HitTestXY) const override;
|
||||
|
||||
Q_PROPERTY(VectorDataLayout vectorDataLayout READ getVectorDataLayout)
|
||||
Q_PROPERTY(int dxColumn READ getDxColumn WRITE setDxColumn)
|
||||
Q_PROPERTY(int dyColumn READ getDyColumn WRITE setDyColumn)
|
||||
Q_PROPERTY(int angleColumn READ getAngleColumn WRITE setAngleColumn)
|
||||
Q_PROPERTY(int lengthColumn READ getLengthColumn WRITE setLengthColumn)
|
||||
public Q_SLOTS:
|
||||
/** \copydoc dxColumn */
|
||||
void setDxColumn(int col);
|
||||
/** \copydoc dyColumn */
|
||||
void setDyColumn(int col) ;
|
||||
/** \brief det dxColumn and dyColumn column at the same time! ALso ensures that vectorDataLayout is set accordingly.
|
||||
*
|
||||
* \see dxColumn, dyColumn
|
||||
*/
|
||||
void setDxDyColumn(int colDx, int colDy) ;
|
||||
/** \copydoc angleColumn */
|
||||
void setAngleColumn(int col) ;
|
||||
/** \brief det angleColumn and lengthColumn column at the same time! ALso ensures that vectorDataLayout is set accordingly.
|
||||
*
|
||||
* \see angleColumn, lengthColumn
|
||||
*/
|
||||
void setAngleAndLengthColumn(int colAngle, int colLength) ;
|
||||
/** \copydoc lengthColumn */
|
||||
void setLengthColumn(int col) ;
|
||||
protected:
|
||||
/** \brief this function interprets vectorDataLayout together with (dxColumn, dyColumn) or (angleColumn, lengthColumn) or ... and returns the \a i -th vectors \f$ \Delta x, \Delta y \f$ */
|
||||
QPointF getVectorDxDy(int i) const;
|
||||
|
||||
/** \brief indicates, which column pairs to use (dxColumn, dyColumn), (angleColumn, lengthColumn), ... */
|
||||
VectorDataLayout vectorDataLayout;
|
||||
/** \brief the column that contains the delta along the x-axis.
|
||||
*
|
||||
* \note Note that this column is only used, when vectorDataLayout is set accordingly to DeltaXDeltaYLayout!
|
||||
* Also note that Setter-functions (e.g. setDxColumn() ) will ensure that vectorDataLayout is set accordingly.
|
||||
*
|
||||
* \see setDxColumn(), setDyColumn(), setDxDyColumn(), getDxColumn(), getDyColumn()
|
||||
*/
|
||||
int dxColumn;
|
||||
/** \brief the column that contains the delta along the y-axis.
|
||||
*
|
||||
* \note Note that this column is only used, when vectorDataLayout is set accordingly to DeltaXDeltaYLayout!
|
||||
* Also note that Setter-functions (e.g. setDyColumn() ) will ensure that vectorDataLayout is set accordingly.
|
||||
*
|
||||
* \see setDxColumn(), setDyColumn(), setDxDyColumn(), getDxColumn(), getDyColumn()
|
||||
*/
|
||||
int dyColumn;
|
||||
/** \brief the column that contains the rotation angle [in radian]
|
||||
*
|
||||
* An angle of 0 means a right-pointing vector and angle is measured count-clockwise,
|
||||
* so angle \f$ \alpha \f$ and length \f$ l \f$ can be converted to \f$ \Delta x, \Delta y \f$ via:
|
||||
* \f[ \Delta x = l\cdot \cos\alpha \f]
|
||||
* \f[ \Delta y = l\cdot \sin\alpha \f]
|
||||
* Note that these calculations are performed in the coordinate-axis-space, NOT in screen pixel space!
|
||||
*
|
||||
* \note Note that this column is only used, when vectorDataLayout is set accordingly to AngleAndLengthLayout!
|
||||
* Also note that Setter-functions (e.g. setAngleColumn() ) will ensure that vectorDataLayout is set accordingly.
|
||||
*
|
||||
* \see setAngleColumn(), setLengthColumn(), getAngleColumn(), getLengthColumn()
|
||||
*/
|
||||
int angleColumn;
|
||||
/** \brief the column that contains the vector length
|
||||
*
|
||||
* \copydetails angleColumn
|
||||
*/
|
||||
int lengthColumn;
|
||||
|
||||
|
||||
/** \brief determines the range of row indexes available in the data columns of this graph
|
||||
*
|
||||
* \param[out] imin first usable row-index
|
||||
* \param[out] imax last usable row-index
|
||||
* \return \c true on success and \c false if the information is not available
|
||||
*/
|
||||
virtual bool getIndexRange(int &imin, int &imax) const override;
|
||||
};
|
||||
|
||||
#endif // JKQTPGRAPHSBASE_H
|
||||
|
BIN
screenshots/vectorfield.png
Normal file
BIN
screenshots/vectorfield.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
BIN
screenshots/vectorfield_small.png
Normal file
BIN
screenshots/vectorfield_small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
Loading…
Reference in New Issue
Block a user