2024-02-09 04:52:30 +08:00
/*
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).
2024-02-09 18:41:19 +08:00
This class immplements the most basic form of vector plot , i . e . the vector are drawn with a length
corresponding to their magnitude .
2024-02-09 04:52:30 +08:00
\ ingroup jkqtplotter_vectorfieldgraphs
2024-02-09 20:48:41 +08:00
\ note This type of plot is sometimes also refered to as < b > quiver plot < / b > ( e . g . in Matlab or matplotlib )
2024-02-09 04:52:30 +08:00
\ 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
2024-02-09 20:48:41 +08:00
// 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)
2024-02-09 04:52:30 +08:00
const auto columnXY = ds - > addLinearGridColumns ( NX , 0 , 6 , NY , - 3 , 3 , " x " , " y " ) ;
2024-02-09 20:48:41 +08:00
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 ) ; } ) ;
2024-02-09 04:52:30 +08:00
// 3. create JKQTPVectorFieldGraph to display the data:
JKQTPVectorFieldGraph * graph1 = new JKQTPVectorFieldGraph ( & plot ) ;
graph1 - > setXYColumns ( columnXY ) ;
graph1 - > setDxColumn ( columnDX ) ;
graph1 - > setDyColumn ( columnDY ) ;
2024-02-09 20:48:41 +08:00
graph1 - > setTitle ( QObject : : tr ( " $ \\ vec{f}(x,y)= \\ bigl[ \\ sin(y) \\ cdot \\ sqrt{x/3}, \\ cos(x) \\ cdot \\ sqrt{x/3} \\ bigr]^ \\ mathrm{T}$ " ) ) ;
2024-02-09 04:52:30 +08:00
// 4. add the graphs to the plot, so it is actually displayed
plot . addGraph ( graph1 ) ;
\ endcode
2024-02-09 20:48:41 +08:00
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 ( ) .
2024-02-09 23:45:40 +08:00
4. Using setVectorLineWidthMode ( JKQTPVectorFieldGraph : : AutoscaleLineWidthFromLength ) you can also
scale the line width of each vector by the vector ' s length . This sometimes gives an easier to read
representation of the vector properties , especially if combined with
setVectorLengthMode ( JKQTPVectorFieldGraph : : IgnoreLength ) :
\ image html JKQTPVectorFieldGraphIgnoreLengthAutoscaleLineWidthFromLength . png
2024-02-09 20:48:41 +08:00
.
2024-02-10 07:26:45 +08:00
\ see \ ref JKQTPlotterVectorFieldExample , JKQTPGraphDecoratedLineStyleMixin , JKQTPXYAndVectorGraph and JKQTPParametrizedVectorFieldGraph for a user - colored alternative
2024-02-09 04:52:30 +08:00
*/
class JKQTPLOTTER_LIB_EXPORT JKQTPVectorFieldGraph : public JKQTPXYAndVectorGraph , public JKQTPGraphDecoratedLineStyleMixin {
Q_OBJECT
public :
2024-02-09 20:48:41 +08:00
/** \brief indicates the position of the point \c (x,y) relative to the vector */
2024-02-09 04:52:30 +08:00
enum VectorAnchorPoint {
2024-02-09 20:48:41 +08:00
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 \c (x,y) \image html JKQTPVectorFieldGraphAnchorMid.png
AnchorTip //!< \brief the vector ends at \c (x,y) \image html JKQTPVectorFieldGraphAnchorTip.png
2024-02-09 04:52:30 +08:00
} ;
Q_ENUM ( VectorAnchorPoint )
2024-02-09 20:48:41 +08:00
/** \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 )
2024-02-09 23:45:40 +08:00
/** \brief describes how the line width scales with the vector properties (or not) */
enum VectorLineWidthMode {
DefaultVectorLineWidth , //!< \brief line width is equal to JKQTPGraphLineStyleMixin::getLineWidth() for all vectors \image html JKQTPVectorFieldGraphIgnoreLength.png
AutoscaleLineWidthFromLength , //!< \brief line width is determined from the vector length. The maximum line width is given by JKQTPGraphLineStyleMixin::getLineWidth() and the minim line width by getMinLineWidth() \image html JKQTPVectorFieldGraphAutoscaleLengthAutoscaleLineWidthFromLength.png
} ;
Q_ENUM ( VectorLineWidthMode )
2024-02-09 04:52:30 +08:00
/** \brief class constructor */
explicit JKQTPVectorFieldGraph ( JKQTBasePlotter * parent = nullptr ) ;
/** \brief class constructor */
JKQTPVectorFieldGraph ( JKQTPlotter * parent ) ;
2024-02-10 07:26:45 +08:00
/** \copydoc JKQTPXYAndVectorGraph::draw() */
2024-02-09 04:52:30 +08:00
virtual void draw ( JKQTPEnhancedPainter & painter ) override ;
2024-02-10 07:26:45 +08:00
/** \copydoc JKQTPXYAndVectorGraph::drawKeyMarker() */
2024-02-09 04:52:30 +08:00
virtual void drawKeyMarker ( JKQTPEnhancedPainter & painter , const QRectF & rect ) override ;
2024-02-10 07:26:45 +08:00
/** \copydoc JKQTPXYAndVectorGraph::getKeyLabelColor() */
2024-02-09 04:52:30 +08:00
virtual QColor getKeyLabelColor ( ) const override ;
2024-02-09 20:48:41 +08:00
/** \copydoc m_vectorLengthMode */
VectorLengthMode getVectorLengthMode ( ) const ;
/** \copydoc m_vectorLengthMode */
void setVectorLengthMode ( VectorLengthMode newMode ) ;
2024-02-09 04:52:30 +08:00
2024-02-09 18:41:19 +08:00
/** \copydoc m_autoscaleLengthFactor */
double getAutoscaleLengthFactor ( ) const ;
/** \copydoc m_autoscaleLengthFactor */
void setAutoscaleLengthFactor ( double newAutoscaleLengthFactor ) ;
2024-02-09 04:52:30 +08:00
/** \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 ) ;
2024-02-09 23:45:40 +08:00
/** \copydoc m_vectorLineWidthMode */
void setVectorLineWidthMode ( VectorLineWidthMode m ) ;
/** \copydoc m_vectorLineWidthMode */
VectorLineWidthMode getVectorLineWidthMode ( ) const ;
/** \copydoc m_minLineWidth */
void setMinLineWidth ( double lw ) ;
/** \copydoc m_minLineWidth */
double getMinLineWIdth ( ) const ;
2024-02-09 20:48:41 +08:00
Q_PROPERTY ( VectorLengthMode vectorLengthMode READ getVectorLengthMode WRITE setVectorLengthMode )
2024-02-09 18:41:19 +08:00
Q_PROPERTY ( bool autoscaleLengthFactor READ getAutoscaleLengthFactor WRITE setAutoscaleLengthFactor )
2024-02-09 04:52:30 +08:00
Q_PROPERTY ( double lengthScaleFactor READ getLengthScaleFactor WRITE setLengthScaleFactor )
Q_PROPERTY ( VectorAnchorPoint anchorPoint READ getAnchorPoint WRITE setAnchorPoint )
2024-02-09 23:45:40 +08:00
Q_PROPERTY ( double minLineWidth READ getMinLineWIdth WRITE setMinLineWidth )
Q_PROPERTY ( VectorLineWidthMode vectorLineWidthMode READ getVectorLineWidthMode WRITE setVectorLineWidthMode )
2024-02-09 04:52:30 +08:00
protected :
2024-02-10 07:26:45 +08:00
/** \brief internal color functor, a customization point for derived classes
*
* Overwrite this in a derived class to be able to color each vector differently */
virtual QColor getLocalVectorColor ( int i , double x , double y , double dx , double dy ) const ;
/** \brief acess to internally calculated (before draw() actually draws) minimum vector length
*/
inline double getMinVecLen ( ) const { return m_minVecLen ; } ;
/** \brief acess to internally calculated (before draw() actually draws) maximum vector length
*/
inline double getMaxVecLen ( ) const { return m_maxVecLen ; } ;
2024-02-09 04:52:30 +08:00
private :
2024-02-09 20:48:41 +08:00
/** \brief indicates how the length of the drawn vectors are determined from the data
2024-02-09 04:52:30 +08:00
*
2024-02-09 20:48:41 +08:00
* Several modes are possible :
* - If \ c = = LengthFromData the vector is drawn from \ c ( x , y ) to \ c ( x + dx , y + dy ) * m_lengthScaleFactor .
* \ image html JKQTPVectorFieldGraphAutoscaleLength . png
* - 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
* .
2024-02-09 18:41:19 +08:00
*
2024-02-09 23:45:40 +08:00
* \ see VectorLengthMode , setVectorLengthMode ( ) , getVectorLengthMode ( ) , m_lengthScaleFactor , m_autoscaleLengthFactor
2024-02-09 04:52:30 +08:00
*/
2024-02-09 20:48:41 +08:00
VectorLengthMode m_vectorLengthMode ;
/** \brief a scaling factor that can be used to modify the result of the autoscaling algorithm (m_vectorLengthMode \c ==AutoscaleLength)
2024-02-09 18:41:19 +08:00
*
* The vector length is further scaled by this value .
2024-02-09 20:48:41 +08:00
* \ see m_vectorLengthMode , setAutoscaleFactor ( ) , getAutoscaleFactor ( )
2024-02-09 18:41:19 +08:00
*/
double m_autoscaleLengthFactor ;
2024-02-09 20:48:41 +08:00
/** \brief if m_vectorLengthMode \c ==false, this is the scale-factor used to calculate the vector length
2024-02-09 18:41:19 +08:00
*
2024-02-09 20:48:41 +08:00
* \ see setLengthScaleFactor ( ) , getLengthScaleFactor ( ) , m_vectorLengthMode
2024-02-09 18:41:19 +08:00
*/
2024-02-09 04:52:30 +08:00
double m_lengthScaleFactor ;
2024-02-09 18:41:19 +08:00
/** \brief defines where the vector is anchored
*
2024-02-09 23:45:40 +08:00
* \ see VectorAnchorPoint , setAnchorPoint ( ) , getAnchorPoint ( )
2024-02-09 18:41:19 +08:00
*/
2024-02-09 04:52:30 +08:00
VectorAnchorPoint m_anchorPoint ;
2024-02-09 23:45:40 +08:00
/** \brief determines how the line width of the vectors is derived.
*
* \ note the available options are described with VectorLineWidthMode
*
* \ see setVectorLineWidthMode ( ) , getVectorLineWidthMode ( ) , VectorLineWidthMode , m_minLineWidth
*/
VectorLineWidthMode m_vectorLineWidthMode ;
/** \brief minimum line-width in pt, used for some modes of m_vectorLineWidthMode
*
* \ see setMinLineWidth ( ) , getMinLineWidth ( ) , m_vectorLineWidthMode
*/
double m_minLineWidth ;
2024-02-09 04:52:30 +08:00
2024-02-10 07:26:45 +08:00
/** \brief internally calculated (before draw() actually draws) minimum vector length
* \ internal
*/
double m_minVecLen ;
/** \brief internally calculated (before draw() actually draws) maximum vector length
* \ internal
*/
double m_maxVecLen ;
2024-02-09 04:52:30 +08:00
} ;
2024-02-10 07:26:45 +08:00
/*! \brief This graph plots a vector field, i.e. a set of vectors (dx,dy) or (angle,length) at positions (x,y).
This class is an extension of JKQTPVectorFieldGraph and additionally supports setting the line - color
from an additional data column .
\ ingroup jkqtplotter_vectorfieldgraphs
\ note This type of plot is sometimes also refered to as < b > quiver plot < / b > ( e . g . in Matlab or matplotlib )
\ image html JKQTPParametrizedVectorFieldGraph . 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)*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 ) * 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) \\ 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 ) ;
\ endcode
Use setVectorColorMode ( ) if you don ' t want to use a custom column , but just use the vector ' s magnitude / length or
rotation angle as parameter for the color .
\ see \ ref JKQTPParametrizedVectorFieldGraphExample , JKQTPVectorFieldGraph , JKQTPColorPaletteStyleAndToolsMixin
*/
class JKQTPLOTTER_LIB_EXPORT JKQTPParametrizedVectorFieldGraph : public JKQTPVectorFieldGraph , public JKQTPColorPaletteStyleAndToolsMixin {
Q_OBJECT
public :
/** \brief determines how the color of the vector is determined */
enum VectorColorMode {
DefaultColor , //!< \brief no color-coding, just use getLineColor() \image html JKQTPParametrizedVectorFieldGraphDefaultColor.png
ColorFromMagnitude , //!< \brief color-coding by vector magnitude/length \image html JKQTPParametrizedVectorFieldGraphColorFromMagnitude.png
ColorFromAngle , //!< \brief color-coding by vector angle \image html JKQTPParametrizedVectorFieldGraphColorFromAngle.png
ColorFromCustomColumn //!< \brief color-coding from getColorColumn() column \image html JKQTPParametrizedVectorFieldGraph.png
} ;
Q_ENUM ( VectorColorMode )
/** \brief class constructor */
explicit JKQTPParametrizedVectorFieldGraph ( JKQTBasePlotter * parent = nullptr ) ;
/** \brief class constructor */
JKQTPParametrizedVectorFieldGraph ( JKQTPlotter * parent ) ;
/** \copydoc JKQTPVectorFieldGraph::drawKeyMarker() */
virtual void drawKeyMarker ( JKQTPEnhancedPainter & painter , const QRectF & rect ) override ;
/** \copydoc JKQTPVectorFieldGraph::draw() */
virtual void draw ( JKQTPEnhancedPainter & painter ) override ;
/** \copydoc JKQTPGraph::setParent() */
virtual void setParent ( JKQTBasePlotter * parent ) override ;
/** \copydoc JKQTPGraph::getOutsideSize() */
virtual void getOutsideSize ( JKQTPEnhancedPainter & painter , int & leftSpace , int & rightSpace , int & topSpace , int & bottomSpace ) override ;
/** \copydoc JKQTPGraph::drawOutside() */
virtual void drawOutside ( JKQTPEnhancedPainter & painter , QRect leftSpace , QRect rightSpace , QRect topSpace , QRect bottomSpace ) override ;
/** \brief determine min/max data value of the image */
virtual void cbGetDataMinMax ( double & imin , double & imax ) override ;
/** \copydoc JKQTPGraph::usesColumn() */
virtual bool usesColumn ( int c ) const override ;
2024-02-12 22:01:46 +08:00
/** \copydoc m_colorColumn */
2024-02-10 07:26:45 +08:00
void setColorColumn ( int __value ) ;
2024-02-12 22:01:46 +08:00
/** \copydoc m_colorColumn */
2024-02-10 07:26:45 +08:00
int getColorColumn ( ) const ;
2024-02-12 22:01:46 +08:00
/** \copydoc m_colorColumn */
2024-02-10 07:26:45 +08:00
void setColorColumn ( size_t __value ) ;
2024-02-12 22:01:46 +08:00
/** \copydoc m_colorColumnContainsRGB */
2024-02-10 07:26:45 +08:00
void setColorColumnContainsRGB ( bool __value ) ;
2024-02-12 22:01:46 +08:00
/** \copydoc m_colorColumnContainsRGB */
2024-02-10 07:26:45 +08:00
bool getColorColumnContainsRGB ( ) const ;
/** \copydoc m_vectorColorMode */
void setVectorColorMode ( VectorColorMode __value ) ;
/** \copydoc m_vectorColorMode */
VectorColorMode getVectorColorMode ( ) const ;
protected :
/** \copdydoc JKQTPVectorFieldGraph::getLocalVectorColor() */
virtual QColor getLocalVectorColor ( int i , double x , double y , double dx , double dy ) const override ;
private :
/** \brief this column contains the symbol color
*
* \ see setColorColumn ( ) , getColorColumn ( )
*/
int m_colorColumn ;
/** \brief if this is true, the value in the colorColumn is converted to an integer, representing a color in ARGB format (as in QRgb)
*
* \ see setColorColumnContainsRGB ( ) , getColorColumnContainsRGB ( )
*/
bool m_colorColumnContainsRGB ;
/** \brief internally used to store the range of the color column
* \ internal
*/
double m_intColMin ;
/** \brief internally used to store the range of the color column
* \ internal
*/
double m_intColMax ;
/** \brief indicates how color is determined from data (either from the vector or from m_colorColumn) */
VectorColorMode m_vectorColorMode ;
} ;
2024-02-09 04:52:30 +08:00
# endif // jkqtpvectorfield_H