#pragma once #include <QGLWidget> #include <QOpenGLWidget> #include <QThread> #include <QThreadPool> #include <qopengl.h> #include <qopenglfunctions.h> #include <qopenglcontext.h> #include <qscrollarea.h> #include <qlabel.h> #include <qevent.h> #include <qrubberband.h> #include "Bitmap.h" #include "Gradient.h" #include <Mandel.h> #include <future> #include <thread> #include <mutex> #include <atomic> #include <tuple> #include <deque> #include <unordered_map> #include <unordered_set> class MandelWidget; class Texture { GLuint id; QOpenGLContext* context; public: Texture(const Bitmap<RGBColor>& pict, GLint param = GL_LINEAR); Texture(const Bitmap<RGBColor>& pict, QOpenGLContext* context); ~Texture(void); Texture(const Texture& other) = delete; Texture& operator=(const Texture& other) = delete; Texture(Texture&& other); Texture& operator=(Texture&& other); void bind(void) const; inline GLuint getId(void) const { return id; } void drawRect(float x, float y, float width, float height); }; struct MandelClip { mnd::MandelViewport view; }; struct PairHash { template <typename T1, typename T2> std::size_t operator () (const std::pair<T1, T2>& p) const { auto h1 = std::hash<T1>{}(p.first); auto h2 = std::hash<T2>{}(p.second); return (h1 ^ 234579245) * 23452354 + h2; } }; struct TripleHash { template <typename T1, typename T2, typename T3> std::size_t operator () (const std::tuple<T1, T2, T3>& p) const { auto h1 = std::hash<T1>{}(std::get<0>(p)); auto h2 = std::hash<T2>{}(std::get<1>(p)); auto h3 = std::hash<T3>{}(std::get<2>(p)); return (((h1 ^ 234579245) * 23452357 + h2) ^ 2345244345) * 23421 + h3; } }; class TexGrid { public: double dpp; std::unordered_map<std::pair<int, int>, std::unique_ptr<Texture>, PairHash> cells; public: inline TexGrid(void) : dpp{ 1.0 } {} inline TexGrid(double dpp) : dpp{ dpp } {} //TexGrid(const TexGrid&) = delete; std::pair<int, int> getCellIndices(double x, double y); std::pair<double, double> getPositions(int i, int j); Texture* getCell(int i, int j); void setCell(int i, int j, std::unique_ptr<Texture> tex); inline size_t countAllocatedCells(void) const { return cells.size(); } void clearCells(void); }; class Job : public QObject, public QRunnable { Q_OBJECT public: mnd::MandelContext& mndContext; Gradient& gradient; int maxIter; TexGrid* grid; int level; int i, j; inline Job(mnd::MandelContext& mndContext, Gradient& gradient, int maxIter, TexGrid* grid, int level, int i, int j) : mndContext{ mndContext }, gradient{ gradient }, maxIter{ maxIter }, grid{ grid }, level{ level }, i{ i }, j{ j } {} void run() override; signals: void done(int level, int i, int j, Bitmap<RGBColor>* bmp); }; class Calcer : public QObject { Q_OBJECT std::unordered_set<std::tuple<int, int, int>, TripleHash> jobs; mnd::MandelContext& mndContext; std::unique_ptr<QThreadPool> threadPool; Gradient& gradient; int maxIter; public: inline Calcer(mnd::MandelContext& mc, Gradient& gradient, int maxIter) : mndContext{ mc }, threadPool{ std::make_unique<QThreadPool>() }, gradient{ gradient }, maxIter{ maxIter } { threadPool->setMaxThreadCount(4); } void setMaxIter(int maxIter); void clearAll(void); public slots: void calc(TexGrid& grid, int level, int i, int j); void notFinished(int level, int i, int j); void redirect(int level, int i, int j, Bitmap<RGBColor>* bmp); signals: void done(int level, int i, int j, Bitmap<RGBColor>* bmp); }; class MandelV : public QObject { Q_OBJECT public: std::unique_ptr<Texture> empty; // a grid should not be deleted once constructed. // to free up memory one can call TexGrid::clearCells() std::unordered_map<int, TexGrid> levels; mnd::MandelContext& mndContext; Calcer calcer; Gradient& gradient; int maxIter; int width; int height; public: static const int chunkSize = 256; MandelV(mnd::MandelContext& mndContext, Gradient& gradient, int maxIter); int getLevel(double dpp); double getDpp(int level); TexGrid& getGrid(int level); inline int getMaxIter(void) const { return this->maxIter; } void setMaxIter(int maxIter); void clear(void); void garbageCollect(int level); void paint(const mnd::MandelViewport& mvp); public slots: void cellReady(int level, int i, int j, Bitmap<RGBColor>* bmp); signals: void redrawRequested(void); }; class MandelView : public QObject { Q_OBJECT private: std::future<void> calc; QThread calcThread; std::mutex mut; std::condition_variable condVar; std::atomic<mnd::MandelInfo> toCalc; std::atomic_bool hasToCalc; std::atomic_bool finish; mnd::Generator* generator; Gradient& gradient; MandelWidget* mWidget; //QOpenGLContext* context; public: MandelView(mnd::Generator& generator, Gradient& gradient, MandelWidget* mWidget); ~MandelView(void); void setGenerator(mnd::Generator &value); void start(); private slots: void loop(); public slots: void adaptViewport(const mnd::MandelInfo vp); signals: void updated(Bitmap<RGBColor>* bitmap); }; class MandelWidget : public QOpenGLWidget { Q_OBJECT private: //QScrollArea qsa; //QLabel ql; mnd::MandelContext& mndContext; Gradient gradient; bool initialized = false; int maxIterations = 2000; QRectF rubberband; bool dragging = false; int dragX, dragY; std::unique_ptr<Texture> tex; mnd::MandelViewport viewport; MandelView mv; std::unique_ptr<MandelV> v; public: MandelWidget(mnd::MandelContext& ctxt, QWidget* parent = nullptr); ~MandelWidget(void) override; /*inline MandelWidget(const MandelWidget& other) : mndContext{ other.mndContext }, mv{ other.mndContext } { }*/ inline const Gradient& getGradient(void) const { return gradient; } void initializeGL(void) override; void resizeGL(int w, int h) override; void paintGL() override; void drawRubberband(void); void zoom(float scale, float x = 0.5f, float y = 0.5f); void setMaxIterations(int maxIter); //void redraw(); void requestRecalc(void); void resizeEvent(QResizeEvent* re) override; void mousePressEvent(QMouseEvent* me) override; void mouseMoveEvent(QMouseEvent* me) override; void mouseReleaseEvent(QMouseEvent* me) override; void wheelEvent(QWheelEvent * we) override; inline const mnd::MandelViewport& getViewport(void) const { return viewport; } signals: void needsUpdate(const mnd::MandelInfo vp); public slots: void viewUpdated(Bitmap<RGBColor>* bitmap); };