Nicolas Winkler 5 سال پیش
والد
کامیت
4123d9799c
3فایلهای تغییر یافته به همراه291 افزوده شده و 339 حذف شده
  1. 5 11
      Gradient.cpp
  2. 219 251
      MandelWidget.cpp
  3. 67 77
      MandelWidget.h

+ 5 - 11
Gradient.cpp

@@ -123,31 +123,24 @@ void Gradient::addColor(RGBColor c, float value)
     colors.push_back({ c, value });
 }
 
+
 RGBColor Gradient::get(float x) const
 {
-    /*while(x >= max) {
-        if (x < 2 * max)
-            x = 2 * max - x;
-        else
-            x -= max;
-    }*/
     if (colors.empty())
         return RGBColor();
     const auto [left, right, lerp] = getNeighbors(x);
     RGBColor lerped = lerpColors(left, right, lerp);
     return lerped;
-    //RGBColor lerped = lerpColors(RGBColor{0, 0, 0}, RGBColor{255, 255, 255}, x / 2000.0f);
-    //printf("rgb: %d, %d, %d\n", left.r, left.g, left.b);
-    return RGBColor{uint8_t(::sin(x * 0.1) * 127 + 127), 0, 0};
 }
 
+
 RGBColor Gradient::lerpColors(RGBColor a, RGBColor b, float val)
 {
     auto mklin = [] (double x) {
-        return x;//::pow(x, 2.4);
+        return x * x;//::pow(x, 2.4);
     };
     auto unlin = [] (double x) {
-        return x;// ::pow(x, 1.0 / 2.4);
+        return ::sqrt(x);// ::pow(x, 1.0 / 2.4);
     };
 
     return RGBColor{
@@ -157,6 +150,7 @@ RGBColor Gradient::lerpColors(RGBColor a, RGBColor b, float val)
     };
 }
 
