1 /**
2  * Mandelbulber v2, a 3D fractal generator       ,=#MKNmMMKmmßMNWy,
3  *                                             ,B" ]L,,p%%%,,,§;, "K
4  * Copyright (C) 2015-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  * Functions for flight animation.
33  *
34  * cKeyframeAnimation contains all functionality for editing and rendering of
35  * a keyframe animation. The class holds a cKeyframes to store
36  * the parameters of the keyframes and a table to display the frames in a widget.
37  * The class can render interpolated sub-frames and validate their path (check
38  * for collision). It exposes slots to manipulate the keyframes.
39  */
40 
41 #include "animation_keyframes.hpp"
42 
43 #include <memory>
44 
45 #include "ui_dock_animation.h"
46 
47 #include "animation_path_data.hpp"
48 #include "cimage.hpp"
49 #include "common_math.h"
50 #include "files.h"
51 #include "global_data.hpp"
52 #include "headless.h"
53 #include "initparameters.hpp"
54 #include "interface.hpp"
55 #include "light.h"
56 #include "netrender.hpp"
57 #include "render_job.hpp"
58 #include "render_window.hpp"
59 #include "rendered_image_widget.hpp"
60 #include "rendering_configuration.hpp"
61 #include "settings.hpp"
62 #include "system_data.hpp"
63 #include "system_directories.hpp"
64 #include "undo.h"
65 #include "write_log.hpp"
66 
67 #include "qt/dock_animation.h"
68 #include "qt/dock_navigation.h"
69 #include "qt/dock_statistics.h"
70 #include "qt/my_progress_bar.h"
71 #include "qt/my_table_widget_keyframes.hpp"
72 #include "qt/player_widget.hpp"
73 #include "qt/pushbutton_anim_sound.h"
74 #include "qt/randomizer_dialog.h"
75 #include "qt/system_tray.hpp"
76 #include "qt/thumbnail_widget.h"
77 
78 cKeyframeAnimation *gKeyframeAnimation = nullptr;
79 
cKeyframeAnimation(cInterface * _interface,std::shared_ptr<cKeyframes> _frames,std::shared_ptr<cImage> _image,QWidget * _imageWidget,std::shared_ptr<cParameterContainer> _params,std::shared_ptr<cFractalContainer> _fractal,QObject * parent)80 cKeyframeAnimation::cKeyframeAnimation(cInterface *_interface, std::shared_ptr<cKeyframes> _frames,
81 	std::shared_ptr<cImage> _image, QWidget *_imageWidget,
82 	std::shared_ptr<cParameterContainer> _params, std::shared_ptr<cFractalContainer> _fractal,
83 	QObject *parent)
84 		: QObject(parent), mainInterface(_interface), keyframes(_frames)
85 {
86 	image = _image;
87 	imageWidget = dynamic_cast<RenderedImage *>(_imageWidget);
88 	params = _params;
89 	fractalParams = _fractal;
90 
91 	if (mainInterface->mainWindow)
92 	{
93 		ui = mainInterface->mainWindow->GetWidgetDockAnimation()->GetUi();
94 
95 		previewSize.setWidth(systemData.GetPreferredThumbnailSize() * 5 / 4);
96 		previewSize.setHeight(previewSize.width() * 3 / 4);
97 
98 		// connect keyframe control buttons
99 		connect(ui->pushButton_add_keyframe, &QPushButton::clicked, this,
100 			&cKeyframeAnimation::slotAddKeyframe);
101 		connect(ui->pushButton_insert_keyframe, &QPushButton::clicked, this,
102 			&cKeyframeAnimation::slotInsertKeyframe);
103 		connect(ui->pushButton_delete_keyframe, &QPushButton::clicked, this,
104 			&cKeyframeAnimation::slotDeleteKeyframe);
105 		connect(ui->pushButton_modify_keyframe, &QPushButton::clicked, this,
106 			&cKeyframeAnimation::slotModifyKeyframe);
107 		connect(ui->pushButton_render_keyframe_animation, &QPushButton::clicked, this,
108 			&cKeyframeAnimation::slotRenderKeyframes);
109 		connect(ui->pushButton_delete_all_keyframe_images, &QPushButton::clicked, this,
110 			&cKeyframeAnimation::slotDeleteAllImages);
111 		connect(ui->pushButton_show_keyframe_animation, &QPushButton::clicked, this,
112 			&cKeyframeAnimation::slotShowAnimation);
113 		connect(ui->pushButton_refresh_keyframe_table, &QPushButton::clicked, this,
114 			&cKeyframeAnimation::slotRefreshTable);
115 		connect(ui->pushButton_keyframe_to_flight_export, &QPushButton::clicked, this,
116 			&cKeyframeAnimation::slotExportKeyframesToFlight);
117 		connect(ui->pushButton_check_for_collisions, &QPushButton::clicked, this,
118 			&cKeyframeAnimation::slotValidate);
119 		connect(ui->pushButton_set_constant_target_distance, &QPushButton::clicked, this,
120 			&cKeyframeAnimation::slotSetConstantTargetDistance);
121 		connect(ui->button_selectAnimKeyframeImageDir, &QPushButton::clicked, this,
122 			&cKeyframeAnimation::slotSelectKeyframeAnimImageDir);
123 		connect(
124 			ui->pushButton_randomize, &QPushButton::clicked, this, &cKeyframeAnimation::slotRandomize);
125 		connect(ui->pushButton_add_all_parameters, &QPushButton::clicked, this,
126 			&cKeyframeAnimation::slotAddAllParameters);
127 		connect(ui->pushButton_set_frames_per_keyframe_to_all, &QPushButton::clicked, this,
128 			&cKeyframeAnimation::slotSetFramesPerKeyframeToAllKeyframes);
129 
130 		connect(ui->tableWidget_keyframe_animation, &QTableWidget::cellChanged, this,
131 			&cKeyframeAnimation::slotTableCellChanged);
132 		connect(ui->spinboxInt_keyframe_first_to_render, &QSpinBox::editingFinished, this,
133 			&cKeyframeAnimation::slotMovedSliderFirstFrame);
134 		connect(ui->spinboxInt_keyframe_last_to_render, &QSpinBox::editingFinished, this,
135 			&cKeyframeAnimation::slotMovedSliderLastFrame);
136 		connect(ui->spinboxInt_frames_per_keyframe, &QSpinBox::editingFinished, this,
137 			&cKeyframeAnimation::UpdateLimitsForFrameRange);
138 		connect(ui->tableWidget_keyframe_animation, &QTableWidget::cellDoubleClicked, this,
139 			&cKeyframeAnimation::slotCellDoubleClicked);
140 		connect(ui->tableWidget_keyframe_animation, &QTableWidget::cellClicked, this,
141 			&cKeyframeAnimation::slotCellClicked);
142 
143 		// connect system tray
144 		connect(mainInterface->systemTray, &cSystemTray::notifyRenderKeyframes, this,
145 			&cKeyframeAnimation::slotRenderKeyframes);
146 		connect(this, &cKeyframeAnimation::notifyRenderKeyframeRenderStatus, mainInterface->systemTray,
147 			&cSystemTray::showMessage);
148 
149 		connect(this, &cKeyframeAnimation::QuestionMessage, mainInterface->mainWindow,
150 			&RenderWindow::slotQuestionMessage);
151 
152 		connect(ui->checkBox_show_camera_path, &QCheckBox::stateChanged, this,
153 			&cKeyframeAnimation::slotUpdateAnimationPathSelection);
154 		connect(ui->checkBox_show_target_path, &QCheckBox::stateChanged, this,
155 			&cKeyframeAnimation::slotUpdateAnimationPathSelection);
156 		connect(ui->checkBox_show_light_path_1, &QCheckBox::stateChanged, this,
157 			&cKeyframeAnimation::slotUpdateAnimationPathSelection);
158 		connect(ui->checkBox_show_light_path_2, &QCheckBox::stateChanged, this,
159 			&cKeyframeAnimation::slotUpdateAnimationPathSelection);
160 		connect(ui->checkBox_show_light_path_3, &QCheckBox::stateChanged, this,
161 			&cKeyframeAnimation::slotUpdateAnimationPathSelection);
162 		connect(ui->checkBox_show_light_path_4, &QCheckBox::stateChanged, this,
163 			&cKeyframeAnimation::slotUpdateAnimationPathSelection);
164 
165 		table = ui->tableWidget_keyframe_animation;
166 
167 		// add default parameters for animation
168 		if (keyframes->GetListOfUsedParameters().size() == 0)
169 		{
170 			keyframes->AddAnimatedParameter("camera", params->GetAsOneParameter("camera"), params);
171 			keyframes->AddAnimatedParameter("target", params->GetAsOneParameter("target"), params);
172 			keyframes->AddAnimatedParameter(
173 				"camera_top", params->GetAsOneParameter("camera_top"), params);
174 			if (mainInterface->mainWindow) PrepareTable();
175 		}
176 	}
177 	else
178 	{
179 		ui = nullptr;
180 		table = nullptr;
181 	}
182 
183 	// NetRender for animation
184 	// signals to NetRender
185 	connect(this, &cKeyframeAnimation::SendNetRenderSetup, gNetRender, &cNetRender::SendSetup);
186 	connect(this, &cKeyframeAnimation::NetRenderCurrentAnimation, gNetRender,
187 		&cNetRender::SetCurrentAnimation);
188 	connect(this, &cKeyframeAnimation::NetRenderConfirmRendered, gNetRender,
189 		&cNetRender::ConfirmRenderedFrame);
190 	connect(
191 		this, &cKeyframeAnimation::NetRenderAddFileToSender, gNetRender, &cNetRender::AddFileToSender);
192 	connect(
193 		this, &cKeyframeAnimation::NetRenderNotifyClientStatus, gNetRender, &cNetRender::NotifyStatus);
194 
195 	// signals from NetRender
196 	connect(
197 		gNetRender, &cNetRender::FinishedFrame, this, &cKeyframeAnimation::slotNetRenderFinishedFrame);
198 	connect(this, &cKeyframeAnimation::NetRenderSendFramesToDoList, gNetRender,
199 		&cNetRender::SendFramesToDoList);
200 	connect(gNetRender, &cNetRender::UpdateFramesToDo, this,
201 		&cKeyframeAnimation::slotNetRenderUpdateFramesToDo);
202 	connect(
203 		this, &cKeyframeAnimation::NetRenderStopAllClients, gNetRender, &cNetRender::StopAllClients);
204 	connect(gNetRender, &cNetRender::animationStopRequest, this,
205 		&cKeyframeAnimation::slotAnimationStopRequest);
206 
207 	connect(this, SIGNAL(showErrorMessage(QString, cErrorMessage::enumMessageType, QWidget *)),
208 		gErrorMessage, SLOT(slotShowMessage(QString, cErrorMessage::enumMessageType, QWidget *)));
209 }
210 
slotAddKeyframe()211 void cKeyframeAnimation::slotAddKeyframe()
212 {
213 	mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
214 	gUndo->Store(params, fractalParams, nullptr, keyframes);
215 
216 	NewKeyframe(keyframes->GetNumberOfFrames());
217 
218 	keyframes->UpdateFramesIndexesTable();
219 }
220 
slotInsertKeyframe()221 void cKeyframeAnimation::slotInsertKeyframe()
222 {
223 	int index = table->currentColumn() - reservedColumns;
224 	if (index < 0) index = 0;
225 
226 	mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
227 	gUndo->Store(params, fractalParams, nullptr, keyframes);
228 
229 	NewKeyframe(index);
230 }
231 
NewKeyframe(int index)232 void cKeyframeAnimation::NewKeyframe(int index)
233 {
234 	if (keyframes)
235 	{
236 		// add new frame to container
237 		keyframes->AddFrame(params, fractalParams, params->Get<int>("frames_per_keyframe"), index);
238 		keyframes->UpdateFramesIndexesTable();
239 
240 		params->Set("frame_no", keyframes->GetFrameIndexForKeyframe(index));
241 
242 		// add column to table
243 		const int newColumn = AddColumn(keyframes->GetFrame(index), index);
244 		table->selectColumn(newColumn);
245 
246 		if (ui->checkBox_show_keyframe_thumbnails->isChecked())
247 		{
248 			cThumbnailWidget *thumbWidget =
249 				new cThumbnailWidget(previewSize.width(), previewSize.height(), 1, table);
250 			thumbWidget->UseOneCPUCore(false);
251 			thumbWidget->AssignParameters(params, fractalParams);
252 			table->setCellWidget(0, newColumn, thumbWidget);
253 		}
254 
255 		UpdateLimitsForFrameRange();
256 
257 		UpdateAnimationPath();
258 	}
259 	else
260 	{
261 		qCritical() << "gAnimFrames not allocated";
262 	}
263 }
264 
DeleteKeyframe(int index) const265 void cKeyframeAnimation::DeleteKeyframe(int index) const
266 {
267 	if (index < 0)
268 	{
269 		cErrorMessage::showMessage(QObject::tr("No keyframe selected"), cErrorMessage::errorMessage,
270 			mainInterface->mainWindow->GetCentralWidget());
271 	}
272 	else
273 	{
274 		gUndo->Store(params, fractalParams, nullptr, keyframes);
275 		keyframes->DeleteFrames(index, index);
276 		table->removeColumn(index + reservedColumns);
277 		keyframes->UpdateFramesIndexesTable();
278 		UpdateLimitsForFrameRange();
279 		UpdateAnimationPath();
280 	}
281 }
282 
slotModifyKeyframe()283 void cKeyframeAnimation::slotModifyKeyframe()
284 {
285 	const int index = table->currentColumn() - reservedColumns;
286 	if (index < 0)
287 	{
288 		cErrorMessage::showMessage(QObject::tr("No keyframe selected"), cErrorMessage::errorMessage,
289 			mainInterface->mainWindow->GetCentralWidget());
290 	}
291 	else
292 	{
293 		if (keyframes)
294 		{
295 			// get latest values of all parameters
296 			mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
297 			gUndo->Store(params, fractalParams, nullptr, keyframes);
298 
299 			int framesPerKeyframe = keyframes->GetFrame(index).numberOfSubFrames;
300 
301 			// add new frame to container
302 			keyframes->DeleteFrames(index, index);
303 			keyframes->AddFrame(params, fractalParams, framesPerKeyframe, index);
304 
305 			keyframes->UpdateFramesIndexesTable();
306 
307 			// add column to table
308 			table->removeColumn(index + reservedColumns);
309 			const int newColumn = AddColumn(keyframes->GetFrame(index), index);
310 			table->selectColumn(newColumn);
311 
312 			if (ui->checkBox_show_keyframe_thumbnails->isChecked())
313 			{
314 				cThumbnailWidget *thumbWidget =
315 					new cThumbnailWidget(previewSize.width(), previewSize.height(), 1, table);
316 				thumbWidget->UseOneCPUCore(false);
317 				thumbWidget->AssignParameters(params, fractalParams);
318 				table->setCellWidget(0, newColumn, thumbWidget);
319 			}
320 
321 			UpdateAnimationPath();
322 		}
323 		else
324 		{
325 			qCritical() << "gAnimFrames not allocated";
326 		}
327 	}
328 }
329 
slotDeleteKeyframe() const330 void cKeyframeAnimation::slotDeleteKeyframe() const
331 {
332 	const int index = table->currentColumn() - reservedColumns;
333 	DeleteKeyframe(index);
334 
335 	keyframes->UpdateFramesIndexesTable();
336 }
337 
slotRenderKeyframes()338 bool cKeyframeAnimation::slotRenderKeyframes()
339 {
340 	// get latest values of all parameters
341 	if (mainInterface->mainWindow)
342 	{
343 		mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
344 	}
345 
346 	if (keyframes)
347 	{
348 		if (keyframes->GetNumberOfFrames() == 0)
349 		{
350 			emit showErrorMessage(QObject::tr("No frames to render"), cErrorMessage::errorMessage,
351 				mainInterface->mainWindow->GetCentralWidget());
352 		}
353 		else if (!QDir(params->Get<QString>("anim_keyframe_dir")).exists())
354 		{
355 			emit showErrorMessage(
356 				QObject::tr("The folder %1 does not exist. Please specify a valid location.")
357 					.arg(params->Get<QString>("anim_keyframe_dir")),
358 				cErrorMessage::errorMessage, mainInterface->mainWindow->GetCentralWidget());
359 		}
360 		else
361 		{
362 			return RenderKeyframes(&gMainInterface->stopRequest);
363 		}
364 	}
365 	else
366 	{
367 		qCritical() << "gAnimFrames not allocated";
368 	}
369 	return false;
370 }
371 
slotIncreaseCurrentTableIndex()372 void cKeyframeAnimation::slotIncreaseCurrentTableIndex()
373 {
374 	int nextColumn = (table->currentColumn() + 1) % table->columnCount();
375 	table->setCurrentCell(table->currentRow(), nextColumn);
376 }
377 
slotDecreaseCurrentTableIndex()378 void cKeyframeAnimation::slotDecreaseCurrentTableIndex()
379 {
380 	int nextColumn = (table->columnCount() + table->currentColumn() - 1) % table->columnCount();
381 	table->setCurrentCell(table->currentRow(), nextColumn);
382 }
383 
PrepareTable()384 void cKeyframeAnimation::PrepareTable()
385 {
386 	table->setRowCount(0);
387 	table->setColumnCount(0);
388 	table->clear();
389 	tableRowNames.clear();
390 	QFontMetrics fm(mainInterface->mainWindow->font());
391 	table->verticalHeader()->setDefaultSectionSize(fm.height() + 6);
392 	AddAnimSoundColumn();
393 	CreateRowsInTable();
394 }
395 
CreateRowsInTable()396 void cKeyframeAnimation::CreateRowsInTable()
397 {
398 	QList<cAnimationFrames::sParameterDescription> parList = keyframes->GetListOfUsedParameters();
399 	table->setIconSize(previewSize);
400 	table->insertRow(0);
401 	table->setVerticalHeaderItem(0, new QTableWidgetItem(tr("Keyframe\npreviews")));
402 	table->setRowHeight(0, previewSize.height());
403 	tableRowNames.append(tr("Keyframe\npreviews"));
404 
405 	table->insertRow(framesPerKeyframeRow);
406 	table->setVerticalHeaderItem(framesPerKeyframeRow, new QTableWidgetItem(tr("Frames/keyframe")));
407 	tableRowNames.append(tr("Frames/keyframe"));
408 
409 	table->insertRow(cameraSpeedRow);
410 	table->setVerticalHeaderItem(
411 		cameraSpeedRow, new QTableWidgetItem(tr("Camera speed [units/frame]")));
412 	tableRowNames.append(tr("Camera speed [units/frame]"));
413 
414 	rowParameter.clear();
415 	rowParameter.append(-1); // prevuew
416 	rowParameter.append(-1); // frames per keyframe
417 	rowParameter.append(-1); // camera speed
418 
419 	parameterRows.clear();
420 	for (int i = 0; i < parList.size(); ++i)
421 	{
422 		int row = AddVariableToTable(parList[i], i);
423 		parameterRows.append(row);
424 	}
425 }
426 
AddVariableToTable(const cAnimationFrames::sParameterDescription & parameterDescription,int index)427 int cKeyframeAnimation::AddVariableToTable(
428 	const cAnimationFrames::sParameterDescription &parameterDescription, int index)
429 {
430 	using namespace parameterContainer;
431 	const enumVarType type = parameterDescription.varType;
432 	const int row = table->rowCount();
433 	if (type == typeVector3)
434 	{
435 		QString varName =
436 			parameterDescription.containerName + "_" + parameterDescription.parameterName + "_x";
437 		AddRow(row, varName, index);
438 
439 		varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_y";
440 		AddRow(row + 1, varName, index);
441 
442 		varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_z";
443 		AddRow(row + 2, varName, index);
444 	}
445 	else if (type == typeVector4)
446 	{
447 		QString varName =
448 			parameterDescription.containerName + "_" + parameterDescription.parameterName + "_x";
449 		AddRow(row, varName, index);
450 
451 		varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_y";
452 		AddRow(row + 1, varName, index);
453 
454 		varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_z";
455 		AddRow(row + 2, varName, index);
456 
457 		varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_w";
458 		AddRow(row + 3, varName, index);
459 	}
460 	else if (type == typeRgb)
461 	{
462 		QString varName =
463 			parameterDescription.containerName + "_" + parameterDescription.parameterName + "_R";
464 		AddRow(row, varName, index);
465 
466 		varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_G";
467 		AddRow(row + 1, varName, index);
468 
469 		varName = parameterDescription.containerName + "_" + parameterDescription.parameterName + "_B";
470 		AddRow(row + 2, varName, index);
471 	}
472 	else
473 	{
474 		const QString varName =
475 			parameterDescription.containerName + "_" + parameterDescription.parameterName;
476 		AddRow(row, varName, index);
477 	}
478 	return row;
479 }
480 
AddRow(int row,const QString & fullParameterName,int index)481 void cKeyframeAnimation::AddRow(int row, const QString &fullParameterName, int index)
482 {
483 	tableRowNames.append(fullParameterName);
484 	table->insertRow(row);
485 	table->setVerticalHeaderItem(row, new QTableWidgetItem(fullParameterName));
486 	rowParameter.append(index);
487 
488 	cPushButtonAnimSound *audioSelectorButton = new cPushButtonAnimSound(table);
489 	table->setCellWidget(row, animSoundColumn, audioSelectorButton);
490 	connect(audioSelectorButton, &cPushButtonAnimSound::signalAudioSelectorClosed, this,
491 		&cKeyframeAnimation::slotRefreshTable);
492 
493 	static_cast<cPushButtonAnimSound *>(table->cellWidget(row, animSoundColumn))
494 		->AssignParameterName(fullParameterName);
495 	static_cast<cPushButtonAnimSound *>(table->cellWidget(row, animSoundColumn))
496 		->AssignAnimation(keyframes);
497 }
498 
AddColumn(const cAnimationFrames::sAnimationFrame & frame,int index)499 int cKeyframeAnimation::AddColumn(const cAnimationFrames::sAnimationFrame &frame, int index)
500 {
501 	table->blockSignals(true);
502 	int newColumn = index + reservedColumns;
503 	if (index == -1)
504 	{
505 		newColumn = table->columnCount();
506 		index = newColumn - reservedColumns;
507 	}
508 	table->insertColumn(newColumn);
509 	table->setColumnWidth(newColumn, previewSize.width());
510 
511 	const int frameNo = keyframes->GetFrameIndexForKeyframe(newColumn - reservedColumns);
512 	const double time = double(frameNo) / params->Get<double>("keyframe_frames_per_second");
513 	const int minutes = int(time / 60);
514 	const int seconds = int(time) % 60;
515 	const int milliseconds = int(time * 1000.0) % 1000;
516 
517 	const QString columnHeader = QString("%1 (%2)\n(%3:%4.%5)")
518 																 .arg(newColumn - reservedColumns)
519 																 .arg(frameNo)
520 																 .arg(minutes)
521 																 .arg(seconds, 2, 10, QChar('0'))
522 																 .arg(milliseconds, 3, 10, QChar('0'));
523 	table->setHorizontalHeaderItem(newColumn, new QTableWidgetItem(columnHeader));
524 
525 	table->setItem(framesPerKeyframeRow, newColumn,
526 		new QTableWidgetItem(QString::number(frame.numberOfSubFrames)));
527 
528 	int nextIndex = min(index + 1, keyframes->GetNumberOfFrames() - 1);
529 	const cAnimationFrames::sAnimationFrame &nextFrame = keyframes->GetFrame(nextIndex);
530 	double cameraSpeed = GetCameraSpeed(frame, nextFrame);
531 	table->setItem(cameraSpeedRow, newColumn, new QTableWidgetItem(QString::number(cameraSpeed)));
532 	table->item(cameraSpeedRow, newColumn)->setFlags(Qt::NoItemFlags);
533 
534 	QList<cAnimationFrames::sParameterDescription> parList = keyframes->GetListOfUsedParameters();
535 
536 	using namespace parameterContainer;
537 	for (int i = 0; i < parList.size(); ++i)
538 	{
539 		const QString parameterName = parList[i].containerName + "_" + parList[i].parameterName;
540 		const enumVarType type = parList[i].varType;
541 		const int row = parameterRows[i];
542 
543 		cOneParameter parameter = frame.parameters.GetAsOneParameter(parameterName);
544 		const parameterContainer::enumMorphType morphType = parameter.GetMorphType();
545 
546 		if (type == typeVector3)
547 		{
548 			const CVector3 val = parameter.Get<CVector3>(valueActual);
549 			table->setItem(row, newColumn, new QTableWidgetItem(QString("%L1").arg(val.x, 0, 'g', 15)));
550 			table->setItem(
551 				row + 1, newColumn, new QTableWidgetItem(QString("%L1").arg(val.y, 0, 'g', 15)));
552 			table->setItem(
553 				row + 2, newColumn, new QTableWidgetItem(QString("%L1").arg(val.z, 0, 'g', 15)));
554 			table->item(row, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
555 			table->item(row + 1, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
556 			table->item(row + 2, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
557 			table->item(row, newColumn)->setForeground(QBrush(Qt::black));
558 			table->item(row + 1, newColumn)->setForeground(QBrush(Qt::black));
559 			table->item(row + 2, newColumn)->setForeground(QBrush(Qt::black));
560 		}
561 		else if (type == typeVector4)
562 		{
563 			const CVector4 val = parameter.Get<CVector4>(valueActual);
564 			table->setItem(row, newColumn, new QTableWidgetItem(QString("%L1").arg(val.x, 0, 'g', 15)));
565 			table->setItem(
566 				row + 1, newColumn, new QTableWidgetItem(QString("%L1").arg(val.y, 0, 'g', 15)));
567 			table->setItem(
568 				row + 2, newColumn, new QTableWidgetItem(QString("%L1").arg(val.z, 0, 'g', 15)));
569 			table->setItem(
570 				row + 3, newColumn, new QTableWidgetItem(QString("%L1").arg(val.w, 0, 'g', 15)));
571 			table->item(row, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
572 			table->item(row + 1, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
573 			table->item(row + 2, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
574 			table->item(row + 3, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
575 			table->item(row, newColumn)->setForeground(QBrush(Qt::black));
576 			table->item(row + 1, newColumn)->setForeground(QBrush(Qt::black));
577 			table->item(row + 2, newColumn)->setForeground(QBrush(Qt::black));
578 			table->item(row + 3, newColumn)->setForeground(QBrush(Qt::black));
579 		}
580 		else if (type == typeRgb)
581 		{
582 			const sRGB val = parameter.Get<sRGB>(valueActual);
583 			table->setItem(row, newColumn, new QTableWidgetItem(QString::number(val.R)));
584 			table->setItem(row + 1, newColumn, new QTableWidgetItem(QString::number(val.G)));
585 			table->setItem(row + 2, newColumn, new QTableWidgetItem(QString::number(val.B)));
586 			table->item(row, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
587 			table->item(row + 1, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
588 			table->item(row + 2, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
589 			table->item(row, newColumn)->setForeground(QBrush(Qt::black));
590 			table->item(row + 1, newColumn)->setForeground(QBrush(Qt::black));
591 			table->item(row + 2, newColumn)->setForeground(QBrush(Qt::black));
592 		}
593 		else
594 		{
595 			const QString val = parameter.Get<QString>(valueActual);
596 			table->setItem(row, newColumn, new QTableWidgetItem(val));
597 			table->item(row, newColumn)->setBackground(QBrush(MorphType2Color(morphType)));
598 			table->item(row, newColumn)->setForeground(QBrush(Qt::black));
599 		}
600 	}
601 	table->blockSignals(false);
602 	return newColumn;
603 }
604 
MorphType2Color(parameterContainer::enumMorphType morphType)605 QColor cKeyframeAnimation::MorphType2Color(parameterContainer::enumMorphType morphType)
606 {
607 	using namespace parameterContainer;
608 	QColor color;
609 	switch (morphType)
610 	{
611 		case morphNone: color = QColor(255, 255, 255); break;						 // white
612 		case morphLinear: color = QColor(200, 200, 200); break;					 // gray
613 		case morphLinearAngle: color = QColor(200, 200, 255); break;		 // gray/blue
614 		case morphCatMullRom: color = QColor(200, 255, 200); break;			 // green
615 		case morphCatMullRomAngle: color = QColor(200, 255, 255); break; // green/blue
616 		case morphAkima: color = QColor(255, 200, 200); break;					 // red
617 		case morphAkimaAngle: color = QColor(255, 200, 255); break;			 // red/blue
618 		case morphCubic: color = QColor(255, 255, 200); break;					 // yellow
619 		case morphCubicAngle: color = QColor(255, 200, 255); break;			 // yellow/blue
620 		case morphSteffen: color = QColor(150, 150, 200); break;				 // blue
621 		case morphSteffenAngle: color = QColor(150, 150, 255); break;		 // blue
622 	}
623 	return color;
624 }
625 
PrepareRenderJob(bool * stopRequest)626 std::shared_ptr<cRenderJob> cKeyframeAnimation::PrepareRenderJob(bool *stopRequest)
627 {
628 	// preparing Render Job
629 	std::shared_ptr<cRenderJob> renderJob(
630 		new cRenderJob(params, fractalParams, image, stopRequest, imageWidget));
631 	connect(renderJob.get(),
632 		SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)), this,
633 		SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)));
634 	connect(renderJob.get(), SIGNAL(updateStatistics(cStatistics)), this,
635 		SIGNAL(updateStatistics(cStatistics)));
636 	if (!systemData.noGui)
637 	{
638 		connect(renderJob.get(), SIGNAL(updateImage()), mainInterface->renderedImage, SLOT(update()));
639 		connect(renderJob.get(), SIGNAL(sendRenderedTilesList(QList<sRenderedTileData>)),
640 			mainInterface->renderedImage, SLOT(showRenderedTilesList(QList<sRenderedTileData>)));
641 	}
642 	return renderJob;
643 }
644 
InitFrameRanges(sFrameRanges * frameRanges)645 bool cKeyframeAnimation::InitFrameRanges(sFrameRanges *frameRanges)
646 {
647 	// range of keyframes to render
648 	frameRanges->startFrame = params->Get<int>("keyframe_first_to_render");
649 	frameRanges->endFrame = params->Get<int>("keyframe_last_to_render");
650 	frameRanges->totalFrames = keyframes->GetTotalNumberOfFrames();
651 	if (frameRanges->totalFrames < 1) frameRanges->totalFrames = 1;
652 
653 	if (frameRanges->endFrame == 0) frameRanges->endFrame = frameRanges->totalFrames;
654 
655 	if (frameRanges->startFrame == frameRanges->endFrame)
656 	{
657 		emit showErrorMessage(
658 			QObject::tr(
659 				"There is no frame to render: first frame to render and last frame to render are equals."),
660 			cErrorMessage::warningMessage);
661 		return false;
662 	}
663 	return true;
664 }
665 
InitFrameMarkers(const sFrameRanges & frameRanges)666 void cKeyframeAnimation::InitFrameMarkers(const sFrameRanges &frameRanges)
667 {
668 	alreadyRenderedFrames.resize(frameRanges.totalFrames);
669 	alreadyRenderedFrames.fill(false);
670 	reservedFrames.clear();
671 	reservedFrames.resize(frameRanges.totalFrames);
672 	reservedFrames.fill(false);
673 }
674 
VerifyAnimation(bool * stopRequest)675 void cKeyframeAnimation::VerifyAnimation(bool *stopRequest)
676 {
677 	// checking for collisions
678 	if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
679 	{
680 		if (params->Get<bool>("keyframe_auto_validate"))
681 		{
682 			keyframes->ClearMorphCache();
683 			QList<int> listOfCollisions =
684 				CheckForCollisions(params->Get<double>("keyframe_collision_thresh"), stopRequest);
685 			if (*stopRequest) throw false;
686 
687 			if (listOfCollisions.size() > 0)
688 			{
689 				QString collisionText;
690 				for (int i = 0; i < listOfCollisions.size(); i++)
691 				{
692 					collisionText += QString("%1").arg(listOfCollisions.at(i));
693 					if (i < listOfCollisions.size() - 1) collisionText += QString(", ");
694 				}
695 				emit showErrorMessage(
696 					QObject::tr("Camera collides with fractal at following frames:\n") + collisionText,
697 					cErrorMessage::warningMessage);
698 			}
699 		}
700 	}
701 }
702 
CheckWhichFramesAreAlreadyRendered(const sFrameRanges & frameRanges)703 void cKeyframeAnimation::CheckWhichFramesAreAlreadyRendered(const sFrameRanges &frameRanges)
704 {
705 	// Check if frames have already been rendered
706 
707 	for (int frameNo = 0; frameNo < keyframes->GetTotalNumberOfFrames(); frameNo++)
708 	{
709 		int index = keyframes->GetKeyframeIndex(frameNo);
710 		int subIndex = keyframes->GetSubIndex(frameNo);
711 
712 		const QString filename = GetKeyframeFilename(index, subIndex, false);
713 
714 		alreadyRenderedFrames[frameNo] = (QFile(filename).exists() || frameNo < frameRanges.startFrame
715 																			|| frameNo >= frameRanges.endFrame);
716 		if (gNetRender->IsClient())
717 		{
718 			if (netRenderListOfFramesToRender.size() > 0)
719 			{
720 				if (frameNo < netRenderListOfFramesToRender[0]) alreadyRenderedFrames[frameNo] = true;
721 			}
722 		}
723 		reservedFrames[frameNo] = alreadyRenderedFrames[frameNo];
724 	}
725 }
726 
AllFramesAlreadyRendered(const sFrameRanges & frameRanges,bool * startRenderKeyframesAgain)727 bool cKeyframeAnimation::AllFramesAlreadyRendered(
728 	const sFrameRanges &frameRanges, bool *startRenderKeyframesAgain)
729 {
730 	bool result = true;
731 
732 	if (!gNetRender->IsClient())
733 	{
734 		if (keyframes->GetNumberOfFrames() - 1 > 0 && frameRanges.unrenderedTotalBeforeRender == 0)
735 		{
736 			bool deletePreviousRender;
737 			const QString questionTitle = QObject::tr("Truncate Image Folder");
738 			const QString questionText =
739 				QObject::tr(
740 					"The animation has already been rendered completely.\n Do you want to purge the output "
741 					"folder?\n")
742 				+ QObject::tr("This will delete all images in the image folder.\nProceed?");
743 			if (!systemData.noGui)
744 			{
745 				QMessageBox::StandardButton reply = QMessageBox::NoButton;
746 				emit QuestionMessage(
747 					questionTitle, questionText, QMessageBox::Yes | QMessageBox::No, &reply);
748 				while (reply == QMessageBox::NoButton)
749 				{
750 					gApplication->processEvents();
751 				}
752 				deletePreviousRender = (reply == QMessageBox::Yes);
753 			}
754 			else
755 			{
756 				// Exit if silent mode
757 				if (systemData.silent)
758 				{
759 					exit(0);
760 				}
761 				deletePreviousRender = cHeadless::ConfirmMessage(questionTitle + "\n" + questionText);
762 			}
763 			if (deletePreviousRender)
764 			{
765 				cAnimationFrames::WipeFramesFromFolder(params->Get<QString>("anim_keyframe_dir"));
766 				if (!systemData.noGui && image->IsMainImage())
767 				{
768 					mainInterface->mainWindow->GetWidgetDockNavigation()->UnlockAllFunctions();
769 					imageWidget->SetEnableClickModes(true);
770 				}
771 				*startRenderKeyframesAgain = true;
772 			}
773 			else
774 			{
775 				result = false;
776 			}
777 		}
778 	}
779 
780 	return result;
781 }
782 
InitJobsForClients(const sFrameRanges & frameRanges)783 void cKeyframeAnimation::InitJobsForClients(const sFrameRanges &frameRanges)
784 {
785 	int numberOfFramesForNetRender;
786 	if (gNetRender->GetClientCount() == 0)
787 	{
788 		numberOfFramesForNetRender = 0;
789 	}
790 	else
791 	{
792 		numberOfFramesForNetRender =
793 			frameRanges.unrenderedTotalBeforeRender / gNetRender->GetClientCount() / 2 + 1;
794 		if (numberOfFramesForNetRender < minFramesForNetRender)
795 			numberOfFramesForNetRender = minFramesForNetRender;
796 	}
797 	if (numberOfFramesForNetRender > maxFramesForNetRender)
798 		numberOfFramesForNetRender = maxFramesForNetRender;
799 
800 	qint32 renderId = rand();
801 	gNetRender->SetCurrentRenderId(renderId);
802 	gNetRender->SetAnimation(true);
803 	int frameIndex = 0;
804 	for (int i = 0; i < gNetRender->GetClientCount(); i++)
805 	{
806 		QList<int> startingFrames;
807 		for (int j = 0; j < numberOfFramesForNetRender; j++)
808 		{
809 			// looking for next unrendered frame
810 			bool notFound = false;
811 			while (alreadyRenderedFrames[frameIndex])
812 			{
813 				frameIndex++;
814 				if (frameIndex >= frameRanges.totalFrames)
815 				{
816 					notFound = true;
817 					break;
818 				}
819 			}
820 			if (notFound)
821 			{
822 				break;
823 			}
824 			else
825 			{
826 				startingFrames.append(frameIndex);
827 				reservedFrames[frameIndex] = true;
828 				frameIndex++;
829 			}
830 		}
831 		if (startingFrames.size() > 0)
832 		{
833 			emit SendNetRenderSetup(i, startingFrames);
834 		}
835 	}
836 	emit NetRenderCurrentAnimation(params, fractalParams, false);
837 }
838 
UpdateCameraAndTarget()839 void cKeyframeAnimation::UpdateCameraAndTarget()
840 {
841 	// recalculation of camera rotation and distance (just for display purposes)
842 	const CVector3 camera = params->Get<CVector3>("camera");
843 	const CVector3 target = params->Get<CVector3>("target");
844 	const CVector3 top = params->Get<CVector3>("camera_top");
845 	cCameraTarget cameraTarget(camera, target, top);
846 	params->Set("camera_rotation", cameraTarget.GetRotation() * 180.0 / M_PI);
847 	params->Set("camera_distance_to_target", cameraTarget.GetDistance());
848 
849 	if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
850 	{
851 		mainInterface->SynchronizeInterface(params, fractalParams, qInterface::write);
852 
853 		// show distance in statistics table
854 		const double distance =
855 			mainInterface->GetDistanceForPoint(params->Get<CVector3>("camera"), params, fractalParams);
856 		mainInterface->mainWindow->GetWidgetDockStatistics()->UpdateDistanceToFractal(distance);
857 	}
858 }
859 
ConfirmAndSendRenderedFrames(const int frameIndex,const QStringList & listOfSavedFiles)860 void cKeyframeAnimation::ConfirmAndSendRenderedFrames(
861 	const int frameIndex, const QStringList &listOfSavedFiles)
862 {
863 	emit NetRenderConfirmRendered(frameIndex, netRenderListOfFramesToRender.size());
864 	netRenderListOfFramesToRender.removeAll(frameIndex);
865 	emit NetRenderNotifyClientStatus();
866 	for (QString channelFileName : listOfSavedFiles)
867 	{
868 		emit NetRenderAddFileToSender(channelFileName);
869 	}
870 }
871 
UpadeProgressInformation(const sFrameRanges & frameRanges,cProgressText * progressText,const int frameIndex,int index)872 void cKeyframeAnimation::UpadeProgressInformation(
873 	const sFrameRanges &frameRanges, cProgressText *progressText, const int frameIndex, int index)
874 {
875 	double percentDoneFrame;
876 	if (frameRanges.unrenderedTotalBeforeRender > 0)
877 		percentDoneFrame = double(renderedFramesCount) / frameRanges.unrenderedTotalBeforeRender;
878 	else
879 		percentDoneFrame = 1.0;
880 
881 	const QString progressTxt = progressText->getText(percentDoneFrame);
882 	emit updateProgressAndStatus(QObject::tr("Rendering animation"),
883 		QObject::tr("Frame %1 of %2 (key %3)").arg(frameIndex).arg(frameRanges.totalFrames).arg(index)
884 			+ " " + progressTxt,
885 		percentDoneFrame, cProgressText::progress_ANIMATION);
886 }
887 
RenderKeyframes(bool * stopRequest)888 bool cKeyframeAnimation::RenderKeyframes(bool *stopRequest)
889 {
890 	mainInterface->DisablePeriodicRefresh();
891 
892 	if (image->IsUsed())
893 	{
894 		emit showErrorMessage(
895 			QObject::tr(
896 				"Rendering engine is busy. Stop unfinished rendering before starting rendering animation"),
897 			cErrorMessage::errorMessage);
898 		return false;
899 	}
900 
901 	*stopRequest = false;
902 	animationStopRequest = false;
903 
904 	// preparing Render Job
905 	std::shared_ptr<cRenderJob> renderJob = PrepareRenderJob(stopRequest);
906 
907 	cRenderingConfiguration config;
908 
909 	if (systemData.noGui)
910 	{
911 		config.DisableRefresh();
912 	}
913 	config.EnableNetRender();
914 	config.DisableProgressiveRender();
915 
916 	renderJob->Init(cRenderJob::keyframeAnim, config);
917 
918 	cProgressText progressText;
919 	progressText.ResetTimer();
920 
921 	keyframes->UpdateFramesIndexesTable();
922 
923 	// range of keyframes to render
924 	sFrameRanges frameRanges;
925 	if (!InitFrameRanges(&frameRanges)) return false;
926 
927 	if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
928 	{
929 		mainInterface->mainWindow->GetWidgetDockNavigation()->LockAllFunctions();
930 		imageWidget->SetEnableClickModes(false);
931 	}
932 
933 	InitFrameMarkers(frameRanges);
934 
935 	try
936 	{
937 		// updating parameters
938 		if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
939 		{
940 			mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
941 			gUndo->Store(params, fractalParams, nullptr, keyframes);
942 		}
943 
944 		keyframes->RefreshAllAudioTracks(params);
945 
946 		// checking for collisions
947 		VerifyAnimation(stopRequest);
948 
949 		// Check if frames have already been rendered
950 		CheckWhichFramesAreAlreadyRendered(frameRanges);
951 
952 		// count number of unrendered frames
953 		frameRanges.unrenderedTotalBeforeRender = 0;
954 		for (int i = 0; i < frameRanges.totalFrames; i++)
955 		{
956 			if (!alreadyRenderedFrames[i]) frameRanges.unrenderedTotalBeforeRender++;
957 		}
958 
959 		// message if all frames are already rendered
960 		bool startRenderKeyframesAgain = false;
961 		bool result = AllFramesAlreadyRendered(frameRanges, &startRenderKeyframesAgain);
962 
963 		if (startRenderKeyframesAgain) return RenderKeyframes(stopRequest);
964 		if (!result) throw false;
965 
966 		animationIsRendered = true;
967 
968 		if (gNetRender->IsServer())
969 		{
970 			InitJobsForClients(frameRanges);
971 		}
972 
973 		keyframes->ClearMorphCache();
974 
975 		// main loop for rendering of frames
976 		renderedFramesCount = 0;
977 		for (int frameIndex = 0; frameIndex < keyframes->GetTotalNumberOfFrames(); frameIndex++)
978 		{
979 			int index = keyframes->GetKeyframeIndex(frameIndex);
980 			int subIndex = keyframes->GetSubIndex(frameIndex);
981 
982 			// skip already rendered frame
983 			if (alreadyRenderedFrames[frameIndex]) continue;
984 			if (reservedFrames[frameIndex]) continue;
985 
986 			if (gNetRender->IsClient())
987 			{
988 				//					//waiting for new jobs when list is empty
989 				//					while (gNetRender->IsClient() && !animationStopRequest &&
990 				// netRenderListOfFramesToRender.size() == 0)
991 				//					{
992 				//						gApplication->processEvents();
993 				//					}
994 
995 				bool frameFound = false;
996 				for (int f = 0; f < netRenderListOfFramesToRender.size(); f++)
997 				{
998 					if (netRenderListOfFramesToRender[f] == frameIndex)
999 					{
1000 						frameFound = true;
1001 						break;
1002 					}
1003 				}
1004 				if (!frameFound) continue;
1005 			}
1006 
1007 			if (gNetRender->IsServer())
1008 			{
1009 				reservedFrames[frameIndex] = true;
1010 			}
1011 
1012 			UpadeProgressInformation(frameRanges, &progressText, frameIndex, index);
1013 
1014 			if (*stopRequest || systemData.globalStopRequest || animationStopRequest) throw false;
1015 
1016 			keyframes->GetInterpolatedFrameAndConsolidate(frameIndex, params, fractalParams);
1017 
1018 			// recalculation of camera rotation and distance (just for display purposes)
1019 			UpdateCameraAndTarget();
1020 
1021 			// render frame
1022 			params->Set("frame_no", frameIndex);
1023 			renderJob->UpdateParameters(params, fractalParams);
1024 			result = renderJob->Execute();
1025 			if (!result) throw false;
1026 
1027 			// save frame
1028 			QStringList listOfSavedFiles;
1029 			const QString filename = GetKeyframeFilename(index, subIndex, gNetRender->IsClient());
1030 
1031 			if (gNetRender->IsClient())
1032 			{
1033 				const ImageFileSave::enumImageFileType fileType =
1034 					ImageFileSave::enumImageFileType(params->Get<int>("keyframe_animation_image_type"));
1035 				listOfSavedFiles = SaveImage(filename, fileType, image, gMainInterface->mainWindow);
1036 			}
1037 			else
1038 			{
1039 				std::shared_ptr<cImage> imageCopy(new cImage(*image.get()));
1040 
1041 				const ImageFileSave::enumImageFileType fileType =
1042 					ImageFileSave::enumImageFileType(params->Get<int>("keyframe_animation_image_type"));
1043 
1044 				cKeyframeSaveImageThread *savingThread =
1045 					new cKeyframeSaveImageThread(imageCopy, filename, fileType);
1046 
1047 				QThread *thread = new QThread();
1048 				thread->setObjectName("Image saving thread");
1049 
1050 				connect(thread, &QThread::started, savingThread, &cKeyframeSaveImageThread::startSaving);
1051 
1052 				connect(savingThread, &cKeyframeSaveImageThread::savingFinished, savingThread,
1053 					&cKeyframeSaveImageThread::deleteLater);
1054 				connect(savingThread, &cKeyframeSaveImageThread::savingFinished, thread, &QThread::quit);
1055 				connect(thread, &QThread::finished, thread, &QThread::deleteLater);
1056 
1057 				savingThread->moveToThread(thread);
1058 				thread->start();
1059 			}
1060 
1061 			renderedFramesCount++;
1062 			alreadyRenderedFrames[frameIndex] = true;
1063 
1064 			// qDebug() << "Finished rendering frame" << frameIndex;
1065 
1066 			if (gNetRender->IsClient())
1067 			{
1068 				ConfirmAndSendRenderedFrames(frameIndex, listOfSavedFiles);
1069 			}
1070 
1071 			gApplication->processEvents();
1072 		}
1073 		//--------------------------------------------------------------------
1074 
1075 		emit updateProgressAndStatus(QObject::tr("Animation finished"), progressText.getText(1.0), 1.0,
1076 			cProgressText::progress_IMAGE);
1077 		emit updateProgressHide();
1078 		emit notifyRenderKeyframeRenderStatus(
1079 			QObject::tr("Animation finished"), progressText.getText(1.0));
1080 	}
1081 	catch (bool ex)
1082 	{
1083 		if (gNetRender->IsServer())
1084 		{
1085 			emit NetRenderStopAllClients();
1086 		}
1087 
1088 		if (gNetRender->IsClient())
1089 		{
1090 			gNetRender->SetStatus(netRenderSts_READY);
1091 			emit NetRenderNotifyClientStatus();
1092 		}
1093 
1094 		QString resultStatus = QObject::tr("Rendering terminated");
1095 		if (ex) resultStatus += " - " + QObject::tr("Error occured, see log output");
1096 		emit updateProgressAndStatus(
1097 			resultStatus, progressText.getText(1.0), cProgressText::progress_ANIMATION);
1098 		emit updateProgressHide();
1099 
1100 		if (!systemData.noGui && image->IsMainImage())
1101 		{
1102 			mainInterface->mainWindow->GetWidgetDockNavigation()->UnlockAllFunctions();
1103 			imageWidget->SetEnableClickModes(true);
1104 		}
1105 		animationIsRendered = false;
1106 		return false;
1107 	}
1108 
1109 	if (!systemData.noGui && image->IsMainImage() && !gNetRender->IsClient())
1110 	{
1111 		mainInterface->mainWindow->GetWidgetDockNavigation()->UnlockAllFunctions();
1112 		imageWidget->SetEnableClickModes(true);
1113 	}
1114 
1115 	if (gNetRender->IsClient())
1116 	{
1117 		gNetRender->SetStatus(netRenderSts_READY);
1118 		emit NetRenderNotifyClientStatus();
1119 	}
1120 
1121 	animationIsRendered = false;
1122 	return true;
1123 }
1124 
RefreshTable()1125 void cKeyframeAnimation::RefreshTable()
1126 {
1127 	keyframes->UpdateFramesIndexesTable();
1128 	UpdateLimitsForFrameRange(); // it is needed to do it also here, because limits must be set just
1129 															 // after loading of settings
1130 	mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1131 
1132 	keyframes->UpdateFramesIndexesTable();
1133 
1134 	keyframes->RefreshAllAudioTracks(params);
1135 
1136 	int lastSelectedColumn = table->currentColumn();
1137 
1138 	PrepareTable();
1139 	gApplication->processEvents();
1140 
1141 	const int noOfKeyframes = keyframes->GetNumberOfFrames();
1142 
1143 	auto tempPar = std::make_shared<cParameterContainer>();
1144 	*tempPar = *params;
1145 	auto tempFract = std::make_shared<cFractalContainer>();
1146 	*tempFract = *fractalParams;
1147 
1148 	for (int i = 0; i < noOfKeyframes; i++)
1149 	{
1150 		const int newColumn = AddColumn(keyframes->GetFrame(i));
1151 
1152 		if (ui->checkBox_show_keyframe_thumbnails->isChecked())
1153 		{
1154 			cThumbnailWidget *thumbWidget =
1155 				new cThumbnailWidget(previewSize.width(), previewSize.height(), 1, table);
1156 			thumbWidget->UseOneCPUCore(true);
1157 			keyframes->GetFrameAndConsolidate(i, tempPar, tempFract);
1158 			tempPar->Set("frame_no", keyframes->GetFrameIndexForKeyframe(i));
1159 			thumbWidget->AssignParameters(tempPar, tempFract);
1160 			table->setCellWidget(0, newColumn, thumbWidget);
1161 		}
1162 		if (i % 100 == 0)
1163 		{
1164 			emit updateProgressAndStatus(QObject::tr("Refreshing animation"),
1165 				tr("Refreshing animation frames"), double(i) / noOfKeyframes,
1166 				cProgressText::progress_ANIMATION);
1167 			gApplication->processEvents();
1168 		}
1169 
1170 		if (systemData.globalStopRequest) break;
1171 	}
1172 
1173 	table->selectColumn(lastSelectedColumn);
1174 
1175 	UpdateLimitsForFrameRange();
1176 
1177 	UpdateAnimationPath();
1178 
1179 	mainInterface->progressBarAnimation->hide();
1180 }
1181 
GetParameterName(int rowNumber)1182 QString cKeyframeAnimation::GetParameterName(int rowNumber)
1183 {
1184 	const int parameterNumber = rowParameter[rowNumber];
1185 
1186 	QString fullParameterName;
1187 	QList<cAnimationFrames::sParameterDescription> list = keyframes->GetListOfUsedParameters();
1188 	if (parameterNumber >= 0)
1189 	{
1190 		fullParameterName =
1191 			list[parameterNumber].containerName + "_" + list[parameterNumber].parameterName;
1192 	}
1193 	else
1194 	{
1195 		qCritical() << "cFlightAnimation::GetParameterNumber(int rowNumber): row not found";
1196 	}
1197 	return fullParameterName;
1198 }
1199 
RenderFrame(int index) const1200 void cKeyframeAnimation::RenderFrame(int index) const
1201 {
1202 	mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1203 	keyframes->GetFrameAndConsolidate(index, params, fractalParams);
1204 
1205 	// recalculation of camera rotation and distance (just for display purposes)
1206 	const CVector3 camera = params->Get<CVector3>("camera");
1207 	const CVector3 target = params->Get<CVector3>("target");
1208 	const CVector3 top = params->Get<CVector3>("camera_top");
1209 	cCameraTarget cameraTarget(camera, target, top);
1210 	params->Set("camera_rotation", cameraTarget.GetRotation() * 180.0 / M_PI);
1211 	params->Set("camera_distance_to_target", cameraTarget.GetDistance());
1212 
1213 	mainInterface->SynchronizeInterface(params, fractalParams, qInterface::write);
1214 
1215 	params->Set("frame_no", keyframes->GetFrameIndexForKeyframe(index));
1216 
1217 	mainInterface->StartRender();
1218 }
1219 
DeleteFramesFrom(int index) const1220 void cKeyframeAnimation::DeleteFramesFrom(int index) const
1221 {
1222 	gUndo->Store(params, fractalParams, nullptr, keyframes);
1223 	for (int i = keyframes->GetNumberOfFrames() - 1; i >= index; i--)
1224 		table->removeColumn(index + reservedColumns);
1225 	keyframes->DeleteFrames(index, keyframes->GetNumberOfFrames() - 1);
1226 
1227 	keyframes->UpdateFramesIndexesTable();
1228 
1229 	UpdateLimitsForFrameRange();
1230 }
1231 
DeleteFramesTo(int index) const1232 void cKeyframeAnimation::DeleteFramesTo(int index) const
1233 {
1234 	gUndo->Store(params, fractalParams, nullptr, keyframes);
1235 	for (int i = 0; i <= index; i++)
1236 		table->removeColumn(reservedColumns);
1237 	keyframes->DeleteFrames(0, index);
1238 
1239 	keyframes->UpdateFramesIndexesTable();
1240 
1241 	UpdateLimitsForFrameRange();
1242 }
1243 
slotSelectKeyframeAnimImageDir() const1244 void cKeyframeAnimation::slotSelectKeyframeAnimImageDir() const
1245 {
1246 	QFileDialog *dialog = new QFileDialog();
1247 	dialog->setFileMode(QFileDialog::DirectoryOnly);
1248 	dialog->setNameFilter(QObject::tr("Animation Image Folder"));
1249 	dialog->setDirectory(QDir::toNativeSeparators(params->Get<QString>("anim_keyframe_dir")));
1250 	dialog->setAcceptMode(QFileDialog::AcceptOpen);
1251 	dialog->setWindowTitle(QObject::tr("Choose Animation Image Folder"));
1252 	dialog->setOption(QFileDialog::ShowDirsOnly);
1253 
1254 	if (dialog->exec())
1255 	{
1256 		QStringList fileNames = dialog->selectedFiles();
1257 		const QString filename = QDir::toNativeSeparators(fileNames.first() + QDir::separator());
1258 		ui->text_anim_keyframe_dir->setText(filename);
1259 		params->Set("anim_keyframe_dir", filename);
1260 	}
1261 }
1262 
slotTableCellChanged(int row,int column)1263 void cKeyframeAnimation::slotTableCellChanged(int row, int column)
1264 {
1265 	table->blockSignals(true);
1266 
1267 	if (row >= reservedRows)
1268 	{
1269 		QTableWidgetItem *cell = table->item(row, column);
1270 		QString cellText = cell->text();
1271 
1272 		const int index = column - reservedColumns;
1273 		cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(index);
1274 
1275 		const QString parameterName = GetParameterName(row);
1276 		const int parameterFirstRow = parameterRows[rowParameter[row]];
1277 		const int vectIndex = row - parameterFirstRow;
1278 
1279 		using namespace parameterContainer;
1280 		const enumVarType type = frame.parameters.GetVarType(parameterName);
1281 
1282 		if (type == typeVector3)
1283 		{
1284 			CVector3 vect = frame.parameters.Get<CVector3>(parameterName);
1285 			if (vectIndex == 0) vect.x = systemData.locale.toDouble(cellText);
1286 			if (vectIndex == 1) vect.y = systemData.locale.toDouble(cellText);
1287 			if (vectIndex == 2) vect.z = systemData.locale.toDouble(cellText);
1288 			frame.parameters.Set(parameterName, vect);
1289 		}
1290 		else if (type == typeVector4)
1291 		{
1292 			CVector4 vect = frame.parameters.Get<CVector4>(parameterName);
1293 			if (vectIndex == 0) vect.x = systemData.locale.toDouble(cellText);
1294 			if (vectIndex == 1) vect.y = systemData.locale.toDouble(cellText);
1295 			if (vectIndex == 2) vect.z = systemData.locale.toDouble(cellText);
1296 			if (vectIndex == 3) vect.w = systemData.locale.toDouble(cellText);
1297 			frame.parameters.Set(parameterName, vect);
1298 		}
1299 		else if (type == typeRgb)
1300 		{
1301 			sRGB col = frame.parameters.Get<sRGB>(parameterName);
1302 			if (vectIndex == 0) col.R = cellText.toInt();
1303 			if (vectIndex == 1) col.G = cellText.toInt();
1304 			if (vectIndex == 2) col.B = cellText.toInt();
1305 			frame.parameters.Set(parameterName, col);
1306 		}
1307 		else
1308 		{
1309 			frame.parameters.Set(parameterName, cellText);
1310 		}
1311 
1312 		keyframes->ModifyFrame(index, frame);
1313 
1314 		// update thumbnail
1315 		if (ui->checkBox_show_keyframe_thumbnails->isChecked())
1316 		{
1317 			auto tempPar = std::make_shared<cParameterContainer>();
1318 			*tempPar = *params;
1319 			auto tempFract = std::make_shared<cFractalContainer>();
1320 			*tempFract = *fractalParams;
1321 
1322 			keyframes->GetFrameAndConsolidate(index, tempPar, tempFract);
1323 			cThumbnailWidget *thumbWidget = static_cast<cThumbnailWidget *>(table->cellWidget(0, column));
1324 
1325 			if (!thumbWidget)
1326 			{
1327 				thumbWidget = new cThumbnailWidget(previewSize.width(), previewSize.height(), 1, table);
1328 				thumbWidget->UseOneCPUCore(true);
1329 				thumbWidget->AssignParameters(tempPar, tempFract);
1330 				table->setCellWidget(0, column, thumbWidget);
1331 			}
1332 			else
1333 			{
1334 				thumbWidget->AssignParameters(tempPar, tempFract);
1335 			}
1336 		}
1337 		UpdateAnimationPath();
1338 	}
1339 	else
1340 	{
1341 		if (row == framesPerKeyframeRow)
1342 		{
1343 			QTableWidgetItem *cell = table->item(row, column);
1344 			QString cellText = cell->text();
1345 			int newFramesPerKey = cellText.toInt();
1346 			if (newFramesPerKey < 1) newFramesPerKey = 1;
1347 
1348 			const int index = column - reservedColumns;
1349 			cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(index);
1350 			frame.numberOfSubFrames = newFramesPerKey;
1351 			keyframes->ModifyFrame(index, frame);
1352 
1353 			keyframes->UpdateFramesIndexesTable();
1354 		}
1355 	}
1356 
1357 	table->blockSignals(false);
1358 
1359 	const int index = min(column - reservedColumns, keyframes->GetNumberOfFrames() - 1);
1360 	int nextIndex = min(index + 1, keyframes->GetNumberOfFrames() - 1);
1361 	double speed = GetCameraSpeed(keyframes->GetFrame(index), keyframes->GetFrame(nextIndex));
1362 	QTableWidgetItem *speedCell = table->item(cameraSpeedRow, index + reservedColumns);
1363 	speedCell->setText(QString::number(speed));
1364 	table->update();
1365 }
1366 
slotDeleteAllImages() const1367 void cKeyframeAnimation::slotDeleteAllImages() const
1368 {
1369 	SynchronizeInterfaceWindow(
1370 		ui->scrollAreaWidgetContents_keyframeAnimationParameters, params, qInterface::read);
1371 
1372 	const QMessageBox::StandardButton reply = QMessageBox::question(
1373 		mainInterface->mainWindow->GetCentralWidget(), QObject::tr("Truncate Image Folder"),
1374 		QObject::tr("This will delete all images in the image folder.\nProceed?"),
1375 		QMessageBox::Yes | QMessageBox::No);
1376 
1377 	if (reply == QMessageBox::Yes)
1378 	{
1379 		cAnimationFrames::WipeFramesFromFolder(params->Get<QString>("anim_keyframe_dir"));
1380 	}
1381 }
1382 
slotShowAnimation() const1383 void cKeyframeAnimation::slotShowAnimation() const
1384 {
1385 	WriteLog("Prepare PlayerWidget class", 2);
1386 
1387 	SynchronizeInterfaceWindow(
1388 		ui->scrollAreaWidgetContents_keyframeAnimationParameters, params, qInterface::read);
1389 
1390 	if (!mainInterface->imageSequencePlayer)
1391 	{
1392 		mainInterface->imageSequencePlayer = new PlayerWidget;
1393 	}
1394 
1395 	mainInterface->imageSequencePlayer->SetFilePath(params->Get<QString>("anim_keyframe_dir"));
1396 	mainInterface->imageSequencePlayer->show();
1397 	mainInterface->imageSequencePlayer->raise();
1398 	mainInterface->imageSequencePlayer->activateWindow();
1399 }
1400 
InterpolateForward(int row,int column)1401 void cKeyframeAnimation::InterpolateForward(int row, int column)
1402 {
1403 	gUndo->Store(params, fractalParams, nullptr, keyframes);
1404 
1405 	QTableWidgetItem *cell = table->item(row, column);
1406 	QString cellText = cell->text();
1407 
1408 	const QString parameterName = GetParameterName(row);
1409 
1410 	const int index = column - reservedColumns;
1411 	cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(index);
1412 
1413 	using namespace parameterContainer;
1414 	const enumVarType type = frame.parameters.GetVarType(parameterName);
1415 
1416 	bool valueIsInteger = false;
1417 	bool valueIsDouble = false;
1418 	bool valueIsText = false;
1419 	int valueInteger = 0;
1420 	double valueDouble = 0.0;
1421 	QString valueText;
1422 
1423 	bool ok;
1424 	const int lastFrame = QInputDialog::getInt(mainInterface->mainWindow, "Parameter interpolation",
1425 		"Enter last keyframe number", index, index + 1, keyframes->GetNumberOfFrames() - 1, 1, &ok);
1426 	if (!ok) return;
1427 
1428 	const int numberOfFrames = (lastFrame - index);
1429 
1430 	switch (type)
1431 	{
1432 		case typeBool:
1433 		case typeInt:
1434 		case typeRgb:
1435 		{
1436 			valueIsInteger = true;
1437 			valueInteger = cellText.toInt();
1438 			// qDebug() << valueInteger;
1439 			break;
1440 		}
1441 		case typeDouble:
1442 		case typeVector3:
1443 		case typeVector4:
1444 		{
1445 			valueIsDouble = true;
1446 			valueDouble = systemData.locale.toDouble(cellText);
1447 			// qDebug() << valueDouble;
1448 			break;
1449 		}
1450 		default:
1451 		{
1452 			valueIsText = true;
1453 			valueText = cellText;
1454 			break;
1455 		}
1456 	}
1457 
1458 	double integerStep = 0.0;
1459 	double doubleStep = 0.0;
1460 
1461 	if (valueIsInteger)
1462 	{
1463 		const int finalInteger =
1464 			QInputDialog::getInt(mainInterface->mainWindow, "Parameter interpolation",
1465 				"Enter value for last keyframe", valueInteger, 0, 2147483647, 1, &ok);
1466 		integerStep = double(finalInteger - valueInteger) / numberOfFrames;
1467 	}
1468 	else if (valueIsDouble)
1469 	{
1470 		const double finalDouble = systemData.locale.toDouble(QInputDialog::getText(
1471 			mainInterface->mainWindow, "Parameter interpolation", "Enter value for last keyframe",
1472 			QLineEdit::Normal, QString("%L1").arg(valueDouble, 0, 'g', 15), &ok));
1473 		doubleStep = (finalDouble - valueDouble) / numberOfFrames;
1474 	}
1475 
1476 	if (!ok) return;
1477 
1478 	for (int i = index + 1; i <= lastFrame; i++)
1479 	{
1480 		QString newCellText;
1481 		if (valueIsInteger)
1482 		{
1483 			const int newValue = int(integerStep * (i - index) + valueInteger);
1484 			newCellText = QString::number(newValue);
1485 		}
1486 		else if (valueIsDouble)
1487 		{
1488 			const double newValue = doubleStep * (i - index) + valueDouble;
1489 			newCellText = QString("%L1").arg(newValue, 0, 'g', 15);
1490 		}
1491 		else if (valueIsText)
1492 		{
1493 			newCellText = valueText;
1494 		}
1495 		QTableWidgetItem *newCell = table->item(row, i + reservedColumns);
1496 		newCell->setText(newCellText);
1497 		newCell->setForeground(QBrush(Qt::black));
1498 	}
1499 }
1500 
CopyToAllKeyframes(int row,int column)1501 void cKeyframeAnimation::CopyToAllKeyframes(int row, int column)
1502 {
1503 	QTableWidgetItem *cell = table->item(row, column);
1504 	QString cellText = cell->text();
1505 
1506 	for (int i = reservedColumns; i < table->columnCount(); i++)
1507 	{
1508 		table->item(row, i)->setText(cellText);
1509 	}
1510 }
1511 
slotRefreshTable()1512 void cKeyframeAnimation::slotRefreshTable()
1513 {
1514 	RefreshTable();
1515 }
1516 
GetKeyframeFilename(int index,int subIndex,bool netRenderCache) const1517 QString cKeyframeAnimation::GetKeyframeFilename(int index, int subIndex, bool netRenderCache) const
1518 {
1519 	const int frameIndex = keyframes->GetFrameIndexForKeyframe(index) + subIndex;
1520 
1521 	QString dir;
1522 	if (netRenderCache)
1523 	{
1524 		dir = systemDirectories.GetNetrenderFolder() + QDir::separator()
1525 					+ QString("pid%1_").arg(QCoreApplication::applicationPid());
1526 	}
1527 	else
1528 	{
1529 		dir = params->Get<QString>("anim_keyframe_dir");
1530 	}
1531 
1532 	QString filename = dir + "frame_" + QString("%1").arg(frameIndex, 7, 10, QChar('0'));
1533 	filename += "."
1534 							+ ImageFileSave::ImageFileExtension(ImageFileSave::enumImageFileType(
1535 								params->Get<int>("keyframe_animation_image_type")));
1536 	return filename;
1537 }
1538 
GetMorphType(int row) const1539 parameterContainer::enumMorphType cKeyframeAnimation::GetMorphType(int row) const
1540 {
1541 	const int parameterIndex = rowParameter.at(row);
1542 	const parameterContainer::enumMorphType morphType =
1543 		keyframes->GetListOfParameters().at(parameterIndex).morphType;
1544 	return morphType;
1545 }
1546 
ChangeMorphType(int row,parameterContainer::enumMorphType morphType)1547 void cKeyframeAnimation::ChangeMorphType(int row, parameterContainer::enumMorphType morphType)
1548 {
1549 	gUndo->Store(params, fractalParams, nullptr, keyframes);
1550 	const int parameterIndex = rowParameter.at(row);
1551 	keyframes->ChangeMorphType(parameterIndex, morphType);
1552 	RefreshTable();
1553 }
1554 
slotExportKeyframesToFlight()1555 void cKeyframeAnimation::slotExportKeyframesToFlight()
1556 {
1557 	mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1558 	gUndo->Store(params, fractalParams, gAnimFrames, keyframes);
1559 
1560 	keyframes->UpdateFramesIndexesTable();
1561 
1562 	if (gAnimFrames->GetFrames().size() > 0)
1563 	{
1564 		const QMessageBox::StandardButton reply = QMessageBox::question(
1565 			mainInterface->mainWindow->GetCentralWidget(), QObject::tr("Export keyframes to flight"),
1566 			QObject::tr(
1567 				"There are already captured flight frames present.\nDiscard current flight frames ?"),
1568 			QMessageBox::Yes | QMessageBox::No);
1569 
1570 		if (reply == QMessageBox::No) return;
1571 	}
1572 
1573 	gAnimFrames->ClearAll();
1574 	gAnimFrames->SetListOfParametersAndClear(keyframes->GetListOfParameters(), params);
1575 
1576 	keyframes->ClearMorphCache();
1577 
1578 	keyframes->RefreshAllAudioTracks(params);
1579 
1580 	for (int frameIndex = 0; frameIndex < keyframes->GetTotalNumberOfFrames(); frameIndex++)
1581 	{
1582 		int index = keyframes->GetKeyframeIndex(frameIndex);
1583 		gAnimFrames->AddFrame(keyframes->GetInterpolatedFrame(frameIndex, params, fractalParams));
1584 
1585 		if (frameIndex % 100 == 0)
1586 		{
1587 			emit updateProgressAndStatus(QObject::tr("Exporting"), tr("Exporting keyframes to flight"),
1588 				double(index) / keyframes->GetNumberOfFrames(), cProgressText::progress_ANIMATION);
1589 			gApplication->processEvents();
1590 		}
1591 	}
1592 	mainInterface->progressBarAnimation->hide();
1593 	ui->tabWidgetFlightKeyframe->setCurrentIndex(0);
1594 	ui->pushButton_flight_refresh_table->animateClick();
1595 }
1596 
UpdateLimitsForFrameRange() const1597 void cKeyframeAnimation::UpdateLimitsForFrameRange() const
1598 {
1599 	int noOfFrames = (keyframes->GetTotalNumberOfFrames() - 1);
1600 	if (noOfFrames < 0) noOfFrames = 0;
1601 
1602 	ui->spinboxInt_keyframe_first_to_render->setMaximum(noOfFrames);
1603 
1604 	ui->spinboxInt_keyframe_last_to_render->setMaximum(noOfFrames);
1605 
1606 	if (lastToRenderMax)
1607 	{
1608 		ui->spinboxInt_keyframe_last_to_render->setValue(noOfFrames);
1609 	}
1610 }
1611 
slotMovedSliderFirstFrame() const1612 void cKeyframeAnimation::slotMovedSliderFirstFrame() const
1613 {
1614 	int value = ui->spinboxInt_keyframe_first_to_render->value();
1615 	if (value > ui->spinboxInt_keyframe_last_to_render->value())
1616 		ui->spinboxInt_keyframe_last_to_render->setValue(value);
1617 }
slotMovedSliderLastFrame()1618 void cKeyframeAnimation::slotMovedSliderLastFrame()
1619 {
1620 	int value = ui->spinboxInt_keyframe_last_to_render->value();
1621 	lastToRenderMax = (value == ui->spinboxInt_keyframe_last_to_render->maximum());
1622 	if (value < ui->spinboxInt_keyframe_first_to_render->value())
1623 		ui->spinboxInt_keyframe_first_to_render->setValue(value);
1624 }
1625 
CheckForCollisions(double minDist,bool * stopRequest)1626 QList<int> cKeyframeAnimation::CheckForCollisions(double minDist, bool *stopRequest)
1627 {
1628 	QList<int> listOfCollisions;
1629 
1630 	auto tempPar = std::make_shared<cParameterContainer>();
1631 	*tempPar = *params;
1632 	auto tempFractPar = std::make_shared<cFractalContainer>();
1633 	*tempFractPar = *fractalParams;
1634 
1635 	*stopRequest = false;
1636 
1637 	keyframes->UpdateFramesIndexesTable();
1638 
1639 	keyframes->ClearMorphCache();
1640 
1641 	for (int frameIndex = 0; frameIndex < keyframes->GetTotalNumberOfFrames(); frameIndex++)
1642 	{
1643 		int index = keyframes->GetKeyframeIndex(frameIndex);
1644 
1645 		gApplication->processEvents();
1646 		if (*stopRequest || systemData.globalStopRequest) return listOfCollisions;
1647 
1648 		keyframes->GetInterpolatedFrameAndConsolidate(frameIndex, tempPar, tempFractPar);
1649 		tempPar->Set("frame_no", frameIndex);
1650 		const CVector3 point = tempPar->Get<CVector3>("camera");
1651 		const double dist = mainInterface->GetDistanceForPoint(point, tempPar, tempFractPar);
1652 		if (dist < minDist)
1653 		{
1654 			listOfCollisions.append(frameIndex);
1655 		}
1656 
1657 		if (frameIndex % 100 == 0)
1658 		{
1659 			emit updateProgressAndStatus(QObject::tr("Checking for collisions"),
1660 				QObject::tr("Checking for collisions on keyframe # %1").arg(index),
1661 				double(frameIndex) / keyframes->GetTotalNumberOfFrames(),
1662 				cProgressText::progress_ANIMATION);
1663 		}
1664 	}
1665 
1666 	emit updateProgressAndStatus(QObject::tr("Checking for collisions"),
1667 		QObject::tr("Checking for collisions finished"), 1.0, cProgressText::progress_ANIMATION);
1668 
1669 	return listOfCollisions;
1670 }
1671 
slotValidate()1672 void cKeyframeAnimation::slotValidate()
1673 {
1674 	// updating parameters
1675 	mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1676 	gUndo->Store(params, fractalParams, nullptr, keyframes);
1677 
1678 	// checking for collisions
1679 	QList<int> listOfCollisions = CheckForCollisions(
1680 		params->Get<double>("keyframe_collision_thresh"), &mainInterface->stopRequest);
1681 	if (listOfCollisions.size() > 0)
1682 	{
1683 		QString collisionText;
1684 		for (int i = 0; i < listOfCollisions.size(); i++)
1685 		{
1686 			collisionText += QString("%1").arg(listOfCollisions.at(i));
1687 			if (i < listOfCollisions.size() - 1) collisionText += QString(", ");
1688 		}
1689 		emit showErrorMessage(
1690 			QObject::tr("Camera collides with fractal at following frames:\n") + collisionText,
1691 			cErrorMessage::warningMessage);
1692 	}
1693 	else
1694 	{
1695 		emit showErrorMessage(QObject::tr("No collisions detected\n"), cErrorMessage::infoMessage);
1696 	}
1697 }
1698 
slotCellDoubleClicked(int row,int column) const1699 void cKeyframeAnimation::slotCellDoubleClicked(int row, int column) const
1700 {
1701 	if (row == 0)
1702 	{
1703 		RenderFrame(column - reservedColumns);
1704 	}
1705 }
1706 
slotCellClicked(int row,int column) const1707 void cKeyframeAnimation::slotCellClicked(int row, int column) const
1708 {
1709 	Q_UNUSED(row);
1710 	Q_UNUSED(column);
1711 	UpdateCameraDistanceInformation();
1712 }
1713 
slotSetConstantTargetDistance()1714 void cKeyframeAnimation::slotSetConstantTargetDistance()
1715 {
1716 	// updating parameters
1717 	mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
1718 	gUndo->Store(params, fractalParams, nullptr, keyframes);
1719 
1720 	const double constDist = params->Get<double>("keyframe_constant_target_distance");
1721 
1722 	for (int key = 0; key < keyframes->GetNumberOfFrames() - 1; key++)
1723 	{
1724 		cAnimationFrames::sAnimationFrame keyframe = keyframes->GetFrame(key);
1725 
1726 		if (keyframe.parameters.IfExists("main_camera") && keyframe.parameters.IfExists("main_target")
1727 				&& keyframe.parameters.IfExists("main_camera_top"))
1728 		{
1729 			const CVector3 camera = keyframe.parameters.Get<CVector3>("main_camera");
1730 			const CVector3 target = keyframe.parameters.Get<CVector3>("main_target");
1731 			const CVector3 top = keyframe.parameters.Get<CVector3>("main_camera_top");
1732 
1733 			cCameraTarget cameraTarget(camera, target, top);
1734 			const CVector3 forwardVector = cameraTarget.GetForwardVector();
1735 
1736 			const CVector3 newTarget = camera + forwardVector * constDist;
1737 
1738 			keyframe.parameters.Set("main_target", newTarget);
1739 			if (keyframe.parameters.IfExists("camera_distance_to_target"))
1740 			{
1741 				keyframe.parameters.Set("camera_distance_to_target", constDist);
1742 			}
1743 
1744 			keyframes->ModifyFrame(key, keyframe);
1745 		}
1746 		else
1747 		{
1748 			cErrorMessage::showMessage(
1749 				QObject::tr("Cannot change target distance. Missing camera parameters in keyframes"),
1750 				cErrorMessage::errorMessage, mainInterface->mainWindow->GetCentralWidget());
1751 			return;
1752 		}
1753 	}
1754 	RefreshTable();
1755 }
1756 
AddAnimSoundColumn() const1757 void cKeyframeAnimation::AddAnimSoundColumn() const
1758 {
1759 	const int newColumn = table->columnCount();
1760 	table->insertColumn(newColumn);
1761 	table->setHorizontalHeaderItem(newColumn, new QTableWidgetItem(tr("Audio")));
1762 }
1763 
UpdateAnimationPath() const1764 void cKeyframeAnimation::UpdateAnimationPath() const
1765 {
1766 	int numberOfKeyframes = keyframes->GetNumberOfFrames();
1767 
1768 	auto tempPar = std::make_shared<cParameterContainer>();
1769 	*tempPar = *params;
1770 	auto tempFractPar = std::make_shared<cFractalContainer>();
1771 	*tempFractPar = *fractalParams;
1772 
1773 	sAnimationPathData animationPathData;
1774 	animationPathData.numberOfKeyframes = numberOfKeyframes;
1775 	animationPathData.numberOfFrames = keyframes->GetTotalNumberOfFrames();
1776 	animationPathData.actualSelectedFrameNo = table->currentColumn() - reservedColumns;
1777 	if (animationPathData.actualSelectedFrameNo < 0) animationPathData.actualSelectedFrameNo = 0;
1778 	animationPathData.cameraPathEnable = params->Get<bool>("show_camera_path");
1779 	animationPathData.targetPathEnable = params->Get<bool>("show_target_path");
1780 
1781 	for (int i = 1; i <= 4; i++)
1782 	{
1783 		if (params->IfExists(cLight::Name("is_defined", i)))
1784 		{
1785 			animationPathData.lightPathEnable[i - 1] =
1786 				params->Get<bool>("show_light_path", i) && params->Get<bool>(cLight::Name("enabled", i));
1787 		}
1788 		else
1789 		{
1790 			animationPathData.lightPathEnable[i - 1] = false;
1791 		}
1792 	}
1793 
1794 	for (int key = 0; key < keyframes->GetNumberOfFrames(); key++)
1795 	{
1796 		animationPathData.framesPeyKey.append(keyframes->GetFrame(key).numberOfSubFrames);
1797 	}
1798 
1799 	keyframes->ClearMorphCache();
1800 
1801 	for (int frameIndex = 0; frameIndex < keyframes->GetTotalNumberOfFrames(); frameIndex++)
1802 	{
1803 		keyframes->GetInterpolatedFrameAndConsolidate(frameIndex, tempPar, tempFractPar);
1804 		sAnimationPathPoint point;
1805 		point.camera = tempPar->Get<CVector3>("camera");
1806 		point.target = tempPar->Get<CVector3>("target");
1807 		CVector3 top = tempPar->Get<CVector3>("camera_top");
1808 		cCameraTarget cameraTarget(point.camera, point.target, top);
1809 
1810 		for (int l = 0; l < 4; l++)
1811 		{
1812 			if (params->IfExists(cLight::Name("is_defined", l + 1)))
1813 			{
1814 				if (params->Get<bool>(cLight::Name("relative_position", l + 1)))
1815 				{
1816 					CVector3 deltaPosition = tempPar->Get<CVector3>(cLight::Name("position", l + 1));
1817 					CVector3 deltaPositionRotated = cameraTarget.GetForwardVector() * deltaPosition.z
1818 																					+ cameraTarget.GetTopVector() * deltaPosition.y
1819 																					+ cameraTarget.GetRightVector() * deltaPosition.x;
1820 					point.lights[l] = point.camera + deltaPositionRotated;
1821 				}
1822 				else
1823 				{
1824 					point.lights[l] = tempPar->Get<CVector3>(cLight::Name("position", l + 1));
1825 				}
1826 				sRGB color16 = tempPar->Get<sRGB>(cLight::Name("color", l + 1));
1827 				sRGB8 color8(quint8(color16.R / 256), quint8(color16.G / 256), quint8(color16.B / 256));
1828 				point.lightColor[l] = color8;
1829 			}
1830 		}
1831 		animationPathData.animationPath.append(point);
1832 	}
1833 	imageWidget->SetAnimationPath(animationPathData);
1834 	imageWidget->update();
1835 }
1836 
slotUpdateAnimationPathSelection()1837 void cKeyframeAnimation::slotUpdateAnimationPathSelection()
1838 {
1839 	SynchronizeInterfaceWindow(ui->tab_keyframe_animation, params, qInterface::read);
1840 	UpdateAnimationPath();
1841 }
1842 
UpdateActualCameraPosition(const CVector3 & cameraPosition)1843 void cKeyframeAnimation::UpdateActualCameraPosition(const CVector3 &cameraPosition)
1844 {
1845 	actualCameraPosition = cameraPosition;
1846 	UpdateCameraDistanceInformation();
1847 }
1848 
UpdateCameraDistanceInformation() const1849 void cKeyframeAnimation::UpdateCameraDistanceInformation() const
1850 {
1851 	if (keyframes->GetNumberOfFrames() > 0)
1852 	{
1853 		int selectedKeyframe = table->currentColumn() - reservedColumns;
1854 		if (selectedKeyframe < 0) selectedKeyframe = 0;
1855 
1856 		cKeyframes::sAnimationFrame keyframe = keyframes->GetFrame(selectedKeyframe);
1857 
1858 		if (keyframe.parameters.IfExists("main_camera"))
1859 		{
1860 			CVector3 camera = keyframe.parameters.Get<CVector3>("main_camera");
1861 			double distance = (camera - actualCameraPosition).Length();
1862 			ui->label_camera_distance_from_keyframe->setText(
1863 				tr("Camera distance from selected keyframe: %1").arg(distance));
1864 		}
1865 	}
1866 }
1867 
SetNetRenderStartingFrames(const QVector<int> & startingFrames)1868 void cKeyframeAnimation::SetNetRenderStartingFrames(const QVector<int> &startingFrames)
1869 {
1870 	netRenderListOfFramesToRender.clear();
1871 	netRenderListOfFramesToRender.append(startingFrames.toList());
1872 }
1873 
slotNetRenderFinishedFrame(int clientIndex,int frameIndex,int sizeOfToDoList)1874 void cKeyframeAnimation::slotNetRenderFinishedFrame(
1875 	int clientIndex, int frameIndex, int sizeOfToDoList)
1876 {
1877 	Q_UNUSED(frameIndex);
1878 
1879 	renderedFramesCount++;
1880 	if (frameIndex < alreadyRenderedFrames.size())
1881 	{
1882 		alreadyRenderedFrames[frameIndex] = true;
1883 	}
1884 
1885 	if (!animationStopRequest && animationIsRendered)
1886 	{
1887 		// qDebug() << "Server: got information about finished frame" << frameIndex << sizeOfToDoList;
1888 
1889 		// counting left frames
1890 		int countLeft = reservedFrames.count(false);
1891 
1892 		// calculate maximum list size
1893 		int numberOfFramesForNetRender = countLeft / gNetRender->GetClientCount() / 2;
1894 		if (numberOfFramesForNetRender < minFramesForNetRender)
1895 			numberOfFramesForNetRender = minFramesForNetRender;
1896 		if (numberOfFramesForNetRender > maxFramesForNetRender)
1897 			numberOfFramesForNetRender = maxFramesForNetRender;
1898 
1899 		// calculate number for frames to supplement
1900 		int numberOfNewFrames = numberOfFramesForNetRender - sizeOfToDoList;
1901 
1902 		if (numberOfNewFrames > 0)
1903 		{
1904 			QList<int> toDoList;
1905 
1906 			// looking for not reserved frame
1907 			for (int f = 0; f < reservedFrames.size(); f++)
1908 			{
1909 				if (!reservedFrames[f])
1910 				{
1911 					toDoList.append(f);
1912 					reservedFrames[f] = true;
1913 				}
1914 				if (toDoList.size() >= numberOfNewFrames) break;
1915 			}
1916 			if (toDoList.size() > 0)
1917 			{
1918 				emit NetRenderSendFramesToDoList(clientIndex, toDoList);
1919 			}
1920 			// qDebug() << "Server: new toDo list" << toDoList;
1921 		}
1922 	}
1923 }
1924 
slotNetRenderUpdateFramesToDo(QList<int> listOfFrames)1925 void cKeyframeAnimation::slotNetRenderUpdateFramesToDo(QList<int> listOfFrames)
1926 {
1927 	if (animationIsRendered)
1928 	{
1929 		netRenderListOfFramesToRender.append(listOfFrames);
1930 		// qDebug() << "Client: got frames toDo:" << listOfFrames;
1931 	}
1932 }
1933 
slotRandomize()1934 void cKeyframeAnimation::slotRandomize()
1935 {
1936 	cRandomizerDialog *randomizer = new cRandomizerDialog(); // deleted by WA_DeleteOnClose
1937 	randomizer->setAttribute(Qt::WA_DeleteOnClose);
1938 	QStringList listOfParameterNames;
1939 	QList<cKeyframes::sParameterDescription> list = keyframes->GetListOfParameters();
1940 	for (const cKeyframes::sParameterDescription &parameter : list)
1941 	{
1942 		QString fullParameterName = parameter.containerName + "_" + parameter.parameterName;
1943 		listOfParameterNames.append(fullParameterName);
1944 	}
1945 
1946 	randomizer->AssignParameters(listOfParameterNames);
1947 	randomizer->show();
1948 }
1949 
slotAnimationStopRequest()1950 void cKeyframeAnimation::slotAnimationStopRequest()
1951 {
1952 	animationStopRequest = true;
1953 }
1954 
InsertKeyframeInBetween(int index)1955 void cKeyframeAnimation::InsertKeyframeInBetween(int index)
1956 {
1957 	if (keyframes)
1958 	{
1959 		auto tempPar = std::make_shared<cParameterContainer>();
1960 		*tempPar = *params;
1961 		auto tempFractPar = std::make_shared<cFractalContainer>();
1962 		*tempFractPar = *fractalParams;
1963 
1964 		keyframes->UpdateFramesIndexesTable();
1965 		keyframes->ClearMorphCache();
1966 
1967 		int frameIndex = keyframes->GetFrameIndexForKeyframe(index - 1)
1968 										 + keyframes->GetFramesPerKeyframe(index - 1) / 2;
1969 
1970 		keyframes->GetInterpolatedFrameAndConsolidate(frameIndex, tempPar, tempFractPar);
1971 
1972 		// add new frame to container
1973 		keyframes->AddFrame(
1974 			tempPar, tempFractPar, keyframes->GetFramesPerKeyframe(index - 1) / 2, index);
1975 		keyframes->UpdateFramesIndexesTable();
1976 
1977 		params->Set("frame_no", keyframes->GetFrameIndexForKeyframe(index));
1978 
1979 		// add column to table
1980 		const int newColumn = AddColumn(keyframes->GetFrame(index), index);
1981 		table->selectColumn(newColumn);
1982 
1983 		if (ui->checkBox_show_keyframe_thumbnails->isChecked())
1984 		{
1985 			cThumbnailWidget *thumbWidget =
1986 				new cThumbnailWidget(previewSize.width(), previewSize.height(), 1, table);
1987 			thumbWidget->UseOneCPUCore(false);
1988 			thumbWidget->AssignParameters(tempPar, tempFractPar);
1989 			table->setCellWidget(0, newColumn, thumbWidget);
1990 		}
1991 
1992 		// change frames per keyframe in previous keyframe
1993 		cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(index - 1);
1994 		frame.numberOfSubFrames = frame.numberOfSubFrames / 2;
1995 		keyframes->ModifyFrame(index - 1, frame);
1996 
1997 		keyframes->UpdateFramesIndexesTable();
1998 
1999 		UpdateLimitsForFrameRange();
2000 
2001 		RefreshTable();
2002 
2003 		UpdateAnimationPath();
2004 	}
2005 	else
2006 	{
2007 		qCritical() << "gAnimFrames not allocated";
2008 	}
2009 }
2010 
DeleteRenderedFramesForKeyframe(int keyframeIndex)2011 void cKeyframeAnimation::DeleteRenderedFramesForKeyframe(int keyframeIndex)
2012 {
2013 	if (keyframeIndex < keyframes->GetNumberOfFrames() - 1)
2014 	{
2015 		int startFrame = keyframes->GetFrameIndexForKeyframe(keyframeIndex);
2016 		int endFrame = keyframes->GetFrameIndexForKeyframe(keyframeIndex + 1) - 1;
2017 
2018 		SynchronizeInterfaceWindow(
2019 			ui->scrollAreaWidgetContents_keyframeAnimationParameters, params, qInterface::read);
2020 
2021 		QString folder = params->Get<QString>("anim_keyframe_dir");
2022 
2023 		const QMessageBox::StandardButton reply = QMessageBox::question(
2024 			mainInterface->mainWindow->GetCentralWidget(), QObject::tr("Deleting rendered frames"),
2025 			QObject::tr("This will delete rendered frames from %1 to %2\n"
2026 									"in the image folder.\n"
2027 									"%3\n"
2028 									"Proceed?")
2029 				.arg(startFrame)
2030 				.arg(endFrame)
2031 				.arg(folder),
2032 			QMessageBox::Yes | QMessageBox::No);
2033 
2034 		if (reply == QMessageBox::Yes)
2035 		{
2036 			cAnimationFrames::WipeFramesFromFolder(folder, startFrame, endFrame);
2037 		}
2038 	}
2039 }
2040 
GetCameraSpeed(const cAnimationFrames::sAnimationFrame & frame,const cAnimationFrames::sAnimationFrame & nextFrame)2041 double cKeyframeAnimation::GetCameraSpeed(const cAnimationFrames::sAnimationFrame &frame,
2042 	const cAnimationFrames::sAnimationFrame &nextFrame)
2043 {
2044 	CVector3 camera = frame.parameters.Get<CVector3>("main_camera");
2045 	CVector3 previousCamera = nextFrame.parameters.Get<CVector3>("main_camera");
2046 	double distance = (camera - previousCamera).Length();
2047 	double speed = distance / frame.numberOfSubFrames;
2048 	return speed;
2049 }
2050 
slotAddAllParameters()2051 void cKeyframeAnimation::slotAddAllParameters()
2052 {
2053 
2054 	struct sParameterInfo
2055 	{
2056 		QString parameterName;
2057 		std::shared_ptr<cParameterContainer> container;
2058 		std::shared_ptr<cParameterContainer> containerBefore;
2059 	};
2060 
2061 	std::shared_ptr<cParameterContainer> parBefore(new cParameterContainer);
2062 	*parBefore = *params;
2063 	std::shared_ptr<cFractalContainer> parFractBefore(new cFractalContainer);
2064 	*parFractBefore = *fractalParams;
2065 
2066 	mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
2067 
2068 	QList<sParameterInfo> listOfAllModifiedParameters;
2069 
2070 	QList<QString> listOfMainParameters = params->GetListOfParameters();
2071 	for (QString parameterName : listOfMainParameters)
2072 	{
2073 		if (!params->isDefaultValue(parameterName)
2074 				&& params->GetParameterType(parameterName) == paramStandard
2075 				&& params->GetAsOneParameter(parameterName).GetMorphType() != morphNone)
2076 		{
2077 			// exceptions
2078 			if (parameterName == "image_width") continue;
2079 			if (parameterName == "image_height") continue;
2080 			if (parameterName == "camera_distance_to_target") continue;
2081 			if (parameterName == "camera_rotation") continue;
2082 
2083 			sParameterInfo par;
2084 			par.parameterName = parameterName;
2085 			par.container = params;
2086 			par.containerBefore = parBefore;
2087 			listOfAllModifiedParameters.append(par);
2088 		}
2089 	}
2090 
2091 	for (int i = 0; i < NUMBER_OF_FRACTALS; i++)
2092 	{
2093 		QList<QString> listOfFractalParameters = fractalParams->at(i)->GetListOfParameters();
2094 		for (QString parameterName : listOfFractalParameters)
2095 		{
2096 			if (!fractalParams->at(i)->isDefaultValue(parameterName)
2097 					&& fractalParams->at(i)->GetParameterType(parameterName) == paramStandard)
2098 			{
2099 				sParameterInfo par;
2100 				par.parameterName = parameterName;
2101 				par.container = fractalParams->at(i);
2102 				par.containerBefore = parFractBefore->at(i);
2103 				listOfAllModifiedParameters.append(par);
2104 			}
2105 		}
2106 	}
2107 
2108 	for (const sParameterInfo &parameterInfo : listOfAllModifiedParameters)
2109 	{
2110 		QString parameterName = parameterInfo.parameterName;
2111 		QString containerName = parameterInfo.container->GetContainerName();
2112 		if (keyframes->IndexOnList(parameterName, containerName) == -1)
2113 		{
2114 			cOneParameter parameter = parameterInfo.containerBefore->GetAsOneParameter(parameterName);
2115 			keyframes->AddAnimatedParameter(parameterName, parameter, params);
2116 		}
2117 	}
2118 
2119 	RefreshTable();
2120 }
2121 
ModifyValueInCells(const QList<QTableWidgetItem * > & selectedItemsList,enumModifyMode mode)2122 void cKeyframeAnimation::ModifyValueInCells(
2123 	const QList<QTableWidgetItem *> &selectedItemsList, enumModifyMode mode)
2124 {
2125 	bool ok;
2126 	const double modifier = QInputDialog::getDouble(
2127 		mainInterface->mainWindow, "Modify values", "Modifier:", 1.0, -1e100, 1e100, 5, &ok);
2128 	if (!ok) return;
2129 
2130 	for (auto cell : selectedItemsList)
2131 	{
2132 		int row = cell->row();
2133 		int column = cell->column();
2134 
2135 		if (row == framesPerKeyframeRow && column >= reservedColumns)
2136 		{
2137 			const int index = column - reservedColumns;
2138 			cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(index);
2139 
2140 			if (mode == modifyModeMultiply)
2141 				frame.numberOfSubFrames *= modifier;
2142 			else if (mode == modifyModeIncrease)
2143 				frame.numberOfSubFrames += modifier;
2144 
2145 			keyframes->ModifyFrame(index, frame);
2146 		}
2147 		else if (row >= reservedRows && column >= reservedColumns)
2148 		{
2149 			QString cellText = cell->text();
2150 
2151 			const int index = column - reservedColumns;
2152 			cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(index);
2153 
2154 			const QString parameterName = GetParameterName(row);
2155 			const int parameterFirstRow = parameterRows[rowParameter[row]];
2156 			const int vectIndex = row - parameterFirstRow;
2157 
2158 			using namespace parameterContainer;
2159 			const enumVarType type = frame.parameters.GetVarType(parameterName);
2160 
2161 			if (type == typeVector3)
2162 			{
2163 				CVector3 vect = frame.parameters.Get<CVector3>(parameterName);
2164 				if (mode == modifyModeMultiply)
2165 				{
2166 					if (vectIndex == 0) vect.x *= modifier;
2167 					if (vectIndex == 1) vect.y *= modifier;
2168 					if (vectIndex == 2) vect.z *= modifier;
2169 				}
2170 				else if (mode == modifyModeIncrease)
2171 				{
2172 					if (vectIndex == 0) vect.x += modifier;
2173 					if (vectIndex == 1) vect.y += modifier;
2174 					if (vectIndex == 2) vect.z += modifier;
2175 				}
2176 				frame.parameters.Set(parameterName, vect);
2177 			}
2178 			else if (type == typeVector4)
2179 			{
2180 				CVector4 vect = frame.parameters.Get<CVector4>(parameterName);
2181 
2182 				if (mode == modifyModeMultiply)
2183 				{
2184 					if (vectIndex == 0) vect.x *= modifier;
2185 					if (vectIndex == 1) vect.y *= modifier;
2186 					if (vectIndex == 2) vect.z *= modifier;
2187 					if (vectIndex == 3) vect.w *= modifier;
2188 				}
2189 				else if (mode == modifyModeIncrease)
2190 				{
2191 					if (vectIndex == 0) vect.x += modifier;
2192 					if (vectIndex == 1) vect.y += modifier;
2193 					if (vectIndex == 2) vect.z += modifier;
2194 					if (vectIndex == 3) vect.w += modifier;
2195 				}
2196 				frame.parameters.Set(parameterName, vect);
2197 			}
2198 			else
2199 			{
2200 				if (mode == modifyModeMultiply)
2201 				{
2202 					frame.parameters.Set(
2203 						parameterName, frame.parameters.Get<double>(parameterName) * modifier);
2204 				}
2205 				else if (mode == modifyModeIncrease)
2206 				{
2207 					frame.parameters.Set(
2208 						parameterName, frame.parameters.Get<double>(parameterName) + modifier);
2209 				}
2210 			}
2211 
2212 			keyframes->ModifyFrame(index, frame);
2213 		}
2214 	}
2215 	RefreshTable();
2216 }
2217 
slotSetFramesPerKeyframeToAllKeyframes(void)2218 void cKeyframeAnimation::slotSetFramesPerKeyframeToAllKeyframes(void)
2219 {
2220 	SynchronizeInterfaceWindow(
2221 		ui->scrollAreaWidgetContents_keyframeAnimationParameters, params, qInterface::read);
2222 	int framesPerKeyframe = params->Get<int>("frames_per_keyframe");
2223 
2224 	for (int i = 0; i < keyframes->GetNumberOfFrames(); i++)
2225 	{
2226 		cAnimationFrames::sAnimationFrame frame = keyframes->GetFrame(i);
2227 		frame.numberOfSubFrames = framesPerKeyframe;
2228 		keyframes->ModifyFrame(i, frame);
2229 	}
2230 	RefreshTable();
2231 }
2232 
AddAnimatedParameter(const QString & parameterName,std::shared_ptr<cParameterContainer> parameterContainer)2233 void cKeyframeAnimation::AddAnimatedParameter(
2234 	const QString &parameterName, std::shared_ptr<cParameterContainer> parameterContainer)
2235 {
2236 	mainInterface->SynchronizeInterface(params, fractalParams, qInterface::read);
2237 	keyframes->AddAnimatedParameter(
2238 		parameterName, parameterContainer->GetAsOneParameter(parameterName));
2239 }
2240 
cKeyframeRenderThread(QString & _settingsText)2241 cKeyframeRenderThread::cKeyframeRenderThread(QString &_settingsText) : QThread()
2242 {
2243 	settingsText = _settingsText;
2244 }
2245 
startAnimationRender()2246 void cKeyframeRenderThread::startAnimationRender()
2247 {
2248 	cSettings parSettings(cSettings::formatFullText);
2249 	parSettings.BeQuiet(true);
2250 	parSettings.LoadFromString(settingsText);
2251 	parSettings.Decode(gPar, gParFractal, nullptr, gKeyframes);
2252 	gNetRender->SetAnimation(true);
2253 	gKeyframeAnimation->RenderKeyframes(&gMainInterface->stopRequest);
2254 	emit renderingFinished();
2255 }
2256 
cKeyframeSaveImageThread(std::shared_ptr<cImage> _image,const QString & _filename,ImageFileSave::enumImageFileType _fileType)2257 cKeyframeSaveImageThread::cKeyframeSaveImageThread(std::shared_ptr<cImage> _image,
2258 	const QString &_filename, ImageFileSave::enumImageFileType _fileType)
2259 {
2260 	image = _image;
2261 	filename = _filename;
2262 	fileType = _fileType;
2263 }
2264 
startSaving()2265 void cKeyframeSaveImageThread::startSaving()
2266 {
2267 	SaveImage(filename, fileType, image, gMainInterface->mainWindow);
2268 	emit savingFinished();
2269 }
2270