/*
Copyright (c) 2008-2020 Jan W. Krieger & Sebastian Isbaner (contour plot)
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 .
*/
#include "jkqtplotter/graphs/jkqtpcontour.h"
#include "jkqtplotter/jkqtpbaseplotter.h"
#include "jkqtplotter/jkqtpimagetools.h"
#include "jkqtplotter/jkqtptools.h"
#include "jkqtcommon/jkqtpenhancedpainter.h"
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtcommon/jkqtpgeometrytools.h"
#include "jkqtcommon/jkqttools.h"
#include
#include
#include
#include
#include
#include
# include
JKQTPContourPlot::JKQTPContourPlot(JKQTBasePlotter *parent) :
JKQTPMathImage(parent)
{
ignoreOnPlane=false;
contourColoringMode=ColorContoursFromPaletteByValue;
relativeLevels=false;
initLineStyle(parent, parentPlotStyle, JKQTPPlotStyleType::Default);
}
JKQTPContourPlot::JKQTPContourPlot(JKQTPlotter *parent) :
JKQTPContourPlot(parent->getPlotter())
{
}
void JKQTPContourPlot::draw(JKQTPEnhancedPainter &painter)
{
//qDebug()<<"JKQTPContourPlot::draw";
ensureImageData();
int numberOfLevels=contourLevels.size();
if (numberOfLevels<=0) return;
int64_t colChecksum=-1;
if (data && Nx*Ny>0) {
colChecksum=static_cast(jkqtp_checksum(reinterpret_cast(data), static_cast(Nx)*static_cast(Ny)* static_cast(getSampleSize()/sizeof(char))));
}
/*if (parent && parent->getDatastore() && imageColumn>=0) {
colChecksum=static_cast(parent->getDatastore()->getColumnChecksum(imageColumn));
}*/
if(contourLinesCache.isEmpty() || (contourLinesCachedForChecksum!=colChecksum) || (contourLinesCachedForChecksum<0)) { // contour lines are only calculated once
QList > lines;
lines.reserve(contourLevels.size());
for(int i =0; i (0));
}
this->calcContourLines(lines);
contourLinesCache.clear();
contourLinesCachedForChecksum=colChecksum;
for (const QVector& l: lines) {
contourLinesCache.push_back(JKQTPUnifyLinesToPolygons(l, qMin(getWidth()/static_cast(getNx()),getHeight()/static_cast(getNy()))/4.0));
}
}
// draw lines
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
QPen p=getLinePen(painter, parent);
painter.setPen(p);
// calculate an image with one pixel per contour level and fill it with the appropriate colors
QImage colorLevels = getPaletteImage(palette,numberOfLevels); // (contourColoringMode==ContourColoringMode::ColorContoursFromPalette)
if (contourColoringMode==ContourColoringMode::SingleColorContours) {
for (int i=0; i(0, (internalDataMax-contourLevels.value(i, 0))*static_cast(colorDataLevels.width())/(internalDataMax-internalDataMin), colorDataLevels.width()-1),0));
}
}
// set override colors
for (int i=0; i contourLinesTransformedSingleLevel;
p.setColor(QColor(colorLevels.pixel(i,0)));
painter.setPen(p);
// transform into plot coordinates
for(auto polygon =contourLinesCache.at(i).begin(); polygon!=contourLinesCache.at(i).end();++polygon ) {
contourLinesTransformedSingleLevel.push_back(QPolygonF());
for (auto& poly: *polygon) {
contourLinesTransformedSingleLevel.last().append(transform(x+poly.x()/double(Nx-1)*width, y+poly.y()/double(Ny-1)*height));
}
//qDebug()<(nLevels+1);
for(int i=1; i<=nLevels; ++i) {
contourLevels.append(min + i*delta);
}
relativeLevels=false;
clearCachedContours();
}
void JKQTPContourPlot::createContourLevelsLog(int nLevels, int m)
{
ensureImageData();
clearContourLevel();
if (!data) return;
if (nLevels<1) return;
double min,max;
getDataMinMax(min,max);
if(min<=0) min=1; // FIXME get smallest number greater zero
int S=floor((log10(max)-log10(min))/log10(m));
if(S<2) S=1;
int P = floor(static_cast(nLevels)/static_cast(S));
if(P<1) P=1;
double delta=min;
contourLevels.append(2*delta);
for (long s=0; signoreOnPlane = __value;
clearCachedContours();
}
bool JKQTPContourPlot::getIgnoreOnPlane() const
{
return this->ignoreOnPlane;
}
int JKQTPContourPlot::getNumberOfLevels() const
{
return this->contourLevels.size();
}
void JKQTPContourPlot::setContourColoringMode(ContourColoringMode __value)
{
this->contourColoringMode = __value;
}
JKQTPContourPlot::ContourColoringMode JKQTPContourPlot::getContourColoringMode() const
{
return this->contourColoringMode;
}
QVector JKQTPContourPlot::getContourLevels() const
{
return this->contourLevels;
}
void JKQTPContourPlot::setRelativeLevels(bool __value)
{
this->relativeLevels = __value;
}
bool JKQTPContourPlot::getRelativeLevels() const
{
return this->relativeLevels;
}
void JKQTPContourPlot::addContourLevel(double level)
{
contourLevels.append(level);
std::sort(contourLevels.begin(), contourLevels.end());
clearCachedContours();
}
void JKQTPContourPlot::addContourLevel(double level, QColor overrideColor)
{
addContourLevel(level);
setOverrideColor(level, overrideColor);
}
void JKQTPContourPlot::setOverrideColor(double level, QColor overrideColor)
{
contourOverrideColor[level]=overrideColor;
}
QColor JKQTPContourPlot::getOverrideColor(int level) const
{
if (level>=0 && level=0 && level=0 && level > &ContourLines)
{
#ifdef JKQTBP_AUTOTIMER
JKQTPAutoOutputTimer jkaat(QString("JKQTPContourPlot::calcContourLines()"));
#else
//qDebug()<<"JKQTPContourPlot::calcContourLines()";
#endif
double scale=1; ///< scale of the contour levels;
if(relativeLevels) {
double min;
double max;
getDataMinMax(min,max);
scale=1/(max-min);
}
enum Position
{
// the positions of points of one box
// vertex 1 +-------------------+ vertex 2
// | \ / |
// | \ m=3 / |
// | \ / |
// | \ / |
// | m=2 X m=2 | the center is vertex 0
// | / \ |
// | / \ |
// | / m=1 \ |
// | / \ |
// vertex 4 +-------------------+ vertex 3
Center=0,
TopLeft=1,
TopRight=2,
BottomRight=3,
BottomLeft=4,
NumPositions=5
};
for ( int yp = 0; yp < (int64_t)getNy() - 1; ++yp ) { // go through image (pixel coordinates) in row major order
QVector vertices(NumPositions);
for ( int xp = 0; xp < (int64_t)getNx() - 1; ++xp ) {
if ( xp == 0 )
{
vertices[TopRight].setX(xp); // will be used for TopLeft later
vertices[TopRight].setY(yp);
vertices[TopRight].setZ(
getPixelValue( vertices[TopRight].x(), vertices[TopRight].y())*scale
);
vertices[BottomRight].setX(xp);
vertices[BottomRight].setY(yp+1);
vertices[BottomRight].setZ(
getPixelValue(vertices[BottomRight].x(), vertices[BottomRight].y())*scale
);
}
vertices[TopLeft] = vertices[TopRight]; // use right vertices of the last box as new left vertices
vertices[BottomLeft] = vertices[BottomRight];
vertices[TopRight].setX(xp + 1);
vertices[TopRight].setY(yp); // <----
vertices[TopRight].setZ(
getPixelValue(vertices[TopRight].x(), vertices[TopRight].y())*scale
);
vertices[BottomRight].setX(xp + 1);
vertices[BottomRight].setY(yp + 1);
vertices[BottomRight].setZ(
getPixelValue(vertices[BottomRight].x(), vertices[BottomRight].y())*scale
);
double zMin = vertices[TopLeft].z();
double zMax = zMin;
double zSum = zMin;
for ( int i = TopRight; i <= BottomLeft; ++i ) {
const double z = vertices[i].z();
zSum += z;
if ( z < zMin )
zMin = z;
if ( z > zMax )
zMax = z;
}
if ( zMax >= contourLevels.first() && zMin <= contourLevels.last() ) {
vertices[Center].setX(xp + 0.5); // pseudo pixel coordinates
vertices[Center].setY(yp + 0.5);
vertices[Center].setZ(0.25 * zSum);
for (int levelIdx=0; levelIdx= zMin && contourLevels.at(levelIdx) <= zMax ) {
QLineF line;
QVector triangle(3);
/* triangle[1]
X
/ \
/ \
/ m \
/ \
triangle[2] +-------------------+ triangle[0]
*/
for (int m = TopLeft; m < NumPositions; m++) { // construct triangles
triangle[0] = vertices[m];
triangle[1] = vertices[Center];
triangle[2] = vertices[(m!=BottomLeft)?(m + 1):TopLeft];
const bool intersects =intersect(line, triangle.at(0),triangle.at(1),triangle.at(2),
contourLevels.at(levelIdx));
if ( intersects ) {
ContourLines[levelIdx]<datatype=JKQTPMathImageDataType::DoubleArray;
}
JKQTPColumnContourPlot::JKQTPColumnContourPlot(JKQTPlotter *parent):
JKQTPColumnContourPlot(parent->getPlotter())
{
}
void JKQTPColumnContourPlot::setImageColumn(int __value)
{
this->imageColumn = __value;
if (parent && __value >= 0 && parent->getDatastore()) {
setNx(parent->getDatastore()->getColumnImageWidth(__value));
setNy(parent->getDatastore()->getColumnImageHeight(__value));
}
}
void JKQTPColumnContourPlot::setImageColumn(size_t __value)
{
setImageColumn(static_cast(__value));
}
int JKQTPColumnContourPlot::getImageColumn() const
{
return this->imageColumn;
}
bool JKQTPColumnContourPlot::usesColumn(int c) const
{
return (c==imageColumn);
}
void JKQTPColumnContourPlot::ensureImageData()
{
if (this->Nx==0 || imageColumn<0 || !parent->getDatastore()->getColumnPointer(imageColumn,0)) {
this->Ny=0;
this->data=nullptr;
this->datatype=JKQTPMathImageDataType::DoubleArray;
} else {
this->datatype=JKQTPMathImageDataType::DoubleArray;
this->data=parent->getDatastore()->getColumnPointer(imageColumn,0);
this->Ny=static_cast(parent->getDatastore()->getRows(imageColumn)/this->Nx);
}
}