moldyn.cpp 22.6 KB
Newer Older
1
/**
Tobias WEBER's avatar
Tobias WEBER committed
2
 * atom dynamics
3
4
5
6
7
 * @author Tobias Weber <tweber@ill.fr>
 * @date Dec-2019
 * @license GPLv3, see 'LICENSE' file
 */

Tobias WEBER's avatar
Tobias WEBER committed
8
#include "moldyn.h"
9

Tobias WEBER's avatar
Tobias WEBER committed
10
#include <QtWidgets/QApplication>
Tobias WEBER's avatar
Tobias WEBER committed
11
#include <QtWidgets/QGridLayout>
Tobias WEBER's avatar
Tobias WEBER committed
12
#include <QtWidgets/QFileDialog>
Tobias WEBER's avatar
Tobias WEBER committed
13
14
#include <QtWidgets/QLabel>
#include <QtWidgets/QSpinBox>
Tobias WEBER's avatar
Tobias WEBER committed
15
#include <QtWidgets/QComboBox>
Tobias WEBER's avatar
Tobias WEBER committed
16
#include <QtWidgets/QMessageBox>
17
#include <QtWidgets/QProgressDialog>
18

Tobias WEBER's avatar
Tobias WEBER committed
19
20
#include <iostream>
#include <tuple>
Tobias WEBER's avatar
Tobias WEBER committed
21
#include <memory>
22

Tobias WEBER's avatar
Tobias WEBER committed
23
24
25
26
27
28
29
30
31
#include "libs/algos.h"
#include "libs/helper.h"

using namespace m_ops;

constexpr t_real g_eps = 1e-6;
constexpr int g_prec = 6;


Tobias WEBER's avatar
Tobias WEBER committed
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

// ----------------------------------------------------------------------------
/**
 * File dialog with options
 */
class MolDynFileDlg : public QFileDialog
{
	public:
		MolDynFileDlg(QWidget *parent, const QString& title, const QString& dir, const QString& filter)
			: QFileDialog(parent, title, dir, filter)
		{
			// options panel with frame skip
			QLabel *labelFrameSkip = new QLabel("Frame Skip: ", this);
			m_spinFrameSkip = new QSpinBox(this);
			m_spinFrameSkip->setValue(10);
Tobias WEBER's avatar
Tobias WEBER committed
47
48
			m_spinFrameSkip->setSingleStep(1);
			m_spinFrameSkip->setRange(0, 9999999);
Tobias WEBER's avatar
Tobias WEBER committed
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

			labelFrameSkip->setSizePolicy(QSizePolicy{QSizePolicy::Fixed, QSizePolicy::Fixed});
			m_spinFrameSkip->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});

			QWidget *pPanel = new QWidget();
			auto pPanelGrid = new QGridLayout(pPanel);
			pPanelGrid->setSpacing(2);
			pPanelGrid->setContentsMargins(4,4,4,4);

			pPanelGrid->addWidget(labelFrameSkip, 0,0,1,1);
			pPanelGrid->addWidget(m_spinFrameSkip, 0,1,1,1);

			// add the options panel to the layout
			setOptions(QFileDialog::DontUseNativeDialog);
			QGridLayout *pGrid = reinterpret_cast<QGridLayout*>(layout());
			if(pGrid)
				pGrid->addWidget(pPanel, pGrid->rowCount(), 0, 1, pGrid->columnCount());
		}


		int GetFrameSkip() const
		{
			return m_spinFrameSkip->value();
		}


	private:
		QSpinBox *m_spinFrameSkip = nullptr;
};
Tobias WEBER's avatar
Tobias WEBER committed
78
// ----------------------------------------------------------------------------
Tobias WEBER's avatar
Tobias WEBER committed
79
80
81



