(C++) QtSurfacePlotWidget
January 25, 2018 · View on GitHub
(C++) QtSurfacePlotWidget



QtSurfacePlotWidget is a Qt class for displaying a surface plot.
Technical facts
./CppQtSurfacePlotWidget/CppQtSurfacePlotWidget.pri
INCLUDEPATH += \ ../../Classes/CppQtSurfacePlotWidget SOURCES += \ ../../Classes/CppQtSurfacePlotWidget/qtsurfaceplotwidget.cpp HEADERS += \ ../../Classes/CppQtSurfacePlotWidget/qtsurfaceplotwidget.h OTHER_FILES += \ ../../Classes/CppQtSurfacePlotWidget/Licence.txt
./CppQtSurfacePlotWidget/qtsurfaceplotwidget.h
//--------------------------------------------------------------------------- /* QtSurfacePlotWidget, Qt widget for displaying a surface plot Copyright 2012-2015 Richel Bilderbeek This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ //--------------------------------------------------------------------------- //From http://www.richelbilderbeek.nl/CppQtSurfacePlotWidget.htm //--------------------------------------------------------------------------- #ifndef QTSURFACEPLOTWIDGET_H #define QTSURFACEPLOTWIDGET_H #include <string> #include <vector> #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #pragma GCC diagnostic ignored "-Wunused-local-typedefs" #include <boost/shared_ptr.hpp> #include <QWidget> #pragma GCC diagnostic pop namespace ribi { struct QtSurfacePlotWidget : public QWidget { Q_OBJECT public: explicit QtSurfacePlotWidget(QWidget *parent = 0); ///A functor to do the plotting struct Function { virtual ~Function() noexcept {} virtual double operator()(const double x, const double y) const noexcept = 0; }; ///Plot a function for a certain range void Plot( const Function& f, const double x_min, const double x_max, const double y_min, const double y_max ) noexcept; ///Sets the image of the surface plot to the values of the vector ///The doubles can be in any range void SetSurfaceGrey(const std::vector<std::vector<double>>& surface); //Sets the image of the surface plot to the values of the vector //Assumes that the chars are in the range [0,255] (a char's range) //If the chars are in a shorter range, they will NOT be rescaled to [0,255] void SetSurfaceGrey(const std::vector<std::vector<unsigned char>>& surface); ///Obtain the version of this class static std::string GetVersion(); ///Obtain the version history of this class static std::vector<std::string> GetVersionHistory(); protected: void paintEvent(QPaintEvent *) override; void resizeEvent(QResizeEvent *) override; private: std::vector<std::vector<unsigned char>> m_surface; ///Rescale calculates a value between old_min and old_max its relative place and transforms ///this relative position to a new_min and new_max ///For example: for the old range [1,5], the value 2 is at 25% of this range. Transforming this ///to the new range range [0,100] results in a 25 static double Rescale( const double value, const double old_min, const double old_max, const double new_min, const double new_max ) noexcept; #ifndef NDEBUG static void Test() noexcept; #endif }; } //~namespace ribi #endif //QTSURFACEPLOTWIDGET_H
./CppQtSurfacePlotWidget/qtsurfaceplotwidget.cpp
//--------------------------------------------------------------------------- /* QtSurfacePlotWidget, Qt widget for displaying a surface plot Copyright 2012-2015 Richel Bilderbeek This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ //--------------------------------------------------------------------------- //From http://www.richelbilderbeek.nl/CppQtSurfacePlotWidget.htm //--------------------------------------------------------------------------- #include <algorithm> #include <cassert> #include <vector> #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #pragma GCC diagnostic ignored "-Wunused-local-typedefs" #pragma GCC diagnostic ignored "-Wunused-but-set-parameter" #include <QPainter> #include "testtimer.h" #include "qtsurfaceplotwidget.h" #include "trace.h" #pragma GCC diagnostic pop ribi::QtSurfacePlotWidget::QtSurfacePlotWidget(QWidget *parent) : QWidget(parent), m_surface{} { #ifndef NDEBUG Test(); #endif std::vector<std::vector<unsigned char>> v(128,std::vector<unsigned char>(128)); for(int y=0; y!=128; ++y) { for (int x=0; x!=128; ++x) { v[y][x] = x + y; } } SetSurfaceGrey(v); } std::string ribi::QtSurfacePlotWidget::GetVersion() { return "1.1"; } std::vector<std::string> ribi::QtSurfacePlotWidget::GetVersionHistory() { return { "2012-07-14: version 1.0: initial version", "2014-07-08: version 1.1: added use of Function functor" }; } void ribi::QtSurfacePlotWidget::paintEvent(QPaintEvent *) { QPainter painter(this); assert(!m_surface.empty()); const int maxy = m_surface.size(); assert(maxy > 0); const int maxx = m_surface[0].size(); assert(maxx > 0); QImage image (maxx,maxy,QImage::Format::Format_RGB32); for (int y=0; y!=maxy; ++y) { std::vector<unsigned char>& line = m_surface[y]; for (int x=0; x!=maxx; ++x) { assert(x < static_cast<int>(line.size())); const unsigned char grey = line[x]; image.setPixel(x,y,qRgb(grey,grey,grey)); } } painter.drawPixmap(0,0,this->width(),this->height(),QPixmap::fromImage(image)); } void ribi::QtSurfacePlotWidget::Plot( const Function& f, const double x_min, const double x_max, const double y_min, const double y_max ) noexcept { assert(x_min < x_max); assert(y_min < y_max); //Evaluate the function in a 2D std::vector const int n_rows = height(); const int n_cols = width(); std::vector<std::vector<double>> v(n_rows,std::vector<double>(n_cols,0.0)); const double n_rows_d = static_cast<double>(n_rows); const double n_cols_d = static_cast<double>(n_cols); for (int y = 0; y!=n_rows; ++y) { const double yD = static_cast<double>(y); const double y_scaled = Rescale(yD,0.0,n_rows_d,y_min,y_max); for (int x = 0; x!=n_cols; ++x) { const double xD = static_cast<double>(x); const double x_scaled = Rescale(xD,0.0,n_cols_d,x_min,x_max); const double z = f(x_scaled,y_scaled); v[y][x] = z; } } //Plot the 2D std::vector SetSurfaceGrey(v); } double ribi::QtSurfacePlotWidget::Rescale( const double value, const double old_min, const double old_max, const double new_min, const double new_max) noexcept { assert(value >= old_min); assert(value <= old_max); const double old_distance = old_max - old_min; //At which relative distance is value on old_min to old_max ? const double distance = (value - old_min) / old_distance; assert(distance >= 0.0); assert(distance <= 1.0); const double new_distance = new_max - new_min; const double new_value = new_min + (distance * new_distance); assert(new_value >= new_min); assert(new_value <= new_max); return new_value; } void ribi::QtSurfacePlotWidget::resizeEvent(QResizeEvent *) { this->repaint(); } void ribi::QtSurfacePlotWidget::SetSurfaceGrey(const std::vector<std::vector<double>>& v) { //Get the size const int maxx = v[0].size(); const int maxy = v.size(); //Minimum and maximum are not given, so these need to be calculated double min_val = *(std::min_element(v[0].begin(),v[0].end())); double max_val = *(std::max_element(v[0].begin(),v[0].end())); for (int y=1; y!=maxy; ++y) //1, because 0 gets know the lines above { const double local_min_val = *(std::min_element(v[y].begin(),v[y].end())); const double local_max_val = *(std::max_element(v[y].begin(),v[y].end())); if (local_min_val < min_val) min_val = local_min_val; if (local_max_val > max_val) max_val = local_max_val; } //Create a black surface of type unsigned char std::vector<std::vector<unsigned char>> w(maxy,std::vector<unsigned char>(maxx,0)); if (min_val == max_val) { //Keep black surface in w black } else { for (int y=0; y!=maxy; ++y) { const std::vector<double>& line_in = v[y]; std::vector<unsigned char>& line_out = w[y]; for (int x=0; x!=maxx; ++x) { assert(min_val != max_val); assert(min_val < max_val); assert(x < static_cast<int>(line_in.size())); const double grey_double = (line_in[x] - min_val) / (max_val - min_val); assert(grey_double >= 0.0 && grey_double <= 1.0); const unsigned char grey = grey_double * 255.0; assert(x < static_cast<int>(line_out.size())); line_out[x] = grey; } } } SetSurfaceGrey(w); } void ribi::QtSurfacePlotWidget::SetSurfaceGrey(const std::vector<std::vector<unsigned char>>& surface) { m_surface = surface; this->repaint(); } #ifndef NDEBUG void ribi::QtSurfacePlotWidget::Test() noexcept { { static bool is_tested{false}; if (is_tested) return; is_tested = true; } const TestTimer test_timer(__func__,__FILE__,1.0); assert(Rescale(2.0,1.0,5.0,0.0,100.0) >= 24.9999 && Rescale(2.0,1.0,5.0,0.0,100.0) < 25.0001); } #endif