1 /**
2  * Mandelbulber v2, a 3D fractal generator       ,=#MKNmMMKmmßMNWy,
3  *                                             ,B" ]L,,p%%%,,,§;, "K
4  * Copyright (C) 2014-21 Mandelbulber Team     §R-==%w["'~5]m%=L.=~5N
5  *                                        ,=mm=§M ]=4 yJKA"/-Nsaj  "Bw,==,,
6  * This file is part of Mandelbulber.    §R.r= jw",M  Km .mM  FW ",§=ß., ,TN
7  *                                     ,4R =%["w[N=7]J '"5=],""]]M,w,-; T=]M
8  * Mandelbulber is free software:     §R.ß~-Q/M=,=5"v"]=Qf,'§"M= =,M.§ Rz]M"Kw
9  * you can redistribute it and/or     §w "xDY.J ' -"m=====WeC=\ ""%""y=%"]"" §
10  * modify it under the terms of the    "§M=M =D=4"N #"%==A%p M§ M6  R' #"=~.4M
11  * GNU General Public License as        §W =, ][T"]C  §  § '§ e===~ U  !§[Z ]N
12  * published by the                    4M",,Jm=,"=e~  §  §  j]]""N  BmM"py=ßM
13  * Free Software Foundation,          ]§ T,M=& 'YmMMpM9MMM%=w=,,=MT]M m§;'§,
14  * either version 3 of the License,    TWw [.j"5=~N[=§%=%W,T ]R,"=="Y[LFT ]N
15  * or (at your option)                   TW=,-#"%=;[  =Q:["V""  ],,M.m == ]N
16  * any later version.                      J§"mr"] ,=,," =="""J]= M"M"]==ß"
17  *                                          §= "=C=4 §"eM "=B:m|4"]#F,§~
18  * Mandelbulber is distributed in            "9w=,,]w em%wJ '"~" ,=,,ß"
19  * the hope that it will be useful,                 . "K=  ,=RMMMßM"""
20  * but WITHOUT ANY WARRANTY;                            .'''
21  * without even the implied warranty
22  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23  *
24  * See the GNU General Public License for more details.
25  * You should have received a copy of the GNU General Public License
26  * along with Mandelbulber. If not, see <http://www.gnu.org/licenses/>.
27  *
28  * ###########################################################################
29  *
30  * Authors: Krzysztof Marczak (buddhi1980@gmail.com), Sebastian Jennen (jenzebas@gmail.com)
31  *
32  * cInterface class - operations connected directly with user interface
33  */
34 
35 #include "interface.hpp"
36 
37 #include "ui_render_window.h"
38 
39 #include "animation_flight.hpp"
40 #include "animation_keyframes.hpp"
41 #include "ao_modes.h"
42 #include "automated_widgets.hpp"
43 #include "calculate_distance.hpp"
44 #include "camera_movement_modes.h"
45 #include "camera_target.hpp"
46 #include "common_math.h"
47 #include "dof.hpp"
48 #include "error_message.hpp"
49 #include "fractparams.hpp"
50 #include "global_data.hpp"
51 #include "headless.h"
52 #include "initparameters.hpp"
53 #include "lights.hpp"
54 #include "material_item_model.h"
55 #include "my_ui_loader.h"
56 #include "netrender.hpp"
57 #include "nine_fractals.hpp"
58 #include "opencl_engine_render_dof.h"
59 #include "opencl_engine_render_fractal.h"
60 #include "opencl_engine_render_post_filter.h"
61 #include "opencl_engine_render_ssao.h"
62 #include "opencl_global.h"
63 #include "post_effect_hdr_blur.h"
64 #include "queue.hpp"
65 #include "random.hpp"
66 #include "randomizer_dialog.h"
67 #include "render_data.hpp"
68 #include "render_job.hpp"
69 #include "render_ssao.h"
70 #include "render_window.hpp"
71 #include "rendered_image_widget.hpp"
72 #include "rendering_configuration.hpp"
73 #include "settings.hpp"
74 #include "system_data.hpp"
75 #include "trace_behind.h"
76 #include "tree_string_list.h"
77 #include "undo.h"
78 #include "write_log.hpp"
79 
80 #include "qt/detached_window.h"
81 #include "qt/dock_effects.h"
82 #include "qt/material_editor.h"
83 #include "qt/my_group_box.h"
84 #include "qt/my_progress_bar.h"
85 #include "qt/player_widget.hpp"
86 #include "qt/preview_file_dialog.h"
87 #include "qt/settings_cleaner.h"
88 #include "qt/system_tray.hpp"
89 
90 // custom includes
91 #ifdef USE_GAMEPAD
92 #include <QtGamepad/qgamepadmanager.h>
93 #endif // USE_GAMEPAD
94 
95 cInterface *gMainInterface = nullptr;
96 
97 // constructor of interface (loading of ui files)
cInterface(QObject * parent)98 cInterface::cInterface(QObject *parent) : QObject(parent)
99 {
100 	mainWindow = nullptr;
101 	detachedWindow = nullptr;
102 	headless = nullptr;
103 	renderedImage = nullptr;
104 	imageSequencePlayer = nullptr;
105 	mainImage = nullptr;
106 	progressBar = nullptr;
107 	progressBarAnimation = nullptr;
108 	progressBarQueueImage = nullptr;
109 	progressBarQueueAnimation = nullptr;
110 	progressBarFrame = nullptr;
111 	progressBarLayout = nullptr;
112 	autoRefreshTimer = nullptr;
113 	stopRequestPulseTimer = nullptr;
114 	materialListModel = nullptr;
115 	materialEditor = nullptr;
116 	scrollAreaMaterialEditor = nullptr;
117 	systemTray = nullptr;
118 	stopRequest = false;
119 	repeatRequest = false;
120 	autoRefreshLastState = false;
121 	lockedDetailLevel = 1.0;
122 	lastSelectedMaterial = 1;
123 	numberOfStartedRenders = 0;
124 }
125 
~cInterface()126 cInterface::~cInterface()
127 {
128 	if (imageSequencePlayer) delete imageSequencePlayer;
129 	if (mainWindow) delete mainWindow;
130 }
131 
ShowUi()132 void cInterface::ShowUi()
133 {
134 	WriteLog("Prepare RenderWindow class", 2);
135 
136 	QFont font = gApplication->font();
137 	font.setPointSizeF(gPar->Get<int>("ui_font_size"));
138 	gApplication->setFont(font);
139 
140 	QFontMetrics fm(gApplication->font());
141 	int pixelFontSize = fm.height();
142 
143 	int thumbnailSize = (pixelFontSize * 8);
144 
145 	systemData.SetPreferredFontSize(pixelFontSize);
146 	systemData.SetPreferredThumbnailSize(thumbnailSize);
147 
148 	systemData.setPreferredCustomFormulaFontSize(gPar->Get<int>("custom_formula_font_size"));
149 
150 	mainWindow = new RenderWindow();
151 
152 	WriteLog("Restoring window geometry", 2);
153 
154 	if (gPar->Get<bool>("image_detached"))
155 	{
156 		DetachMainImageWidget();
157 		mainWindow->ui->actionDetach_image_from_main_window->setChecked(true);
158 	}
159 
160 	mainWindow->restoreGeometry(settings.value("mainWindowGeometry").toByteArray());
161 
162 	WriteLog("Restoring window state", 2);
163 
164 	if (!mainWindow->restoreState(settings.value("mainWindowState").toByteArray()))
165 	{
166 		mainWindow->tabifyDockWidget(
167 			mainWindow->ui->dockWidget_materialEditor, mainWindow->ui->dockWidget_effects);
168 		mainWindow->tabifyDockWidget(
169 			mainWindow->ui->dockWidget_effects, mainWindow->ui->dockWidget_image_adjustments);
170 		mainWindow->tabifyDockWidget(
171 			mainWindow->ui->dockWidget_image_adjustments, mainWindow->ui->dockWidget_rendering_engine);
172 		mainWindow->tabifyDockWidget(
173 			mainWindow->ui->dockWidget_rendering_engine, mainWindow->ui->dockWidget_objects);
174 		mainWindow->ui->dockWidget_animation->hide();
175 		mainWindow->ui->dockWidget_info->hide();
176 		mainWindow->ui->dockWidget_gamepad_dock->hide();
177 		mainWindow->ui->dockWidget_histogram->hide();
178 		mainWindow->ui->dockWidget_queue_dock->hide();
179 	}
180 
181 	mainWindow->setFont(font);
182 
183 #ifdef __APPLE__
184 	mainWindow->ui->actionAbout_Qt->setText(QApplication::translate("RenderWindow", "Info &Qt", 0));
185 	mainWindow->ui->actionAbout_Manual->setText(
186 		QApplication::translate("RenderWindow", "About &User Manual", 0));
187 	mainWindow->ui->actionAbout_Mandelbulber->setText(
188 		QApplication::translate("RenderWindow", "&Info Mandelbulber", 0));
189 	mainWindow->ui->actionAbout_ThirdParty->setText(
190 		QApplication::translate("RenderWindow", "Info &Third Party", 0));
191 #endif
192 
193 	WriteLog("mainWindow->show()", 2);
194 	mainWindow->show();
195 
196 	WriteLog("Prepare RenderedImage class", 2);
197 	renderedImage = new RenderedImage(mainWindow);
198 
199 	mainWindow->ui->scrollAreaLayoutRenderedImage->addWidget(renderedImage);
200 
201 	// setup main image
202 	WriteLog("Setup of main image", 2);
203 	mainImage.reset(new cImage(gPar->Get<int>("image_width"), gPar->Get<int>("image_height")));
204 	mainImage->CreatePreview(1.0, 800, 600, renderedImage);
205 	mainImage->CompileImage();
206 	mainImage->ConvertTo8bitChar();
207 	mainImage->UpdatePreview();
208 	mainImage->SetAsMainImage();
209 	renderedImage->setMinimumSize(
210 		int(mainImage->GetPreviewWidth()), int(mainImage->GetPreviewHeight()));
211 	renderedImage->AssignImage(mainImage);
212 	renderedImage->AssignParameters(gPar, gParFractal);
213 
214 	WriteLog("Prepare progress and status bar", 2);
215 	progressBarLayout = new QVBoxLayout();
216 	progressBarLayout->setSpacing(0);
217 	progressBarLayout->setContentsMargins(0, 0, 0, 0);
218 	progressBarFrame = new QFrame(mainWindow->ui->statusbar);
219 
220 	progressBarQueueImage = mainWindow->ui->widgetDockQueue->GetProgressBarImage();
221 	progressBarQueueAnimation = mainWindow->ui->widgetDockQueue->GetProgressBarAnimation();
222 
223 	progressBarAnimation = new MyProgressBar(progressBarFrame);
224 	progressBarAnimation->setMaximum(1000);
225 	progressBarAnimation->setAlignment(Qt::AlignCenter);
226 	progressBarAnimation->hide();
227 	progressBarLayout->addWidget(progressBarAnimation);
228 
229 	progressBar = new MyProgressBar(progressBarFrame);
230 	progressBar->setMaximum(1000);
231 	progressBar->setAlignment(Qt::AlignCenter);
232 	progressBarLayout->addWidget(progressBar);
233 
234 	QFrame *progressBarFrameInternal = new QFrame(mainWindow->ui->statusbar);
235 	progressBarFrameInternal->setLayout(progressBarLayout);
236 	mainWindow->ui->statusbar->addPermanentWidget(progressBarFrameInternal);
237 
238 	mainWindow->setWindowTitle(QString("Mandelbulber (") + systemData.lastSettingsFile + ")");
239 
240 #ifndef USE_EXR
241 	{
242 		mainWindow->ui->actionSave_as_EXR->setVisible(false);
243 		mainWindow->ui->widgetDockAnimation->DisableEXR();
244 	}
245 #endif
246 
247 #ifndef USE_TIFF
248 	{
249 		mainWindow->ui->actionSave_as_TIFF->setVisible(false);
250 		mainWindow->ui->widgetDockAnimation->DisableTIFF();
251 	}
252 #endif
253 
254 #ifndef USE_GAMEPAD
255 	{
256 		delete mainWindow->ui->dockWidget_gamepad_dock;
257 		mainWindow->ui->dockWidget_gamepad_dock = nullptr;
258 	}
259 #endif
260 
261 #ifndef USE_OPENCL
262 	mainWindow->GetWidgetDockNavigation()->EnableOpenCLModeComboBox(false);
263 #else
264 	mainWindow->GetWidgetDockNavigation()->EnableOpenCLModeComboBox(
265 		gPar->Get<bool>("opencl_enabled"));
266 
267 	if (gPar->Get<bool>("opencl_enabled") && gPar->Get<bool>("opencl_mode") > 0)
268 		mainWindow->GetWidgetDockImageAdjustments()->SetAntialiasingOpenCL(true);
269 #endif
270 
271 	QShortcut *shortcut = new QShortcut(QKeySequence(Qt::Key_F11), mainWindow);
272 	shortcut->setContext(Qt::ApplicationShortcut);
273 	connect(shortcut, &QShortcut::activated, mainWindow, &RenderWindow::ToggleFullScreen);
274 
275 	mainWindow->ui->actionImport_settings_from_Mandelbulb3d->setVisible(false);
276 
277 	if (gPar->Get<bool>("ui_colorize"))
278 		ColorizeGroupBoxes(mainWindow, gPar->Get<int>("ui_colorize_random_seed"));
279 
280 	renderedImage->show();
281 
282 	mainWindow->setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
283 	mainWindow->setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
284 	mainWindow->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
285 	mainWindow->setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
286 
287 	// loading default ui for all fractal components
288 	mainWindow->ui->widgetDockFractal->InitializeFractalUi();
289 	InitMaterialsUi();
290 	scrollAreaMaterialEditor = mainWindow->ui->scrollArea_material;
291 
292 	// change some default settings with keeping compatibility with older versions
293 	StartupDefaultSettings();
294 
295 	systemData.numberOfThreads = gPar->Get<int>("limit_CPU_cores");
296 	systemData.threadsPriority = enumRenderingThreadPriority(gPar->Get<int>("threads_priority"));
297 
298 	ComboMouseClickUpdate();
299 
300 	mainWindow->slotPopulateRecentSettings();
301 	mainWindow->slotPopulateCustomWindowStates();
302 	systemTray = new cSystemTray(mainImage, mainWindow);
303 
304 	// installing event filter for disabling tooltips
305 	gApplication->installEventFilter(mainWindow);
306 
307 	WriteLog("cInterface::ConnectSignals(void)", 2);
308 	ConnectSignals();
309 	WriteLog("cInterface::ConnectSignals(void) finished", 2);
310 }
311 
ConnectSignals() const312 void cInterface::ConnectSignals() const
313 {
314 	// other
315 
316 	connect(mainWindow->ui->checkBox_show_cursor, SIGNAL(stateChanged(int)), mainWindow,
317 		SLOT(slotChangedCheckBoxCursorVisibility(int)));
318 
319 	connect(mainWindow->ui->comboBox_mouse_click_function, SIGNAL(currentIndexChanged(int)),
320 		mainWindow, SLOT(slotChangedComboMouseClickFunction(int)));
321 
322 	connect(mainWindow->ui->comboBox_grid_type, SIGNAL(currentIndexChanged(int)), mainWindow,
323 		SLOT(slotChangedComboGridType(int)));
324 
325 	connect(mainWindow, SIGNAL(AppendToLog(const QString &)), mainWindow->ui->log_text,
326 		SLOT(appendMessage(const QString &)));
327 
328 	// menu actions
329 	connect(mainWindow->ui->actionQuit, &QAction::triggered, mainWindow, &RenderWindow::slotQuit);
330 	connect(mainWindow->ui->actionSave_docks_positions, &QAction::triggered, mainWindow,
331 		&RenderWindow::slotMenuSaveDocksPositions);
332 	connect(mainWindow->ui->actionDefault_docks_positions, &QAction::triggered, mainWindow,
333 		&RenderWindow::slotMenuResetDocksPositions);
334 	connect(mainWindow->ui->actionAnimation_docks_positions, &QAction::triggered, mainWindow,
335 		&RenderWindow::slotMenuAnimationDocksPositions);
336 	connect(mainWindow->ui->actionStack_all_docks, &QAction::triggered, mainWindow,
337 		&RenderWindow::slotStackAllDocks);
338 	connect(mainWindow->ui->actionShow_animation_dock, &QAction::triggered, mainWindow,
339 		&RenderWindow::slotUpdateDocksAndToolbarByAction);
340 	connect(mainWindow->ui->actionShow_toolbar, &QAction::triggered, mainWindow,
341 		&RenderWindow::slotUpdateDocksAndToolbarByAction);
342 	connect(mainWindow->ui->actionShow_info_dock, &QAction::triggered, mainWindow,
343 		&RenderWindow::slotUpdateDocksAndToolbarByAction);
344 	connect(mainWindow->ui->actionShow_statistics_dock, &QAction::triggered, mainWindow,
345 		&RenderWindow::slotUpdateDocksAndToolbarByAction);
346 	connect(mainWindow->ui->actionShow_gamepad_dock, &QAction::triggered, mainWindow,
347 		&RenderWindow::slotUpdateDocksAndToolbarByAction);
348 	connect(mainWindow->ui->actionShow_queue_dock, &QAction::triggered, mainWindow,
349 		&RenderWindow::slotUpdateDocksAndToolbarByAction);
350 	connect(mainWindow->ui->actionShow_measurement_dock, &QAction::triggered, mainWindow,
351 		&RenderWindow::slotUpdateDocksAndToolbarByAction);
352 	connect(mainWindow->ui->actionSave_settings, &QAction::triggered, mainWindow,
353 		&RenderWindow::slotMenuSaveSettings);
354 	connect(mainWindow->ui->actionSave_settings_to_clipboard, &QAction::triggered, mainWindow,
355 		&RenderWindow::slotMenuSaveSettingsToClipboard);
356 	connect(mainWindow->ui->actionLoad_settings, &QAction::triggered, mainWindow,
357 		&RenderWindow::slotMenuLoadSettings);
358 	connect(mainWindow->ui->actionLoad_settings_from_clipboard, &QAction::triggered, mainWindow,
359 		&RenderWindow::slotMenuLoadSettingsFromClipboard);
360 	connect(mainWindow->ui->actionLoad_example, &QAction::triggered, mainWindow,
361 		&RenderWindow::slotMenuLoadExample);
362 	connect(mainWindow->ui->actionImport_settings_from_old_Mandelbulber, &QAction::triggered,
363 		mainWindow, &RenderWindow::slotImportOldSettings);
364 	connect(mainWindow->ui->actionImport_settings_from_Mandelbulb3d, &QAction::triggered, mainWindow,
365 		&RenderWindow::slotImportMandelbulb3dSettings);
366 	connect(mainWindow->ui->actionExportVoxelLayers, &QAction::triggered, mainWindow,
367 		&RenderWindow::slotExportVoxelLayers);
368 	connect(mainWindow->ui->actionExport_Mesh, &QAction::triggered, mainWindow,
369 		&RenderWindow::slotExportMesh);
370 	connect(mainWindow->ui->actionSave_as_JPG, &QAction::triggered, mainWindow,
371 		&RenderWindow::slotMenuSaveImageJPEG);
372 	connect(mainWindow->ui->actionSave_as_IMAGE, &QAction::triggered, mainWindow,
373 		&RenderWindow::slotMenuSaveImageAll);
374 	connect(mainWindow->ui->actionSave_as_PNG, &QAction::triggered, mainWindow,
375 		&RenderWindow::slotMenuSaveImagePNG);
376 	connect(mainWindow->ui->actionSave_as_PNG_16_bit, &QAction::triggered, mainWindow,
377 		&RenderWindow::slotMenuSaveImagePNG16);
378 	connect(mainWindow->ui->actionSave_as_PNG_16_bit_with_alpha_channel, &QAction::triggered,
379 		mainWindow, &RenderWindow::slotMenuSaveImagePNG16Alpha);
380 #ifdef USE_EXR
381 	connect(mainWindow->ui->actionSave_as_EXR, &QAction::triggered, mainWindow,
382 		&RenderWindow::slotMenuSaveImageEXR);
383 #endif // USE_EXR
384 
385 #ifdef USE_TIFF
386 	connect(mainWindow->ui->actionSave_as_TIFF, &QAction::triggered, mainWindow,
387 		&RenderWindow::slotMenuSaveImageTIFF);
388 #endif // USE_TIFF
389 
390 	connect(mainWindow->ui->actionAbout_Qt, &QAction::triggered, mainWindow,
391 		&RenderWindow::slotMenuAboutQt);
392 	connect(mainWindow->ui->actionUser_Manual, &QAction::triggered, mainWindow,
393 		&RenderWindow::slotMenuAboutManual);
394 	connect(mainWindow->ui->actionUser_News, &QAction::triggered, mainWindow,
395 		&RenderWindow::slotMenuAboutNews);
396 	connect(mainWindow->ui->actionUser_HotKeys, &QAction::triggered, mainWindow,
397 		&RenderWindow::slotMenuAboutHotKeys);
398 	connect(mainWindow->ui->actionAbout_Mandelbulber, &QAction::triggered, mainWindow,
399 		&RenderWindow::slotMenuAboutMandelbulber);
400 	connect(mainWindow->ui->actionAbout_ThirdParty, &QAction::triggered, mainWindow,
401 		&RenderWindow::slotMenuAboutThirdParty);
402 	connect(mainWindow->ui->actionRender_Image, &QAction::triggered, mainWindow,
403 		&RenderWindow::slotMenuRenderImage);
404 	connect(mainWindow->ui->actionStop_rendering, &QAction::triggered, mainWindow,
405 		&RenderWindow::slotMenuStopRendering);
406 	connect(mainWindow->ui->actionUndo, &QAction::triggered, mainWindow, &RenderWindow::slotMenuUndo);
407 	connect(mainWindow->ui->actionRedo, &QAction::triggered, mainWindow, &RenderWindow::slotMenuRedo);
408 	connect(mainWindow->ui->actionRandomizeAll, &QAction::triggered, mainWindow,
409 		&RenderWindow::slotMenuRandomizeAll);
410 	connect(mainWindow->ui->actionCleanSettings, &QAction::triggered, mainWindow,
411 		&RenderWindow::slotCleanSettings);
412 
413 	connect(mainWindow->ui->actionProgramPreferences, &QAction::triggered, mainWindow,
414 		&RenderWindow::slotMenuProgramPreferences);
415 	connect(mainWindow->ui->actionDetach_image_from_main_window, &QAction::triggered, mainWindow,
416 		&RenderWindow::slotDetachMainImage);
417 
418 	connect(mainWindow->ui->scrollAreaForImage, SIGNAL(resized(int, int)), mainWindow,
419 		SLOT(slotResizedScrolledAreaImage(int, int)));
420 	connect(mainWindow->ui->comboBox_image_preview_scale, SIGNAL(currentIndexChanged(int)),
421 		mainWindow, SLOT(slotChangedComboImageScale(int)));
422 
423 	// rendered image widget
424 	connect(
425 		renderedImage, SIGNAL(mouseMoved(int, int)), mainWindow, SLOT(slotMouseMovedOnImage(int, int)));
426 	connect(
427 		renderedImage, &RenderedImage::singleClick, mainWindow, &RenderWindow::slotMouseClickOnImage);
428 	connect(renderedImage, &RenderedImage::keyPress, mainWindow, &RenderWindow::slotKeyPressOnImage);
429 	connect(
430 		renderedImage, &RenderedImage::keyRelease, mainWindow, &RenderWindow::slotKeyReleaseOnImage);
431 	connect(renderedImage, &RenderedImage::mouseWheelRotatedWithKey, mainWindow,
432 		&RenderWindow::slotMouseWheelRotatedWithKeyOnImage);
433 	connect(
434 		renderedImage, &RenderedImage::mouseDragStart, mainWindow, &RenderWindow::slotMouseDragStart);
435 	connect(
436 		renderedImage, &RenderedImage::mouseDragFinish, mainWindow, &RenderWindow::slotMouseDragFinish);
437 	connect(
438 		renderedImage, &RenderedImage::mouseDragDelta, mainWindow, &RenderWindow::slotMouseDragDelta);
439 
440 	connect(mainWindow->ui->widgetDockRenderingEngine, SIGNAL(stateChangedConnectDetailLevel(int)),
441 		mainWindow->ui->widgetImageAdjustments, SLOT(slotCheckedDetailLevelLock(int)));
442 
443 	// DockWidgets and Toolbar
444 
445 	connect(mainWindow->ui->toolBar, SIGNAL(visibilityChanged(bool)), mainWindow,
446 		SLOT(slotUpdateDocksAndToolbarByView()));
447 	connect(mainWindow->ui->dockWidget_animation, SIGNAL(visibilityChanged(bool)), mainWindow,
448 		SLOT(slotUpdateDocksAndToolbarByView()));
449 	connect(mainWindow->ui->dockWidget_info, SIGNAL(visibilityChanged(bool)), mainWindow,
450 		SLOT(slotUpdateDocksAndToolbarByView()));
451 	connect(mainWindow->ui->dockWidget_histogram, SIGNAL(visibilityChanged(bool)), mainWindow,
452 		SLOT(slotUpdateDocksAndToolbarByView()));
453 #ifdef USE_GAMEPAD
454 	connect(mainWindow->ui->dockWidget_gamepad_dock, SIGNAL(visibilityChanged(bool)), mainWindow,
455 		SLOT(slotUpdateDocksAndToolbarByView()));
456 #endif
457 	connect(mainWindow->ui->dockWidget_queue_dock, SIGNAL(visibilityChanged(bool)), mainWindow,
458 		SLOT(slotUpdateDocksAndToolbarByView()));
459 	connect(mainWindow->ui->dockWidget_measurement, SIGNAL(visibilityChanged(bool)), mainWindow,
460 		SLOT(slotUpdateDocksAndToolbarByView()));
461 
462 	connect(mainWindow->ui->actionAdd_Settings_to_Toolbar, &QAction::triggered, mainWindow,
463 		&RenderWindow::slotPresetAddToToolbar);
464 	connect(mainWindow->ui->actionAdd_CustomWindowStateToMenu, &QAction::triggered, mainWindow,
465 		&RenderWindow::slotCustomWindowStateAddToMenu);
466 	connect(mainWindow->ui->actionRemove_Window_settings, &QAction::triggered, mainWindow,
467 		&RenderWindow::slotCustomWindowRemovePopup);
468 
469 	//------------------------------------------------
470 	mainWindow->slotUpdateDocksAndToolbarByView();
471 }
472 
473 // Reading ad writing parameters from/to ui to/from parameters container
SynchronizeInterface(std::shared_ptr<cParameterContainer> par,std::shared_ptr<cFractalContainer> parFractal,qInterface::enumReadWrite mode) const474 void cInterface::SynchronizeInterface(std::shared_ptr<cParameterContainer> par,
475 	std::shared_ptr<cFractalContainer> parFractal, qInterface::enumReadWrite mode) const
476 {
477 	WriteLog(
478 		"cInterface::SynchronizeInterface(std::shared_ptr<cParameterContainer> par, cFractalContainer "
479 		"*parFractal, "
480 		"enumReadWrite mode)",
481 		2);
482 
483 	if (!gInterfaceReadyForSynchronization && mode == qInterface::read) return;
484 
485 	WriteLog("cInterface::SynchronizeInterface: dockWidget_effects", 3);
486 	SynchronizeInterfaceWindow(mainWindow->ui->dockWidget_effects, par, mode);
487 	WriteLog("cInterface::SynchronizeInterface: dockWidget_image_adjustments", 3);
488 	SynchronizeInterfaceWindow(mainWindow->ui->dockWidget_image_adjustments, par, mode);
489 	WriteLog("cInterface::SynchronizeInterface: dockWidget_navigation", 3);
490 	SynchronizeInterfaceWindow(mainWindow->ui->dockWidget_navigation, par, mode);
491 	WriteLog("cInterface::SynchronizeInterface: dockWidget_rendering_engine", 3);
492 	SynchronizeInterfaceWindow(mainWindow->ui->dockWidget_rendering_engine, par, mode);
493 	WriteLog("cInterface::SynchronizeInterface: dockWidget_queue_dock", 3);
494 	SynchronizeInterfaceWindow(mainWindow->ui->dockWidget_queue_dock, par, mode);
495 	WriteLog("cInterface::SynchronizeInterface: centralwidget", 3);
496 	SynchronizeInterfaceWindow(mainWindow->ui->centralwidget, par, mode);
497 	WriteLog("cInterface::SynchronizeInterface: dockWidgetContents_animation", 3);
498 	SynchronizeInterfaceWindow(mainWindow->ui->dockWidgetContents_animation, par, mode);
499 	WriteLog("cInterface::SynchronizeInterface: dockWidget_measurement", 3);
500 	SynchronizeInterfaceWindow(mainWindow->ui->dockWidget_measurement, par, mode);
501 	WriteLog("cInterface::SynchronizeInterface: materialEditor", 3);
502 	SynchronizeInterfaceWindow(materialEditor, par, mode);
503 
504 	if (detachedWindow)
505 	{
506 		WriteLog("cInterface::SynchronizeInterface: materialEditor", 3);
507 		SynchronizeInterfaceWindow(detachedWindow, par, mode);
508 	}
509 
510 	mainWindow->ui->widgetDockFractal->SynchronizeInterfaceFractals(par, parFractal, mode);
511 }
512 
StartRender(bool noUndo)513 void cInterface::StartRender(bool noUndo)
514 {
515 	numberOfStartedRenders++;
516 	if (!mainImage->IsUsed())
517 	{
518 		mainImage->BlockImage();
519 		WriteLog("cInterface::StartRender(void) - image was free", 2);
520 	}
521 	else
522 	{
523 		WriteLog("cInterface::StartRender(void) - image was used by another instance", 2);
524 		stopRequest = true;
525 		while (mainImage->IsUsed())
526 		{
527 			gApplication->processEvents();
528 			stopRequest = true;
529 		}
530 		mainImage->BlockImage();
531 	}
532 
533 	gNetRender->SetAnimation(false);
534 
535 	repeatRequest = false;
536 	progressBarAnimation->hide();
537 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
538 
539 	if (mainWindow->ui->widgetDockNavigation->AutoRefreshIsChecked())
540 	{
541 		// check if something was changed in settings
542 		cSettings tempSettings(cSettings::formatCondensedText);
543 		tempSettings.CreateText(gPar, gParFractal);
544 		autoRefreshLastHash = tempSettings.GetHashCode();
545 	}
546 
547 	if (!noUndo) gUndo->Store(gPar, gParFractal);
548 
549 	DisableJuliaPointMode();
550 
551 	cRenderJob *renderJob = new cRenderJob(
552 		gPar, gParFractal, mainImage, &stopRequest, renderedImage); // deleted by deleteLater()
553 
554 	connect(renderJob, SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)),
555 		mainWindow, SLOT(slotUpdateProgressAndStatus(const QString &, const QString &, double)));
556 	connect(renderJob, SIGNAL(updateStatistics(cStatistics)), mainWindow->ui->widgetDockStatistics,
557 		SLOT(slotUpdateStatistics(cStatistics)));
558 	connect(renderJob, &cRenderJob::fullyRendered, systemTray, &cSystemTray::showMessage);
559 	connect(renderJob, SIGNAL(updateImage()), renderedImage, SLOT(update()));
560 	connect(renderJob, SIGNAL(sendRenderedTilesList(QList<sRenderedTileData>)), renderedImage,
561 		SLOT(showRenderedTilesList(QList<sRenderedTileData>)));
562 	connect(renderJob, &cRenderJob::fullyRenderedTime, this, &cInterface::slotAutoSaveImage);
563 
564 	cRenderingConfiguration config;
565 	config.EnableNetRender();
566 
567 	if (!renderJob->Init(cRenderJob::still, config))
568 	{
569 		mainImage->ReleaseImage();
570 		cErrorMessage::showMessage(
571 			QObject::tr("Cannot init renderJob, see log output for more information."),
572 			cErrorMessage::errorMessage);
573 		return;
574 	}
575 
576 	// show distance in statistics table
577 	double distance = GetDistanceForPoint(gPar->Get<CVector3>("camera"), gPar, gParFractal);
578 	mainWindow->ui->widgetDockStatistics->UpdateDistanceToFractal(distance);
579 	gKeyframeAnimation->UpdateActualCameraPosition(gPar->Get<CVector3>("camera"));
580 
581 	QThread *thread = new QThread; // deleted by deleteLater()
582 	renderJob->moveToThread(thread);
583 	QObject::connect(thread, SIGNAL(started()), renderJob, SLOT(slotExecute()));
584 	QObject::connect(renderJob, SIGNAL(finished()), thread, SLOT(quit()));
585 	QObject::connect(renderJob, SIGNAL(finished()), renderJob, SLOT(deleteLater()));
586 	QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
587 
588 	thread->setObjectName("RenderJob");
589 	thread->start();
590 	numberOfStartedRenders--;
591 }
592 
MoveCamera(QString buttonName,bool synchronizeAndRender)593 void cInterface::MoveCamera(QString buttonName, bool synchronizeAndRender)
594 {
595 	using namespace cameraMovementEnums;
596 
597 	WriteLog("cInterface::MoveCamera(QString buttonName): button: " + buttonName, 2);
598 
599 	// get data from interface
600 	if (synchronizeAndRender) SynchronizeInterface(gPar, gParFractal, qInterface::read);
601 	CVector3 camera = gPar->Get<CVector3>("camera");
602 	CVector3 target = gPar->Get<CVector3>("target");
603 	CVector3 topVector = gPar->Get<CVector3>("camera_top");
604 	cCameraTarget cameraTarget(camera, target, topVector);
605 
606 	bool legacyCoordinateSystem = gPar->Get<bool>("legacy_coordinate_system");
607 	double reverse = legacyCoordinateSystem ? -1.0 : 1.0;
608 
609 	// get direction vector
610 	CVector3 direction;
611 	if (buttonName == "bu_move_left")
612 		direction = cameraTarget.GetRightVector() * (-1.0);
613 	else if (buttonName == "bu_move_right")
614 		direction = cameraTarget.GetRightVector() * (1.0);
615 	else if (buttonName == "bu_move_up")
616 		direction = cameraTarget.GetTopVector() * (1.0) * reverse;
617 	else if (buttonName == "bu_move_down")
618 		direction = cameraTarget.GetTopVector() * (-1.0) * reverse;
619 	else if (buttonName == "bu_move_forward")
620 		direction = cameraTarget.GetForwardVector() * (1.0);
621 	else if (buttonName == "bu_move_backward")
622 		direction = cameraTarget.GetForwardVector() * (-1.0);
623 
624 	enumCameraMovementStepMode stepMode =
625 		enumCameraMovementStepMode(gPar->Get<int>("camera_absolute_distance_mode"));
626 	enumCameraMovementMode movementMode =
627 		enumCameraMovementMode(gPar->Get<int>("camera_movement_mode"));
628 
629 	// movement step
630 	double step;
631 	if (stepMode == absolute)
632 	{
633 		step = gPar->Get<double>("camera_movement_step");
634 	}
635 	else
636 	{
637 		double relativeStep = gPar->Get<double>("camera_movement_step");
638 
639 		CVector3 point;
640 		if (movementMode == moveTarget)
641 			point = target;
642 		else
643 			point = camera;
644 
645 		double distance = GetDistanceForPoint(point, gPar, gParFractal);
646 
647 		step = relativeStep * distance;
648 	}
649 
650 	// movement
651 	if (movementMode == moveCamera)
652 		camera += direction * step;
653 	else if (movementMode == moveTarget)
654 		target += direction * step;
655 	else if (movementMode == fixedDistance)
656 	{
657 		camera += direction * step;
658 		target += direction * step;
659 	}
660 
661 	// put data to interface
662 	gPar->Set("camera", camera);
663 	gPar->Set("target", target);
664 
665 	// recalculation of camera-target
666 	cCameraTarget::enumRotationMode rollMode =
667 		cCameraTarget::enumRotationMode(gPar->Get<int>("camera_straight_rotation"));
668 	if (movementMode == moveCamera)
669 		cameraTarget.SetCamera(camera, rollMode);
670 	else if (movementMode == moveTarget)
671 		cameraTarget.SetTarget(target, rollMode);
672 	else if (movementMode == fixedDistance)
673 		cameraTarget.SetCameraTargetTop(camera, target, topVector);
674 
675 	topVector = cameraTarget.GetTopVector();
676 	gPar->Set("camera_top", topVector);
677 	CVector3 rotation = cameraTarget.GetRotation();
678 	gPar->Set("camera_rotation", rotation * (180.0 / M_PI));
679 	double dist = cameraTarget.GetDistance();
680 	gPar->Set("camera_distance_to_target", dist);
681 
682 	if (synchronizeAndRender) SynchronizeInterface(gPar, gParFractal, qInterface::write);
683 	if (synchronizeAndRender) StartRender();
684 }
685 
CameraOrTargetEdited() const686 void cInterface::CameraOrTargetEdited() const
687 {
688 	WriteLog("cInterface::CameraOrTargetEdited(void)", 2);
689 
690 	// get data from interface before synchronization
691 	CVector3 camera = gPar->Get<CVector3>("camera");
692 	CVector3 target = gPar->Get<CVector3>("target");
693 	CVector3 topVector = gPar->Get<CVector3>("camera_top");
694 	cCameraTarget cameraTarget(camera, target, topVector);
695 
696 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
697 	camera = gPar->Get<CVector3>("camera");
698 	target = gPar->Get<CVector3>("target");
699 
700 	// recalculation of camera-target
701 	cCameraTarget::enumRotationMode rollMode =
702 		cCameraTarget::enumRotationMode(gPar->Get<int>("camera_straight_rotation"));
703 	cameraTarget.SetCamera(camera, rollMode);
704 	cameraTarget.SetTarget(target, rollMode);
705 
706 	topVector = cameraTarget.GetTopVector();
707 	gPar->Set("camera_top", topVector);
708 	CVector3 rotation = cameraTarget.GetRotation();
709 	gPar->Set("camera_rotation", rotation * (180.0 / M_PI));
710 	double dist = cameraTarget.GetDistance();
711 	gPar->Set("camera_distance_to_target", dist);
712 
713 	SynchronizeInterface(gPar, gParFractal, qInterface::write);
714 }
715 
RotateCamera(QString buttonName,bool synchronizeAndRender)716 void cInterface::RotateCamera(QString buttonName, bool synchronizeAndRender)
717 {
718 	using namespace cameraMovementEnums;
719 
720 	WriteLog("cInterface::RotateCamera(QString buttonName): button: " + buttonName, 2);
721 
722 	// get data from interface
723 	if (synchronizeAndRender) SynchronizeInterface(gPar, gParFractal, qInterface::read);
724 	CVector3 camera = gPar->Get<CVector3>("camera");
725 	CVector3 target = gPar->Get<CVector3>("target");
726 	CVector3 topVector = gPar->Get<CVector3>("camera_top");
727 	cCameraTarget cameraTarget(camera, target, topVector);
728 	double distance = cameraTarget.GetDistance();
729 
730 	enumCameraRotationMode rotationMode =
731 		enumCameraRotationMode(gPar->Get<int>("camera_rotation_mode"));
732 	cCameraTarget::enumRotationMode rollMode =
733 		cCameraTarget::enumRotationMode(gPar->Get<int>("camera_straight_rotation"));
734 
735 	bool legacyCoordinateSystem = gPar->Get<bool>("legacy_coordinate_system");
736 	double reverse = legacyCoordinateSystem ? -1.0 : 1.0;
737 
738 	CVector3 rotationAxis;
739 	if (rollMode == cCameraTarget::constantRoll)
740 	{
741 		if (buttonName == "bu_rotate_left")
742 			rotationAxis = CVector3(0.0, 0.0, 1.0);
743 		else if (buttonName == "bu_rotate_right")
744 			rotationAxis = CVector3(0.0, 0.0, -1.0);
745 		else if (buttonName == "bu_rotate_up")
746 		{
747 			rotationAxis = CVector3(1.0 * reverse, 0.0, 0.0);
748 			rotationAxis = rotationAxis.RotateAroundVectorByAngle(
749 				CVector3(0.0, 0.0, 1.0), cameraTarget.GetRotation().x);
750 		}
751 		else if (buttonName == "bu_rotate_down")
752 		{
753 			rotationAxis = CVector3(-1.0 * reverse, 0.0, 0.0);
754 			rotationAxis = rotationAxis.RotateAroundVectorByAngle(
755 				CVector3(0.0, 0.0, 1.0), cameraTarget.GetRotation().x);
756 		}
757 		else if (buttonName == "bu_rotate_roll_left")
758 			rotationAxis = cameraTarget.GetForwardVector() * (-1.0) * reverse;
759 		else if (buttonName == "bu_rotate_roll_right")
760 			rotationAxis = cameraTarget.GetForwardVector() * (1.0) * reverse;
761 	}
762 	else
763 	{
764 		if (buttonName == "bu_rotate_left")
765 			rotationAxis = cameraTarget.GetTopVector() * (1.0);
766 		else if (buttonName == "bu_rotate_right")
767 			rotationAxis = cameraTarget.GetTopVector() * (-1.0);
768 		else if (buttonName == "bu_rotate_up")
769 			rotationAxis = cameraTarget.GetRightVector() * (1.0) * reverse;
770 		else if (buttonName == "bu_rotate_down")
771 			rotationAxis = cameraTarget.GetRightVector() * (-1.0) * reverse;
772 		else if (buttonName == "bu_rotate_roll_left")
773 			rotationAxis = cameraTarget.GetForwardVector() * (-1.0) * reverse;
774 		else if (buttonName == "bu_rotate_roll_right")
775 			rotationAxis = cameraTarget.GetForwardVector() * (1.0) * reverse;
776 	}
777 
778 	if (rotationMode == rotateAroundTarget) rotationAxis *= -1.0;
779 
780 	// rotation of vectors
781 	CVector3 forwardVector = cameraTarget.GetForwardVector();
782 	double rotationStep = gPar->Get<double>("camera_rotation_step") * (M_PI / 180.0);
783 	forwardVector = forwardVector.RotateAroundVectorByAngle(rotationAxis, rotationStep);
784 	topVector = topVector.RotateAroundVectorByAngle(rotationAxis, rotationStep);
785 
786 	if (rotationMode == rotateCamera)
787 	{
788 		target = camera + forwardVector * distance;
789 	}
790 	else
791 	{
792 		camera = target - forwardVector * distance;
793 	}
794 
795 	// recalculation of camera-target
796 	cameraTarget.SetCameraTargetTop(camera, target, topVector);
797 
798 	gPar->Set("camera", camera);
799 	gPar->Set("target", target);
800 	gPar->Set("camera_top", topVector);
801 	CVector3 rotation = cameraTarget.GetRotation();
802 	gPar->Set("camera_rotation", rotation * (180.0 / M_PI));
803 	double dist = cameraTarget.GetDistance();
804 	gPar->Set("camera_distance_to_target", dist);
805 
806 	if (synchronizeAndRender) SynchronizeInterface(gPar, gParFractal, qInterface::write);
807 	if (synchronizeAndRender) StartRender();
808 }
809 
RotationEdited() const810 void cInterface::RotationEdited() const
811 {
812 	using namespace cameraMovementEnums;
813 
814 	WriteLog("cInterface::RotationEdited(void)", 2);
815 	// get data from interface before synchronization
816 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
817 	CVector3 camera = gPar->Get<CVector3>("camera");
818 	CVector3 target = gPar->Get<CVector3>("target");
819 	CVector3 topVector = gPar->Get<CVector3>("camera_top");
820 	cCameraTarget cameraTarget(camera, target, topVector);
821 	double distance = cameraTarget.GetDistance();
822 	CVector3 rotation = gPar->Get<CVector3>("camera_rotation") * (M_PI / 180.0);
823 
824 	enumCameraRotationMode rotationMode =
825 		enumCameraRotationMode(gPar->Get<int>("camera_rotation_mode"));
826 
827 	CVector3 forwardVector(0.0, 1.0, 0.0);
828 	forwardVector = forwardVector.RotateAroundVectorByAngle(CVector3(0.0, 1.0, 0.0), rotation.z);
829 	forwardVector = forwardVector.RotateAroundVectorByAngle(CVector3(1.0, 0.0, 0.0), rotation.y);
830 	forwardVector = forwardVector.RotateAroundVectorByAngle(CVector3(0.0, 0.0, 1.0), rotation.x);
831 
832 	if (rotationMode == rotateCamera)
833 	{
834 		target = camera + forwardVector * distance;
835 	}
836 	else
837 	{
838 		camera = target - forwardVector * distance;
839 	}
840 	cameraTarget.SetCameraTargetRotation(camera, target, rotation.z);
841 	gPar->Set("camera", camera);
842 	gPar->Set("target", target);
843 	gPar->Set("camera_top", cameraTarget.GetTopVector());
844 	SynchronizeInterface(gPar, gParFractal, qInterface::write);
845 }
846 
CameraDistanceEdited() const847 void cInterface::CameraDistanceEdited() const
848 {
849 	using namespace cameraMovementEnums;
850 
851 	WriteLog("cInterface::CameraDistanceEdited()", 2);
852 
853 	SynchronizeInterfaceWindow(mainWindow->ui->dockWidget_navigation, gPar, qInterface::read);
854 	CVector3 camera = gPar->Get<CVector3>("camera");
855 	CVector3 target = gPar->Get<CVector3>("target");
856 	CVector3 topVector = gPar->Get<CVector3>("camera_top");
857 	cCameraTarget cameraTarget(camera, target, topVector);
858 	CVector3 forwardVector = cameraTarget.GetForwardVector();
859 
860 	double distance = gPar->Get<double>("camera_distance_to_target");
861 
862 	enumCameraMovementMode movementMode =
863 		enumCameraMovementMode(gPar->Get<int>("camera_movement_mode"));
864 	if (movementMode == moveTarget)
865 	{
866 		target = camera + forwardVector * distance;
867 	}
868 	else
869 	{
870 		camera = target - forwardVector * distance;
871 	}
872 
873 	cameraTarget.SetCameraTargetTop(camera, target, topVector);
874 	gPar->Set("camera", camera);
875 	gPar->Set("target", target);
876 	gPar->Set("camera_top", cameraTarget.GetTopVector());
877 	SynchronizeInterfaceWindow(mainWindow->ui->dockWidget_navigation, gPar, qInterface::write);
878 }
879 
IFSDefaultsDodecahedron(std::shared_ptr<cParameterContainer> parFractal) const880 void cInterface::IFSDefaultsDodecahedron(std::shared_ptr<cParameterContainer> parFractal) const
881 {
882 	double phi = (1 + sqrt(5.0)) / 2.0;
883 	parFractal->Set("IFS_scale", phi * phi);
884 	parFractal->Set("IFS_direction_0", CVector3(phi * phi, 1.0, -phi));
885 	parFractal->Set("IFS_direction_1", CVector3(-phi, phi * phi, 1.0));
886 	parFractal->Set("IFS_direction_2", CVector3(1.0, -phi, phi * phi));
887 	parFractal->Set("IFS_enabled_0", true);
888 	parFractal->Set("IFS_enabled_1", true);
889 	parFractal->Set("IFS_enabled_2", true);
890 	parFractal->Set("IFS_offset", CVector3(1.0, 1.0, 1.0));
891 	parFractal->Set("IFS_abs_x", true);
892 	parFractal->Set("IFS_abs_y", true);
893 	parFractal->Set("IFS_abs_z", true);
894 	parFractal->Set("IFS_menger_sponge_mode", false);
895 }
896 
IFSDefaultsIcosahedron(std::shared_ptr<cParameterContainer> parFractal) const897 void cInterface::IFSDefaultsIcosahedron(std::shared_ptr<cParameterContainer> parFractal) const
898 {
899 	double phi = (1 + sqrt(5.0)) / 2.0;
900 	parFractal->Set("IFS_scale", 2.0);
901 	parFractal->Set("IFS_direction_3", CVector3(-phi * phi, 1.0, phi));
902 	parFractal->Set("IFS_direction_4", CVector3(phi, -phi * phi, 1.0));
903 	parFractal->Set("IFS_enabled_3", true);
904 	parFractal->Set("IFS_enabled_4", true);
905 	parFractal->Set("IFS_offset", CVector3(1.0, 0.0, phi));
906 	parFractal->Set("IFS_abs_x", true);
907 	parFractal->Set("IFS_abs_y", true);
908 	parFractal->Set("IFS_abs_z", true);
909 	parFractal->Set("IFS_menger_sponge_mode", false);
910 }
911 
IFSDefaultsOctahedron(std::shared_ptr<cParameterContainer> parFractal)912 void cInterface::IFSDefaultsOctahedron(std::shared_ptr<cParameterContainer> parFractal)
913 {
914 	parFractal->Set("IFS_scale", 2.0);
915 	parFractal->Set("IFS_direction_5", CVector3(1.0, -1.0, 0));
916 	parFractal->Set("IFS_direction_6", CVector3(1.0, 0.0, -1.0));
917 	parFractal->Set("IFS_direction_7", CVector3(0.0, 1.0, -1.0));
918 	parFractal->Set("IFS_enabled_5", true);
919 	parFractal->Set("IFS_enabled_6", true);
920 	parFractal->Set("IFS_enabled_7", true);
921 	parFractal->Set("IFS_offset", CVector3(1.0, 0.0, 0.0));
922 	parFractal->Set("IFS_abs_x", true);
923 	parFractal->Set("IFS_abs_y", true);
924 	parFractal->Set("IFS_abs_z", true);
925 	parFractal->Set("IFS_menger_sponge_mode", false);
926 }
927 
IFSDefaultsMengerSponge(std::shared_ptr<cParameterContainer> parFractal)928 void cInterface::IFSDefaultsMengerSponge(std::shared_ptr<cParameterContainer> parFractal)
929 {
930 	parFractal->Set("IFS_scale", 3.0);
931 	parFractal->Set("IFS_direction_5", CVector3(1.0, -1.0, 0));
932 	parFractal->Set("IFS_direction_6", CVector3(1.0, 0.0, -1.0));
933 	parFractal->Set("IFS_direction_7", CVector3(0.0, 1.0, -1.0));
934 	parFractal->Set("IFS_enabled_5", true);
935 	parFractal->Set("IFS_enabled_6", true);
936 	parFractal->Set("IFS_enabled_7", true);
937 	parFractal->Set("IFS_offset", CVector3(1.0, 1.0, 1.0));
938 	parFractal->Set("IFS_abs_x", true);
939 	parFractal->Set("IFS_abs_y", true);
940 	parFractal->Set("IFS_abs_z", true);
941 	parFractal->Set("IFS_menger_sponge_mode", true);
942 }
943 
IFSDefaultsReset(std::shared_ptr<cParameterContainer> parFractal)944 void cInterface::IFSDefaultsReset(std::shared_ptr<cParameterContainer> parFractal)
945 {
946 	for (int i = 0; i < 9; i++)
947 	{
948 		parFractal->Set("IFS_direction", i, CVector3(1.0, 0.0, 0.0));
949 		parFractal->Set("IFS_rotations", i, CVector3(0.0, 0.0, 0.0));
950 		parFractal->Set("IFS_distance", i, 0.0);
951 		parFractal->Set("IFS_enabled", i, false);
952 		parFractal->Set("IFS_intensity", i, 1.0);
953 	}
954 	parFractal->Set("IFS_offset", CVector3(1.0, 0.0, 0.0));
955 	parFractal->Set("IFS_abs_x", false);
956 	parFractal->Set("IFS_abs_y", false);
957 	parFractal->Set("IFS_abs_z", false);
958 	parFractal->Set("IFS_menger_sponge_mode", false);
959 	parFractal->Set("IFS_scale", 2.0);
960 	parFractal->Set("IFS_rotation", CVector3(0.0, 0.0, 0.0));
961 	parFractal->Set("IFS_rotation_enabled", false);
962 	parFractal->Set("IFS_edge", CVector3(0.0, 0.0, 0.0));
963 	parFractal->Set("IFS_edge_enabled", false);
964 }
965 
RefreshMainImage()966 void cInterface::RefreshMainImage()
967 {
968 	if (!mainImage->IsUsed())
969 	{
970 		SynchronizeInterface(gPar, gParFractal, qInterface::read);
971 		sImageAdjustments imageAdjustments;
972 		imageAdjustments.brightness = gPar->Get<float>("brightness");
973 		imageAdjustments.contrast = gPar->Get<float>("contrast");
974 		imageAdjustments.imageGamma = gPar->Get<float>("gamma");
975 		imageAdjustments.saturation = gPar->Get<float>("saturation");
976 		imageAdjustments.hdrEnabled = gPar->Get<bool>("hdr");
977 
978 		mainImage->SetImageParameters(imageAdjustments);
979 		mainImage->CompileImage();
980 
981 		mainImage->ConvertTo8bitChar();
982 		mainImage->UpdatePreview();
983 		if (mainImage->GetImageWidget()) mainImage->GetImageWidget()->update();
984 	}
985 	else
986 	{
987 		cErrorMessage::showMessage(
988 			QObject::tr("You cannot apply changes during rendering. You will do this after rendering."),
989 			cErrorMessage::warningMessage, mainWindow);
990 	}
991 }
992 
RefreshPostEffects()993 void cInterface::RefreshPostEffects()
994 {
995 	if (!mainImage->IsUsed())
996 	{
997 		mainImage->NullPostEffect();
998 
999 		RefreshMainImage();
1000 
1001 		// replace image size parameters in case if user changed image size just before image update
1002 		gPar->Set("image_width", int(mainImage->GetWidth()));
1003 		gPar->Set("image_height", int(mainImage->GetHeight()));
1004 
1005 		stopRequest = false;
1006 		if (gPar->Get<bool>("ambient_occlusion_enabled")
1007 				&& gPar->Get<int>("ambient_occlusion_mode") == params::AOModeScreenSpace)
1008 		{
1009 			if (gPar->Get<bool>("opencl_enabled")
1010 					&& cOpenClEngineRenderFractal::enumClRenderEngineMode(gPar->Get<int>("opencl_mode"))
1011 							 != cOpenClEngineRenderFractal::clRenderEngineTypeNone)
1012 			{
1013 #ifdef USE_OPENCL
1014 				sParamRender params(gPar);
1015 				gOpenCl->openClEngineRenderSSAO->Lock();
1016 				cRegion<int> region(0, 0, mainImage->GetWidth(), mainImage->GetHeight());
1017 				gOpenCl->openClEngineRenderSSAO->SetParameters(&params, region);
1018 				if (gOpenCl->openClEngineRenderSSAO->LoadSourcesAndCompile(gPar))
1019 				{
1020 					gOpenCl->openClEngineRenderSSAO->CreateKernel4Program(gPar);
1021 					size_t neededMem = gOpenCl->openClEngineRenderSSAO->CalcNeededMemory();
1022 					WriteLogDouble("OpenCl render SSAO - needed mem:", neededMem / 1048576.0, 2);
1023 					if (neededMem / 1048576 < size_t(gPar->Get<int>("opencl_memory_limit")))
1024 					{
1025 						gOpenCl->openClEngineRenderSSAO->PreAllocateBuffers(gPar);
1026 						gOpenCl->openClEngineRenderSSAO->CreateCommandQueue();
1027 						gOpenCl->openClEngineRenderSSAO->Render(mainImage, &stopRequest);
1028 					}
1029 					else
1030 					{
1031 						cErrorMessage::showMessage(
1032 							QObject::tr("Not enough free memory in OpenCL device to render SSAO effect!"),
1033 							cErrorMessage::errorMessage, mainWindow);
1034 					}
1035 				}
1036 				gOpenCl->openClEngineRenderSSAO->ReleaseMemory();
1037 				gOpenCl->openClEngineRenderSSAO->Unlock();
1038 #endif
1039 			}
1040 			else
1041 			{
1042 				std::shared_ptr<sParamRender> params(new sParamRender(gPar));
1043 				std::shared_ptr<sRenderData> data(new sRenderData());
1044 				data->stopRequest = &stopRequest;
1045 				data->screenRegion = cRegion<int>(0, 0, mainImage->GetWidth(), mainImage->GetHeight());
1046 				cRenderSSAO rendererSSAO(params, data, mainImage);
1047 				QObject::connect(&rendererSSAO,
1048 					SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)), mainWindow,
1049 					SLOT(slotUpdateProgressAndStatus(const QString &, const QString &, double)));
1050 				connect(&rendererSSAO, SIGNAL(updateImage()), renderedImage, SLOT(update()));
1051 
1052 				rendererSSAO.RenderSSAO();
1053 
1054 				mainImage->CompileImage();
1055 				mainImage->ConvertTo8bitChar();
1056 				mainImage->UpdatePreview();
1057 				if (mainImage->GetImageWidget()) mainImage->GetImageWidget()->update();
1058 			}
1059 		}
1060 
1061 		if (gPar->Get<bool>("DOF_enabled"))
1062 		{
1063 			if (gPar->Get<bool>("opencl_enabled")
1064 					&& cOpenClEngineRenderFractal::enumClRenderEngineMode(gPar->Get<int>("opencl_mode"))
1065 							 != cOpenClEngineRenderFractal::clRenderEngineTypeNone)
1066 			{
1067 #ifdef USE_OPENCL
1068 				cRegion<int> screenRegion(0, 0, mainImage->GetWidth(), mainImage->GetHeight());
1069 				sParamRender params(gPar);
1070 				gOpenCl->openclEngineRenderDOF->RenderDOF(
1071 					&params, gPar, mainImage, &stopRequest, screenRegion);
1072 #endif
1073 			}
1074 			else
1075 			{
1076 				sParamRender params(gPar);
1077 				// cRenderingConfiguration config;
1078 				cPostRenderingDOF dof(mainImage);
1079 				connect(&dof, SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)),
1080 					mainWindow, SLOT(slotUpdateProgressAndStatus(const QString &, const QString &, double)));
1081 				connect(&dof, SIGNAL(updateImage()), renderedImage, SLOT(update()));
1082 				cRegion<int> screenRegion(0, 0, mainImage->GetWidth(), mainImage->GetHeight());
1083 				dof.Render(screenRegion,
1084 					params.DOFRadius * (mainImage->GetWidth() + mainImage->GetHeight()) / 2000.0,
1085 					params.DOFFocus, params.DOFNumberOfPasses, params.DOFBlurOpacity, params.DOFMaxRadius,
1086 					&stopRequest);
1087 			}
1088 		}
1089 
1090 		if (gPar->Get<bool>("opencl_enabled")
1091 				&& cOpenClEngineRenderFractal::enumClRenderEngineMode(gPar->Get<int>("opencl_mode"))
1092 						 != cOpenClEngineRenderFractal::clRenderEngineTypeNone)
1093 		{
1094 #ifdef USE_OPENCL
1095 
1096 			connect(gOpenCl->openclEngineRenderPostFilter,
1097 				SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)), mainWindow,
1098 				SLOT(slotUpdateProgressAndStatus(const QString &, const QString &, double)));
1099 
1100 			for (int i = cOpenClEngineRenderPostFilter::hdrBlur;
1101 					 i <= cOpenClEngineRenderPostFilter::chromaticAberration; i++)
1102 			{
1103 
1104 				bool skip = false;
1105 				switch (cOpenClEngineRenderPostFilter::enumPostEffectType(i))
1106 				{
1107 					case cOpenClEngineRenderPostFilter::hdrBlur:
1108 					{
1109 						if (!gPar->Get<bool>("hdr_blur_enabled")) skip = true;
1110 						break;
1111 					}
1112 					case cOpenClEngineRenderPostFilter::chromaticAberration:
1113 					{
1114 						if (!gPar->Get<bool>("post_chromatic_aberration_enabled")) skip = true;
1115 						break;
1116 					}
1117 				}
1118 
1119 				if (skip) continue;
1120 
1121 				sParamRender params(gPar);
1122 				gOpenCl->openclEngineRenderPostFilter->Lock();
1123 				cRegion<int> region(0, 0, mainImage->GetWidth(), mainImage->GetHeight());
1124 				gOpenCl->openclEngineRenderPostFilter->SetParameters(
1125 					&params, region, cOpenClEngineRenderPostFilter::enumPostEffectType(i));
1126 				if (gOpenCl->openclEngineRenderPostFilter->LoadSourcesAndCompile(gPar))
1127 				{
1128 					gOpenCl->openclEngineRenderPostFilter->CreateKernel4Program(gPar);
1129 					size_t neededMem = gOpenCl->openclEngineRenderPostFilter->CalcNeededMemory();
1130 					WriteLogDouble("OpenCl render Post Filter - needed mem:", neededMem / 1048576.0, 2);
1131 					if (neededMem / 1048576 < size_t(gPar->Get<int>("opencl_memory_limit")))
1132 					{
1133 						gOpenCl->openclEngineRenderPostFilter->PreAllocateBuffers(gPar);
1134 						gOpenCl->openclEngineRenderPostFilter->CreateCommandQueue();
1135 						gOpenCl->openclEngineRenderPostFilter->Render(mainImage, &stopRequest);
1136 					}
1137 					else
1138 					{
1139 						cErrorMessage::showMessage(
1140 							QObject::tr("Not enough free memory in OpenCL device to render SSAO effect!"),
1141 							cErrorMessage::errorMessage, mainWindow);
1142 					}
1143 				}
1144 				gOpenCl->openclEngineRenderPostFilter->ReleaseMemory();
1145 				gOpenCl->openclEngineRenderPostFilter->Unlock();
1146 			}
1147 #endif
1148 		}
1149 		else
1150 		{
1151 			if (gPar->Get<bool>("hdr_blur_enabled"))
1152 			{
1153 				std::unique_ptr<cPostEffectHdrBlur> hdrBlur(new cPostEffectHdrBlur(mainImage));
1154 				double blurRadius = gPar->Get<double>("hdr_blur_radius");
1155 				double blurIntensity = gPar->Get<double>("hdr_blur_intensity");
1156 				hdrBlur->SetParameters(blurRadius, blurIntensity);
1157 				QObject::connect(hdrBlur.get(),
1158 					SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)), mainWindow,
1159 					SLOT(slotUpdateProgressAndStatus(const QString &, const QString &, double)));
1160 				hdrBlur->Render(&stopRequest);
1161 			}
1162 		}
1163 
1164 		mainImage->CompileImage();
1165 
1166 		mainImage->ConvertTo8bitChar();
1167 		mainImage->UpdatePreview();
1168 		if (mainImage->GetImageWidget()) mainImage->GetImageWidget()->update();
1169 	}
1170 	else
1171 	{
1172 		cErrorMessage::showMessage(
1173 			QObject::tr("You cannot apply changes during rendering. You will do this after rendering."),
1174 			cErrorMessage::warningMessage, mainWindow);
1175 	}
1176 }
1177 
AutoFog() const1178 void cInterface::AutoFog() const
1179 {
1180 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
1181 	double distance = GetDistanceForPoint(gPar->Get<CVector3>("camera"), gPar, gParFractal);
1182 	double fogDensity = 0.5;
1183 	double fogDistanceFactor = distance;
1184 	double fogColour1Distance = distance * 0.5;
1185 	double fogColour2Distance = distance;
1186 	gPar->Set("volumetric_fog_distance_factor", fogDistanceFactor);
1187 	gPar->Set("volumetric_fog_colour_1_distance", fogColour1Distance);
1188 	gPar->Set("volumetric_fog_colour_2_distance", fogColour2Distance);
1189 	gPar->Set("volumetric_fog_density", fogDensity);
1190 	SynchronizeInterface(gPar, gParFractal, qInterface::write);
1191 }
1192 
GetDistanceForPoint(CVector3 point,std::shared_ptr<cParameterContainer> par,std::shared_ptr<cFractalContainer> parFractal)1193 double cInterface::GetDistanceForPoint(CVector3 point, std::shared_ptr<cParameterContainer> par,
1194 	std::shared_ptr<cFractalContainer> parFractal)
1195 {
1196 	std::shared_ptr<sParamRender> params(new sParamRender(par));
1197 	std::shared_ptr<cNineFractals> fractals(new cNineFractals(parFractal, par));
1198 	sDistanceIn in(point, 0, false);
1199 	sDistanceOut out;
1200 
1201 	bool openClEnabled = false;
1202 #ifdef USE_OPENCL
1203 	openClEnabled = par->Get<bool>("opencl_enabled") && parFractal->isUsedCustomFormula();
1204 
1205 	if (openClEnabled)
1206 	{
1207 		gOpenCl->openClEngineRenderFractal->Lock();
1208 		gOpenCl->openClEngineRenderFractal->SetDistanceMode();
1209 		gOpenCl->openClEngineRenderFractal->SetParameters(
1210 			par, parFractal, params, fractals, nullptr, false);
1211 		if (gOpenCl->openClEngineRenderFractal->LoadSourcesAndCompile(par))
1212 		{
1213 			gOpenCl->openClEngineRenderFractal->CreateKernel4Program(par);
1214 			gOpenCl->openClEngineRenderFractal->PreAllocateBuffers(par);
1215 			gOpenCl->openClEngineRenderFractal->CreateCommandQueue();
1216 		}
1217 		else
1218 		{
1219 			gOpenCl->openClEngineRenderFractal->ReleaseMemory();
1220 			gOpenCl->openClEngineRenderFractal->Unlock();
1221 			return 0.0;
1222 		}
1223 	}
1224 #endif
1225 
1226 	double dist;
1227 	if (openClEnabled)
1228 	{
1229 #ifdef USE_OPENCL
1230 		dist = gOpenCl->openClEngineRenderFractal->CalculateDistance(point);
1231 #endif
1232 	}
1233 	else
1234 	{
1235 		dist = CalculateDistance(*params, *fractals, in, &out);
1236 	}
1237 
1238 #ifdef USE_OPENCL
1239 	if (openClEnabled)
1240 	{
1241 		gOpenCl->openClEngineRenderFractal->ReleaseMemory();
1242 		gOpenCl->openClEngineRenderFractal->Unlock();
1243 	}
1244 #endif
1245 
1246 	return dist;
1247 }
1248 
GetDistanceForPoint(CVector3 point) const1249 double cInterface::GetDistanceForPoint(CVector3 point) const
1250 {
1251 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
1252 	double distance = GetDistanceForPoint(point, gPar, gParFractal);
1253 	return distance;
1254 }
1255 
SetByMouse(CVector2<double> screenPoint,Qt::MouseButton button,const QList<QVariant> & mode)1256 void cInterface::SetByMouse(
1257 	CVector2<double> screenPoint, Qt::MouseButton button, const QList<QVariant> &mode)
1258 {
1259 	using namespace cameraMovementEnums;
1260 
1261 	WriteLog(
1262 		QString("MoveCameraByMouse(CVector2<double> screenPoint, Qt::MouseButton button): button: ")
1263 			+ button,
1264 		2);
1265 	// get data from interface
1266 
1267 	RenderedImage::enumClickMode clickMode = RenderedImage::enumClickMode(mode.at(0).toInt());
1268 
1269 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
1270 	CVector3 camera = gPar->Get<CVector3>("camera");
1271 	CVector3 target = gPar->Get<CVector3>("target");
1272 	CVector3 topVector = gPar->Get<CVector3>("camera_top");
1273 	cCameraTarget cameraTarget(camera, target, topVector);
1274 
1275 	enumCameraMovementStepMode stepMode =
1276 		enumCameraMovementStepMode(gPar->Get<int>("camera_absolute_distance_mode"));
1277 	enumCameraMovementMode movementMode =
1278 		enumCameraMovementMode(gPar->Get<int>("camera_movement_mode"));
1279 	params::enumPerspectiveType perspType =
1280 		params::enumPerspectiveType(gPar->Get<int>("perspective_type"));
1281 	cCameraTarget::enumRotationMode rollMode =
1282 		cCameraTarget::enumRotationMode(gPar->Get<int>("camera_straight_rotation"));
1283 	double movementStep = gPar->Get<double>("camera_movement_step");
1284 	double fov = CalcFOV(gPar->Get<double>("fov"), perspType);
1285 	bool legacyCoordinateSystem = gPar->Get<bool>("legacy_coordinate_system");
1286 	double reverse = legacyCoordinateSystem ? -1.0 : 1.0;
1287 
1288 	double sweetSpotHAngle = gPar->Get<double>("sweet_spot_horizontal_angle") / 180.0 * M_PI;
1289 	double sweetSpotVAngle = gPar->Get<double>("sweet_spot_vertical_angle") / 180.0 * M_PI;
1290 
1291 	CVector2<double> imagePoint;
1292 	imagePoint = screenPoint / mainImage->GetPreviewScale();
1293 
1294 	int width = mainImage->GetWidth();
1295 	int height = mainImage->GetHeight();
1296 
1297 	if (imagePoint.x >= 0 && imagePoint.x < mainImage->GetWidth() && imagePoint.y >= 0
1298 			&& imagePoint.y < mainImage->GetHeight())
1299 	{
1300 		double depth = mainImage->GetPixelZBuffer(imagePoint.x, imagePoint.y);
1301 		if (depth < 1e10)
1302 		{
1303 			CVector3 viewVector;
1304 			double aspectRatio = double(width) / height;
1305 
1306 			if (perspType == params::perspEquirectangular) aspectRatio = 2.0;
1307 
1308 			double wheelDistance = 1.0;
1309 			if (clickMode == RenderedImage::clickMoveCamera)
1310 			{
1311 
1312 				if (mode.length() > 1) // if mouse wheel delta is available
1313 				{
1314 					int wheelDelta = mode.at(1).toInt();
1315 					wheelDistance = 0.001 * fabs(wheelDelta);
1316 				}
1317 			}
1318 
1319 			CVector3 angles = cameraTarget.GetRotation();
1320 			CRotationMatrix mRot;
1321 			mRot.SetRotation(angles);
1322 			mRot.RotateZ(-sweetSpotHAngle);
1323 			mRot.RotateX(sweetSpotVAngle);
1324 
1325 			CVector2<double> normalizedPoint;
1326 			normalizedPoint.x = (imagePoint.x / width - 0.5) * aspectRatio;
1327 			normalizedPoint.y = (imagePoint.y / height - 0.5) * (-1.0) * reverse;
1328 
1329 			normalizedPoint *= wheelDistance;
1330 
1331 			viewVector = CalculateViewVector(normalizedPoint, fov, perspType, mRot);
1332 
1333 			CVector3 point = camera + viewVector * depth;
1334 
1335 			switch (clickMode)
1336 			{
1337 				case RenderedImage::clickMoveCamera:
1338 				{
1339 					double distance = (camera - point).Length();
1340 
1341 					double moveDistance = (stepMode == absolute) ? movementStep : distance * movementStep;
1342 					moveDistance *= wheelDistance;
1343 
1344 					if (stepMode == relative)
1345 					{
1346 						if (moveDistance > depth * 0.99) moveDistance = depth * 0.99;
1347 					}
1348 
1349 					if (button == Qt::RightButton)
1350 					{
1351 						moveDistance *= -1.0;
1352 					}
1353 
1354 					switch (movementMode)
1355 					{
1356 						case moveTarget: target = point; break;
1357 
1358 						case moveCamera: camera += viewVector * moveDistance; break;
1359 
1360 						case fixedDistance:
1361 							camera += viewVector * moveDistance;
1362 							target = point;
1363 							break;
1364 					}
1365 
1366 					// recalculation of camera-target
1367 					if (movementMode == moveCamera)
1368 						cameraTarget.SetCamera(camera, rollMode);
1369 					else if (movementMode == moveTarget)
1370 						cameraTarget.SetTarget(target, rollMode);
1371 					else if (movementMode == fixedDistance)
1372 						cameraTarget.SetCameraTargetTop(camera, target, topVector);
1373 
1374 					if (rollMode == cCameraTarget::constantRoll)
1375 					{
1376 						cameraTarget.SetCameraTargetRotation(camera, target, angles.z);
1377 					}
1378 
1379 					gPar->Set("camera", camera);
1380 					gPar->Set("target", target);
1381 
1382 					topVector = cameraTarget.GetTopVector();
1383 					gPar->Set("camera_top", topVector);
1384 					CVector3 rotation = cameraTarget.GetRotation();
1385 					gPar->Set("camera_rotation", rotation * (180.0 / M_PI));
1386 					double dist = cameraTarget.GetDistance();
1387 					gPar->Set("camera_distance_to_target", dist);
1388 
1389 					SynchronizeInterface(gPar, gParFractal, qInterface::write);
1390 					renderedImage->setNewZ(depth - moveDistance);
1391 
1392 					StartRender();
1393 
1394 					break;
1395 				}
1396 				case RenderedImage::clickFogVisibility:
1397 				{
1398 					double fogDepth = depth;
1399 					gPar->Set("basic_fog_visibility", fogDepth);
1400 					mainWindow->ui->widgetEffects->SynchronizeInterfaceBasicFogEnabled(gPar);
1401 					StartRender();
1402 					break;
1403 				}
1404 				case RenderedImage::clickDOFFocus:
1405 				{
1406 					DisablePeriodicRefresh();
1407 					double DOF = depth;
1408 					gPar->Set("DOF_focus", DOF);
1409 					mainWindow->ui->widgetEffects->SynchronizeInterfaceDOFEnabled(gPar);
1410 					gUndo->Store(gPar, gParFractal);
1411 					RefreshPostEffects();
1412 					ReEnablePeriodicRefresh();
1413 					break;
1414 				}
1415 				case RenderedImage::clickPlaceLight:
1416 				{
1417 					int lightIndex = mode.at(1).toInt();
1418 					double frontDist = gPar->Get<double>("aux_light_manual_placement_dist");
1419 					bool placeBehind = gPar->Get<bool>("aux_light_place_behind");
1420 					double distanceLimit = gPar->Get<double>("view_distance_max");
1421 					bool relativePosition = gPar->Get<bool>(cLight::Name("relative_position", lightIndex));
1422 
1423 					CVector3 pointCorrected;
1424 
1425 					if (!placeBehind)
1426 					{
1427 						pointCorrected = point - viewVector * frontDist;
1428 					}
1429 					else
1430 					{
1431 						frontDist = traceBehindFractal(gPar, gParFractal, frontDist, viewVector, depth,
1432 													1.0 / mainImage->GetHeight(), distanceLimit)
1433 												* (-1.0);
1434 						pointCorrected = point - viewVector * frontDist;
1435 					}
1436 
1437 					double estDistance = GetDistanceForPoint(pointCorrected, gPar, gParFractal);
1438 					double intensity = estDistance * estDistance;
1439 
1440 					if (relativePosition)
1441 					{
1442 						// without rotation
1443 						CVector3 viewVectorTemp =
1444 							CalculateViewVector(normalizedPoint, fov, perspType, CRotationMatrix());
1445 
1446 						CVector3 point2 = viewVectorTemp * (depth - frontDist);
1447 						pointCorrected = CVector3(point2.x, point2.z, point2.y);
1448 					}
1449 
1450 					gPar->Set(cLight::Name("position", lightIndex), pointCorrected);
1451 					gPar->Set(cLight::Name("intensity", lightIndex), intensity);
1452 					mainWindow->ui->widgetEffects->SynchronizeInterfaceLights(gPar);
1453 					StartRender();
1454 					break;
1455 				}
1456 				case RenderedImage::clickGetJuliaConstant:
1457 				{
1458 					gPar->Set("julia_c", point);
1459 					mainWindow->ui->widgetDockFractal->EnableJuliaMode();
1460 					mainWindow->ui->widgetDockFractal->SynchronizeInterfaceJulia();
1461 
1462 					// StartRender();
1463 					break;
1464 				}
1465 				case RenderedImage::clickPlacePrimitive:
1466 				{
1467 					QString parameterName = mode.at(3).toString() + "_position";
1468 					gPar->Set(parameterName, point);
1469 					mainWindow->ui->widgetDockFractal->SynchronizeInterfacePrimitives();
1470 					break;
1471 				}
1472 				case RenderedImage::clickDoNothing:
1473 					// nothing
1474 					break;
1475 				case RenderedImage::clickFlightSpeedControl:
1476 					// nothing
1477 					break;
1478 				case RenderedImage::clickPlaceRandomLightCenter:
1479 				{
1480 					double distanceCameraToCenter = CVector3(camera - point).Length();
1481 					gPar->Set("random_lights_distribution_center", point);
1482 					gPar->Set("random_lights_distribution_radius", 0.5 * distanceCameraToCenter);
1483 					gPar->Set("random_lights_max_distance_from_fractal", 0.1 * distanceCameraToCenter);
1484 					mainWindow->ui->widgetEffects->SynchronizeInterfaceRandomLights(gPar);
1485 					StartRender();
1486 					break;
1487 				}
1488 				case RenderedImage::clickGetPoint:
1489 				{
1490 					DisablePeriodicRefresh();
1491 					SynchronizeInterface(gPar, gParFractal, qInterface::read);
1492 					CVector3 oldPoint = gPar->Get<CVector3>("meas_point");
1493 					double distanceFromLast = (point - oldPoint).Length();
1494 					double distanceFromCamera = (point - camera).Length();
1495 					CVector3 midPoint = 0.5 * (point + oldPoint);
1496 					gPar->Set("meas_point", point);
1497 					gPar->Set("meas_midpoint", midPoint);
1498 					gPar->Set("meas_distance_from_last", distanceFromLast);
1499 					gPar->Set("meas_distance_from_camera", distanceFromCamera);
1500 					SynchronizeInterfaceWindow(
1501 						mainWindow->ui->dockWidget_measurement, gPar, qInterface::write);
1502 					if (!mainWindow->ui->actionShow_measurement_dock->isChecked())
1503 					{
1504 						mainWindow->ui->actionShow_measurement_dock->setChecked(true);
1505 						mainWindow->slotUpdateDocksAndToolbarByAction();
1506 					}
1507 					ReEnablePeriodicRefresh();
1508 					break;
1509 				}
1510 				case RenderedImage::clickWrapLimitsAroundObject:
1511 				{
1512 					double distanceCameraToCenter = CVector3(camera - point).Length();
1513 					CVector3 distanceV111_100 = CVector3(1.0 * distanceCameraToCenter,
1514 						1.0 * distanceCameraToCenter, 1.0 * distanceCameraToCenter);
1515 					CVector3 limitMin = point - distanceV111_100;
1516 					CVector3 limitMax = point + distanceV111_100;
1517 					// try to find object close limits in the bounding box defined by point +- 100% distance
1518 					// to view vector
1519 					SetBoundingBoxAsLimits(limitMin, limitMax);
1520 					break;
1521 				}
1522 			}
1523 		}
1524 	}
1525 }
1526 
MouseDragStart(CVector2<double> screenPoint,Qt::MouseButtons button,const QList<QVariant> & mode)1527 void cInterface::MouseDragStart(
1528 	CVector2<double> screenPoint, Qt::MouseButtons button, const QList<QVariant> &mode)
1529 {
1530 	mouseDragData = sMouseDragInitData();
1531 
1532 	RenderedImage::enumClickMode clickMode = RenderedImage::enumClickMode(mode.at(0).toInt());
1533 
1534 	int lightIndex = -1;
1535 	if (clickMode == RenderedImage::clickPlaceLight)
1536 	{
1537 		lightIndex = mode.at(1).toInt();
1538 		mouseDragData.lightDrag = true;
1539 
1540 		mouseDragData.lightStartPosition = gPar->Get<CVector3>(cLight::Name("position", lightIndex));
1541 	}
1542 	else
1543 	{
1544 		mouseDragData.cameraDrag = true;
1545 	}
1546 
1547 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
1548 	CVector3 camera = gPar->Get<CVector3>("camera");
1549 	CVector3 target = gPar->Get<CVector3>("target");
1550 	CVector3 topVector = gPar->Get<CVector3>("camera_top");
1551 	cCameraTarget cameraTarget(camera, target, topVector);
1552 
1553 	params::enumPerspectiveType perspType =
1554 		params::enumPerspectiveType(gPar->Get<int>("perspective_type"));
1555 	double sweetSpotHAngle = gPar->Get<double>("sweet_spot_horizontal_angle") / 180.0 * M_PI;
1556 	double sweetSpotVAngle = gPar->Get<double>("sweet_spot_vertical_angle") / 180.0 * M_PI;
1557 	double fov = CalcFOV(gPar->Get<double>("fov"), perspType);
1558 	bool legacyCoordinateSystem = gPar->Get<bool>("legacy_coordinate_system");
1559 	double reverse = legacyCoordinateSystem ? -1.0 : 1.0;
1560 
1561 	CVector2<double> imagePoint;
1562 	imagePoint = screenPoint / mainImage->GetPreviewScale();
1563 
1564 	int width = mainImage->GetWidth();
1565 	int height = mainImage->GetHeight();
1566 
1567 	if (imagePoint.x >= 0 && imagePoint.x < mainImage->GetWidth() && imagePoint.y >= 0
1568 			&& imagePoint.y < mainImage->GetHeight())
1569 	{
1570 		double depth = mainImage->GetPixelZBuffer(imagePoint.x, imagePoint.y);
1571 		if (depth < 1e10 || clickMode == RenderedImage::clickPlaceLight)
1572 		{
1573 			CVector3 viewVector;
1574 			double aspectRatio = double(width) / height;
1575 
1576 			if (perspType == params::perspEquirectangular) aspectRatio = 2.0;
1577 
1578 			CVector3 angles = cameraTarget.GetRotation();
1579 			CRotationMatrix mRot;
1580 			mRot.SetRotation(angles);
1581 			mRot.RotateZ(-sweetSpotHAngle);
1582 			mRot.RotateX(sweetSpotVAngle);
1583 
1584 			CVector2<double> normalizedPoint;
1585 			normalizedPoint.x = (imagePoint.x / width - 0.5) * aspectRatio;
1586 			normalizedPoint.y = (imagePoint.y / height - 0.5) * (-1.0) * reverse;
1587 
1588 			viewVector = CalculateViewVector(normalizedPoint, fov, perspType, mRot);
1589 
1590 			CVector3 point = camera + viewVector * depth;
1591 
1592 			mouseDragData.startCamera = camera;
1593 			mouseDragData.startTarget = target;
1594 			mouseDragData.startTopVector = topVector;
1595 			mouseDragData.startIndicatedPoint = point;
1596 			mouseDragData.button = button;
1597 			mouseDragData.startScreenPoint = screenPoint;
1598 			mouseDragData.startNormalizedPoint = normalizedPoint;
1599 			mouseDragData.startZ = depth;
1600 			mouseDragData.lastRefreshTime.restart();
1601 			mouseDragData.lastStartRenderingTime = 0;
1602 			mouseDragData.lightIndex = lightIndex;
1603 
1604 			if (clickMode == RenderedImage::clickMoveCamera
1605 					|| clickMode == RenderedImage::clickPlaceLight)
1606 			{
1607 				mouseDragData.draggingStarted = true;
1608 			}
1609 		}
1610 	}
1611 }
1612 
MouseDragFinish()1613 void cInterface::MouseDragFinish()
1614 {
1615 	mouseDragData.draggingStarted = false;
1616 }
1617 
MouseDragCameraLeftButton(const sMouseDragTempData & dragTempData)1618 void cInterface::MouseDragCameraLeftButton(const sMouseDragTempData &dragTempData)
1619 {
1620 	CVector3 camera = mouseDragData.startCamera;
1621 	cCameraTarget cameraTarget(camera, mouseDragData.startTarget, mouseDragData.startTopVector);
1622 
1623 	CVector3 angles = cameraTarget.GetRotation();
1624 	CRotationMatrix mRot;
1625 	mRot.SetRotation(angles);
1626 	mRot.RotateZ(-dragTempData.sweetSpotHAngle);
1627 	mRot.RotateX(dragTempData.sweetSpotVAngle);
1628 
1629 	CVector2<double> normalizedPoint;
1630 	normalizedPoint.x =
1631 		(dragTempData.imagePoint.x / dragTempData.width - 0.5) * dragTempData.aspectRatio;
1632 	normalizedPoint.y =
1633 		(dragTempData.imagePoint.y / dragTempData.height - 0.5) * (-1.0) * dragTempData.reverse;
1634 
1635 	CVector3 viewVector =
1636 		CalculateViewVector(normalizedPoint, dragTempData.fov, dragTempData.perspType, mRot);
1637 
1638 	CVector3 point = camera + viewVector * mouseDragData.startZ;
1639 	CVector3 deltaPoint = point - mouseDragData.startIndicatedPoint;
1640 	CVector3 pointCamera = camera - point;
1641 	pointCamera.Normalize();
1642 
1643 	CVector3 relativeVector;
1644 	relativeVector.z = pointCamera.Dot(deltaPoint);
1645 	relativeVector.x = pointCamera.Cross(cameraTarget.GetTopVector()).Dot(deltaPoint);
1646 	relativeVector.y = pointCamera.Cross(cameraTarget.GetRightVector()).Dot(deltaPoint);
1647 
1648 	double ratio = (camera - mouseDragData.startTarget).Length() / (camera - point).Length();
1649 	if (dragTempData.perspType == params::perspThreePoint)
1650 	{
1651 		ratio /= -pointCamera.Dot(cameraTarget.GetForwardVector());
1652 	}
1653 	relativeVector *= ratio;
1654 
1655 	CVector3 newTarget = mouseDragData.startTarget;
1656 	newTarget -= relativeVector.x * cameraTarget.GetRightVector();
1657 	newTarget += relativeVector.y * cameraTarget.GetTopVector();
1658 	newTarget += relativeVector.z * cameraTarget.GetForwardVector();
1659 
1660 	cameraTarget.SetTarget(newTarget, dragTempData.rollMode);
1661 
1662 	gPar->Set("camera", camera);
1663 	gPar->Set("target", newTarget);
1664 	CVector3 topVector = cameraTarget.GetTopVector();
1665 	gPar->Set("camera_top", topVector);
1666 	CVector3 rotation = cameraTarget.GetRotation();
1667 	gPar->Set("camera_rotation", rotation * (180.0 / M_PI));
1668 	double dist = cameraTarget.GetDistance();
1669 	gPar->Set("camera_distance_to_target", dist);
1670 }
1671 
MouseDragCaneraRightButton(int dx,int dy,const sMouseDragTempData & dragTempData)1672 void cInterface::MouseDragCaneraRightButton(int dx, int dy, const sMouseDragTempData &dragTempData)
1673 {
1674 	cCameraTarget cameraTarget(
1675 		mouseDragData.startCamera, mouseDragData.startTarget, mouseDragData.startTopVector);
1676 
1677 	CVector3 shiftedCamera = mouseDragData.startCamera - mouseDragData.startIndicatedPoint;
1678 	CVector3 shiftedTarget = mouseDragData.startTarget - mouseDragData.startIndicatedPoint;
1679 	shiftedCamera = shiftedCamera.RotateAroundVectorByAngle(
1680 		cameraTarget.GetTopVector(), (double)(dx) / mainImage->GetPreviewWidth() * M_PI_2);
1681 	shiftedTarget = shiftedTarget.RotateAroundVectorByAngle(
1682 		cameraTarget.GetTopVector(), (double)(dx) / mainImage->GetPreviewWidth() * M_PI_2);
1683 
1684 	CVector3 newCamera = shiftedCamera + mouseDragData.startIndicatedPoint;
1685 	CVector3 newTarget = shiftedTarget + mouseDragData.startIndicatedPoint;
1686 	cameraTarget.SetCamera(newCamera, dragTempData.rollMode);
1687 	cameraTarget.SetTarget(newTarget, dragTempData.rollMode);
1688 
1689 	shiftedCamera = shiftedCamera.RotateAroundVectorByAngle(
1690 		cameraTarget.GetRightVector(), (double)(dy) / mainImage->GetPreviewHeight() * M_PI_2);
1691 	shiftedTarget = shiftedTarget.RotateAroundVectorByAngle(
1692 		cameraTarget.GetRightVector(), (double)(dy) / mainImage->GetPreviewHeight() * M_PI_2);
1693 
1694 	newCamera = shiftedCamera + mouseDragData.startIndicatedPoint;
1695 	newTarget = shiftedTarget + mouseDragData.startIndicatedPoint;
1696 
1697 	cameraTarget.SetCamera(newCamera, dragTempData.rollMode);
1698 	cameraTarget.SetTarget(newTarget, dragTempData.rollMode);
1699 
1700 	gPar->Set("camera", cameraTarget.GetCamera());
1701 	gPar->Set("target", cameraTarget.GetTarget());
1702 	CVector3 topVector = cameraTarget.GetTopVector();
1703 	gPar->Set("camera_top", topVector);
1704 	CVector3 rotation = cameraTarget.GetRotation();
1705 	gPar->Set("camera_rotation", rotation * (180.0 / M_PI));
1706 	double dist = cameraTarget.GetDistance();
1707 	gPar->Set("camera_distance_to_target", dist);
1708 }
1709 
MouseDragCameraMiddleButton(int dx)1710 void cInterface::MouseDragCameraMiddleButton(int dx)
1711 {
1712 	double angle = -(double)(dx) / mainImage->GetPreviewHeight() * M_PI_2;
1713 	cCameraTarget cameraTarget(
1714 		mouseDragData.startCamera, mouseDragData.startTarget, mouseDragData.startTopVector);
1715 	CVector3 newTopVector =
1716 		mouseDragData.startTopVector.RotateAroundVectorByAngle(cameraTarget.GetForwardVector(), angle);
1717 	cameraTarget.SetCameraTargetTop(
1718 		mouseDragData.startCamera, mouseDragData.startTarget, newTopVector);
1719 	gPar->Set("camera_top", newTopVector);
1720 	CVector3 rotation = cameraTarget.GetRotation();
1721 	gPar->Set("camera_rotation", rotation * (180.0 / M_PI));
1722 }
1723 
MouseDragLeftRightButtons(const sMouseDragTempData & dragTempData)1724 void cInterface::MouseDragLeftRightButtons(const sMouseDragTempData &dragTempData)
1725 {
1726 	CVector3 camera = mouseDragData.startCamera;
1727 	cCameraTarget cameraTarget(camera, mouseDragData.startTarget, mouseDragData.startTopVector);
1728 
1729 	CVector3 angles = cameraTarget.GetRotation();
1730 	CRotationMatrix mRot;
1731 	mRot.SetRotation(angles);
1732 	mRot.RotateZ(-dragTempData.sweetSpotHAngle);
1733 	mRot.RotateX(dragTempData.sweetSpotVAngle);
1734 
1735 	CVector2<double> normalizedPoint;
1736 	normalizedPoint.x =
1737 		(dragTempData.imagePoint.x / dragTempData.width - 0.5) * dragTempData.aspectRatio;
1738 	normalizedPoint.y =
1739 		(dragTempData.imagePoint.y / dragTempData.height - 0.5) * (-1.0) * dragTempData.reverse;
1740 
1741 	CVector3 viewVector =
1742 		CalculateViewVector(normalizedPoint, dragTempData.fov, dragTempData.perspType, mRot);
1743 
1744 	CVector3 point = camera + viewVector * mouseDragData.startZ;
1745 	CVector3 deltaPoint = point - mouseDragData.startIndicatedPoint;
1746 	CVector3 newTarget = mouseDragData.startTarget + deltaPoint;
1747 	CVector3 newCamera = mouseDragData.startCamera + deltaPoint;
1748 
1749 	cameraTarget.SetCameraTargetTop(newCamera, newTarget, cameraTarget.GetTopVector());
1750 
1751 	gPar->Set("camera", newCamera);
1752 	gPar->Set("target", newTarget);
1753 	CVector3 topVector = cameraTarget.GetTopVector();
1754 	gPar->Set("camera_top", topVector);
1755 	CVector3 rotation = cameraTarget.GetRotation();
1756 	gPar->Set("camera_rotation", rotation * (180.0 / M_PI));
1757 	double dist = cameraTarget.GetDistance();
1758 	gPar->Set("camera_distance_to_target", dist);
1759 }
1760 
LightDragLeftButton(const sMouseDragTempData & dragTempData,int dx,int dy)1761 void cInterface::LightDragLeftButton(const sMouseDragTempData &dragTempData, int dx, int dy)
1762 {
1763 	bool relativePosition =
1764 		gPar->Get<bool>(cLight::Name("relative_position", mouseDragData.lightIndex));
1765 
1766 	cCameraTarget cameraTarget(
1767 		mouseDragData.startCamera, mouseDragData.startTarget, mouseDragData.startTopVector);
1768 
1769 	CVector3 lightPosition = mouseDragData.lightStartPosition;
1770 
1771 	if (relativePosition)
1772 	{
1773 		CVector3 deltaPositionRotated = cameraTarget.GetForwardVector() * lightPosition.z
1774 																		+ cameraTarget.GetTopVector() * lightPosition.y
1775 																		+ cameraTarget.GetRightVector() * lightPosition.x;
1776 		lightPosition = mouseDragData.startCamera + deltaPositionRotated;
1777 	}
1778 
1779 	CRotationMatrix mRotInv;
1780 	CVector3 rotation = cameraTarget.GetRotation();
1781 	mRotInv.RotateY(-rotation.z);
1782 	mRotInv.RotateX(-rotation.y);
1783 	mRotInv.RotateZ(-rotation.x);
1784 
1785 	CVector3 lightScreenPosition =
1786 		InvProjection3D(lightPosition, mouseDragData.startCamera, mRotInv, dragTempData.perspType,
1787 			dragTempData.fov, mainImage->GetPreviewWidth(), mainImage->GetPreviewHeight());
1788 	CVector3 newLightScreenPosition = lightScreenPosition + CVector3(dx, dy, 0.0);
1789 
1790 	CRotationMatrix mRot;
1791 	mRot.SetRotation(rotation);
1792 
1793 	CVector2<double> normalizedPoint;
1794 	normalizedPoint.x =
1795 		(newLightScreenPosition.x / mainImage->GetPreviewWidth() - 0.5) * dragTempData.aspectRatio;
1796 	normalizedPoint.y = (newLightScreenPosition.y / mainImage->GetPreviewHeight() - 0.5) * (-1.0)
1797 											* dragTempData.reverse;
1798 
1799 	CVector3 viewVector =
1800 		CalculateViewVector(normalizedPoint, dragTempData.fov, dragTempData.perspType, mRot);
1801 
1802 	CVector3 newLightPosition;
1803 	if (relativePosition)
1804 	{
1805 		// without rotation
1806 		CVector3 viewVectorTemp = CalculateViewVector(
1807 			normalizedPoint, dragTempData.fov, dragTempData.perspType, CRotationMatrix());
1808 		CVector3 point2 = viewVectorTemp * lightScreenPosition.z;
1809 		newLightPosition = CVector3(point2.x, point2.z, point2.y);
1810 	}
1811 	else
1812 	{
1813 		newLightPosition = mouseDragData.startCamera + viewVector * lightScreenPosition.z;
1814 	}
1815 
1816 	gPar->Set(cLight::Name("position", mouseDragData.lightIndex), newLightPosition);
1817 }
1818 
MouseDragDelta(int dx,int dy)1819 void cInterface::MouseDragDelta(int dx, int dy)
1820 {
1821 	if (mouseDragData.draggingStarted)
1822 	{
1823 		if (numberOfStartedRenders > 1) stopRequest = true;
1824 
1825 		if (mouseDragData.lastRefreshTime.elapsed()
1826 					> gPar->Get<double>("auto_refresh_period") * 1000 + mouseDragData.lastStartRenderingTime
1827 				&& numberOfStartedRenders < 2)
1828 		{
1829 			sMouseDragTempData dragTempData;
1830 
1831 			mouseDragData.lastRefreshTime.restart();
1832 			dragTempData.perspType = params::enumPerspectiveType(gPar->Get<int>("perspective_type"));
1833 			dragTempData.sweetSpotHAngle =
1834 				gPar->Get<double>("sweet_spot_horizontal_angle") / 180.0 * M_PI;
1835 			dragTempData.sweetSpotVAngle = gPar->Get<double>("sweet_spot_vertical_angle") / 180.0 * M_PI;
1836 			dragTempData.legacyCoordinateSystem = gPar->Get<bool>("legacy_coordinate_system");
1837 			dragTempData.reverse = dragTempData.legacyCoordinateSystem ? -1.0 : 1.0;
1838 			dragTempData.fov = CalcFOV(gPar->Get<double>("fov"), dragTempData.perspType);
1839 			dragTempData.rollMode =
1840 				cCameraTarget::enumRotationMode(gPar->Get<int>("camera_straight_rotation"));
1841 
1842 			dragTempData.newScreenPoint = CVector2<double>(
1843 				mouseDragData.startScreenPoint.x - dx, mouseDragData.startScreenPoint.y - dy);
1844 			dragTempData.imagePoint = dragTempData.newScreenPoint / mainImage->GetPreviewScale();
1845 
1846 			dragTempData.width = mainImage->GetWidth();
1847 			dragTempData.height = mainImage->GetHeight();
1848 			dragTempData.aspectRatio = double(dragTempData.width) / dragTempData.height;
1849 			if (dragTempData.perspType == params::perspEquirectangular) dragTempData.aspectRatio = 2.0;
1850 
1851 			if (mouseDragData.cameraDrag)
1852 			{
1853 				switch (mouseDragData.button)
1854 				{
1855 					case Qt::LeftButton:
1856 					{
1857 						MouseDragCameraLeftButton(dragTempData);
1858 						break;
1859 					}
1860 					case Qt::RightButton:
1861 					{
1862 						MouseDragCaneraRightButton(dx, dy, dragTempData);
1863 						break;
1864 					}
1865 					case Qt::MiddleButton:
1866 					{
1867 						MouseDragCameraMiddleButton(dx);
1868 						break;
1869 					}
1870 
1871 					case (Qt::LeftButton | Qt::RightButton):
1872 					{
1873 						MouseDragLeftRightButtons(dragTempData);
1874 						break;
1875 					}
1876 
1877 					default: break;
1878 				}
1879 
1880 				QElapsedTimer timerStartRender;
1881 				timerStartRender.start();
1882 				SynchronizeInterface(gPar, gParFractal, qInterface::write);
1883 				StartRender();
1884 				mouseDragData.lastStartRenderingTime = timerStartRender.elapsed();
1885 			}
1886 			else if (mouseDragData.lightDrag)
1887 			{
1888 				switch (mouseDragData.button)
1889 				{
1890 					case Qt::LeftButton:
1891 					{
1892 						LightDragLeftButton(dragTempData, dx, dy);
1893 						break;
1894 					}
1895 					default:
1896 					{
1897 						break;
1898 					}
1899 				}
1900 				QElapsedTimer timerStartRender;
1901 				timerStartRender.start();
1902 				SynchronizeInterface(gPar, gParFractal, qInterface::write);
1903 				renderedImage->update();
1904 				mouseDragData.lastStartRenderingTime = timerStartRender.elapsed();
1905 			}
1906 		}
1907 	}
1908 }
1909 
MoveLightByWheel(double deltaWheel)1910 void cInterface::MoveLightByWheel(double deltaWheel)
1911 {
1912 	double deltaLog = exp(deltaWheel * 0.0001);
1913 
1914 	CVector3 lightPosition =
1915 		gPar->Get<CVector3>(cLight::Name("position", renderedImage->GetCurrentLightIndex()));
1916 
1917 	bool relativePosition =
1918 		gPar->Get<bool>(cLight::Name("relative_position", renderedImage->GetCurrentLightIndex()));
1919 
1920 	CVector3 newLightPosition;
1921 
1922 	if (relativePosition)
1923 	{
1924 		newLightPosition = lightPosition * deltaLog;
1925 	}
1926 	else
1927 	{
1928 		CVector3 cameraPosition = gPar->Get<CVector3>("camera");
1929 		CVector3 lightVector = lightPosition - cameraPosition;
1930 		CVector3 newLightVector = lightVector * deltaLog;
1931 		newLightPosition = cameraPosition + newLightVector;
1932 	}
1933 
1934 	gPar->Set(cLight::Name("position", renderedImage->GetCurrentLightIndex()), newLightPosition);
1935 	SynchronizeInterface(gPar, gParFractal, qInterface::write);
1936 	renderedImage->update();
1937 }
1938 
MovementStepModeChanged(int mode) const1939 void cInterface::MovementStepModeChanged(int mode) const
1940 {
1941 	using namespace cameraMovementEnums;
1942 
1943 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
1944 	enumCameraMovementStepMode stepMode = enumCameraMovementStepMode(mode);
1945 	double distance = GetDistanceForPoint(gPar->Get<CVector3>("camera"), gPar, gParFractal);
1946 	double oldStep = gPar->Get<double>("camera_movement_step");
1947 	double newStep;
1948 	if (stepMode == absolute)
1949 	{
1950 		newStep = oldStep * distance;
1951 		if (distance > 1.0 && newStep > distance * 0.5) newStep = distance * 0.5;
1952 	}
1953 	else
1954 	{
1955 		newStep = oldStep / distance;
1956 		if (distance > 1.0 && newStep > 0.5) newStep = 0.5;
1957 	}
1958 	gPar->Set("camera_movement_step", newStep);
1959 	SynchronizeInterfaceWindow(mainWindow->ui->dockWidget_navigation, gPar, qInterface::write);
1960 }
1961 
Undo()1962 void cInterface::Undo()
1963 {
1964 	bool refreshFrames = false;
1965 	bool refreshKeyframes = false;
1966 	DisablePeriodicRefresh();
1967 	gInterfaceReadyForSynchronization = false;
1968 	if (gUndo->Undo(gPar, gParFractal, gAnimFrames, gKeyframes, &refreshFrames, &refreshKeyframes))
1969 	{
1970 		RebuildPrimitives(gPar);
1971 		materialListModel->Regenerate();
1972 		mainWindow->ui->widgetEffects->RegenerateLights();
1973 		gInterfaceReadyForSynchronization = false;
1974 		SynchronizeInterface(gPar, gParFractal, qInterface::write);
1975 		if (refreshFrames) gFlightAnimation->RefreshTable();
1976 		if (refreshKeyframes) gKeyframeAnimation->RefreshTable();
1977 		StartRender(true);
1978 	}
1979 	gInterfaceReadyForSynchronization = true;
1980 	ReEnablePeriodicRefresh();
1981 }
1982 
Redo()1983 void cInterface::Redo()
1984 {
1985 	bool refreshFrames = false;
1986 	bool refreshKeyframes = false;
1987 	DisablePeriodicRefresh();
1988 	gInterfaceReadyForSynchronization = false;
1989 	if (gUndo->Redo(gPar, gParFractal, gAnimFrames, gKeyframes, &refreshFrames, &refreshKeyframes))
1990 	{
1991 		RebuildPrimitives(gPar);
1992 		materialListModel->Regenerate();
1993 		mainWindow->ui->widgetEffects->RegenerateLights();
1994 		gInterfaceReadyForSynchronization = false;
1995 		SynchronizeInterface(gPar, gParFractal, qInterface::write);
1996 		if (refreshFrames) gFlightAnimation->RefreshTable();
1997 		if (refreshKeyframes) gKeyframeAnimation->RefreshTable();
1998 		StartRender(true);
1999 	}
2000 	gInterfaceReadyForSynchronization = true;
2001 	ReEnablePeriodicRefresh();
2002 }
2003 
ResetView()2004 void cInterface::ResetView()
2005 {
2006 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
2007 
2008 	CVector3 camera = gPar->Get<CVector3>("camera");
2009 	CVector3 target = gPar->Get<CVector3>("target");
2010 	CVector3 topVector = gPar->Get<CVector3>("camera_top");
2011 	cCameraTarget cameraTarget(camera, target, topVector);
2012 	CVector3 forwardVector = cameraTarget.GetForwardVector();
2013 	params::enumPerspectiveType perspType =
2014 		params::enumPerspectiveType(gPar->Get<int>("perspective_type"));
2015 	double fov = CalcFOV(gPar->Get<double>("fov"), perspType);
2016 	double DEFactor = gPar->Get<double>("DE_factor");
2017 
2018 	std::shared_ptr<cParameterContainer> parTemp(new cParameterContainer);
2019 	*parTemp = *gPar;
2020 	parTemp->Set("limits_enabled", false);
2021 	parTemp->Set("interior_mode", false);
2022 
2023 	QStringList listOfParameters = parTemp->GetListOfParameters();
2024 	for (const QString &parameterName : listOfParameters)
2025 	{
2026 		if (parameterName.contains("primitive_plane") && parameterName.contains("enabled"))
2027 		{
2028 			parTemp->Set(parameterName, false);
2029 		}
2030 
2031 		if (parameterName.contains("primitive_water") && parameterName.contains("enabled"))
2032 		{
2033 			parTemp->Set(parameterName, false);
2034 		}
2035 	}
2036 
2037 	// calculate size of the fractal in random directions
2038 	double maxDist = 0.0;
2039 
2040 	std::shared_ptr<sParamRender> params(new sParamRender(parTemp));
2041 	std::shared_ptr<cNineFractals> fractals(new cNineFractals(gParFractal, parTemp));
2042 
2043 	bool openClEnabled = false;
2044 #ifdef USE_OPENCL
2045 	openClEnabled = parTemp->Get<bool>("opencl_enabled") && gParFractal->isUsedCustomFormula();
2046 
2047 	if (openClEnabled)
2048 	{
2049 		gOpenCl->openClEngineRenderFractal->Lock();
2050 		gOpenCl->openClEngineRenderFractal->SetDistanceMode();
2051 		gOpenCl->openClEngineRenderFractal->SetParameters(
2052 			parTemp, gParFractal, params, fractals, nullptr, false);
2053 		if (gOpenCl->openClEngineRenderFractal->LoadSourcesAndCompile(parTemp))
2054 		{
2055 			gOpenCl->openClEngineRenderFractal->CreateKernel4Program(parTemp);
2056 			gOpenCl->openClEngineRenderFractal->PreAllocateBuffers(parTemp);
2057 			gOpenCl->openClEngineRenderFractal->CreateCommandQueue();
2058 		}
2059 		else
2060 		{
2061 			gOpenCl->openClEngineRenderFractal->ReleaseMemory();
2062 			gOpenCl->openClEngineRenderFractal->Unlock();
2063 			return;
2064 		}
2065 	}
2066 #endif
2067 
2068 	for (int i = 0; i < 100; i++)
2069 	{
2070 		cProgressText::ProgressStatusText(QObject::tr("Resetting view"),
2071 			QObject::tr("Fractal size calculation"), i / 50.0, cProgressText::progress_IMAGE);
2072 		CVector3 direction(
2073 			Random(1000) / 500.0 - 1.0, Random(1000) / 500.0 - 1.0, Random(1000) / 500.0 - 1.0);
2074 		direction.Normalize();
2075 		double distStep;
2076 		double scan;
2077 
2078 		for (scan = 100.0; scan > 0; scan -= distStep)
2079 		{
2080 			CVector3 point = direction * scan;
2081 			sDistanceIn in(point, 0, false);
2082 			sDistanceOut out;
2083 
2084 			double dist;
2085 			if (openClEnabled)
2086 			{
2087 #ifdef USE_OPENCL
2088 				dist = gOpenCl->openClEngineRenderFractal->CalculateDistance(point);
2089 #endif
2090 			}
2091 			else
2092 			{
2093 				dist = CalculateDistance(*params, *fractals, in, &out);
2094 			}
2095 			if (dist < 0.1)
2096 			{
2097 				break;
2098 			}
2099 			distStep = dist * DEFactor * 0.5;
2100 			if (distStep > 1.0) distStep = 1.0;
2101 			// qDebug() << "i" << i << "scan" << scan << "direction" << direction.Debug();
2102 		}
2103 		if (scan > maxDist) maxDist = scan;
2104 	}
2105 	cProgressText::ProgressStatusText(
2106 		QObject::tr("Resetting view"), QObject::tr("Done"), 1.0, cProgressText::progress_IMAGE);
2107 
2108 	double newCameraDist;
2109 
2110 #ifdef USE_OPENCL
2111 	if (openClEnabled)
2112 	{
2113 		gOpenCl->openClEngineRenderFractal->ReleaseMemory();
2114 		gOpenCl->openClEngineRenderFractal->Unlock();
2115 	}
2116 #endif
2117 
2118 	if (perspType == params::perspThreePoint)
2119 	{
2120 		newCameraDist = maxDist / fov * 2.0 * sqrt(2);
2121 	}
2122 	else if (perspType == params::perspEquirectangular)
2123 	{
2124 		newCameraDist = maxDist / fov * 4.0;
2125 	}
2126 	else
2127 	{
2128 		newCameraDist = maxDist / fov * 2.0;
2129 	}
2130 
2131 	if (newCameraDist < 0.1) newCameraDist = 0.1;
2132 
2133 	gPar->Set("target", CVector3(0.0, 0.0, 0.0));
2134 	CVector3 newCamera = forwardVector * newCameraDist * (-1.0);
2135 	gPar->Set("camera", newCamera);
2136 	gPar->Set("camera_distance_to_target", newCameraDist);
2137 	gPar->Set("view_distance_max", (newCameraDist + maxDist) * 2.0);
2138 	SynchronizeInterface(gPar, gParFractal, qInterface::write);
2139 	StartRender();
2140 }
2141 
BoundingBoxMove(char dimension,double moveLower,double moveUpper)2142 void cInterface::BoundingBoxMove(char dimension, double moveLower, double moveUpper)
2143 {
2144 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
2145 	CVector3 limitMin = gPar->Get<CVector3>("limit_min");
2146 	CVector3 limitMax = gPar->Get<CVector3>("limit_max");
2147 	CVector3 limitDifference = limitMax - limitMin;
2148 	switch (dimension)
2149 	{
2150 		case 'x':
2151 			limitMin.x -= moveLower * limitDifference.x;
2152 			limitMax.x += moveUpper * limitDifference.x;
2153 			break;
2154 		case 'y':
2155 			limitMin.y -= moveLower * limitDifference.y;
2156 			limitMax.y += moveUpper * limitDifference.y;
2157 			break;
2158 		case 'z':
2159 			limitMin.z -= moveLower * limitDifference.z;
2160 			limitMax.z += moveUpper * limitDifference.z;
2161 			break;
2162 		default: break;
2163 	}
2164 	gPar->Set("limit_min", limitMin);
2165 	gPar->Set("limit_max", limitMax);
2166 	SynchronizeInterface(gPar, gParFractal, qInterface::write);
2167 }
2168 
SetBoundingBoxAsLimitsTotal()2169 void cInterface::SetBoundingBoxAsLimitsTotal()
2170 {
2171 	double outerBounding = gPar->Get<double>("limit_outer_bounding");
2172 	CVector3 outerBoundingMin(-outerBounding, -outerBounding, -outerBounding);
2173 	CVector3 outerBoundingMax(outerBounding, outerBounding, outerBounding);
2174 	SetBoundingBoxAsLimits(outerBoundingMin, outerBoundingMax);
2175 }
2176 
SetBoundingBoxAsLimits(CVector3 outerBoundingMin,CVector3 outerBoundingMax)2177 void cInterface::SetBoundingBoxAsLimits(CVector3 outerBoundingMin, CVector3 outerBoundingMax)
2178 {
2179 	CVector3 boundingCenter = (outerBoundingMin + outerBoundingMax) / 2;
2180 
2181 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
2182 
2183 	auto parTemp = std::make_shared<cParameterContainer>();
2184 	*parTemp = *gPar;
2185 
2186 	parTemp->Set("limits_enabled", false);
2187 	parTemp->Set("interior_mode", false);
2188 
2189 	std::shared_ptr<sParamRender> params(new sParamRender(parTemp));
2190 	std::shared_ptr<cNineFractals> fractals(new cNineFractals(gParFractal, parTemp));
2191 
2192 	CVector3 direction;
2193 	CVector3 orthDirection;
2194 	CVector3 point;
2195 	double dist;
2196 
2197 	stopRequest = false;
2198 
2199 	// negative x limit
2200 	cProgressText::ProgressStatusText(
2201 		QObject::tr("bounding box as limit"), QObject::tr("Negative X Limit"), 0.0 / 6.0);
2202 	direction = CVector3(1, 0, 0);
2203 	orthDirection = CVector3(0, 1, 0);
2204 	point = CVector3(outerBoundingMin.x, boundingCenter.y, boundingCenter.z);
2205 	dist = CalculateDistanceMinPlane(params, fractals, point, direction, orthDirection, &stopRequest);
2206 	double minX = point.x + dist;
2207 
2208 	// negative y limit
2209 	cProgressText::ProgressStatusText(
2210 		QObject::tr("bounding box as limit"), QObject::tr("Negative Y Limit"), 1.0 / 6.0);
2211 	direction = CVector3(0, 1, 0);
2212 	orthDirection = CVector3(0, 0, 1);
2213 	point = CVector3(boundingCenter.x, outerBoundingMin.y, boundingCenter.z);
2214 	dist = CalculateDistanceMinPlane(params, fractals, point, direction, orthDirection, &stopRequest);
2215 	double minY = point.y + dist;
2216 
2217 	// negative z limit
2218 	cProgressText::ProgressStatusText(
2219 		QObject::tr("bounding box as limit"), QObject::tr("Negative Z Limit"), 2.0 / 6.0);
2220 	direction = CVector3(0, 0, 1);
2221 	orthDirection = CVector3(1, 0, 0);
2222 	point = CVector3(boundingCenter.x, boundingCenter.y, outerBoundingMin.z);
2223 	dist = CalculateDistanceMinPlane(params, fractals, point, direction, orthDirection, &stopRequest);
2224 	double minZ = point.z + dist;
2225 
2226 	// positive x limit
2227 	cProgressText::ProgressStatusText(
2228 		QObject::tr("bounding box as limit"), QObject::tr("Positive X Limit"), 3.0 / 6.0);
2229 	direction = CVector3(-1, 0, 0);
2230 	orthDirection = CVector3(0, -1, 0);
2231 	point = CVector3(outerBoundingMax.x, boundingCenter.y, boundingCenter.z);
2232 	dist = CalculateDistanceMinPlane(params, fractals, point, direction, orthDirection, &stopRequest);
2233 	double maxX = point.x - dist;
2234 
2235 	// positive y limit
2236 	cProgressText::ProgressStatusText(
2237 		QObject::tr("bounding box as limit"), QObject::tr("Positive Y Limit"), 4.0 / 6.0);
2238 	direction = CVector3(0, -1, 0);
2239 	orthDirection = CVector3(0, 0, -1);
2240 	point = CVector3(boundingCenter.x, outerBoundingMax.y, boundingCenter.z);
2241 	dist = CalculateDistanceMinPlane(params, fractals, point, direction, orthDirection, &stopRequest);
2242 	double maxY = point.y - dist;
2243 
2244 	// positive z limit
2245 	cProgressText::ProgressStatusText(
2246 		QObject::tr("bounding box as limit"), QObject::tr("Positive Z Limit"), 5.0 / 6.0);
2247 	direction = CVector3(0, 0, -1);
2248 	orthDirection = CVector3(-1, 0, 0);
2249 	point = CVector3(boundingCenter.x, boundingCenter.y, outerBoundingMax.z);
2250 	dist = CalculateDistanceMinPlane(params, fractals, point, direction, orthDirection, &stopRequest);
2251 	double maxZ = point.z - dist;
2252 
2253 	double medX = (maxX + minX) / 2.0;
2254 	double medY = (maxY + minY) / 2.0;
2255 	double medZ = (maxZ + minZ) / 2.0;
2256 	double rangeX = maxX - minX;
2257 	double rangeY = maxY - minY;
2258 	double rangeZ = maxZ - minZ;
2259 
2260 	gPar->Set("limit_min", CVector3(medX - rangeX * 0.6, medY - rangeY * 0.6, medZ - rangeZ * 0.6));
2261 	gPar->Set("limit_max", CVector3(medX + rangeX * 0.6, medY + rangeY * 0.6, medZ + rangeZ * 0.6));
2262 
2263 	cProgressText::ProgressStatusText(QObject::tr("bounding box as limit"), QObject::tr("Done"), 1.0);
2264 	SynchronizeInterface(gPar, gParFractal, qInterface::write);
2265 }
2266 
NewPrimitive(const QString & primitiveType,int index)2267 void cInterface::NewPrimitive(const QString &primitiveType, int index)
2268 {
2269 	QString primitiveName = QString("primitive_") + primitiveType;
2270 	QString uiFileName = systemDirectories.sharedDir + "formula" + QDir::separator() + "ui"
2271 											 + QDir::separator() + primitiveName + ".ui";
2272 	fractal::enumObjectType objectType = PrimitiveNameToEnum(primitiveType);
2273 
2274 	int newId = 0;
2275 	if (index == 0)
2276 	{
2277 		// look for the lowest free id
2278 		bool occupied = true;
2279 
2280 		while (occupied)
2281 		{
2282 			newId++;
2283 			occupied = false;
2284 			for (const auto &primitiveItem : listOfPrimitives)
2285 			{
2286 				if (objectType == primitiveItem.type && newId == primitiveItem.id) occupied = true;
2287 			}
2288 		}
2289 	}
2290 	else
2291 	{
2292 		newId = index; // use given index
2293 	}
2294 
2295 	// name for new primitive
2296 	QString primitiveFullName = primitiveName + "_" + QString::number(newId);
2297 
2298 	sPrimitiveItem newItem(objectType, newId, primitiveFullName);
2299 	listOfPrimitives.append(newItem);
2300 
2301 	// main widget for primitive
2302 	QWidget *mainWidget =
2303 		new QWidget(mainWindow->ui->widgetDockFractal->GetContainerWithPrimitives());
2304 	mainWidget->setObjectName(QString("widgetmain_") + primitiveFullName);
2305 	QVBoxLayout *layout = new QVBoxLayout();
2306 	mainWidget->setLayout(layout);
2307 
2308 	QHBoxLayout *buttonsLayout = new QHBoxLayout();
2309 	layout->addLayout(buttonsLayout);
2310 
2311 	QPushButton *setPositionButton = new QPushButton(
2312 		QObject::tr("Set position of\n%1 # %2\nby mouse pointer").arg(primitiveType).arg(newId),
2313 		mainWidget);
2314 	setPositionButton->setObjectName(QString("setPositionButton_") + primitiveFullName);
2315 	buttonsLayout->addWidget(setPositionButton);
2316 
2317 	QPushButton *deleteButton = new QPushButton(
2318 		QObject::tr("Delete\n") + primitiveType + " # " + QString::number(newId), mainWidget);
2319 	deleteButton->setObjectName(QString("deleteButton_") + primitiveFullName);
2320 	buttonsLayout->addWidget(deleteButton);
2321 
2322 	QHBoxLayout *buttonsLayout2 = new QHBoxLayout();
2323 	layout->addLayout(buttonsLayout2);
2324 
2325 	QPushButton *alignButton = new QPushButton(QObject::tr("Align rotation to camera"), mainWidget);
2326 	alignButton->setObjectName(QString("alignButton_") + primitiveFullName);
2327 	buttonsLayout2->addWidget(alignButton);
2328 
2329 	MyGroupBox *groupBox = new MyGroupBox(mainWidget);
2330 	groupBox->setObjectName(QString("groupCheck_") + primitiveFullName + "_enabled");
2331 	groupBox->setCheckable(true);
2332 	groupBox->setTitle(primitiveType + " # " + QString::number(newId));
2333 	layout->addWidget(groupBox);
2334 
2335 	QVBoxLayout *layoutGroupBox = new QVBoxLayout();
2336 	groupBox->setLayout(layoutGroupBox);
2337 
2338 	// load ui
2339 	MyUiLoader loader;
2340 	QFile uiFile(uiFileName);
2341 	if (uiFile.exists())
2342 	{
2343 		uiFile.open(QFile::ReadOnly);
2344 		QWidget *primitiveWidget = loader.load(&uiFile);
2345 		uiFile.close();
2346 		primitiveWidget->setObjectName(QString("widgetui_") + primitiveFullName);
2347 		layoutGroupBox->addWidget(primitiveWidget);
2348 
2349 		// put widget into layout
2350 		QVBoxLayout *primitivesLayout = mainWindow->ui->widgetDockFractal->GetLayoutWithPrimitives();
2351 		primitivesLayout->addWidget(mainWidget);
2352 
2353 		// rename widgets
2354 		QList<QWidget *> listOfWidgets = primitiveWidget->findChildren<QWidget *>();
2355 		for (auto &widget : listOfWidgets)
2356 		{
2357 			QString name = widget->objectName();
2358 			int firstDash = name.indexOf('_');
2359 			QString newName = name.insert(firstDash + 1, primitiveFullName + "_");
2360 			widget->setObjectName(newName);
2361 		}
2362 
2363 		connect(deleteButton, SIGNAL(clicked()), mainWindow, SLOT(slotPressedButtonDeletePrimitive()));
2364 		connect(setPositionButton, SIGNAL(clicked()), mainWindow,
2365 			SLOT(slotPressedButtonSetPositionPrimitive()));
2366 		connect(
2367 			alignButton, SIGNAL(clicked()), mainWindow, SLOT(slotPressedButtonAlignPrimitiveAngle()));
2368 
2369 		// adding parameters
2370 		if (index == 0) // for only new primitive
2371 		{
2372 			InitPrimitiveParams(objectType, primitiveFullName, gPar);
2373 			gPar->Set(primitiveFullName + "_enabled", true);
2374 		}
2375 
2376 		mainWindow->automatedWidgets->ConnectSignalsForSlidersInWindow(mainWidget);
2377 		SynchronizeInterfaceWindow(mainWidget, gPar, qInterface::write);
2378 
2379 		if (gPar->Get<bool>("ui_colorize"))
2380 		{
2381 			cInterface::ColorizeGroupBoxes(
2382 				groupBox, gPar->Get<int>("ui_colorize_random_seed") + Random(65535));
2383 		}
2384 
2385 		ComboMouseClickUpdate();
2386 	}
2387 	else
2388 	{
2389 		cErrorMessage::showMessage(QObject::tr("Can't open file ") + uiFileName
2390 																 + QObject::tr(" Primitive object ui file can't be loaded"),
2391 			cErrorMessage::errorMessage, mainWindow);
2392 	}
2393 }
2394 
DeletePrimitive(const QString & primitiveName)2395 void cInterface::DeletePrimitive(const QString &primitiveName)
2396 {
2397 	QString primitiveWidgetName = QString("widgetmain_") + primitiveName;
2398 	fractal::enumObjectType objectType = fractal::objNone;
2399 
2400 	// delete widget
2401 	QWidget *primitiveWidget =
2402 		mainWindow->ui->widgetDockFractal->GetContainerWithPrimitives()->findChild<QWidget *>(
2403 			primitiveWidgetName);
2404 	delete primitiveWidget;
2405 
2406 	// remove item from list
2407 	for (int i = 0; i < listOfPrimitives.size(); i++)
2408 	{
2409 		if (primitiveName == listOfPrimitives.at(i).name)
2410 		{
2411 			objectType = listOfPrimitives.at(i).type;
2412 			listOfPrimitives.removeAt(i);
2413 		}
2414 	}
2415 
2416 	DeletePrimitiveParams(objectType, primitiveName, gPar);
2417 	ComboMouseClickUpdate();
2418 }
2419 
RebuildPrimitives(std::shared_ptr<cParameterContainer> par)2420 void cInterface::RebuildPrimitives(std::shared_ptr<cParameterContainer> par)
2421 {
2422 	// clear all widgets
2423 	for (const auto &primitiveItem : listOfPrimitives)
2424 	{
2425 		QString widgetName = QString("widgetmain_") + primitiveItem.name;
2426 		QWidget *widget =
2427 			mainWindow->ui->widgetDockFractal->GetContainerWithPrimitives()->findChild<QWidget *>(
2428 				widgetName);
2429 		delete widget;
2430 	}
2431 	listOfPrimitives.clear();
2432 
2433 	QList<QString> listOfParameters = par->GetListOfParameters();
2434 	for (auto &parameterName : listOfParameters)
2435 	{
2436 		if (parameterName.left(parameterName.indexOf('_')) == "primitive")
2437 		{
2438 			QStringList split = parameterName.split('_');
2439 			QString primitiveName = split.at(0) + "_" + split.at(1) + "_" + split.at(2);
2440 			QString objectTypeString = split.at(1);
2441 			int index = split.at(2).toInt();
2442 
2443 			bool found = false;
2444 			for (const auto &listOfPrimitive : listOfPrimitives)
2445 			{
2446 				if (listOfPrimitive.name == primitiveName)
2447 				{
2448 					found = true;
2449 					break;
2450 				}
2451 			}
2452 
2453 			if (!found)
2454 			{
2455 				NewPrimitive(objectTypeString, index);
2456 			}
2457 		}
2458 	}
2459 }
2460 
ComboMouseClickUpdate() const2461 void cInterface::ComboMouseClickUpdate() const
2462 {
2463 	QComboBox *combo = mainWindow->ui->comboBox_mouse_click_function;
2464 	int lastIndex = combo->currentIndex();
2465 
2466 	combo->clear();
2467 	QList<QVariant> item;
2468 
2469 	item.clear();
2470 	item.append(int(RenderedImage::clickDoNothing));
2471 	combo->addItem(QObject::tr("No action"), item);
2472 
2473 	item.clear();
2474 	item.append(int(RenderedImage::clickMoveCamera));
2475 	combo->addItem(QObject::tr("Move the camera"), item);
2476 
2477 	item.clear();
2478 	item.append(int(RenderedImage::clickFogVisibility));
2479 	combo->addItem(QObject::tr("Set fog visibility"), item);
2480 
2481 	item.clear();
2482 	item.append(int(RenderedImage::clickDOFFocus));
2483 	combo->addItem(QObject::tr("Set DOF focus"), item);
2484 
2485 	item.clear();
2486 	item.append(int(RenderedImage::clickGetJuliaConstant));
2487 	combo->addItem(QObject::tr("Get Julia constant"), item);
2488 
2489 	QList<int> listOfLights = cLights::GetListOfLights(gPar);
2490 
2491 	for (int lightIndex : listOfLights)
2492 	{
2493 		item.clear();
2494 		item.append(int(RenderedImage::clickPlaceLight));
2495 		item.append(lightIndex);
2496 		combo->addItem(QObject::tr("Place light #%1").arg(lightIndex), item);
2497 	}
2498 
2499 	item.clear();
2500 	item.append(int(RenderedImage::clickPlaceRandomLightCenter));
2501 	combo->addItem(QObject::tr("Place random light center"), item);
2502 
2503 	item.clear();
2504 	item.append(int(RenderedImage::clickGetPoint));
2505 	combo->addItem(QObject::tr("Get point coordinates"), item);
2506 
2507 	item.clear();
2508 	item.append(int(RenderedImage::clickWrapLimitsAroundObject));
2509 	combo->addItem(QObject::tr("Wrap Limits around object"), item);
2510 
2511 	if (listOfPrimitives.size() > 0)
2512 	{
2513 		for (const auto &primitiveItem : listOfPrimitives)
2514 		{
2515 			QString primitiveName = PrimitiveNames(primitiveItem.type);
2516 			int index = primitiveItem.id;
2517 			QString comboItemString =
2518 				QString(QObject::tr("Place ")) + primitiveName + QString(" #") + QString::number(index);
2519 			item.clear();
2520 			item.append(int(RenderedImage::clickPlacePrimitive));
2521 			item.append(int(primitiveItem.type));
2522 			item.append(primitiveItem.id);
2523 			item.append(primitiveItem.name);
2524 			combo->addItem(comboItemString, item);
2525 		}
2526 	}
2527 
2528 	if (lastIndex < combo->count())
2529 	{
2530 		combo->setCurrentIndex(lastIndex);
2531 	}
2532 }
2533 
QuitApplicationDialog()2534 bool cInterface::QuitApplicationDialog()
2535 {
2536 	bool quit = false;
2537 	int closeResult;
2538 	bool quitDoNotAskAgain = gPar->Get<bool>("quit_do_not_ask_again");
2539 	QMessageBox *messageBox = nullptr;
2540 	QAbstractButton *btnYesAndDoNotAskAgain = nullptr;
2541 	if (quitDoNotAskAgain)
2542 	{
2543 		closeResult = QMessageBox::Ok;
2544 	}
2545 	else
2546 	{
2547 		messageBox = new QMessageBox(mainWindow);
2548 		QString messageText = QObject::tr("Are you sure to close the application?");
2549 		messageBox->setText(messageText);
2550 		messageBox->setWindowTitle(QObject::tr("Quit?"));
2551 		messageBox->setIcon(QMessageBox::Question);
2552 		messageBox->addButton(QMessageBox::Ok);
2553 		messageBox->addButton(QMessageBox::Cancel);
2554 		btnYesAndDoNotAskAgain =
2555 			messageBox->addButton(QObject::tr("Yes, don't ask again"), QMessageBox::YesRole);
2556 		messageBox->setDefaultButton(QMessageBox::Ok);
2557 		closeResult = messageBox->exec();
2558 	}
2559 
2560 	switch (closeResult)
2561 	{
2562 		case QMessageBox::Cancel:
2563 		{
2564 			// nothing
2565 			break;
2566 		}
2567 		case QMessageBox::Yes:
2568 		default:
2569 		{
2570 			if (messageBox && messageBox->clickedButton() == btnYesAndDoNotAskAgain)
2571 				gPar->Set("quit_do_not_ask_again", true);
2572 			DisablePeriodicRefresh();
2573 			stopRequest = true;
2574 			systemData.globalStopRequest = true;
2575 			gQueue->stopRequest = true;
2576 			WriteLog("Quit application", 2);
2577 			// save applications settings
2578 			cSettings parSettings(cSettings::formatAppSettings);
2579 			SynchronizeInterface(gPar, gParFractal, qInterface::read);
2580 			parSettings.CreateText(gPar, gParFractal, gAnimFrames, gKeyframes);
2581 			parSettings.SaveToFile(systemData.GetIniFile());
2582 
2583 			while (cRenderJob::GetRunningJobCount() > 0)
2584 			{
2585 				gApplication->processEvents();
2586 			}
2587 
2588 			QFile::remove(systemDirectories.GetAutosaveFile());
2589 
2590 			if (detachedWindow)
2591 			{
2592 				settings.setValue("detachedWindowGeometry", detachedWindow->saveGeometry());
2593 			}
2594 
2595 			ClearNetRenderCache();
2596 
2597 			gApplication->quit();
2598 			quit = true;
2599 			break;
2600 		}
2601 	}
2602 	if (messageBox) delete messageBox;
2603 	return quit;
2604 }
2605 
AutoRecovery() const2606 void cInterface::AutoRecovery() const
2607 {
2608 	if (QFile::exists(systemDirectories.GetAutosaveFile()))
2609 	{
2610 		// auto recovery dialog
2611 		QMessageBox::StandardButton reply;
2612 		reply = QMessageBox::question(mainWindow->ui->centralwidget, QObject::tr("Auto recovery"),
2613 			QObject::tr(
2614 				"Application has not been closed properly\nDo you want to recover your latest work?"),
2615 			QMessageBox::Yes | QMessageBox::No);
2616 
2617 		if (reply == QMessageBox::Yes)
2618 		{
2619 			cSettings parSettings(cSettings::formatFullText);
2620 			gInterfaceReadyForSynchronization = false;
2621 			parSettings.LoadFromFile(systemDirectories.GetAutosaveFile());
2622 			parSettings.Decode(gPar, gParFractal, gAnimFrames, gKeyframes);
2623 			gMainInterface->RebuildPrimitives(gPar);
2624 			materialListModel->Regenerate();
2625 			mainWindow->ui->widgetEffects->RegenerateLights();
2626 			SynchronizeInterface(gPar, gParFractal, qInterface::write);
2627 			gInterfaceReadyForSynchronization = true;
2628 			gFlightAnimation->RefreshTable();
2629 			gKeyframeAnimation->RefreshTable();
2630 		}
2631 		else
2632 		{
2633 			return;
2634 		}
2635 	}
2636 }
2637 
2638 // ReSharper disable once CppMemberFunctionMayBeStatic
DataFolderUpgrade() const2639 bool cInterface::DataFolderUpgrade() const
2640 {
2641 #ifndef _WIN32
2642 	if (systemData.IsUpgraded()) return false; // already upgraded, nothing to do
2643 	bool upgradeDoNotAskAgain = gPar->Get<bool>("upgrade_do_not_ask_again");
2644 	if (upgradeDoNotAskAgain) return false; // user does not want to upgrade ever
2645 
2646 	QAbstractButton *btnNoAndDoNotAskAgain = nullptr;
2647 	QMessageBox *messageBox = new QMessageBox(mainWindow);
2648 	QString messageText = QObject::tr(
2649 		"In Mandelbulber 2.10 the default data structure changed for linux and MacOS:\n"
2650 		"Instead of keeping all working folders/files in ~/.mandelbulber these are now split into"
2651 		"<ul><li><b>.mandelbulber</b> for program internal folders/files:<br>"
2652 		"undo, toolbar, queue, thumbnails, mandelbulber.ini, miscellaneous meta files</li>"
2653 		"<li><b>mandelbulber</b> for user defined folders/files:<br>"
2654 		"settings, images, materials, slices, animation, textures</li></ul>\n"
2655 		"Do you want to upgrade now to this new structure? Program will restart after upgrade.");
2656 	messageBox->setText(messageText);
2657 	messageBox->setTextFormat(Qt::RichText);
2658 	messageBox->setWindowTitle(QObject::tr("Data folder upgrade"));
2659 	messageBox->setIcon(QMessageBox::Question);
2660 	messageBox->addButton(QMessageBox::Ok);
2661 	messageBox->addButton(QMessageBox::Cancel);
2662 	btnNoAndDoNotAskAgain =
2663 		messageBox->addButton(QObject::tr("No, don't ask again"), QMessageBox::YesRole);
2664 	messageBox->setDefaultButton(QMessageBox::Ok);
2665 	int dialogResult = messageBox->exec();
2666 
2667 	switch (dialogResult)
2668 	{
2669 		case QMessageBox::Cancel:
2670 		default:
2671 		{
2672 			if (messageBox->clickedButton() == btnNoAndDoNotAskAgain)
2673 				gPar->Set("upgrade_do_not_ask_again", true);
2674 			break;
2675 		}
2676 		case QMessageBox::Ok:
2677 		{
2678 			systemData.Upgrade();
2679 			// Needs restart
2680 			QProcess::startDetached(qApp->arguments()[0], QStringList());
2681 			delete messageBox;
2682 			return true;
2683 			break;
2684 		}
2685 	}
2686 	delete messageBox;
2687 #endif
2688 	return false;
2689 }
2690 
OptimizeStepFactor(double qualityTarget)2691 void cInterface::OptimizeStepFactor(double qualityTarget)
2692 {
2693 	DisablePeriodicRefresh();
2694 
2695 	// check if main image is not used by other rendering process
2696 	if (mainImage->IsUsed())
2697 	{
2698 		cErrorMessage::showMessage(
2699 			QObject::tr("Rendering engine is busy. Stop unfinished rendering before starting new one"),
2700 			cErrorMessage::errorMessage);
2701 		return;
2702 	}
2703 
2704 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
2705 	gUndo->Store(gPar, gParFractal);
2706 
2707 	auto tempParam = std::make_shared<cParameterContainer>();
2708 	*tempParam = *gPar;
2709 	auto tempFractal = std::make_shared<cFractalContainer>();
2710 	*tempFractal = *gParFractal;
2711 
2712 	// disabling all slow effects
2713 	tempParam->Set("light1_cast_shadows", false);
2714 	tempParam->Set("ambient_occlusion", false);
2715 	tempParam->Set("DOF_enabled", false);
2716 	tempParam->Set("iteration_threshold_mode", false);
2717 	tempParam->Set("raytraced_reflections", false);
2718 	tempParam->Set("textured_background", false);
2719 	tempParam->Set("iteration_fog_enable", false);
2720 	tempParam->Set("fake_lights_enabled", false);
2721 	tempParam->Set("main_light_volumetric_enabled", false);
2722 	tempParam->Set("opencl_mode", 0); // disable OpenCL
2723 	for (int i = 1; i <= 4; i++)
2724 	{
2725 		tempParam->Set("aux_light_enabled", i, false);
2726 		tempParam->Set("aux_light_volumetric_enabled", i, false);
2727 	}
2728 
2729 	int maxDimension = max(gPar->Get<int>("image_width"), gPar->Get<int>("image_height"));
2730 	if (maxDimension == 0) maxDimension = 1;
2731 	int newWidth = double(gPar->Get<int>("image_width")) / maxDimension * 256.0;
2732 	int newHeight = double(gPar->Get<int>("image_height")) / maxDimension * 256.0;
2733 
2734 	tempParam->Set("image_width", newWidth);
2735 	tempParam->Set("image_height", newHeight);
2736 	tempParam->Set("detail_level", 4.0);
2737 
2738 	int scanCount = 0;
2739 	double DEFactor = 1.0;
2740 	double step = 1.0;
2741 
2742 	std::unique_ptr<cRenderJob> renderJob(
2743 		new cRenderJob(tempParam, tempFractal, mainImage, &stopRequest, renderedImage));
2744 	QObject::connect(renderJob.get(), SIGNAL(updateStatistics(cStatistics)),
2745 		mainWindow->ui->widgetDockStatistics, SLOT(slotUpdateStatistics(cStatistics)));
2746 	connect(renderJob.get(), SIGNAL(updateImage()), renderedImage, SLOT(update()));
2747 
2748 	cRenderingConfiguration config;
2749 	config.DisableRefresh();
2750 	config.DisableProgressiveRender();
2751 	config.DisableNetRender();
2752 	config.SetMaxRenderTime(5.0);
2753 
2754 	renderJob->Init(cRenderJob::still, config);
2755 
2756 	cProgressText::ProgressStatusText(QObject::tr("Looking for optimal DE factor"),
2757 		QString("Percentage of wrong distance estimations: ") + QString::number(0.0), 0.0,
2758 		cProgressText::progress_IMAGE);
2759 
2760 	stopRequest = false;
2761 
2762 	double missedDE = 0.0;
2763 
2764 	for (int i = 0; i < 100; i++)
2765 	{
2766 		if (stopRequest || systemData.globalStopRequest) return;
2767 		scanCount++;
2768 		tempParam->Set("DE_factor", DEFactor);
2769 
2770 		gPar->Set("DE_factor", DEFactor);
2771 		SynchronizeInterface(gPar, gParFractal, qInterface::write);
2772 
2773 		renderJob->UpdateParameters(tempParam, tempFractal);
2774 		renderJob->Execute();
2775 		missedDE = renderJob->GetStatistics().GetMissedDEPercentage();
2776 
2777 		if (missedDE < qualityTarget)
2778 		{
2779 			if (scanCount == 1)
2780 			{
2781 				if (step < 10000)
2782 				{
2783 					DEFactor = DEFactor * 2.0;
2784 					step = step * 2.0;
2785 					scanCount = 0;
2786 					continue;
2787 				}
2788 				else
2789 				{
2790 					return;
2791 				}
2792 			}
2793 			else
2794 			{
2795 				if (step < 0.05 * DEFactor) break;
2796 				DEFactor += step;
2797 			}
2798 		}
2799 
2800 		double progress = 1.0 - 1.0 / (1.0 + pow(qualityTarget / (missedDE - qualityTarget), 2.0));
2801 		cProgressText::ProgressStatusText(QObject::tr("Looking for optimal DE factor"),
2802 			QObject::tr("Percentage of wrong distance estimations: %1").arg(missedDE), progress,
2803 			cProgressText::progress_IMAGE);
2804 
2805 		step /= 2.0;
2806 		DEFactor -= step;
2807 	}
2808 
2809 	gPar->Set("DE_factor", DEFactor);
2810 	SynchronizeInterface(gPar, gParFractal, qInterface::write);
2811 	cProgressText::ProgressStatusText(QObject::tr("Idle"),
2812 		QObject::tr("Optimal DE factor is: %1 which gives %2% of bad distance estimations")
2813 			.arg(DEFactor)
2814 			.arg(missedDE),
2815 		1.0, cProgressText::progress_IMAGE);
2816 
2817 	ReEnablePeriodicRefresh();
2818 }
2819 
ResetFormula(int fractalNumber) const2820 void cInterface::ResetFormula(int fractalNumber) const
2821 {
2822 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
2823 	gUndo->Store(gPar, gParFractal, gAnimFrames, gKeyframes);
2824 	std::shared_ptr<cParameterContainer> fractal = gParFractal->at(fractalNumber);
2825 
2826 	QStringList exclude = {"formula_code"};
2827 	fractal->ResetAllToDefault(exclude);
2828 
2829 	QStringList listToReset = {"formula_iterations", "formula_weight", "formula_start_iteration",
2830 		"formula_stop_iteration", "julia_mode", "julia_c", "fractal_constant_factor", "initial_waxis",
2831 		"formula_position", "formula_rotation", "formula_repeat", "formula_scale",
2832 		"dont_add_c_constant", "check_for_bailout"};
2833 
2834 	for (int i = 0; i < listToReset.size(); i++)
2835 	{
2836 		cOneParameter oneParameter =
2837 			gPar->GetAsOneParameter(listToReset[i] + QString("_%1").arg(fractalNumber + 1));
2838 		oneParameter.SetMultiVal(oneParameter.GetMultiVal(valueDefault), valueActual);
2839 		gPar->SetFromOneParameter(listToReset[i] + QString("_%1").arg(fractalNumber + 1), oneParameter);
2840 	}
2841 
2842 	gUndo->Store(gPar, gParFractal, gAnimFrames, gKeyframes);
2843 	SynchronizeInterface(gPar, gParFractal, qInterface::write);
2844 }
2845 
PeriodicRefresh()2846 void cInterface::PeriodicRefresh()
2847 {
2848 	if (!mouseDragData.draggingStarted)
2849 	{
2850 		if (mainWindow->ui->widgetDockNavigation->AutoRefreshIsChecked())
2851 		{
2852 			// check if something was changed in settings
2853 			SynchronizeInterface(gPar, gParFractal, qInterface::read);
2854 			cSettings tempSettings(cSettings::formatCondensedText);
2855 			tempSettings.CreateText(gPar, gParFractal);
2856 			QString newHash = tempSettings.GetHashCode();
2857 
2858 			if (newHash != autoRefreshLastHash)
2859 			{
2860 				autoRefreshLastHash = newHash;
2861 				StartRender();
2862 			}
2863 		}
2864 	}
2865 
2866 	autoRefreshTimer->start(int(gPar->Get<double>("auto_refresh_period") * 1000.0));
2867 }
2868 
DisablePeriodicRefresh()2869 void cInterface::DisablePeriodicRefresh()
2870 {
2871 	if (mainWindow)
2872 	{
2873 		autoRefreshLastState = mainWindow->ui->widgetDockNavigation->AutoRefreshIsChecked();
2874 		mainWindow->ui->widgetDockNavigation->AutoRefreshSetChecked(false);
2875 		gPar->Set("auto_refresh", false);
2876 	}
2877 }
2878 
ReEnablePeriodicRefresh()2879 void cInterface::ReEnablePeriodicRefresh()
2880 {
2881 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
2882 	cSettings tempSettings(cSettings::formatCondensedText);
2883 	tempSettings.CreateText(gPar, gParFractal);
2884 	autoRefreshLastHash = tempSettings.GetHashCode();
2885 	if (autoRefreshLastState)
2886 	{
2887 		mainWindow->ui->widgetDockNavigation->AutoRefreshSetChecked(true);
2888 		gPar->Set("auto_refresh", true);
2889 	}
2890 }
2891 
InitPeriodicRefresh()2892 void cInterface::InitPeriodicRefresh()
2893 {
2894 	autoRefreshTimer = new QTimer(mainWindow);
2895 	autoRefreshTimer->setSingleShot(true);
2896 	connect(autoRefreshTimer, SIGNAL(timeout()), mainWindow, SLOT(slotAutoRefresh()));
2897 	autoRefreshTimer->start(int(gPar->Get<double>("auto_refresh_period") * 1000.0));
2898 }
2899 
InitMaterialsUi()2900 void cInterface::InitMaterialsUi()
2901 {
2902 	materialEditor = new cMaterialEditor(mainWindow->ui->scrollArea_material);
2903 
2904 	if (gPar->Get<bool>("ui_colorize"))
2905 		materialEditor->Colorize(gPar->Get<int>("ui_colorize_random_seed"));
2906 
2907 	mainWindow->ui->verticalLayout_materials->addWidget(materialEditor);
2908 
2909 	materialListModel = new cMaterialItemModel(mainWindow->ui->scrollArea_material);
2910 	materialListModel->AssignContainer(gPar);
2911 	mainWindow->ui->widget_material_list_view->SetModel(materialListModel);
2912 
2913 	if (systemData.settingsLoadedFromCLI)
2914 	{
2915 		materialListModel->Regenerate();
2916 	}
2917 	else
2918 	{
2919 		materialListModel->insertRows(0, 1, QModelIndex());
2920 	}
2921 
2922 	materialEditor->AssignMaterial(gPar, 1);
2923 	connect(materialEditor, SIGNAL(materialChanged(int)), materialListModel,
2924 		SLOT(slotMaterialChanged(int)));
2925 
2926 	connect(mainWindow->ui->widget_material_list_view, SIGNAL(materialSelected(int)), mainWindow,
2927 		SLOT(slotMaterialSelected(int)));
2928 
2929 	connect(mainWindow->ui->widget_material_list_view, SIGNAL(materialEdited()), mainWindow,
2930 		SLOT(slotMaterialEdited()));
2931 }
2932 
MaterialSelected(int matIndex)2933 void cInterface::MaterialSelected(int matIndex)
2934 {
2935 	if (materialEditor)
2936 	{
2937 		delete materialEditor;
2938 		materialEditor = new cMaterialEditor(mainWindow->ui->scrollArea_material);
2939 
2940 		if (gPar->Get<bool>("ui_colorize"))
2941 			materialEditor->Colorize(gPar->Get<int>("ui_colorize_random_seed"));
2942 
2943 		mainWindow->ui->verticalLayout_materials->addWidget(materialEditor);
2944 		materialEditor->AssignMaterial(gPar, matIndex);
2945 		connect(materialEditor, SIGNAL(materialChanged(int)), materialListModel,
2946 			SLOT(slotMaterialChanged(int)));
2947 
2948 		if (matIndex != lastSelectedMaterial)
2949 		{
2950 			mainWindow->ui->dockWidget_materialEditor->raise();
2951 			lastSelectedMaterial = matIndex;
2952 		}
2953 	}
2954 }
2955 
StartupDefaultSettings()2956 void cInterface::StartupDefaultSettings()
2957 {
2958 	gPar->Set("DE_factor", 1.0);
2959 	gPar->Set("ambient_occlusion_enabled", true);
2960 	gPar->Set("ambient_occlusion_mode", int(params::AOModeScreenSpace));
2961 	gPar->Set("ambient_occlusion_quality", 4);
2962 	gPar->Set("raytraced_reflections", true);
2963 	gPar->Set("detail_level", 2.0);
2964 }
2965 
DisableJuliaPointMode() const2966 void cInterface::DisableJuliaPointMode() const
2967 {
2968 	QList<QVariant> itemMouseMove;
2969 	itemMouseMove.append(int(RenderedImage::clickMoveCamera));
2970 
2971 	QList<QVariant> itemJuliaMode;
2972 	itemJuliaMode.append(int(RenderedImage::clickGetJuliaConstant));
2973 
2974 	if (mainWindow->ui->comboBox_mouse_click_function->currentData() == itemJuliaMode)
2975 	{
2976 		int index = mainWindow->ui->comboBox_mouse_click_function->findData(itemMouseMove);
2977 		mainWindow->ui->comboBox_mouse_click_function->setCurrentIndex(index);
2978 		renderedImage->setClickMode(itemMouseMove);
2979 	}
2980 }
2981 // function to create icons with actual color in ColorButtons
2982 
ConnectProgressAndStatisticsSignals() const2983 void cInterface::ConnectProgressAndStatisticsSignals() const
2984 {
2985 	QObject::connect(gFlightAnimation,
2986 		SIGNAL(updateProgressAndStatus(
2987 			const QString &, const QString &, double, cProgressText::enumProgressType)),
2988 		mainWindow,
2989 		SLOT(slotUpdateProgressAndStatus(
2990 			const QString &, const QString &, double, cProgressText::enumProgressType)));
2991 	QObject::connect(gFlightAnimation, SIGNAL(updateProgressHide(cProgressText::enumProgressType)),
2992 		mainWindow, SLOT(slotUpdateProgressHide(cProgressText::enumProgressType)));
2993 	QObject::connect(gFlightAnimation, SIGNAL(updateStatistics(cStatistics)),
2994 		mainWindow->ui->widgetDockStatistics, SLOT(slotUpdateStatistics(cStatistics)));
2995 	QObject::connect(gKeyframeAnimation,
2996 		SIGNAL(updateProgressAndStatus(
2997 			const QString &, const QString &, double, cProgressText::enumProgressType)),
2998 		mainWindow,
2999 		SLOT(slotUpdateProgressAndStatus(
3000 			const QString &, const QString &, double, cProgressText::enumProgressType)));
3001 	QObject::connect(gKeyframeAnimation, SIGNAL(updateProgressHide(cProgressText::enumProgressType)),
3002 		mainWindow, SLOT(slotUpdateProgressHide(cProgressText::enumProgressType)));
3003 	QObject::connect(gKeyframeAnimation, SIGNAL(updateStatistics(cStatistics)),
3004 		mainWindow->ui->widgetDockStatistics, SLOT(slotUpdateStatistics(cStatistics)));
3005 }
3006 
MakeIconForButton(QColor & color,QPushButton * pushbutton)3007 void MakeIconForButton(QColor &color, QPushButton *pushbutton)
3008 {
3009 	const int w = 40;
3010 	const int h = 15;
3011 	QPixmap pix(w, h);
3012 	QPainter painter(&pix);
3013 	painter.fillRect(QRect(0, 0, w, h), color);
3014 	painter.drawRect(0, 0, w - 1, h - 1);
3015 	QIcon icon(pix);
3016 	pushbutton->setIcon(icon);
3017 	pushbutton->setIconSize(QSize(w, h));
3018 }
3019 
DetachMainImageWidget()3020 void cInterface::DetachMainImageWidget()
3021 {
3022 	if (!detachedWindow)
3023 	{
3024 		detachedWindow = new cDetachedWindow;
3025 		connect(detachedWindow, SIGNAL(ReturnToOrigin()),
3026 			mainWindow->ui->actionDetach_image_from_main_window, SLOT(toggle()));
3027 		connect(detachedWindow, SIGNAL(ReturnToOrigin()), mainWindow, SLOT(slotDetachMainImage()));
3028 	}
3029 	mainWindow->ui->verticalLayout->removeWidget(mainWindow->ui->widgetWithImage);
3030 	detachedWindow->InstallImageWidget(mainWindow->ui->widgetWithImage);
3031 	detachedWindow->restoreGeometry(settings.value("detachedWindowGeometry").toByteArray());
3032 	detachedWindow->show();
3033 	// mainWindow->centralWidget()->hide();
3034 	gPar->Set("image_detached", true);
3035 }
3036 
AttachMainImageWidget()3037 void cInterface::AttachMainImageWidget()
3038 {
3039 	if (detachedWindow)
3040 	{
3041 		detachedWindow->RemoveImageWidget(mainWindow->ui->widgetWithImage);
3042 		mainWindow->ui->verticalLayout->addWidget(mainWindow->ui->widgetWithImage);
3043 		settings.setValue("detachedWindowGeometry", detachedWindow->saveGeometry());
3044 		// mainWindow->centralWidget()->show();
3045 		gPar->Set("image_detached", false);
3046 	}
3047 }
3048 
CameraMovementModeChanged(int index)3049 void cInterface::CameraMovementModeChanged(int index)
3050 {
3051 	renderedImage->SetCameraMovementMode(index);
3052 }
3053 
ColorizeGroupBoxes(QWidget * window,int randomSeed)3054 void cInterface::ColorizeGroupBoxes(QWidget *window, int randomSeed)
3055 {
3056 	QList<QGroupBox *> widgets;
3057 	widgets = window->findChildren<QGroupBox *>();
3058 	if (qobject_cast<QGroupBox *>(window)) // check if QGroupBox
3059 	{
3060 		widgets.append(static_cast<QGroupBox *>(window));
3061 	}
3062 	QPalette palette = window->palette();
3063 	QColor globalColor = palette.window().color();
3064 	int brightness = globalColor.value();
3065 
3066 	int rBase = globalColor.red();
3067 	int gBase = globalColor.green();
3068 	int bBase = globalColor.blue();
3069 
3070 	cRandom random;
3071 	random.Initialize(randomSeed);
3072 
3073 	foreach (QGroupBox *groupbox, widgets)
3074 	{
3075 		QColor buttonColor;
3076 		if (brightness > 20)
3077 		{
3078 			int r = random.Random(40) + rBase - 20;
3079 			r = clamp(r, 0, 255);
3080 			int g = random.Random(40) + gBase - 20;
3081 			g = clamp(g, 0, 255);
3082 			int b = random.Random(40) + bBase - 20;
3083 			b = clamp(b, 0, 255);
3084 			buttonColor = QColor(r, g, b);
3085 		}
3086 		else
3087 		{
3088 			int r = random.Random(40) + rBase;
3089 			r = clamp(r, 0, 255);
3090 			int g = random.Random(40) + gBase;
3091 			g = clamp(g, 0, 255);
3092 			int b = random.Random(40) + bBase;
3093 			b = clamp(b, 0, 255);
3094 			buttonColor = QColor(r, g, b);
3095 		}
3096 
3097 		QPalette newPalette(buttonColor);
3098 		groupbox->setPalette(newPalette);
3099 		groupbox->setAutoFillBackground(true);
3100 	}
3101 }
3102 
3103 // selective saving of settings (from selected widget)
SaveLocalSettings(const QWidget * widget)3104 void cInterface::SaveLocalSettings(const QWidget *widget)
3105 {
3106 	QStringList listOfParameters = CreateListOfParametersInWidget(widget);
3107 
3108 	cSettings parSettings(cSettings::formatCondensedText);
3109 	parSettings.SetListOfParametersToProcess(listOfParameters);
3110 
3111 	SynchronizeInterface(gPar, gParFractal, qInterface::read);
3112 	parSettings.CreateText(gPar, gParFractal, gAnimFrames, gKeyframes);
3113 
3114 	QString proposedFilename = widget->objectName();
3115 
3116 	QFileDialog dialog(mainWindow);
3117 	dialog.setOption(QFileDialog::DontUseNativeDialog);
3118 	dialog.setFileMode(QFileDialog::AnyFile);
3119 	dialog.setNameFilter(tr("Fractals (*.txt *.fract)"));
3120 	dialog.setDirectory(
3121 		QDir::toNativeSeparators(QFileInfo(systemData.lastSettingsFile).absolutePath()));
3122 	dialog.selectFile(QDir::toNativeSeparators(proposedFilename));
3123 	dialog.setAcceptMode(QFileDialog::AcceptSave);
3124 	dialog.setWindowTitle(tr("Save settings from %1").arg(widget->objectName()));
3125 	dialog.setDefaultSuffix("fract");
3126 	QStringList filenames;
3127 	if (dialog.exec())
3128 	{
3129 		filenames = dialog.selectedFiles();
3130 		QString filename = QDir::toNativeSeparators(filenames.first());
3131 		parSettings.SaveToFile(filename);
3132 	}
3133 }
3134 
LoadLocalSettings(const QWidget * widget)3135 void cInterface::LoadLocalSettings(const QWidget *widget)
3136 {
3137 	QString proposedFilename = widget->objectName();
3138 
3139 	PreviewFileDialog dialog(mainWindow);
3140 	dialog.setOption(QFileDialog::DontUseNativeDialog);
3141 	dialog.setFileMode(QFileDialog::ExistingFile);
3142 	dialog.setNameFilter(tr("Fractals (*.txt *.fract)"));
3143 	dialog.setDirectory(
3144 		QDir::toNativeSeparators(QFileInfo(systemData.lastSettingsFile).absolutePath()));
3145 	dialog.selectFile(QDir::toNativeSeparators(proposedFilename));
3146 	dialog.setAcceptMode(QFileDialog::AcceptOpen);
3147 	dialog.setWindowTitle(tr("Load settings to %1").arg(widget->objectName()));
3148 	QStringList filenames;
3149 
3150 	if (dialog.exec())
3151 	{
3152 		filenames = dialog.selectedFiles();
3153 		QString filename = QDir::toNativeSeparators(filenames.first());
3154 
3155 		for (int i = 0; i < 2; i++) // repeat twice to refresh comboboxes and get new list of widgets
3156 		{
3157 			QStringList listOfParameters = CreateListOfParametersInWidget(widget);
3158 
3159 			SynchronizeInterface(
3160 				gPar, gParFractal, qInterface::read); // update appParam before loading new settings
3161 
3162 			cSettings parSettings(cSettings::formatFullText);
3163 			parSettings.SetListOfParametersToProcess(listOfParameters);
3164 
3165 			if (widget->objectName().contains("widgetTabFractal"))
3166 			{
3167 				int fractalIndex = widget->objectName().right(1).toInt();
3168 				parSettings.SetFractalFormulaIndex(fractalIndex);
3169 			}
3170 
3171 			DisablePeriodicRefresh();
3172 			gInterfaceReadyForSynchronization = false;
3173 			parSettings.LoadFromFile(filename);
3174 			parSettings.Decode(gPar, gParFractal, gAnimFrames, gKeyframes);
3175 
3176 			SynchronizeInterface(gPar, gParFractal, qInterface::write);
3177 			gInterfaceReadyForSynchronization = true;
3178 
3179 			ComboMouseClickUpdate();
3180 			ReEnablePeriodicRefresh();
3181 		}
3182 	}
3183 }
3184 
ResetLocalSettings(const QWidget * widget)3185 void cInterface::ResetLocalSettings(const QWidget *widget)
3186 {
3187 	QStringList listOfParameters = CreateListOfParametersInWidget(widget);
3188 
3189 	for (QString fullParameterName : listOfParameters)
3190 	{
3191 		const int firstUnderscore = fullParameterName.indexOf('_');
3192 		const QString containerName = fullParameterName.left(firstUnderscore);
3193 		const QString parameterName = fullParameterName.mid(firstUnderscore + 1);
3194 
3195 		std::shared_ptr<cParameterContainer> container = nullptr;
3196 		if (containerName == "main")
3197 		{
3198 			container = gPar;
3199 		}
3200 		else if (containerName.indexOf("fractal") >= 0)
3201 		{
3202 			const int index = containerName.rightRef(1).toInt();
3203 			if (index < 4)
3204 			{
3205 				container = gParFractal->at(index);
3206 			}
3207 		}
3208 
3209 		if (container)
3210 		{
3211 			cOneParameter oneParam = container->GetAsOneParameter(parameterName);
3212 			oneParam.SetMultiVal(oneParam.GetMultiVal(valueDefault), valueActual);
3213 			container->SetFromOneParameter(parameterName, oneParam);
3214 		}
3215 	}
3216 	SynchronizeInterface(gPar, gParFractal, qInterface::write);
3217 }
3218 
RandomizeLocalSettings(const QWidget * widget)3219 void cInterface::RandomizeLocalSettings(const QWidget *widget)
3220 {
3221 	cRandomizerDialog *randomizer = new cRandomizerDialog();
3222 	randomizer->setAttribute(Qt::WA_DeleteOnClose);
3223 	randomizer->AssignSourceWidget(widget);
3224 	randomizer->show();
3225 }
3226 
CreateListOfParametersInWidget(const QWidget * inputWidget)3227 QStringList cInterface::CreateListOfParametersInWidget(const QWidget *inputWidget)
3228 {
3229 	QList<QWidget *> listOfWidgets = inputWidget->findChildren<QWidget *>();
3230 	QSet<QString> listOfParameters;
3231 
3232 	foreach (QWidget *widget, listOfWidgets)
3233 	{
3234 		CommonMyWidgetWrapper *myWidget = dynamic_cast<CommonMyWidgetWrapper *>(widget);
3235 		if (myWidget)
3236 		{
3237 			QString parameterName = myWidget->getFullParameterName();
3238 			QString containerName = myWidget->getParameterContainerName();
3239 			QString fullParameterName = containerName + "_" + parameterName;
3240 			listOfParameters.insert(fullParameterName);
3241 		}
3242 	}
3243 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
3244 	QStringList list(listOfParameters.toList());
3245 #else
3246 	QStringList list(listOfParameters.values());
3247 #endif
3248 	return list;
3249 }
3250 
GlobalStopRequest()3251 void cInterface::GlobalStopRequest()
3252 {
3253 	systemData.globalStopRequest = true;
3254 	if (!stopRequestPulseTimer)
3255 	{
3256 		stopRequestPulseTimer = new QTimer(mainWindow); // will be deleted with parent
3257 		connect(stopRequestPulseTimer, SIGNAL(timeout()), mainWindow, SLOT(ResetGlobalStopRequest()));
3258 		stopRequestPulseTimer->setSingleShot(true);
3259 	}
3260 
3261 	stopRequestPulseTimer->start(1000);
3262 }
3263 
ResetGlobalStopRequest()3264 void cInterface::ResetGlobalStopRequest()
3265 {
3266 	while (cRenderJob::GetRunningJobCount() > 0)
3267 	{
3268 		gApplication->processEvents();
3269 	}
3270 
3271 	systemData.globalStopRequest = false;
3272 }
3273 
CleanSettings()3274 void cInterface::CleanSettings()
3275 {
3276 	if (QMessageBox::Yes
3277 			== QMessageBox::question(mainWindow, tr("Cleaning up"),
3278 				tr("Do you want to clean up settings?\nIt will take a while"),
3279 				QMessageBox::Yes | QMessageBox::No))
3280 	{
3281 		SynchronizeInterface(gPar, gParFractal, qInterface::read);
3282 		gUndo->Store(gPar, gParFractal);
3283 		gUndo->Store(gPar, gParFractal);
3284 		cSettingsCleaner *cleaner = new cSettingsCleaner(mainWindow);
3285 		cleaner->show();
3286 		cleaner->runCleaner();
3287 		cleaner->exec();
3288 		delete cleaner;
3289 		SynchronizeInterface(gPar, gParFractal, qInterface::write);
3290 	}
3291 }
3292 
slotAutoSaveImage(double timeSeconds)3293 void cInterface::slotAutoSaveImage(double timeSeconds)
3294 {
3295 	if (timeSeconds > 600)
3296 	{
3297 		QString filename = systemDirectories.GetImagesFolder() + QDir::separator() + "autosave.png";
3298 		SaveImage(filename, ImageFileSave::IMAGE_FILE_TYPE_PNG, gMainInterface->mainImage, nullptr);
3299 		mainWindow->ui->statusbar->showMessage(tr("Image auto-saved to %1").arg(filename), 0);
3300 	}
3301 }
3302 
UpdateMainImagePreview()3303 void cInterface::UpdateMainImagePreview()
3304 {
3305 	renderedImage->update();
3306 }
3307 
ChangeLightWireframeVisibility(bool enable)3308 void cInterface::ChangeLightWireframeVisibility(bool enable)
3309 {
3310 	renderedImage->SetLightsVisibility(enable);
3311 	renderedImage->update();
3312 }
3313