Tobias WEBER's avatar
Tobias WEBER committed
82
83
84
85
86
87
88
89
90
91
92
// ----------------------------------------------------------------------------
MolDynDlg::MolDynDlg(QWidget* pParent) : QMainWindow{pParent},
	m_sett{new QSettings{"tobis_stuff", "moldyn"}}
{
	setWindowTitle("Molecular Dynamics");
	this->setObjectName("moldyn");

	m_status = new QStatusBar(this);
	this->setStatusBar(m_status);


Tobias WEBER's avatar
Tobias WEBER committed
93
94
95
96
97
98
99
	QWidget *pMainPanel = new QWidget();
	auto pMainGrid = new QGridLayout(pMainPanel);
	pMainGrid->setSpacing(2);
	pMainGrid->setContentsMargins(4,4,4,4);
	this->setCentralWidget(pMainPanel);


Tobias WEBER's avatar
Tobias WEBER committed
100
101
102
103
104
	// menu bar
	{
		m_menu = new QMenuBar(this);
		m_menu->setNativeMenuBar(m_sett ? m_sett->value("native_gui", false).toBool() : false);

Tobias WEBER's avatar
Tobias WEBER committed
105
		// File
Tobias WEBER's avatar
Tobias WEBER committed
106
107
108
109
		auto menuFile = new QMenu("File", m_menu);

		auto acNew = new QAction("New", menuFile);
		auto acLoad = new QAction("Load...", menuFile);
Tobias WEBER's avatar
Tobias WEBER committed
110
		auto acSaveAs = new QAction("Save As...", menuFile);
Tobias WEBER's avatar
Tobias WEBER committed
111
112
113
114
115
		auto acExit = new QAction("Exit", menuFile);

		menuFile->addAction(acNew);
		menuFile->addSeparator();
		menuFile->addAction(acLoad);
Tobias WEBER's avatar
Tobias WEBER committed
116
		menuFile->addAction(acSaveAs);
Tobias WEBER's avatar
Tobias WEBER committed
117
118
119
120
121
		menuFile->addSeparator();
		menuFile->addAction(acExit);

		connect(acNew, &QAction::triggered, this, &MolDynDlg::New);
		connect(acLoad, &QAction::triggered, this, &MolDynDlg::Load);
Tobias WEBER's avatar
Tobias WEBER committed
122
		connect(acSaveAs, &QAction::triggered, this, &MolDynDlg::SaveAs);
Tobias WEBER's avatar
Tobias WEBER committed
123
124
		connect(acExit, &QAction::triggered, this, &QDialog::close);

Tobias WEBER's avatar
Tobias WEBER committed
125
126

		// Edit
Tobias WEBER's avatar
Tobias WEBER committed
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
		auto menuEdit = new QMenu("Edit", m_menu);
		auto acSelectNone = new QAction("Select None", menuEdit);

		menuEdit->addAction(acSelectNone);

		connect(acSelectNone, &QAction::triggered, this, &MolDynDlg::SelectNone);


		// Calculate
		auto menuCalc = new QMenu("Calculate", m_menu);
		auto acCalcDist = new QAction("Distance Between Selected Atoms", menuEdit);

		menuCalc->addAction(acCalcDist);

		connect(acCalcDist, &QAction::triggered, this, &MolDynDlg::CalculateDistanceBetweenAtoms);
Tobias WEBER's avatar
Tobias WEBER committed
142
143


Tobias WEBER's avatar
Tobias WEBER committed
144
		m_menu->addMenu(menuFile);
Tobias WEBER's avatar
Tobias WEBER committed
145
146
		m_menu->addMenu(menuEdit);
		m_menu->addMenu(menuCalc);
Tobias WEBER's avatar
Tobias WEBER committed
147
148
149
150
		this->setMenuBar(m_menu);
	}


Tobias WEBER's avatar
Tobias WEBER committed
151
152
153
154
155
156
157
158
159
160
	// context menus
	{
		m_atomContextMenu = new QMenu(this);
		m_atomContextMenu->setTitle("Atoms");
		m_atomContextMenu->addAction("Delete Atom", this, &MolDynDlg::DeleteAtomUnderCursor);
		m_atomContextMenu->addAction("Delete All Atoms Of Selected Type", this, &MolDynDlg::DeleteAllAtomsOfSameType);
		m_atomContextMenu->addAction("Only Keep Atoms Of Selected Type", this, &MolDynDlg::KeepAtomsOfSameType);
	}


Tobias WEBER's avatar
Tobias WEBER committed
161
162
163
164
165
	// plot widget
	{
		m_plot = new GlPlot(this);
		m_plot->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Expanding});

Tobias WEBER's avatar
Tobias WEBER committed
166
		m_plot->GetImpl()->EnablePicker(1);
Tobias WEBER's avatar
Tobias WEBER committed
167
168
169
170
171
172
173
174
175
176
		m_plot->GetImpl()->SetLight(0, m::create<t_vec3_gl>({ 5, 5, 5 }));
		m_plot->GetImpl()->SetLight(1, m::create<t_vec3_gl>({ -5, -5, -5 }));
		m_plot->GetImpl()->SetCoordMax(1.);
		m_plot->GetImpl()->SetCamBase(m::create<t_mat_gl>({1,0,0,0,  0,0,1,0,  0,-1,0,-1.5,  0,0,0,1}),
			m::create<t_vec_gl>({1,0,0,0}), m::create<t_vec_gl>({0,0,1,0}));

		connect(m_plot, &GlPlot::AfterGLInitialisation, this, &MolDynDlg::AfterGLInitialisation);
		connect(m_plot->GetImpl(), &GlPlot_impl::PickerIntersection, this, &MolDynDlg::PickerIntersection);
		connect(m_plot, &GlPlot::MouseDown, this, &MolDynDlg::PlotMouseDown);
		connect(m_plot, &GlPlot::MouseUp, this, &MolDynDlg::PlotMouseUp);
Tobias WEBER's avatar
Tobias WEBER committed
177
		connect(m_plot, &GlPlot::MouseClick, this, &MolDynDlg::PlotMouseClick);
Tobias WEBER's avatar
Tobias WEBER committed
178

Tobias WEBER's avatar
Tobias WEBER committed
179
		//this->setCentralWidget(m_plot);
Tobias WEBER's avatar
Tobias WEBER committed
180
		pMainGrid->addWidget(m_plot, 0,0,1,9);
