added violin plots and an example demonstrating their usage/generation from random data

This commit is contained in:
jkriege2 2019-06-16 19:15:40 +02:00
parent 1363cf7012
commit f5abce1e6d
32 changed files with 2443 additions and 4 deletions

View File

@ -86,6 +86,7 @@ addSimpleTest(datastore_statistics_2d)
addSimpleTest(datastore_regression)
addSimpleTest(datastore_groupedstat)
addSimpleTest(contourplot)
addSimpleTest(violinplot)
#addSimpleTest(rgbimageplot_opencv)
#addSimpleTest(imageplot_opencv)

View File

@ -62,6 +62,9 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
<tr><td> \image html jkqtplotter_simpletest_boxplot_small.png
<td> \subpage JKQTPlotterBoxplotsGraphs
<td> `JKQTPBoxplotVerticalGraph`, `JKQTPBoxplotHorizontalGraph`, ...
<tr><td> \image html jkqtplotter_simpletest_violinplot_small.png
<td> \subpage JKQTPlotterViolinplotGraphs
<td> `JKQTPViolinplotVerticalElement`, `JKQTPViolinplotHorizontalElement`, ...
</table>

View File

@ -377,6 +377,9 @@ This group assembles graphs that show their data with symbols and optionally wit
<tr>
<td>\image html jkqtplotter_simpletest_boxplot_small.png
<td> JKQTPBoxplotVerticalGraph, JKQTPBoxplotHorizontalGraph
<tr>
<td>\image html JKQTPViolinplotVerticalElement.png
<td> JKQTPViolinplotVerticalElement, JKQTPViolinplotHorizontalElement
</table>
\see \ref jkqtptools_math_statistics_adaptors for shortcuts to calculate statistical properties of data and then adding a plot with the results.
@ -418,6 +421,9 @@ This group assembles graphs that show their data with symbols and optionally wit
<tr>
<td>\image html geo_boxplot_small.png
<td> JKQTPBoxplotVerticalElement, JKQTPBoxplotHorizontalElement
<tr>
<td>\image html JKQTPViolinplotVerticalElement.png
<td> JKQTPViolinplotVerticalElement, JKQTPViolinplotHorizontalElement
</table>
Examples:

View File

@ -26,7 +26,7 @@ Changes, compared to \ref page_whatsnew_V2018_08 "v2018.08" include:
<li> changed: using static const variables instead of <code>#define</code> for fixed default values (e.g. JKQTPImageTools::LUTSIZE, JKQTPImageTools::PALETTE_ICON_WIDTH, JKQTPlotterDrawinTools::ABS_MIN_LINEWIDTH, JKQTMathText::ABS_MIN_LINEWIDTH ...)</li>
<li> Updates to JKQTPlotter:
<ul>
<li> new: added JKQTPSingleColumnSymbolsGraph for single-column data, e.g. drawn as (random) scatter or bee-swar plots </li>
<li> new: added JKQTPSingleColumnSymbolsGraph for single-column data, e.g. drawn as (random) scatter or bee-swarm or rug plots </li>
<li> new: stacked barcharts with JKQTPBarVerticalStackableGraph, JKQTPBarHorizontalStackableGraph </li>
<li> new: use/support of C++11 features (e.g. \c std::function<> and lambda functions in JKQTPXFunctionLineGraph / JKQTPYFunctionLineGraph )</li>
<li> new: \ref jkqtpopencvinterface "optional OpenCV interface" </li>
@ -46,6 +46,7 @@ Changes, compared to \ref page_whatsnew_V2018_08 "v2018.08" include:
<li> new: several new plot symbols, see: JKQTPGraphSymbols </li>
<li> new: Statistics library with functions to calculate histograms, regression, kernel density estimates, ... see: \ref jkqtptools_math_statistics </li>
<li> new: iterator interface and improved documentation for JKQTPDatastore </li>
<li> new: violin plots (see \ref JKQTPlotterViolinplotGraphs ) </li>
<li> changed: removed old selection-code and replaced by general highlighting feature </li>
<li> changed: JKQTPStepHorizontalGraph has been renamed to JKQTPSpecialLineHorizontalGraph (vertical variants also) and have gained additional features (baseline for filling and drawing of symbols) </li>
<li> changed: filled curve graphs (e.g. JKQTPSpecialLineHorizontalGraph) are now merely a specializedly initialized JKQTPSpecialLineHorizontalGraph </li>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -24,6 +24,7 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_parsedfunctionplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_parsedfunctionplot) | [Plotting Parsed Mathematical Functions as Line Graphs](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_parsedfunctionplot) | `JKQTPXParsedFunctionLineGraph` <br> plotting functions with the internal math equation parser/evaluator |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_geometric_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_geometric) | [Plotting Geometric Objects](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_geometric) | |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_boxplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_boxplot) | [Plotting Box Plots](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_boxplot) | `JKQTPBoxplotVerticalGraph`, `JKQTPBoxplotHorizontalGraph` |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_violinplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_violinplot) | [Plotting Violin Plots](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_violinplot) | `JKQTPViolinplotVerticalElement`, `JKQTPViolinplotHorizontalElement` |
## Styling the Plot, Keys, Axes, ...

View File

@ -0,0 +1,111 @@
# Example (JKQTPlotter): Violin Plots {#JKQTPlotterViolinplotGraphs}
[TOC]
[JKQTPlotterBasicJKQTPDatastoreStatistics]: @ref JKQTPlotterBasicJKQTPDatastoreStatistics "Advanced 1-Dimensional Statistics with JKQTPDatastore"
[statisticslibrary]: @ref jkqtptools_math_statistics "JKQTPlotter Statistics Library"
[JKQTPlotterBoxplotStyling]: @ref JKQTPlotterBoxplotStyling "Styling different aspects of boxplots"
This project (see [`simpletest_violinplot`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_violinplot) demonstrates how to use JKQTPlotter to draw <a href="https://en.wikipedia.org/wiki/Violin_plot">violin plots</a> using the classes `JKQTPViolinplotVerticalElement` and `JKQTPViolinplotHorizontalElement`. Violin plots can be thought of as an extension to box plots, as they are also used to represent the distribution of a random variable, but contain more info than the "simple" 5-number statistics used for boxplots: Violin Plots show an estimate of the desnsity distribution of the random vriable, e.g. calculated as a kernel density estimate, or as a simple histogram. The Plotting classes themselves do not calculate these estimates, but only draw them into the plot. The density estimates are calculated by functions from the [statisticslibrary].
The source code of the main application is (see [`jkqtplotter_simpletest_violinplot.cpp`](jkqtplotter_simpletest_violinplot.cpp).
# Generating a test Dataset
First we generate some random numbers from a bimodal distribution (and as a by-product also from two single-distributions that form the bimodal):
```.cpp
size_t randomdatacol1=datastore1->addColumn("random data N(1,1)+N(6,2)");
size_t randomdatacol2=datastore1->addColumn("random data N(1,1)");
size_t randomdatacol3=datastore1->addColumn("random data N(6,2)");
std::random_device rd; // random number generators:
std::mt19937 gen{rd()};
std::uniform_int_distribution<> ddecide(0,1);
std::normal_distribution<> d1{1,1};
std::normal_distribution<> d2{6,2};
for (size_t i=0; i<50; i++) {
double v=0;
if (i%2==0) {
v=d1(gen);
datastore1->appendToColumn(randomdatacol2, v);
} else {
v=d2(gen);
datastore1->appendToColumn(randomdatacol3, v);
}
datastore1->appendToColumn(randomdatacol1, v);
}
```
# Visualizing data as a Rug Plot
Samples from the bimodal (built from two gaussian distributions `d1` and `d2`) are collected in `randomdatacol1`, whereas `randomdatacol2` and `randomdatacol3` collect those numbers that were drawn from `d1` or `d2` respectively.
Such data can be visualized by `JKQTPSingleColumnSymbolsGraph`, here using a rug plot (using `gData1->setPositionScatterStyle(JKQTPSingleColumnSymbolsGraph::RugPlot);` ... but also e.g. a ee swarm plot would be possible):
```.cpp
JKQTPSingleColumnSymbolsGraph* gData1;
plot->addGraph(gData1=new JKQTPSingleColumnSymbolsGraph(plot));
gData1->setPosition(0);
gData1->setPositionScatterStyle(JKQTPSingleColumnSymbolsGraph::RugPlot);
gData1->setDataColumn(randomdatacol1);
gData1->setDataDirection(JKQTPSingleColumnSymbolsGraph::DataDirection::Y);
```
# Drawing the (vertical) Violin Plot
Now we need to calculate the kernel density estimate from the data in `randomdatacol1` and store the result in two new columns `cViol1Cat` and `cViol1Freq`:
```.cpp
size_t cViol1Cat=datastore1->addColumn("violin 1, cat");
size_t cViol1Freq=datastore1->addColumn("violin 1, KDE");
jkqtpstatKDE1DAutoranged(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1),
datastore1->backInserter(cViol1Cat), datastore1->backInserter(cViol1Freq),
51, jkqtpstatKernel1DEpanechnikov,
jkqtpstatEstimateKDEBandwidth(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1)));
```
Finally we can add a `JKQTPViolinplotVerticalElement` to the plot and provide it with the kernel density estimate from above and with some additional statistical properties (minimum, maximum, average and median) of the dataset:
```.cpp
JKQTPViolinplotVerticalElement* gViol1;
plot->addGraph(gViol1=new JKQTPViolinplotVerticalElement(plot));
gViol1->setPos(2);
gViol1->setMin(jkqtpstatMinimum(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1)));
gViol1->setMax(jkqtpstatMaximum(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1)));
gViol1->setMean(jkqtpstatAverage(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1)));
gViol1->setMedian(jkqtpstatMedian(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1)));
gViol1->setViolinPositionColumn(cViol1Cat);
gViol1->setViolinFrequencyColumn(cViol1Freq);
gViol1->setColor(gData1->getSymbolColor());
gViol1->setViolinStyle(JKQTPGraphViolinplotStyleMixin::SmoothViolin);
gViol1->setViolinPositionMode(JKQTPGraphViolinplotStyleMixin::ViolinBoth);
```
The center of the `gData1` was set to 0 and the center of the violin plot is set to `2`. With `JKQTPViolinplotVerticalElement::setViolinStyle()` you can choose the style of the violin plot and with `JKQTPViolinplotVerticalElement::setViolinPositionMode()` you can select whether the density estimate should be displayed on the left, the right or on both sides of the center-line.
The result looks like this, if we use the same method as above to calculate also the violin plots for `randomdatacol2` and `randomdatacol3`:
![jkqtplotter_simpletest_violinplot_vert](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_violinplot_vert.png)
Note that we set different styles for the three plots with:
```.cpp
gViol2->setViolinStyle(JKQTPGraphViolinplotStyleMixin::StepViolin); // green plot
gViol3->setViolinStyle(JKQTPGraphViolinplotStyleMixin::BoxViolin); // blue plot
```
Also for the green and blue plot, we did not calculate a kernel density estimate, but rather a simple histogram:
```.cpp
size_t cViol2Cat=datastore1->addColumn("violin 2, cat");
size_t cViol2Freq=datastore1->addColumn("violin 2, Histogram");
jkqtpstatHistogram1DAutoranged(datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2),
datastore1->backInserter(cViol2Cat), datastore1->backInserter(cViol2Freq),
21, true, false, JKQTPStatHistogramBinXMode::XIsMid);
```
# Drawing a horizontal Violin Plot
Finally note that if you use `JKQTPViolinplotHorizontalElement` instead of the `JKQTPViolinplotVerticalElement` used above, you can also draw horizontal violin plots:
![jkqtplotter_simpletest_violinplot_hor](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_violinplot_hor.png)

View File

@ -0,0 +1,159 @@
/** \example jkqtplotter_simpletest_violinplot->cpp
* Shows how to draw Violin-Plot with JKQTPlotter
*
* \ref JKQTPlotterViolinplotGraphs
*/
#include <QApplication>
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphsviolinplot.h"
#include "jkqtplotter/jkqtpgraphssinglecolumnsymbols.h"
#include "jkqtplotter/jkqtpgraphsstatisticsadaptors.h"
#include "jkqtcommon/jkqtpstatisticstools.h"
#include <random>
#include <cmath>
template <class TGraph, JKQTPSingleColumnSymbolsGraph::DataDirection DataOrientation>
void showPlot() {
// 1. create a plotter window and get a pointer to the internal datastore (for convenience)
JKQTPlotter* plot=new JKQTPlotter();
plot->getPlotter()->setUseAntiAliasingForGraphs(true); // nicer (but slower) plotting
plot->getPlotter()->setUseAntiAliasingForSystem(true); // nicer (but slower) plotting
plot->getPlotter()->setUseAntiAliasingForText(true); // nicer (but slower) text rendering
JKQTPDatastore* datastore1=plot->getDatastore();
// 2. Now we create random data drawn from a bimodal distribution,
// also we add each single distribution as a separate column
size_t randomdatacol1=datastore1->addColumn("random data N(1,1)+N(6,2)");
size_t randomdatacol2=datastore1->addColumn("random data N(1,1)");
size_t randomdatacol3=datastore1->addColumn("random data N(6,2)");
std::random_device rd; // random number generators:
std::mt19937 gen{rd()};
std::uniform_int_distribution<> ddecide(0,1);
std::normal_distribution<> d1{1,1};
std::normal_distribution<> d2{6,2};
for (size_t i=0; i<50; i++) {
double v=0;
if (i%2==0) {
v=d1(gen);
datastore1->appendToColumn(randomdatacol2, v);
} else {
v=d2(gen);
datastore1->appendToColumn(randomdatacol3, v);
}
datastore1->appendToColumn(randomdatacol1, v);
}
const QString d1_latex="$\\mathcal{N}("+jkqtp_floattolatexqstr(d1.mean(), 1)+","+jkqtp_floattolatexqstr(d1.stddev(), 1)+")+\\mathcal{N}("+jkqtp_floattolatexqstr(d2.mean(), 1)+","+jkqtp_floattolatexqstr(d2.stddev(), 1)+")$";
const QString d2_latex="$\\mathcal{N}("+jkqtp_floattolatexqstr(d1.mean(), 1)+","+jkqtp_floattolatexqstr(d1.stddev(), 1)+")$";
const QString d3_latex="$\\mathcal{N}("+jkqtp_floattolatexqstr(d2.mean(), 1)+","+jkqtp_floattolatexqstr(d2.stddev(), 1)+")$";
// 3. we visualize the data as scatter plots
JKQTPSingleColumnSymbolsGraph* gData1;
plot->addGraph(gData1=new JKQTPSingleColumnSymbolsGraph(plot));
gData1->setPosition(0);
gData1->setPositionScatterStyle(JKQTPSingleColumnSymbolsGraph::RugPlot);
gData1->setDataColumn(randomdatacol1);
gData1->setTitle(d3_latex);
gData1->setDataDirection(DataOrientation);
JKQTPSingleColumnSymbolsGraph* gData2;
plot->addGraph(gData2=new JKQTPSingleColumnSymbolsGraph(plot));
gData2->setPosition(5);
gData2->setPositionScatterStyle(JKQTPSingleColumnSymbolsGraph::RugPlot);
gData2->setDataColumn(randomdatacol2);
gData2->setTitle(d3_latex);
gData2->setDataDirection(DataOrientation);
JKQTPSingleColumnSymbolsGraph* gData3;
plot->addGraph(gData3=new JKQTPSingleColumnSymbolsGraph(plot));
gData3->setPosition(10);
gData3->setPositionScatterStyle(JKQTPSingleColumnSymbolsGraph::RugPlot);
gData3->setDataColumn(randomdatacol3);
gData3->setTitle(d3_latex);
gData3->setDataDirection(DataOrientation);
auto pos=JKQTPGraphViolinplotStyleMixin::ViolinBoth;
// 4. In order to draw violin plots we first need to calculate the
// kernel density estimate (KDE) for the violin
size_t cViol1Cat=datastore1->addColumn("violin 1, cat");
size_t cViol1Freq=datastore1->addColumn("violin 1, KDE");
jkqtpstatKDE1DAutoranged(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1),
datastore1->backInserter(cViol1Cat), datastore1->backInserter(cViol1Freq),
51, jkqtpstatKernel1DEpanechnikov,
jkqtpstatEstimateKDEBandwidth(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1)));
TGraph* gViol1;
plot->addGraph(gViol1=new TGraph(plot));
gViol1->setPos(2);
gViol1->setMin(jkqtpstatMinimum(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1)));
gViol1->setMax(jkqtpstatMaximum(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1)));
gViol1->setMean(jkqtpstatAverage(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1)));
gViol1->setMedian(jkqtpstatMedian(datastore1->begin(randomdatacol1), datastore1->end(randomdatacol1)));
gViol1->setViolinPositionColumn(cViol1Cat);
gViol1->setViolinFrequencyColumn(cViol1Freq);
gViol1->setColor(gData1->getSymbolColor());
gViol1->setViolinStyle(JKQTPGraphViolinplotStyleMixin::SmoothViolin);
gViol1->setViolinPositionMode(pos);
gViol1->setTitle("violin plot "+d1_latex);
size_t cViol2Cat=datastore1->addColumn("violin 2, cat");
size_t cViol2Freq=datastore1->addColumn("violin 2, Histogram");
jkqtpstatHistogram1DAutoranged(datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2),
datastore1->backInserter(cViol2Cat), datastore1->backInserter(cViol2Freq),
21, true, false, JKQTPStatHistogramBinXMode::XIsMid);
TGraph* gViol2;
plot->addGraph(gViol2=new TGraph(plot));
gViol2->setPos(7);
gViol2->setMin(jkqtpstatMinimum(datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2)));
gViol2->setMax(jkqtpstatMaximum(datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2)));
gViol2->setMean(jkqtpstatAverage(datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2)));
gViol2->setMedian(jkqtpstatMedian(datastore1->begin(randomdatacol2), datastore1->end(randomdatacol2)));
gViol2->setViolinPositionColumn(cViol2Cat);
gViol2->setViolinFrequencyColumn(cViol2Freq);
gViol2->setColor(gData2->getSymbolColor());
gViol2->setViolinStyle(JKQTPGraphViolinplotStyleMixin::StepViolin);
gViol2->setViolinPositionMode(pos);
gViol2->setTitle("step violin plot "+d2_latex);
size_t cViol3Cat=datastore1->addColumn("violin 3, cat");
size_t cViol3Freq=datastore1->addColumn("violin 3, Histogram");
jkqtpstatHistogram1DAutoranged(datastore1->begin(randomdatacol3), datastore1->end(randomdatacol3),
datastore1->backInserter(cViol3Cat), datastore1->backInserter(cViol3Freq),
10, true, false, JKQTPStatHistogramBinXMode::XIsMid);
TGraph* gViol3;
plot->addGraph(gViol3=new TGraph(plot));
gViol3->setPos(12);
gViol3->setMin(jkqtpstatMinimum(datastore1->begin(randomdatacol3), datastore1->end(randomdatacol3)));
gViol3->setMax(jkqtpstatMaximum(datastore1->begin(randomdatacol3), datastore1->end(randomdatacol3)));
gViol3->setMean(jkqtpstatAverage(datastore1->begin(randomdatacol3), datastore1->end(randomdatacol3)));
gViol3->setMedian(jkqtpstatMedian(datastore1->begin(randomdatacol3), datastore1->end(randomdatacol3)));
gViol3->setViolinPositionColumn(cViol3Cat);
gViol3->setViolinFrequencyColumn(cViol3Freq);
gViol3->setColor(gData3->getSymbolColor());
gViol3->setViolinStyle(JKQTPGraphViolinplotStyleMixin::BoxViolin);
gViol3->setViolinPositionMode(pos);
gViol3->setTitle("box violin plot "+d3_latex);
// autoscale and style the plot
plot->zoomToFit();
plot->getPlotter()->setKeyPosition(JKQTPKeyInsideBottomRight);
plot->setShowZeroAxes(false);
plot->getPlotter()->setGridColor(QColor(230,230,230));
// show plotter and make it a decent size
plot->show();
plot->resize(800,600);
}
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
showPlot<JKQTPViolinplotHorizontalElement,JKQTPSingleColumnSymbolsGraph::DataDirection::X>();
showPlot<JKQTPViolinplotVerticalElement,JKQTPSingleColumnSymbolsGraph::DataDirection::Y>();
return app.exec();
}