+
 std::tuple<RGBColor, RGBColor, float> Gradient::getNeighbors(float x) const
 {
     for (auto it = colors.begin(); it != colors.end(); ++it) {

+ 219 - 251
MandelWidget.cpp

@@ -6,8 +6,7 @@ using namespace mnd;
 #include <QOpenGLVertexArrayObject>
 
 
-Texture::Texture(const Bitmap<RGBColor>& bitmap, GLint param) :
-    context{ nullptr }
+Texture::Texture(const Bitmap<RGBColor>& bitmap, GLint param)
 {
     glGenTextures(1, &id);
     glBindTexture(GL_TEXTURE_2D, id);
@@ -31,31 +30,6 @@ Texture::Texture(const Bitmap<RGBColor>& bitmap, GLint param) :
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, param);
 }
 
-Texture::Texture(const Bitmap<RGBColor>& bitmap, QOpenGLContext* context) :
-    context{ context }
-{
-    context->functions()->glGenTextures(1, &id);
-    context->functions()->glBindTexture(GL_TEXTURE_2D, id);
-
-    long lineLength = (bitmap.width * 3 + 3) & ~3;
-
-    unsigned char* pixels = new unsigned char[lineLength * bitmap.height];
-    for (int i = 0; i < bitmap.width; i++) {
-        for (int j = 0; j < bitmap.height; j++) {
-            int index = i * 3 + j * lineLength;
-            RGBColor c = bitmap.get(i, j);
-            pixels[index] = c.r;
-            pixels[index + 1] = c.g;
-            pixels[index + 2] = c.b;
-        }
-    }
-    context->functions()->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, int(bitmap.width), int(bitmap.height), 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
-    context->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    context->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    context->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    context->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-}
-
 
 Texture::~Texture(void)
 {
@@ -65,8 +39,7 @@ Texture::~Texture(void)
 
 
 Texture::Texture(Texture&& other) :
-    id{ other.id },
-    context{ other.context }
+    id{ other.id }
 {
     other.id = 0;
 }
@@ -75,7 +48,6 @@ Texture::Texture(Texture&& other) :
 Texture& Texture::operator=(Texture&& other)
 {
     this->id = other.id;
-    this->context = other.context;
     other.id = 0;
     return *this;
 }
@@ -106,19 +78,57 @@ void Texture::drawRect(float x, float y, float width, float height)
 }
 
 
-std::pair<int, int> TexGrid::getCellIndices(double x, double y)
+void TextureClip::drawRect(float x, float y, float width, float height)
+{
+    glColor3ub(255, 255, 255);
+    glEnable(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, texture->getId());
+    glBegin(GL_TRIANGLE_STRIP);
+    glTexCoord2f(tx, ty);
+    glVertex2f(x, y);
+    glTexCoord2f(tx + tw, ty);
+    glVertex2f(x + width, y);
+    glTexCoord2f(tx, ty + th);
+    glVertex2f(x, y + height);
+    glTexCoord2f(tx + tw, ty + th);
+    glVertex2f(x + width, y + height);
+    glEnd();
+    glDisable(GL_TEXTURE_2D);
+}
+
+
+TextureClip TextureClip::clip(float x, float y, float w, float h)
+{
+    TextureClip result(this->texture);
+    result.tx = this->tx + x * this->tw;
+    result.ty = this->ty + y * this->th;
+    result.tw = this->tw * w;
+    result.th = this->th * h;
+    return result;
+}
+
+
+TexGrid::TexGrid(MandelV& owner, int level) :
+    owner{ owner },
+    level{ level },
+    dpp{ owner.getDpp(level) }
+{
+}
+
+
+std::pair<GridIndex, GridIndex> TexGrid::getCellIndices(double x, double y)
 {
     return { ::floor(x / dpp / MandelV::chunkSize), ::floor(y / dpp / MandelV::chunkSize) };
 }
 
 
-std::pair<double, double> TexGrid::getPositions(int x, int y)
+std::pair<double, double> TexGrid::getPositions(GridIndex x, GridIndex y)
 {
     return { x * dpp * MandelV::chunkSize, y * dpp * MandelV::chunkSize };
 }
 
 
-Texture* TexGrid::getCell(int i, int j)
+GridElement* TexGrid::getCell(GridIndex i, GridIndex j)
 {
     auto cIt = cells.find({i, j});
     if (cIt != cells.end()) {
@@ -130,7 +140,7 @@ Texture* TexGrid::getCell(int i, int j)
 }
 
 
-void TexGrid::setCell(int i, int j, std::unique_ptr<Texture> tex)
+void TexGrid::setCell(GridIndex i, GridIndex j, std::unique_ptr<GridElement> tex)
 {
     cells[{i, j}] = std::move(tex);
 }
@@ -175,27 +185,53 @@ void Calcer::clearAll(void)
 }
 
 
-void Calcer::calc(TexGrid& grid, int level, int i, int j)
+void Calcer::calc(TexGrid& grid, int level, GridIndex i, GridIndex j, int priority)
 {
+    jobsMutex.lock();
     if (jobs.find({ level, i, j }) == jobs.end()) {
         Job* job = new Job(mndContext, gradient, maxIter, &grid, level, i, j);
         connect(job, &Job::done, this, &Calcer::redirect);
         connect(job, &QObject::destroyed, this, [this, level, i, j] () { this->notFinished(level, i, j); });
-        jobs.emplace(level, i, j);
-        threadPool->start(job);
+        jobs.emplace(std::tuple{level, i, j}, job);
+        threadPool->start(job, priority);
+    }
+    jobsMutex.unlock();
+}
+
+
+void Calcer::setCurrentLevel(int level)
+{
+    if (this->currentLevel != level) {
+        this->currentLevel = level;
+        std::vector<QRunnable*> toCancel;
+        jobsMutex.lock();
+        for (auto&[tup, job] : jobs) {
+            auto& [level, i, j] = tup;
+            if(level != currentLevel) {
+                toCancel.push_back(job);
+            }
+        }
+        jobsMutex.unlock();
+        for (auto* job : toCancel) {
+            threadPool->cancel(job);
+        }
     }
 }
 
 
-void Calcer::notFinished(int level, int i, int j)
+void Calcer::notFinished(int level, GridIndex i, GridIndex j)
 {
+    jobsMutex.lock();
     jobs.erase({ level, i, j });
+    jobsMutex.unlock();
 }
 
 
-void Calcer::redirect(int level, int i, int j, Bitmap<RGBColor>* bmp)
+void Calcer::redirect(int level, GridIndex i, GridIndex j, Bitmap<RGBColor>* bmp)
 {
+    jobsMutex.lock();
     jobs.erase({ level, i, j });
+    jobsMutex.unlock();
     emit done(level, i, j, bmp);
 }
 
@@ -240,8 +276,8 @@ TexGrid& MandelV::getGrid(int level)
         return it->second;
     }
     else {
-        levels.insert(std::pair<int, TexGrid>{ level, TexGrid{ getDpp(level) } });
-        return levels[level];
+        levels.insert(std::pair<int, TexGrid>{ level, TexGrid{ *this, level } });
+        return levels.at(level);
     }
 }
 