Tobias WEBER's avatar
Tobias WEBER committed
181
182
183
184
185
	}


	// controls
	{
Tobias WEBER's avatar
Tobias WEBER committed
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
		auto labCoordSys = new QLabel("Coordinates:", this);
		auto labFrames = new QLabel("Frames:", this);
		auto labScale = new QLabel("Scale:", this);
		labCoordSys->setSizePolicy(QSizePolicy{QSizePolicy::Fixed, QSizePolicy::Fixed});
		labFrames->setSizePolicy(QSizePolicy{QSizePolicy::Fixed, QSizePolicy::Fixed});
		labScale->setSizePolicy(QSizePolicy{QSizePolicy::Fixed, QSizePolicy::Fixed});

		auto comboCoordSys = new QComboBox(this);
		comboCoordSys->addItem("Fractional Units (rlu)");
		comboCoordSys->addItem("Lab Units (A)");
		comboCoordSys->setFocusPolicy(Qt::StrongFocus);

		m_spinScale = new QDoubleSpinBox(this);
		m_spinScale->setDecimals(4);
		m_spinScale->setRange(1e-4, 1e4);
		m_spinScale->setSingleStep(0.1);
		m_spinScale->setValue(0.4);
		m_spinScale->setFocusPolicy(Qt::StrongFocus);

Tobias WEBER's avatar
Tobias WEBER committed
205
206
207
		m_slider = new QSlider(Qt::Horizontal, this);
		m_slider->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Minimum});
		m_slider->setMinimum(0);
Tobias WEBER's avatar
Tobias WEBER committed
208
209
		m_slider->setSingleStep(1);
		m_slider->setPageStep(10);
Tobias WEBER's avatar
Tobias WEBER committed
210
		m_slider->setTracking(1);
Tobias WEBER's avatar
Tobias WEBER committed
211
		m_slider->setFocusPolicy(Qt::StrongFocus);
Tobias WEBER's avatar
Tobias WEBER committed
212
213

		connect(m_slider, &QSlider::valueChanged, this, &MolDynDlg::SliderValueChanged);
Tobias WEBER's avatar
Tobias WEBER committed
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
		connect(comboCoordSys, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this](int val)
		{
			if(this->m_plot)
				this->m_plot->GetImpl()->SetCoordSys(val);
		});
		connect(m_spinScale, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, [this](double val)
		{
			if(!this->m_plot) return;

			// hack to trigger update
			SliderValueChanged(m_slider->value());
		});

		pMainGrid->addWidget(labCoordSys, 1,0,1,1);
		pMainGrid->addWidget(comboCoordSys, 1,1,1,1);
		pMainGrid->addWidget(labScale, 1,2,1,1);
		pMainGrid->addWidget(m_spinScale, 1,3,1,1);
		pMainGrid->addWidget(labFrames, 1,4,1,1);
		pMainGrid->addWidget(m_slider, 1,5,1,4);
Tobias WEBER's avatar
Tobias WEBER committed
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
	}


	// restore window size and position
	if(m_sett && m_sett->contains("geo"))
		restoreGeometry(m_sett->value("geo").toByteArray());
	else
		resize(600, 500);

	m_ignoreChanges = 0;
}



// ----------------------------------------------------------------------------
/**
Tobias WEBER's avatar
Tobias WEBER committed
249
 * add an atom
Tobias WEBER's avatar
Tobias WEBER committed
250
 */
251
std::size_t MolDynDlg::Add3DItem(const t_vec& vec, const t_vec& col, t_real scale, const std::string& label)
Tobias WEBER's avatar
Tobias WEBER committed
252
{
Tobias WEBER's avatar
Tobias WEBER committed
253
	auto obj = m_plot->GetImpl()->AddLinkedObject(m_sphere, 0,0,0, col[0],col[1],col[2],1);
Tobias WEBER's avatar
Tobias WEBER committed
254
	Change3DItem(obj, &vec, &col, &scale, &label);
255
	return obj;
Tobias WEBER's avatar
Tobias WEBER committed
256
}
Tobias WEBER's avatar
Tobias WEBER committed
257
258
259
260
261
262
263


/**
 * change an atom
 */
void MolDynDlg::Change3DItem(std::size_t obj, const t_vec *vec, const t_vec *col, const t_real *scale, const std::string *label)
{
Tobias WEBER's avatar
Tobias WEBER committed
264
265
266
267
268
269
	if(vec)
	{
		t_mat_gl mat = m::hom_translation<t_mat_gl>((*vec)[0], (*vec)[1], (*vec)[2]);
		if(scale) mat *= m::hom_scaling<t_mat_gl>(*scale, *scale, *scale);
		m_plot->GetImpl()->SetObjectMatrix(obj, mat);
	}
Tobias WEBER's avatar
Tobias WEBER committed
270
271
272
273
274

	if(col) m_plot->GetImpl()->SetObjectCol(obj, (*col)[0], (*col)[1], (*col)[2], 1);
	if(label) m_plot->GetImpl()->SetObjectLabel(obj, *label);
	if(label) m_plot->GetImpl()->SetObjectDataString(obj, *label);
}
Tobias WEBER's avatar
Tobias WEBER committed
275
276
277
278
// ----------------------------------------------------------------------------



