///////////////////////////////////////////////////////////////////////////////////////////////////
// dqtplotdialog.cpp
// Пример диалогового окна для рисования графика и полутонового изображения выборки
// при помощи QCustomPlot (http://www.qcustomplot.com/index.php/introduction).
//
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// Университет ИТМО
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "dqtplotdialog.h"
#include "qcustomplot.h"
#include "sample_complex.h"
using namespace std;
//-------------------------------------------------------------------------------------------------
// конструктор
DQtPlotDialog::DQtPlotDialog(QWidget* parent, Qt::WindowFlags f)
: QDialog(parent, f)
{
m_ui.setupUi(this);
// создаем связь между сигналом(событием) от объекта и функцией-обработкой события
connect(m_ui.m_qPB_Draw, SIGNAL(clicked()), this, SLOT(onBtnDraw()));
}
//-------------------------------------------------------------------------------------------------
DQtPlotDialog::~DQtPlotDialog()
{
}
//-------------------------------------------------------------------------------------------------
// перехватывается событие Resize для диалогового окна
// (это нужно чтобы сохранить одинаковоые масштабы по осям Х и Y для полутонового отображения)
void DQtPlotDialog::resizeEvent(QResizeEvent *event)
{
// на случай, если размер виджета не квадратный - задаем одинаковый масштаб по ося X и Y
RescaleCustomPlot(m_ui.m_qW_Grayscale);
// отрисовка
m_ui.m_qW_Grayscale->replot();
}
//-------------------------------------------------------------------------------------------------
// отклик на нажатие кнопки "нарисовать"
void DQtPlotDialog::onBtnDraw()
{
// сoздаем выборку и заполняем ее какими-то значениями
int dim=1024; // размер выборки
double dx=0.01; // шаг по x
SampleComplex sample(dim);
for(int i=0; i<dim; ++i)
{
double x = sample.CalcCoord(i, dx);
double value_x=1.;
if(x!=0)
value_x=sin(PI*x)/(PI*x);
for(int j=0; j<dim; ++j)
{
double y = sample.CalcCoord(j, dx);
double value_y=1.;
if(y!=0)
value_y=sin(2*PI*y)/(2*PI*y);
sample(i,j)=value_x*value_y;
}
}
// рисуем график
DrawGraph(sample, m_ui.m_qW_Graph, dx, "x", "Sinc(x)*Sinc(2*y)");
// рисуем полутоновое отображение
DrawGrayscale(sample, m_ui.m_qW_Grayscale, dx, "x", "y");
}
//-------------------------------------------------------------------------------------------------
// рисование графика центральных сечений по Х и Y для двумерного массива
// sample - массив комплексных чисел, отображается только вещественная часть
// qGraph - имя виджета, на котором рисовать
// параметры графика:
// dx - шаг по оси Х
// sXName, sYName - подписи к осям Х, Y
void DQtPlotDialog::DrawGraph(SampleComplex& sample, QCustomPlot *qGraph, double dx, QString sXName, QString sYName)
{
// размер выборки
int dim = sample.GetSize();
// очищаем предыдущий нарисованный график
qGraph->clearGraphs();
// рисование легенды
qGraph->legend->setVisible(true);
// параметры графика сечения по Х
qGraph->addGraph();
qGraph->graph(0)->setPen(QPen(Qt::red));
qGraph->graph(0)->setName("Section X");
// параметры графика сечения по Y
qGraph->addGraph();
qGraph->graph(1)->setPen(QPen(Qt::blue));
qGraph->graph(1)->setName("Section Y");
// параметры оси Х
qGraph->xAxis->setLabel(sXName);
// максимальное и минимальное значение по оси
double min = sample.CalcCoord(0, dx);
double max = sample.CalcCoord(dim, dx);
qGraph->xAxis->setRange(min, max);
// можно задать цену деления по оси (можно оставить автоматическое опеределение)
qGraph->xAxis->setAutoTickStep(false);
qGraph->xAxis->setTickStep(1.);
// задаем значения графиков
min=max=0;
for (int i = 0; i < sample.GetSize(); i++)
{
// координата точки по Х
double x = sample.CalcCoord(i, dx);
// значание в сечении по Х
double value_x = sample(i, dim/2).real();
qGraph->graph(0)->addData(x, value_x);
// значание в сечении по Y
double value_y = sample(dim/2, i).real();
qGraph->graph(1)->addData(x, value_y);
// вычисляем min значение по оси Y
if(min>value_x)
min=value_x;
if(min>value_y)
min=value_y;
}
// параметры оси Y
qGraph->yAxis->setLabel(sYName);
// максимальное и минимальное значение по оси
// (min вычислен, max предполагаем равным 1 (для интенсивности)
qGraph->yAxis->setRange(min, 1.);
// можно задать цену деления по оси (можно оставить автоматическое опеределение)
qGraph->yAxis->setAutoTickStep(false);
qGraph->yAxis->setTickStep(0.2);
// нулевое значение по осям X и Y рисуем толстой линией
QPen qAxisPen;
qAxisPen.setWidth(2.);
qGraph->xAxis->grid()->setZeroLinePen(qAxisPen);
qGraph->yAxis->grid()->setZeroLinePen(qAxisPen);
// рисуем сами графики
qGraph->replot();
}
//-------------------------------------------------------------------------------------------------
// рисование полутонового отображения двумерного массива
// sample - массив комплексных чисел, отображается только вещественная часть
// qGraph - имя виджета, на котором рисовать
// параметры графика:
// dx - шаг по оси Х
// sXName, sYName - подписи к осям Х, Y
void DQtPlotDialog::DrawGrayscale(SampleComplex& sample, QCustomPlot *qGrayscale, double dx, QString sXName, QString sYName)
{
// размер выборки
int dim = sample.GetSize();
// очищаем предыдущий нарисованный график
qGrayscale->clearPlottables();
// создаем карту уровней для отрисовки и задаем размерность
QCPColorMap *qMap = new QCPColorMap(qGrayscale->xAxis, qGrayscale->yAxis);
qMap->data()->setSize(dim, dim);
// задаем максимальное и минимальное значение по осям
double min = sample.CalcCoord(0, dx);
double max = sample.CalcCoord(dim, dx);
qMap->data()->setRange(QCPRange(min, max), QCPRange(min, max));
// заполняем карту уровней значениями из массива sample
for (int i = 0; i < sample.GetSize(); i++)
{
for (int j = 0; j < sample.GetSize(); j++)
{
qMap->data()->setCell(i, j, sample(i, j).real());
}
}
// устанавливаем тип карты уровней - полутоновое отображение
qMap->setGradient(QCPColorGradient::gpGrayscale);
qMap->rescaleDataRange();
// параметры оси X
qGrayscale->xAxis->setLabel(sXName);
// можно задать цену деления по оси (можно оставить автоматическое опеределение)
qGrayscale->xAxis->setAutoTickStep(false);
qGrayscale->xAxis->setTickStep(1);
// параметры оси X
qGrayscale->yAxis->setLabel(sYName);
// можно задать цену деления по оси (можно оставить автоматическое опеределение)
qGrayscale->yAxis->setAutoTickStep(false);
qGrayscale->yAxis->setTickStep(1);
// задаем заполненную карту уровней в QCustomPlot для отрисовки
qGrayscale->addPlottable(qMap);
// пересчитываем оси QCustomPlot под заданную карту уровней
qGrayscale->rescaleAxes();
// на случай, если размер виджета не квадратный - задаем одинаковый масштаб по осям X и Y
RescaleCustomPlot(qGrayscale);
// отрисовка
qGrayscale->replot();
}
//-------------------------------------------------------------------------------------------------
// функция, задающая одинаковый масштаб по осям X и Y
// должна вызываться при первоначальной отрисовке и при масштабировании
void DQtPlotDialog::RescaleCustomPlot(QCustomPlot *qPlot)
{
// определяем ширину и высоту области для отрисовки
QSize r=qPlot->axisRect()->size();
// если ширина больше высоты - меняем ось X
if(r.width() > r.height())
{
qPlot->xAxis->setScaleRatio(qPlot->yAxis, 1.);
}
// если ширина меньше высоты - меняем ось X
else if(r.width() < r.height())
{
qPlot->yAxis->setScaleRatio(qPlot->xAxis, 1.);
}
}
//-------------------------------------------------------------------------------------------------