2019-01-20 23:15:10 +08:00
|
|
|
/*
|
2022-07-19 19:40:43 +08:00
|
|
|
Copyright (c) 2008-2022 Jan W. Krieger (<jan@jkrieger.de>)
|
2019-01-20 23:15:10 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
2019-02-08 00:24:46 +08:00
|
|
|
the Free Software Foundation, either version 2.1 of the License, or
|
2019-01-20 23:15:10 +08:00
|
|
|
(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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-06-20 22:06:31 +08:00
|
|
|
#include "jkqtplotter/graphs/jkqtpsinglecolumnsymbols.h"
|
2019-01-20 23:15:10 +08:00
|
|
|
#include "jkqtplotter/jkqtpbaseplotter.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <iostream>
|
2019-05-30 04:40:02 +08:00
|
|
|
#include "jkqtplotter/jkqtptools.h"
|
2019-01-20 23:15:10 +08:00
|
|
|
#include "jkqtplotter/jkqtpbaseelements.h"
|
|
|
|
#include "jkqtplotter/jkqtplotter.h"
|
2019-05-30 04:40:02 +08:00
|
|
|
#include "jkqtcommon/jkqtpdrawingtools.h"
|
2019-01-26 02:24:19 +08:00
|
|
|
#include <random>
|
2019-01-20 23:15:10 +08:00
|
|
|
|
|
|
|
#define SmallestGreaterZeroCompare_xvsgz() if ((xvsgz>10.0*DBL_MIN)&&((smallestGreaterZero<10.0*DBL_MIN) || (xvsgz<smallestGreaterZero))) smallestGreaterZero=xvsgz;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JKQTPSingleColumnSymbolsGraph::JKQTPSingleColumnSymbolsGraph(JKQTBasePlotter *parent):
|
2024-01-10 17:35:43 +08:00
|
|
|
JKQTPSingleColumnGraph(parent), seedValue(123456), positionScatterStyle(NoScatter), position(0), width(1)
|
|
|
|
|
2019-01-20 23:15:10 +08:00
|
|
|
{
|
|
|
|
parentPlotStyle=-1;
|
|
|
|
dataDirection=DataDirection::Y;
|
|
|
|
|
2020-09-26 21:58:58 +08:00
|
|
|
initSymbolStyle(parent, parentPlotStyle, JKQTPPlotStyleType::Default);
|
2019-01-20 23:15:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JKQTPSingleColumnSymbolsGraph::JKQTPSingleColumnSymbolsGraph(JKQTPlotter *parent):
|
2019-01-26 19:35:57 +08:00
|
|
|
JKQTPSingleColumnSymbolsGraph(parent->getPlotter())
|
2019-01-20 23:15:10 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool JKQTPSingleColumnSymbolsGraph::getXMinMax(double &minx, double &maxx, double &smallestGreaterZero)
|
|
|
|
{
|
2019-06-21 21:46:53 +08:00
|
|
|
//std::cout<<"JKQTPSingleColumnSymbolsGraph::getXMinMax(minx="<<minx<<", maxx="<<maxx<<", smallestGreaterZero="<<smallestGreaterZero<<")\n";
|
2019-01-20 23:15:10 +08:00
|
|
|
if (dataDirection==DataDirection::X) {
|
2019-06-21 21:46:53 +08:00
|
|
|
bool res= getDataMinMax(dataColumn, minx, maxx, smallestGreaterZero);
|
|
|
|
//std::cout<<"JKQTPSingleColumnSymbolsGraph::getXMinMax(minx="<<minx<<", maxx="<<maxx<<", smallestGreaterZero="<<smallestGreaterZero<<"): dataDirection==DataDirection::X -->"<<std::boolalpha<<res<<"\n";
|
|
|
|
return res;
|
2019-01-20 23:15:10 +08:00
|
|
|
} else {
|
|
|
|
minx=position;
|
|
|
|
maxx=position;
|
|
|
|
if (positionScatterStyle!=NoScatter) {
|
|
|
|
minx=position-width/2;
|
|
|
|
maxx=position+width/2;
|
|
|
|
}
|
2019-06-21 21:46:53 +08:00
|
|
|
///std::cout<<"JKQTPSingleColumnSymbolsGraph::getXMinMax(minx="<<minx<<", maxx="<<maxx<<", smallestGreaterZero="<<smallestGreaterZero<<"): dataDirection!=DataDirection::X -->"<<std::boolalpha<<true<<"\n";
|
2019-01-20 23:15:10 +08:00
|
|
|
return true;
|
|
|
|
//smallestGreaterZero=qMax(double(0.0), qMin(baseline, baseline+peakHeight));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JKQTPSingleColumnSymbolsGraph::getYMinMax(double &miny, double &maxy, double &smallestGreaterZero)
|
|
|
|
{
|
2019-06-21 21:46:53 +08:00
|
|
|
//std::cout<<"JKQTPSingleColumnSymbolsGraph::getYMinMax(miny="<<miny<<", maxy="<<maxy<<", smallestGreaterZero="<<smallestGreaterZero<<")\n";
|
2019-01-20 23:15:10 +08:00
|
|
|
if (dataDirection==DataDirection::Y) {
|
2019-06-21 21:46:53 +08:00
|
|
|
bool res= getDataMinMax(dataColumn, miny, maxy, smallestGreaterZero);
|
|
|
|
//std::cout<<"JKQTPSingleColumnSymbolsGraph::getYMinMax(miny="<<miny<<", maxy="<<maxy<<", smallestGreaterZero="<<smallestGreaterZero<<"): dataDirection!=DataDirection::Y -->"<<std::boolalpha<<res<<"\n";
|
|
|
|
return res;
|
2019-01-20 23:15:10 +08:00
|
|
|
} else {
|
|
|
|
miny=position;
|
|
|
|
maxy=position;
|
|
|
|
if (positionScatterStyle!=NoScatter) {
|
|
|
|
miny=position-width/2;
|
|
|
|
maxy=position+width/2;
|
|
|
|
}
|
2019-06-21 21:46:53 +08:00
|
|
|
//std::cout<<"JKQTPSingleColumnSymbolsGraph::getYMinMax(miny="<<miny<<", maxy="<<maxy<<", smallestGreaterZero="<<smallestGreaterZero<<"): dataDirection!=DataDirection::Y -->"<<std::boolalpha<<true<<" position="<<position<<", width="<<width<<"\n";
|
2019-01-20 23:15:10 +08:00
|
|
|
return true;
|
|
|
|
//smallestGreaterZero=qMax(double(0.0), qMin(baseline, baseline+peakHeight));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void JKQTPSingleColumnSymbolsGraph::draw(JKQTPEnhancedPainter &painter)
|
|
|
|
{
|
|
|
|
#ifdef JKQTBP_AUTOTIMER
|
|
|
|
JKQTPAutoOutputTimer jkaaot("JKQTPSingleColumnSymbolsGraph::draw");
|
|
|
|
#endif
|
2019-05-06 01:31:20 +08:00
|
|
|
clearHitTestData();
|
|
|
|
|
2019-01-20 23:15:10 +08:00
|
|
|
if (parent==nullptr) return;
|
|
|
|
JKQTPDatastore* datastore=parent->getDatastore();
|
|
|
|
if (datastore==nullptr) return;
|
|
|
|
if (dataColumn<0) return;
|
|
|
|
|
|
|
|
drawErrorsBefore(painter);
|
2019-02-08 00:24:46 +08:00
|
|
|
{
|
|
|
|
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
|
|
|
|
|
2019-05-13 04:22:48 +08:00
|
|
|
int imax=static_cast<int>(datastore->getRows(static_cast<size_t>(dataColumn)));
|
2019-02-08 00:24:46 +08:00
|
|
|
int imin=0;
|
|
|
|
if (imax<imin) {
|
|
|
|
int h=imin;
|
|
|
|
imin=imax;
|
|
|
|
imax=h;
|
|
|
|
}
|
|
|
|
if (imin<0) imin=0;
|
|
|
|
if (imax<0) imax=0;
|
2019-05-06 01:31:20 +08:00
|
|
|
reserveHitTestData(imax-imin);
|
2019-01-20 23:15:10 +08:00
|
|
|
|
2019-02-08 00:24:46 +08:00
|
|
|
std::random_device rd; // random number generators:
|
|
|
|
std::minstd_rand gen{rd()};
|
|
|
|
gen.seed(seedValue);
|
|
|
|
std::uniform_real_distribution<> dRandomScatter{position-width/2.0, position+width/2.0};
|
2019-01-20 23:15:10 +08:00
|
|
|
|
2019-04-22 19:27:50 +08:00
|
|
|
const double symSize=parent->pt2px(painter, getSymbolSize());
|
2019-06-16 19:27:40 +08:00
|
|
|
QPen p=painter.pen();
|
|
|
|
p.setColor(getSymbolColor());
|
|
|
|
p.setWidthF(qMax(JKQTPlotterDrawingTools::ABS_MIN_LINEWIDTH, parent->pt2px(painter, getSymbolLineWidth())));
|
|
|
|
p.setStyle(Qt::SolidLine);
|
|
|
|
p.setCapStyle(Qt::FlatCap);
|
2019-01-20 23:15:10 +08:00
|
|
|
|
|
|
|
|
2019-02-08 00:24:46 +08:00
|
|
|
QVector<QPointF> plotSymbols; // collects symbol locations e.g. for BeeSwarmScatter-plots
|
|
|
|
plotSymbols.reserve(qMax(100,imax-imin));
|
|
|
|
auto doesCollide=[&plotSymbols,&symSize](double x, double y)->bool {
|
|
|
|
for (auto& p: plotSymbols) {
|
|
|
|
if (fabs(p.x()-x)<symSize && fabs(p.y()-y)<symSize) {
|
|
|
|
return true;
|
|
|
|
}
|
2019-01-20 23:15:10 +08:00
|
|
|
}
|
2019-02-08 00:24:46 +08:00
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (dataDirection==DataDirection::X) {
|
|
|
|
intSortData();
|
|
|
|
for (int iii=imin; iii<imax; iii++) {
|
|
|
|
int i=qBound<int>(imin, getDataIndex(static_cast<int>(iii)), imax);
|
|
|
|
const double xv=datastore->get(dataColumn,i);
|
|
|
|
double yv=position;
|
|
|
|
if (positionScatterStyle==RandomScatter) {
|
|
|
|
yv=dRandomScatter(gen);
|
|
|
|
}
|
|
|
|
const double x=transformX(xv);
|
|
|
|
double y=transformY(yv);
|
|
|
|
if (positionScatterStyle==BeeSwarmScatter) {
|
|
|
|
while (doesCollide(x,y)) {
|
|
|
|
if (i%2==0) {
|
|
|
|
y=y-symSize;
|
|
|
|
} else {
|
|
|
|
y=y+symSize;
|
|
|
|
}
|
2019-01-20 23:15:10 +08:00
|
|
|
}
|
|
|
|
}
|
2019-02-08 00:24:46 +08:00
|
|
|
plotSymbols.append(QPointF(x,y));
|
|
|
|
if (JKQTPIsOKFloat(xv) && JKQTPIsOKFloat(yv)) {
|
2019-06-16 19:27:40 +08:00
|
|
|
if (positionScatterStyle!=RugPlot) {
|
|
|
|
plotStyledSymbol(parent, painter, x, y);
|
|
|
|
} else {
|
2020-08-26 17:00:25 +08:00
|
|
|
painter.save(); auto __finalpaintinner=JKQTPFinally([&painter]() {painter.restore();});
|
2019-06-16 19:27:40 +08:00
|
|
|
painter.setPen(p);
|
|
|
|
painter.drawLine(QLineF(x, y-symSize,x,y+symSize));
|
|
|
|
}
|
2019-05-06 01:31:20 +08:00
|
|
|
addHitTestData(xv, yv,iii, datastore);
|
2019-02-08 00:24:46 +08:00
|
|
|
}
|
2019-01-20 23:15:10 +08:00
|
|
|
}
|
2019-02-08 00:24:46 +08:00
|
|
|
} else {
|
|
|
|
intSortData();
|
|
|
|
for (int iii=imin; iii<imax; iii++) {
|
|
|
|
int i=qBound<int>(imin, getDataIndex(static_cast<int>(iii)), imax);
|
|
|
|
double xv=position;
|
|
|
|
if (positionScatterStyle==RandomScatter) {
|
|
|
|
xv=dRandomScatter(gen);
|
|
|
|
}
|
|
|
|
const double yv=datastore->get(dataColumn,i);
|
|
|
|
double x=transformX(xv);
|
|
|
|
const double y=transformY(yv);
|
|
|
|
if (positionScatterStyle==BeeSwarmScatter) {
|
|
|
|
while (doesCollide(x,y)) {
|
|
|
|
if (i%2==0) {
|
|
|
|
x=x-symSize;
|
|
|
|
} else {
|
|
|
|
x=x+symSize;
|
|
|
|
}
|
2019-01-20 23:15:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
plotSymbols.append(QPointF(x,y));
|
2019-02-08 00:24:46 +08:00
|
|
|
if (JKQTPIsOKFloat(xv) && JKQTPIsOKFloat(yv)) {
|
2019-06-16 19:27:40 +08:00
|
|
|
if (positionScatterStyle!=RugPlot) {
|
|
|
|
plotStyledSymbol(parent, painter, x, y);
|
|
|
|
} else {
|
2020-08-26 17:00:25 +08:00
|
|
|
painter.save(); auto __finalpaintinner=JKQTPFinally([&painter]() {painter.restore();});
|
2019-06-16 19:27:40 +08:00
|
|
|
painter.setPen(p);
|
|
|
|
painter.drawLine(QLineF(x-symSize, y,x+symSize,y));
|
|
|
|
}
|
2019-05-06 01:31:20 +08:00
|
|
|
addHitTestData(xv, yv,iii, datastore);
|
2019-02-08 00:24:46 +08:00
|
|
|
}
|
2019-01-20 23:15:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-02-08 00:24:46 +08:00
|
|
|
}
|
2019-01-20 23:15:10 +08:00
|
|
|
|
|
|
|
drawErrorsAfter(painter);
|
|
|
|
}
|
|
|
|
|
2023-12-19 05:24:59 +08:00
|
|
|
void JKQTPSingleColumnSymbolsGraph::drawKeyMarker(JKQTPEnhancedPainter &painter, const QRectF &rect)
|
2019-01-20 23:15:10 +08:00
|
|
|
{
|
|
|
|
const double minSize=qMin(rect.width(), rect.height());
|
2019-04-22 23:19:52 +08:00
|
|
|
//const double maxSize=qMax(rect.width(), rect.height());
|
2019-04-22 19:27:50 +08:00
|
|
|
double symbolSize=parent->pt2px(painter, this->getSymbolSize());
|
2019-01-20 23:15:10 +08:00
|
|
|
if (symbolSize>minSize*0.9) symbolSize=minSize*0.9;
|
2019-04-22 19:27:50 +08:00
|
|
|
double symbolWidth=parent->pt2px(painter, this->getSymbolLineWidth()*parent->getLineWidthMultiplier());
|
2019-01-20 23:15:10 +08:00
|
|
|
if (symbolWidth>0.3*symbolSize) symbolWidth=0.3*symbolSize;
|
|
|
|
|
2019-02-08 00:24:46 +08:00
|
|
|
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
|
2019-04-22 19:27:50 +08:00
|
|
|
QPen p=getSymbolPen(painter, parent);
|
2019-01-20 23:15:10 +08:00
|
|
|
painter.setPen(p);
|
2019-06-16 19:27:40 +08:00
|
|
|
if (positionScatterStyle!=RugPlot) {
|
2022-09-02 20:19:47 +08:00
|
|
|
JKQTPPlotSymbol(painter, rect.left()+rect.width()/2.0, rect.top()+rect.height()/2.0, getSymbolType(), symbolSize, symbolWidth, getKeyLabelColor(), getSymbolFillColor(),getSymbolFont());
|
2019-06-16 19:27:40 +08:00
|
|
|
} else {
|
|
|
|
painter.translate(rect.center());
|
|
|
|
if (dataDirection==DataDirection::X) {
|
|
|
|
painter.rotate(90);
|
|
|
|
}
|
|
|
|
painter.drawLine(QLineF(-rect.width()/4.0, rect.height()*(-0.4), rect.width()/4.0, rect.height()*(-0.4)));
|
|
|
|
painter.drawLine(QLineF(-rect.width()/4.0, rect.height()*(-0.2), rect.width()/4.0, rect.height()*(-0.2)));
|
|
|
|
painter.drawLine(QLineF(-rect.width()/4.0, rect.height()*0.0, rect.width()/4.0, rect.height()*0.0));
|
|
|
|
painter.drawLine(QLineF(-rect.width()/4.0, rect.height()*0.05, rect.width()/4.0, rect.height()*0.05));
|
|
|
|
painter.drawLine(QLineF(-rect.width()/4.0, rect.height()*0.15, rect.width()/4.0, rect.height()*0.15));
|
|
|
|
painter.drawLine(QLineF(-rect.width()/4.0, rect.height()*0.3, rect.width()/4.0, rect.height()*0.3));
|
|
|
|
painter.drawLine(QLineF(-rect.width()/4.0, rect.height()*0.45, rect.width()/4.0, rect.height()*0.45));
|
|
|
|
}
|
2019-04-22 19:27:50 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
QColor JKQTPSingleColumnSymbolsGraph::getKeyLabelColor() const
|
|
|
|
{
|
|
|
|
return getSymbolColor();
|
|
|
|
}
|
2019-02-08 00:24:46 +08:00
|
|
|
|
2019-04-22 19:27:50 +08:00
|
|
|
void JKQTPSingleColumnSymbolsGraph::setColor(QColor col)
|
|
|
|
{
|
|
|
|
setSymbolColor(col);
|
2020-09-26 21:58:58 +08:00
|
|
|
setSymbolFillColor(JKQTPGetDerivedColor(parent->getCurrentPlotterStyle().graphsStyle.defaultGraphStyle.fillColorDerivationMode, col));
|
2019-04-22 19:27:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void JKQTPSingleColumnSymbolsGraph::setPosition(double __value)
|
|
|
|
{
|
|
|
|
this->position = __value;
|
2019-01-20 23:15:10 +08:00
|
|
|
}
|
|
|
|
|
2019-04-22 19:27:50 +08:00
|
|
|
double JKQTPSingleColumnSymbolsGraph::getPosition() const
|
|
|
|
{
|
|
|
|
return this->position;
|
|
|
|
}
|
2019-01-20 23:15:10 +08:00
|
|
|
|
2019-04-22 19:27:50 +08:00
|
|
|
void JKQTPSingleColumnSymbolsGraph::setWidth(double __value)
|
|
|
|
{
|
|
|
|
width=__value;
|
|
|
|
}
|
|
|
|
|
|
|
|
double JKQTPSingleColumnSymbolsGraph::getWidth() const
|
|
|
|
{
|
|
|
|
return width;
|
|
|
|
}
|
|
|
|
|
|
|
|
void JKQTPSingleColumnSymbolsGraph::setPositionScatterStyle(JKQTPSingleColumnSymbolsGraph::ScatterStyle __value)
|
|
|
|
{
|
|
|
|
this->positionScatterStyle = __value;
|
|
|
|
}
|
|
|
|
|
|
|
|
JKQTPSingleColumnSymbolsGraph::ScatterStyle JKQTPSingleColumnSymbolsGraph::getPositionScatterStyle() const
|
|
|
|
{
|
|
|
|
return this->positionScatterStyle;
|
|
|
|
}
|
2019-01-20 23:15:10 +08:00
|
|
|
|
2019-04-22 19:27:50 +08:00
|
|
|
void JKQTPSingleColumnSymbolsGraph::setSeedValue(unsigned int val) {
|
|
|
|
seedValue=val;
|
2019-01-20 23:15:10 +08:00
|
|
|
}
|
|
|
|
|
2019-04-22 19:27:50 +08:00
|
|
|
unsigned int JKQTPSingleColumnSymbolsGraph::getSeedValue() const {
|
|
|
|
return seedValue;
|
2019-01-20 23:15:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|