Tobias WEBER's avatar
Tobias WEBER committed
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
// ----------------------------------------------------------------------------
/**
 * calculate the distance between selected atoms
 */
void MolDynDlg::CalculateDistanceBetweenAtoms()
{
	std::vector<std::tuple<std::size_t, std::size_t>> objs;

	for(const auto& obj : m_sphereHandles)
	{
		// continue if object isn't selected
		if(!m_plot->GetImpl()->GetObjectHighlight(obj))
			continue;

		// get indices for selected atoms
		const auto [bOk, atomTypeIdx, atomSubTypeIdx, sphereIdx] = GetAtomIndexFromHandle(obj);
		if(!bOk)
		{
			QMessageBox::critical(this, "Molecular Dynamics", "Atom handle not found, data is corrupted.");
			return;
		}

		objs.push_back(std::make_tuple(atomTypeIdx, atomSubTypeIdx));
	}

	if(objs.size() <= 1)
	{
		QMessageBox::critical(this, "Molecular Dynamics", "At least two atoms have to be selected.");
		return;
	}


	// get coordinates of first atom
	auto [firstObjTypeIdx, firstObjSubTypeIdx] = objs[0];
	auto firstObjCoords = m_mol.GetAtomCoords(firstObjTypeIdx, firstObjSubTypeIdx);

	std::vector<t_vec> firstObjCoordsCryst;
	firstObjCoordsCryst.reserve(m_mol.GetFrameCount());

	for(std::size_t frameidx=0; frameidx<m_mol.GetFrameCount(); ++frameidx)
		firstObjCoordsCryst.push_back(m_crystA * firstObjCoords[frameidx]);


	// get distances to other selected atoms
	for(std::size_t objIdx=1; objIdx<objs.size(); ++objIdx)
	{
		auto [objTypeIdx, objSubTypeIdx] = objs[objIdx];
		const auto objCoords = m_mol.GetAtomCoords(objTypeIdx, objSubTypeIdx);
		
		for(std::size_t frameidx=0; frameidx<m_mol.GetFrameCount(); ++frameidx)
		{
			t_real dist = m::get_dist_uc(m_crystA, firstObjCoordsCryst[frameidx], objCoords[frameidx]);

			// TODO: plot/save/...
			std::cout << dist << std::endl;
		}
	}
}
// ----------------------------------------------------------------------------


Tobias WEBER's avatar
Tobias WEBER committed
340
341
342
343

// ----------------------------------------------------------------------------
void MolDynDlg::New()
{
Tobias WEBER's avatar
Tobias WEBER committed
344
	m_mol.Clear();
Tobias WEBER's avatar
Tobias WEBER committed
345
346
347
348

	for(const auto& obj : m_sphereHandles)
		m_plot->GetImpl()->RemoveObject(obj);

349
	m_sphereHandles.clear();
Tobias WEBER's avatar
Tobias WEBER committed
350
351
352
	m_slider->setValue(0);

	m_plot->update();
Tobias WEBER's avatar
Tobias WEBER committed
353
354
355
356
357
}


