diff --git a/JKQtPlotterBuildAllExamples.pro b/JKQtPlotterBuildAllExamples.pro index deaa934880..b293c056e2 100644 --- a/JKQtPlotterBuildAllExamples.pro +++ b/JKQtPlotterBuildAllExamples.pro @@ -52,41 +52,42 @@ defineTest(addSimpleTest) { export (SUBDIRS) } +addSimpleTest(advplotstyling) addSimpleTest(barchart) +addSimpleTest(boxplot) +addSimpleTest(contourplot) +addSimpleTest(datastore) +addSimpleTest(datastore_groupedstat) +addSimpleTest(datastore_iterators) +addSimpleTest(datastore_regression) +addSimpleTest(datastore_statistics) +addSimpleTest(datastore_statistics_2d) addSimpleTest(dateaxes) addSimpleTest(errorbarstyles) +addSimpleTest(evalcurve) +addSimpleTest(filledgraphs) +addSimpleTest(functionplot) +addSimpleTest(geo_arrows) +addSimpleTest(geo_simple) +addSimpleTest(geometric) addSimpleTest(imageplot) addSimpleTest(imageplot_modifier) +addSimpleTest(imageplot_nodatastore) +addSimpleTest(impulsesplot) addSimpleTest(logaxes) +addSimpleTest(mandelbrot) +addSimpleTest(parametriccurve) +addSimpleTest(paramscatterplot) +addSimpleTest(paramscatterplot_image) +addSimpleTest(parsedfunctionplot) +addSimpleTest(rgbimageplot) +addSimpleTest(rgbimageplot_qt) +addSimpleTest(speed) addSimpleTest(stackedbars) addSimpleTest(symbols_and_errors) addSimpleTest(symbols_and_styles) -addSimpleTest(filledgraphs) -addSimpleTest(speed) -addSimpleTest(rgbimageplot) -addSimpleTest(rgbimageplot_qt) -addSimpleTest(impulsesplot) -addSimpleTest(paramscatterplot) -addSimpleTest(paramscatterplot_image) -addSimpleTest(parametriccurve) -addSimpleTest(parsedfunctionplot) -addSimpleTest(functionplot) -addSimpleTest(geometric) -addSimpleTest(geo_simple) -addSimpleTest(geo_arrows) addSimpleTest(ui) -addSimpleTest(boxplot) -addSimpleTest(advplotstyling) -addSimpleTest(imageplot_nodatastore) -addSimpleTest(datastore) -addSimpleTest(datastore_iterators) -addSimpleTest(datastore_statistics) -addSimpleTest(datastore_statistics_2d) -addSimpleTest(datastore_regression) -addSimpleTest(datastore_groupedstat) -addSimpleTest(contourplot) addSimpleTest(violinplot) -addSimpleTest(evalcurve) #addSimpleTest(rgbimageplot_opencv) #addSimpleTest(imageplot_opencv) diff --git a/doc/dox/examples_and_tutorials.dox b/doc/dox/examples_and_tutorials.dox index 27693b23ea..86646e11d3 100644 --- a/doc/dox/examples_and_tutorials.dox +++ b/doc/dox/examples_and_tutorials.dox @@ -212,6 +212,9 @@ All test-projects are Qt-projects that use qmake to build. You can load them int \image html test_distributionplot_small.png \subpage JKQTPlotterDistributionPlot Combines several different graphs to draw random values, their distribution and some statistical properties + \image html mandelbrot_small.png + \subpage JKQTPlotterMandelbrot + diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e2e4670dbb..e2948a0910 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -12,7 +12,6 @@ add_subdirectory(jkqtmathtext_test) add_subdirectory(jkqtplot_test) -add_subdirectory(geo_arrows) add_subdirectory(advplotstyling) add_subdirectory(barchart) add_subdirectory(boxplot) @@ -26,26 +25,29 @@ add_subdirectory(datastore_statistics_2d) add_subdirectory(dateaxes) add_subdirectory(distributionplot) add_subdirectory(errorbarstyles) +add_subdirectory(evalcurve) add_subdirectory(filledgraphs) add_subdirectory(functionplot) -add_subdirectory(geometric) +add_subdirectory(geo_arrows) add_subdirectory(geo_simple) +add_subdirectory(geometric) add_subdirectory(imageplot) -add_subdirectory(imageplot_userpal) +add_subdirectory(imageplot_cimg) add_subdirectory(imageplot_modifier) add_subdirectory(imageplot_nodatastore) add_subdirectory(imageplot_opencv) -add_subdirectory(imageplot_cimg) +add_subdirectory(imageplot_userpal) add_subdirectory(impulsesplot) add_subdirectory(logaxes) +add_subdirectory(mandelbrot) add_subdirectory(multiplot) add_subdirectory(parametriccurve) add_subdirectory(paramscatterplot) add_subdirectory(paramscatterplot_image) add_subdirectory(parsedfunctionplot) add_subdirectory(rgbimageplot) -add_subdirectory(rgbimageplot_opencv) add_subdirectory(rgbimageplot_cimg) +add_subdirectory(rgbimageplot_opencv) add_subdirectory(rgbimageplot_qt) add_subdirectory(simpletest) add_subdirectory(speed) @@ -58,6 +60,5 @@ add_subdirectory(symbols_and_styles) add_subdirectory(ui) add_subdirectory(user_interaction) add_subdirectory(violinplot) -add_subdirectory(evalcurve) diff --git a/examples/README.md b/examples/README.md index 94a38933a3..595053278b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -70,6 +70,7 @@ All test-projects are Qt-projects that use qmake to build. You can load them int | [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_test_user_interaction_small.gif)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/user_interaction) | [User Interaction](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/user_interaction) | different possibilities of user-interaction in JKQtPlotter | + ## Data Management & Statistics (Tutorials) | Screenshot | Description | Notes | @@ -88,6 +89,7 @@ All test-projects are Qt-projects that use qmake to build. You can load them int |:-------------:| ------------- | ------------- | | [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/test_multiplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/multiplot) | [Layouting Several Plots](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/multiplot) | Combining plots in Qt Layouts
linking plot axes
copy data from a `std::map` int the datastore
print plots/print preview | | [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/test_distributionplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/distributionplot) | [Plotting a Statistical Distribution of Data](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/distributionplot) | Combines several different graphs to draw random values, their distribution and some statistical properties | +| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/mandelbrot_small.gif)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/mandelbrot) | [Mandelbrot Set Explorer](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/mandelbrot) | | diff --git a/examples/mandelbrot/CMakeLists.txt b/examples/mandelbrot/CMakeLists.txt new file mode 100644 index 0000000000..dc0eb4151a --- /dev/null +++ b/examples/mandelbrot/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.0) + +set(EXAMPLE_NAME mandelbrot) +set(EXENAME jkqtptest_${EXAMPLE_NAME}) + +message( STATUS ".. Building Example ${EXAMPLE_NAME}" ) + + +# Set up source files +set(SOURCES ${EXAMPLE_NAME}.cpp mandelbrotmainwindow.cpp) +set(HEADERS mandelbrotmainwindow.h) +set(RESOURCES ) +set(UIS mandelbrotmainwindow.ui) + +add_executable(${EXENAME} WIN32 ${SOURCES} ${HEADERS} ${RESOURCES} ${UIS}) +target_include_directories(${EXENAME} PRIVATE ../../lib) +if(JKQtPlotter_BUILD_STATIC_LIBS) + target_link_libraries(${EXENAME} JKQTPlotterLib) +elseif(JKQtPlotter_BUILD_SHARED_LIBS) + target_link_libraries(${EXENAME} JKQTPlotterSharedLib) +endif() + + + +# Installation +install(TARGETS ${EXENAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +#Installation of Qt DLLs on Windows +jkqtplotter_deployqt(${EXENAME}) diff --git a/examples/mandelbrot/README.md b/examples/mandelbrot/README.md new file mode 100644 index 0000000000..6b267d4648 --- /dev/null +++ b/examples/mandelbrot/README.md @@ -0,0 +1,200 @@ +# Example (JKQTPlotter): Mandelbrot Set Explorer {#JKQTPlotterMandelbrot} + +## Introduction and Usage + +This project (see `./examples/mandelbrot/`) shows how to calculate and visualize the [Mandelbrot set](https://en.wikipedia.org/wiki/Mandelbrot_set) using `JKQTPlotter` and its `JKQTPMathImage`. + +The source code of the main application is (see [`mandelbrot.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/mandelbrot/mandelbrotmainwindow.cpp): + +![mandelbrot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/mandelbrot.png) + +You can use any of the several zooming methods (by mouse-wheel, panning, by drawing a rectangle ...) and the application will automaticaly calculate the zoomed area. Here is an example: + +1. Select the Zoom by Mouse Rectangle tool: ![mandelbrot_zoom_pre](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/mandelbrot_zoom_pre.png) +2. Drag open a rectangle that you want to zoom into: ![mandelbrot_zoom](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/mandelbrot_zoom.png) +3. When you release the mouse, the new image will be calculated. + + +## How it works + +In the constructor, the ui, containing a JKQTPlotter `ui->plot`, is initialized. Then the JKQTPlotter is set up: + +```.cpp + // 1. set the graph scales manually + ui->plot->setXY(-2,1,-1,1); + ui->plot->setAbsoluteXY(-5,5,-5,5); + // 2. set the asxpect ratio to width/height + ui->plot->getPlotter()->setMaintainAspectRatio(true); + ui->plot->getPlotter()->setAspectRatio(static_cast(ui->plot->width())/static_cast(ui->plot->height())); + // 3. disable grids + ui->plot->getXAxis()->setDrawGrid(false); + ui->plot->getYAxis()->setDrawGrid(false); +``` + +Then a `JKQTPMathImage` is added which displays an image column `mandelbrot_col_display`: + +```.cpp + graph=new JKQTPColumnMathImage(ui->plot); + graph->setTitle(""); + // image column with the data + graph->setImageColumn(mandelbrot_col_display); + // image color range is calculated manually! + graph->setAutoImageRange(false); + graph->setImageMin(0); + graph->setImageMax(ui->spinMaxIterations->value()); + // set image size + graph->setX(ui->plot->getXMin()); + graph->setY(ui->plot->getYMin()); + graph->setWidth(ui->plot->getXMax()-ui->plot->getXMin()); + graph->setHeight(ui->plot->getYMax()-ui->plot->getYMin()); + // add graph to plot + ui->plot->addGraph(graph); +``` + +In between thise two code blocks, two image columns are added to the internal `JKQTPDatastore`: + +```.cpp + mandelbrot_col=ds->addImageColumn(300,200, "mandelbrot_image_calculate"); + mandelbrot_col_display=ds->copyColumn(mandelbrot_col, "mandelbrot_image_display"); +``` + +As mentioned before, `mandelbrot_col_display` is used for plotting and the baclground column (of the same size) `mandelbrot_col` is used to calculate a new image: + +```.cpp + calculateMandelSet(ui->plot->getXMin(), ui->plot->getXMax(), ui->plot->getYMin(), ui->plot->getYMax(), 300, 200, ui->spinMaxIterations->value()); +``` + +When calculation finished, the contents of `mandelbrot_col` is copied to `mandelbrot_col_display`: + +```.cpp + ui->plot->getDatastore()->copyColumnData(mandelbrot_col_display, mandelbrot_col); +``` + +In order to implement the zoom functionality, the signal `JKQTPlotter::zoomChangedLocally` is connected to a function, which recalculates the new image for the new zoom-range: + + +```.cpp +void MandelbrotMainWindow::plotZoomChangedLocally(double newxmin, double newxmax, double newymin, double newymax, JKQTPlotter */*sender*/) +{ + calculateMandelSet(newxmin, newxmax, newymin, newymax, ui->plot->getXAxis()->getParentPlotWidth(), ui->plot->getYAxis()->getParentPlotWidth(), ui->spinMaxIterations->value()); + ui->plot->getDatastore()->copyColumnData(mandelbrot_col_display, mandelbrot_col); + if (ui->chkLogScaling->isChecked()) { + std::transform(ui->plot->getDatastore()->begin(mandelbrot_col), ui->plot->getDatastore()->end(mandelbrot_col), ui->plot->getDatastore()->begin(mandelbrot_col), &log10); + } + graph->setX(newxmin); + graph->setY(newymin); + graph->setWidth(newxmax-newxmin); + graph->setHeight(newymax-newymin); + // this call ensures correctly set NX and NY + graph->setImageColumn(mandelbrot_col_display); + ui->plot->redrawPlot(); +} +``` + +The actual calculation is performed in `calculateMandelSet()`: + +```.cpp +void MandelbrotMainWindow::calculateMandelSet(double rmin, double rmax, double imin, double imax, size_t width, size_t height, unsigned int max_iterations) { + QElapsedTimer timer; + timer.start(); + + auto ds=ui->plot->getDatastore(); + + // ensure the image column has the correct size + ds->resizeImageColumn(mandelbrot_col, width, height); + qDebug()<<"calculating for "<begin(mandelbrot_col); pix!= ds->end(mandelbrot_col); ++pix) { + // calculate the pixels coordinate in the imaginary plane + const double r0=static_cast(pix.getImagePositionX())/static_cast(width)*(rmax-rmin)+rmin; + const double i0=static_cast(pix.getImagePositionY())/static_cast(height)*(imax-imin)+imin; + //qDebug()<=2 gives the color of + // the point. + while(ri*ri+ii*ii<=2.0*2.0 && iteration(timer.nsecsElapsed())/1000000.0<<"ms"; +} +``` + +Here the actual algorithm to calculate the mandelbrot set is implemented. It iterates over all pixels `pix` in `mandelbrot_col` and updates their value according to the result of the calculation with `*pix=iteration;`. + +In order to speed up the program, it actually uses a parallelized version of the algorithm: + +```.cpp +void MandelbrotMainWindow::calculateMandelSet(double rmin, double rmax, double imin, double imax, size_t width, size_t height, unsigned int max_iterations) { + QElapsedTimer timer; + timer.start(); + + auto ds=ui->plot->getDatastore(); + + // ensure the image column has the correct size + ds->resizeImageColumn(mandelbrot_col, width, height); + qDebug()<<"calculating for "<(100,width*height/std::max(2, std::thread::hardware_concurrency()-1)); + + std::vector threads; + for (size_t offset=0; offsetbegin(mandelbrot_col)+static_cast(offset); + // stop iterating at begin+offset+blocksize, or at the end + const auto pix_end=pix+static_cast(blocksize); + for (; pix!=pix_end; ++pix) { + // calculate the pixels coordinate in the imaginary plane + const double r0=static_cast(pix.getImagePositionX())/static_cast(width)*(rmax-rmin)+rmin; + const double i0=static_cast(pix.getImagePositionY())/static_cast(height)*(imax-imin)+imin; + //qDebug()<=2 gives the color of + // the point. + while(ri*ri+ii*ii<=2.0*2.0 && iteration(timer.nsecsElapsed())/1000000.0<<"ms"; +} +``` + + diff --git a/examples/mandelbrot/mandelbrot.cpp b/examples/mandelbrot/mandelbrot.cpp new file mode 100644 index 0000000000..191027258a --- /dev/null +++ b/examples/mandelbrot/mandelbrot.cpp @@ -0,0 +1,21 @@ +/** \example mandelbrot.cpp + * Shows how to plot the Mandelbrot set with JKQTPlotter, also providing a zooming feature. + * + * \ref JKQTPlotterMandelbrot + */ + +#include +#include +#include "mandelbrotmainwindow.h" + + + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + MandelbrotMainWindow widMain; + + widMain.show(); + + return app.exec(); +} diff --git a/examples/mandelbrot/mandelbrot.pro b/examples/mandelbrot/mandelbrot.pro new file mode 100644 index 0000000000..3a0a5c6054 --- /dev/null +++ b/examples/mandelbrot/mandelbrot.pro @@ -0,0 +1,27 @@ +# source code for this simple demo +SOURCES = mandelbrot.cpp + +# configure Qt +CONFIG += link_prl qt +QT += core gui xml svg +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport + +# output executable name +TARGET = mandelbrot + +# include JKQTPlotter source code +DEPENDPATH += ../../lib ../../qmake/staticlib/jkqtplotterlib +INCLUDEPATH += ../../lib +CONFIG (debug, debug|release) { + LIBS += -L../../qmake/staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug +} else { + LIBS += -L../../qmake/staticlib/jkqtplotterlib/release -ljkqtplotterlib +} +message("LIBS = $$LIBS") + +win32-msvc*: DEFINES += _USE_MATH_DEFINES +win32-msvc*: DEFINES += NOMINMAX + + + + diff --git a/examples/mandelbrot/mandelbrot_and_lib.pro b/examples/mandelbrot/mandelbrot_and_lib.pro new file mode 100644 index 0000000000..73c1a0cf07 --- /dev/null +++ b/examples/mandelbrot/mandelbrot_and_lib.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs + +SUBDIRS += jkqtplotterlib mandelbrot + +jkqtplotterlib.file = ../../qmake/staticlib/jkqtplotterlib/jkqtplotterlib.pro + +mandelbrot.file=$$PWD/mandelbrot.pro +mandelbrot.depends = jkqtplotterlib diff --git a/examples/mandelbrot/mandelbrotmainwindow.cpp b/examples/mandelbrot/mandelbrotmainwindow.cpp new file mode 100644 index 0000000000..f6dd87a351 --- /dev/null +++ b/examples/mandelbrot/mandelbrotmainwindow.cpp @@ -0,0 +1,209 @@ +#include "mandelbrotmainwindow.h" +#include "ui_mandelbrotmainwindow.h" +#include +#include + +MandelbrotMainWindow::MandelbrotMainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MandelbrotMainWindow) +{ + ui->setupUi(this); + + // format graph: + // 1. set the graph scales manually + ui->plot->setXY(-2,1,-1,1); + ui->plot->setAbsoluteXY(-5,5,-5,5); + // 2. set the asxpect ratio to width/height + ui->plot->getPlotter()->setMaintainAspectRatio(true); + ui->plot->getPlotter()->setAspectRatio(static_cast(ui->plot->width())/static_cast(ui->plot->height())); + // 3. disable grids + ui->plot->getXAxis()->setDrawGrid(false); + ui->plot->getYAxis()->setDrawGrid(false); + + JKQTPDatastore* ds=ui->plot->getDatastore(); + + mandelbrot_col=ds->addImageColumn(300,200, "mandelbrot_image_calculate"); + mandelbrot_col_display=ds->copyColumn(mandelbrot_col, "mandelbrot_image_display"); + calculateMandelSet(ui->plot->getXMin(), ui->plot->getXMax(), ui->plot->getYMin(), ui->plot->getYMax(), 300, 200, ui->spinMaxIterations->value()); + ui->plot->getDatastore()->copyColumnData(mandelbrot_col_display, mandelbrot_col); + + // 4. create a graph (JKQTPColumnMathImage) with the column created above as data + // The data is color-coded with the color-palette JKQTPMathImageMATLAB + // the converted range of data is determined automatically because setAutoImageRange(true) + graph=new JKQTPColumnMathImage(ui->plot); + graph->setTitle(""); + // image column with the data + graph->setImageColumn(mandelbrot_col_display); + // image color range is calculated manually! + graph->setAutoImageRange(false); + graph->setImageMin(0); + graph->setImageMax(ui->spinMaxIterations->value()); + // set image size + graph->setX(ui->plot->getXMin()); + graph->setY(ui->plot->getYMin()); + graph->setWidth(ui->plot->getXMax()-ui->plot->getXMin()); + graph->setHeight(ui->plot->getYMax()-ui->plot->getYMin()); + // add graph to plot + ui->plot->addGraph(graph); + + + ui->cmbColorPalette->setCurrentColorPalette(graph->getColorPalette()); + + connect(ui->action_Reset_View, &QAction::triggered, this, &MandelbrotMainWindow::resetView); + connect(ui->cmbColorPalette, &JKQTPMathImageColorPaletteComboBox::currentPaletteChanged, this, &MandelbrotMainWindow::paletteChanged); + connect(ui->spinMaxIterations, static_cast(&QSpinBox::valueChanged), this, &MandelbrotMainWindow::maxIterationsChanged); + connect(ui->chkLogScaling, &QCheckBox::toggled, this, &MandelbrotMainWindow::logScalingChanged); + connect(ui->plot, &JKQTPlotter::zoomChangedLocally, this, &MandelbrotMainWindow::plotZoomChangedLocally); + connect(ui->plot, &JKQTPlotter::widgetResized, this, &MandelbrotMainWindow::plotResized); +} + +MandelbrotMainWindow::~MandelbrotMainWindow() +{ + delete ui; +} + +void MandelbrotMainWindow::paletteChanged(JKQTPMathImageColorPalette pal) +{ + graph->setColorPalette(pal); + ui->plot->redrawPlot(); +} + +void MandelbrotMainWindow::maxIterationsChanged(int/* maxIter*/) +{ + graph->setAutoImageRange(false); + graph->setImageMin(0); + if (ui->chkLogScaling->isChecked()) { + graph->setImageMax(log10(ui->spinMaxIterations->value())); + } else { + graph->setImageMax(ui->spinMaxIterations->value()); + } + plotZoomChangedLocally(ui->plot->getXMin(), ui->plot->getXMax(), ui->plot->getYMin(), ui->plot->getYMax(), ui->plot); +} + +void MandelbrotMainWindow::logScalingChanged(bool en) +{ + if (en) { + std::transform(ui->plot->getDatastore()->begin(mandelbrot_col), ui->plot->getDatastore()->end(mandelbrot_col), ui->plot->getDatastore()->begin(mandelbrot_col_display), &log10); + } else { + std::copy(ui->plot->getDatastore()->begin(mandelbrot_col), ui->plot->getDatastore()->end(mandelbrot_col), ui->plot->getDatastore()->begin(mandelbrot_col_display)); + } + graph->setAutoImageRange(false); + graph->setImageMin(0); + if (ui->chkLogScaling->isChecked()) { + graph->setImageMax(log10(ui->spinMaxIterations->value())); + } else { + graph->setImageMax(ui->spinMaxIterations->value()); + } + ui->plot->redrawPlot(); +} + +void MandelbrotMainWindow::plotResized(int /*new_width*/, int /*new_height*/, JKQTPlotter */*sender*/) +{ + ui->plot->getPlotter()->setAspectRatio(static_cast(ui->plot->width())/static_cast(ui->plot->height())); + ui->plot->getPlotter()->setMaintainAspectRatio(true); + qDebug()<<"plotResized: aspect="<(ui->plot->width())/static_cast(ui->plot->height()); + plotZoomChangedLocally(ui->plot->getXMin(), ui->plot->getXMax(), ui->plot->getYMin(), ui->plot->getYMax(), ui->plot); +} + +void MandelbrotMainWindow::resetView() +{ + ui->plot->setXY(-2,1,-1,1); + ui->plot->redrawPlot(); +} + +void MandelbrotMainWindow::plotZoomChangedLocally(double newxmin, double newxmax, double newymin, double newymax, JKQTPlotter */*sender*/) +{ + calculateMandelSet(newxmin, newxmax, newymin, newymax, ui->plot->getXAxis()->getParentPlotWidth(), ui->plot->getYAxis()->getParentPlotWidth(), ui->spinMaxIterations->value()); + ui->plot->getDatastore()->copyColumnData(mandelbrot_col_display, mandelbrot_col); + if (ui->chkLogScaling->isChecked()) { + std::transform(ui->plot->getDatastore()->begin(mandelbrot_col), ui->plot->getDatastore()->end(mandelbrot_col), ui->plot->getDatastore()->begin(mandelbrot_col), &log10); + } + graph->setX(newxmin); + graph->setY(newymin); + graph->setWidth(newxmax-newxmin); + graph->setHeight(newymax-newymin); + // this call ensures correctly set NX and NY + graph->setImageColumn(mandelbrot_col_display); + ui->plot->redrawPlot(); +} + + +void MandelbrotMainWindow::calculateMandelSet(double rmin, double rmax, double imin, double imax, size_t width, size_t height, unsigned int max_iterations) { + QElapsedTimer timer; + timer.start(); + + auto ds=ui->plot->getDatastore(); + + // ensure the image column has the correct size + ds->resizeImageColumn(mandelbrot_col, width, height); + qDebug()<<"calculating for "<(100,width*height/std::max(2, std::thread::hardware_concurrency()-1)); + + std::vector threads; + for (size_t offset=0; offsetbegin(mandelbrot_col)+static_cast(offset); + // stop iterating at begin+offset+blocksize, or at the end + const auto pix_end=pix+static_cast(blocksize); + for (; pix!=pix_end; ++pix) { + // calculate the pixels coordinate in the imaginary plane + const double r0=static_cast(pix.getImagePositionX())/static_cast(width)*(rmax-rmin)+rmin; + const double i0=static_cast(pix.getImagePositionY())/static_cast(height)*(imax-imin)+imin; + //qDebug()<=2 gives the color of + // the point. + while(ri*ri+ii*ii<=2.0*2.0 && iteration=2 gives the color of + // the point. + while(ri*ri+ii*ii<=2.0*2.0 && iteration(timer.nsecsElapsed())/1000000.0<<"ms"; +} diff --git a/examples/mandelbrot/mandelbrotmainwindow.h b/examples/mandelbrot/mandelbrotmainwindow.h new file mode 100644 index 0000000000..800a8a618d --- /dev/null +++ b/examples/mandelbrot/mandelbrotmainwindow.h @@ -0,0 +1,37 @@ +#ifndef MANDELBROTMAINWINDOW_H +#define MANDELBROTMAINWINDOW_H + +#include +#include "jkqtplotter/graphs/jkqtpimage.h" + +namespace Ui { +class MandelbrotMainWindow; +} + +class MandelbrotMainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MandelbrotMainWindow(QWidget *parent = nullptr); + ~MandelbrotMainWindow(); +protected slots: + void paletteChanged(JKQTPMathImageColorPalette pal); + void plotZoomChangedLocally(double newxmin, double newxmax, double newymin, double newymax, JKQTPlotter* sender); + void maxIterationsChanged(int maxIter); + void logScalingChanged(bool en); + void plotResized(int new_width, int new_height, JKQTPlotter* sender); + void resetView(); +protected: + void calculateMandelSet(double rmin, double rmax, double imin, double imax, size_t width, size_t height, unsigned int max_iterations = 1000); +private: + Ui::MandelbrotMainWindow *ui; + // graph representing Mandelbrot Set + JKQTPColumnMathImage* graph; + // column for Mandelbrot Set image data, used for calculations (double-buffering with mandelbrot_col_display) + size_t mandelbrot_col; + // column for Mandelbrot Set image data, used for display (double-buffering with mandelbrot_col) + size_t mandelbrot_col_display; +}; + +#endif // MANDELBROTMAINWINDOW_H diff --git a/examples/mandelbrot/mandelbrotmainwindow.ui b/examples/mandelbrot/mandelbrotmainwindow.ui new file mode 100644 index 0000000000..3cd98b0491 --- /dev/null +++ b/examples/mandelbrot/mandelbrotmainwindow.ui @@ -0,0 +1,145 @@ + + + MandelbrotMainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + + + + Color Palette: + + + + + + + + + + logarithmic color scaling + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + max. iterations: + + + + + + + 1 + + + 1000000 + + + 100 + + + + + + + + + + + + + + 0 + 0 + 800 + 21 + + + + + &Menu + + + + + + + + + + + &Exit + + + + + &Reset View + + + + + &Exit + + + + + + JKQTPlotter + QWidget +
jkqtplotter/jkqtplotter.h
+ 1 +
+ + JKQTPMathImageColorPaletteComboBox + QComboBox +
jkqtplotter/gui/jkqtpcomboboxes.h
+
+
+ + + + actionExit + triggered() + MandelbrotMainWindow + close() + + + -1 + -1 + + + 399 + 299 + + + + +
diff --git a/screenshots/mandelbrot.png b/screenshots/mandelbrot.png new file mode 100644 index 0000000000..eae1c5b90d Binary files /dev/null and b/screenshots/mandelbrot.png differ diff --git a/screenshots/mandelbrot_small.png b/screenshots/mandelbrot_small.png new file mode 100644 index 0000000000..6d85462626 Binary files /dev/null and b/screenshots/mandelbrot_small.png differ diff --git a/screenshots/mandelbrot_zoom.png b/screenshots/mandelbrot_zoom.png new file mode 100644 index 0000000000..fc4a8f8e71 Binary files /dev/null and b/screenshots/mandelbrot_zoom.png differ diff --git a/screenshots/mandelbrot_zoom_pre.png b/screenshots/mandelbrot_zoom_pre.png new file mode 100644 index 0000000000..ff360d4329 Binary files /dev/null and b/screenshots/mandelbrot_zoom_pre.png differ