View File

@ -0,0 +1,27 @@
# source code for this simple demo
SOURCES = jkqtplotter_simpletest_violinplot.cpp
# configure Qt
CONFIG += link_prl qt
QT += core gui xml svg
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
# output executable name
TARGET = jkqtplotter_simpletest_violinplot
# include JKQTPlotter source code
DEPENDPATH += ../../lib ../../staticlib/jkqtplotterlib
INCLUDEPATH += ../../lib
CONFIG (debug, debug|release) {
LIBS += -L../../staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug
} else {
LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib
}
message("LIBS = $$LIBS")
win32-msvc*: DEFINES += _USE_MATH_DEFINES
win32-msvc*: DEFINES += NOMINMAX

View File

@ -0,0 +1,8 @@
TEMPLATE = subdirs
SUBDIRS += jkqtplotterlib jkqtplotter_simpletest_violinplot
jkqtplotterlib.file = ../../staticlib/jkqtplotterlib/jkqtplotterlib.pro
jkqtplotter_simpletest_violinplot.file=$$PWD/jkqtplotter_simpletest_violinplot.pro
jkqtplotter_simpletest_violinplot.depends = jkqtplotterlib

View File

@ -35,6 +35,8 @@ isEmpty(JKQTP_PLOTTER_PRI_INCLUDED) {
$$PWD/jkqtplotter/jkqtpgraphsimageoverlays.h \
$$PWD/jkqtplotter/jkqtpgraphscontour.h \
$$PWD/jkqtplotter/jkqtpgraphsimagergb.h \
$$PWD/jkqtplotter/jkqtpgraphsviolinplot.h \
$$PWD/jkqtplotter/jkqtpgraphsviolinplotstylingmixins.h \
$$PWD/jkqtplotter/jkqtpgraphsstatisticsadaptors.h \
$$PWD/jkqtplotter/jkqtplotter.h \
$$PWD/jkqtplotter/jkqtplotterstyle.h \
@ -76,7 +78,9 @@ isEmpty(JKQTP_PLOTTER_PRI_INCLUDED) {
$$PWD/jkqtplotter/jkqtpgraphssinglecolumnsymbols.cpp \
$$PWD/jkqtplotter/jkqtpgraphsimageoverlays.cpp \
$$PWD/jkqtplotter/jkqtpgraphscontour.cpp \
$$PWD/jkqtplotter/jkqtpgraphsimagergb.cpp \
$$PWD/jkqtplotter/jkqtpgraphsimagergb.cpp \
$$PWD/jkqtplotter/jkqtpgraphsviolinplot.cpp \
$$PWD/jkqtplotter/jkqtpgraphsviolinplotstylingmixins.cpp \
$$PWD/jkqtplotter/jkqtpgraphsstatisticsadaptors.cpp \
$$PWD/jkqtplotter/jkqtplotter.cpp \
$$PWD/jkqtplotter/jkqtplotterstyle.cpp \

View File

@ -41,12 +41,13 @@
The second coordinate is:
- if positionScatterStyle==NoScatter it is simply the value of position, e.g. <br>\image html JKQTPSingleColumnSymbolsGraph_NoScatter.png
- if positionScatterStyle==RugPlot is like a NoScatter-plot, but with lines instead of symbols (see <a href="https://en.wikipedia.org/wiki/Rug_plot">https://en.wikipedia.org/wiki/Rug_plot</a>) <br>\image html JKQTPSingleColumnSymbolsGraph_RugPlot.png
- if positionScatterStyle==RandomScatter it scatters around position in the range [position-width/2 ... position+width/2], e.g. <br>\image html JKQTPSingleColumnSymbolsGraph_RandomScatter.png
- if positionScatterStyle==BeeSwarmScatter it scatters around position with an algorithm that tries to avoid any overlay of the plot symbols (SLOW!), e.g. <br>\image html JKQTPSingleColumnSymbolsGraph_BeeSwarmScatter.png
.
All example images above have dataDirection==DataDirection::X !
\see \ref JKQTPlotterDistributionPlot
\see \ref JKQTPlotterDistributionPlot, \ref JKQTPlotterViolinplotGraphs
*/
class JKQTP_LIB_EXPORT JKQTPSingleColumnSymbolsGraph: public JKQTPSingleColumnGraph, public JKQTPGraphSymbolStyleMixin {
@ -55,9 +56,9 @@ class JKQTP_LIB_EXPORT JKQTPSingleColumnSymbolsGraph: public JKQTPSingleColumnGr
/** \brief describes how data from dataColumn of a JKQTPSingleColumnSymbolsGraph positioned at position */
enum ScatterStyle {
NoScatter, /*!< \brief missing coordinate is exactly position for every datapoint in dataColumn \image html JKQTPSingleColumnSymbolsGraph_NoScatter.png */
RugPlot, /*!< \brief like NoScatter but draws each data-point as a horzintal/vertical line, centered around position, not as a symbol ("rug plot", see e.g. <a href="https://en.wikipedia.org/wiki/Rug_plot">https://en.wikipedia.org/wiki/Rug_plot</a>). \image html JKQTPSingleColumnSymbolsGraph_RugPlot.png */
RandomScatter, /*!< \brief missing coordinate scatters around position (with distribution-width width ) \image html JKQTPSingleColumnSymbolsGraph_RandomScatter.png */
BeeSwarmScatter, /*!< \brief missing coordinate scatters around position and the algorithm tries to prevent overlay of two symbols ("bee swarm plot", see e.g. <a href="https://www.r-statistics.com/2011/03/beeswarm-boxplot-and-plotting-it-with-r/">https://www.r-statistics.com/2011/03/beeswarm-boxplot-and-plotting-it-with-r/</a>). Note that this algorithm can be rather slow!!! \image html JKQTPSingleColumnSymbolsGraph_BeeSwarmScatter.png */
RugPlot, /*!< \brief like NoScatter but draws each data-point as a horzintal/vertical line, centered around position, not as a symbol ("rug plot", see e.g. <a href="https://en.wikipedia.org/wiki/Rug_plot">https://en.wikipedia.org/wiki/Rug_plot</a>). \image html JKQTPSingleColumnSymbolsGraph_RugPlot.png */
};
/** \brief class constructor */

View File

@ -0,0 +1,490 @@
/*
Copyright (c) 2008-2019 Jan W. Krieger (<jan@jkrieger.de>)
This software is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License (LGPL) as published by
the Free Software Foundation, either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License (LGPL) for more details.
You should have received a copy of the GNU Lesser General Public License (LGPL)
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "jkqtplotter/jkqtpgraphsviolinplot.h"
#include "jkqtplotter/jkqtpbaseplotter.h"
#include <stdlib.h>
#include <QDebug>
#include <iostream>
#include "jkqtplotter/jkqtptools.h"
#include "jkqtplotter/jkqtpgraphsimage.h"
#include "jkqtplotter/jkqtpbaseelements.h"
#include "jkqtplotter/jkqtplotter.h"
#define SmallestGreaterZeroCompare_xvsgz() if ((xvsgz>10.0*DBL_MIN)&&((smallestGreaterZero<10.0*DBL_MIN) || (xvsgz<smallestGreaterZero))) smallestGreaterZero=xvsgz;
JKQTPViolinplotVerticalElement::JKQTPViolinplotVerticalElement(JKQTBasePlotter* parent):
JKQTPPlotObject(parent)
{
pos=JKQTP_NAN;
median=JKQTP_NAN;
mean=JKQTP_NAN;
min=JKQTP_NAN;
max=JKQTP_NAN;
drawMean=false;
drawMinMax=false;
violinPositionColumn=-1;
violinFrequencyColumn=-1;
initViolinplotStyle(parent, parentPlotStyle);
setMeanSymbolType(JKQTPPlus);
}
JKQTPViolinplotVerticalElement::JKQTPViolinplotVerticalElement(JKQTPlotter* parent):
JKQTPViolinplotVerticalElement(parent->getPlotter())
{
}
void JKQTPViolinplotVerticalElement::draw(JKQTPEnhancedPainter& painter) {
#ifdef JKQTBP_AUTOTIMER
JKQTPAutoOutputTimer jkaaot("JKQTPViolinplotVerticalElement::draw");
#endif
if (parent==nullptr) return;
//drawErrorsBefore(painter);
{
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
const double xv=pos;
const double minv=min;
const double maxv=max;
const double medianv=median;
const double meanv=mean;
//std::cout<<"(xv, yv) = ( "<<xv<<", "<<yv<<" )\n";
if (JKQTPIsOKFloat(xv) ) {
//std::cout<<"boxplot(med="<<medianv<<", min="<<minv<<", max="<<maxv<<", p25="<<p25v<<", p75="<<p75v<<")\n";
const double x=transformX(xv);
const double min=transformY(minv);
const double max=transformY(maxv);
const double median=transformY(medianv);
const double mean=transformY(meanv);
// collect single-value labels for hitTest()-data at the bottom of this loop!
QStringList labelValues, labelNames;
int labMedian=-1, labMean=-1, labMin=-1, labMax=-1;
labelNames<<"pos";
labelValues<<jkqtp_floattolatexqstr(xv, 3);
if (JKQTPIsOKFloat(minv)) { labelNames<<"\\min"; labelValues<<jkqtp_floattolatexqstr(minv, 3); labMin=labelValues.size()-1; }
if (JKQTPIsOKFloat(medianv)) {
labelNames<<"\\median"; labelValues<<jkqtp_floattolatexqstr(medianv, 3); labMedian=labelValues.size()-1;
}
if (JKQTPIsOKFloat(meanv)) { labelNames<<"\\mu"; labelValues<<jkqtp_floattolatexqstr(meanv, 3); labMean=labelValues.size()-1; }
if (JKQTPIsOKFloat(maxv)) { labelNames<<"\\max"; labelValues<<jkqtp_floattolatexqstr(maxv, 3); labMax=labelValues.size()-1; }
double w=parent->pt2px(painter,getViolinWidthAbsolute());
double xma=x+w/2.0;
double xmi=x-w/2.0;
QVector<double> violPos;
if (violinPositionColumn>=0) violPos=transformY(getParent()->getDatastore()->getData(static_cast<size_t>(violinPositionColumn)));
QVector<double> violFreq;
if (violinFrequencyColumn>=0) violFreq=getParent()->getDatastore()->getData(static_cast<size_t>(violinFrequencyColumn));
plotVerticalViolinplot(parent, painter, x, xmi, xma, violPos, violFreq, (drawMinMax)?min:JKQTP_NAN, (drawMedian)?median:JKQTP_NAN, (drawMinMax)?max:JKQTP_NAN, (drawMean)?mean:JKQTP_NAN);
if (drawMedian && JKQTPIsOKFloat(medianv)) {
if (JKQTPIsOKFloat(medianv)) {
QStringList sl=labelValues, sll=labelNames;
sl[labMedian]="\\ul{"+sl[labMedian]+"}";
sll[labMedian]="\\ul{"+sll[labMedian]+"}";
addHitTestData(xv, medianv, "\\ensuremath{\\begin{bmatrix}"+sll.join("\\\\")+"\\end{bmatrix}\\;=\\;\\begin{bmatrix}"+sl.join("\\\\")+"\\end{bmatrix}}");
}
}
if (drawMinMax) {
if (JKQTPIsOKFloat(minv)) {
QStringList sl=labelValues, sll=labelNames;
sl[labMin]="\\ul{"+sl[labMin]+"}";
sll[labMin]="\\ul{"+sll[labMin]+"}";
addHitTestData(xv, minv, "\\ensuremath{\\begin{bmatrix}"+sll.join("\\\\")+"\\end{bmatrix}\\;=\\;\\begin{bmatrix}"+sl.join("\\\\")+"\\end{bmatrix}}");
}
if (JKQTPIsOKFloat(maxv)) {
QStringList sl=labelValues, sll=labelNames;
sl[labMax]="\\ul{"+sl[labMax]+"}";
sll[labMax]="\\ul{"+sll[labMax]+"}";
addHitTestData(xv, maxv, "\\ensuremath{\\begin{bmatrix}"+sll.join("\\\\")+"\\end{bmatrix}\\;=\\;\\begin{bmatrix}"+sl.join("\\\\")+"\\end{bmatrix}}");
}
}
if (drawMean && JKQTPIsOKFloat(meanv)) {
if (JKQTPIsOKFloat(meanv)) {
QStringList sl=labelValues, sll=labelNames;
sl[labMean]="\\ul{"+sl[labMean]+"}";
sll[labMean]="\\ul{"+sll[labMean]+"}";
addHitTestData(xv, meanv, "\\ensuremath{\\begin{bmatrix}"+sll.join("\\\\")+"\\end{bmatrix}\\;=\\;\\begin{bmatrix}"+sl.join("\\\\")+"\\end{bmatrix}}");
}
}
}
}
//drawErrorsAfter(painter);
}
bool JKQTPViolinplotVerticalElement::getXMinMax(double& minx, double& maxx, double& smallestGreaterZero) {
minx=0;
maxx=0;
smallestGreaterZero=0;
if (parent==nullptr) return false;
double xv=pos;
double x=transformX(xv);
double xma=backtransformX(x-parent->pt2px(getViolinWidthAbsolute(), 72));
double xmi=backtransformX(x+parent->pt2px(getViolinWidthAbsolute(), 72));
if (xmi>xma) qSwap(xmi,xma);
maxx=xma;
minx=xmi;
double xvsgz;
xvsgz=xmi; SmallestGreaterZeroCompare_xvsgz();
xvsgz=xma; SmallestGreaterZeroCompare_xvsgz();
return true;
}
bool JKQTPViolinplotVerticalElement::getYMinMax(double& miny, double& maxy, double& smallestGreaterZero) {
miny=0;
maxy=0;
smallestGreaterZero=0;
if (parent==nullptr) return false ;
if (drawMedian) {
maxy=median;
miny=median;
}
if (min>maxy) maxy=min;
if (drawMinMax && (min<miny)) miny=min;
if (drawMinMax && (max>maxy)) maxy=max;
if (drawMinMax && (max<miny)) miny=max;
double xvsgz;
xvsgz=median; SmallestGreaterZeroCompare_xvsgz();
xvsgz=min; SmallestGreaterZeroCompare_xvsgz();
xvsgz=max; SmallestGreaterZeroCompare_xvsgz();
return true;
}
void JKQTPViolinplotVerticalElement::setPos(double __value)
{
this->pos = __value;
}
double JKQTPViolinplotVerticalElement::getPos() const
{
return this->pos;
}
void JKQTPViolinplotVerticalElement::setMedian(double __value)
{
if (this->median != __value) {
this->median = __value;
drawMedian=true;
}
}
double JKQTPViolinplotVerticalElement::getMedian() const
{
return this->median;
}
void JKQTPViolinplotVerticalElement::setMean(double __value)
{
if (this->mean != __value) {
this->mean = __value;
drawMean=true;
}
}
double JKQTPViolinplotVerticalElement::getMean() const
{
return this->mean;
}
void JKQTPViolinplotVerticalElement::setMin(double __value)
{
if (this->min != __value) {
this->min = __value;
drawMinMax=true;
}
}
double JKQTPViolinplotVerticalElement::getMin() const
{
return this->min;
}
void JKQTPViolinplotVerticalElement::setMax(double __value)
{
if (this->max != __value) {
this->max = __value;
drawMinMax=true;
}
}
double JKQTPViolinplotVerticalElement::getMax() const
{
return this->max;
}
void JKQTPViolinplotVerticalElement::setDrawMean(bool __value)
{
this->drawMean = __value;
}
bool JKQTPViolinplotVerticalElement::getDrawMean() const
{
return this->drawMean;
}
void JKQTPViolinplotVerticalElement::setDrawMedian(bool __value)
{
this->drawMedian = __value;
}
bool JKQTPViolinplotVerticalElement::getDrawMedian() const
{
return this->drawMedian;
}
void JKQTPViolinplotVerticalElement::setDrawMinMax(bool __value)
{
this->drawMinMax = __value;
}
bool JKQTPViolinplotVerticalElement::getDrawMinMax() const
{
return this->drawMinMax;
}
void JKQTPViolinplotVerticalElement::setViolinPositionColumn(int __value)
{
violinPositionColumn=__value;
}
void JKQTPViolinplotVerticalElement::setViolinPositionColumn(size_t __value)
{
violinPositionColumn=static_cast<int>(__value);
}
int JKQTPViolinplotVerticalElement::getViolinPositionColumn() const
{
return violinPositionColumn;
}
void JKQTPViolinplotVerticalElement::setViolinFrequencyColumn(int __value)
{
violinFrequencyColumn=__value;
}
void JKQTPViolinplotVerticalElement::setViolinFrequencyColumn(size_t __value)
{
violinFrequencyColumn=static_cast<int>(__value);
}
int JKQTPViolinplotVerticalElement::getViolinFrequencyColumn() const
{
return violinFrequencyColumn;
}
void JKQTPViolinplotVerticalElement::drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) {
plotVerticalKeyMarker(parent, painter, rect);
}
QColor JKQTPViolinplotVerticalElement::getKeyLabelColor() const {
return getLineColor();
}
void JKQTPViolinplotVerticalElement::setColor(QColor c)
{
setViolinplotColor(c, getParent());
}
void JKQTPViolinplotHorizontalElement::drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) {
plotHorizontalKeyMarker(parent, painter, rect);
}
bool JKQTPViolinplotHorizontalElement::getXMinMax(double& minx, double& maxx, double& smallestGreaterZero) {
minx=0;
maxx=0;
smallestGreaterZero=0;
if (parent==nullptr) return false;
if (drawMedian) {
maxx=median;
minx=median;
}
if (min>maxx) maxx=min;
if (drawMinMax && (min<minx)) minx=min;
if (drawMinMax && (max>maxx)) maxx=max;
if (drawMinMax && (max<minx)) minx=max;
double xvsgz;
xvsgz=median; SmallestGreaterZeroCompare_xvsgz();
xvsgz=min; SmallestGreaterZeroCompare_xvsgz();
xvsgz=max; SmallestGreaterZeroCompare_xvsgz();
return true;
}
bool JKQTPViolinplotHorizontalElement::getYMinMax(double& miny, double& maxy, double& smallestGreaterZero) {
miny=0;
maxy=0;
smallestGreaterZero=0;
if (parent==nullptr) return false;
double xv=pos;
double x=transformY(xv);
double xma=backtransformY(x-parent->pt2px(getViolinWidthAbsolute(), 72));
double xmi=backtransformY(x+parent->pt2px(getViolinWidthAbsolute(), 72));
if (xmi>xma) qSwap(xmi,xma);
maxy=xma;
miny=xmi;
double xvsgz;
xvsgz=xmi; SmallestGreaterZeroCompare_xvsgz();
xvsgz=xma; SmallestGreaterZeroCompare_xvsgz();
return true;
}
JKQTPViolinplotHorizontalElement::JKQTPViolinplotHorizontalElement(JKQTBasePlotter *parent):
JKQTPViolinplotVerticalElement(parent)
{
}
JKQTPViolinplotHorizontalElement::JKQTPViolinplotHorizontalElement(JKQTPlotter *parent):
JKQTPViolinplotHorizontalElement(parent->getPlotter())
{
}
void JKQTPViolinplotHorizontalElement::draw(JKQTPEnhancedPainter& painter) {
#ifdef JKQTBP_AUTOTIMER
JKQTPAutoOutputTimer jkaaot("JKQTPViolinplotHorizontalElement::draw");
#endif
if (parent==nullptr) return;
//drawErrorsBefore(painter);
{
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
const double yv=pos;
const double minv=min;
const double maxv=max;
const double medianv=median;
const double meanv=mean;
//std::cout<<"(xv, yv) = ( "<<xv<<", "<<yv<<" )\n";
if (JKQTPIsOKFloat(yv) ) {
//std::cout<<"boxplot(med="<<medianv<<", min="<<minv<<", max="<<maxv<<", p25="<<p25v<<", p75="<<p75v<<")\n";
const double y=transformY(yv);
const double min=transformX(minv);
const double max=transformX(maxv);
const double median=transformX(medianv);
const double mean=transformX(meanv);
// collect single-value labels for hitTest()-data at the bottom of this loop!
QStringList labelValues, labelNames;
int labMedian=-1, labMean=-1, labMin=-1, labMax=-1;
labelNames<<"pos";
labelValues<<jkqtp_floattolatexqstr(yv, 3);
if (JKQTPIsOKFloat(minv)) { labelNames<<"\\min"; labelValues<<jkqtp_floattolatexqstr(minv, 3); labMin=labelValues.size()-1; }
if (JKQTPIsOKFloat(medianv)) {
labelNames<<"\\median"; labelValues<<jkqtp_floattolatexqstr(medianv, 3); labMedian=labelValues.size()-1;
}
if (JKQTPIsOKFloat(meanv)) { labelNames<<"\\mu"; labelValues<<jkqtp_floattolatexqstr(meanv, 3); labMean=labelValues.size()-1; }
if (JKQTPIsOKFloat(maxv)) { labelNames<<"\\max"; labelValues<<jkqtp_floattolatexqstr(maxv, 3); labMax=labelValues.size()-1; }
double w=parent->pt2px(painter,getViolinWidthAbsolute());
double yma=y+w/2.0;
double ymi=y-w/2.0;
QVector<double> violPos;
if (violinPositionColumn>=0) violPos=transformX(getParent()->getDatastore()->getData(static_cast<size_t>(violinPositionColumn)));
QVector<double> violFreq;
if (violinFrequencyColumn>=0) violFreq=getParent()->getDatastore()->getData(static_cast<size_t>(violinFrequencyColumn));
plotHorizontalViolinplot(parent, painter, y, ymi, yma, violPos, violFreq, (drawMinMax)?min:JKQTP_NAN, (drawMedian)?median:JKQTP_NAN, (drawMinMax)?max:JKQTP_NAN, (drawMean)?mean:JKQTP_NAN);
if (drawMedian && JKQTPIsOKFloat(medianv)) {
if (JKQTPIsOKFloat(medianv)) {
QStringList sl=labelValues, sll=labelNames;
sl[labMedian]="\\ul{"+sl[labMedian]+"}";
sll[labMedian]="\\ul{"+sll[labMedian]+"}";
addHitTestData(medianv, yv, "\\ensuremath{\\begin{bmatrix}"+sll.join("\\\\")+"\\end{bmatrix}\\;=\\;\\begin{bmatrix}"+sl.join("\\\\")+"\\end{bmatrix}}");
} }
if (drawMinMax) {
if (JKQTPIsOKFloat(minv)) {
QStringList sl=labelValues, sll=labelNames;
sl[labMin]="\\ul{"+sl[labMin]+"}";
sll[labMin]="\\ul{"+sll[labMin]+"}";
addHitTestData(minv, yv, "\\ensuremath{\\begin{bmatrix}"+sll.join("\\\\")+"\\end{bmatrix}\\;=\\;\\begin{bmatrix}"+sl.join("\\\\")+"\\end{bmatrix}}");
}
if (JKQTPIsOKFloat(maxv)) {
QStringList sl=labelValues, sll=labelNames;
sl[labMax]="\\ul{"+sl[labMax]+"}";
sll[labMax]="\\ul{"+sll[labMax]+"}";
addHitTestData(maxv, yv, "\\ensuremath{\\begin{bmatrix}"+sll.join("\\\\")+"\\end{bmatrix}\\;=\\;\\begin{bmatrix}"+sl.join("\\\\")+"\\end{bmatrix}}");
}
}
if (drawMean && JKQTPIsOKFloat(meanv)) {
if (JKQTPIsOKFloat(meanv)) {
QStringList sl=labelValues, sll=labelNames;
sl[labMean]="\\ul{"+sl[labMean]+"}";
sll[labMean]="\\ul{"+sll[labMean]+"}";
addHitTestData(meanv, yv, "\\ensuremath{\\begin{bmatrix}"+sll.join("\\\\")+"\\end{bmatrix}\\;=\\;\\begin{bmatrix}"+sl.join("\\\\")+"\\end{bmatrix}}");
}
}
}
}
//drawErrorsAfter(painter);
}

View File

@ -0,0 +1,222 @@
/*
Copyright (c) 2008-2019 Jan W. Krieger (<jan@jkrieger.de>)
This software is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License (LGPL) as published by
the Free Software Foundation, either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License (LGPL) for more details.
You should have received a copy of the GNU Lesser General Public License (LGPL)
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QString>
#include <QPainter>
#include <QPair>
#include "jkqtplotter/jkqtptools.h"
#include "jkqtcommon/jkqtp_imexport.h"
#include "jkqtplotter/jkqtpimagetools.h"
#include "jkqtplotter/jkqtpgraphsbase.h"
#include "jkqtplotter/jkqtpgraphsviolinplotstylingmixins.h"
#include "jkqtplotter/jkqtpgraphsbasestylingmixins.h"
#ifndef jkqtpgraphsviolinplot_H
#define jkqtpgraphsviolinplot_H
/*! \brief This implements a single vertical <a href="https://en.wikipedia.org/wiki/Violin_plot">Violin Plot</a> as a "geometric element"
\ingroup jkqtplotter_statgraphs
\ingroup jkqtplotter_geoplots
Draws a vertical violin plot in one of the following styles:
\image html JKQTPGraphViolinplot_SmoothViolin.png
\image html JKQTPGraphViolinplot_StepViolin.png
\image html JKQTPGraphViolinplot_BoxViolin.png
This graph/plot element represents a single such plot. Its center is positioned at an x-coordinate provided by setPos().
The width of the full plot (left+right!) is given by JKQTPGraphViolinplotStyleMixin::setViolinWidthAbsolute().
The data is represented partly by single values (e.g. setMedian(), setMin(), ...) and partly by the two columns set with
setViolinPositionColumn() and setViolinFrequencyColumn(). The latter two represent a density estimate (or histogram) of the
distribution represented by this plot, which is evaluated at the y-positions from setViolinPositionColumn(). For each such
y-position a (not necessarily normalized) frequency is given by the values in setViolinFrequencyColumn().
\image html JKQTPViolinplotHorizontalElement_elements.png
\see \ref JKQTPlotterViolinplotGraphs
*/
class JKQTP_LIB_EXPORT JKQTPViolinplotVerticalElement: public JKQTPPlotObject, public JKQTPGraphViolinplotStyleMixin {
Q_OBJECT
public:
/** \brief class constructor */
JKQTPViolinplotVerticalElement(JKQTBasePlotter* parent=nullptr);
/** \brief class constructor */
JKQTPViolinplotVerticalElement(JKQTPlotter* parent);
/** \brief plots the graph to the plotter object specified as parent */
virtual void draw(JKQTPEnhancedPainter& painter) override;
/** \brief plots a key marker inside the specified rectangle \a rect */
virtual void drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) override;
/** \brief returns the color to be used for the key label */
virtual QColor getKeyLabelColor() const override;
/*! \brief set the color of the graph (colors all elements, based on the given color \a c )*/
virtual void setColor(QColor c);
/** \brief get the maximum and minimum x-value of the graph
*
* The result is given in the two parameters which are call-by-reference parameters!
*/
virtual bool getXMinMax(double& minx, double& maxx, double& smallestGreaterZero) override;
/** \brief get the maximum and minimum y-value of the graph
*
* The result is given in the two parameters which are call-by-reference parameters!
*/
virtual bool getYMinMax(double& miny, double& maxy, double& smallestGreaterZero) override;
/*! \copydoc pos */
void setPos(double __value);
/*! \copydoc pos */
double getPos() const;
/*! \copydoc median */
void setMedian(double __value);
/*! \copydoc median */
double getMedian() const;
/*! \copydoc mean */
void setMean(double __value);
/*! \copydoc mean */
double getMean() const;
/*! \copydoc min */
void setMin(double __value);
/*! \copydoc min */
double getMin() const;
/*! \copydoc max */
void setMax(double __value);
/*! \copydoc max */
double getMax() const;
/*! \copydoc drawMean */
void setDrawMean(bool __value);
/*! \copydoc drawMean */
bool getDrawMean() const;
/*! \copydoc drawMedian */
void setDrawMedian(bool __value);
/*! \copydoc drawMedian */
bool getDrawMedian() const;
/*! \copydoc drawMinMax */
void setDrawMinMax(bool __value);
/*! \copydoc drawMinMax */
bool getDrawMinMax() const;
/*! \copydoc violinPositionColumn */
void setViolinPositionColumn(int __value);
/*! \copydoc violinPositionColumn */
void setViolinPositionColumn(size_t __value);
/*! \copydoc violinPositionColumn */
int getViolinPositionColumn() const;
/*! \copydoc violinFrequencyColumn */
void setViolinFrequencyColumn(int __value);
/*! \copydoc violinFrequencyColumn */
void setViolinFrequencyColumn(size_t __value);
/*! \copydoc violinFrequencyColumn */
int getViolinFrequencyColumn() const;
protected:
/** \brief the position of the boxplot on the "other" axis */
double pos;
/** \brief the median value to be used for the boxplot */
double median;
/** \brief the mean value to be used for the boxplot */
double mean;
/** \brief indicates whether to draw the mean */
bool drawMean;
/** \brief indicates whether to draw the median */
bool drawMedian;
/** \brief indicates whether to draw the percentiles */
bool drawMinMax;
/** \brief the minimum value to be used for the boxplot */
double min;
/** \brief the maximum value to be used for the boxplot */
double max;
/** \brief column with data for the violin outline: category values (along min-max-axis) */
int violinPositionColumn;
/** \brief column with data for the violin outline: frequency values (perpendicular to min-max-axis) */
int violinFrequencyColumn;
};
/*! \brief This implements a single horizontal <a href="https://en.wikipedia.org/wiki/Violin_plot">Violin Plot</a> as a "geometric element"
\ingroup jkqtplotter_statgraphs
\ingroup jkqtplotter_geoplots
Draws a horizontal violin plot in one of the following styles:
\image html JKQTPGraphViolinplot_HSmoothViolin.png
\image html JKQTPGraphViolinplot_HStepViolin.png
\image html JKQTPGraphViolinplot_HBoxViolin.png
This graph/plot element represents a single such plot. Its center is positioned at an y-coordinate provided by setPos().
The width of the full plot (top+bottom!) is given by JKQTPGraphViolinplotStyleMixin::setViolinWidthAbsolute().
The data is represented partly by single values (e.g. setMedian(), setMin(), ...) and partly by the two columns set with
setViolinPositionColumn() and setViolinFrequencyColumn(). The latter two represent a density estimate (or histogram) of the
distribution represented by this plot, which is evaluated at the x-positions from setViolinPositionColumn(). For each such
x-position a (not necessarily normalized) frequency is given by the values in setViolinFrequencyColumn().
\image html JKQTPViolinplotHorizontalElement_elements.png
\see \ref JKQTPlotterViolinplotGraphs
*/
class JKQTP_LIB_EXPORT JKQTPViolinplotHorizontalElement: public JKQTPViolinplotVerticalElement {
Q_OBJECT
public:
/** \brief class constructor */
JKQTPViolinplotHorizontalElement(JKQTBasePlotter* parent=nullptr);
/** \brief class constructor */
JKQTPViolinplotHorizontalElement(JKQTPlotter* parent);
/** \brief plots the graph to the plotter object specified as parent */
virtual void draw(JKQTPEnhancedPainter& painter) override;
/** \brief plots a key marker inside the specified rectangle \a rect */
virtual void drawKeyMarker(JKQTPEnhancedPainter& painter, QRectF& rect) override;
/** \brief get the maximum and minimum x-value of the graph
*
* The result is given in the two parameters which are call-by-reference parameters!
*/
virtual bool getXMinMax(double& minx, double& maxx, double& smallestGreaterZero) override;
/** \brief get the maximum and minimum y-value of the graph
*
* The result is given in the two parameters which are call-by-reference parameters!
*/
virtual bool getYMinMax(double& miny, double& maxy, double& smallestGreaterZero) override;
};
#endif // jkqtpgraphsviolinplot_H

View File

@ -0,0 +1,978 @@
/*
Copyright (c) 2008-2019 Jan W. Krieger (<jan@jkrieger.de>)
This software is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License (LGPL) as published by
the Free Software Foundation, either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License (LGPL) for more details.
You should have received a copy of the GNU Lesser General Public License (LGPL)
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "jkqtplotter/jkqtpgraphsviolinplotstylingmixins.h"
#include "jkqtplotter/jkqtpbaseplotter.h"
#include <stdlib.h>
#include <QDebug>
#include <iostream>
#include "jkqtplotter/jkqtptools.h"
#include "jkqtplotter/jkqtpgraphsimage.h"
#include "jkqtplotter/jkqtpbaseelements.h"
#include "jkqtplotter/jkqtplotter.h"
JKQTPGraphViolinplotStyleMixin::JKQTPGraphViolinplotStyleMixin()
{
m_whiskerLinePen=QPen(getLineColor(), getLineWidth());
whiskerLineWidth=getLineWidth();
m_whiskerCapLinePen=QPen(getLineColor(), getLineWidth());
whiskerCapLineWidth=getLineWidth();
m_medianLinePen=QPen(getLineColor(), getLineWidth());
medianLineWidth=getLineWidth();
m_meanSymbolLinePen=QPen(getLineColor(), getLineWidth());
m_meanSymbolLineWidth=1;
m_meanSymbolType=JKQTPGraphSymbols::JKQTPDefaultSymbol;
m_meanSymbolSize=12;
m_meanSymbolFillColor=m_meanSymbolLinePen.color().lighter();
violinWidthAbsolute=m_meanSymbolSize*6.0;
relativeWhiskerWidth=0.5;
m_violinStyle=ViolinStyle::SmoothViolin;
m_violinPositionMode=ViolinPositionMode::ViolinBoth;
}
void JKQTPGraphViolinplotStyleMixin::initViolinplotStyle(JKQTBasePlotter *parent, int &parentPlotStyle)
{
setFillStyle(Qt::SolidPattern);
setFillColor(parent->getCurrentPlotterStyle().plotBackgroundBrush.color());
initLineStyle(parent, parentPlotStyle);
if (parent) { // get style settings from parent object
if (parentPlotStyle<0) parentPlotStyle=parent->getNextStyle();
m_whiskerLinePen.setColor(parent->getPlotStyle(parentPlotStyle).color());
m_whiskerLinePen.setStyle(parent->getPlotStyle(parentPlotStyle).style());
whiskerLineWidth=parent->getPlotStyle(parentPlotStyle).widthF();
m_whiskerCapLinePen.setColor(parent->getPlotStyle(parentPlotStyle).color());
m_whiskerCapLinePen.setStyle(parent->getPlotStyle(parentPlotStyle).style());
whiskerCapLineWidth=parent->getPlotStyle(parentPlotStyle).widthF();
m_medianLinePen.setColor(parent->getPlotStyle(parentPlotStyle).color());
m_medianLinePen.setStyle(parent->getPlotStyle(parentPlotStyle).style());
medianLineWidth=parent->getPlotStyle(parentPlotStyle).widthF();
m_meanSymbolLinePen=QPen(parent->getPlotStyle(parentPlotStyle).color(), parent->getPlotStyle(parentPlotStyle).style());
m_meanSymbolSize=parent->getPlotStyle(parentPlotStyle).symbolSize();
m_meanSymbolLineWidth=parent->getPlotStyle(parentPlotStyle).symbolLineWidthF();
m_meanSymbolType=parent->getPlotStyle(parentPlotStyle).symbol();
m_meanSymbolFillColor=parent->getPlotStyle(parentPlotStyle).symbolFillColor();
}
setWhiskerLineColor(getLineColor());
setWhiskerCapLineColor(getLineColor());
setMedianLineColor(getLineColor());
setMeanSymbolType(JKQTPPlus);
if (m_meanSymbolSize>0) {
violinWidthAbsolute=m_meanSymbolSize*6.0;
}
}
void JKQTPGraphViolinplotStyleMixin::setViolinWidthAbsolute(double __value)
{
violinWidthAbsolute=__value;
}
double JKQTPGraphViolinplotStyleMixin::getViolinWidthAbsolute() const
{
return violinWidthAbsolute;
}
void JKQTPGraphViolinplotStyleMixin::setViolinplotColor(QColor c, JKQTBasePlotter *parent)
{
setLineColor(c);
setFillColor(JKQTPGetDerivedColor(parent->getCurrentPlotterStyle().graphFillColorDerivationMode, c));
setMeanColor(c);
setMeanFillColor(JKQTPGetDerivedColor(parent->getCurrentPlotterStyle().graphFillColorDerivationMode, c));
setWhiskerLineColor(getLineColor());
setWhiskerCapLineColor(getLineColor());
setMedianLineColor(getLineColor());
c.setAlphaF(0.5);
setHighlightingLineColor(c);
}
void JKQTPGraphViolinplotStyleMixin::setViolinplotColor(QColor c, QColor bc, JKQTBasePlotter *parent)
{
setViolinplotColor(c, parent);
setFillColor(bc);
setMeanFillColor(bc);
}
void JKQTPGraphViolinplotStyleMixin::setRelativeWhiskerWidth(double __value)
{
relativeWhiskerWidth=__value;
}
double JKQTPGraphViolinplotStyleMixin::getRelativeWhiskerWidth() const
{
return relativeWhiskerWidth;
}
void JKQTPGraphViolinplotStyleMixin::setMeanSymbolType(JKQTPGraphSymbols __value)
{
m_meanSymbolType=__value;
}
JKQTPGraphSymbols JKQTPGraphViolinplotStyleMixin::getMeanSymbolType() const
{
return m_meanSymbolType;
}
void JKQTPGraphViolinplotStyleMixin::setMeanSize(double __value)
{
m_meanSymbolSize=__value;
}
double JKQTPGraphViolinplotStyleMixin::getMeanSize() const
{
return m_meanSymbolSize;
}
void JKQTPGraphViolinplotStyleMixin::setMeanColor(const QColor &__value)
{
m_meanSymbolLinePen.setColor(__value);
}
QColor JKQTPGraphViolinplotStyleMixin::getMeanColor() const
{
return m_meanSymbolLinePen.color();
}
void JKQTPGraphViolinplotStyleMixin::setMeanFillColor(const QColor &__value)
{
m_meanSymbolFillColor=__value;
}
QColor JKQTPGraphViolinplotStyleMixin::getMeanFillColor() const
{
return m_meanSymbolFillColor;
}
void JKQTPGraphViolinplotStyleMixin::setMeanLineWidth(double __value)
{
m_meanSymbolLineWidth=__value;
}
double JKQTPGraphViolinplotStyleMixin::getMeanLineWidth() const
{
return m_meanSymbolLineWidth;
}
JKQTPGraphViolinplotStyleMixin::ViolinStyle JKQTPGraphViolinplotStyleMixin::getViolinStyle() const
{
return m_violinStyle;
}
void JKQTPGraphViolinplotStyleMixin::setViolinStyle(JKQTPGraphViolinplotStyleMixin::ViolinStyle style)
{
m_violinStyle=style;
}
JKQTPGraphViolinplotStyleMixin::ViolinPositionMode JKQTPGraphViolinplotStyleMixin::getViolinPositionMode() const
{
return m_violinPositionMode;
}
void JKQTPGraphViolinplotStyleMixin::setViolinPositionMode(JKQTPGraphViolinplotStyleMixin::ViolinPositionMode positionMode)
{
m_violinPositionMode=positionMode;
}
void JKQTPGraphViolinplotStyleMixin::setMeanLineStyle(Qt::PenStyle __value)
{
this->m_meanSymbolLinePen.setStyle(__value);
}
Qt::PenStyle JKQTPGraphViolinplotStyleMixin::getMeanLineStyle() const
{
return this->m_meanSymbolLinePen.style();
}
void JKQTPGraphViolinplotStyleMixin::setMeanLineDashOffset(qreal offset)
{
m_meanSymbolLinePen.setDashOffset(offset);
}
qreal JKQTPGraphViolinplotStyleMixin::getMeanLineDashOffset() const
{
return m_meanSymbolLinePen.dashOffset();
}
void JKQTPGraphViolinplotStyleMixin::setMeanLineDashPattern(const QVector<qreal> &pattern)
{
m_meanSymbolLinePen.setDashPattern(pattern);
m_meanSymbolLinePen.setStyle(Qt::CustomDashLine);
}
QVector<qreal> JKQTPGraphViolinplotStyleMixin::getMeanLineDashPattern() const
{
return m_meanSymbolLinePen.dashPattern();
}
void JKQTPGraphViolinplotStyleMixin::setMedianLineWidth(double __value)
{
medianLineWidth=__value;
}
double JKQTPGraphViolinplotStyleMixin::getMedianLineWidth() const
{
return medianLineWidth;
}
void JKQTPGraphViolinplotStyleMixin::setMedianLineColor(QColor __value)
{
m_medianLinePen.setColor(__value);
}
QColor JKQTPGraphViolinplotStyleMixin::getMedianLineColor() const
{
return m_medianLinePen.color();
}
void JKQTPGraphViolinplotStyleMixin::setMedianLineStyle(Qt::PenStyle __value)
{
this->m_medianLinePen.setStyle(__value);
}
Qt::PenStyle JKQTPGraphViolinplotStyleMixin::getMedianLineStyle() const
{
return this->m_medianLinePen.style();
}
void JKQTPGraphViolinplotStyleMixin::setMedianLineDashOffset(qreal offset)
{
m_medianLinePen.setDashOffset(offset);
}
qreal JKQTPGraphViolinplotStyleMixin::getMedianLineDashOffset() const
{
return m_medianLinePen.dashOffset();
}
void JKQTPGraphViolinplotStyleMixin::setMedianLineDashPattern(const QVector<qreal> &pattern)
{
m_medianLinePen.setDashPattern(pattern);
m_medianLinePen.setStyle(Qt::CustomDashLine);
}
QVector<qreal> JKQTPGraphViolinplotStyleMixin::getMedianLineDashPattern() const
{
return m_medianLinePen.dashPattern();
}
void JKQTPGraphViolinplotStyleMixin::setMedianLineJoinStyle(Qt::PenJoinStyle style)
{
m_medianLinePen.setJoinStyle(style);
}
Qt::PenJoinStyle JKQTPGraphViolinplotStyleMixin::getMedianLineJoinStyle() const
{
return m_medianLinePen.joinStyle();
}
void JKQTPGraphViolinplotStyleMixin::setMedianLineCapStyle(Qt::PenCapStyle style)
{
m_medianLinePen.setCapStyle(style);
}
Qt::PenCapStyle JKQTPGraphViolinplotStyleMixin::getMedianLineCapStyle() const
{
return m_medianLinePen.capStyle();
}
void JKQTPGraphViolinplotStyleMixin::setMedianLineBrush(const QBrush &style)
{
m_medianLinePen.setBrush(style);
}
QBrush JKQTPGraphViolinplotStyleMixin::getMedianLineBrush() const
{
return m_medianLinePen.brush();
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerLineStyle(Qt::PenStyle __value)
{
this->m_whiskerLinePen.setStyle(__value);
}
Qt::PenStyle JKQTPGraphViolinplotStyleMixin::getWhiskerLineStyle() const
{
return this->m_whiskerLinePen.style();
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerLineWidth(double __value)
{
whiskerLineWidth=__value;
}
double JKQTPGraphViolinplotStyleMixin::getWhiskerLineWidth() const
{
return whiskerLineWidth;
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerLineColor(QColor __value)
{
m_whiskerLinePen.setColor(__value);
}
QColor JKQTPGraphViolinplotStyleMixin::getWhiskerLineColor() const
{
return m_whiskerLinePen.color();
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerLineDashOffset(qreal offset)
{
m_whiskerLinePen.setDashOffset(offset);
}
qreal JKQTPGraphViolinplotStyleMixin::getWhiskerLineDashOffset() const
{
return m_whiskerLinePen.dashOffset();
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerLineDashPattern(const QVector<qreal> &pattern)
{
m_whiskerLinePen.setDashPattern(pattern);
m_whiskerLinePen.setStyle(Qt::CustomDashLine);
}
QVector<qreal> JKQTPGraphViolinplotStyleMixin::getWhiskerLineDashPattern() const
{
return m_whiskerLinePen.dashPattern();
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerLineJoinStyle(Qt::PenJoinStyle style)
{
m_whiskerLinePen.setJoinStyle(style);
}
Qt::PenJoinStyle JKQTPGraphViolinplotStyleMixin::getWhiskerLineJoinStyle() const
{
return m_whiskerLinePen.joinStyle();
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerLineCapStyle(Qt::PenCapStyle style)
{
m_whiskerLinePen.setCapStyle(style);
}
Qt::PenCapStyle JKQTPGraphViolinplotStyleMixin::getWhiskerLineCapStyle() const
{
return m_whiskerLinePen.capStyle();
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerLineBrush(const QBrush &style)
{
m_whiskerLinePen.setBrush(style);
}
QBrush JKQTPGraphViolinplotStyleMixin::getWhiskerLineBrush() const
{
return m_whiskerLinePen.brush();
}
QPen JKQTPGraphViolinplotStyleMixin::getWhiskerPen(JKQTPEnhancedPainter &painter, JKQTBasePlotter *parent) const
{
QPen pw=m_whiskerLinePen;
pw.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*whiskerLineWidth)));
pw.setJoinStyle(Qt::MiterJoin);
pw.setCapStyle(Qt::FlatCap);
return pw;
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerCapLineStyle(Qt::PenStyle __value)
{
this->m_whiskerCapLinePen.setStyle(__value);
}
Qt::PenStyle JKQTPGraphViolinplotStyleMixin::getWhiskerCapLineStyle() const
{
return this->m_whiskerCapLinePen.style();
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerCapLineWidth(double __value)
{
whiskerCapLineWidth=__value;
}
double JKQTPGraphViolinplotStyleMixin::getWhiskerCapLineWidth() const
{
return whiskerCapLineWidth;
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerCapLineColor(QColor __value)
{
m_whiskerCapLinePen.setColor(__value);
}
QColor JKQTPGraphViolinplotStyleMixin::getWhiskerCapLineColor() const
{
return m_whiskerCapLinePen.color();
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerCapLineDashOffset(qreal offset)
{
m_whiskerCapLinePen.setDashOffset(offset);
}
qreal JKQTPGraphViolinplotStyleMixin::getWhiskerCapLineDashOffset() const
{
return m_whiskerCapLinePen.dashOffset();
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerCapLineDashPattern(const QVector<qreal> &pattern)
{
m_whiskerCapLinePen.setDashPattern(pattern);
m_whiskerCapLinePen.setStyle(Qt::CustomDashLine);
}
QVector<qreal> JKQTPGraphViolinplotStyleMixin::getWhiskerCapLineDashPattern() const
{
return m_whiskerCapLinePen.dashPattern();
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerCapLineJoinStyle(Qt::PenJoinStyle style)
{
m_whiskerCapLinePen.setJoinStyle(style);
}
Qt::PenJoinStyle JKQTPGraphViolinplotStyleMixin::getWhiskerCapLineJoinStyle() const
{
return m_whiskerCapLinePen.joinStyle();
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerCapLineCapStyle(Qt::PenCapStyle style)
{
m_whiskerCapLinePen.setCapStyle(style);
}
Qt::PenCapStyle JKQTPGraphViolinplotStyleMixin::getWhiskerCapLineCapStyle() const
{
return m_whiskerCapLinePen.capStyle();
}
void JKQTPGraphViolinplotStyleMixin::setWhiskerCapLineBrush(const QBrush &style)
{
m_whiskerCapLinePen.setBrush(style);
}
QBrush JKQTPGraphViolinplotStyleMixin::getWhiskerCapLineBrush() const
{
return m_whiskerCapLinePen.brush();
}
QPen JKQTPGraphViolinplotStyleMixin::getWhiskerCapPen(JKQTPEnhancedPainter &painter, JKQTBasePlotter *parent) const
{
QPen pw=m_whiskerCapLinePen;
pw.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*whiskerCapLineWidth)));
pw.setJoinStyle(Qt::MiterJoin);
return pw;
}
QPen JKQTPGraphViolinplotStyleMixin::getMedianPen(JKQTPEnhancedPainter &painter, JKQTBasePlotter *parent) const
{
QPen pw=m_medianLinePen;
pw.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*medianLineWidth)));
pw.setJoinStyle(Qt::MiterJoin);
pw.setCapStyle(Qt::FlatCap);
return pw;
}
QPen JKQTPGraphViolinplotStyleMixin::getMeanSymbolPen(JKQTPEnhancedPainter& painter, JKQTBasePlotter* parent) const {
QPen p=m_meanSymbolLinePen;
p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*m_meanSymbolLineWidth)));
p.setStyle(Qt::SolidLine);
p.setJoinStyle(Qt::RoundJoin);
p.setCapStyle(Qt::RoundCap);
return p;
}
QPen JKQTPGraphViolinplotStyleMixin::getMeanLinePen(JKQTPEnhancedPainter& painter, JKQTBasePlotter* parent) const {
QPen p=m_meanSymbolLinePen;
p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH,parent->pt2px(painter, parent->getLineWidthMultiplier()*m_meanSymbolLineWidth)));
p.setJoinStyle(Qt::MiterJoin);
p.setCapStyle(Qt::FlatCap);
return p;
}
QBrush JKQTPGraphViolinplotStyleMixin::getMeanSymbolBrush(JKQTPEnhancedPainter& /*painter*/, JKQTBasePlotter* /*parent*/) const {
QBrush b;
b.setColor(m_meanSymbolFillColor);
return b;
}
void JKQTPGraphViolinplotStyleMixin::plotStyledMeanSymbol(JKQTBasePlotter *parent, JKQTPEnhancedPainter &painter, double x, double y) const
{
JKQTPPlotSymbol(painter, x, y,m_meanSymbolType, parent->pt2px(painter, m_meanSymbolSize), parent->pt2px(painter, m_meanSymbolLineWidth*parent->getLineWidthMultiplier()), m_meanSymbolLinePen.color(), m_meanSymbolFillColor);
}
void JKQTPGraphViolinplotStyleMixin::plotVerticalViolinplot(JKQTBasePlotter *parent, JKQTPEnhancedPainter &painter, double xp, double xpleft, double xpright, const QVector<double> &violin_cat, const QVector<double> &violin_freq, double minp, double medianp, double maxp, double meanp) const
{
if (JKQTPIsOKFloat(xp) ) {
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
painter.setBrush(Qt::NoBrush);
double minstop=medianp;
double maxstop=medianp;
if (!JKQTPIsOKFloat(minstop) && JKQTPIsOKFloat(meanp)) minstop=meanp;
else if (!JKQTPIsOKFloat(minstop) && JKQTPIsOKFloat(maxp)) minstop=maxp;
if (!JKQTPIsOKFloat(maxstop) && JKQTPIsOKFloat(meanp)) maxstop=meanp;
else if (!JKQTPIsOKFloat(maxstop) && JKQTPIsOKFloat(minp)) maxstop=minp;
const double wl=fabs(xpright-xp);
const double wr=fabs(xpleft-xp);
const double xprightWhisker=xp+wr*getRelativeWhiskerWidth();
const double xpleftWhisker=xp-wl*getRelativeWhiskerWidth();
// draw violin
const int NViol=std::min(violin_cat.size(), violin_freq.size());
double medianMin=xpleft;
double medianMax=xpright;
if (NViol>0) {
double fmin=violin_freq[0], fmax=violin_freq[0];
for (int i=1; i<NViol; i++) {
fmin=std::min(fmin, violin_freq[i]);
fmax=std::max(fmax, violin_freq[i]);
}
const double freqFactorr=1.0/fmax*fabs(xpright-xp);
const double freqFactorl=1.0/fmax*fabs(xpleft-xp);
if (m_violinStyle==ViolinStyle::BoxViolin && NViol>1) {
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
painter.setPen(getLinePen(painter, parent));
painter.setBrush(getFillBrush(painter, parent));
for (int i=0; i<NViol; i++) {
if (i==0) {
const double hl=(violin_cat[1]-violin_cat[0]);
if (m_violinPositionMode==ViolinBoth) {
painter.drawRect(QRectF(QPointF(xp-violin_freq[i]*freqFactorl, violin_cat.first()), QPointF(xp+violin_freq[i]*freqFactorr, violin_cat.first()+hl/2.0)));
} else if (m_violinPositionMode==ViolinLeft) {
painter.drawRect(QRectF(QPointF(xp-violin_freq[i]*freqFactorl, violin_cat.first()), QPointF(xp, violin_cat.first()+hl/2.0)));
} else if (m_violinPositionMode==ViolinRight) {
painter.drawRect(QRectF(QPointF(xp, violin_cat.first()), QPointF(xp+violin_freq[i]*freqFactorr, violin_cat.first()+hl/2.0)));
}
} else if (i>0 && i<NViol-1) {
const double hl=(violin_cat[i]-violin_cat[i-1]);
if (m_violinPositionMode==ViolinBoth) {
painter.drawRect(QRectF(QPointF(xp-violin_freq[i]*freqFactorl, violin_cat[i]-hl/2.0), QPointF(xp+violin_freq[i]*freqFactorr, violin_cat[i]+hl/2.0)));
} else if (m_violinPositionMode==ViolinLeft) {
painter.drawRect(QRectF(QPointF(xp-violin_freq[i]*freqFactorl, violin_cat[i]-hl/2.0), QPointF(xp, violin_cat[i]+hl/2.0)));
} else if (m_violinPositionMode==ViolinRight) {
painter.drawRect(QRectF(QPointF(xp, violin_cat[i]-hl/2.0), QPointF(xp+violin_freq[i]*freqFactorr, violin_cat[i]+hl/2.0)));
}
} else if (i==NViol-1) {
const double hl=(violin_cat[i]-violin_cat[i-1]);
if (m_violinPositionMode==ViolinBoth) {
painter.drawRect(QRectF(QPointF(xp-violin_freq[i]*freqFactorl, violin_cat[i]-hl/2.0), QPointF(xp+violin_freq[i]*freqFactorr, violin_cat[i])));
} else if (m_violinPositionMode==ViolinLeft) {
painter.drawRect(QRectF(QPointF(xp-violin_freq[i]*freqFactorl, violin_cat[i]-hl/2.0), QPointF(xp, violin_cat[i])));
} else if (m_violinPositionMode==ViolinRight) {
painter.drawRect(QRectF(QPointF(xp, violin_cat[i]-hl/2.0), QPointF(xp+violin_freq[i]*freqFactorr, violin_cat[i])));
}
}
}
} else if (m_violinStyle==ViolinStyle::SmoothViolin && NViol>1) {
QPolygonF pleft, pright;
pleft<<QPointF(xp, violin_cat.first());
pright<<QPointF(xp, violin_cat.last());
for (int i=0; i<NViol; i++) {
pleft<<QPointF(xp-violin_freq[i]*freqFactorl, violin_cat[i]);
pright<<QPointF(xp+violin_freq[NViol-1-i]*freqFactorr, violin_cat[NViol-1-i]);
}
pleft<<QPointF(xp, violin_cat.last());
pright<<QPointF(xp, violin_cat.first());
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
painter.setPen(getLinePen(painter, parent));
painter.setBrush(getFillBrush(painter, parent));
if (m_violinPositionMode==ViolinBoth) {
pleft.append(pright);
painter.drawPolygon(pleft);
} else if (m_violinPositionMode==ViolinLeft) {
painter.drawPolygon(pleft);
} else if (m_violinPositionMode==ViolinRight) {
painter.drawPolygon(pright);
}
} else if (m_violinStyle==ViolinStyle::StepViolin && NViol>1) {
QPolygonF pleft, pright;
for (int i=0; i<NViol; i++) {
if (i==0) {
const double hl=(violin_cat[1]-violin_cat[0]);
pleft<<QPointF(xp, violin_cat.first())
<<QPointF(xp-violin_freq[i]*freqFactorl, violin_cat.first())
<<QPointF(xp-violin_freq[i]*freqFactorl, violin_cat.first()+hl/2.0);
} else if (i>0 && i<NViol-1) {
const double hl=(violin_cat[i]-violin_cat[i-1]);
pleft<<QPointF(xp-violin_freq[i]*freqFactorl, violin_cat[i]-hl/2.0)
<<QPointF(xp-violin_freq[i]*freqFactorl, violin_cat[i]+hl/2.0);
} else if (i==NViol-1) {
const double hl=(violin_cat[i]-violin_cat[i-1]);
pleft<<QPointF(xp-violin_freq[i]*freqFactorl, violin_cat[i]-hl/2.0)
<<QPointF(xp-violin_freq[i]*freqFactorl, violin_cat[i])
<<QPointF(xp, violin_cat[i]);
}
if (i==0) {
const double hr=fabs(violin_cat[NViol-1]-violin_cat[NViol-2]);
pright<<QPointF(xp, violin_cat.last())
<<QPointF(xp+violin_freq.last()*freqFactorr, violin_cat.last())
<<QPointF(xp+violin_freq.last()*freqFactorr, violin_cat.last()+hr/2.0);
} else if (i>0 && i<NViol-1) {
const double hr=fabs(violin_cat[NViol-1-i]-violin_cat[NViol-1-i-1]);
pright<<QPointF(xp+violin_freq[NViol-1-i]*freqFactorr, violin_cat[NViol-1-i]-hr/2.0)
<<QPointF(xp+violin_freq[NViol-1-i]*freqFactorr, violin_cat[NViol-1-i]+hr/2.0);
} else if (i==NViol-1) {
const double hr=fabs(violin_cat[1]-violin_cat[0]);
pright<<QPointF(xp+violin_freq[0]*freqFactorr, violin_cat[0]-hr/2.0)
<<QPointF(xp+violin_freq[0]*freqFactorr, violin_cat[0])
<<QPointF(xp, violin_cat[0]);
}
}
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
painter.setPen(getLinePen(painter, parent));
painter.setBrush(getFillBrush(painter, parent));
if (m_violinPositionMode==ViolinBoth) {
pleft.append(pright);
painter.drawPolygon(pleft);
} else if (m_violinPositionMode==ViolinLeft) {
painter.drawPolygon(pleft);
} else if (m_violinPositionMode==ViolinRight) {
painter.drawPolygon(pright);
}
}
}
// whisker lines
painter.setPen(getWhiskerPen(painter, parent));
if (JKQTPIsOKFloat(minp) && JKQTPIsOKFloat(minstop)) {
painter.drawLine(QLineF(xp, minp, xp, minstop));
}
if (JKQTPIsOKFloat(maxp) && JKQTPIsOKFloat(maxstop)) {
painter.drawLine(QLineF(xp, maxp, xp, maxstop));
}
// whisker caps
if (JKQTPIsOKFloat(xpleftWhisker) && JKQTPIsOKFloat(xprightWhisker)){
painter.setPen(getWhiskerCapPen(painter, parent));
if (JKQTPIsOKFloat(minp)) {
painter.drawLine(QLineF(xpleftWhisker, minp, xprightWhisker, minp));
}
if (JKQTPIsOKFloat(maxp)) {
painter.drawLine(QLineF(xpleftWhisker, maxp, xprightWhisker, maxp));
}
}
// draw median
if (JKQTPIsOKFloat(medianp)) {
painter.setBrush(Qt::NoBrush);
painter.setPen(getMedianPen(painter, parent));
painter.drawLine(QLineF(medianMin, medianp, medianMax, medianp));
}
// draw mean
if (JKQTPIsOKFloat(meanp)) {
painter.setBrush(Qt::NoBrush);
plotStyledMeanSymbol(parent, painter,xp,meanp);
}
}
}
void JKQTPGraphViolinplotStyleMixin::plotHorizontalViolinplot(JKQTBasePlotter *parent, JKQTPEnhancedPainter &painter, double yp, double ypbottom, double yptop, const QVector<double> &violin_cat, const QVector<double> &violin_freq, double minp, double medianp, double maxp, double meanp) const
{
if (JKQTPIsOKFloat(yp) ) {
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
painter.setBrush(Qt::NoBrush);
double minstop=medianp;
double maxstop=medianp;
if (!JKQTPIsOKFloat(minstop) && JKQTPIsOKFloat(meanp)) minstop=meanp;
else if (!JKQTPIsOKFloat(minstop) && JKQTPIsOKFloat(maxp)) minstop=maxp;
if (!JKQTPIsOKFloat(maxstop) && JKQTPIsOKFloat(meanp)) maxstop=meanp;
else if (!JKQTPIsOKFloat(maxstop) && JKQTPIsOKFloat(minp)) maxstop=minp;
const double wbot=fabs(ypbottom-yp);
const double wtop=fabs(yptop-yp);
const double xbottomWhisker=yp+wbot*getRelativeWhiskerWidth();
const double xptopWhisker=yp-wtop*getRelativeWhiskerWidth();
// draw violin
const int NViol=std::min(violin_cat.size(), violin_freq.size());
double medianMin=ypbottom;
double medianMax=yptop;
if (NViol>0) {
double fmin=violin_freq[0], fmax=violin_freq[0];
for (int i=1; i<NViol; i++) {
fmin=std::min(fmin, violin_freq[i]);
fmax=std::max(fmax, violin_freq[i]);
}
const double freqFactorr=1.0/fmax*fabs(yptop-yp);
const double freqFactorl=1.0/fmax*fabs(ypbottom-yp);
if (m_violinStyle==ViolinStyle::BoxViolin && NViol>1) {
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
painter.setPen(getLinePen(painter, parent));
painter.setBrush(getFillBrush(painter, parent));
for (int i=0; i<NViol; i++) {
if (i==0) {
const double hl=(violin_cat[1]-violin_cat[0]);
if (m_violinPositionMode==ViolinBoth) {
painter.drawRect(QRectF(QPointF(violin_cat.first(), yp+violin_freq[i]*freqFactorl), QPointF(violin_cat.first()+hl/2.0, yp-violin_freq[i]*freqFactorr)));
} else if (m_violinPositionMode==ViolinLeft) {
painter.drawRect(QRectF(QPointF(violin_cat.first(), yp+violin_freq[i]*freqFactorl), QPointF(violin_cat.first()+hl/2.0, yp)));
} else if (m_violinPositionMode==ViolinRight) {
painter.drawRect(QRectF(QPointF(violin_cat.first(), yp), QPointF(violin_cat.first()+hl/2.0, yp-violin_freq[i]*freqFactorr)));
}
} else if (i>0 && i<NViol-1) {
const double hl=(violin_cat[i]-violin_cat[i-1]);
if (m_violinPositionMode==ViolinBoth) {
painter.drawRect(QRectF(QPointF(violin_cat[i]-hl/2.0, yp+violin_freq[i]*freqFactorl), QPointF(violin_cat[i]+hl/2.0, yp-violin_freq[i]*freqFactorr)));
} else if (m_violinPositionMode==ViolinLeft) {
painter.drawRect(QRectF(QPointF(violin_cat[i]-hl/2.0, yp+violin_freq[i]*freqFactorl), QPointF(violin_cat[i]+hl/2.0, yp)));
} else if (m_violinPositionMode==ViolinRight) {
painter.drawRect(QRectF(QPointF(violin_cat[i]-hl/2.0, yp), QPointF(violin_cat[i]+hl/2.0, yp-violin_freq[i]*freqFactorr)));
}
} else if (i==NViol-1) {
const double hl=(violin_cat[i]-violin_cat[i-1]);
if (m_violinPositionMode==ViolinBoth) {
painter.drawRect(QRectF(QPointF(violin_cat[i]-hl/2.0, yp+violin_freq[i]*freqFactorl), QPointF(violin_cat[i], yp-violin_freq[i]*freqFactorr)));
} else if (m_violinPositionMode==ViolinLeft) {
painter.drawRect(QRectF(QPointF(violin_cat[i]-hl/2.0, yp+violin_freq[i]*freqFactorl), QPointF(violin_cat[i], yp)));
} else if (m_violinPositionMode==ViolinRight) {
painter.drawRect(QRectF(QPointF(violin_cat[i]-hl/2.0, yp), QPointF(violin_cat[i], yp-violin_freq[i]*freqFactorr)));
}
}
}
} else if (m_violinStyle==ViolinStyle::SmoothViolin && NViol>1) {
QPolygonF pleft, pright;
pleft<<QPointF(violin_cat.first(), yp);
pright<<QPointF(violin_cat.last(), yp);
for (int i=0; i<NViol; i++) {
pleft<<QPointF(violin_cat[i], yp+violin_freq[i]*freqFactorl);
pright<<QPointF(violin_cat[NViol-1-i], yp-violin_freq[NViol-1-i]*freqFactorr);
}
pleft<<QPointF(violin_cat.last(), yp);
pright<<QPointF(violin_cat.first(), yp);
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
painter.setPen(getLinePen(painter, parent));
painter.setBrush(getFillBrush(painter, parent));
if (m_violinPositionMode==ViolinBoth) {
pleft.append(pright);
painter.drawPolygon(pleft);
} else if (m_violinPositionMode==ViolinLeft) {
painter.drawPolygon(pleft);
} else if (m_violinPositionMode==ViolinRight) {
painter.drawPolygon(pright);
}
} else if (m_violinStyle==ViolinStyle::StepViolin && NViol>1) {
QPolygonF pleft, pright;
for (int i=0; i<NViol; i++) {
if (i==0) {
const double hl=(violin_cat[1]-violin_cat[0]);
pleft<<QPointF(violin_cat.first(), yp)
<<QPointF(violin_cat.first(), yp+violin_freq[i]*freqFactorl)
<<QPointF(violin_cat.first()+hl/2.0, yp+violin_freq[i]*freqFactorl);
} else if (i>0 && i<NViol-1) {
const double hl=(violin_cat[i]-violin_cat[i-1]);
pleft<<QPointF(violin_cat[i]-hl/2.0, yp+violin_freq[i]*freqFactorl)
<<QPointF(violin_cat[i]+hl/2.0, yp+violin_freq[i]*freqFactorl);
} else if (i==NViol-1) {
const double hl=(violin_cat[i]-violin_cat[i-1]);
pleft<<QPointF(violin_cat[i]-hl/2.0, yp+violin_freq[i]*freqFactorl)
<<QPointF(violin_cat[i], yp+violin_freq[i]*freqFactorl)
<<QPointF(violin_cat[i], yp);
}
if (i==0) {
const double hr=-fabs(violin_cat[NViol-1]-violin_cat[NViol-2]);
pright<<QPointF(violin_cat.last(), yp)
<<QPointF(violin_cat.last(), yp-violin_freq.last()*freqFactorr)
<<QPointF(violin_cat.last()+hr/2.0, yp-violin_freq.last()*freqFactorr);
} else if (i>0 && i<NViol-1) {
const double hr=-fabs(violin_cat[NViol-1-i]-violin_cat[NViol-1-i-1]);
pright<<QPointF(violin_cat[NViol-1-i]-hr/2.0, yp-violin_freq[NViol-1-i]*freqFactorr)
<<QPointF(violin_cat[NViol-1-i]+hr/2.0, yp-violin_freq[NViol-1-i]*freqFactorr);
} else if (i==NViol-1) {
const double hr=-fabs(violin_cat[1]-violin_cat[0]);
pright<<QPointF(violin_cat[0]-hr/2.0, yp-violin_freq[0]*freqFactorr)
<<QPointF(violin_cat[0], yp-violin_freq[0]*freqFactorr)
<<QPointF(violin_cat[0], yp);
}
}
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
painter.setPen(getLinePen(painter, parent));
painter.setBrush(getFillBrush(painter, parent));
if (m_violinPositionMode==ViolinBoth) {
pleft.append(pright);
painter.drawPolygon(pleft);
} else if (m_violinPositionMode==ViolinLeft) {
painter.drawPolygon(pleft);
} else if (m_violinPositionMode==ViolinRight) {
painter.drawPolygon(pright);
}
}
}
// whisker lines
painter.setPen(getWhiskerPen(painter, parent));
if (JKQTPIsOKFloat(minp) && JKQTPIsOKFloat(minstop)) {
painter.drawLine(QLineF(minp, yp, minstop, yp));
}
if (JKQTPIsOKFloat(maxp) && JKQTPIsOKFloat(maxstop)) {
painter.drawLine(QLineF(maxp, yp, maxstop, yp));
}
// whisker caps
if (JKQTPIsOKFloat(xptopWhisker) && JKQTPIsOKFloat(xbottomWhisker)){
painter.setPen(getWhiskerCapPen(painter, parent));
if (JKQTPIsOKFloat(minp)) {
painter.drawLine(QLineF(minp, xptopWhisker, minp, xbottomWhisker));
}
if (JKQTPIsOKFloat(maxp)) {
painter.drawLine(QLineF(maxp, xptopWhisker, maxp, xbottomWhisker));
}
}
// draw median
if (JKQTPIsOKFloat(medianp)) {
painter.setBrush(Qt::NoBrush);
painter.setPen(getMedianPen(painter, parent));
painter.drawLine(QLineF(medianp, medianMin, medianp, medianMax));
}
// draw mean
if (JKQTPIsOKFloat(meanp)) {
painter.setBrush(Qt::NoBrush);
plotStyledMeanSymbol(parent, painter,meanp,yp);
}
}
}
void JKQTPGraphViolinplotStyleMixin::plotVerticalKeyMarker(JKQTBasePlotter *parent, JKQTPEnhancedPainter &painter, const QRectF &rect)
{
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
QPen p=getLinePenForRects(painter, parent);
QPen pw=getWhiskerPen(painter, parent);
QPen pm=getMedianPen(painter, parent);
QPen np(Qt::NoPen);
QBrush b=getFillBrush(painter, parent);
p.setWidthF(qMin(0.5, p.widthF()));
p.setStyle(Qt::SolidLine);
pw.setWidthF(qMin(0.5, pw.widthF()));
pw.setStyle(Qt::SolidLine);
pm.setWidthF(qMin(0.5, pm.widthF()));
pm.setStyle(Qt::SolidLine);
const double x=rect.left()+rect.width()/2.0;
const double xma=x+rect.width()/2.5;
const double xmi=x-rect.width()/2.5;
const double min=rect.bottom();
const double max=rect.top();
const double median=max+rect.height()/2.0;
QPainterPath path;
path.moveTo(x,max);
path.cubicTo(x,max+rect.height()/4.0,xma,median-rect.height()/4.0,xma,median);
path.cubicTo(xma,median+rect.height()/4.0,x,min-rect.height()/4.0,x,min);
path.cubicTo(x,min-rect.height()/4.0,xmi,median+rect.height()/4.0,xmi,median);
path.cubicTo(xmi,median-rect.height()/4.0,x,max+rect.height()/4.0,x,max);
painter.setPen(p);
{
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
painter.setBrush(b);
painter.drawPath(path);
}
painter.setPen(pm);
painter.drawLine(QLineF(xmi, median, xma, median));
painter.setPen(pw);
painter.drawLine(QLineF(x, max, x, min));
}
void JKQTPGraphViolinplotStyleMixin::plotHorizontalKeyMarker(JKQTBasePlotter *parent, JKQTPEnhancedPainter &painter, const QRectF &rect)
{
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
painter.translate(rect.center());
painter.rotate(90);
painter.translate(-1.0*rect.height()/2.0, -1.0*rect.width()/2.0);
plotVerticalKeyMarker(parent, painter, QRectF(0,0,rect.height(),rect.width()));
}

View File

@ -0,0 +1,427 @@
/*
Copyright (c) 2008-2019 Jan W. Krieger (<jan@jkrieger.de>)
This software is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License (LGPL) as published by
the Free Software Foundation, either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License (LGPL) for more details.
You should have received a copy of the GNU Lesser General Public License (LGPL)
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QString>
#include <QPainter>
#include "jkqtplotter/jkqtptools.h"
#include "jkqtcommon/jkqtp_imexport.h"
#include "jkqtplotter/jkqtpgraphsbase.h"
#include "jkqtplotter/jkqtpgraphsbasestylingmixins.h"
#ifndef jkqtpgraphsviolinplotstylingmixins_H
#define jkqtpgraphsviolinplotstylingmixins_H
/*! \brief Styling Mix-In for Violinplots
\ingroup jkqtplotter_basegraphs_stylemixins
\image html JKQTPViolinplotHorizontalElement_elements.png
*/
class JKQTP_LIB_EXPORT JKQTPGraphViolinplotStyleMixin: public JKQTPGraphLineStyleMixin, public JKQTPGraphFillStyleMixin {
Q_GADGET
public:
/** \brief class constructor */
JKQTPGraphViolinplotStyleMixin();
void initViolinplotStyle(JKQTBasePlotter* parent, int &parentPlotStyle);
/*! \copydoc violinWidthAbsolute */
void setViolinWidthAbsolute(double __value);
/*! \copydoc violinWidthAbsolute */
double getViolinWidthAbsolute() const;
/*! \copydoc relativeWhiskerWidth */
void setRelativeWhiskerWidth(double __value);
/*! \copydoc relativeWhiskerWidth */
double getRelativeWhiskerWidth() const;
/*! \brief set the line style of whisker lines */
void setWhiskerLineStyle(Qt::PenStyle __value);
/*! \brief get the line style of whisker lines */
Qt::PenStyle getWhiskerLineStyle() const;
/*! \brief set the width [pt] of whisker lines */
void setWhiskerLineWidth(double __value);
/*! \brief get the width [pt] of whisker lines */
double getWhiskerLineWidth() const;
/*! \brief set the color of whisker lines */
void setWhiskerLineColor(QColor __value);
/*! \brief get the color of whisker lines */
QColor getWhiskerLineColor() const;
/** \brief sets the dash offset for a custom dash style of whisker lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashOffset
*/
void setWhiskerLineDashOffset(qreal offset);
/** \brief returns the dash offset for a custom dash style of whisker lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashOffset
*/
qreal getWhiskerLineDashOffset() const;
/** \brief sets the dash pattern for a custom dash style of whisker lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashPattern
*/
void setWhiskerLineDashPattern(const QVector<qreal> &pattern);
/** \brief gets the dash pattern for a custom dash style of whisker lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashPattern
*/
QVector<qreal> getWhiskerLineDashPattern() const;
/** \brief sets the join style of whisker lines
* \see https://doc.qt.io/qt-5/qpen.html#setJoinStyle
*/
void setWhiskerLineJoinStyle(Qt::PenJoinStyle style);
/** \brief returns the join style of whisker lines
* \see https://doc.qt.io/qt-5/qpen.html#setJoinStyle
*/
Qt::PenJoinStyle getWhiskerLineJoinStyle() const;
/** \brief sets the cap style of whisker lines
* \see https://doc.qt.io/qt-5/qpen.html#setCapStyle
*/
void setWhiskerLineCapStyle(Qt::PenCapStyle style);
/** \brief gets the cap style of whisker lines
* \see https://doc.qt.io/qt-5/qpen.html#setCapStyle
*/
Qt::PenCapStyle getWhiskerLineCapStyle() const;
/** \brief sets the brush used to fill the line area of whisker lines
* \see https://doc.qt.io/qt-5/qpen.html#setBrush
*/
void setWhiskerLineBrush(const QBrush& style);
/** \brief gets the brush used to fill the line area of whisker lines
* \see https://doc.qt.io/qt-5/qpen.html#setBrush
*/
QBrush getWhiskerLineBrush() const;
/** \brief build a pen to be used for drawing whiskers */
QPen getWhiskerPen(JKQTPEnhancedPainter &painter, JKQTBasePlotter *parent) const;
/*! \brief set the line style of whisker cap lines */
void setWhiskerCapLineStyle(Qt::PenStyle __value);
/*! \brief get the line style of whisker cap lines */
Qt::PenStyle getWhiskerCapLineStyle() const;
/*! \brief set the width [pt] of whisker cap lines */
void setWhiskerCapLineWidth(double __value);
/*! \brief get the width [pt] of whisker cap lines */
double getWhiskerCapLineWidth() const;
/*! \brief set the color of whisker cap lines */
void setWhiskerCapLineColor(QColor __value);
/*! \brief get the color of whisker cap lines */
QColor getWhiskerCapLineColor() const;
/** \brief sets the dash offset for a custom dash style of whisker cap lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashOffset
*/
void setWhiskerCapLineDashOffset(qreal offset);
/** \brief returns the dash offset for a custom dash style of whisker cap lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashOffset
*/
qreal getWhiskerCapLineDashOffset() const;
/** \brief sets the dash pattern for a custom dash style of whisker cap lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashPattern
*/
void setWhiskerCapLineDashPattern(const QVector<qreal> &pattern);
/** \brief gets the dash pattern for a custom dash style of whisker cap lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashPattern
*/
QVector<qreal> getWhiskerCapLineDashPattern() const;
/** \brief sets the join style of whisker cap lines
* \see https://doc.qt.io/qt-5/qpen.html#setJoinStyle
*/
void setWhiskerCapLineJoinStyle(Qt::PenJoinStyle style);
/** \brief returns the join style of whisker cap lines
* \see https://doc.qt.io/qt-5/qpen.html#setJoinStyle
*/
Qt::PenJoinStyle getWhiskerCapLineJoinStyle() const;
/** \brief sets the cap style of whisker cap lines
* \see https://doc.qt.io/qt-5/qpen.html#setCapStyle
*/
void setWhiskerCapLineCapStyle(Qt::PenCapStyle style);
/** \brief gets the cap style of whisker cap lines
* \see https://doc.qt.io/qt-5/qpen.html#setCapStyle
*/
Qt::PenCapStyle getWhiskerCapLineCapStyle() const;
/** \brief sets the brush used to fill the line area of whisker cap lines
* \see https://doc.qt.io/qt-5/qpen.html#setBrush
*/
void setWhiskerCapLineBrush(const QBrush& style);
/** \brief gets the brush used to fill the line area of whisker cap lines
* \see https://doc.qt.io/qt-5/qpen.html#setBrush
*/
QBrush getWhiskerCapLineBrush() const;
/** \brief build a pen to be used for drawing whisker caps */
QPen getWhiskerCapPen(JKQTPEnhancedPainter &painter, JKQTBasePlotter *parent) const;
/*! \brief set the line style of median lines */
void setMedianLineStyle(Qt::PenStyle __value);
/*! \brief get the line style of median lines */
Qt::PenStyle getMedianLineStyle() const;
/*! \brief set the width [pt] of median lines */
void setMedianLineWidth(double __value);
/*! \brief get the width [pt] of median lines */
double getMedianLineWidth() const;
/*! \brief set the color of median lines */
void setMedianLineColor(QColor __value);
/*! \brief get the color of median lines */
QColor getMedianLineColor() const;
/** \brief sets the dash offset for a custom dash style of median lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashOffset
*/
void setMedianLineDashOffset(qreal offset);
/** \brief returns the dash offset for a custom dash style of median lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashOffset
*/
qreal getMedianLineDashOffset() const;
/** \brief sets the dash pattern for a custom dash style of median lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashPattern
*/
void setMedianLineDashPattern(const QVector<qreal> &pattern);
/** \brief gets the dash pattern for a custom dash style of median lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashPattern
*/
QVector<qreal> getMedianLineDashPattern() const;
/** \brief sets the join style of median lines
* \see https://doc.qt.io/qt-5/qpen.html#setJoinStyle
*/
void setMedianLineJoinStyle(Qt::PenJoinStyle style);
/** \brief returns the join style of median lines
* \see https://doc.qt.io/qt-5/qpen.html#setJoinStyle
*/
Qt::PenJoinStyle getMedianLineJoinStyle() const;
/** \brief sets the cap style of median lines
* \see https://doc.qt.io/qt-5/qpen.html#setCapStyle
*/
void setMedianLineCapStyle(Qt::PenCapStyle style);
/** \brief gets the cap style of median lines
* \see https://doc.qt.io/qt-5/qpen.html#setCapStyle
*/
Qt::PenCapStyle getMedianLineCapStyle() const;
/** \brief sets the brush used to fill the line area of median lines
* \see https://doc.qt.io/qt-5/qpen.html#setBrush
*/
void setMedianLineBrush(const QBrush& style);
/** \brief gets the brush used to fill the line area of median lines
* \see https://doc.qt.io/qt-5/qpen.html#setBrush
*/
QBrush getMedianLineBrush() const;
/** \brief build a pen to be used for drawing medians */
QPen getMedianPen(JKQTPEnhancedPainter &painter, JKQTBasePlotter *parent) const;
/*! \brief set the line style of Mean lines */
void setMeanLineStyle(Qt::PenStyle __value);
/*! \brief get the line style of Mean lines */
Qt::PenStyle getMeanLineStyle() const;
/** \brief sets the dash offset for a custom dash style of Mean lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashOffset
*/
void setMeanLineDashOffset(qreal offset);
/** \brief returns the dash offset for a custom dash style of Mean lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashOffset
*/
qreal getMeanLineDashOffset() const;
/** \brief sets the dash pattern for a custom dash style of Mean lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashPattern
*/
void setMeanLineDashPattern(const QVector<qreal> &pattern);
/** \brief gets the dash pattern for a custom dash style of Mean lines
* \see https://doc.qt.io/qt-5/qpen.html#setDashPattern
*/
QVector<qreal> getMeanLineDashPattern() const;
/** \brief set the type of the symbol for the mean */
void setMeanSymbolType(JKQTPGraphSymbols __value);
/** \brief get the type of the symbol for the mean */
JKQTPGraphSymbols getMeanSymbolType() const;
/** \brief set the size (=diameter in pt) of the symbol for the mean (in pt) */
void setMeanSize(double __value);
/** \brief get the size (=diameter in pt) of the symbol for the mean (in pt) */
double getMeanSize() const;
/** \brief set the color of the symbol for the mean, or mean line */
void setMeanColor(const QColor & __value);
/** \brief set the color of the symbol for the mean, or mean line */
QColor getMeanColor() const;
/** \brief set the color of filling of the symbol for the mean */
void setMeanFillColor(const QColor & __value);
/** \brief set the color of filling of the symbol for the mean */
QColor getMeanFillColor() const;
/** \brief set the line width of the symbol for the mean outline, or mean line (in pt) */
void setMeanLineWidth(double __value);
/** \brief get the line width of the symbol for the mean outline, or mean line (in pt) */
double getMeanLineWidth() const;
/** \brief defines how to draw the violin graph */
enum ViolinStyle {
SmoothViolin, /*!< \brief connect all violin points by lines, resulting in a (nearly) smooth curve \image html JKQTPGraphViolinplot_SmoothViolin.png */
StepViolin, /*!< \brief connect violin points by a steped line, but fully filled \image html JKQTPGraphViolinplot_StepViolin.png */
BoxViolin, /*!< \brief violin datapoints are drawn like a boxplot \image html JKQTPGraphViolinplot_BoxViolin.png */
};
/** \brief returns the style of the violin plot */
ViolinStyle getViolinStyle() const;
/** \brief set the style of the violin plot */
void setViolinStyle(ViolinStyle style);
/** \brief defines where to draw the violin graph with respect to the violin center position */
enum ViolinPositionMode {
ViolinLeft, /*!< \brief draw violin on the left hand side only (for vertical violins) \image html JKQTPGraphViolinplot_ViolinLeft.png */
ViolinBottom=ViolinLeft, /*!< \brief draw violin on the bottom side only (for horizontal violins) \image html JKQTPGraphViolinplot_ViolinBottom.png */
ViolinRight, /*!< \brief draw violin on the right hand side only (for vertical violins) \image html JKQTPGraphViolinplot_ViolinRight.png */
ViolinTop=ViolinRight, /*!< \brief draw violin on the top side only (for horizontal violins) \image html JKQTPGraphViolinplot_ViolinTop.png */
ViolinBoth /*!< \brief draw violin on the left+right or top+bottom side \image html JKQTPGraphViolinplot_ViolinBoth.png \image html JKQTPGraphViolinplot_ViolinHBoth.png */
};
/** \brief returns the position mode of the violin plot */
ViolinPositionMode getViolinPositionMode() const;
/** \brief set the position mode of the violin plot */
void setViolinPositionMode(ViolinPositionMode positionMode);
/** \brief constructs a QPen from the line styling properties to draw the mean line */
QPen getMeanLinePen(JKQTPEnhancedPainter &painter, JKQTBasePlotter* parent) const;
/** \brief constructs a QPen from the line styling properties to draw the mean symbol */
QPen getMeanSymbolPen(JKQTPEnhancedPainter &painter, JKQTBasePlotter* parent) const;
/** \brief constructs a QPen from the line styling properties */
QBrush getMeanSymbolBrush(JKQTPEnhancedPainter &painter, JKQTBasePlotter* parent) const;
/*! \brief set the color of the graph (colors all elements, based on the given color \a c )*/
void setViolinplotColor(QColor c, JKQTBasePlotter *parent);
/*! \brief set the color of the graph (colors all elements, based on the given color \a c , sets background colors from \a bc )*/
void setViolinplotColor(QColor c, QColor bc, JKQTBasePlotter *parent);
protected:
/*! \brief plot a symbol at location x,y (in painter coordinates), using the current style
\param parent parent JKQTBasePlotter of the graph that uses this mix-in (used e.g. for line-width transformation)
\param painter the <a href="http://doc.qt.io/qt-5/qpainter.html">QPainter</a> to draw to
\param x x-coordinate of the symbol center
\param y y-coordinate of the symbol center
*/
void plotStyledMeanSymbol(JKQTBasePlotter* parent, JKQTPEnhancedPainter &painter, double x, double y) const;
/** \brief draws a vertical boxplot, with all coordinates/sizes given in coordinates of the given painter,
* using the style properties declared in this class. Provide a parameter with \c JKQTP_NAN of you
* don't want it to be drawn, or don't know its value
*
* \param parent parent JKQTBasePlotter of the graph that uses this mix-in (used e.g. for line-width transformation)
* \param painter the <a href="http://doc.qt.io/qt-5/qpainter.html">QPainter</a> to draw to
* \param xp x-coordinate of the boxplot center
* \param xpleft x-coordinate of the boxplot box left edge
* \param xpright x-coordinate of the boxplot box right edge
* \param violin_cat category-dataset for the violin (y-values of KDE/histogram/...)
* \param violin_freq frequency/count-dataset for the violin (y-values/frequencies of KDE/histogram/...)
* \param minp y-coordinate of the minimum (lower whisker)
* \param medianp y-coordinate the median
* \param maxp y-coordinate of the maximum (upper whisker)
* \param meanp y-coordinate of the mean (symbol or line)
*/
void plotVerticalViolinplot(JKQTBasePlotter* parent, JKQTPEnhancedPainter &painter, double xp, double xpleft, double xpright, const QVector<double> &violin_cat, const QVector<double>& violin_freq, double minp, double medianp, double maxp, double meanp=JKQTP_NAN) const;
/** \brief draws a horizontal boxplot, with all coordinates/sizes given in coordinates of the given painter,
* using the style properties declared in this class. Provide a parameter with \c JKQTP_NAN of you
* don't want it to be drawn, or don't know its value
*
* \param parent parent JKQTBasePlotter of the graph that uses this mix-in (used e.g. for line-width transformation)
* \param painter the <a href="http://doc.qt.io/qt-5/qpainter.html">QPainter</a> to draw to
* \param yp y-coordinate of the boxplot center
* \param ypbottom y-coordinate of the boxplot box bottom edge
* \param yptop y-coordinate of the boxplot box top edge
* \param violin_cat category-dataset for the violin (x-values of KDE/histogram/...)
* \param violin_freq frequency/count-dataset for the violin (y-values/frequencies of KDE/histogram/...)
* \param minp x-coordinate of the minimum (lower whisker)
* \param medianp x-coordinate the median
* \param maxp x-coordinate of the maximum (upper whisker)
* \param meanp x-coordinate of the mean (symbol or line)
*/
void plotHorizontalViolinplot(JKQTBasePlotter* parent, JKQTPEnhancedPainter &painter, double yp, double ypbottom, double yptop, const QVector<double>& violin_cat, const QVector<double>& violin_freq, double minp, double medianp, double maxp, double meanp=JKQTP_NAN) const;
/** \brief draw a small, stylized, vertical symbol into \a rect that symbolizes a boxplot, e.g. in a plot legend */
void plotVerticalKeyMarker(JKQTBasePlotter* parent, JKQTPEnhancedPainter &painter, const QRectF& rect);
/** \brief draw a small, stylized, horizontal symbol into \a rect that symbolizes a boxplot, e.g. in a plot legend */
void plotHorizontalKeyMarker(JKQTBasePlotter* parent, JKQTPEnhancedPainter &painter, const QRectF& rect);
private:
/** \brief which symbol to use for the datapoints */
JKQTPGraphSymbols m_meanSymbolType;
/** \brief size (diameter in pt) of the symbol for the data points, given in pt */
double m_meanSymbolSize;
/** \brief outline color of the symbol or line pen of the mean-line */
QPen m_meanSymbolLinePen;
/** \brief color of the symbol filling */
QColor m_meanSymbolFillColor;
/** \brief width (in pt) of the lines used to plot the symbol for the data points, given in pt */
double m_meanSymbolLineWidth;
/** \brief line style of the whisker lines */
QPen m_whiskerLinePen;
/** \brief line width (in pt) of the whisker lines */
double whiskerLineWidth;
/** \brief line style of the whisker cap lines */
QPen m_whiskerCapLinePen;
/** \brief line width (in pt) of the whisker cap lines */
double whiskerCapLineWidth;
/** \brief line style of the median lines */
QPen m_medianLinePen;
/** \brief line width (in pt) of the median lines */
double medianLineWidth;
/** \brief width of box in pt.
*
* \note If several boxplots are drawn, the width is typically calculated,
* based on a relative width and the position distances, see JKQTPBoxplotVerticalGraph::boxWidthRelative */
double violinWidthAbsolute;
/** \brief width of the whiskers, relative to the box width (default: 0.5) */
double relativeWhiskerWidth;
/** \brief the style of the violin plot */
ViolinStyle m_violinStyle;
/** \brief the position mode of the violin plot */
ViolinPositionMode m_violinPositionMode;
};
#endif // jkqtpgraphsviolinplotstylingmixins_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB