diff --git a/README.md b/README.md
index e5cb604adb..35f2839df5 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index 83f8452b0f..79c66b1377 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -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})
diff --git a/doc/dox/examples_and_tutorials.dox b/doc/dox/examples_and_tutorials.dox
index 068647bb3e..0f23b6c258 100644
--- a/doc/dox/examples_and_tutorials.dox
+++ b/doc/dox/examples_and_tutorials.dox
@@ -119,6 +119,9 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
\image html geo_rect_small.png
| JKQTPGeoRectangle
diff --git a/doc/dox/todo.dox b/doc/dox/todo.dox
index fc0c282611..5252ad70f0 100644
--- a/doc/dox/todo.dox
+++ b/doc/dox/todo.dox
@@ -12,7 +12,6 @@ This page lists several todos and wishes for future version of JKQTPlotter
data management: allow for other datatypes than double, would be good to have, double, float, ints, bool, string ... as for images
data management: binding for the Eigen library
graphic elements: annotation graphic element with text positionable like legend, or with (0..1),(0..1)-coordinates within plot
- graphic elements: cubic/bezier curves for graphic elements
graphic elements: make coordinate systems selectable for all: x/y-axis, 0..1/0..1, topleft/topright...
graphs: barchart/ranges chart with (x,y1,y2) or (x1,x2,y)
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
diff --git a/doc/dox/whatsnew.dox b/doc/dox/whatsnew.dox
index 45e3da7d53..aa8c0d2321 100644
--- a/doc/dox/whatsnew.dox
+++ b/doc/dox/whatsnew.dox
@@ -135,6 +135,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
NEW: JKQTPFinancialGraph for drawing candlestick or OHLC graphs of financial data, such as stock amrket prices (+example \ref JKQTPlotterFinancialChartExample)
NEW: stacked barcharts may have a small separation (default 1pt)
NEW: autoscaling for barcharts works now, also when stacked and unstacked charts are combined in one plot
+ NEW: JKQTPGeoBezierCurve for drawing bezier curves of degree 1-4 (+example \ref JKQTPlotterGeometricBezier)
JKQTMathText:
diff --git a/doc/images/JKQTPGeoBezierCurveGraphic.png b/doc/images/JKQTPGeoBezierCurveGraphic.png
new file mode 100644
index 0000000000..3b76e9a32d
Binary files /dev/null and b/doc/images/JKQTPGeoBezierCurveGraphic.png differ
diff --git a/doc/images/JKQTPGeoBezierCurveLogGraphic.png b/doc/images/JKQTPGeoBezierCurveLogGraphic.png
new file mode 100644
index 0000000000..21e8ede7d7
Binary files /dev/null and b/doc/images/JKQTPGeoBezierCurveLogGraphic.png differ
diff --git a/doc/images/JKQTPGeoBezierCurveLogMath.png b/doc/images/JKQTPGeoBezierCurveLogMath.png
new file mode 100644
index 0000000000..7e07db43c8
Binary files /dev/null and b/doc/images/JKQTPGeoBezierCurveLogMath.png differ
diff --git a/doc/images/JKQTPGeoBezierCurveMath.png b/doc/images/JKQTPGeoBezierCurveMath.png
new file mode 100644
index 0000000000..3b76e9a32d
Binary files /dev/null and b/doc/images/JKQTPGeoBezierCurveMath.png differ
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 47047b7231..ed055d609a 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -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)
diff --git a/examples/geo_bezier/CMakeLists.txt b/examples/geo_bezier/CMakeLists.txt
new file mode 100644
index 0000000000..0f3799d2c5
--- /dev/null
+++ b/examples/geo_bezier/CMakeLists.txt
@@ -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})
diff --git a/examples/geo_bezier/README.md b/examples/geo_bezier/README.md
new file mode 100644
index 0000000000..1b1402c4a6
--- /dev/null
+++ b/examples/geo_bezier/README.md
@@ -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)
+
diff --git a/examples/geo_bezier/geo_bezier.cpp b/examples/geo_bezier/geo_bezier.cpp
new file mode 100644
index 0000000000..4c23c7f2e3
--- /dev/null
+++ b/examples/geo_bezier/geo_bezier.cpp
@@ -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
+#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();
+}
diff --git a/lib/jkqtcommon/jkqtpmathtools.h b/lib/jkqtcommon/jkqtpmathtools.h
index 391fb282f8..1bec8ddb46 100644
--- a/lib/jkqtcommon/jkqtpmathtools.h
+++ b/lib/jkqtcommon/jkqtpmathtools.h
@@ -333,8 +333,8 @@ inline T jkqtp_sqr(const T& v) {
\ingroup jkqtptools_math_basic
*/
- template
- inline T jkqtp_pow4(T x) {
+template
+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
+#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
+#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
+std::function 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(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
diff --git a/lib/jkqtplotter/graphs/jkqtpgeolines.cpp b/lib/jkqtplotter/graphs/jkqtpgeolines.cpp
index 4c3dc95393..f1f65472a1 100644
--- a/lib/jkqtplotter/graphs/jkqtpgeolines.cpp
+++ b/lib/jkqtplotter/graphs/jkqtpgeolines.cpp
@@ -521,6 +521,8 @@ JKQTPGeoPolyLines::JKQTPGeoPolyLines(JKQTBasePlotter* parent, const QVectorpoints=points;
+ setHeadDecoratorStyle(JKQTPNoDecorator);
+ setTailDecoratorStyle(JKQTPNoDecorator);
}
JKQTPGeoPolyLines::JKQTPGeoPolyLines(JKQTPlotter* parent, const QVector& 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; imaxx) maxx=x;
+ if (x0) {
+ miny=points[0].y();
+ maxy=points[0].y();
+ for (int i=1; imaxy) maxy=y;
+ if (y=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 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 plotfunc;
+ const auto B2_0=jkqtp_makeBernstein(2,0);
+ const auto B2_1=jkqtp_makeBernstein(2,1);
+ const auto B2_2=jkqtp_makeBernstein(2,2);
+ const auto B3_0=jkqtp_makeBernstein(3,0);
+ const auto B3_1=jkqtp_makeBernstein(3,1);
+ const auto B3_2=jkqtp_makeBernstein(3,2);
+ const auto B3_3=jkqtp_makeBernstein(3,3);
+ const auto B4_0=jkqtp_makeBernstein(4,0);
+ const auto B4_1=jkqtp_makeBernstein(4,1);
+ const auto B4_2=jkqtp_makeBernstein(4,2);
+ const auto B4_3=jkqtp_makeBernstein(4,3);
+ const auto B4_4=jkqtp_makeBernstein(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 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 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 &__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 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();
+}
diff --git a/lib/jkqtplotter/graphs/jkqtpgeolines.h b/lib/jkqtplotter/graphs/jkqtpgeolines.h
index e784295294..f0913dc146 100644
--- a/lib/jkqtplotter/graphs/jkqtpgeolines.h
+++ b/lib/jkqtplotter/graphs/jkqtpgeolines.h
@@ -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 & __value);
+ /** \copydoc points */
+ QVector 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 points;
+};
+
+
/** \brief This JKQTPGeometricPlotElement is used to draw an arc
* \ingroup jkqtplotter_geoplots
*
diff --git a/lib/jkqtplotter/jkqtpdatastorage.cpp b/lib/jkqtplotter/jkqtpdatastorage.cpp
index 15e7dfc8f5..1f3cd448a5 100644
--- a/lib/jkqtplotter/jkqtpdatastorage.cpp
+++ b/lib/jkqtplotter/jkqtpdatastorage.cpp
@@ -714,6 +714,119 @@ void JKQTPDatastore::copyColumnData(size_t toColumn, size_t fromColumn)
setColumnImageWidth(toColumn, getColumnImageWidth(static_cast(fromColumn)));
}
+////////////////////////////////////////////////////////////////////////////////////////////////
+std::pair JKQTPDatastore::addCopiedPoints(const std::list &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 JKQTPDatastore::addCopiedPoints(const std::list &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 JKQTPDatastore::addCopiedPoints(const std::vector &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 JKQTPDatastore::addCopiedPoints(const std::vector &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 JKQTPDatastore::addCopiedPoints(const QList &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 JKQTPDatastore::addCopiedPoints(const QList &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 JKQTPDatastore::addCopiedPoints(const QVector &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 JKQTPDatastore::addCopiedPoints(const QVector &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) {
diff --git a/lib/jkqtplotter/jkqtpdatastorage.h b/lib/jkqtplotter/jkqtpdatastorage.h
index f7ff02cce5..53175a5227 100644
--- a/lib/jkqtplotter/jkqtpdatastorage.h
+++ b/lib/jkqtplotter/jkqtpdatastorage.h
@@ -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 addCopiedPoints(const QList& points, const QString& namex=QString(""), const QString &namey=QString(""));
+ std::pair addCopiedPoints(const QList& points, const QString& namex=QString(""), const QString &namey=QString(""));
+#if QT_VERSION addCopiedPoints(const QVector& points, const QString& namex=QString(""), const QString &namey=QString(""));
+ std::pair addCopiedPoints(const QVector& points, const QString& namex=QString(""), const QString &namey=QString(""));
+#endif
+ std::pair addCopiedPoints(const std::vector& points, const QString& namex=QString(""), const QString &namey=QString(""));
+ std::pair addCopiedPoints(const std::vector& points, const QString& namex=QString(""), const QString &namey=QString(""));
+ std::pair addCopiedPoints(const std::list& points, const QString& namex=QString(""), const QString &namey=QString(""));
+ std::pair addCopiedPoints(const std::list& 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
diff --git a/screenshots/geo_bezier.png b/screenshots/geo_bezier.png
new file mode 100644
index 0000000000..f2c0637cb1
Binary files /dev/null and b/screenshots/geo_bezier.png differ
diff --git a/screenshots/geo_bezier_small.png b/screenshots/geo_bezier_small.png
new file mode 100644
index 0000000000..e3e79c5fdb
Binary files /dev/null and b/screenshots/geo_bezier_small.png differ
|