void MolDynDlg::Load()
{
358
359
	if(!m_plot) return;

Tobias WEBER's avatar
Tobias WEBER committed
360
361
362
	try
	{
		QString dirLast = m_sett->value("dir", "").toString();
Tobias WEBER's avatar
Tobias WEBER committed
363
364
365
366
367
368
369
370
		auto filedlg = std::make_shared<MolDynFileDlg>(this, "Load File", dirLast, "Molecular Dynamics File (*)");
		if(!filedlg->exec())
			return;
		auto files = filedlg->selectedFiles();
		if(!files.size())
			return;
		
		QString filename = files[0];
Tobias WEBER's avatar
Tobias WEBER committed
371
		if(filename == "" || !QFile::exists(filename))
Tobias WEBER's avatar
Tobias WEBER committed
372
373
374
			return;
		m_sett->setValue("dir", QFileInfo(filename).path());

Tobias WEBER's avatar
Tobias WEBER committed
375
		New();
376

Tobias WEBER's avatar
Tobias WEBER committed
377

378
379
380
		std::shared_ptr<QProgressDialog> dlgProgress = std::make_shared<QProgressDialog>(
			"Loading \"" + QFileInfo(filename).fileName() + "\"...", "Cancel", 0, 1000, this);
		bool bCancelled = 0;
Tobias WEBER's avatar
Tobias WEBER committed
381
		auto progressHandler = [dlgProgress, &bCancelled](t_real percentage) -> bool
382
383
384
385
		{
			dlgProgress->setValue(int(percentage*10));
			bCancelled = dlgProgress->wasCanceled();
			return !bCancelled;
Tobias WEBER's avatar
Tobias WEBER committed
386
387
		};
		m_mol.SubscribeToLoadProgress(progressHandler);
388
389
		dlgProgress->setWindowModality(Qt::WindowModal);

Tobias WEBER's avatar
Tobias WEBER committed
390

Tobias WEBER's avatar
Tobias WEBER committed
391
		if(!m_mol.LoadFile(filename.toStdString(), filedlg->GetFrameSkip()))
Tobias WEBER's avatar
Tobias WEBER committed
392
		{
393
394
395
			// only show error if not explicitely cancelled
			if(!bCancelled)
				QMessageBox::critical(this, "Molecular Dynamics", "Error loading file.");
Tobias WEBER's avatar
Tobias WEBER committed
396
			return;
Tobias WEBER's avatar
Tobias WEBER committed
397
398
		}

Tobias WEBER's avatar
Tobias WEBER committed
399
400

		m_mol.UnsubscribeFromLoadProgress(&progressHandler);
Tobias WEBER's avatar
Tobias WEBER committed
401
		m_slider->setMaximum(m_mol.GetFrameCount() - 1);
Tobias WEBER's avatar
Tobias WEBER committed
402

Tobias WEBER's avatar
Tobias WEBER committed
403

Tobias WEBER's avatar
Tobias WEBER committed
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
		// crystal A and B matrices
		const t_vec& _a = m_mol.GetBaseA();
		const t_vec& _b = m_mol.GetBaseB();
		const t_vec& _c = m_mol.GetBaseC();

		m_crystA = m::create<t_mat>({
			_a[0],	_b[0],	_c[0],
			_a[1],	_b[1],	_c[1],
			_a[2], 	_b[2],	_c[2] });

		bool ok = true;
		std::tie(m_crystB, ok) = m::inv(m_crystA);
		if(!ok)
		{
			m_crystB = m::unit<t_mat>();
			QMessageBox::critical(this, "Molecular Dynamics", "Error: Cannot invert A matrix.");
		}

		m_crystB /= t_real_gl(2)*m::pi<t_real_gl>;
		t_mat_gl matA{m_crystA};
		m_plot->GetImpl()->SetBTrafo(m_crystB, &matA);

		std::cout << "A matrix: " << m_crystA << ", \n"
			<< "B matrix: " << m_crystB << "." << std::endl;


Tobias WEBER's avatar
Tobias WEBER committed
430
		// atom colors
Tobias WEBER's avatar
Tobias WEBER committed
431
432
433
434
435
436
437
438
439
440
		std::vector<t_vec> cols =
		{
			m::create<t_vec>({1, 0, 0}),
			m::create<t_vec>({0, 0, 1}),
			m::create<t_vec>({0, 0.5, 0}),
			m::create<t_vec>({0, 0.5, 0.5}),
			m::create<t_vec>({0.5, 0.5, 0}),
			m::create<t_vec>({0, 0, 0}),
		};

Tobias WEBER's avatar
Tobias WEBER committed
441
		// add atoms to 3d view
Tobias WEBER's avatar
Tobias WEBER committed
442
443
444
		if(m_mol.GetFrameCount())
		{
			const auto& frame = m_mol.GetFrame(0);
445
			m_sphereHandles.reserve(frame.GetNumAtoms());
Tobias WEBER's avatar
Tobias WEBER committed
446
447
448
449
450

			for(std::size_t atomidx=0; atomidx<frame.GetNumAtoms(); ++atomidx)
			{
				const auto& coords = frame.GetCoords(atomidx);
				for(const t_vec& vec : coords)
451
				{
Tobias WEBER's avatar
Tobias WEBER committed
452
453
					t_real atomscale = m_spinScale->value();
					std::size_t handle = Add3DItem(vec, cols[atomidx % cols.size()], atomscale, m_mol.GetAtomName(atomidx));
454
455
					m_sphereHandles.push_back(handle);
				}
Tobias WEBER's avatar
Tobias WEBER committed
456
457
458
459
460
461
462
463
			}
		}
	}
	catch(const std::exception& ex)
	{
		QMessageBox::critical(this, "Molecular Dynamics", ex.what());
	}

Tobias WEBER's avatar
Tobias WEBER committed
464
	m_plot->update();
Tobias WEBER's avatar
Tobias WEBER committed
465
466
467
}


Tobias WEBER's avatar
Tobias WEBER committed
468
void MolDynDlg::SaveAs()
Tobias WEBER's avatar
Tobias WEBER committed
469
{
Tobias WEBER's avatar
Tobias WEBER committed
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
	try
	{
		QString dirLast = m_sett->value("dir", "").toString();
		QString filename = QFileDialog::getSaveFileName(this, "Save File", dirLast, "Molecular Dynamics File (*)");
		if(filename == "")
			return;
		m_sett->setValue("dir", QFileInfo(filename).path());


		std::shared_ptr<QProgressDialog> dlgProgress = std::make_shared<QProgressDialog>(
			"Saving \"" + QFileInfo(filename).fileName() + "\"...", "Cancel", 0, 1000, this);
		bool bCancelled = 0;
		auto progressHandler = [dlgProgress, &bCancelled](t_real percentage) -> bool
		{
			dlgProgress->setValue(int(percentage*10));
			bCancelled = dlgProgress->wasCanceled();
			return !bCancelled;
		};
		m_mol.SubscribeToSaveProgress(progressHandler);
		dlgProgress->setWindowModality(Qt::WindowModal);


		if(!m_mol.SaveFile(filename.toStdString()))
		{
			QMessageBox::critical(this, "Molecular Dynamics", "Error saving file.");
		}


		m_mol.UnsubscribeFromSaveProgress(&progressHandler);
	}
	catch(const std::exception& ex)
	{
		QMessageBox::critical(this, "Molecular Dynamics", ex.what());
	}
Tobias WEBER's avatar
Tobias WEBER committed
504
505
506
507
508
509
510
511
512
513
514
}
// ----------------------------------------------------------------------------