@@ -291,200 +327,140 @@ void MandelV::garbageCollect(int level)
 }
 
 
+GridElement* MandelV::searchAbove(int level, GridIndex i, GridIndex j, int recursionLevel)
+{
+    auto& grid = getGrid(level);
+    auto& gridAbove = getGrid(level + 1);
+
+    GridIndex ai = (i < 0 ? (i - 1) : i) / 2;
+    GridIndex aj = (j < 0 ? (j - 1) : j) / 2;
+
+    GridElement* above = gridAbove.getCell(ai, aj);
+
+    if (above == nullptr && recursionLevel > 0) {
+        auto abFound = searchAbove(level + 1, ai, aj, recursionLevel - 1);
+        if (abFound)
+            above = abFound;
+    }
+
+    if (above != nullptr) {
+        auto newElement = std::make_unique<GridElement>(
+            false, above->img.clip((i & 1) * 0.5f, (j & 1) * 0.5f, 0.5f, 0.5f)
+        );
+        GridElement* ret = newElement.get();
+        grid.setCell(i, j, std::move(newElement));
+        return ret;
+    }
+    else {
+        return nullptr;
+    }
+}
+
+
+std::unique_ptr<GridElement> MandelV::searchUnder(int level, GridIndex i, GridIndex j, int recursionLevel)
+{
+    /*if (recursionLevel == 0)
+        return nullptr;
+
+    auto& gridUnder = getGrid(level - 1);
+
+    int ai = i * 2;
+    int aj = j * 2;
+
+    GridElement* u00 = gridUnder.getCell(ai, aj);
+    GridElement* u01 = gridUnder.getCell(ai, aj + 1);
+    GridElement* u10 = gridUnder.getCell(ai + 1, aj);
+    GridElement* u11 = gridUnder.getCell(ai + 1, aj + 1);
+
+    if (   u00 != nullptr
+        && u01 != nullptr
+        && u10 != nullptr
+        && u11 != nullptr) {
+        GLuint FramebufferName = 0;
+        auto newElement = std::make_unique<GridElement>(
+            false, above->img.clip((i & 1) * 0.5f, (j & 1) * 0.5f, 0.5f, 0.5f)
+        );
+        return newElement;
+    }
+    else {
+        return searchAbove(level - 1, ai, aj, recursionLevel - 1);
+    }*/
+    return nullptr;
+}
+
+
 void MandelV::paint(const mnd::MandelViewport& mvp)
 {
     double dpp = mvp.width / width;
     int level = getLevel(dpp) - 1;
     garbageCollect(level);
+    emit calcer.setCurrentLevel(level);
 
     auto& grid = getGrid(level);
     double gw = getDpp(level) * chunkSize;
 
     double w = width * gw / mvp.width;
     //double h = height * gw / mvp.height;
-    printf("level: %d, dpp: %f, width: %f\n", level, dpp, w);
+    //printf("level: %d, dpp: %f, width: %f\n", level, dpp, w);
 
     auto [left, top] = grid.getCellIndices(mvp.x, mvp.y);
     auto [right, bottom] = grid.getCellIndices(mvp.right(), mvp.bottom());
     auto [realXLeft, realYTop] = grid.getPositions(left, top);
     realXLeft = (realXLeft - mvp.x) * width / mvp.width;
     realYTop = (realYTop - mvp.y) * height / mvp.height;
-    for(int i = left; i <= right; i++) {
-        for(int j = top; j <= bottom; j++) {
+    for(GridIndex i = left; i <= right; i++) {
+        for(GridIndex j = top; j <= bottom; j++) {
             double x = realXLeft + (i - left) * w;
             double y = realYTop + (j - top) * w;
 
-            Texture* t = grid.getCell(i, j);
+            GridElement* t = grid.getCell(i, j);
+
+            if (t == nullptr) {
+                auto above = searchAbove(level, i, j, 2);
+                if (above) {
+                    t = above;
+                }
+            }
+
             if (t != nullptr) {
-                t->drawRect(x, y, w, w);
+                t->img.drawRect(x, y, w, w);
                 /*glBegin(GL_LINE_LOOP);
                 glVertex2f(x, y);
                 glVertex2f(x + w, y);
                 glVertex2f(x + w, y + w);
                 glVertex2f(x, y + w);
                 glEnd();*/
+
+                if (!t->enoughResolution) {
+                    calcer.calc(grid, level, i, j, 100);
+                }
             }
             else {
-                calcer.calc(grid, level, i, j);
+                calcer.calc(grid, level, i, j, 1000);
                 this->empty->drawRect(x, y, w, w);
             }
         }
     }
 }
 
-void MandelV::cellReady(int level, int i, int j, Bitmap<RGBColor>* bmp)
+void MandelV::cellReady(int level, GridIndex i, GridIndex j, Bitmap<RGBColor>* bmp)
 {
-    this->getGrid(level).setCell(i, j, std::make_unique<Texture>(*bmp));
+
+    this->getGrid(level).setCell(i, j,
+        std::make_unique<GridElement>(true, TextureClip{ std::make_shared<Texture>(*bmp) }));
     delete bmp;
     emit redrawRequested();
 }
 
 
-MandelView::MandelView(mnd::Generator& generator, Gradient &gradient, MandelWidget* mWidget) :
-    generator{ &generator },
-    gradient{ gradient },
-    mWidget{ mWidget }
-    //context{ new QOpenGLContext(this) }
-{
-    //context->setShareContext(mWidget->context()->contextHandle());
-    hasToCalc.store(false);
-    finish.store(false);
-}
-
-
-MandelView::~MandelView(void)
-{
-    finish.store(true);
-    condVar.notify_one();
-    //calcThread.wait(100);
-    calcThread.wait(100);
-    calcThread.terminate();
-}
-
-
-void MandelView::setGenerator(mnd::Generator& value)
-{
-    generator = &value;
-}
-
-void MandelView::start(void)
-{
-    this->moveToThread(&calcThread);
-    connect(&calcThread, SIGNAL(started()), this, SLOT(loop()));
-    calcThread.start();
-}
-
-
-void MandelView::loop(void)
-{
-    printf("thread!\n"); fflush(stdout);
-    //QGLWidget* hiddenWidget = new QGLWidget(nullptr, mWidget);
-    //hiddenWidget->setVisible(false);
-    //hiddenWidget->context()->contextHandle()->moveToThread(&calcThread);
-    //QOpenGLContext* context = hiddenWidget->context()->contextHandle();
-    //context->setShareContext(mWidget->context()->contextHandle());
-    //context->create();
-    //printf("sharing: %d\n", QOpenGLContext::areSharing(hiddenWidget->context()->contextHandle(), mWidget->context()->contextHandle()));
-    //fflush(stdout);
-    //std::this_thread::sleep_for(std::chrono::milliseconds(3000));
-    std::unique_lock<std::mutex> lock(mut);
-    while(true) {
-        printf("calcing!\n"); fflush(stdout);
-        if (finish.load()) {
-            break;
-        }
-        if (hasToCalc.exchange(false)) {
-            const MandelInfo& mi = toCalc.load();
-            auto fmap = Bitmap<float>(mi.bWidth, mi.bHeight);
-            generator->generate(mi, fmap.pixels.get());
-            auto* bitmap = new Bitmap<RGBColor>(fmap.map<RGBColor>([&mi, this](float i) {
-                return i >= mi.maxIter ? RGBColor{ 0, 0, 0 } : gradient.get(i);
-            }));
-                /*return i >= mi.maxIter ?
-                            RGBColor{ 0,0,0 } :
-                            RGBColor{ uint8_t(cos(i * 0.015f) * 127 + 127),
-                                      uint8_t(sin(i * 0.01f) * 127 + 127),
-                                      uint8_t(i) }; }));//uint8_t(::sin(i * 0.01f) * 100 + 100), uint8_t(i) }; });
-*/
-            //hiddenWidget->makeCurrent();
-            //Texture* tex = new Texture(bitmap);
-            //hiddenWidget->doneCurrent();
-            //Texture* tex = 0;
-            emit updated(bitmap);
-        }
-        printf("finished calcing!\n"); fflush(stdout);
-        condVar.wait(lock);
-        printf("waking!\n"); fflush(stdout);
-    }
-}
-
-void MandelView::adaptViewport(const MandelInfo mi)
-{
-    //bmp->get(0, 0) = RGBColor{ 10, uint8_t(sin(1 / vp.width) * 127 + 127), 10 };
-    /*printf("adapted\n");
-    if (calc.valid()) {
-        auto status = calc.wait_for(std::chrono::milliseconds(0));
-        if (status == std::future_status::deferred) {
-            printf("deferred\n");
-        } else if (status == std::future_status::timeout) {
-            printf("timeout\n");
-        } else if (status == std::future_status::ready) {
-            printf("ready!\n");
-        }
-    }*/
-    /*if (!calc.valid() || calc.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) {
-        toCalc = mi;
-        hasToCalc = true;
-        calc = std::async([this, mi] () {
-            QGLWidget* hiddenWidget = new QGLWidget(nullptr, (QGLWidget*) mWidget);
-            QOpenGLContext* context = hiddenWidget->context()->contextHandle();
-            hiddenWidget->makeCurrent();
-            //context->setShareContext(mWidget->context()->contextHandle());
-            //context->create();
-            printf("sharing: %d\n", QOpenGLContext::areSharing(context, mWidget->context()->contextHandle()));
-            fflush(stdout);
-            //std::this_thread::sleep_for(std::chrono::milliseconds(1000));
-            do {
-                auto fmap = Bitmap<float>(mi.bWidth, mi.bHeight);
-                generator->generate(mi, fmap.pixels.get());
-                auto bitmap = fmap.map<RGBColor>([&mi](float i) { return i > mi.maxIter ?
-                                RGBColor{ 0,0,0 } :
-                                RGBColor{ uint8_t(cos(i * 0.015f) * 127 + 127),
-                                          uint8_t(sin(i * 0.01f) * 127 + 127),
-                                          uint8_t(i) }; });//uint8_t(::sin(i * 0.01f) * 100 + 100), uint8_t(i) }; });
-
-                Texture* tex = new Texture(bitmap, context);
-                //Texture* tex = 0;
-                emit updated(tex);
-            } while(hasToCalc.exchange(false));
-        });
-    }
-    else {*/
-
-    //std::unique_lock<std::mutex> lock(mut, std::try_to_lock);
-    toCalc = mi;
-    hasToCalc.exchange(true);
-    condVar.notify_one();
-    //}
-}
-
-
 MandelWidget::MandelWidget(mnd::MandelContext& ctxt, QWidget* parent) :
     QOpenGLWidget{ parent },
-    mndContext{ ctxt },
-    mv{ ctxt.getDefaultGenerator(), gradient, this }
+    mndContext{ ctxt }
 {
     this->setContentsMargins(0, 0, 0, 0);
     this->setSizePolicy(QSizePolicy::Expanding,
         QSizePolicy::Expanding);
-    QObject::connect(&mv, &MandelView::updated, this, &MandelWidget::viewUpdated, Qt::AutoConnection);
-    QObject::connect(this, &MandelWidget::needsUpdate, &mv, &MandelView::adaptViewport, Qt::DirectConnection);
-
-    /*if (!ctxt.getDevices().empty()) {
-        if (auto* gen = ctxt.getDevices()[0].getGeneratorDouble(); gen) {
-            mv.setGenerator(*gen);
-        }
-    }*/
+    qRegisterMetaType<GridIndex>("GridIndex");
 }
 
 
@@ -505,22 +481,8 @@ void MandelWidget::initializeGL(void)
 
     //glShadeModel(GL_SMOOTH);
 
-    /*CpuGenerator<double> cpg;
-    MandelInfo mi;
-    mi.bWidth = this->width();//ql.geometry().width();
-    mi.bHeight = this->height(); //ql.geometry().height();
-    mi.maxIter = 250;
-    mi.view = viewport;
-    auto bitmap = cpg.generate(mi);*/
-    Bitmap<RGBColor> bitmap(1, 1);
-    bitmap.get(0, 0) = RGBColor{50, 50, 50};
     v = nullptr;
-
-    tex = std::make_unique<Texture>(bitmap, context());
-    mv.start();
     requestRecalc();
-
-
 }
 
 
@@ -530,31 +492,12 @@ void MandelWidget::paintGL(void)
         v = std::make_unique<MandelV>(mndContext, gradient, maxIterations);
         QObject::connect(v.get(), &MandelV::redrawRequested, this, static_cast<void(QOpenGLWidget::*)(void)>(&QOpenGLWidget::update));
     }
