MandelWidget.cpp 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109
  1. #include "MandelWidget.h"
  2. #include <cmath>
  3. #include <sstream>
  4. #include <QStyle>
  5. #include <QStyleOption>
  6. #include <QOpenGLShader>
  7. #include <QOpenGLFunctions_3_0>
  8. using namespace mnd;
  9. #include <cstdio>
  10. Texture::Texture(QOpenGLFunctions& gl, const Bitmap<RGBColor>& bitmap, GLint param) :
  11. gl{ gl }
  12. {
  13. gl.glGenTextures(1, &id);
  14. gl.glBindTexture(GL_TEXTURE_2D, id);
  15. //int lineLength = (bitmap.width * 3 + 3) & ~3;
  16. /*std::unique_ptr<unsigned char[]> pixels = std::make_unique<unsigned char[]>(lineLength * bitmap.height);
  17. for (int i = 0; i < bitmap.width; i++) {
  18. for (int j = 0; j < bitmap.height; j++) {
  19. int index = i * 3 + j * lineLength;
  20. RGBColor c = bitmap.get(i, j);
  21. pixels[index] = c.r;
  22. pixels[index + 1] = c.g;
  23. pixels[index + 2] = c.b;
  24. }
  25. }*/
  26. gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, int(bitmap.width), int(bitmap.height), 0, GL_RGB, GL_UNSIGNED_BYTE, reinterpret_cast<char*> (bitmap.pixels.get()));
  27. gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  28. gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  29. gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, param);
  30. gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, param);
  31. }
  32. Texture::~Texture(void)
  33. {
  34. if (id != 0)
  35. gl.glDeleteTextures(1, &id);
  36. }
  37. Texture::Texture(Texture&& other) :
  38. id{ other.id },
  39. gl{ other.gl }
  40. {
  41. other.id = 0;
  42. }
  43. void Texture::bind(void) const
  44. {
  45. gl.glBindTexture(GL_TEXTURE_2D, id);
  46. }
  47. void Texture::drawRect(QOpenGLShaderProgram* program,
  48. float x, float y, float width, float height,
  49. float tx, float ty, float tw, float th)
  50. {
  51. #if 1
  52. GLfloat const vertices[] = {
  53. x, y, 0.0f,
  54. x, y + height, 0.0f,
  55. x + width, y + height, 0.0f,
  56. x + width, y, 0.0f,
  57. };
  58. GLfloat const texCoords[] = {
  59. tx, ty,
  60. tx, ty + th,
  61. tx + tw, ty + th,
  62. tx + tw, ty,
  63. };
  64. QColor color(255, 255, 255);
  65. int vertexLoc = program->attributeLocation("vertex");
  66. int texCoordsLoc = program->attributeLocation("texCoord");
  67. int colorLocation = program->uniformLocation("color");
  68. int texLoc = program->uniformLocation("tex");
  69. program->enableAttributeArray(vertexLoc);
  70. program->enableAttributeArray(texCoordsLoc);
  71. program->setAttributeArray(vertexLoc, vertices, 3);
  72. program->setAttributeArray(texCoordsLoc, texCoords, 2);
  73. program->setUniformValue(colorLocation, color);
  74. auto& gl3 = *QOpenGLContext::currentContext()->functions();
  75. gl3.glUniform1i(texLoc, 0);
  76. gl3.glActiveTexture(GL_TEXTURE0 + 0);
  77. gl3.glBindTexture(GL_TEXTURE_2D, id);
  78. gl3.glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  79. program->disableAttributeArray(vertexLoc);
  80. program->disableAttributeArray(texCoordsLoc);
  81. #else
  82. gl.glColor3ub(255, 255, 255);
  83. gl.glEnable(GL_TEXTURE_2D);
  84. bind();
  85. gl.glBegin(GL_TRIANGLE_STRIP);
  86. gl.glTexCoord2f(0, 0);
  87. gl.glVertex2f(x, y);
  88. gl.glTexCoord2f(1, 0);
  89. gl.glVertex2f(x + width, y);
  90. gl.glTexCoord2f(0, 1);
  91. gl.glVertex2f(x, y + height);
  92. gl.glTexCoord2f(1, 1);
  93. gl.glVertex2f(x + width, y + height);
  94. gl.glEnd();
  95. gl.glDisable(GL_TEXTURE_2D);
  96. #endif
  97. }
  98. CellImage::~CellImage(void)
  99. {
  100. }
  101. TextureClip::~TextureClip(void)
  102. {
  103. }
  104. void TextureClip::drawRect(QOpenGLShaderProgram* program,
  105. float x, float y, float width, float height)
  106. {
  107. /*
  108. auto& gl = texture->gl;
  109. gl.glColor3ub(255, 255, 255);
  110. gl.glEnable(GL_TEXTURE_2D);
  111. gl.glBindTexture(GL_TEXTURE_2D, texture->getId());
  112. gl.glBegin(GL_TRIANGLE_STRIP);
  113. gl.glTexCoord2f(tx, ty);
  114. gl.glVertex2f(x, y);
  115. gl.glTexCoord2f(tx + tw, ty);
  116. gl.glVertex2f(x + width, y);
  117. gl.glTexCoord2f(tx, ty + th);
  118. gl.glVertex2f(x, y + height);
  119. gl.glTexCoord2f(tx + tw, ty + th);
  120. gl.glVertex2f(x + width, y + height);
  121. gl.glEnd();
  122. gl.glDisable(GL_TEXTURE_2D);*/
  123. texture->drawRect(program, x, y, width, height,
  124. tx, ty, tw, th);
  125. }
  126. TextureClip TextureClip::clip(float x, float y, float w, float h)
  127. {
  128. float tx = this->tx + x * this->tw;
  129. float ty = this->ty + y * this->th;
  130. float tw = this->tw * w;
  131. float th = this->th * h;
  132. return TextureClip{ this->texture, tx, ty, tw, th };
  133. }
  134. std::shared_ptr<CellImage> TextureClip::clip(short i, short j)
  135. {
  136. return std::make_shared<TextureClip>(clip(i * 0.5f, j * 0.5f, 0.5f, 0.5f));
  137. }
  138. int TextureClip::getRecalcPriority() const
  139. {
  140. return int(1.0f / tw);
  141. }
  142. QuadImage::~QuadImage(void)
  143. {
  144. }
  145. void QuadImage::drawRect(QOpenGLShaderProgram* program,
  146. float x, float y, float width, float height)
  147. {
  148. for (int i = 0; i < 2; i++) {
  149. for (int j = 0; j < 2; j++) {
  150. this->cells[i][j]->drawRect(program,
  151. x + i * 0.5f * width,
  152. y + j * 0.5f * height,
  153. width * 0.5f,
  154. height * 0.5f);
  155. }
  156. }
  157. }
  158. std::shared_ptr<CellImage> QuadImage::clip(short i, short j)
  159. {
  160. return cells[i][j];
  161. }
  162. int QuadImage::getRecalcPriority() const
  163. {
  164. return 1;
  165. }
  166. TexGrid::TexGrid(MandelView& owner, int level) :
  167. owner{ owner },
  168. level{ level },
  169. dpp{ owner.getDpp(level) }
  170. {
  171. }
  172. std::pair<GridIndex, GridIndex> TexGrid::getCellIndices(mnd::Real x, mnd::Real y)
  173. {
  174. return { GridIndex(mnd::floor(x / dpp / MandelView::chunkSize)), GridIndex(mnd::floor(y / dpp / MandelView::chunkSize)) };
  175. }
  176. std::pair<mnd::Real, mnd::Real> TexGrid::getPositions(GridIndex x, GridIndex y)
  177. {
  178. return { mnd::Real(x) * dpp * MandelView::chunkSize, mnd::Real(y) * dpp * MandelView::chunkSize };
  179. }
  180. GridElement* TexGrid::getCell(GridIndex i, GridIndex j)
  181. {
  182. auto cIt = cells.find({i, j});
  183. if (cIt != cells.end()) {
  184. return cIt->second.get();
  185. }
  186. else {
  187. return nullptr;
  188. }
  189. }
  190. void TexGrid::setCell(GridIndex i, GridIndex j, std::unique_ptr<GridElement> tex)
  191. {
  192. cells[{i, j}] = std::move(tex);
  193. }
  194. void TexGrid::clearCells(void)
  195. {
  196. cells.clear();
  197. }
  198. void TexGrid::clearUncleanCells(void)
  199. {
  200. for (auto it = cells.begin(); it != cells.end();) {
  201. if (it->second->img->getRecalcPriority() > 1)
  202. cells.erase(it++);
  203. else ++it;
  204. }
  205. }
  206. void Job::run(void)
  207. {
  208. auto [absX, absY] = grid->getPositions(i, j);
  209. mnd::Real gw = grid->dpp * MandelView::chunkSize;
  210. Bitmap<float> f(MandelView::chunkSize, MandelView::chunkSize);
  211. mnd::MandelInfo mi = owner.getMandelInfo();
  212. mi.view.x = absX;
  213. mi.view.y = absY;
  214. mi.view.width = mi.view.height = gw;
  215. mi.bWidth = mi.bHeight = MandelView::chunkSize;
  216. try {
  217. generator->generate(mi, f.pixels.get());
  218. auto* rgb = new Bitmap<RGBColor>(f.map<RGBColor>([&mi, this] (float i) {
  219. return i >= mi.maxIter ? RGBColor{ 0, 0, 0 } : gradient.get(i);
  220. }));
  221. emit done(level, i, j, calcState, rgb);
  222. }
  223. catch(std::exception& ex) {
  224. printf("wat: %s?!\n", ex.what()); fflush(stdout);
  225. exit(1);
  226. }
  227. catch(...) {
  228. printf("wat?!\n"); fflush(stdout);
  229. exit(1);
  230. }
  231. }
  232. Calcer::Calcer(mnd::MandelGenerator* generator, MandelWidget& owner) :
  233. jobsMutex{ QMutex::Recursive },
  234. generator{ generator },
  235. threadPool{ std::make_unique<QThreadPool>() },
  236. owner{ owner },
  237. gradient{ owner.getGradient() }
  238. {
  239. threadPool->setMaxThreadCount(1);
  240. }
  241. void Calcer::clearAll(void)
  242. {
  243. this->threadPool->clear();
  244. }
  245. void Calcer::calc(TexGrid& grid, int level, GridIndex i, GridIndex j, int priority)
  246. {
  247. jobsMutex.lock();
  248. if (jobs.find({ level, i, j }) == jobs.end()) {
  249. Job* job = new Job(generator, gradient, owner, &grid, level, i, j, calcState);
  250. connect(job, &Job::done, this, &Calcer::redirect);
  251. connect(job, &QObject::destroyed, this, [this, level, i, j] () { this->notFinished(level, i, j); });
  252. jobs.emplace(std::tuple{level, i, j}, job);
  253. threadPool->start(job, priority);
  254. }
  255. jobsMutex.unlock();
  256. }
  257. void Calcer::setCurrentLevel(int level)
  258. {
  259. if (this->currentLevel != level) {
  260. this->currentLevel = level;
  261. std::vector<QRunnable*> toCancel;
  262. jobsMutex.lock();
  263. for (auto&[tup, job] : jobs) {
  264. auto& [level, i, j] = tup;
  265. if(level != currentLevel) {
  266. toCancel.push_back(job);
  267. }
  268. }
  269. jobsMutex.unlock();
  270. for (auto* job : toCancel) {
  271. if (threadPool->tryTake(job)) {
  272. delete job;
  273. }
  274. }
  275. }
  276. }
  277. void Calcer::notFinished(int level, GridIndex i, GridIndex j)
  278. {
  279. jobsMutex.lock();
  280. jobs.erase({ level, i, j });
  281. jobsMutex.unlock();
  282. }
  283. void Calcer::redirect(int level, GridIndex i, GridIndex j, long calcState, Bitmap<RGBColor>* bmp)
  284. {
  285. jobsMutex.lock();
  286. jobs.erase({ level, i, j });
  287. jobsMutex.unlock();
  288. if (this->calcState == calcState) {
  289. emit done(level, i, j, bmp);
  290. }
  291. else {
  292. delete bmp;
  293. }
  294. }
  295. const int MandelView::chunkSize = 256;
  296. MandelView::MandelView(mnd::MandelGenerator* generator, MandelWidget& owner) :
  297. generator{ generator },
  298. calcer{ generator, owner },
  299. owner{ owner },
  300. width{ 0 },
  301. height{ 0 }
  302. {
  303. /*Bitmap<RGBColor> emp(8, 8);
  304. for(auto i = 0; i < emp.width; i++) {
  305. for(auto j = 0; j < emp.height; j++) {
  306. if((i + j) & 0x1) { // if i+j is odd
  307. emp.get(i, j) = RGBColor{ 255, 255, 255 };
  308. }
  309. else {
  310. emp.get(i, j) = RGBColor{ 120, 120, 120 };
  311. }
  312. }
  313. }*/
  314. Bitmap<RGBColor> emp(1, 1);
  315. emp.get(0, 0) = RGBColor{ 0, 0, 0 };
  316. auto& gl = *QOpenGLContext::currentContext()->functions();
  317. empty = std::make_unique<Texture>(gl, emp, GL_NEAREST);
  318. connect(&calcer, &Calcer::done, this, &MandelView::cellReady);
  319. }
  320. int MandelView::getLevel(mnd::Real dpp)
  321. {
  322. return int(mnd::log2(dpp / chunkSize));
  323. }
  324. mnd::Real MandelView::getDpp(int level)
  325. {
  326. return mnd::pow(mnd::Real(2), mnd::Real(level)) * chunkSize;
  327. }
  328. TexGrid& MandelView::getGrid(int level)
  329. {
  330. auto it = levels.find(level);
  331. if (it != levels.end()) {
  332. return it->second;
  333. }
  334. else {
  335. levels.insert(std::pair<int, TexGrid>{ level, TexGrid{ *this, level } });
  336. return levels.at(level);
  337. }
  338. }
  339. void MandelView::setGenerator(mnd::MandelGenerator* generator)
  340. {
  341. if (this->generator != generator) {
  342. this->generator = generator;
  343. calcer.setGenerator(generator);
  344. clearCells();
  345. emit redrawRequested();
  346. }
  347. }
  348. void MandelView::clearCells(void)
  349. {
  350. for(auto& [level, grid] : this->levels) {
  351. grid.clearCells();
  352. }
  353. }
  354. void MandelView::garbageCollect(int level, GridIndex /*i*/, GridIndex /*j*/)
  355. {
  356. for(auto& [l, grid] : levels) {
  357. int dist = ::abs(l - level);
  358. if (dist == 1) {
  359. grid.clearUncleanCells();
  360. }
  361. if (dist > 20) {
  362. grid.clearCells();
  363. }
  364. else if (dist > 10) {
  365. if (grid.countAllocatedCells() > 50)
  366. grid.clearCells();
  367. }
  368. else if (dist > 3) {
  369. if (grid.countAllocatedCells() > 150)
  370. grid.clearCells();
  371. }
  372. else if (dist > 0) {
  373. if (grid.countAllocatedCells() > 350)
  374. grid.clearCells();
  375. }
  376. else {
  377. if (grid.countAllocatedCells() > 2500)
  378. grid.clearCells();
  379. }
  380. }
  381. }
  382. GridElement* MandelView::searchAbove(int level, GridIndex i, GridIndex j, int recursionLevel)
  383. {
  384. auto& grid = getGrid(level);
  385. auto& gridAbove = getGrid(level + 1);
  386. GridIndex ai = (i < 0 ? (i - 1) : i) / 2;
  387. GridIndex aj = (j < 0 ? (j - 1) : j) / 2;
  388. GridElement* above = gridAbove.getCell(ai, aj);
  389. if (above == nullptr && recursionLevel > 0) {
  390. auto abFound = searchAbove(level + 1, ai, aj, recursionLevel - 1);
  391. if (abFound)
  392. above = abFound;
  393. }
  394. if (above != nullptr) {
  395. auto newElement = std::make_unique<GridElement>(
  396. false, above->img->clip(short(i & 1), short(j & 1))
  397. );
  398. GridElement* ret = newElement.get();
  399. grid.setCell(i, j, std::move(newElement));
  400. return ret;
  401. }
  402. else {
  403. return nullptr;
  404. }
  405. }
  406. GridElement* MandelView::searchUnder(int level, GridIndex i, GridIndex j, int recursionLevel)
  407. {
  408. if (recursionLevel == 0)
  409. return nullptr;
  410. auto& grid = getGrid(level);
  411. auto& gridUnder = getGrid(level - 1);
  412. GridIndex ai = i * 2;
  413. GridIndex aj = j * 2;
  414. GridElement* u00 = gridUnder.getCell(ai, aj);
  415. GridElement* u01 = gridUnder.getCell(ai, aj + 1);
  416. GridElement* u10 = gridUnder.getCell(ai + 1, aj);
  417. GridElement* u11 = gridUnder.getCell(ai + 1, aj + 1);
  418. /*if ( u00 == nullptr
  419. || u01 == nullptr
  420. || u10 == nullptr
  421. || u11 == nullptr) {
  422. auto abFound = searchUnder(level + 1, ai, aj, recursionLevel - 1);
  423. if (abFound)
  424. above = abFound;
  425. }*/
  426. if ( u00 != nullptr
  427. && u01 != nullptr
  428. && u10 != nullptr
  429. && u11 != nullptr) {
  430. auto newElement = std::make_unique<GridElement>(
  431. false, std::make_shared<QuadImage>(u00->img, u01->img, u10->img, u11->img)
  432. );
  433. GridElement* ret = newElement.get();
  434. grid.setCell(i, j, std::move(newElement));
  435. return ret;
  436. }
  437. else {
  438. return nullptr;
  439. }
  440. }
  441. void MandelView::paint(const mnd::MandelViewport& mvp)
  442. {
  443. mnd::Real dpp = mvp.width / width;
  444. int level = getLevel(dpp) - 1;
  445. auto& grid = getGrid(level);
  446. mnd::Real gw = getDpp(level) * chunkSize;
  447. auto [left, top] = grid.getCellIndices(mvp.x, mvp.y);
  448. auto [right, bottom] = grid.getCellIndices(mvp.right(), mvp.bottom());
  449. garbageCollect(level, (left + right) / 2, (top + bottom) / 2);
  450. emit calcer.setCurrentLevel(level);
  451. mnd::Real w = width * gw / mvp.width;
  452. auto [realXLeft, realYTop] = grid.getPositions(left, top);
  453. realXLeft = ((realXLeft - mvp.x) * mnd::Real(width)) / mvp.width;
  454. realYTop = ((realYTop - mvp.y) * mnd::Real(height)) / mvp.height;
  455. for(GridIndex i = left; i <= right; i++) {
  456. for(GridIndex j = top; j <= bottom; j++) {
  457. mnd::Real x = w * int(i - left) + realXLeft;
  458. mnd::Real y = w * int(j - top) + realYTop;
  459. GridElement* t = grid.getCell(i, j);
  460. if (t == nullptr) {
  461. auto under = searchUnder(level, i, j, 1);
  462. if (under) {
  463. t = under;
  464. }
  465. else {
  466. auto above = searchAbove(level, i, j, 3);
  467. if (above) {
  468. t = above;
  469. }
  470. }
  471. }
  472. if (t != nullptr) {
  473. t->img->drawRect(this->owner.program,float(x), float(y), float(w), float(w));
  474. /*glBegin(GL_LINE_LOOP);
  475. glVertex2f(float(x), float(y));
  476. glVertex2f(float(x) + float(w), float(y));
  477. glVertex2f(float(x) + float(w), float(y) + float(w));
  478. glVertex2f(float(x), float(y) + float(w));
  479. glEnd();*/
  480. if (!t->enoughResolution) {
  481. calcer.calc(grid, level, i, j, t->img->getRecalcPriority());
  482. }
  483. }
  484. else {
  485. calcer.calc(grid, level, i, j, 1000);
  486. this->empty->drawRect(this->owner.program,
  487. float(x), float(y), float(w), float(w));
  488. }
  489. }
  490. }
  491. }
  492. void MandelView::cellReady(int level, GridIndex i, GridIndex j, Bitmap<RGBColor>* bmp)
  493. {
  494. auto& gl = *QOpenGLContext::currentContext()->functions();
  495. this->getGrid(level).setCell(i, j,
  496. std::make_unique<GridElement>(true, std::make_shared<TextureClip>(std::make_shared<Texture>(gl, *bmp))));
  497. delete bmp;
  498. emit redrawRequested();
  499. }
  500. MandelWidget::MandelWidget(mnd::MandelContext& ctxt, mnd::MandelGenerator* generator, QWidget* parent) :
  501. QOpenGLWidget{ parent },
  502. mndContext{ ctxt },
  503. generator{ generator },
  504. gradient{ Gradient::defaultGradient() }
  505. {
  506. //this->setContentsMargins(0, 0, 0, 0);
  507. this->setSizePolicy(QSizePolicy::Expanding,
  508. QSizePolicy::Expanding);
  509. qRegisterMetaType<GridIndex>("GridIndex");
  510. this->format().setSwapInterval(1);
  511. }
  512. MandelWidget::~MandelWidget()
  513. {
  514. }
  515. void MandelWidget::setGradient(Gradient g)
  516. {
  517. this->gradient = std::move(g);
  518. if (mandelView) {
  519. mandelView->clearCells();
  520. mandelView->calcer.changeState();
  521. }
  522. emit update();
  523. }
  524. void MandelWidget::setSmoothColoring(bool sc)
  525. {
  526. if (sc != mandelInfo.smooth) {
  527. mandelInfo.smooth = sc;
  528. if (mandelView) {
  529. mandelView->clearCells();
  530. emit update();
  531. }
  532. }
  533. }
  534. void MandelWidget::setDisplayInfo(bool di)
  535. {
  536. if (di != this->displayInfo) {
  537. this->displayInfo = di;
  538. emit update();
  539. }
  540. }
  541. void MandelWidget::setMaxIterations(int maxIter)
  542. {
  543. if (mandelInfo.maxIter != maxIter) {
  544. mandelInfo.maxIter = maxIter;
  545. if (mandelView) {
  546. mandelView->clearCells();
  547. mandelView->calcer.clearAll();
  548. mandelView->calcer.changeState();
  549. }
  550. emit update();
  551. }
  552. }
  553. void MandelWidget::setJuliaPos(const mnd::Real& x, const mnd::Real& y)
  554. {
  555. mandelInfo.juliaX = x;
  556. mandelInfo.juliaY = y;
  557. if (mandelView)
  558. mandelView->calcer.changeState();
  559. emit update();
  560. }
  561. void MandelWidget::setGenerator(mnd::MandelGenerator* generator)
  562. {
  563. if (this->generator != generator) {
  564. this->generator = generator;
  565. if (mandelView)
  566. mandelView->setGenerator(generator);
  567. }
  568. }
  569. void MandelWidget::clearAll(void)
  570. {
  571. mandelView->clearCells();
  572. mandelView->calcer.clearAll();
  573. }
  574. void MandelWidget::initializeGL(void)
  575. {
  576. auto& gl = *this->context()->functions();
  577. gl.glClearColor(0, 0, 0, 0);
  578. gl.glDisable(GL_DEPTH_TEST);
  579. // looks not even better
  580. gl.glEnable(GL_FRAMEBUFFER_SRGB);
  581. //glShadeModel(GL_SMOOTH);
  582. program = new QOpenGLShaderProgram{ this->context() };
  583. bool vert = program->addShaderFromSourceCode(QOpenGLShader::Vertex,
  584. "attribute highp vec4 vertex;\n"
  585. "attribute highp vec2 texCoord;\n"
  586. "uniform highp mat4 matrix;\n"
  587. "varying highp vec2 texc;\n"
  588. "void main(void)\n"
  589. "{\n"
  590. " gl_Position = matrix * vertex;\n"
  591. " texc = texCoord;\n"
  592. "}");
  593. bool frag = program->addShaderFromSourceCode(QOpenGLShader::Fragment,
  594. "uniform mediump vec4 color;\n"
  595. "varying highp vec2 texc;\n"
  596. "uniform sampler2D tex;\n"
  597. "void main(void)\n"
  598. "{\n"
  599. " gl_FragColor = color * texture2D(tex, texc);\n"
  600. // " gl_FragColor.g = 0.3;\n"
  601. "}");
  602. //program.link();
  603. bool bound = program->bind();
  604. //gl3.glBindSampler(0, id);
  605. mandelView = nullptr;
  606. requestRecalc();
  607. }
  608. void MandelWidget::resizeGL(int w, int h)
  609. {
  610. auto& gl = *this->context()->functions();
  611. double aspect = double(w) / h;
  612. currentViewport.height = currentViewport.width / aspect;
  613. targetViewport = currentViewport;
  614. float pixelRatio = this->devicePixelRatioF();
  615. gl.glViewport(0, 0, w * pixelRatio, h * pixelRatio);
  616. if (mandelView.get() != nullptr) {
  617. mandelView->width = w;
  618. mandelView->height = h;
  619. //printf("resize: %d, %d\n", w, h);
  620. }
  621. }
  622. void MandelWidget::paintGL(void)
  623. {
  624. //auto& gl = *QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_0>();
  625. if (mandelView == nullptr) {
  626. mandelView = std::make_unique<MandelView>(generator, *this);
  627. QObject::connect(mandelView.get(), &MandelView::redrawRequested, this, static_cast<void(QOpenGLWidget::*)(void)>(&QOpenGLWidget::update));
  628. }
  629. //if (program)
  630. //program->bind();
  631. /*
  632. int width = this->width();
  633. int height = this->height();
  634. float pixelRatio = this->devicePixelRatioF();
  635. mandelView->width = width * pixelRatio;
  636. mandelView->height = height * pixelRatio;
  637. //glViewport(0, 0, width, height);
  638. gl.glMatrixMode(GL_PROJECTION);
  639. gl.glLoadIdentity();
  640. #ifdef QT_OPENGL_ES_1
  641. gl.glOrthof(0, width * pixelRatio, height * pixelRatio, 0, -1.0, 1.0);
  642. #else
  643. gl.glOrtho(0, double(width) * pixelRatio, double(height) * pixelRatio, 0, -1.0, 1.0);
  644. #endif
  645. gl.glMatrixMode(GL_MODELVIEW);
  646. gl.glLoadIdentity();
  647. gl.glClear(GL_COLOR_BUFFER_BIT);
  648. QPainter painter{ this };
  649. mandelView->paint(this->currentViewport, painter);
  650. if (rubberbanding)
  651. drawRubberband();
  652. if (displayInfo)
  653. drawInfo();
  654. if (selectingPoint)
  655. drawPoint();*/
  656. //QPainter painter{ this };
  657. QPainter painter{ this };
  658. painter.beginNativePainting();
  659. updateAnimations();
  660. mandelView->paint(this->currentViewport);
  661. static GLfloat const triangleVertices[] = {
  662. 0.0, 20, 0.0f,
  663. 49, 50, 0.0f,
  664. -60, 70, 0.0f
  665. };
  666. QColor color(0, 255, 0);
  667. QMatrix4x4 pmvMatrix;
  668. pmvMatrix.ortho(rect());
  669. int vertexLocation = program->attributeLocation("vertex");
  670. int matrixLocation = program->uniformLocation("matrix");
  671. int colorLocation = program->uniformLocation("color");
  672. program->enableAttributeArray(vertexLocation);
  673. program->setAttributeArray(vertexLocation, triangleVertices, 3);
  674. program->setUniformValue(matrixLocation, pmvMatrix);
  675. program->setUniformValue(colorLocation, color);
  676. auto& gl3 = *QOpenGLContext::currentContext()->functions();
  677. gl3.glDrawArrays(GL_TRIANGLES, 0, 3);
  678. program->disableAttributeArray(vertexLocation);
  679. painter.endNativePainting();
  680. if (rubberbanding)
  681. drawRubberband();
  682. if (displayInfo)
  683. drawInfo();
  684. if (selectingPoint)
  685. drawPoint();
  686. }
  687. void MandelWidget::updateAnimations(void)
  688. {
  689. if (mnd::abs(currentViewport.width / targetViewport.width - 1.0) < 1e-3
  690. && mnd::abs(currentViewport.height / targetViewport.height - 1.0) < 1e-3) {
  691. // animation finished
  692. currentViewport = targetViewport;
  693. }
  694. else {
  695. auto now = std::chrono::high_resolution_clock::now();
  696. auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(now - lastAnimUpdate).count();
  697. const mnd::Real factor = mnd::Real(::pow(0.97, millis));
  698. const mnd::Real one(1.0);
  699. currentViewport.x = currentViewport.x * factor + targetViewport.x * (one - factor);
  700. currentViewport.y = currentViewport.y * factor + targetViewport.y * (one - factor);
  701. currentViewport.width = currentViewport.width * factor + targetViewport.width * (one - factor);
  702. currentViewport.height = currentViewport.height * factor + targetViewport.height * (one - factor);
  703. lastAnimUpdate = now;
  704. emit update();
  705. }
  706. }
  707. void MandelWidget::drawRubberband(void)
  708. {
  709. QPainter rubberbandPainter{ this };
  710. rubberbandPainter.fillRect(rubberband, QColor{ 125, 140, 225, 120 });
  711. QPen pen{ QColor{ 100, 115, 200 } };
  712. pen.setWidth(2);
  713. rubberbandPainter.setPen(pen);
  714. rubberbandPainter.drawRect(rubberband);
  715. //QStyleOption so;
  716. //style()->drawControl(QStyle::CE_RubberBand, &so, &rubberbandPainter, this);
  717. }
  718. void MandelWidget::drawInfo(void)
  719. {
  720. const float DIST_FROM_BORDER = 15;
  721. float maxWidth = this->width() - 2 * DIST_FROM_BORDER;
  722. mnd::Real distPerPixel = currentViewport.width / this->width();
  723. float log10 = (mnd::convert<float>(mnd::log(distPerPixel)) + ::logf(maxWidth)) / ::logf(10);
  724. mnd::Real displayDist = mnd::pow(mnd::Real(10), ::floor(log10));
  725. float pixels = mnd::convert<float>(displayDist / distPerPixel);
  726. int factor = 1;
  727. for (int i = 9; i > 1; i--) {
  728. if (pixels * i < maxWidth) {
  729. factor *= i;
  730. pixels *= i;
  731. displayDist *= i;
  732. break;
  733. }
  734. }
  735. std::stringstream dis;
  736. if (::abs(log10) < 3) {
  737. dis << mnd::convert<float>(displayDist);
  738. }
  739. else {
  740. dis << factor << "e" << int(::floor(log10));
  741. }
  742. if (maxWidth > 400) {
  743. dis << "; per pixel: " << distPerPixel;
  744. }
  745. float lineY = this->height() - DIST_FROM_BORDER;
  746. float lineXEnd = DIST_FROM_BORDER + pixels;
  747. QPainter infoPainter{ this };
  748. infoPainter.setPen(Qt::white);
  749. infoPainter.setFont(QFont("Arial", 12));
  750. infoPainter.drawLine(QPointF{ DIST_FROM_BORDER, lineY }, QPointF{ lineXEnd, lineY });
  751. infoPainter.drawLine(QPointF{ DIST_FROM_BORDER, lineY }, QPointF{ DIST_FROM_BORDER, lineY - 5 });
  752. infoPainter.drawLine(QPointF{ lineXEnd, lineY }, QPointF{ lineXEnd, lineY - 5 });
  753. infoPainter.drawText(int(DIST_FROM_BORDER), int(lineY - 20), int(lineXEnd - DIST_FROM_BORDER), 20,
  754. Qt::AlignCenter, QString::fromStdString(dis.str()));
  755. infoPainter.end();
  756. }
  757. void MandelWidget::drawPoint(void)
  758. {
  759. QPainter pointPainter{ this };
  760. pointPainter.setPen(QColor{ 255, 255, 255 });
  761. pointPainter.drawLine(0, pointY, width(), pointY);
  762. pointPainter.drawLine(pointX, 0, pointX, height());
  763. /*glColor3ub(255, 255, 255);
  764. glBegin(GL_LINES);
  765. glVertex2f(0, pointY);
  766. glVertex2f(width(), pointY);
  767. glVertex2f(pointX, 0);
  768. glVertex2f(pointX, height());
  769. glEnd();*/
  770. }
  771. void MandelWidget::zoom(float scale, float x, float y)
  772. {
  773. targetViewport.zoom(scale, x, y);
  774. lastAnimUpdate = std::chrono::high_resolution_clock::now();
  775. //currentViewport.zoom(scale, x, y);
  776. requestRecalc();
  777. }
  778. void MandelWidget::setViewport(const mnd::MandelViewport& viewport)
  779. {
  780. targetViewport = viewport;
  781. targetViewport.adjustAspectRatio(this->width(), this->height());
  782. currentViewport = targetViewport;
  783. //lastAnimUpdate = std::chrono::high_resolution_clock::now();
  784. //currentViewport.zoom(scale, x, y);
  785. requestRecalc();
  786. }
  787. void MandelWidget::selectPoint(void)
  788. {
  789. this->selectingPoint = true;
  790. this->setMouseTracking(true);
  791. }
  792. void MandelWidget::stopSelectingPoint(void)
  793. {
  794. this->selectingPoint = false;
  795. this->setMouseTracking(false);
  796. }
  797. void MandelWidget::requestRecalc()
  798. {
  799. emit update();
  800. }
  801. void MandelWidget::resizeEvent(QResizeEvent* re)
  802. {
  803. QOpenGLWidget::resizeEvent(re);
  804. double aspect = double(geometry().width()) / geometry().height();
  805. currentViewport.height = currentViewport.width / aspect;
  806. targetViewport = currentViewport;
  807. if (mandelView.get() != nullptr) {
  808. mandelView->width = this->width();
  809. mandelView->height = this->height();
  810. }
  811. requestRecalc();
  812. }
  813. void MandelWidget::mousePressEvent(QMouseEvent* me)
  814. {
  815. QOpenGLWidget::mousePressEvent(me);
  816. if (me->button() == Qt::RightButton) {
  817. rubberbanding = true;
  818. rubberband.setCoords(me->x(), me->y(), me->x(), me->y());
  819. update();
  820. me->accept();
  821. }
  822. else if (me->button() == Qt::LeftButton) {
  823. dragging = true;
  824. dragX = me->x();
  825. dragY = me->y();
  826. me->accept();
  827. }
  828. }
  829. void MandelWidget::mouseMoveEvent(QMouseEvent* me)
  830. {
  831. QOpenGLWidget::mouseMoveEvent(me);
  832. if (rubberbanding) {
  833. QRectF& rect = rubberband;
  834. double aspect = double(geometry().width()) / geometry().height();
  835. rect.setBottomRight(QPoint(me->x(), me->y()));
  836. if (rect.width() > rect.height() * aspect)
  837. rect.setHeight(rect.width() / aspect);
  838. else
  839. rect.setWidth(rect.height() * aspect);
  840. update();
  841. }
  842. else if (selectingPoint) {
  843. pointX = me->x();
  844. pointY = me->y();
  845. update();
  846. }
  847. else if (dragging) {
  848. double deltaX = me->x() - dragX;
  849. double deltaY = me->y() - dragY;
  850. this->currentViewport.x -= deltaX * currentViewport.width / this->width();
  851. this->currentViewport.y -= deltaY * currentViewport.height / this->height();
  852. targetViewport = currentViewport;
  853. dragX = me->x(); dragY = me->y();
  854. update();
  855. }
  856. me->accept();
  857. }
  858. void MandelWidget::mouseReleaseEvent(QMouseEvent* me)
  859. {
  860. QOpenGLWidget::mouseReleaseEvent(me);
  861. if (rubberbanding) {
  862. QRect rect = rubberband.toRect();
  863. if(rect.width() != 0 && rect.height() != 0) {
  864. QRect full = this->geometry();
  865. targetViewport.x += mnd::Real(rect.left()) * targetViewport.width / full.width();
  866. targetViewport.y += mnd::Real(rect.top()) * targetViewport.height / full.height();
  867. targetViewport.width *= mnd::Real(rect.width()) / full.width();
  868. targetViewport.height *= mnd::Real(rect.height()) / full.height();
  869. targetViewport.normalize();
  870. currentViewport = targetViewport;
  871. }
  872. requestRecalc();
  873. rubberbanding = false;
  874. }
  875. else if (selectingPoint) {
  876. selectingPoint = false;
  877. this->setMouseTracking(false);
  878. mnd::Real x = currentViewport.x + currentViewport.width * mnd::convert<mnd::Real>(float(me->x()) / width());
  879. mnd::Real y = currentViewport.y + currentViewport.height * mnd::convert<mnd::Real>(float(me->y()) / height());
  880. emit pointSelected(x, y);
  881. update();
  882. }
  883. dragging = false;
  884. //requestRecalc();
  885. }
  886. void MandelWidget::wheelEvent(QWheelEvent* we)
  887. {
  888. QOpenGLWidget::wheelEvent(we);
  889. float x = float(we->x()) / this->width();
  890. float y = float(we->y()) / this->height();
  891. float scale = ::powf(0.9975f, we->angleDelta().y());
  892. zoom(scale, x, y);
  893. if (!we->pixelDelta().isNull())
  894. this->currentViewport = this->targetViewport;
  895. we->accept();
  896. }
  897. /*void MandelWidget::viewUpdated(Bitmap<RGBColor>* bitmap)
  898. {
  899. if (bitmap != nullptr) {
  900. delete bitmap;
  901. emit repaint();
  902. }
  903. }*/