NEW: JKQTPGeoBezierCurve for drawing bezier curves of degree 1-4 (+example)

This commit is contained in:
jkriege2 2024-02-13 23:49:23 +01:00
parent 4e7431ff77
commit 2365caf83b
21 changed files with 772 additions and 5 deletions

View File

@ -61,7 +61,7 @@ This software is licensed under the term of the [GNU Lesser General Public Licen
- [contour plots](https://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__imagelots__contour.html)
- [vector field graphs/quiver plots](https://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__vectorfieldgraphs.html)
- [financial graphs (candlestick/OHLC)](https://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__financialgraphs.html)
- [geometric forms](http://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__geoplots.html) / [annotations](http://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__annotations.html)
- [geometric forms (lines, rectangles, polygons, circles, bezier-curves, ...)](http://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__geoplots.html) / [annotations (labels, text, ranges, ...)](http://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__annotations.html)
- can be easily extended by deriving a new graph from [JKQTPPlotElement](http://jkriege2.github.io/JKQtPlotter/class_j_k_q_t_p_plot_element.html), [JKQTPPlotAnnotationElement](http://jkriege2.github.io/JKQtPlotter/class_j_k_q_t_p_plot_annotation_element.html), [JKQTPGeometricPlotElement](http://jkriege2.github.io/JKQtPlotter/class_j_k_q_t_p_geometric_plot_element.html), [JKQTPGraph](http://jkriege2.github.io/JKQtPlotter/class_j_k_q_t_p_graph.html)
- optional: [OpenCV interface](http://jkriege2.github.io/JKQtPlotter/group__jkqtpinterfaceopencv.html), [CImg interfaces](http://jkriege2.github.io/JKQtPlotter/group__jkqtpinterfacecimg.html)
- CMake-based build system

View File

@ -248,6 +248,7 @@ if(JKQtPlotter_BUILD_EXAMPLES)
vectorfield
paramvectorfield
financialgraphs
geo_bezier
)
@ -279,6 +280,7 @@ if(JKQtPlotter_BUILD_EXAMPLES)
vectorfield/JKQTPVectorFieldGraph,JKQTPVectorFieldGraphAnchorBottom,JKQTPVectorFieldGraphAnchorMid,JKQTPVectorFieldGraphAnchorTip,JKQTPVectorFieldGraphAutoscaleLength,JKQTPVectorFieldGraphLengthFromData,JKQTPVectorFieldGraphIgnoreLength,JKQTPVectorFieldGraphIgnoreLengthAutoscaleLineWidthFromLength,JKQTPVectorFieldGraphAutoscaleLengthAutoscaleLineWidthFromLength/--iteratefunctorsteps
paramvectorfield/JKQTPParametrizedVectorFieldGraph,JKQTPParametrizedVectorFieldGraphColorFromMagnitude,JKQTPParametrizedVectorFieldGraphColorFromAngle,JKQTPParametrizedVectorFieldGraphDefaultColor/--iteratefunctorsteps
financialgraphs/JKQTPFinancialGraph,JKQTPFinancialGraphCandleStick,JKQTPFinancialGraphSetCandlestickTwoColor,JKQTPFinancialGraphSetCandlestickTwoColor2,JKQTPFinancialGraphSetCandlestickOneColor,JKQTPFinancialGraphOHLC,JKQTPFinancialGraphSetOHLCTwoColor,JKQTPFinancialGraphSidyBySide/--iteratefunctorsteps
geo_bezier/JKQTPGeoBezierCurveGraphic,JKQTPGeoBezierCurveMath,JKQTPGeoBezierCurveLogMath,JKQTPGeoBezierCurveLogGraphic/--iteratefunctorsteps
)
@ -289,7 +291,6 @@ if(JKQtPlotter_BUILD_EXAMPLES)
foreach(ex ${JKQTPlotter_GenerateDocScreenshots_From})
set(example ${ex})
set(basename ${ex})

View File

@ -119,6 +119,9 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
<tr><td> \image html geo_arrows_small.png
<td> \subpage JKQTPlotterGeometricArrows
<td> `JKQTPGeoArrow`, ...
<tr><td> \image html geo_bezier_small.png
<td> \subpage JKQTPlotterGeometricBezier
<td> `JKQTPGeoBezierCurve`, ...
<tr><td> \image html geo_coordinateaxis0_small.png
<td> \subpage JKQTPlotterGeometricCoordinateAxis0
<td> `JKQTPCoordinateAxisStyle::drawMode0`, `JKQTPGeoPolygon`, `JKQTPGeoEllipse`

View File

@ -302,6 +302,9 @@ This group assembles graphs that add (textual) labels to the datapoints in a plo
<tr>
<td>\image html geo_arrows_small.png
<td> JKQTPGeoArrow
<tr>
<td>\image html geo_bezier_small.png
<td> JKQTPGeoBezierCurve
<tr>
<td>\image html geo_rect_small.png
<td> JKQTPGeoRectangle

View File

@ -12,7 +12,6 @@ This page lists several todos and wishes for future version of JKQTPlotter
<li>data management: allow for other datatypes than double, would be good to have, double, float, ints, bool, string ... as for images</li>
<li>data management: binding for the <a href="https://eigen.tuxfamily.org/index.php?title=Main_Page">Eigen library</a></li>
<li>graphic elements: annotation graphic element with text positionable like legend, or with (0..1),(0..1)-coordinates within plot</li>
<li>graphic elements: cubic/bezier curves for graphic elements</li>
<li>graphic elements: make coordinate systems selectable for all: x/y-axis, 0..1/0..1, topleft/topright... </li>
<li>graphs: barchart/ranges chart with (x,y1,y2) or (x1,x2,y)</li>
<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>

View File

@ -135,6 +135,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
<li>NEW: JKQTPFinancialGraph for drawing candlestick or OHLC graphs of financial data, such as stock amrket prices (+example \ref JKQTPlotterFinancialChartExample)</li>
<li>NEW: stacked barcharts may have a small separation (default 1pt)</li>
<li>NEW: autoscaling for barcharts works now, also when stacked and unstacked charts are combined in one plot</li>
<li>NEW: JKQTPGeoBezierCurve for drawing bezier curves of degree 1-4 (+example \ref JKQTPlotterGeometricBezier)</li>
</ul></li>
<li>JKQTMathText:<ul>

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -72,6 +72,7 @@ if (JKQtPlotter_BUILD_LIB_JKQTPLOTTER)
add_subdirectory(financialgraphs)
add_subdirectory(functionplot)
add_subdirectory(geo_arrows)
add_subdirectory(geo_bezier)
add_subdirectory(geo_simple)
add_subdirectory(geo_coordinateaxis0)
add_subdirectory(geometric)

View File

@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.23)
set(EXAMPLE_NAME geo_bezier)
set(EXENAME jkqtptest_${EXAMPLE_NAME})
message( STATUS ".. Building Example ${EXAMPLE_NAME}" )
# Set up source files
set(SOURCES ${EXAMPLE_NAME}.cpp)
set(HEADERS )
set(RESOURCES )
set(UIS )
add_executable(${EXENAME} WIN32 ${SOURCES} ${HEADERS} ${RESOURCES} ${UIS})
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})

View File

@ -0,0 +1,34 @@
# Example (JKQTPlotter): Plotting Arrows {#JKQTPlotterGeometricBezier}
This project shows the capabilities of JKQTPlotter to also draw arrows as geometric elements, using JKQTPGeoBezierCurve.
The source code of the main application can be found in [`geo_bezier.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/geo_bezier/geo_bezier.cpp). First a plot is generated. Then several types of bezier curves are added to the plot and their control points shown.
Here is an example for drawing a cubic bézier curve:
```.cpp
JKQTPGeoBezierCurve* bezCubic=new JKQTPGeoBezierCurve(&plot);
bezCubic->setCubic(QPointF(0.25,0.25), QPointF(0.8,2.5), QPointF(3.25,0.2), QPointF(3.75,2.75));
bezCubic->setLineColor(QColor("maroon"));
bezCubic->setHeadDecoratorSizeFactor(JKQTPArrow);
bezCubic->setTailDecoratorSizeFactor(JKQTPArrow);
plot.addGraph(bezCubic);
```
Finally we also add symbols for each control point and a poly-line connecting them:
```.cpp
JKQTPGeoPolyLines* l2;
plot.addGraph(l2=new JKQTPGeoPolyLines(&plot, bezCubic->getPoints()));
l2->setLineColor(QColor("darkgrey"));
l2->setLineWidth(1);
JKQTPXYScatterGraph* scatCubic=new JKQTPXYScatterGraph(&plot);
scatCubic->setXYColumns(plot.getDatastore()->addCopiedPoints(bezCubic->getPoints()));
scatCubic->setSymbolColor(QColor("blue"));
scatCubic->setSymbolType(JKQTPCircle);
plot.addGraph(scatCubic);
```
Here is the resulting plot:
![geo_bezier](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/doc/images/geo_bezier.png)

View File

@ -0,0 +1,149 @@
/** \example geo_bezier.cpp
* Shows how to plot bezier curves as geometric elements with JKQTPlotter
*
* \ref JKQTPlotterGeometricBezier
*/
#include "jkqtpexampleapplication.h"
#include <QApplication>
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/graphs/jkqtpgeolines.h"
#include "jkqtplotter/graphs/jkqtpscatter.h"
int main(int argc, char* argv[])
{
JKQTPAppSettingController highDPIController(argc, argv);
JKQTPExampleApplication app(argc, argv);
// 1. create a plotter window
JKQTPlotter plot;
// 2. format graph:
// 2.1 set the graph scales manually
plot.setXY(0.05,10.05,0.05,3.15);
// 2.2 set the asxpect ratio to 1
plot.getPlotter()->setMaintainAspectRatio(true);
plot.getPlotter()->setAspectRatio(10.05/3.05);
plot.getPlotter()->setMaintainAxisAspectRatio(true);
plot.getPlotter()->setAxisAspectRatio(10.05/3.05);
// 2.3 set the asxpect ratio to 1
plot.getXAxis()->setDrawGrid(false);
plot.getYAxis()->setDrawGrid(false);
auto fx1=[&](double i) { return 0.25+(3.0-i)*9.0/4.0;};
auto fx2=[&](double i) { return (4.0-i)*9.0/4.0;};
// 3.1 demonastrate linear bezier curve
JKQTPGeoBezierCurve* bezLine=new JKQTPGeoBezierCurve(&plot);
bezLine->setDrawMode(JKQTPGeometricPlotElement::DrawAsGraphicElement);
bezLine->setLine(QPointF(fx1(0),0.25), QPointF(fx2(0),2.75));
bezLine->setLineColor(QColor("maroon"));
plot.addGraph(bezLine);
JKQTPXYScatterGraph* scatLine=new JKQTPXYScatterGraph(&plot);
scatLine->setXYColumns(plot.getDatastore()->addCopiedPoints(bezLine->getPoints()));
scatLine->setSymbolColor(QColor("blue"));
scatLine->setSymbolType(JKQTPCircle);
plot.addGraph(scatLine);
// 3.2 demonastrate quad bezier curve
JKQTPGeoPolyLines* l1;
JKQTPGeoBezierCurve* bezQuad=new JKQTPGeoBezierCurve(&plot);
bezQuad->setDrawMode(JKQTPGeometricPlotElement::DrawAsGraphicElement);
bezQuad->setQuad(QPointF(fx1(1),0.25), QPointF(fx1(1)+0.25,2.5), QPointF(fx2(1),2.75));
bezQuad->setLineColor(QColor("maroon"));
plot.addGraph(bezQuad);
plot.addGraph(l1=new JKQTPGeoPolyLines(&plot, bezQuad->getPoints()));
l1->setLineColor(QColor("darkgrey"));
l1->setLineWidth(1);
JKQTPXYScatterGraph* scatQuad=new JKQTPXYScatterGraph(&plot);
scatQuad->setXYColumns(plot.getDatastore()->addCopiedPoints(bezQuad->getPoints()));
scatQuad->setSymbolColor(QColor("blue"));
scatQuad->setSymbolType(JKQTPCircle);
plot.addGraph(scatQuad);
// 3.3 demonastrate cubic bezier curve
JKQTPGeoPolyLines* l2;
JKQTPGeoBezierCurve* bezCubic=new JKQTPGeoBezierCurve(&plot);
bezCubic->setDrawMode(JKQTPGeometricPlotElement::DrawAsGraphicElement);
bezCubic->setCubic(QPointF(fx1(2),0.25), QPointF(fx1(2)+0.4,2.5), QPointF(fx2(2)-0.25,0.2), QPointF(fx2(2),2.75));
bezCubic->setLineColor(QColor("maroon"));
bezCubic->setHeadDecoratorStyle(JKQTPArrow);
bezCubic->setTailDecoratorStyle(JKQTPArrow);
plot.addGraph(bezCubic);
plot.addGraph(l2=new JKQTPGeoPolyLines(&plot, bezCubic->getPoints()));
l2->setLineColor(QColor("darkgrey"));
l2->setLineWidth(1);
JKQTPXYScatterGraph* scatCubic=new JKQTPXYScatterGraph(&plot);
scatCubic->setXYColumns(plot.getDatastore()->addCopiedPoints(bezCubic->getPoints()));
scatCubic->setSymbolColor(QColor("blue"));
scatCubic->setSymbolType(JKQTPCircle);
plot.addGraph(scatCubic);
// 3.4 demonastrate quartic bezier curve
JKQTPGeoPolyLines* l3;
JKQTPGeoBezierCurve* bezQuart=new JKQTPGeoBezierCurve(&plot);
bezQuart->setDrawMode(JKQTPGeometricPlotElement::DrawAsGraphicElement);
bezQuart->setQuartic(QPointF(fx1(3),2.25), QPointF((fx2(3)+fx1(3))/2.0,2), QPointF(fx1(3),0.75), QPointF((fx2(3)+fx1(3))/2.0,1.5), QPointF(fx2(3),0.2));
bezQuart->setLineColor(QColor("maroon"));
bezQuart->setHeadDecoratorStyle(JKQTPArrow);
bezQuart->setTailDecoratorStyle(JKQTPArrow);
plot.addGraph(bezQuart);
plot.addGraph(l3=new JKQTPGeoPolyLines(&plot, bezQuart->getPoints()));
l3->setLineColor(QColor("darkgrey"));
l3->setLineWidth(1);
JKQTPXYScatterGraph* scatQuartic=new JKQTPXYScatterGraph(&plot);
scatQuartic->setXYColumns(plot.getDatastore()->addCopiedPoints(bezQuart->getPoints()));
scatQuartic->setSymbolColor(QColor("blue"));
scatQuartic->setSymbolType(JKQTPCircle);
plot.addGraph(scatQuartic);
// 4. show plotter and make it a decent size
plot.show();
plot.resize(600/plot.devicePixelRatioF(),250/plot.devicePixelRatioF());
app.addExportStepFunctor([&](){
bezLine->setDrawMode(JKQTPGeometricPlotElement::DrawAsMathematicalCurve);
bezQuad->setDrawMode(JKQTPGeometricPlotElement::DrawAsMathematicalCurve);
bezCubic->setDrawMode(JKQTPGeometricPlotElement::DrawAsMathematicalCurve);
bezQuart->setDrawMode(JKQTPGeometricPlotElement::DrawAsMathematicalCurve);
plot.redrawPlot();
});
app.addExportStepFunctor([&](){
plot.getPlotter()->setMaintainAspectRatio(false);
plot.getPlotter()->setMaintainAxisAspectRatio(false);
plot.setXY(0.2,8.8,0.15,3.9);
bezLine->setDrawMode(JKQTPGeometricPlotElement::DrawAsMathematicalCurve);
bezQuad->setDrawMode(JKQTPGeometricPlotElement::DrawAsMathematicalCurve);
bezCubic->setDrawMode(JKQTPGeometricPlotElement::DrawAsMathematicalCurve);
bezQuart->setDrawMode(JKQTPGeometricPlotElement::DrawAsMathematicalCurve);
l1->setDrawMode(JKQTPGeometricPlotElement::DrawAsMathematicalCurve);
l2->setDrawMode(JKQTPGeometricPlotElement::DrawAsMathematicalCurve);
l3->setDrawMode(JKQTPGeometricPlotElement::DrawAsMathematicalCurve);
plot.getXAxis()->setLogAxis(true);
plot.getYAxis()->setLogAxis(true);
plot.redrawPlot();
});
app.addExportStepFunctor([&](){
plot.getPlotter()->setMaintainAspectRatio(false);
plot.getPlotter()->setMaintainAxisAspectRatio(false);
plot.getXAxis()->setLogAxis(true);
plot.getYAxis()->setLogAxis(true);
bezLine->setDrawMode(JKQTPGeometricPlotElement::DrawAsGraphicElement);
bezQuad->setDrawMode(JKQTPGeometricPlotElement::DrawAsGraphicElement);
bezCubic->setDrawMode(JKQTPGeometricPlotElement::DrawAsGraphicElement);
bezQuart->setDrawMode(JKQTPGeometricPlotElement::DrawAsGraphicElement);
l1->setDrawMode(JKQTPGeometricPlotElement::DrawAsGraphicElement);
l2->setDrawMode(JKQTPGeometricPlotElement::DrawAsGraphicElement);
l3->setDrawMode(JKQTPGeometricPlotElement::DrawAsGraphicElement);
plot.redrawPlot();
});
return app.exec();
}

View File

@ -333,8 +333,8 @@ inline T jkqtp_sqr(const T& v) {
\ingroup jkqtptools_math_basic
*/
template <class T>
inline T jkqtp_pow4(T x) {
template <class T>
inline T jkqtp_pow4(T x) {
const T xx=x*x;
return xx*xx;
}
@ -529,6 +529,7 @@ inline double jkqtp_polyEval(double x, PolyItP firstP, PolyItP lastP) {
return v;
}
/*! \brief a C++-functor, which evaluates a polynomial
\ingroup jkqtptools_math_basic
*/
@ -578,12 +579,79 @@ QString jkqtp_polynomialModel2Latex(PolyItP firstP, PolyItP lastP) {
return str;
}
/*! \brief Calculates a factorial \f$ n!=n\cdot(n-1)\cdot(n-2)\cdot...\cdot2\cdot1 \f$
\ingroup jkqtptools_math_basic
*/
template <class T=int>
#ifdef __cpp_consteval
consteval
#else
constexpr
#endif
T jkqtp_factorial(T n) {
T result = 1;
for (T i =1; i <= n; i++){
result = result*i;
}
return result;
}
/*! \brief Calculates a combination \f$ \left(\stackrel{n}{k}\right)=\frac{n!}{k!\cdot(n-k)!} \f$
\ingroup jkqtptools_math_basic
*/
template <class T=int>
#ifdef __cpp_consteval
consteval
#else
constexpr
#endif
T jkqtp_combination(T n, T k) {
if (n==k) return 1;
if (k==0) return 1;
if (k>n) return 0;
return jkqtp_factorial(n)/(jkqtp_factorial(k)*jkqtp_factorial(n-k));
}
/*! \brief creates a functor that evaluates the Bernstein polynomial \f$ B_i^n(t):=\left(\stackrel{n}{i}\right)\cdot t^i\cdot(1-t)^{n-1},\ \ \ \ 0\leq i\leq n \f$
\ingroup jkqtptools_math_basic
*/
template <class T>
std::function<T(T)> jkqtp_makeBernstein(int n, int i){
if (n==0 && i==0) return [=](T t) { return 1; };
if (n==1 && i==0) return [=](T t) { return (1.0-t); };
if (n==1 && i==1) return [=](T t) { return t; };
if (n==2 && i==0) return [=](T t) { return jkqtp_sqr(1.0-t); };
if (n==2 && i==1) return [=](T t) { return T(2.0)*t*(1.0-t); };
if (n==2 && i==2) return [=](T t) { return jkqtp_sqr(t); };
if (n==3 && i==0) return [=](T t) { return T(1)*jkqtp_cube(1.0-t); };
if (n==3 && i==1) return [=](T t) { return T(3)*t*jkqtp_sqr(1.0-t); };
if (n==3 && i==2) return [=](T t) { return T(3)*jkqtp_sqr(t)*(1.0-t); };
if (n==3 && i==3) return [=](T t) { return T(1)*jkqtp_cube(t); };
if (n==4 && i==0) return [=](T t) { return T(1)*jkqtp_pow4(1.0-t); };
if (n==4 && i==1) return [=](T t) { return T(4)*t*jkqtp_cube(1.0-t); };
if (n==4 && i==2) return [=](T t) { return T(6)*jkqtp_sqr(t)*jkqtp_sqr(1.0-t); };
if (n==4 && i==3) return [=](T t) { return T(4)*jkqtp_cube(t)*(1.0-t); };
if (n==4 && i==4) return [=](T t) { return T(1)*jkqtp_pow4(t); };
const T fac=jkqtp_combination<int64_t>(n,i);
return [=](T t) { return fac*pow(t,i)*pow(1.0-t,n-i); };
}
/*! \brief calculate the grwates common divisor (GCD) of \a a and \a b
\ingroup jkqtptools_math_basic
*/
JKQTCOMMON_LIB_EXPORT uint64_t jkqtp_gcd(uint64_t a, uint64_t b);
/*! \brief calculates numeratur integer part \a intpart , \a num and denominator \a denom of a fraction, representing a given floating-point number \a input
\ingroup jkqtptools_math_basic

View File

@ -521,6 +521,8 @@ JKQTPGeoPolyLines::JKQTPGeoPolyLines(JKQTBasePlotter* parent, const QVector<QPoi
JKQTPGeoBaseDecoratedLine(parent)
{
this->points=points;
setHeadDecoratorStyle(JKQTPNoDecorator);
setTailDecoratorStyle(JKQTPNoDecorator);
}
JKQTPGeoPolyLines::JKQTPGeoPolyLines(JKQTPlotter* parent, const QVector<QPointF>& points):
@ -845,3 +847,226 @@ bool JKQTPGeoArc::getYMinMax(double& miny, double& maxy, double& smallestGreater
JKQTPGeoBezierCurve::JKQTPGeoBezierCurve(JKQTBasePlotter *parent, const QPointF &start, const QPointF &control1, const QPointF &end):
JKQTPGeoBaseDecoratedLine(parent)
{
setQuad(start,control1,end);
setHeadDecoratorStyle(JKQTPNoDecorator);
setTailDecoratorStyle(JKQTPNoDecorator);
}
JKQTPGeoBezierCurve::JKQTPGeoBezierCurve(JKQTPlotter *parent, const QPointF &start, const QPointF &control1, const QPointF &end):
JKQTPGeoBezierCurve(parent->getPlotter(),start,control1,end)
{
}
JKQTPGeoBezierCurve::JKQTPGeoBezierCurve(JKQTBasePlotter *parent, const QPointF &start, const QPointF &control1, const QPointF &control2, const QPointF &end):
JKQTPGeoBaseDecoratedLine(parent)
{
setCubic(start,control1,control2,end);
setHeadDecoratorStyle(JKQTPNoDecorator);
setTailDecoratorStyle(JKQTPNoDecorator);
}
JKQTPGeoBezierCurve::JKQTPGeoBezierCurve(JKQTPlotter *parent, const QPointF &start, const QPointF &control1, const QPointF &control2, const QPointF &end):
JKQTPGeoBezierCurve(parent->getPlotter(),start,control1,control2,end)
{
}
JKQTPGeoBezierCurve::JKQTPGeoBezierCurve(JKQTBasePlotter *parent):
JKQTPGeoBaseDecoratedLine(parent)
{
setHeadDecoratorStyle(JKQTPNoDecorator);
setTailDecoratorStyle(JKQTPNoDecorator);
}
JKQTPGeoBezierCurve::JKQTPGeoBezierCurve(JKQTPlotter *parent):
JKQTPGeoBezierCurve(parent->getPlotter())
{
}
bool JKQTPGeoBezierCurve::getXMinMax(double& minx, double& maxx, double& smallestGreaterZero) {
minx=0;
maxx=0;
smallestGreaterZero=0;
if (points.size()>0) {
minx=points[0].x();
maxx=points[0].x();
for (int i=1; i<points.size(); i++) {
double x=points[i].x();
if (x>maxx) maxx=x;
if (x<minx) minx=x;
double xvsgz;
xvsgz=x; SmallestGreaterZeroCompare_xvsgz();
}
return true;
}
return false;
//qDebug()<<"getXMinMax"<<minx<<maxx;
}
bool JKQTPGeoBezierCurve::getYMinMax(double& miny, double& maxy, double& smallestGreaterZero) {
miny=0;
maxy=0;
smallestGreaterZero=0;
if (points.size()>0) {
miny=points[0].y();
maxy=points[0].y();
for (int i=1; i<points.size(); i++) {
double y=points[i].y();
if (y>maxy) maxy=y;
if (y<miny) miny=y;
double xvsgz;
xvsgz=y; SmallestGreaterZeroCompare_xvsgz();
}
return true;
}
return false;
//qDebug()<<"getYMinMax"<<miny<<maxy;
}
void JKQTPGeoBezierCurve::draw(JKQTPEnhancedPainter& painter) {
clearHitTestData();
if (points.size()>=2) {
reserveHitTestData(points.size());
double angle1, angle2;
QPointF xx1, xx2;
bool doDrawDecorator=false;
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
painter.setPen(getLinePen(painter, parent));
painter.setBrush(Qt::NoBrush);
if ((points.size()<=4) && ((getDrawMode()==DrawAsGraphicElement) || (getXAxis()->isLinearAxis() && getYAxis()->isLinearAxis()))) {
const QVector<QPointF> path=transform(points);
angle1=atan2(path[1].y()-path[0].y(), path[1].x()-path[0].x());
angle2=atan2(path[path.size()-2].y()-path[path.size()-1].y(), path[path.size()-2].x()-path[path.size()-1].x());
xx1=path[0];
xx2=path[path.size()-1];
QPainterPath ppath;
if (path.size()>0) ppath.moveTo(path[0]);
if (path.size()==2) ppath.lineTo(path[1]);
else if (path.size()==3) ppath.quadTo(path[1], path[2]);
else if (path.size()==4) ppath.cubicTo(path[1], path[2], path[3]);
// draw corrected line
if (path.size()>0) {
painter.drawPath(ppath);
doDrawDecorator=true;
}
} else {
if (points.size()>1) {
std::function<QPointF(double)> plotfunc;
const auto B2_0=jkqtp_makeBernstein<double>(2,0);
const auto B2_1=jkqtp_makeBernstein<double>(2,1);
const auto B2_2=jkqtp_makeBernstein<double>(2,2);
const auto B3_0=jkqtp_makeBernstein<double>(3,0);
const auto B3_1=jkqtp_makeBernstein<double>(3,1);
const auto B3_2=jkqtp_makeBernstein<double>(3,2);
const auto B3_3=jkqtp_makeBernstein<double>(3,3);
const auto B4_0=jkqtp_makeBernstein<double>(4,0);
const auto B4_1=jkqtp_makeBernstein<double>(4,1);
const auto B4_2=jkqtp_makeBernstein<double>(4,2);
const auto B4_3=jkqtp_makeBernstein<double>(4,3);
const auto B4_4=jkqtp_makeBernstein<double>(4,4);
if (points.size()==2) plotfunc=[&](double t) -> QPointF { return points[0]+t*(points[1]-points[0]); };
else if (points.size()==3) plotfunc=[&](double t) -> QPointF { return points[0]*B2_0(t)+points[1]*B2_1(t)+points[2]*B2_2(t); };
else if (points.size()==4) plotfunc=[&](double t) -> QPointF { return points[0]*B3_0(t)+points[1]*B3_1(t)+points[2]*B3_2(t)+points[3]*B3_3(t); };
else if (points.size()==5) plotfunc=[&](double t) -> QPointF { return points[0]*B4_0(t)+points[1]*B4_1(t)+points[2]*B4_2(t)+points[3]*B4_3(t)+points[4]*B4_4(t); };
if (plotfunc) {
std::function<QPointF(double)> fTransformedFunc= std::bind([plotfunc](const JKQTPPlotElement* plot, double t) -> QPointF { return plot->transform(plotfunc(t)); }, this, std::placeholders::_1);
const int minSamples=10;
const int maxRefinementDegree=5;
const double slopeTolerance=0.005;
const int minPixelPerSample=32;
const double maxConsecutiveAngleDegree=0.2;
JKQTPAdaptiveFunctionGraphEvaluator evaluator(fTransformedFunc, minSamples, maxRefinementDegree, slopeTolerance, minPixelPerSample);
QVector<QPointF> data=evaluator.evaluate(0,1);
data=JKQTPSimplyfyLineSegemnts(data, maxConsecutiveAngleDegree);
painter.drawPolylineFast(data.data(), data.size());
doDrawDecorator=true;
}
}
}
// potentially draw line-end decorators/arrows
if (doDrawDecorator) {
painter.setBrush(getLineColor());
JKQTPPlotLineDecorator(painter, xx1.x(), xx1.y(), angle1, getTailDecoratorStyle(), calcTailDecoratorSize(getLinePen(painter, getParent()).widthF()));
JKQTPPlotLineDecorator(painter, xx2.x(), xx2.y(), angle2, getHeadDecoratorStyle(), calcHeadDecoratorSize(getLinePen(painter, getParent()).widthF()));
}
for (const auto& p:points) {
addHitTestData(p.x(), p.y());
}
}
}
void JKQTPGeoBezierCurve::setPoints(const QVector<QPointF> &__value)
{
const int maxPoints=5;
if (points.size()<2 || points.size()>maxPoints) throw std::runtime_error("JKQTPGeoBezierCurve only supports 2, 3 ... or "+std::to_string(maxPoints)+" points, but you supplied "+std::to_string(__value.size())+"!");
points=__value;
}
QVector<QPointF> JKQTPGeoBezierCurve::getPoints() const
{
return points;
}
QPointF JKQTPGeoBezierCurve::getStart() const
{
return points[0];
}
QPointF JKQTPGeoBezierCurve::getEnd() const
{
return points.last();
}
QPointF JKQTPGeoBezierCurve::getControl1() const
{
return points[1];
}
QPointF JKQTPGeoBezierCurve::getControl2() const
{
return points[2];
}
void JKQTPGeoBezierCurve::setLine(const QPointF &start, const QPointF &end)
{
points={start,end};
}
void JKQTPGeoBezierCurve::setQuad(const QPointF &start, const QPointF &control1, const QPointF &end)
{
points={start,control1,end};
}
void JKQTPGeoBezierCurve::setCubic(const QPointF &start, const QPointF &control1, const QPointF &control2, const QPointF &end)
{
points={start,control1,control2,end};
}
void JKQTPGeoBezierCurve::setQuartic(const QPointF &start, const QPointF &control1, const QPointF &control2, const QPointF &control3, const QPointF &end)
{
points={start,control1,control2,control3,end};
}
int JKQTPGeoBezierCurve::getDegree() const
{
return points.size()-1;
}
int JKQTPGeoBezierCurve::getNumberOfCOntrolPoints() const
{
return points.size();
}

View File

@ -452,6 +452,127 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPGeoPolyLines: public JKQTPGeoBaseDecoratedLine
};
/** \brief This JKQTPGeometricPlotElement is used to draw a bezier curve
* \ingroup jkqtplotter_geoplots
*
* \image html JKQTPlotterGeometricBezierGraphic.png "drawn with JKQTPGeometricPlotElement::DrawMode::DrawAsGraphicElement"
*
* \see \ref JKQTPlotterGeometricBezier, JKQTPGeoBaseDecoratedLine
*
* \section JKQTPGeoBezierCurveDrawMode DrawMode for JKQTPGeoBezierCurve
* This class support JKQTPGeometricPlotElement::DrawMode::DrawAsMathematicalCurve , which will only have a significant effect with logarithmic axes.
* THe image above is the default JKQTPGeometricPlotElement::DrawMode::DrawAsGraphicElement on a linear axis.
*
* On logarithmic axes (x&y) the two modes draw very different shapes:
*
* \image html JKQTPlotterGeometricBezierLogMath.png "drawn with JKQTPGeometricPlotElement::DrawMode::DrawAsMathematicalCurve"
* \image html JKQTPlotterGeometricBezierLogGraphic.png "drawn with JKQTPGeometricPlotElement::DrawMode::DrawAsGraphicElement"
*
* For DrawAsGraphicElement only the control points are converted to screen-coordinates, but drawing takes place in the (linear) screen-system.
* For DrawAsMathematicalCurve drawing is done in the log-coordinate system.
*
* \section JKQTPGeoBezierCurveDecorators Line-End Decorators for JKQTPGeoBezierCurve
*
* You can also activate line-end decorators (aka arrows) for this poly-line, by using code like this:
* \code
* bezier->setHeadDecoratorStyle(JKQTPFilledDoubleArrow);
* bezier->setTailDecoratorStyle(JKQTPCircleDecorator);
* \endcode
*
* \see \ref JKQTPlotterGeometricBezier
*
*/
class JKQTPLOTTER_LIB_EXPORT JKQTPGeoBezierCurve: public JKQTPGeoBaseDecoratedLine {
Q_OBJECT
public:
/** \brief class constructor with start, end and one control point (i.e. a quadratic bezier curve)
*
* \param parent the parent plotter object
* \param points points on the polygon
*/
JKQTPGeoBezierCurve(JKQTBasePlotter* parent, const QPointF& start, const QPointF& control1, const QPointF& end);
/** \brief class constructor with start, end and one control point (i.e. a quadratic bezier curve)
*
* \param parent the parent plotter object
* \param points points on the polygon
*/
JKQTPGeoBezierCurve(JKQTPlotter* parent, const QPointF& start, const QPointF& control1, const QPointF& end);
/** \brief class constructor with start, end and two control points (i.e. a cubic bezier curve)
*
* \param parent the parent plotter object
* \param points points on the polygon
*/
JKQTPGeoBezierCurve(JKQTBasePlotter* parent, const QPointF& start, const QPointF& control1, const QPointF& control2, const QPointF& end);
/** \brief class constructor with start, end and two control points (i.e. a cubic bezier curve)
*
* \param parent the parent plotter object
* \param points points on the polygon
*/
JKQTPGeoBezierCurve(JKQTPlotter* parent, const QPointF& start, const QPointF& control1, const QPointF& control2, const QPointF& end);
/** \brief class constructor
*
* \param parent the parent plotter object
*/
JKQTPGeoBezierCurve(JKQTBasePlotter* parent);
/** \brief class constructor
*
* \param parent the parent plotter object
*/
JKQTPGeoBezierCurve(JKQTPlotter* parent);
/** \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;
/** \brief plots the graph to the plotter object specified as parent
*
* \note This function support JKQTPGeometricPlotElement::DrawMode::DrawAsMathematicalCurve. If set,
* and non-linear axes are chosen, the points of the poly-line will be possibly
* connected by curves, instead of straight lines. In the mode
* JKQTPGeometricPlotElement::DrawMode::DrawAsGraphicElement the points are connected by straight
* lines, independent of the linearity or non-linearity of the coordinate axes.
*/
virtual void draw(JKQTPEnhancedPainter& painter) override;
/** \copydoc points */
void setPoints(const QVector<QPointF> & __value);
/** \copydoc points */
QVector<QPointF> getPoints() const;
/** \brief get the start point of the curve */
QPointF getStart()const;
/** \brief get the end point of the curve */
QPointF getEnd()const;
/** \brief get the control point 1 of the curve */
QPointF getControl1()const;
/** \brief get the control point 2 of the curve */
QPointF getControl2()const;
/** \brief set a linear bezier curve (2 control points) */
void setLine(const QPointF& start, const QPointF& end);
/** \brief set a quadratic bezier curve (3 control points) */
void setQuad(const QPointF& start, const QPointF& control1, const QPointF& end);
/** \brief set a cubic bezier curve (4 control points) */
void setCubic(const QPointF& start, const QPointF& control1, const QPointF& control2, const QPointF& end);
/** \brief set a cubic bezier curve (5 control points) */
void setQuartic(const QPointF& start, const QPointF& control1, const QPointF& control2, const QPointF& control3, const QPointF& end);
/** \brief get the degree of the curve (number of points -1) */
int getDegree() const;
/** \brief get the number of control points (including start and end) */
int getNumberOfCOntrolPoints() const;
protected:
/** \brief list with all control points of the bezier curve
*
* \note This class supports at most 4 points are alllowed
*/
QVector<QPointF> points;
};
/** \brief This JKQTPGeometricPlotElement is used to draw an arc
* \ingroup jkqtplotter_geoplots
*

View File

@ -714,6 +714,119 @@ void JKQTPDatastore::copyColumnData(size_t toColumn, size_t fromColumn)
setColumnImageWidth(toColumn, getColumnImageWidth(static_cast<int>(fromColumn)));
}
////////////////////////////////////////////////////////////////////////////////////////////////
std::pair<size_t, size_t> JKQTPDatastore::addCopiedPoints(const std::list<QPoint> &points, const QString &namex, const QString &namey)
{
const auto cx=addColumn(points.size(),namex);
const auto cy=addColumn(points.size(),namey);
int i=0;
for (const auto& p: points) {
set(cx, i, p.x());
set(cy, i, p.y());
i++;
}
return {cx,cy};
}
////////////////////////////////////////////////////////////////////////////////////////////////
std::pair<size_t, size_t> JKQTPDatastore::addCopiedPoints(const std::list<QPointF> &points, const QString &namex, const QString &namey)
{
const auto cx=addColumn(points.size(),namex);
const auto cy=addColumn(points.size(),namey);
int i=0;
for (const auto& p: points) {
set(cx, i, p.x());
set(cy, i, p.y());
i++;
}
return {cx,cy};
}
////////////////////////////////////////////////////////////////////////////////////////////////
std::pair<size_t, size_t> JKQTPDatastore::addCopiedPoints(const std::vector<QPoint> &points, const QString &namex, const QString &namey)
{
const auto cx=addColumn(points.size(),namex);
const auto cy=addColumn(points.size(),namey);
int i=0;
for (const auto& p: points) {
set(cx, i, p.x());
set(cy, i, p.y());
i++;
}
return {cx,cy};
}
////////////////////////////////////////////////////////////////////////////////////////////////
std::pair<size_t, size_t> JKQTPDatastore::addCopiedPoints(const std::vector<QPointF> &points, const QString &namex, const QString &namey)
{
const auto cx=addColumn(points.size(),namex);
const auto cy=addColumn(points.size(),namey);
int i=0;
for (const auto& p: points) {
set(cx, i, p.x());
set(cy, i, p.y());
i++;
}
return {cx,cy};
}
////////////////////////////////////////////////////////////////////////////////////////////////
std::pair<size_t, size_t> JKQTPDatastore::addCopiedPoints(const QList<QPoint> &points, const QString &namex, const QString &namey)
{
const auto cx=addColumn(points.size(),namex);
const auto cy=addColumn(points.size(),namey);
int i=0;
for (const auto& p: points) {
set(cx, i, p.x());
set(cy, i, p.y());
i++;
}
return {cx,cy};
}
////////////////////////////////////////////////////////////////////////////////////////////////
std::pair<size_t, size_t> JKQTPDatastore::addCopiedPoints(const QList<QPointF> &points, const QString &namex, const QString &namey)
{
const auto cx=addColumn(points.size(),namex);
const auto cy=addColumn(points.size(),namey);
int i=0;
for (const auto& p: points) {
set(cx, i, p.x());
set(cy, i, p.y());
i++;
}
return {cx,cy};
}
#if QT_VERSION<QT_VERSION_CHECK(6,0,0)
////////////////////////////////////////////////////////////////////////////////////////////////
std::pair<size_t, size_t> JKQTPDatastore::addCopiedPoints(const QVector<QPoint> &points, const QString &namex, const QString &namey)
{
const auto cx=addColumn(points.size(),namex);
const auto cy=addColumn(points.size(),namey);
int i=0;
for (const auto& p: points) {
set(cx, i, p.x());
set(cy, i, p.y());
i++;
}
return {cx,cy};
}
////////////////////////////////////////////////////////////////////////////////////////////////
std::pair<size_t, size_t> JKQTPDatastore::addCopiedPoints(const QVector<QPointF> &points, const QString &namex, const QString &namey)
{
const auto cx=addColumn(points.size(),namex);
const auto cy=addColumn(points.size(),namey);
int i=0;
for (const auto& p: points) {
set(cx, i, p.x());
set(cy, i, p.y());
i++;
}
return {cx,cy};
}
#endif
////////////////////////////////////////////////////////////////////////////////////////////////
size_t JKQTPDatastore::addLinearColumn(size_t rows, double start, double end, const QString& name) {

View File

@ -824,6 +824,25 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPDatastore{
/** \brief add two columns to the datastore. They will be filled with the values from \a points (first column: x-value, second column: y-value)
*
* \param points list of datapoints to add
* \param namex name for the column with the x-values
* \param namey name for the column with the y-values
* \return the IDs of the newly created column
*
*/
std::pair<size_t,size_t> addCopiedPoints(const QList<QPointF>& points, const QString& namex=QString(""), const QString &namey=QString(""));
std::pair<size_t,size_t> addCopiedPoints(const QList<QPoint>& points, const QString& namex=QString(""), const QString &namey=QString(""));
#if QT_VERSION<QT_VERSION_CHECK(6,0,0)
std::pair<size_t,size_t> addCopiedPoints(const QVector<QPointF>& points, const QString& namex=QString(""), const QString &namey=QString(""));
std::pair<size_t,size_t> addCopiedPoints(const QVector<QPoint>& points, const QString& namex=QString(""), const QString &namey=QString(""));
#endif
std::pair<size_t,size_t> addCopiedPoints(const std::vector<QPointF>& points, const QString& namex=QString(""), const QString &namey=QString(""));
std::pair<size_t,size_t> addCopiedPoints(const std::vector<QPoint>& points, const QString& namex=QString(""), const QString &namey=QString(""));
std::pair<size_t,size_t> addCopiedPoints(const std::list<QPointF>& points, const QString& namex=QString(""), const QString &namey=QString(""));
std::pair<size_t,size_t> addCopiedPoints(const std::list<QPoint>& points, const QString& namex=QString(""), const QString &namey=QString(""));
/** \brief add one column to the datastore. It will be filled with the values from \a first ... \a last
*
* \tparam TIterator a standard C++ iterator

BIN
screenshots/geo_bezier.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB