#pragma once #include #include #include #include #include #include #include //#include //#include //#include #include "Bitmap.h" #include "Gradient.h" #include #include #include #include #include #include #include using GridIndex = long long; Q_DECLARE_METATYPE(GridIndex) class Texture { GLuint id; public: Texture(const Bitmap& pict, GLint param = GL_LINEAR); ~Texture(void); Texture(const Texture& other) = delete; Texture& operator=(const Texture& other) = delete; Texture(Texture&& other); Texture& operator=(Texture&& other); private: void bind(void) const; public: inline GLuint getId(void) const { return id; } void drawRect(float x, float y, float width, float height); }; class CellImage { public: virtual ~CellImage(void); virtual void drawRect(float x, float y, float width, float height) = 0; virtual std::shared_ptr clip(short i, short j) = 0; virtual int getRecalcPriority(void) const = 0; }; class TextureClip : public CellImage { std::shared_ptr texture; float tx, ty, tw, th; public: inline TextureClip(std::shared_ptr tex) : texture{ std::move(tex) }, tx{ 0 }, ty{ 0 }, tw{ 1 }, th{ 1 } {} virtual ~TextureClip(void); void drawRect(float x, float y, float width, float height); TextureClip clip(float x, float y, float w, float h); std::shared_ptr clip(short i, short j); int getRecalcPriority(void) const; }; class QuadImage : public CellImage { std::shared_ptr cells[2][2]; public: inline QuadImage(std::shared_ptr i00, std::shared_ptr i01, std::shared_ptr i10, std::shared_ptr i11) : cells{ { std::move(i00), std::move(i01) }, { std::move(i10), std::move(i11) } } {} virtual ~QuadImage(void); void drawRect(float x, float y, float width, float height); std::shared_ptr clip(short i, short j); int getRecalcPriority(void) const; }; struct PairHash { template std::size_t operator () (const std::pair& p) const { auto h1 = std::hash{}(p.first); auto h2 = std::hash{}(p.second); return (h1 ^ 234579245) * 23452354 + h2; } }; struct TripleHash { template std::size_t operator () (const std::tuple& p) const { auto h1 = std::hash{}(std::get<0>(p)); auto h2 = std::hash{}(std::get<1>(p)); auto h3 = std::hash{}(std::get<2>(p)); return (((h1 ^ 234579245) * 23452357 + h2) ^ 2345244345) * 23421 + h3; } }; struct GridElement { bool enoughResolution; std::shared_ptr img; inline GridElement(bool enoughResolution, std::shared_ptr img) : enoughResolution{ enoughResolution }, img{ std::move(img) } {} }; class MandelV; class TexGrid { public: MandelV& owner; int level; double dpp; std::unordered_map, std::unique_ptr, PairHash> cells; public: //inline TexGrid(MandelV& owner) : level{ 1.0 }, owner{ owner } {} TexGrid(MandelV& owner, int level); std::pair getCellIndices(double x, double y); std::pair getPositions(GridIndex i, GridIndex j); GridElement* getCell(GridIndex i, GridIndex j); void setCell(GridIndex i, GridIndex j, std::unique_ptr 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; GridIndex i, j; inline Job(mnd::MandelContext& mndContext, Gradient& gradient, int maxIter, TexGrid* grid, int level, GridIndex i, GridIndex j) : mndContext{ mndContext }, gradient{ gradient }, maxIter{ maxIter }, grid{ grid }, level{ level }, i{ i }, j{ j } {} void run() override; signals: void done(int level, GridIndex i, GridIndex j, Bitmap* bmp); }; class Calcer : public QObject { Q_OBJECT /// tuple contains level, i, j of the job std::unordered_map, Job*, TripleHash> jobs; QMutex jobsMutex; mnd::MandelContext& mndContext; std::unique_ptr threadPool; Gradient& gradient; int maxIter; int currentLevel; public: inline Calcer(mnd::MandelContext& mc, Gradient& gradient, int maxIter) : jobsMutex{ QMutex::Recursive }, mndContext{ mc }, threadPool{ std::make_unique() }, gradient{ gradient }, maxIter{ maxIter } { } void setMaxIter(int maxIter); void clearAll(void); public slots: void calc(TexGrid& grid, int level, GridIndex i, GridIndex j, int priority); void setCurrentLevel(int level); void notFinished(int level, GridIndex i, GridIndex j); void redirect(int level, GridIndex i, GridIndex j, Bitmap* bmp); signals: void done(int level, GridIndex i, GridIndex j, Bitmap* bmp); }; class MandelV : public QObject { Q_OBJECT public: std::unique_ptr empty; // a grid should not be deleted once constructed. // to free up memory one can call TexGrid::clearCells() std::unordered_map 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, GridIndex i, GridIndex j); GridElement* searchAbove(int level, GridIndex i, GridIndex j, int recursionLevel); GridElement* searchUnder(int level, GridIndex i, GridIndex j, int recursionLevel); void paint(const mnd::MandelViewport& mvp); public slots: void cellReady(int level, GridIndex i, GridIndex j, Bitmap* bmp); signals: void redrawRequested(void); }; class MandelWidget : public QOpenGLWidget { Q_OBJECT private: mnd::MandelContext& mndContext; Gradient gradient; bool initialized = false; int maxIterations = 2000; volatile bool rubberbanding = false; QRectF rubberband; volatile bool dragging = false; int dragX, dragY; mnd::MandelViewport currentViewport; mnd::MandelViewport targetViewport; std::chrono::time_point lastAnimUpdate; std::unique_ptr v; public: MandelWidget(mnd::MandelContext& ctxt, QWidget* parent = nullptr); ~MandelWidget(void) override; inline const Gradient& getGradient(void) const { return gradient; } void initializeGL(void) override; void resizeGL(int w, int h) override; void paintGL() override; private: void updateAnimations(void); void drawRubberband(void); public: 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 targetViewport; } signals: void needsUpdate(const mnd::MandelInfo vp); public slots: //void viewUpdated(Bitmap* bitmap); };