Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

 

 

(C++) QtSurfacePlotWidget

 

QtQt CreatorLubuntu

 

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 (C) 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 (C) 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

 

 

 

 

 

Go back to Richel Bilderbeek's C++ page.

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict

This page has been created by the tool CodeToHtml