--- /dev/null
+# This file is used to ignore files which are generated
+# ----------------------------------------------------------------------------
+
+*~
+*.autosave
+*.a
+*.core
+*.moc
+*.o
+*.obj
+*.orig
+*.rej
+*.so
+*.so.*
+*_pch.h.cpp
+*_resource.rc
+*.qm
+.#*
+*.*#
+core
+!core/
+tags
+.DS_Store
+.directory
+*.debug
+Makefile*
+*.prl
+*.app
+moc_*.cpp
+ui_*.h
+qrc_*.cpp
+Thumbs.db
+*.res
+*.rc
+/.qmake.cache
+/.qmake.stash
+
+# qtcreator generated files
+*.pro.user*
+CMakeLists.txt.user*
+
+# xemacs temporary files
+*.flc
+
+# Vim temporary files
+.*.swp
+
+# Visual Studio generated files
+*.ib_pdb_index
+*.idb
+*.ilk
+*.pdb
+*.sln
+*.suo
+*.vcproj
+*vcproj.*.*.user
+*.ncb
+*.sdf
+*.opensdf
+*.vcxproj
+*vcxproj.*
+
+# MinGW generated files
+*.Debug
+*.Release
+
+# Python byte code
+*.pyc
+
+# Binaries
+# --------
+*.dll
+*.exe
+
+# Build directories
+# -----------------
+build-*/
--- /dev/null
+cmake_minimum_required(VERSION 3.5)
+
+project(bigintmandel VERSION 0.1 LANGUAGES CXX)
+
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Concurrent)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Concurrent)
+
+set(PROJECT_SOURCES
+ main.cpp
+ bigintwidget.cpp
+ bigintwidget.h
+ mandel.cpp
+ mandel.h
+)
+
+if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
+ qt_add_executable(bigintmandel
+ MANUAL_FINALIZATION
+ ${PROJECT_SOURCES}
+ )
+# Define target properties for Android with Qt 6 as:
+# set_property(TARGET bigintmandel APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
+# ${CMAKE_CURRENT_SOURCE_DIR}/android)
+# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
+else()
+ if(ANDROID)
+ add_library(bigintmandel SHARED
+ ${PROJECT_SOURCES}
+ )
+# Define properties for Android with Qt 5 after find_package() calls as:
+# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
+ else()
+ add_executable(bigintmandel
+ ${PROJECT_SOURCES}
+ )
+ endif()
+endif()
+
+target_link_libraries(
+ bigintmandel
+ PRIVATE
+ Qt${QT_VERSION_MAJOR}::Widgets
+ Qt${QT_VERSION_MAJOR}::Concurrent
+)
+
+# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
+# If you are developing for iOS or macOS you should consider setting an
+# explicit, fixed bundle identifier manually though.
+if(${QT_VERSION} VERSION_LESS 6.1.0)
+ set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.bigintmandel)
+endif()
+set_target_properties(bigintmandel PROPERTIES
+ ${BUNDLE_ID_OPTION}
+ MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
+ MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
+ MACOSX_BUNDLE TRUE
+ WIN32_EXECUTABLE TRUE
+)
+
+include(GNUInstallDirs)
+install(TARGETS bigintmandel
+ BUNDLE DESTINATION .
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
+
+if(QT_VERSION_MAJOR EQUAL 6)
+ qt_finalize_executable(bigintmandel)
+endif()
--- /dev/null
+
+// bigintwidget.cpp
+
+#include <QLayout>
+#include <QMouseEvent>
+#include <QtConcurrent/QtConcurrent>
+
+#include "bigintwidget.h"
+
+BigintWidget::BigintWidget(QWidget *parent)
+: QWidget(parent),
+ fw(new QFutureWatcher<MandelResultCell>(this)),
+ meta(1024, QSize(1800, 1000)),
+ img_label(new QLabel(this)),
+ img_dirty(true)
+{
+ connect(
+ fw,
+ &QFutureWatcher<MandelResultCell>::resultReadyAt,
+ this,
+ &BigintWidget::finished_cell
+ );
+ connect(
+ fw,
+ &QFutureWatcher<MandelResultCell>::finished,
+ this,
+ &BigintWidget::finished
+ );
+ fw->setFuture(QtConcurrent::mapped(meta.get_cells(), MandelMeta::iterate));
+ setLayout(new QVBoxLayout());
+ layout()->addWidget(img_label);
+ img_label->setMinimumSize(meta.get_size());
+}
+
+BigintWidget::~BigintWidget() {
+ fw->cancel();
+ fw->waitForFinished();
+}
+
+void BigintWidget::finished_cell(int num) {
+ meta.finished_cell(num, fw->resultAt(num));
+ img_dirty = true;
+ update();
+}
+
+void BigintWidget::finished() {
+ img_dirty = true;
+ update();
+}
+
+void BigintWidget::paintEvent(QPaintEvent *event) {
+ if (img_dirty) {
+ img_label->setPixmap(meta.get_pixmap());
+ img_dirty = false;
+ }
+}
+
+void BigintWidget::mousePressEvent(QMouseEvent *event) {
+ if (!fw->isFinished() || event->button() != Qt::MouseButton::LeftButton)
+ return;
+ QPoint pos = img_label->mapFromParent(event->pos());
+ if (pos.x() < 0 || pos.x() >= meta.get_width()
+ || pos.y() < 0 || pos.y() >= meta.get_height())
+ return;
+ meta.zoom2x(pos);
+ fw->setFuture(QtConcurrent::mapped(meta.get_cells(), MandelMeta::iterate));
+ img_dirty = true;
+ update();
+}
--- /dev/null
+
+// bigintwidget.h
+
+#ifndef BIGINTWIDGET_H
+#define BIGINTWIDGET_H
+
+#include <QFutureWatcher>
+#include <QLabel>
+#include <QWidget>
+
+#include "mandel.h"
+
+class BigintWidget : public QWidget {
+ Q_OBJECT
+
+ QFutureWatcher<MandelResultCell> *fw;
+ MandelMeta meta;
+ QLabel *img_label;
+ bool img_dirty;
+
+public:
+ BigintWidget(QWidget *parent = nullptr);
+ ~BigintWidget();
+ void finished_cell(int num);
+ void finished();
+ void paintEvent(QPaintEvent *event);
+ void mousePressEvent(QMouseEvent *event);
+};
+
+#endif // BIGINTWIDGET_H
--- /dev/null
+
+// colors.h
+
+#ifndef COLORS_H
+#define COLORS_H
+
+#include <QVector>
+#include <QColor>
+
+static QVector<QColor> colors = {
+ "#3868b8",
+ "#3468b4",
+ "#3468b4",
+ "#3468b0",
+ "#3468b0",
+ "#3068ac",
+ "#3068ac",
+ "#3064a8",
+ "#3064a8",
+ "#2c64a4",
+ "#2c64a4",
+ "#2c64a4",
+ "#2c64a0",
+ "#2c64a0",
+ "#28649c",
+ "#28649c",
+ "#286098",
+ "#286098",
+ "#246094",
+ "#246094",
+ "#246090",
+ "#246090",
+ "#20608c",
+ "#20608c",
+ "#206088",
+ "#205c88",
+ "#1c5c84",
+ "#1c5c84",
+ "#1c5c80",
+ "#1c5c80",
+ "#185c7c",
+ "#185c7c",
+ "#185c78",
+ "#185c78",
+ "#185c78",
+ "#3c6c70",
+ "#607868",
+ "#808464",
+ "#a4945c",
+ "#c8a054",
+ "#e8ac50",
+ "#e4a854",
+ "#e4a458",
+ "#e0a058",
+ "#e09c5c",
+ "#e0985c",
+ "#dc9460",
+ "#dc9060",
+ "#dc9064",
+ "#d88c64",
+ "#d88868",
+ "#d88468",
+ "#d4806c",
+ "#d47c70",
+ "#d07870",
+ "#d07874",
+ "#d07474",
+ "#cc7078",
+ "#cc6c78",
+ "#cc687c",
+ "#c8647c",
+ "#c86080",
+ "#c86080",
+ "#c4647c",
+ "#c0687c",
+ "#bc6c7c",
+ "#b8707c",
+ "#4cc470",
+ "#48c870",
+ "#40cc6c",
+ "#3cd06c",
+ "#38d06c",
+ "#3ccc70",
+ "#40cc70",
+ "#40cc74",
+ "#44c874",
+ "#44c878",
+ "#48c878",
+ "#4cc87c",
+ "#4cc47c",
+ "#50c480",
+ "#50c480",
+ "#54c484",
+ "#58c084",
+ "#58c084",
+ "#5cc088",
+ "#5cc088",
+ "#60bc8c",
+ "#64bc8c",
+ "#64bc90",
+ "#68bc90",
+ "#68b894",
+ "#6cb894",
+ "#70b898",
+ "#70b498",
+ "#74b49c",
+ "#74b49c",
+ "#78b49c",
+ "#78b0a0",
+ "#7cb0a0",
+ "#80b0a4",
+ "#80b0a4",
+ "#84aca8",
+ "#84aca8",
+ "#88acac",
+ "#8cacac",
+ "#8ca8b0",
+ "#90a8b0",
+ "#90a8b4",
+ "#94a8b4",
+ "#98a4b4",
+ "#98a4b8",
+ "#9ca4b8",
+ "#9ca0bc",
+ "#a0a0bc",
+ "#a4a0c0",
+ "#a4a0c0",
+ "#a89cc4",
+ "#a89cc4",
+ "#ac9cc8",
+ "#b09cc8",
+ "#b098cc",
+ "#b498cc",
+ "#b498cc",
+ "#b898d0",
+ "#b894d0",
+ "#bc94d4",
+ "#c094d4",
+ "#c094d8",
+ "#c490d8",
+ "#c490dc",
+ "#c890dc",
+ "#cc8ce0",
+ "#cc8ce0",
+ "#d08ce4",
+ "#d08ce4",
+ "#d488e4",
+ "#d888e8",
+ "#d888e8",
+ "#dc88ec",
+ "#dc84ec",
+ "#e084f0",
+ "#e484f0",
+ "#e484f4",
+ "#e880f4",
+ "#e880f8",
+ "#ec80f8",
+ "#ec80f8",
+ "#e87cf0",
+ "#e47cec",
+ "#e07ce8",
+ "#dc78e0",
+ "#d878dc",
+ "#d478d8",
+ "#d474d4",
+ "#d074cc",
+ "#cc74c8",
+ "#c870c4",
+ "#c470c0",
+ "#c070b8",
+ "#bc6cb4",
+ "#bc6cb0",
+ "#b86ca8",
+ "#b468a4",
+ "#b068a0",
+ "#ac689c",
+ "#a86494",
+ "#a46490",
+ "#a4648c",
+ "#a06488",
+ "#9c6080",
+ "#98607c",
+ "#946078",
+ "#905c74",
+ "#8c5c6c",
+ "#8c5c68",
+ "#885864",
+ "#84585c",
+ "#805858",
+ "#7c5454",
+ "#785450",
+ "#745448",
+ "#745044",
+ "#705040",
+ "#6c503c",
+ "#684c34",
+ "#644c30",
+ "#604c2c",
+ "#604c28",
+ "#5c502c",
+ "#5c5030",
+ "#5c5034",
+ "#5c5038",
+ "#5c5038",
+ "#5c503c",
+ "#5c5440",
+ "#585444",
+ "#585444",
+ "#585448",
+ "#58544c",
+ "#585450",
+ "#585850",
+ "#585854",
+ "#585858",
+ "#54585c",
+ "#54585c",
+ "#545860",
+ "#545c64",
+ "#545c68",
+ "#545c6c",
+ "#545c6c",
+ "#545c70",
+ "#505c74",
+ "#506078",
+ "#506078",
+ "#50607c",
+ "#506080",
+ "#506084",
+ "#506084",
+ "#506488",
+ "#4c648c",
+ "#4c6490",
+ "#4c6490",
+ "#4c6494",
+ "#4c6498",
+ "#4c689c",
+ "#4c68a0",
+ "#4c68a0",
+ "#4868a4",
+ "#4868a8",
+ "#4868ac",
+ "#486cac",
+ "#486cb0",
+ "#486cb4",
+ "#486cb8",
+ "#486cb8",
+ "#446cbc",
+ "#4470c0",
+ "#4470c4",
+ "#4470c4",
+ "#4470c8",
+ "#4470cc",
+ "#4470d0",
+ "#4470d0",
+ "#406ccc",
+ "#406ccc",
+ "#406cc8",
+ "#406cc8",
+ "#3c6cc4",
+ "#3c6cc4",
+ "#3c6cc0",
+ "#3c6cc0",
+ "#386cbc",
+ "#3868bc",
+ "#3868b8",
+ "#3868b8",
+};
+
+#endif // COLORS_H
--- /dev/null
+
+// main.cpp
+
+#include <QApplication>
+
+#include "bigintwidget.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ BigintWidget w;
+ w.show();
+ return a.exec();
+}
--- /dev/null
+
+// mandel.cpp
+
+#include "colors.h"
+#include "mandel.h"
+
+MandelResultCell::MandelResultCell() {}
+
+MandelResultCell::MandelResultCell(size_t iter, QPointF rpos)
+: iter(iter), rpos(rpos) {}
+
+static inline void incrpos(QPoint &pos, const int width) {
+ pos.setX(pos.x() + 1);
+ if (pos.x() == width) {
+ pos.setX(0);
+ pos.setY(pos.y() + 1);
+ }
+}
+
+MandelMeta::MandelMeta(size_t max_iter, const QSize size)
+: max_iter(max_iter),
+ img(size, QImage::Format_RGB888),
+ cells(size.width() * size.height(), MandelCell(this)),
+ center_f(qreal(-3) / 4, 0),
+ one(1),
+ four(4),
+ scale(std::max(qreal(3) / size.width(), qreal(3) / size.height()))
+{
+ QVector<MandelCell>::iterator i;
+ QPointF c(size.width() / 2., size.height() / 2.);
+ QPoint pos;
+
+ for (i = cells.begin(); i != cells.end(); incrpos(pos, size.width()), i++) {
+ i->set_meta(this);
+ i->set_pos(pos);
+ }
+}
+
+void MandelMeta::zoom2x(QPoint pos) {
+ QVector<MandelCell>::iterator i;
+ center_f = cells[img.width() * pos.y() + pos.x()].get_rpos0();
+ scale /= 2;
+ img.fill(Qt::GlobalColor::black);
+ for (i = cells.begin(); i != cells.end(); i++)
+ i->reset_iter_and_rpos();
+}
+
+void MandelMeta::finished_cell(int num, const MandelResultCell &cell) {
+ img.setPixelColor(
+ cells[num].update_result(cell.iter, cell.rpos),
+ cell.iter < max_iter
+ ? colors[cell.iter % colors.size()]
+ : Qt::GlobalColor::black
+ );
+}
+
+MandelResultCell MandelMeta::iterate(const MandelCell &cell) {
+ return cell.iterate();
+}
+
+void MandelCell::reset_rpos0() {
+ QPointF center_f = meta->get_center_f();
+ rpos0 = QPointF(
+ center_f.x() + (pos.x() - meta->get_width() / 2.) * meta->get_scale(),
+ center_f.y() + (pos.y() - meta->get_height() / 2.) * meta->get_scale()
+ );
+}
+
+MandelCell::MandelCell(MandelMeta *meta) : meta(meta), iter(0) {}
+
+MandelCell::MandelCell(const MandelCell &cell)
+: meta(cell.meta), pos(cell.pos), iter(cell.iter),
+ rpos(cell.rpos), rpos0(cell.rpos0) {}
+
+QPoint MandelCell::update_result(size_t iter, const QPointF &rpos) {
+ this->iter = iter;
+ this->rpos = rpos;
+ return pos;
+}
+
+void MandelCell::reset_iter_and_rpos() {
+ iter = 0;
+ rpos = QPointF();
+ reset_rpos0();
+}
+
+void MandelCell::set_meta(MandelMeta *meta) {
+ this->meta = meta;
+}
+
+void MandelCell::set_pos(QPoint pos) {
+ this->pos = pos;
+ reset_rpos0();
+}
+
+MandelResultCell MandelCell::iterate() const {
+ size_t iter = this->iter;
+ QPointF rpos = this->rpos, sq;
+ for (; iter < this->meta->get_max_iter(); iter++) {
+ sq = QPointF(rpos.x() * rpos.x(), rpos.y() * rpos.y());
+ if (sq.x() + sq.y() > meta->get_four())
+ break;
+ rpos = QPointF(
+ sq.x() - sq.y() + rpos0.x(),
+ 2 * rpos.x() * rpos.y() + rpos0.y()
+ );
+ }
+ return MandelResultCell(iter, rpos);
+}
--- /dev/null
+
+// mandel.h
+
+#ifndef MANDEL_H
+#define MANDEL_H
+
+#include <QPixmap>
+#include <QPoint>
+#include <QVector>
+
+class MandelCell;
+
+class MandelResultCell {
+ friend class MandelMeta;
+
+ size_t iter;
+ QPointF rpos;
+public:
+ MandelResultCell();
+ MandelResultCell(size_t iter, QPointF rpos);
+};
+
+class MandelMeta {
+ size_t max_iter;
+ QImage img;
+ QVector<MandelCell> cells;
+ QPointF center_f;
+ qreal one, four, scale;
+
+public:
+ MandelMeta(size_t max_iter, QSize size);
+ const size_t get_max_iter() const { return max_iter; }
+ const QPixmap get_pixmap() const { return QPixmap::fromImage(img); }
+ const int get_width() const { return img.width(); }
+ const int get_height() const { return img.height(); }
+ const QSize get_size() const { return img.size(); }
+ const QVector<MandelCell> get_cells() const { return cells; }
+ const QPointF get_center_f() const { return center_f; }
+ const qreal get_four() const { return four; }
+ const qreal get_scale() const { return scale; }
+
+ void zoom2x(QPoint pos);
+ void finished_cell(int num, const MandelResultCell &cell);
+ static MandelResultCell iterate(const MandelCell &cell);
+};
+
+class MandelCell {
+ MandelMeta *meta;
+ QPoint pos;
+ size_t iter;
+ QPointF rpos, rpos0;
+
+protected:
+ void reset_rpos0();
+
+public:
+ MandelCell(MandelMeta *meta);
+ MandelCell(const MandelCell &cell);
+ inline const size_t get_iter() const { return iter; }
+ inline const QPointF get_rpos0() const { return rpos0; }
+
+ QPoint update_result(size_t iter, const QPointF &rpos);
+ void reset_iter_and_rpos();
+ void set_meta(MandelMeta *meta);
+ void set_pos(const QPoint pos);
+ MandelResultCell iterate() const;
+};
+
+#endif // MANDEL_H