MandelWidget.cpp 20 KB

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