-    /*if (!initialized) {
-        emit needsUpdate(viewport);
-        initialized = true;
-    }*/
 
     int width = this->width();
     int height = this->height();
     v->width = width;
     v->height = height;
 
-    //v = std::make_unique<MandelV>(context());
-    /*CpuGenerator<double> cpg;
-    ClGenerator clg;
-    MandelGenerator& mg = cpg;
-    MandelInfo mi;
-    mi.bWidth = width;
-    mi.bHeight = height;
-    mi.maxIter = 5000;
-    mi.view = viewport;*/
-    //auto bitmap = mg.generate(mi);
-    /*Bitmap<RGBColor> bitmap(1000, 1000);
-    for (int i = 0; i < 1000 * 1000; i++)
-        bitmap.pixels[i] = RGBColor{5, uint8_t((i % 1000) ^ (i / 1000)), 50};
-    tex = std::make_unique<Texture>(bitmap);*/
-
     glViewport(0, 0, width, height);
 
     glMatrixMode(GL_PROJECTION);
@@ -568,15 +511,11 @@ void MandelWidget::paintGL(void)
 
     glClear(GL_COLOR_BUFFER_BIT);
     glLoadIdentity();
-    //tex->drawRect(0, 0, width, height);
 
-    //v->empty = std::move(*tex)
-    //v->empty.bind();
     v->paint(this->viewport);
