MandelWidget.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. #include "MandelWidget.h"
  2. #include <cmath>
  3. using namespace mnd;
  4. #include <QOpenGLVertexArrayObject>
  5. Texture::Texture(const Bitmap<RGBColor>& bitmap, GLint param)
  6. {
  7. glGenTextures(1, &id);
  8. glBindTexture(GL_TEXTURE_2D, id);
  9. long lineLength = (bitmap.width * 3 + 3) & ~3;
  10. unsigned char* pixels = new unsigned char[lineLength * bitmap.height];
  11. for (int i = 0; i < bitmap.width; i++) {
  12. for (int j = 0; j < bitmap.height; j++) {
  13. int index = i * 3 + j * lineLength;
  14. RGBColor c = bitmap.get(i, j);
  15. pixels[index] = c.r;
  16. pixels[index + 1] = c.g;
  17. pixels[index + 2] = c.b;
  18. }
  19. }
  20. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, int(bitmap.width), int(bitmap.height), 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
  21. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  22. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  23. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, param);
  24. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, param);
  25. }
  26. Texture::~Texture(void)
  27. {
  28. if (id != 0)
  29. glDeleteTextures(1, &id);
  30. }
  31. Texture::Texture(Texture&& other) :
  32. id{ other.id }
  33. {
  34. other.id = 0;
  35. }
  36. Texture& Texture::operator=(Texture&& other)
  37. {
  38. this->id = other.id;
  39. other.id = 0;
  40. return *this;
  41. }
  42. void Texture::bind(void) const
  43. {
  44. glBindTexture(GL_TEXTURE_2D, id);
  45. }
  46. void Texture::drawRect(float x, float y, float width, float height)
  47. {
  48. glColor3ub(255, 255, 255);
  49. glEnable(GL_TEXTURE_2D);
  50. bind();
  51. glBegin(GL_TRIANGLE_STRIP);
  52. glTexCoord2f(0, 0);
  53. glVertex2f(x, y);
  54. glTexCoord2f(1, 0);
  55. glVertex2f(x + width, y);
  56. glTexCoord2f(0, 1);
  57. glVertex2f(x, y + height);
  58. glTexCoord2f(1, 1);
  59. glVertex2f(x + width, y + height);
  60. glEnd();
  61. glDisable(GL_TEXTURE_2D);
  62. }
  63. void TextureClip::drawRect(float x, float y, float width, float height)
  64. {
  65. glColor3ub(255, 255, 255);
  66. glEnable(GL_TEXTURE_2D);
  67. glBindTexture(GL_TEXTURE_2D, texture->getId());
  68. glBegin(GL_TRIANGLE_STRIP);
  69. glTexCoord2f(tx, ty);
  70. glVertex2f(x, y);
  71. glTexCoord2f(tx + tw, ty);
  72. glVertex2f(x + width, y);
  73. glTexCoord2f(tx, ty + th);
  74. glVertex2f(x, y + height);
  75. glTexCoord2f(tx + tw, ty + th);
  76. glVertex2f(x + width, y + height);
  77. glEnd();
  78. glDisable(GL_TEXTURE_2D);
  79. }
  80. TextureClip TextureClip::clip(float x, float y, float w, float h)
  81. {
  82. TextureClip result(this->texture);
  83. result.tx = this->tx + x * this->tw;
  84. result.ty = this->ty + y * this->th;
  85. result.tw = this->tw * w;
  86. result.th = this->th * h;
  87. return result;
  88. }
  89. TexGrid::TexGrid(MandelV& owner, int level) :
  90. owner{ owner },
  91. level{ level },
  92. dpp{ owner.getDpp(level) }
  93. {
  94. }
  95. std::pair<GridIndex, GridIndex> TexGrid::getCellIndices(double x, double y)
  96. {
  97. return { ::floor(x / dpp / MandelV::chunkSize), ::floor(y / dpp / MandelV::chunkSize) };
  98. }
  99. std::pair<double, double> TexGrid::getPositions(GridIndex x, GridIndex y)
  100. {
  101. return { x * dpp * MandelV::chunkSize, y * dpp * MandelV::chunkSize };
  102. }
  103. GridElement* TexGrid::getCell(GridIndex i, GridIndex j)
  104. {
  105. auto cIt = cells.find({i, j});
  106. if (cIt != cells.end()) {
  107. return cIt->second.get();
  108. }
  109. else {
  110. return nullptr;
  111. }
  112. }
  113. void TexGrid::setCell(GridIndex i, GridIndex j, std::unique_ptr<GridElement> tex)
  114. {
  115. cells[{i, j}] = std::move(tex);
  116. }
  117. void TexGrid::clearCells(void)
  118. {
  119. cells.clear();
  120. }
  121. void Job::run(void)
  122. {
  123. auto [absX, absY] = grid->getPositions(i, j);
  124. double gw = grid->dpp * MandelV::chunkSize;
  125. Bitmap<float> f(MandelV::chunkSize, MandelV::chunkSize);
  126. mnd::MandelInfo mi;
  127. mi.view.x = absX;
  128. mi.view.y = absY;
  129. mi.view.width = mi.view.height = gw;
  130. mi.bWidth = mi.bHeight = MandelV::chunkSize;
  131. mi.maxIter = maxIter;
  132. mndContext.getDefaultGenerator().generate(mi, f.pixels.get());
  133. auto* rgb = new Bitmap<RGBColor>(f.map<RGBColor>([&mi, this](float i) {
  134. return i >= mi.maxIter ? RGBColor{ 0, 0, 0 } : gradient.get(i);
  135. }));
  136. emit done(level, i, j, rgb);
  137. }
  138. void Calcer::setMaxIter(int maxIter)
  139. {
  140. this->maxIter = maxIter;
  141. clearAll();
  142. }
  143. void Calcer::clearAll(void)
  144. {
  145. this->threadPool->clear();
  146. }
  147. void Calcer::calc(TexGrid& grid, int level, GridIndex i, GridIndex j, int priority)
  148. {
  149. jobsMutex.lock();
  150. if (jobs.find({ level, i, j }) == jobs.end()) {
  151. Job* job = new Job(mndContext, gradient, maxIter, &grid, level, i, j);
  152. connect(job, &Job::done, this, &Calcer::redirect);
  153. connect(job, &QObject::destroyed, this, [this, level, i, j] () { this->notFinished(level, i, j); });
  154. jobs.emplace(std::tuple{level, i, j}, job);
  155. threadPool->start(job, priority);
  156. }
  157. jobsMutex.unlock();
  158. }
  159. void Calcer::setCurrentLevel(int level)
  160. {
  161. if (this->currentLevel != level) {
  162. this->currentLevel = level;
  163. std::vector<QRunnable*> toCancel;
  164. jobsMutex.lock();
  165. for (auto&[tup, job] : jobs) {
  166. auto& [level, i, j] = tup;
  167. if(level != currentLevel) {
  168. toCancel.push_back(job);
  169. }
  170. }
  171. jobsMutex.unlock();
  172. for (auto* job : toCancel) {
  173. threadPool->cancel(job);
  174. }
  175. }
  176. }
  177. void Calcer::notFinished(int level, GridIndex i, GridIndex j)
  178. {
  179. jobsMutex.lock();
  180. jobs.erase({ level, i, j });
  181. jobsMutex.unlock();
  182. }
  183. void Calcer::redirect(int level, GridIndex i, GridIndex j, Bitmap<RGBColor>* bmp)
  184. {
  185. jobsMutex.lock();
  186. jobs.erase({ level, i, j });
  187. jobsMutex.unlock();
  188. emit done(level, i, j, bmp);
  189. }
  190. MandelV::MandelV(mnd::MandelContext& mndContext, Gradient& gradient, int maxIter) :
  191. mndContext{ mndContext },
  192. calcer{ mndContext, gradient, maxIter },
  193. gradient{ gradient },
  194. maxIter{ maxIter }
  195. {
  196. Bitmap<RGBColor> emp(8, 8);
  197. for(auto i = 0; i < emp.width; i++) {
  198. for(auto j = 0; j < emp.height; j++) {
  199. if((i + j) & 0x1) { // if i+j is odd
  200. emp.get(i, j) = RGBColor{ 255, 255, 255 };
  201. }
  202. else {
  203. emp.get(i, j) = RGBColor{ 120, 120, 120 };
  204. }
  205. }
  206. }
  207. empty = std::make_unique<Texture>(emp, GL_NEAREST);
  208. connect(&calcer, &Calcer::done, this, &MandelV::cellReady);
  209. }
  210. int MandelV::getLevel(double dpp) {
  211. return int(::log2(dpp / chunkSize));
  212. }
  213. double MandelV::getDpp(int level)
  214. {
  215. return ::pow(2, level) * chunkSize;
  216. }
  217. TexGrid& MandelV::getGrid(int level)
  218. {
  219. auto it = levels.find(level);
  220. if (it != levels.end()) {
  221. return it->second;
  222. }
  223. else {
  224. levels.insert(std::pair<int, TexGrid>{ level, TexGrid{ *this, level } });
  225. return levels.at(level);
  226. }
  227. }
  228. void MandelV::setMaxIter(int maxIter)
  229. {
  230. this->maxIter = maxIter;
  231. calcer.setMaxIter(maxIter);
  232. clear();
  233. emit redrawRequested();
  234. }
  235. void MandelV::clear(void)
  236. {
  237. for(auto& [level, grid] : this->levels) {
  238. grid.clearCells();
  239. }
  240. }
  241. void MandelV::garbageCollect(int level)
  242. {
  243. for(auto& [l, grid] : levels) {
  244. int dist = ::abs(l - level);
  245. if (dist > 20) {
  246. grid.clearCells();
  247. }
  248. else if (dist > 10) {
  249. if (grid.countAllocatedCells() > 50)
  250. grid.clearCells();
  251. }
  252. else if (dist > 3) {
  253. if (grid.countAllocatedCells() > 150)
  254. grid.clearCells();
  255. }
  256. else if (dist > 0) {
  257. if (grid.countAllocatedCells() > 350)
  258. grid.clearCells();
  259. }
  260. else {
  261. if (grid.countAllocatedCells() > 2500)
  262. grid.clearCells();
  263. }
  264. }
  265. }
  266. GridElement* MandelV::searchAbove(int level, GridIndex i, GridIndex j, int recursionLevel)
  267. {
  268. auto& grid = getGrid(level);
  269. auto& gridAbove = getGrid(level + 1);
  270. GridIndex ai = (i < 0 ? (i - 1) : i) / 2;
  271. GridIndex aj = (j < 0 ? (j - 1) : j) / 2;
  272. GridElement* above = gridAbove.getCell(ai, aj);
  273. if (above == nullptr && recursionLevel > 0) {
  274. auto abFound = searchAbove(level + 1, ai, aj, recursionLevel - 1);
  275. if (abFound)
  276. above = abFound;
  277. }
  278. if (above != nullptr) {
  279. auto newElement = std::make_unique<GridElement>(
  280. false, above->img.clip((i & 1) * 0.5f, (j & 1) * 0.5f, 0.5f, 0.5f)
  281. );
  282. GridElement* ret = newElement.get();
  283. grid.setCell(i, j, std::move(newElement));
  284. return ret;
  285. }
  286. else {
  287. return nullptr;
  288. }
  289. }
  290. std::unique_ptr<GridElement> MandelV::searchUnder(int level, GridIndex i, GridIndex j, int recursionLevel)
  291. {
  292. /*if (recursionLevel == 0)
  293. return nullptr;
  294. auto& gridUnder = getGrid(level - 1);
  295. int ai = i * 2;
  296. int aj = j * 2;
  297. GridElement* u00 = gridUnder.getCell(ai, aj);
  298. GridElement* u01 = gridUnder.getCell(ai, aj + 1);
  299. GridElement* u10 = gridUnder.getCell(ai + 1, aj);
  300. GridElement* u11 = gridUnder.getCell(ai + 1, aj + 1);
  301. if ( u00 != nullptr
  302. && u01 != nullptr
  303. && u10 != nullptr
  304. && u11 != nullptr) {
  305. GLuint FramebufferName = 0;
  306. auto newElement = std::make_unique<GridElement>(
  307. false, above->img.clip((i & 1) * 0.5f, (j & 1) * 0.5f, 0.5f, 0.5f)
  308. );
  309. return newElement;
  310. }
  311. else {
  312. return searchAbove(level - 1, ai, aj, recursionLevel - 1);
  313. }*/
  314. return nullptr;
  315. }
  316. void MandelV::paint(const mnd::MandelViewport& mvp)
  317. {
  318. double dpp = mvp.width / width;
  319. int level = getLevel(dpp) - 1;
  320. garbageCollect(level);
  321. emit calcer.setCurrentLevel(level);
  322. auto& grid = getGrid(level);
  323. double gw = getDpp(level) * chunkSize;
  324. double w = width * gw / mvp.width;
  325. //double h = height * gw / mvp.height;
  326. //printf("level: %d, dpp: %f, width: %f\n", level, dpp, w);
  327. auto [left, top] = grid.getCellIndices(mvp.x, mvp.y);
  328. auto [right, bottom] = grid.getCellIndices(mvp.right(), mvp.bottom());
  329. auto [realXLeft, realYTop] = grid.getPositions(left, top);
  330. realXLeft = (realXLeft - mvp.x) * width / mvp.width;
  331. realYTop = (realYTop - mvp.y) * height / mvp.height;
  332. for(GridIndex i = left; i <= right; i++) {
  333. for(GridIndex j = top; j <= bottom; j++) {
  334. double x = realXLeft + (i - left) * w;
  335. double y = realYTop + (j - top) * w;
  336. GridElement* t = grid.getCell(i, j);
  337. if (t == nullptr) {
  338. auto above = searchAbove(level, i, j, 2);
  339. if (above) {
  340. t = above;
  341. }
  342. }
  343. if (t != nullptr) {
  344. t->img.drawRect(x, y, w, w);
  345. /*glBegin(GL_LINE_LOOP);
  346. glVertex2f(x, y);
  347. glVertex2f(x + w, y);
  348. glVertex2f(x + w, y + w);
  349. glVertex2f(x, y + w);
  350. glEnd();*/
  351. if (!t->enoughResolution) {
  352. calcer.calc(grid, level, i, j, 100);
  353. }
  354. }
  355. else {
  356. calcer.calc(grid, level, i, j, 1000);
  357. this->empty->drawRect(x, y, w, w);
  358. }
  359. }
  360. }
  361. }
  362. void MandelV::cellReady(int level, GridIndex i, GridIndex j, Bitmap<RGBColor>* bmp)
  363. {
  364. this->getGrid(level).setCell(i, j,
  365. std::make_unique<GridElement>(true, TextureClip{ std::make_shared<Texture>(*bmp) }));
  366. delete bmp;
  367. emit redrawRequested();
  368. }
  369. MandelWidget::MandelWidget(mnd::MandelContext& ctxt, QWidget* parent) :
  370. QOpenGLWidget{ parent },
  371. mndContext{ ctxt }
  372. {
  373. this->setContentsMargins(0, 0, 0, 0);
  374. this->setSizePolicy(QSizePolicy::Expanding,
  375. QSizePolicy::Expanding);
  376. qRegisterMetaType<GridIndex>("GridIndex");
  377. }
  378. MandelWidget::~MandelWidget()
  379. {
  380. }
  381. void MandelWidget::initializeGL(void)
  382. {
  383. this->context()->functions()->glClearColor(0, 0, 0, 0);
  384. this->context()->makeCurrent(nullptr);
  385. glDisable(GL_DEPTH_TEST);
  386. // looks not even better
  387. //glDisable(GL_FRAMEBUFFER_SRGB);
  388. //glShadeModel(GL_SMOOTH);
  389. v = nullptr;
  390. requestRecalc();
  391. }
  392. void MandelWidget::paintGL(void)
  393. {
  394. if (v == nullptr) {
  395. v = std::make_unique<MandelV>(mndContext, gradient, maxIterations);
  396. QObject::connect(v.get(), &MandelV::redrawRequested, this, static_cast<void(QOpenGLWidget::*)(void)>(&QOpenGLWidget::update));
  397. }
  398. int width = this->width();
  399. int height = this->height();
  400. v->width = width;
  401. v->height = height;
  402. glViewport(0, 0, width, height);
  403. glMatrixMode(GL_PROJECTION);
  404. glLoadIdentity();
  405. #ifdef QT_OPENGL_ES_1
  406. glOrthof(0, width, height, 0, -1.0, 1.0);
  407. #else
  408. glOrtho(0, width, height, 0, -1.0, 1.0);
  409. #endif
  410. glMatrixMode(GL_MODELVIEW);
  411. glClear(GL_COLOR_BUFFER_BIT);
  412. glLoadIdentity();
  413. v->paint(this->viewport);
  414. if (rubberbanding)
  415. drawRubberband();
  416. }
  417. void MandelWidget::drawRubberband(void)
  418. {
  419. glColor3ub(10, 200, 10);
  420. glBegin(GL_LINE_LOOP);
  421. glVertex2d(rubberband.x(), rubberband.y());
  422. glVertex2d(rubberband.right(), rubberband.y());
  423. glVertex2d(rubberband.right(), rubberband.bottom());
  424. glVertex2d(rubberband.x(), rubberband.bottom());
  425. glEnd();
  426. glEnable(GL_BLEND);
  427. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  428. glColor4f(0.1f, 0.9f, 0.1f, 0.2f);
  429. glBegin(GL_TRIANGLE_FAN);
  430. glVertex2d(rubberband.x(), rubberband.y());
  431. glVertex2d(rubberband.right(), rubberband.y());
  432. glVertex2d(rubberband.right(), rubberband.bottom());
  433. glVertex2d(rubberband.x(), rubberband.bottom());
  434. glEnd();
  435. glDisable(GL_BLEND);
  436. }
  437. void MandelWidget::zoom(float scale, float x, float y)
  438. {
  439. viewport.zoom(scale, x, y);
  440. //viewport.zoomCenter(scale);
  441. requestRecalc();
  442. }
  443. void MandelWidget::setMaxIterations(int maxIter)
  444. {
  445. this->maxIterations = maxIter;
  446. if (v)
  447. v->setMaxIter(maxIter);
  448. requestRecalc();
  449. }
  450. void MandelWidget::requestRecalc()
  451. {
  452. //emit needsUpdate(MandelInfo{ viewport, this->width(), this->height(), maxIterations });
  453. this->update();
  454. }
  455. void MandelWidget::resizeGL(int width, int height)
  456. {
  457. //glViewport(0, 0, (GLint) width, (GLint) height);
  458. this->update();
  459. }
  460. /*void MandelWidget::redraw(void)
  461. {
  462. /*CpuGenerator<double> cpg;
  463. MandelInfo mi;
  464. mi.bWidth = this->geometry().width();//ql.geometry().width();
  465. mi.bHeight = this->geometry().height(); //ql.geometry().height();
  466. mi.maxIter = 250;
  467. mi.view = viewport;*/
  468. //update();
  469. //emit needsUpdate(viewport);
  470. //auto bitmap = cpg.generate(mi).map<uint32_t>([](RGBColor rgb) { return 255 << 24 | rgb.b << 16 | rgb.g << 8 | rgb.r; });
  471. //}
  472. void MandelWidget::resizeEvent(QResizeEvent* re)
  473. {
  474. QOpenGLWidget::resizeEvent(re);
  475. double aspect = double(geometry().width()) / geometry().height();
  476. //if (viewport.width > viewport.height * aspect)
  477. viewport.height = (viewport.width / aspect);
  478. //else
  479. // viewport.width = (viewport.height * aspect);
  480. if (v.get() != nullptr) {
  481. v->width = this->width();
  482. v->height = this->height();
  483. }
  484. //printf("resized\n");
  485. requestRecalc();
  486. //redraw();
  487. }
  488. void MandelWidget::mousePressEvent(QMouseEvent* me)
  489. {
  490. QOpenGLWidget::mousePressEvent(me);
  491. if (me->button() == Qt::RightButton) {
  492. rubberbanding = true;
  493. rubberband.setCoords(me->x(), me->y(), 0, 0);
  494. //emit repaint();
  495. me->accept();
  496. }
  497. else if (me->button() == Qt::LeftButton) {
  498. dragging = true;
  499. dragX = me->x();
  500. dragY = me->y();
  501. me->accept();
  502. }
  503. }
  504. void MandelWidget::mouseMoveEvent(QMouseEvent* me)
  505. {
  506. QOpenGLWidget::mouseMoveEvent(me);
  507. if (rubberbanding) {
  508. QRectF& rect = rubberband;
  509. double aspect = double(geometry().width()) / geometry().height();
  510. rect.setBottomRight(QPoint(me->x(), me->y()));
  511. if (rect.width() > rect.height() * aspect)
  512. rect.setHeight(rect.width() / aspect);
  513. else
  514. rect.setWidth(rect.height() * aspect);
  515. emit repaint();
  516. }
  517. else if (dragging) {
  518. double deltaX = me->x() - dragX;
  519. double deltaY = me->y() - dragY;
  520. this->viewport.x -= deltaX * viewport.width / this->width();
  521. this->viewport.y -= deltaY * viewport.height / this->height();
  522. dragX = me->x(); dragY = me->y();
  523. emit repaint();
  524. }
  525. me->accept();
  526. }
  527. void MandelWidget::mouseReleaseEvent(QMouseEvent* me)
  528. {
  529. QOpenGLWidget::mouseReleaseEvent(me);
  530. if (rubberbanding) {
  531. QRect rect = rubberband.toRect();
  532. QRect full = this->geometry();
  533. viewport.x += double(rect.left()) * viewport.width / full.width();
  534. viewport.y += double(rect.top()) * viewport.height / full.height();
  535. viewport.width *= double(rect.width()) / full.width();
  536. viewport.height *= double(rect.height()) / full.height();
  537. viewport.normalize();
  538. requestRecalc();
  539. rubberbanding = false;
  540. }
  541. dragging = false;
  542. //requestRecalc();
  543. }
  544. void MandelWidget::wheelEvent(QWheelEvent* we)
  545. {
  546. QOpenGLWidget::wheelEvent(we);
  547. float x = float(we->x()) / this->width();
  548. float y = float(we->y()) / this->height();
  549. float scale = ::pow(0.9975, we->angleDelta().y());
  550. //printf("scale: %f\n", double(scale));
  551. zoom(scale, x, y);
  552. we->accept();
  553. }
  554. void MandelWidget::viewUpdated(Bitmap<RGBColor>* bitmap)
  555. {
  556. if (bitmap != nullptr) {
  557. tex = std::make_unique<Texture>(*bitmap);
  558. delete bitmap;
  559. emit repaint();
  560. }
  561. }