// ----------------------------------------------------------------------------
/**
 * mouse hovers over 3d object
 */
void MolDynDlg::PickerIntersection(const t_vec3_gl* pos, std::size_t objIdx, const t_vec3_gl* posSphere)
{
515
516
	if(!m_plot) return;

Tobias WEBER's avatar
Tobias WEBER committed
517
518
519
520
521
522
523
524
	if(pos)
		m_curPickedObj = long(objIdx);
	else
		m_curPickedObj = -1;


	if(m_curPickedObj > 0)
	{
525
526
		const std::string& label = m_plot->GetImpl()->GetObjectDataString(m_curPickedObj);
		SetStatusMsg(label);
Tobias WEBER's avatar
Tobias WEBER committed
527
528
529
	}
	else
	{
Tobias WEBER's avatar
Tobias WEBER committed
530
		SetStatusMsg("");
Tobias WEBER's avatar
Tobias WEBER committed
531
532
533
534
535
536
537
538
	}
}



/**
 * set status label text in 3d dialog
 */
Tobias WEBER's avatar
Tobias WEBER committed
539
void MolDynDlg::SetStatusMsg(const std::string& msg)
Tobias WEBER's avatar
Tobias WEBER committed
540
541
542
543
544
545
546
547
548
549
550
551
552
{
	if(!m_status) return;

	m_status->showMessage(msg.c_str());
}



/**
 * mouse button pressed
 */
void MolDynDlg::PlotMouseDown(bool left, bool mid, bool right)
{
Tobias WEBER's avatar
Tobias WEBER committed
553
554
	if(!m_plot) return;

Tobias WEBER's avatar
Tobias WEBER committed
555
556
	if(left && m_curPickedObj > 0)
	{
Tobias WEBER's avatar
Tobias WEBER committed
557
		m_plot->GetImpl()->SetObjectHighlight(m_curPickedObj, !m_plot->GetImpl()->GetObjectHighlight(m_curPickedObj));
Tobias WEBER's avatar
Tobias WEBER committed
558
		m_plot->update();
Tobias WEBER's avatar
Tobias WEBER committed
559
560
561
562
563
564
565
566
567
568
	}
}


/**
 * mouse button released
 */
void MolDynDlg::PlotMouseUp(bool left, bool mid, bool right)
{
}
Tobias WEBER's avatar
Tobias WEBER committed
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588


/**
 * mouse button clicked
 */
void MolDynDlg::PlotMouseClick(bool left, bool mid, bool right)
{
	// show context menu
	if(right && m_curPickedObj > 0)
	{
		QString atomLabel = m_plot->GetImpl()->GetObjectDataString(m_curPickedObj).c_str();
		m_atomContextMenu->actions()[0]->setText("Delete This \"" + atomLabel + "\" Atom");
		m_atomContextMenu->actions()[1]->setText("Delete All \"" + atomLabel + "\" Atoms");
		m_atomContextMenu->actions()[2]->setText("Delete All But \"" + atomLabel + "\" Atoms");

		auto ptGlob = QCursor::pos();
		ptGlob.setY(ptGlob.y() + 8);
		m_atomContextMenu->popup(ptGlob);
	}
}
Tobias WEBER's avatar
Tobias WEBER committed
589
590


Tobias WEBER's avatar
Tobias WEBER committed
591
592
593
// ----------------------------------------------------------------------------


Tobias WEBER's avatar
Tobias WEBER committed
594
595
596
597
598
599
600
601
602
603
604
605
606
/**
 * unselect all atoms
 */
void MolDynDlg::SelectNone()
{
	if(!m_plot) return;

	for(auto handle : m_sphereHandles)
		m_plot->GetImpl()->SetObjectHighlight(handle, 0);

	m_plot->update();
}

Tobias WEBER's avatar
Tobias WEBER committed
607

Tobias WEBER's avatar
Tobias WEBER committed
608
609
void MolDynDlg::SliderValueChanged(int val)
{
610
611
612
613
614
615
616
617
	if(!m_plot) return;

	if(val < 0 || val >= m_mol.GetFrameCount())
		return;


	// update atom position with selected frame
	const auto& frame = m_mol.GetFrame(val);
Tobias WEBER's avatar
Tobias WEBER committed
618
	t_real atomscale = m_spinScale->value();
619
620
621
622
623
624
625
626

	std::size_t counter = 0;
	for(std::size_t atomidx=0; atomidx<frame.GetNumAtoms(); ++atomidx)
	{
		const auto& coords = frame.GetCoords(atomidx);
		for(const t_vec& vec : coords)
		{
			std::size_t obj = m_sphereHandles[counter];
Tobias WEBER's avatar
Tobias WEBER committed
627
			Change3DItem(obj, &vec, nullptr, &atomscale);
628
629
630
631
632
633

			++counter;
		}
	}

	m_plot->update();
Tobias WEBER's avatar
Tobias WEBER committed
634
635
636
}