-    //*tex = std::move(v->empty);
 
-    //if (dragging)
-    //    drawRubberband();
+    if (rubberbanding)
+        drawRubberband();
 }
 
 
@@ -589,6 +528,17 @@ void MandelWidget::drawRubberband(void)
     glVertex2d(rubberband.right(), rubberband.bottom());
     glVertex2d(rubberband.x(), rubberband.bottom());
     glEnd();
+
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glColor4f(0.1f, 0.9f, 0.1f, 0.2f);
+    glBegin(GL_TRIANGLE_FAN);
+    glVertex2d(rubberband.x(), rubberband.y());
+    glVertex2d(rubberband.right(), rubberband.y());
+    glVertex2d(rubberband.right(), rubberband.bottom());
+    glVertex2d(rubberband.x(), rubberband.bottom());
+    glEnd();
+    glDisable(GL_BLEND);
 }
 
 
@@ -652,7 +602,7 @@ void MandelWidget::resizeEvent(QResizeEvent* re)
         v->width = this->width();
         v->height = this->height();
     }
-    printf("resized\n");
+    //printf("resized\n");
     requestRecalc();
     //redraw();
 }
@@ -661,26 +611,38 @@ void MandelWidget::resizeEvent(QResizeEvent* re)
 void MandelWidget::mousePressEvent(QMouseEvent* me)
 {
     QOpenGLWidget::mousePressEvent(me);
-    rubberband.setCoords(me->x(), me->y(), 0, 0);
-    dragging = true;
-    dragX = me->x();
-    dragY = me->y();
+    if (me->button() == Qt::RightButton) {
+        rubberbanding = true;
+        rubberband.setCoords(me->x(), me->y(), 0, 0);
+        //emit repaint();
+        me->accept();
+    }
+    else if (me->button() == Qt::LeftButton) {
+        dragging = true;
+        dragX = me->x();
+        dragY = me->y();
+        me->accept();
+    }
 }
 
 
 void MandelWidget::mouseMoveEvent(QMouseEvent* me)
 {
     QOpenGLWidget::mouseMoveEvent(me);
-    /*QRectF& rect = rubberband;
-    float aspect = float(geometry().width()) / geometry().height();
-    rect.setBottomRight(QPoint(me->x(), me->y()));
-    if (rect.width() > rect.height() * aspect)
-        rect.setHeight(rect.width() / aspect);
-    else
-        rect.setWidth(rect.height() * aspect);*/
-    if (dragging) {
-        float deltaX = me->x() - dragX;
-        float deltaY = me->y() - dragY;
+    if (rubberbanding) {
+        QRectF& rect = rubberband;
+        double aspect = double(geometry().width()) / geometry().height();
+        rect.setBottomRight(QPoint(me->x(), me->y()));
+        if (rect.width() > rect.height() * aspect)
+            rect.setHeight(rect.width() / aspect);
+        else
+            rect.setWidth(rect.height() * aspect);
+
+        emit repaint();
+    }
+    else if (dragging) {
+        double deltaX = me->x() - dragX;
+        double deltaY = me->y() - dragY;
 
         this->viewport.x -= deltaX * viewport.width / this->width();
         this->viewport.y -= deltaY * viewport.height / this->height();
@@ -688,21 +650,27 @@ void MandelWidget::mouseMoveEvent(QMouseEvent* me)
 
         emit repaint();
     }
+    me->accept();
 }
 
 
 void MandelWidget::mouseReleaseEvent(QMouseEvent* me)
 {
     QOpenGLWidget::mouseReleaseEvent(me);
-    /*QRect rect = rubberband.toRect();
-    QRect full = this->geometry();
-
-    viewport.x += double(rect.left()) * viewport.width / full.width();
-    viewport.y += double(rect.top()) * viewport.height / full.height();
-    viewport.width *= double(rect.width()) / full.width();
-    viewport.height *= double(rect.height()) / full.height();
-    viewport.normalize();*/
+    if (rubberbanding) {
+        QRect rect = rubberband.toRect();
+        QRect full = this->geometry();
+
+        viewport.x += double(rect.left()) * viewport.width / full.width();
+        viewport.y += double(rect.top()) * viewport.height / full.height();
+        viewport.width *= double(rect.width()) / full.width();
+        viewport.height *= double(rect.height()) / full.height();
+        viewport.normalize();
+        requestRecalc();
+        rubberbanding = false;
+    }
     dragging = false;
+
     //requestRecalc();
 }
 
@@ -712,7 +680,7 @@ void MandelWidget::wheelEvent(QWheelEvent* we)
     float x = float(we->x()) / this->width();
     float y = float(we->y()) / this->height();
     float scale = ::pow(0.9975, we->angleDelta().y());
-    printf("scale: %f\n", double(scale));
+    //printf("scale: %f\n", double(scale));
     zoom(scale, x, y);
     we->accept();
 }

