Almond.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. #include "Almond.h"
  2. #include <QIntValidator>
  3. #include <QIcon>
  4. #include <QFileDialog>
  5. #include <QMessageBox>
  6. #include <QStatusBar>
  7. #include <QGradient>
  8. #include <QWindow>
  9. #include <QKeyEvent>
  10. #include "Serialize.h"
  11. #include <cmath>
  12. Almond::Almond(QWidget* parent) :
  13. QMainWindow{ parent, Qt::WindowFlags() },
  14. mandelContext{ mnd::initializeContext() }
  15. {
  16. ui.setupUi(this);
  17. fractalWidget = new FractalWidget(this);
  18. fractalWidget->setGenerator(&mandelContext.getDefaultGenerator());
  19. fractalWidget->setGradient(alm::Gradient::defaultGradient());
  20. fractalWidget->setSmoothColoring(ui.smooth->isChecked());
  21. QSizePolicy fsp{ QSizePolicy::Expanding, QSizePolicy::Expanding };
  22. fsp.setHorizontalStretch(2);
  23. fractalWidget->setSizePolicy(fsp);
  24. connect(fractalWidget, &FractalWidget::pointSelected, this, &Almond::pointSelected);
  25. customGeneratorDialog = std::make_unique<CustomGenerator>(mandelContext);
  26. customGenerator = nullptr;
  27. customViewSave = mnd::MandelViewport::centerView();
  28. on_maxIterations_editingFinished();
  29. currentView = MANDELBROT;
  30. mandelGenerator = &mandelContext.getDefaultGenerator();
  31. mandelViewSave = fractalWidget->getViewport();
  32. ui.mandel_container->addWidget(fractalWidget);
  33. //ui.mandel_container->addWidget(mw.get());
  34. ui.maxIterations->setValidator(new QIntValidator(1, 1000000000, this));
  35. ui.backgroundProgress->setEnabled(false);
  36. ui.cancelProgress->setEnabled(false);
  37. amw = new AlmondMenuWidget(this);
  38. amw->setMainMenu(ui.dockWidgetContents_2);
  39. eim = new ExportImageMenu();
  40. evm = new ExportVideoMenu();
  41. about = new About(this);
  42. gradientMenu = new GradientMenu();
  43. AlmondSubMenu* imageSm = amw->addSubMenu(eim);
  44. AlmondSubMenu* videoSm = amw->addSubMenu(evm);
  45. AlmondSubMenu* gradientSm = amw->addSubMenu(gradientMenu);
  46. AlmondSubMenu* aboutSm = amw->addSubMenu(about);
  47. ui.dockWidget_2->setWidget(amw);
  48. connect(amw, &AlmondMenuWidget::submenuCancel, [this] (int) { amw->showMainMenu(); });
  49. //connect(amw, &AlmondMenuWidget::submenuOK, this, &Almond::submenuOK);
  50. connect(imageSm, &AlmondSubMenu::accepted, this, &Almond::imageExportOk);
  51. connect(videoSm, &AlmondSubMenu::accepted, this, &Almond::videoExportOk);
  52. connect(gradientSm, &AlmondSubMenu::accepted, this, &Almond::gradientEditOk);
  53. connect(gradientSm, &AlmondSubMenu::cancelled, [this] () {
  54. fractalWidget->setGradient(gradientMenu->getGradientBefore());
  55. });
  56. connect(gradientMenu, &GradientMenu::gradientChanged, [this] () {
  57. fractalWidget->setGradient(gradientMenu->getGradient());
  58. });
  59. connect(aboutSm, &AlmondSubMenu::accepted, [] () {});
  60. connect(aboutSm, &AlmondSubMenu::cancelled, [] () {});
  61. fractalWidget->setGradient(gradientMenu->getGradient());
  62. /*QStatusBar* bar = new QStatusBar(this);
  63. bar->addWidget(new QLabel("ayay"));
  64. auto* p = new QPushButton("About");
  65. bar->addPermanentWidget(p);
  66. QObject::connect(p, &QPushButton::clicked, [this]() {
  67. toggleFullscreen();
  68. });
  69. bar->setFixedHeight(bar->sizeHint().height());
  70. //ui.mainContainer->addWidget(bar);
  71. this->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
  72. this->setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);*/
  73. installEventFilter(this);
  74. backgroundTasks.setMaxThreadCount(1);
  75. QIcon icon{ ":/icons/icon" };
  76. icon.addFile(":/icons/icon@2x");
  77. this->setWindowIcon(icon);
  78. // replace vertical layout with gridflowlayout
  79. /*GridFlowLayout* gfl = new GridFlowLayout(nullptr);
  80. //ui.horizontalLayout_4->addItem(gfl);
  81. for (int i = 0; i < ui.verticalLayout_right->count(); i++) {
  82. printf("%d: \n", i);
  83. gfl->addItem(ui.verticalLayout_right->takeAt(i));
  84. }
  85. ui.verticalLayout_right->setEnabled(false);
  86. delete ui.dockWidgetContents_2->layout();
  87. ui.dockWidgetContents_2->setLayout(gfl);*/
  88. }
  89. Almond::~Almond(void)
  90. {
  91. }
  92. void Almond::submitBackgroundTask(BackgroundTask* task)
  93. {
  94. QObject::connect(task, &BackgroundTask::finished, this, &Almond::backgroundTaskFinished);
  95. QObject::connect(task, &BackgroundTask::progress, this, &Almond::backgroundTaskProgress);
  96. int running = backgroundTasks.activeThreadCount();
  97. backgroundTasks.start(task);
  98. if (running == 0) {
  99. ui.backgroundProgress->setRange(0, 0);
  100. ui.backgroundProgress->setFormat("");
  101. ui.backgroundProgress->setEnabled(true);
  102. ui.cancelProgress->setEnabled(true);
  103. }
  104. }
  105. void Almond::stopBackgroundTask(void)
  106. {
  107. stoppingBackgroundTasks = true;
  108. }
  109. void Almond::closeEvent(QCloseEvent* ce)
  110. {
  111. Q_UNUSED(ce);
  112. if (!this->backgroundTasks.waitForDone(1)) {
  113. int answer = QMessageBox::question(this, tr("Running Tasks"),
  114. tr("There are unfinished background tasks. Do you really want to quit?"),
  115. QMessageBox::Yes | QMessageBox::No);
  116. if (answer == QMessageBox::Yes) {
  117. ce->accept();
  118. }
  119. else {
  120. ce->ignore();
  121. }
  122. }
  123. }
  124. bool Almond::eventFilter(QObject *target, QEvent *event)
  125. {
  126. if (event->type() == QEvent::KeyPress) {
  127. QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
  128. if (keyEvent->key() == Qt::Key_F11) {
  129. emit toggleFullscreen();
  130. }
  131. }
  132. return QObject::eventFilter(target, event);
  133. }
  134. void Almond::submenuOK(int smIndex)
  135. {
  136. switch(smIndex) {
  137. case 0:
  138. emit imageExportOk();
  139. break;
  140. case 1:
  141. emit videoExportOk();
  142. break;
  143. }
  144. }
  145. void Almond::imageExportOk(void)
  146. {
  147. mnd::MandelInfo mi;
  148. mi.maxIter = eim->getMaxIterations();
  149. mi.view = fractalWidget->getViewport();
  150. mi.bWidth = eim->getWidth();
  151. mi.bHeight = eim->getHeight();
  152. mi.view.adjustAspectRatio(mi.bWidth, mi.bHeight);
  153. mi.smooth = true;
  154. if (currentView == JULIA) {
  155. mi.julia = fractalWidget->getMandelInfo().julia;
  156. mi.juliaX = fractalWidget->getMandelInfo().juliaX;
  157. mi.juliaY = fractalWidget->getMandelInfo().juliaY;
  158. }
  159. mnd::MandelGenerator* currentGenerator = fractalWidget->getGenerator();
  160. if (currentGenerator == nullptr)
  161. currentGenerator = &mandelContext.getDefaultGenerator();
  162. alm::ImageExportInfo iei;
  163. iei.drawInfo = mi;
  164. iei.generator = currentGenerator;
  165. iei.gradient = fractalWidget->getGradient();
  166. iei.path = eim->getPath().toStdString();
  167. iei.options.jpegQuality = 95;
  168. submitBackgroundTask(new ImageExportTask(iei, [this] () { return stoppingBackgroundTasks; }));
  169. amw->showMainMenu();
  170. }
  171. void Almond::videoExportOk(void)
  172. {
  173. ExportVideoInfo evi = evm->getInfo();
  174. evi.gradient = fractalWidget->getGradient();
  175. evi.mi.smooth = fractalWidget->getMandelInfo().smooth;
  176. if (currentView == JULIA) {
  177. evi.mi.julia = fractalWidget->getMandelInfo().julia;
  178. evi.mi.juliaX = fractalWidget->getMandelInfo().juliaX;
  179. evi.mi.juliaY = fractalWidget->getMandelInfo().juliaY;
  180. }
  181. if (evi.path == "") {
  182. QMessageBox errMsg = QMessageBox(QMessageBox::Icon::Critical, "Error", "No path specified.");
  183. errMsg.exec();
  184. }
  185. else {
  186. MandelVideoGenerator mvg(evi);
  187. mnd::MandelGenerator& g = *fractalWidget->getGenerator();
  188. //printf("wii: %ld\n", evi.mi.bWidth);
  189. fflush(stdout);
  190. submitBackgroundTask(new VideoExportTask(std::move(mvg), g));
  191. amw->showMainMenu();
  192. }
  193. }
  194. void Almond::gradientEditOk(void)
  195. {
  196. fractalWidget->setGradient(gradientMenu->getGradient());
  197. amw->showMainMenu();
  198. }
  199. void Almond::toggleFullscreen(void)
  200. {
  201. if (fullscreenMode) {
  202. auto* m = this->takeCentralWidget();
  203. ui.mandel_container->addWidget(m);
  204. ui.mainContainer->setStretch(0, 2);
  205. ui.mainContainer->setStretch(1, 2);
  206. this->setCentralWidget(cw);
  207. if (maximizedBeforeFullscreen)
  208. this->showMaximized();
  209. else
  210. this->showNormal();
  211. fullscreenMode = false;
  212. }
  213. else {
  214. cw = this->takeCentralWidget();
  215. this->setCentralWidget(fractalWidget);
  216. maximizedBeforeFullscreen = this->isMaximized();
  217. emit this->showFullScreen();
  218. fullscreenMode = true;
  219. }
  220. }
  221. void Almond::backgroundTaskFinished(bool succ, QString message)
  222. {
  223. if (succ) {
  224. QMessageBox info = QMessageBox(QMessageBox::Icon::Information, "Task Finished", message);
  225. //info->setParent(this);
  226. emit info.exec();
  227. }
  228. else {
  229. QMessageBox info = QMessageBox(QMessageBox::Icon::Critical, "Task Failed", message);
  230. //info->setParent(this);
  231. emit info.exec();
  232. }
  233. ui.backgroundProgress->setFormat(tr("Export Progress"));
  234. ui.backgroundProgress->setEnabled(false);
  235. ui.backgroundProgress->setRange(0, 100);
  236. ui.backgroundProgress->setValue(0);
  237. ui.cancelProgress->setEnabled(false);
  238. stoppingBackgroundTasks = false;
  239. }
  240. void Almond::backgroundTaskProgress(float percentage)
  241. {
  242. QObject* task = QObject::sender();
  243. if (auto* bt = qobject_cast<BackgroundTask*>(task)) {
  244. ui.backgroundProgress->setFormat(QString::fromStdString(bt->getShortDescription() + ": %p%"));
  245. }
  246. if (percentage > 0) {
  247. ui.backgroundProgress->setRange(0, 100);
  248. ui.backgroundProgress->setValue(percentage);
  249. }
  250. else {
  251. ui.backgroundProgress->reset();
  252. ui.backgroundProgress->setRange(0, 0);
  253. ui.backgroundProgress->setValue(-1);
  254. }
  255. }
  256. void Almond::on_zoom_out_clicked()
  257. {
  258. fractalWidget->zoom(2);
  259. }
  260. void Almond::on_zoom_in_clicked()
  261. {
  262. fractalWidget->zoom(0.5);
  263. }
  264. void Almond::on_maxIterations_editingFinished()
  265. {
  266. QString text = ui.maxIterations->text();
  267. int maxIter = text.toInt();
  268. fractalWidget->setMaxIterations(maxIter);
  269. }
  270. void Almond::on_chooseGradient_clicked()
  271. {
  272. this->gradientMenu->setGradient(fractalWidget->getGradient());
  273. emit this->amw->showSubMenu(2);
  274. }
  275. void Almond::on_exportVideo_clicked()
  276. {
  277. evm->setEndViewport(fractalWidget->getViewport());
  278. emit this->amw->showSubMenu(1);
  279. }
  280. void Almond::on_smooth_stateChanged(int checked)
  281. {
  282. fractalWidget->setSmoothColoring(checked != Qt::Unchecked);
  283. }
  284. void Almond::on_exportImage_clicked()
  285. {
  286. this->amw->showSubMenu(0);
  287. return;
  288. }
  289. void Almond::on_resetZoom_clicked()
  290. {
  291. if (currentView == MANDELBROT) {
  292. fractalWidget->setViewport(mnd::MandelViewport::standardView());
  293. }
  294. else {
  295. fractalWidget->setViewport(mnd::MandelViewport::centerView());
  296. }
  297. }
  298. void Almond::on_displayInfo_stateChanged(int checked)
  299. {
  300. this->fractalWidget->setDisplayInfo(checked != Qt::Unchecked);
  301. }
  302. void Almond::on_chooseGenerator_clicked()
  303. {
  304. std::unique_ptr<ChooseGenerators> generatorsDialog;
  305. if (currentView == MANDELBROT || currentView == JULIA)
  306. generatorsDialog = std::make_unique<ChooseGenerators>(mandelContext, *mandelGenerator, *this);
  307. else if (currentView == CUSTOM)
  308. generatorsDialog = std::make_unique<ChooseGenerators>(mandelContext, this->currentCustom->gc, *customGenerator, *this);
  309. else
  310. return;
  311. auto response = generatorsDialog->exec();
  312. auto gen = generatorsDialog->extractChosenGenerator();
  313. if (gen) {
  314. if (currentView == MANDELBROT || currentView == JULIA) {
  315. mandelGenerator = gen.get();
  316. }
  317. else if (currentView == CUSTOM) {
  318. customGenerator = gen.get();
  319. }
  320. currentGenerator = gen.get();
  321. this->fractalWidget->setGenerator(currentGenerator);
  322. adjustedGenerators.push_back(std::move(gen));
  323. }
  324. else {
  325. //mandelGenerator = &mandelContext.getDefaultGenerator();
  326. }
  327. //this->currentView = MANDELBROT;
  328. //this->mw->getMandelInfo().julia = false;
  329. //printf("dialog executed\n"); fflush(stdout);
  330. }
  331. void Almond::pointSelected(mnd::Real x, mnd::Real y)
  332. {
  333. if (currentView != JULIA) {
  334. saveView();
  335. this->fractalWidget->setViewport(mnd::MandelViewport::centerView());
  336. this->fractalWidget->getMandelInfo().julia = true;
  337. this->fractalWidget->getMandelInfo().juliaX = x;
  338. this->fractalWidget->getMandelInfo().juliaY = y;
  339. this->fractalWidget->clearCells();
  340. }
  341. currentView = JULIA;
  342. }
  343. void Almond::on_wMandel_clicked()
  344. {
  345. }
  346. void Almond::saveView()
  347. {
  348. if (currentView == MANDELBROT)
  349. mandelViewSave = fractalWidget->getViewport();
  350. else if (currentView == CUSTOM)
  351. customViewSave = fractalWidget->getViewport();
  352. }
  353. void Almond::setViewType(ViewType v)
  354. {
  355. saveView();
  356. if (v == MANDELBROT) {
  357. currentGenerator = mandelGenerator;
  358. emit this->fractalWidget->stopSelectingPoint();
  359. this->fractalWidget->setViewport(mandelViewSave);
  360. this->fractalWidget->setGenerator(currentGenerator);
  361. this->fractalWidget->getMandelInfo().julia = false;
  362. this->fractalWidget->clearCells();
  363. currentView = MANDELBROT;
  364. }
  365. else if (v == CUSTOM) {
  366. if (customGenerator != nullptr) {
  367. currentGenerator = customGenerator;
  368. this->fractalWidget->setGenerator(currentGenerator);
  369. emit this->fractalWidget->stopSelectingPoint();
  370. this->fractalWidget->setViewport(customViewSave);
  371. this->fractalWidget->getMandelInfo().julia = false;
  372. this->fractalWidget->clearCells();
  373. currentView = CUSTOM;
  374. }
  375. else {
  376. setViewType(MANDELBROT);
  377. }
  378. }
  379. else if (v == JULIA) {
  380. if (currentView == MANDELBROT) {
  381. emit this->fractalWidget->selectJuliaPoint();
  382. }
  383. else {
  384. currentView = MANDELBROT;
  385. currentGenerator = mandelGenerator;
  386. this->fractalWidget->setGenerator(currentGenerator);
  387. this->fractalWidget->setViewport(mandelViewSave);
  388. this->fractalWidget->getMandelInfo().julia = false;
  389. this->fractalWidget->clearCells();
  390. emit this->fractalWidget->selectJuliaPoint();
  391. }
  392. }
  393. }
  394. void Almond::on_wMandel_toggled(bool checked)
  395. {
  396. if (checked)
  397. setViewType(MANDELBROT);
  398. }
  399. void Almond::on_radioButton_toggled(bool checked)
  400. {
  401. saveView();
  402. if (checked) {
  403. setViewType(JULIA);
  404. }
  405. }
  406. void Almond::on_radioButton_2_toggled(bool checked)
  407. {
  408. saveView();
  409. if (checked) {
  410. if (customGenerator == nullptr) {
  411. customGeneratorDialog->exec();
  412. if (auto* frac = customGeneratorDialog->getLastCompiled()) {
  413. customGenerator = frac->gc.adaptiveGenerator.get();
  414. customGenerators.push_back(std::make_unique<FractalDef>(std::move(*frac)));
  415. currentCustom = customGenerators[customGenerators.size() - 1].get();
  416. }
  417. }
  418. setViewType(CUSTOM);
  419. }
  420. }
  421. void Almond::on_createCustom_clicked()
  422. {
  423. auto response = customGeneratorDialog->exec();
  424. if (response != 1)
  425. return;
  426. if (auto* frac = customGeneratorDialog->getLastCompiled()) {
  427. customGenerator = frac->gc.adaptiveGenerator.get();
  428. customGenerators.push_back(std::make_unique<FractalDef>(std::move(*frac)));
  429. currentCustom = customGenerators[customGenerators.size() - 1].get();
  430. this->ui.radioButton_2->setChecked(true);
  431. setViewType(CUSTOM);
  432. }
  433. }
  434. void Almond::on_cancelProgress_clicked()
  435. {
  436. stopBackgroundTask();
  437. }
  438. void Almond::on_aboutBtn_clicked()
  439. {
  440. amw->showSubMenu(3);
  441. }
  442. void Almond::on_loadBtn_clicked()
  443. {
  444. QString filename =
  445. QFileDialog::getOpenFileName(this, tr("Load View"), "", "Almond XML Files (*.xml)");
  446. QFile file{ filename };
  447. if (file.open(QFile::ReadOnly)) {
  448. alm::ImageView iv = alm::fromXml<alm::ImageView>(file.readAll().toStdString());
  449. this->ui.smooth->setCheckState(iv.view.smooth ? Qt::Checked : Qt::Unchecked);
  450. this->ui.maxIterations->setText(QString::number(iv.view.maxIter));
  451. this->fractalWidget->setMaxIterations(iv.view.maxIter);
  452. this->fractalWidget->setSmoothColoring(iv.view.smooth);
  453. this->fractalWidget->setGradient(iv.gradient);
  454. this->fractalWidget->setViewport(iv.view.view);
  455. }
  456. }
  457. void Almond::on_saveBtn_clicked()
  458. {
  459. QString filename =
  460. QFileDialog::getSaveFileName(this, tr("Save View"), "", "Almond XML Files (*.xml)");
  461. QFile file{ filename };
  462. if (file.open(QFile::WriteOnly)) {
  463. alm::ImageView iv;
  464. iv.view = fractalWidget->getMandelInfo();
  465. iv.view.bWidth = fractalWidget->getResolutionX();
  466. iv.view.bHeight = fractalWidget->getResolutionY();
  467. iv.gradient = fractalWidget->getGradient();
  468. const std::string& xml = alm::toXml(iv);
  469. file.write(xml.c_str());
  470. }
  471. }