Tobias WEBER's avatar
Tobias WEBER committed
637
638
639
// ----------------------------------------------------------------------------


Tobias WEBER's avatar
Tobias WEBER committed
640
/**
Tobias WEBER's avatar
Tobias WEBER committed
641
642
 * get the index of the atom in the m_mol data structure
 * from the handle of the displayed 3d object
Tobias WEBER's avatar
Tobias WEBER committed
643
 */
Tobias WEBER's avatar
Tobias WEBER committed
644
645
std::tuple<bool, std::size_t, std::size_t, std::size_t> 
MolDynDlg::GetAtomIndexFromHandle(std::size_t handle) const
Tobias WEBER's avatar
Tobias WEBER committed
646
{
Tobias WEBER's avatar
Tobias WEBER committed
647
	// find handle in sphere handle vector
Tobias WEBER's avatar
Tobias WEBER committed
648
	auto iter = std::find(m_sphereHandles.begin(), m_sphereHandles.end(), handle);
Tobias WEBER's avatar
Tobias WEBER committed
649
	if(iter == m_sphereHandles.end())
Tobias WEBER's avatar
Tobias WEBER committed
650
		return std::make_tuple(0, 0, 0, 0);
Tobias WEBER's avatar
Tobias WEBER committed
651
652

	std::size_t sphereIdx = iter - m_sphereHandles.begin();
Tobias WEBER's avatar
Tobias WEBER committed
653

Tobias WEBER's avatar
Tobias WEBER committed
654
655
656
657
658
659
660
661
662
663
664
	std::size_t atomCountsSoFar = 0;
	std::size_t atomTypeIdx = 0;
	for(atomTypeIdx=0; atomTypeIdx<m_mol.GetAtomCount(); ++atomTypeIdx)
	{
		std::size_t numAtoms = m_mol.GetAtomNum(atomTypeIdx);
		if(atomCountsSoFar + numAtoms > sphereIdx)
			break;

		atomCountsSoFar += numAtoms;
	}

Tobias WEBER's avatar
Tobias WEBER committed
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
	std::size_t atomSubTypeIdx = sphereIdx-atomCountsSoFar;

	return std::make_tuple(1, atomTypeIdx, atomSubTypeIdx, sphereIdx);
}


/**
 * delete one atom
 */
void MolDynDlg::DeleteAtomUnderCursor()
{
	// nothing under cursor
	if(m_curPickedObj <= 0)
		return;

	// atom type to be deleted
	const std::string& atomLabel = m_plot->GetImpl()->GetObjectDataString(m_curPickedObj);

	const auto [bOk, atomTypeIdx, atomSubTypeIdx, sphereIdx] = GetAtomIndexFromHandle(m_curPickedObj);
	if(!bOk)
	{
		QMessageBox::critical(this, "Molecular Dynamics", "Atom handle not found, data is corrupted.");
		return;
	}

Tobias WEBER's avatar
Tobias WEBER committed
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
	if(m_mol.GetAtomName(atomTypeIdx) != atomLabel)
	{
		QMessageBox::critical(this, "Molecular Dynamics", "Mismatch in atom type, data is corrupted.");
		return;
	}

	// remove 3d objects
	m_plot->GetImpl()->RemoveObject(m_sphereHandles[sphereIdx]);
	m_sphereHandles.erase(m_sphereHandles.begin()+sphereIdx);

	// remove atom
	m_mol.RemoveAtom(atomTypeIdx, atomSubTypeIdx);

	SetStatusMsg("1 atom removed.");
	m_plot->update();
Tobias WEBER's avatar
Tobias WEBER committed
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
}


/**
 * delete all atoms of the type under the cursor
 */
void MolDynDlg::DeleteAllAtomsOfSameType()
{
	// nothing under cursor
	if(m_curPickedObj <= 0)
		return;

	// atom type to be deleted
	const std::string& atomLabel = m_plot->GetImpl()->GetObjectDataString(m_curPickedObj);

	std::size_t startIdx = 0;
	std::size_t totalRemoved = 0;
	for(std::size_t atomIdx=0; atomIdx<m_mol.GetAtomCount();)
	{
		std::size_t numAtoms = m_mol.GetAtomNum(atomIdx);

		if(m_mol.GetAtomName(atomIdx) == atomLabel)
		{
			// remove 3d objects
			for(std::size_t sphereIdx=startIdx; sphereIdx<startIdx+numAtoms; ++sphereIdx)
Tobias WEBER's avatar
Tobias WEBER committed
730
				m_plot->GetImpl()->RemoveObject(m_sphereHandles[sphereIdx]);
Tobias WEBER's avatar
Tobias WEBER committed
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
			m_sphereHandles.erase(m_sphereHandles.begin()+startIdx, m_sphereHandles.begin()+startIdx+numAtoms);

			// remove atoms
			m_mol.RemoveAtoms(atomIdx);

			totalRemoved += numAtoms;
		}
		else
		{
			startIdx += numAtoms;
			++atomIdx;
		}
	}

	SetStatusMsg(std::to_string(totalRemoved) + " atoms removed.");
	m_plot->update();
}