+ 67 - 77
MandelWidget.h

@@ -2,38 +2,34 @@
 
 #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 <QMouseEvent>
+#include <QOpenGLContext>
+#include <QOpenGLFunctions>
+#include <QMutex>
+//#include <qopengl.h>
+//#include <qopenglfunctions.h>
+//#include <qopenglcontext.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;
+
+using GridIndex = long long;
+Q_DECLARE_METATYPE(GridIndex)
 
 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;
@@ -42,18 +38,31 @@ public:
     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);
 };
 
 
-struct MandelClip
+class TextureClip
 {
-    mnd::MandelViewport view;
+    std::shared_ptr<Texture> texture;
+    float tx, ty, tw, th;
+public:
+    inline TextureClip(std::shared_ptr<Texture> tex) :
+        texture{ std::move(tex) },
+        tx{ 0 }, ty{ 0 }, tw{ 1 }, th{ 1 }
+    {}
+
+    void drawRect(float x, float y, float width, float height);
+
+    TextureClip clip(float x, float y, float w, float h);
 };
 
+
 struct PairHash {
     template <typename T1, typename T2>
     std::size_t operator () (const std::pair<T1, T2>& p) const {
@@ -74,19 +83,35 @@ struct TripleHash {
 };
 
 
+struct GridElement
+{
+    bool enoughResolution;
+    TextureClip img;
+    inline GridElement(bool enoughResolution, TextureClip img) :
+        enoughResolution{ enoughResolution },
+        img{ std::move(img) }
+    {}
+};
+
+
+class MandelV;
+
+
 class TexGrid
 {
 public:
+    MandelV& owner;
+    int level;
     double dpp;
-    std::unordered_map<std::pair<int, int>, std::unique_ptr<Texture>, PairHash> cells;
+    std::unordered_map<std::pair<GridIndex, GridIndex>, std::unique_ptr<GridElement>, 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 TexGrid(MandelV& owner) : level{ 1.0 }, owner{ owner } {}
+    TexGrid(MandelV& owner, int level);
+
+    std::pair<GridIndex, GridIndex> getCellIndices(double x, double y);
+    std::pair<double, double> getPositions(GridIndex i, GridIndex j);
+    GridElement* getCell(GridIndex i, GridIndex j);
+    void setCell(GridIndex i, GridIndex j, std::unique_ptr<GridElement> tex);
 
     inline size_t countAllocatedCells(void) const { return cells.size(); }
     void clearCells(void);
@@ -102,13 +127,13 @@ public:
     int maxIter;
     TexGrid* grid;
     int level;
-    int i, j;
+    GridIndex i, j;
 
     inline Job(mnd::MandelContext& mndContext,
                Gradient& gradient,
                int maxIter,
                TexGrid* grid,
-               int level, int i, int j) :
+               int level, GridIndex i, GridIndex j) :
         mndContext{ mndContext },
         gradient{ gradient },
         maxIter{ maxIter },
@@ -119,37 +144,41 @@ public:
 
     void run() override;
 signals:
-    void done(int level, int i, int j, Bitmap<RGBColor>* bmp);
+    void done(int level, GridIndex i, GridIndex j, Bitmap<RGBColor>* bmp);
 };
 
 
 class Calcer : public QObject
 {
     Q_OBJECT
-    std::unordered_set<std::tuple<int, int, int>, TripleHash> jobs;
+    /// tuple contains level, i, j of the job
+    std::unordered_map<std::tuple<int, GridIndex, GridIndex>, Job*, TripleHash> jobs;
+    QMutex jobsMutex;
     mnd::MandelContext& mndContext;
     std::unique_ptr<QThreadPool> 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<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);
+    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<RGBColor>* bmp);
 signals:
-    void done(int level, int i, int j, Bitmap<RGBColor>* bmp);
+    void done(int level, GridIndex i, GridIndex j, Bitmap<RGBColor>* bmp);
 };
 
 
@@ -182,52 +211,20 @@ public:
     void clear(void);
 
     void garbageCollect(int level);
+    GridElement* searchAbove(int level, GridIndex i, GridIndex j, int recursionLevel);
+    std::unique_ptr<GridElement> searchUnder(int level, GridIndex i, GridIndex j, int recursionLevel);
     void paint(const mnd::MandelViewport& mvp);
 public slots:
-    void cellReady(int level, int i, int j, Bitmap<RGBColor>* bmp);
+    void cellReady(int level, GridIndex i, GridIndex 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;
@@ -236,25 +233,18 @@ private:
     int maxIterations = 2000;
 
 
+    volatile bool rubberbanding = false;
     QRectF rubberband;
-    bool dragging = false;
+    volatile 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;