Almond.cpp 16 KB

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