/**
 * delete all atoms NOT of the type under the cursor
 */
void MolDynDlg::KeepAtomsOfSameType()
{
	// nothing under cursor
	if(m_curPickedObj <= 0)
		return;

	// atom type to be deleted
	const std::string& atomLabel = m_plot->GetImpl()->GetObjectDataString(m_curPickedObj);

	std::size_t startIdx = 0;
	std::size_t totalRemoved = 0;
	for(std::size_t atomIdx=0; atomIdx<m_mol.GetAtomCount();)
	{
		std::size_t numAtoms = m_mol.GetAtomNum(atomIdx);

		if(m_mol.GetAtomName(atomIdx) != atomLabel)
		{
			// remove 3d objects
			for(std::size_t sphereIdx=startIdx; sphereIdx<startIdx+numAtoms; ++sphereIdx)
Tobias WEBER's avatar
Tobias WEBER committed
772
				m_plot->GetImpl()->RemoveObject(m_sphereHandles[sphereIdx]);
Tobias WEBER's avatar
Tobias WEBER committed
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
			m_sphereHandles.erase(m_sphereHandles.begin()+startIdx, m_sphereHandles.begin()+startIdx+numAtoms);

			// remove atoms
			m_mol.RemoveAtoms(atomIdx);

			totalRemoved += numAtoms;
		}
		else
		{
			startIdx += numAtoms;
			++atomIdx;
		}
	}

	SetStatusMsg(std::to_string(totalRemoved) + " atoms removed.");
	m_plot->update();
}



Tobias WEBER's avatar
Tobias WEBER committed
793
794
795
796
797
798
799
800
801
// ----------------------------------------------------------------------------
void MolDynDlg::AfterGLInitialisation()
{
	if(!m_plot) return;

	// reference sphere for linked objects
	m_sphere = m_plot->GetImpl()->AddSphere(0.05, 0.,0.,0., 1.,1.,1.,1.);
	m_plot->GetImpl()->SetObjectVisible(m_sphere, false);

Tobias WEBER's avatar
Tobias WEBER committed
802
803
804
	// B matrix
	m_plot->GetImpl()->SetBTrafo(m_crystB);

Tobias WEBER's avatar
Tobias WEBER committed
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
	// GL device info
	auto [strGlVer, strGlShaderVer, strGlVendor, strGlRenderer]
		= m_plot->GetImpl()->GetGlDescr();
	std::cout << "GL Version: " << strGlVer << ", Shader Version: " << strGlShaderVer << "." << std::endl;
	std::cout << "GL Device: " << strGlRenderer << ", " << strGlVendor << "." << std::endl;
}


void MolDynDlg::closeEvent(QCloseEvent *evt)
{
	if(m_sett)
	{
		m_sett->setValue("geo", saveGeometry());
	}
}
Tobias WEBER's avatar
Tobias WEBER committed
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838


void MolDynDlg::keyPressEvent(QKeyEvent *evt)
{
	if(evt->key()==Qt::Key_Left || evt->key()==Qt::Key_Down)
		m_slider->setValue(m_slider->value() - m_slider->singleStep());
	else if(evt->key()==Qt::Key_Right || evt->key()==Qt::Key_Up)
		m_slider->setValue(m_slider->value() + m_slider->singleStep());
	else if(evt->key()==Qt::Key_PageUp)
		m_slider->setValue(m_slider->value() + m_slider->pageStep());
	else if(evt->key()==Qt::Key_PageDown)
		m_slider->setValue(m_slider->value() - m_slider->pageStep());
	else if(evt->key()==Qt::Key_Home)
		m_slider->setValue(m_slider->minimum());
	else if(evt->key()==Qt::Key_End)
		m_slider->setValue(m_slider->maximum());

	QMainWindow::keyPressEvent(evt);
}
Tobias WEBER's avatar
Tobias WEBER committed
839
840
841
842
843
844
845
// ----------------------------------------------------------------------------





// ----------------------------------------------------------------------------
846
847
848

int main(int argc, char** argv)
{
Tobias WEBER's avatar
Tobias WEBER committed
849
850
	set_gl_format(1, _GL_MAJ_VER, _GL_MIN_VER, 8);
	tl2::set_locales();
851

Tobias WEBER's avatar
Tobias WEBER committed
852
853
854
	auto app = std::make_unique<QApplication>(argc, argv);
	auto dlg = std::make_unique<MolDynDlg>(nullptr);
	dlg->show();
855

Tobias WEBER's avatar
Tobias WEBER committed
856
	return app->exec();
857
}
Tobias WEBER's avatar
Tobias WEBER committed
858
// ----------------------------------------------------------------------------