moldyn.cpp 38.7 KB
Newer Older
1
/**
Tobias WEBER's avatar
Tobias WEBER committed
2
 * atom dynamics tool
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
#include <QtWidgets/QSpinBox>
Tobias WEBER's avatar
Tobias WEBER committed
14
#include <QtWidgets/QComboBox>
Tobias WEBER's avatar
Tobias WEBER committed
15
#include <QtWidgets/QMessageBox>
16
#include <QtWidgets/QProgressDialog>
17

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

22
23
#include "tlibs2/libs/algos.h"
#include "tlibs2/libs/helper.h"
Tobias WEBER's avatar
Tobias WEBER committed
24

Tobias WEBER's avatar
Tobias WEBER committed
25
using namespace tl2_ops;
Tobias WEBER's avatar
Tobias WEBER committed
26
27
28
29
30

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


Tobias WEBER's avatar
Tobias WEBER committed
31
#define PROG_NAME "Molecular Dynamics Tool"
32
#define PROG_VER "0.2"
Tobias WEBER's avatar
Tobias WEBER committed
33

34
35
#define DATA_SEP "#|#"	// data separation string

Tobias WEBER's avatar
Tobias WEBER committed
36

Tobias WEBER's avatar
Tobias WEBER committed
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

// ----------------------------------------------------------------------------
/**
 * 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
52
53
			m_spinFrameSkip->setSingleStep(1);
			m_spinFrameSkip->setRange(0, 9999999);
Tobias WEBER's avatar
Tobias WEBER committed
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

			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
83
// ----------------------------------------------------------------------------
Tobias WEBER's avatar
Tobias WEBER committed
84
85
86



Tobias WEBER's avatar
Tobias WEBER committed
87
88
// ----------------------------------------------------------------------------
MolDynDlg::MolDynDlg(QWidget* pParent) : QMainWindow{pParent},
Tobias WEBER's avatar
Tobias WEBER committed
89
	m_sett{new QSettings{"takin", "moldyn"}}
Tobias WEBER's avatar
Tobias WEBER committed
90
{
91
	setWindowTitle(QString(PROG_NAME) + QString(" Version ") + QString(PROG_VER));
Tobias WEBER's avatar
Tobias WEBER committed
92
93
94
	this->setObjectName("moldyn");

	m_status = new QStatusBar(this);
Tobias WEBER's avatar
Tobias WEBER committed
95
	m_statusCurAtom = new QLabel(m_status);
Tobias WEBER's avatar
Tobias WEBER committed
96
	m_statusAtoms = new QLabel(m_status);
Tobias WEBER's avatar
Tobias WEBER committed
97
	m_status->addWidget(m_statusCurAtom);
Tobias WEBER's avatar
Tobias WEBER committed
98
	m_status->addPermanentWidget(m_statusAtoms);
Tobias WEBER's avatar
Tobias WEBER committed
99
100
101
	this->setStatusBar(m_status);


Tobias WEBER's avatar
Tobias WEBER committed
102
103
104
105
106
107
108
	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
109
110
111
112
113
	// 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
114

Tobias WEBER's avatar
Tobias WEBER committed
115
		// File
Tobias WEBER's avatar
Tobias WEBER committed
116
117
118
119
		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
120
		auto acSaveAs = new QAction("Save As...", menuFile);
Tobias WEBER's avatar
Tobias WEBER committed
121
		auto acExit = new QAction("Quit", menuFile);
Tobias WEBER's avatar
Tobias WEBER committed
122
123
124
125

		menuFile->addAction(acNew);
		menuFile->addSeparator();
		menuFile->addAction(acLoad);
Tobias WEBER's avatar
Tobias WEBER committed
126
		menuFile->addAction(acSaveAs);
Tobias WEBER's avatar
Tobias WEBER committed
127
128
129
130
131
		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
132
		connect(acSaveAs, &QAction::triggered, this, &MolDynDlg::SaveAs);
Tobias WEBER's avatar
Tobias WEBER committed
133
134
		connect(acExit, &QAction::triggered, this, &QDialog::close);

Tobias WEBER's avatar
Tobias WEBER committed
135
136

		// Edit
Tobias WEBER's avatar
Tobias WEBER committed
137
		auto menuEdit = new QMenu("Edit", m_menu);
Tobias WEBER's avatar
Tobias WEBER committed
138
		auto acSelectAll = new QAction("Select All", menuEdit);
Tobias WEBER's avatar
Tobias WEBER committed
139
140
		auto acSelectNone = new QAction("Select None", menuEdit);

Tobias WEBER's avatar
Tobias WEBER committed
141
		menuEdit->addAction(acSelectAll);
Tobias WEBER's avatar
Tobias WEBER committed
142
143
		menuEdit->addAction(acSelectNone);

Tobias WEBER's avatar
Tobias WEBER committed
144
		connect(acSelectAll, &QAction::triggered, this, &MolDynDlg::SelectAll);
Tobias WEBER's avatar
Tobias WEBER committed
145
146
147
148
149
		connect(acSelectNone, &QAction::triggered, this, &MolDynDlg::SelectNone);


		// Calculate
		auto menuCalc = new QMenu("Calculate", m_menu);
Tobias WEBER's avatar
Tobias WEBER committed
150
151
152
		auto acCalcDist = new QAction("Distance Between Selected Atoms...", menuEdit);
		auto acCalcPos = new QAction("Positions Of Selected Atoms...", menuEdit);
		auto acCalcDeltaDist = new QAction("Distances to Initial Position of Selected Atoms...", menuEdit);
Tobias WEBER's avatar
Tobias WEBER committed
153
		auto acCalcHull = new QAction("Convex Hull of Selected Atoms", menuEdit);
Tobias WEBER's avatar
Tobias WEBER committed
154
155

		menuCalc->addAction(acCalcDist);
Tobias WEBER's avatar
Tobias WEBER committed
156
157
		menuCalc->addAction(acCalcPos);
		menuCalc->addAction(acCalcDeltaDist);
158
#ifdef USE_QHULL
Tobias WEBER's avatar
Tobias WEBER committed
159
160
		menuCalc->addSeparator();
		menuCalc->addAction(acCalcHull);
161
#endif
Tobias WEBER's avatar
Tobias WEBER committed
162
163

		connect(acCalcDist, &QAction::triggered, this, &MolDynDlg::CalculateDistanceBetweenAtoms);
Tobias WEBER's avatar
Tobias WEBER committed
164
165
		connect(acCalcPos, &QAction::triggered, this, &MolDynDlg::CalculatePositionsOfAtoms);
		connect(acCalcDeltaDist, &QAction::triggered, this, &MolDynDlg::CalculateDeltaDistancesOfAtoms);
Tobias WEBER's avatar
Tobias WEBER committed
166
		connect(acCalcHull, &QAction::triggered, this, &MolDynDlg::CalculateConvexHullOfAtoms);
Tobias WEBER's avatar
Tobias WEBER committed
167
168
169
170
171
172
173
174
175
176
177
178


		// Help
		auto menuHelp = new QMenu("Help", m_menu);
		auto acHelpInfo = new QAction("Infos...", menuHelp);

		menuHelp->addAction(acHelpInfo);

		connect(acHelpInfo, &QAction::triggered, this, [this]()
		{
			QString strHelp;

179
			strHelp = QString{PROG_NAME} + " v" + QString{PROG_VER} + ", part of the Takin 2 package.\n";
Tobias WEBER's avatar
Tobias WEBER committed
180
181
182
183
184
185
186
187
188
189
190
191
192
193
			strHelp += "Written by Tobias Weber <tweber@ill.fr>\nin December 2019.\n\n";

			strHelp += "This program is free software: you can redistribute it and/or modify "
				"it under the terms of the GNU General Public License as published by "
				"the Free Software Foundation, version 3 of the License.\n\n"
				"This program is distributed in the hope that it will be useful, "
				"but WITHOUT ANY WARRANTY; without even the implied warranty of "
				"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "
				"GNU General Public License for more details.\n\n"
				"You should have received a copy of the GNU General Public License "
				"along with this program.  If not, see <http://www.gnu.org/licenses/>.";

			QMessageBox::information(this, PROG_NAME, strHelp);
		});
Tobias WEBER's avatar
Tobias WEBER committed
194
195


Tobias WEBER's avatar
Tobias WEBER committed
196
		m_menu->addMenu(menuFile);
Tobias WEBER's avatar
Tobias WEBER committed
197
198
		m_menu->addMenu(menuEdit);
		m_menu->addMenu(menuCalc);
Tobias WEBER's avatar
Tobias WEBER committed
199
		m_menu->addMenu(menuHelp);
Tobias WEBER's avatar
Tobias WEBER committed
200
201
202
203
		this->setMenuBar(m_menu);
	}


Tobias WEBER's avatar
Tobias WEBER committed
204
205
206
207
208
	// context menus
	{
		m_atomContextMenu = new QMenu(this);
		m_atomContextMenu->setTitle("Atoms");
		m_atomContextMenu->addAction("Delete Atom", this, &MolDynDlg::DeleteAtomUnderCursor);
209
210
211
212
        m_atomContextMenu->addSeparator();
        m_atomContextMenu->addAction("Delete Selected Atoms", this, &MolDynDlg::DeleteSelectedAtoms);
        m_atomContextMenu->addAction("Delete All But Selected Atoms", this, &MolDynDlg::OnlyKeepSelectedAtoms);
        m_atomContextMenu->addSeparator();
Tobias WEBER's avatar
Tobias WEBER committed
213
214
		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
215
216
		m_atomContextMenu->addSeparator();
		m_atomContextMenu->addAction("Select All Atoms of Same Type", this, &MolDynDlg::SelectAtomsOfSameType);
Tobias WEBER's avatar
Tobias WEBER committed
217
218
219
	}


Tobias WEBER's avatar
Tobias WEBER committed
220
221
	// plot widget
	{
Tobias WEBER's avatar
Tobias WEBER committed
222
		m_plot = new tl2::GlPlot(this);
Tobias WEBER's avatar
Tobias WEBER committed
223
224
		m_plot->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Expanding});

Tobias WEBER's avatar
Tobias WEBER committed
225
226
227
228
229
		m_plot->GetRenderer()->EnablePicker(1);
		m_plot->GetRenderer()->SetLight(0, tl2::create<t_vec3_gl>({ 5, 5, 5 }));
		m_plot->GetRenderer()->SetLight(1, tl2::create<t_vec3_gl>({ -5, -5, -5 }));
		m_plot->GetRenderer()->SetCoordMax(1.);
		m_plot->GetRenderer()->SetCamBase(tl2::create<t_mat_gl>({1,0,0,0,  0,0,1,0,  0,-1,0,-1.5,  0,0,0,1}),
Tobias WEBER's avatar
Tobias WEBER committed
230
			tl2::create<t_vec_gl>({1,0,0,0}), tl2::create<t_vec_gl>({0,0,1,0}));
Tobias WEBER's avatar
Tobias WEBER committed
231

Tobias WEBER's avatar
Tobias WEBER committed
232
233
234
235
236
237
		connect(m_plot, &tl2::GlPlot::AfterGLInitialisation, this, &MolDynDlg::AfterGLInitialisation);
		connect(m_plot, &tl2::GlPlot::GLInitialisationFailed, this, &MolDynDlg::GLInitialisationFailed);
		connect(m_plot->GetRenderer(), &tl2::GlPlotRenderer::PickerIntersection, this, &MolDynDlg::PickerIntersection);
		connect(m_plot, &tl2::GlPlot::MouseDown, this, &MolDynDlg::PlotMouseDown);
		connect(m_plot, &tl2::GlPlot::MouseUp, this, &MolDynDlg::PlotMouseUp);
		connect(m_plot, &tl2::GlPlot::MouseClick, this, &MolDynDlg::PlotMouseClick);
Tobias WEBER's avatar
Tobias WEBER committed
238

Tobias WEBER's avatar
Tobias WEBER committed
239
		//this->setCentralWidget(m_plot);
Tobias WEBER's avatar
Tobias WEBER committed
240
		pMainGrid->addWidget(m_plot, 0,0,1,9);
Tobias WEBER's avatar
Tobias WEBER committed
241
242
243
244
245
	}


	// controls
	{
Tobias WEBER's avatar
Tobias WEBER committed
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
		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
265
266
267
268
269
270
271
		m_sliderFrame = new QSlider(Qt::Horizontal, this);
		m_sliderFrame->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Minimum});
		m_sliderFrame->setMinimum(0);
		m_sliderFrame->setSingleStep(1);
		m_sliderFrame->setPageStep(10);
		m_sliderFrame->setTracking(1);
		m_sliderFrame->setFocusPolicy(Qt::StrongFocus);
Tobias WEBER's avatar
Tobias WEBER committed
272

Tobias WEBER's avatar
Tobias WEBER committed
273
		connect(m_sliderFrame, &QSlider::valueChanged, this, &MolDynDlg::SliderValueChanged);
Tobias WEBER's avatar
Tobias WEBER committed
274
275
276
		connect(comboCoordSys, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this](int val)
		{
			if(this->m_plot)
Tobias WEBER's avatar
Tobias WEBER committed
277
				this->m_plot->GetRenderer()->SetCoordSys(val);
Tobias WEBER's avatar
Tobias WEBER committed
278
279
280
281
282
283
		});
		connect(m_spinScale, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, [this](double val)
		{
			if(!this->m_plot) return;

			// hack to trigger update
Tobias WEBER's avatar
Tobias WEBER committed
284
			SliderValueChanged(m_sliderFrame->value());
Tobias WEBER's avatar
Tobias WEBER committed
285
286
287
288
289
290
291
		});

		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);
Tobias WEBER's avatar
Tobias WEBER committed
292
		pMainGrid->addWidget(m_sliderFrame, 1,5,1,4);
Tobias WEBER's avatar
Tobias WEBER committed
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
	}


	// 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
309
 * add an atom
Tobias WEBER's avatar
Tobias WEBER committed
310
 */
Tobias WEBER's avatar
Tobias WEBER committed
311
std::size_t MolDynDlg::Add3DAtom(const t_vec& vec, const t_vec& col, t_real scale, const std::string& typelabel, int atomindex)
Tobias WEBER's avatar
Tobias WEBER committed
312
{
Tobias WEBER's avatar
Tobias WEBER committed
313
	auto obj = m_plot->GetRenderer()->AddLinkedObject(m_sphere, 0,0,0, col[0],col[1],col[2],1);
Tobias WEBER's avatar
Tobias WEBER committed
314
	Change3DAtom(obj, &vec, &col, &scale, &typelabel, atomindex);
315
	return obj;
Tobias WEBER's avatar
Tobias WEBER committed
316
}
Tobias WEBER's avatar
Tobias WEBER committed
317
318
319
320
321


/**
 * change an atom
 */
Tobias WEBER's avatar
Tobias WEBER committed
322
void MolDynDlg::Change3DAtom(std::size_t obj, const t_vec *vec, const t_vec *col, const t_real *scale,
323
	const std::string *label, int atomindex)
Tobias WEBER's avatar
Tobias WEBER committed
324
{
Tobias WEBER's avatar
Tobias WEBER committed
325
326
	if(vec)
	{
Tobias WEBER's avatar
Tobias WEBER committed
327
328
329
330
		t_mat_gl mat = tl2::hom_translation<t_mat_gl>(
			t_real_gl((*vec)[0]), t_real_gl((*vec)[1]), t_real_gl((*vec)[2]));
		if(scale) mat *= tl2::hom_scaling<t_mat_gl>(
			t_real_gl(*scale), t_real_gl(*scale), t_real_gl(*scale));
Tobias WEBER's avatar
Tobias WEBER committed
331
		m_plot->GetRenderer()->SetObjectMatrix(obj, mat);
Tobias WEBER's avatar
Tobias WEBER committed
332
	}
Tobias WEBER's avatar
Tobias WEBER committed
333

Tobias WEBER's avatar
Tobias WEBER committed
334
	if(col) m_plot->GetRenderer()->SetObjectCol(obj, (*col)[0], (*col)[1], (*col)[2], 1);
335
336
337
338
339
	if(label)
	{
		std::ostringstream ostrData;
		ostrData << *label << DATA_SEP << atomindex;

Tobias WEBER's avatar
Tobias WEBER committed
340
341
		m_plot->GetRenderer()->SetObjectLabel(obj, *label);
		m_plot->GetRenderer()->SetObjectDataString(obj, ostrData.str());
342
	}
Tobias WEBER's avatar
Tobias WEBER committed
343
}
Tobias WEBER's avatar
Tobias WEBER committed
344
345
346
347
// ----------------------------------------------------------------------------



Tobias WEBER's avatar
Tobias WEBER committed
348
// ----------------------------------------------------------------------------
Tobias WEBER's avatar
Tobias WEBER committed
349
350
351
352
353
354
355
356
357

std::vector<std::tuple<std::size_t, std::size_t>> MolDynDlg::GetSelectedAtoms()
{
	// get selected atoms
	std::vector<std::tuple<std::size_t, std::size_t>> objs;

	for(const auto& obj : m_sphereHandles)
	{
		// continue if object isn't selected
Tobias WEBER's avatar
Tobias WEBER committed
358
		if(!m_plot->GetRenderer()->GetObjectHighlight(obj))
Tobias WEBER's avatar
Tobias WEBER committed
359
360
			continue;

Tobias WEBER's avatar
Tobias WEBER committed
361
		//const auto [typelabel, _atomSubTypeIdx] = SplitDataString(m_plot->GetRenderer()->GetObjectDataString(obj));
Tobias WEBER's avatar
Tobias WEBER committed
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377

		// get indices for selected atoms
		const auto [bOk, atomTypeIdx, atomSubTypeIdx, sphereIdx] = GetAtomIndexFromHandle(obj);
		if(!bOk /*|| _atomSubTypeIdx != atomSubTypeIdx*/)
		{
			QMessageBox::critical(this, PROG_NAME, "Atom handle not found, data is corrupted.");
			return std::vector<std::tuple<std::size_t, std::size_t>>{};
		}

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

	return objs;
}


Tobias WEBER's avatar
Tobias WEBER committed
378
379
380
381
382
/**
 * calculate the distance between selected atoms
 */
void MolDynDlg::CalculateDistanceBetweenAtoms()
{
Tobias WEBER's avatar
Tobias WEBER committed
383
	try
Tobias WEBER's avatar
Tobias WEBER committed
384
	{
Tobias WEBER's avatar
Tobias WEBER committed
385
		// get selected atoms
Tobias WEBER's avatar
Tobias WEBER committed
386
		std::vector<std::tuple<std::size_t, std::size_t>> objs = GetSelectedAtoms();
Tobias WEBER's avatar
Tobias WEBER committed
387

Tobias WEBER's avatar
Tobias WEBER committed
388
		if(objs.size() <= 1)
Tobias WEBER's avatar
Tobias WEBER committed
389
		{
Tobias WEBER's avatar
Tobias WEBER committed
390
			QMessageBox::critical(this, PROG_NAME, "At least two atoms have to be selected.");
Tobias WEBER's avatar
Tobias WEBER committed
391
392
393
394
			return;
		}


Tobias WEBER's avatar
Tobias WEBER committed
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
		// create file
		QString dirLast = m_sett->value("dir", "").toString();
		QString filename = QFileDialog::getSaveFileName(this, "Save File", dirLast, "Data File (*.dat)");
		if(filename == "")
			return;

		std::ofstream ofstr(filename.toStdString());
		if(!ofstr)
		{
			QMessageBox::critical(this, PROG_NAME, "Cannot open file.");
			return;
		}

		ofstr.precision(g_prec);
		m_sett->setValue("dir", QFileInfo(filename).path());


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


417
418
		// output data file header infos
		ofstr << "#\n";
Tobias WEBER's avatar
Tobias WEBER committed
419
420
		ofstr << "# Column 1: Frame\n";
		ofstr << "# Columns 2, 3, ...: Distances to first atom (A)\n";
421
422
423
424
425
426
427
428
429
430
431
		ofstr << "# Atoms: ";

		for(std::size_t objIdx=0; objIdx<objs.size(); ++objIdx)
		{
			auto [objTypeIdx, objSubTypeIdx] = objs[objIdx];
			ofstr << m_mol.GetAtomName(objTypeIdx) << "#" << (objSubTypeIdx+1);
			if(objIdx < objs.size()-1)
				ofstr << ", ";
		}
		ofstr << "\n#\n";

Tobias WEBER's avatar
Tobias WEBER committed
432
433
434
435
436
437
438
439
440
441
442

		// progress dialog
		std::shared_ptr<QProgressDialog> dlgProgress = std::make_shared<QProgressDialog>(
			"Calculating...", "Cancel", 1, objs.size(), this);
		dlgProgress->setWindowModality(Qt::WindowModal);

		// 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);
Tobias WEBER's avatar
Tobias WEBER committed
443

Tobias WEBER's avatar
Tobias WEBER committed
444
445
			for(std::size_t frameidx=0; frameidx<m_mol.GetFrameCount(); ++frameidx)
			{
Tobias WEBER's avatar
Tobias WEBER committed
446
				t_real dist = tl2::get_dist_uc(m_crystA, firstObjCoords[frameidx], objCoords[frameidx]);
Tobias WEBER's avatar
Tobias WEBER committed
447

Tobias WEBER's avatar
Tobias WEBER committed
448
				ofstr
Tobias WEBER's avatar
Tobias WEBER committed
449
450
451
452
453
454
455
456
457
458
459
460
461
					<< std::left << std::setw(g_prec*1.5) << frameidx << " "
					<< std::left << std::setw(g_prec*1.5) << dist << "\n";
			}

			dlgProgress->setValue(objIdx+1);
			if(dlgProgress->wasCanceled())
			{
				ofstr << "\n# WARNING: Calculation aborted by user.\n";
				break;
			}
		}
	}
	catch(const std::exception& ex)
Tobias WEBER's avatar
Tobias WEBER committed
462
	{
Tobias WEBER's avatar
Tobias WEBER committed
463
		QMessageBox::critical(this, PROG_NAME, ex.what());
Tobias WEBER's avatar
Tobias WEBER committed
464
	}
Tobias WEBER's avatar
Tobias WEBER committed
465
466
467
468
469
470
471
472
473
474
475
}


/**
 * calculate positions of selected atoms
 */
void MolDynDlg::CalculatePositionsOfAtoms()
{
	try
	{
		// get selected atoms
Tobias WEBER's avatar
Tobias WEBER committed
476
		std::vector<std::tuple<std::size_t, std::size_t>> objs = GetSelectedAtoms();
Tobias WEBER's avatar
Tobias WEBER committed
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499

		if(objs.size() <= 0)
		{
			QMessageBox::critical(this, PROG_NAME, "At least one atom has to be selected.");
			return;
		}


		// create file
		QString dirLast = m_sett->value("dir", "").toString();
		QString filename = QFileDialog::getSaveFileName(this, "Save File", dirLast, "Data File (*.dat)");
		if(filename == "")
			return;

		std::ofstream ofstr(filename.toStdString());
		if(!ofstr)
		{
			QMessageBox::critical(this, PROG_NAME, "Cannot open file.");
			return;
		}

		ofstr.precision(g_prec);
		m_sett->setValue("dir", QFileInfo(filename).path());
Tobias WEBER's avatar
Tobias WEBER committed
500
501


502
503
		// output data file header infos
		ofstr << "#\n";
Tobias WEBER's avatar
Tobias WEBER committed
504
505
		ofstr << "# Column 1: Frame\n";
		ofstr << "# Columns 2, 3, 4: x, y, z position of atom  (A)\n";
506
507
508
509
510
511
512
513
514
515
516
		ofstr << "# Atoms: ";

		for(std::size_t objIdx=0; objIdx<objs.size(); ++objIdx)
		{
			auto [objTypeIdx, objSubTypeIdx] = objs[objIdx];
			ofstr << m_mol.GetAtomName(objTypeIdx) << "#" << (objSubTypeIdx+1);
			if(objIdx < objs.size()-1)
				ofstr << ", ";
		}
		ofstr << "\n#\n";

Tobias WEBER's avatar
Tobias WEBER committed
517
518
519
520
521

		// progress dialog
		std::shared_ptr<QProgressDialog> dlgProgress = std::make_shared<QProgressDialog>(
			"Calculating...", "Cancel", 0, m_mol.GetFrameCount(), this);
		dlgProgress->setWindowModality(Qt::WindowModal);
Tobias WEBER's avatar
Tobias WEBER committed
522

Tobias WEBER's avatar
Tobias WEBER committed
523
524
525
526
		// iterate all selected atoms
		for(std::size_t frameidx=0; frameidx<m_mol.GetFrameCount(); ++frameidx)
		{
			ofstr << std::left << std::setw(g_prec*1.5) << frameidx << " ";
Tobias WEBER's avatar
Tobias WEBER committed
527

Tobias WEBER's avatar
Tobias WEBER committed
528
529
530
531
			for(std::size_t objIdx=0; objIdx<objs.size(); ++objIdx)
			{
				auto [objTypeIdx, objSubTypeIdx] = objs[objIdx];
				const t_vec& coords = m_mol.GetAtomCoords(objTypeIdx, objSubTypeIdx, frameidx);
Tobias WEBER's avatar
Tobias WEBER committed
532

533
				ofstr
Tobias WEBER's avatar
Tobias WEBER committed
534
535
536
537
					<< std::left << std::setw(g_prec*1.5) << coords[0] << " "
					<< std::left << std::setw(g_prec*1.5) << coords[1] << " "
					<< std::left << std::setw(g_prec*1.5) << coords[2] << "  ";
			}
Tobias WEBER's avatar
Tobias WEBER committed
538

Tobias WEBER's avatar
Tobias WEBER committed
539
			ofstr << "\n";
Tobias WEBER's avatar
Tobias WEBER committed
540

Tobias WEBER's avatar
Tobias WEBER committed
541
542
543
544
545
546
547
548
549
			dlgProgress->setValue(frameidx+1);
			if(dlgProgress->wasCanceled())
			{
				ofstr << "\n# WARNING: Calculation aborted by user.\n";
				break;
			}
		}
	}
	catch(const std::exception& ex)
Tobias WEBER's avatar
Tobias WEBER committed
550
	{
Tobias WEBER's avatar
Tobias WEBER committed
551
552
553
554
555
556
557
558
559
560
561
562
563
		QMessageBox::critical(this, PROG_NAME, ex.what());
	}
}


/**
 * calculate distance to initial positions of selected atoms
 */
void MolDynDlg::CalculateDeltaDistancesOfAtoms()
{
	try
	{
		// get selected atoms
Tobias WEBER's avatar
Tobias WEBER committed
564
		std::vector<std::tuple<std::size_t, std::size_t>> objs = GetSelectedAtoms();
Tobias WEBER's avatar
Tobias WEBER committed
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589

		if(objs.size() <= 0)
		{
			QMessageBox::critical(this, PROG_NAME, "At least one atom has to be selected.");
			return;
		}


		// create file
		QString dirLast = m_sett->value("dir", "").toString();
		QString filename = QFileDialog::getSaveFileName(this, "Save File", dirLast, "Data File (*.dat)");
		if(filename == "")
			return;

		std::ofstream ofstr(filename.toStdString());
		if(!ofstr)
		{
			QMessageBox::critical(this, PROG_NAME, "Cannot open file.");
			return;
		}

		ofstr.precision(g_prec);
		m_sett->setValue("dir", QFileInfo(filename).path());


590
591
		// output data file header infos
		ofstr << "#\n";
Tobias WEBER's avatar
Tobias WEBER committed
592
		ofstr << "# Column 1: Frame\n";
593
594
595
596
597
598
599
600
601
602
603
		ofstr << "# Column 2, 3, ...: Distance deltas (A)\n";
		ofstr << "# Atoms: ";

		for(std::size_t objIdx=0; objIdx<objs.size(); ++objIdx)
		{
			auto [objTypeIdx, objSubTypeIdx] = objs[objIdx];
			ofstr << m_mol.GetAtomName(objTypeIdx) << "#" << (objSubTypeIdx+1);
			if(objIdx < objs.size()-1)
				ofstr << ", ";
		}
		ofstr << "\n#\n";
Tobias WEBER's avatar
Tobias WEBER committed
604

Tobias WEBER's avatar
Tobias WEBER committed
605
606
607
608
609
610
611

		// progress dialog
		std::shared_ptr<QProgressDialog> dlgProgress = std::make_shared<QProgressDialog>(
			"Calculating...", "Cancel", 0, m_mol.GetFrameCount(), this);
		dlgProgress->setWindowModality(Qt::WindowModal);

		// iterate all selected atoms
Tobias WEBER's avatar
Tobias WEBER committed
612
613
		for(std::size_t frameidx=0; frameidx<m_mol.GetFrameCount(); ++frameidx)
		{
Tobias WEBER's avatar
Tobias WEBER committed
614
615
616
617
618
619
620
621
			ofstr << std::left << std::setw(g_prec*1.5) << frameidx << " ";

			for(std::size_t objIdx=0; objIdx<objs.size(); ++objIdx)
			{
				auto [objTypeIdx, objSubTypeIdx] = objs[objIdx];
			 	const t_vec& coords = m_mol.GetAtomCoords(objTypeIdx, objSubTypeIdx, frameidx);
				const t_vec& coordsInitial = m_mol.GetAtomCoords(objTypeIdx, objSubTypeIdx, 0);

Tobias WEBER's avatar
Tobias WEBER committed
622
				t_real dist = tl2::get_dist_uc(m_crystA, coords, coordsInitial);
Tobias WEBER's avatar
Tobias WEBER committed
623

Tobias WEBER's avatar
Tobias WEBER committed
624
625
626
627
628
629
630
631
632
633
634
				ofstr << std::left << std::setw(g_prec*1.5) << dist << " ";
			}

			ofstr << "\n";

			dlgProgress->setValue(frameidx+1);
			if(dlgProgress->wasCanceled())
			{
				ofstr << "\n# WARNING: Calculation aborted by user.\n";
				break;
			}
Tobias WEBER's avatar
Tobias WEBER committed
635
636
		}
	}
Tobias WEBER's avatar
Tobias WEBER committed
637
638
639
640
	catch(const std::exception& ex)
	{
		QMessageBox::critical(this, PROG_NAME, ex.what());
	}
Tobias WEBER's avatar
Tobias WEBER committed
641
}
Tobias WEBER's avatar
Tobias WEBER committed
642
643
644
645
646
647
648
649


/**
 * calculate the convex hull of selected atoms
 */
void MolDynDlg::CalculateConvexHullOfAtoms()
{
	// get selected atoms
650
	Hull hull;
Tobias WEBER's avatar
Tobias WEBER committed
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
	hull.vertices = GetSelectedAtoms();

	if(hull.vertices.size() <= 3)
	{
		QMessageBox::critical(this, PROG_NAME, "At least four atoms have to be selected.");
		return;
	}

	// mark indices for hull calculation
	m_hulls.emplace_back(std::move(hull));
	CalculateConvexHulls();
}


/**
 * calculate all convex hulls for the atom indices given in m_hulls
 */
void MolDynDlg::CalculateConvexHulls()
{
670
#ifdef USE_QHULL
Tobias WEBER's avatar
Tobias WEBER committed
671
672
	std::size_t frameidx = m_sliderFrame->value();

673
	for(auto& hull : m_hulls)
Tobias WEBER's avatar
Tobias WEBER committed
674
	{
675
676
677
		// remove old plot object
		if(hull.plotObj)
		{
Tobias WEBER's avatar
Tobias WEBER committed
678
			m_plot->GetRenderer()->RemoveObject(*hull.plotObj);
679
680
681
			hull.plotObj.reset();
		}

Tobias WEBER's avatar
Tobias WEBER committed
682
683
684
685
686
687
688
689
690
		std::vector<t_vec> vertices;

		for(const auto [objTypeIdx, objSubTypeIdx] : hull.vertices)
		{
			const t_vec& coords = m_mol.GetAtomCoords(objTypeIdx, objSubTypeIdx, frameidx);
			vertices.push_back(coords);
		}


Tobias WEBER's avatar
Tobias WEBER committed
691
		auto [polys, normals, dists] = tl2_qh::get_convexhull<t_vec>(vertices);
Tobias WEBER's avatar
Tobias WEBER committed
692
693
694
695
696
697


		std::vector<t_vec3_gl> glvertices;
		std::vector<t_vec3_gl> glnormals;

		for(std::size_t polyidx=0; polyidx<polys.size(); ++polyidx)
Tobias WEBER's avatar
Tobias WEBER committed
698
		{
Tobias WEBER's avatar
Tobias WEBER committed
699
700
701
			const auto& poly = polys[polyidx];
			const auto& normal = normals[polyidx];

Tobias WEBER's avatar
Tobias WEBER committed
702
			for(const auto& vert : poly)
Tobias WEBER's avatar
Tobias WEBER committed
703
704
705
				glvertices.emplace_back(tl2::convert<t_vec3_gl, t_vec>(vert));

			glnormals.emplace_back(tl2::convert<t_vec3_gl, t_vec>(normal));
Tobias WEBER's avatar
Tobias WEBER committed
706
707
		}

Tobias WEBER's avatar
Tobias WEBER committed
708
		hull.plotObj = m_plot->GetRenderer()->AddTriangleObject(glvertices, glnormals, 0,0,1,0.5);
Tobias WEBER's avatar
Tobias WEBER committed
709

Tobias WEBER's avatar
Tobias WEBER committed
710
711
		// TODO
	}
712
713
714
715

#else
	QMessageBox::critical(this, "Error", "Calculation of convex hull is disabled.");
#endif
Tobias WEBER's avatar
Tobias WEBER committed
716
}
Tobias WEBER's avatar
Tobias WEBER committed
717
718
719
// ----------------------------------------------------------------------------


Tobias WEBER's avatar
Tobias WEBER committed
720

Tobias WEBER's avatar
Tobias WEBER committed
721

Tobias WEBER's avatar
Tobias WEBER committed
722
723
724
// ----------------------------------------------------------------------------
void MolDynDlg::New()
{
Tobias WEBER's avatar
Tobias WEBER committed
725
	m_mol.Clear();
Tobias WEBER's avatar
Tobias WEBER committed
726

727
728
729
	for(auto& hull : m_hulls)
	{
		if(hull.plotObj)
Tobias WEBER's avatar
Tobias WEBER committed
730
			m_plot->GetRenderer()->RemoveObject(*hull.plotObj);
731
	}
Tobias WEBER's avatar
Tobias WEBER committed
732

733
	m_hulls.clear();
Tobias WEBER's avatar
Tobias WEBER committed
734
735

	for(const auto& obj : m_sphereHandles)
Tobias WEBER's avatar
Tobias WEBER committed
736
		m_plot->GetRenderer()->RemoveObject(obj);
Tobias WEBER's avatar
Tobias WEBER committed
737

738
	m_sphereHandles.clear();
Tobias WEBER's avatar
Tobias WEBER committed
739
	m_sliderFrame->setValue(0);
Tobias WEBER's avatar
Tobias WEBER committed
740
741

	m_plot->update();
Tobias WEBER's avatar
Tobias WEBER committed
742
743
744
745
746
}


void MolDynDlg::Load()
{
747
748
	if(!m_plot) return;

Tobias WEBER's avatar
Tobias WEBER committed
749
750
751
	try
	{
		QString dirLast = m_sett->value("dir", "").toString();
Tobias WEBER's avatar
Tobias WEBER committed
752
753
754
755
756
757
		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;
Tobias WEBER's avatar
Tobias WEBER committed
758

Tobias WEBER's avatar
Tobias WEBER committed
759
		QString filename = files[0];
Tobias WEBER's avatar
Tobias WEBER committed
760
		if(filename == "" || !QFile::exists(filename))
Tobias WEBER's avatar
Tobias WEBER committed
761
762
763
			return;
		m_sett->setValue("dir", QFileInfo(filename).path());

Tobias WEBER's avatar
Tobias WEBER committed
764
		New();
765

Tobias WEBER's avatar
Tobias WEBER committed
766

767
768
769
		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
770
		auto progressHandler = [dlgProgress, &bCancelled](t_real percentage) -> bool
771
772
773
774
		{
			dlgProgress->setValue(int(percentage*10));
			bCancelled = dlgProgress->wasCanceled();
			return !bCancelled;
Tobias WEBER's avatar
Tobias WEBER committed
775
776
		};
		m_mol.SubscribeToLoadProgress(progressHandler);
777
778
		dlgProgress->setWindowModality(Qt::WindowModal);

Tobias WEBER's avatar
Tobias WEBER committed
779

Tobias WEBER's avatar
Tobias WEBER committed
780
		if(!m_mol.LoadFile(filename.toStdString(), filedlg->GetFrameSkip()))
Tobias WEBER's avatar
Tobias WEBER committed
781
		{
782
783
			// only show error if not explicitely cancelled
			if(!bCancelled)
Tobias WEBER's avatar
Tobias WEBER committed
784
				QMessageBox::critical(this, PROG_NAME, "Error loading file.");
Tobias WEBER's avatar
Tobias WEBER committed
785
			return;
Tobias WEBER's avatar
Tobias WEBER committed
786
787
		}

Tobias WEBER's avatar
Tobias WEBER committed
788
789

		m_mol.UnsubscribeFromLoadProgress(&progressHandler);
Tobias WEBER's avatar
Tobias WEBER committed
790
		m_sliderFrame->setMaximum(m_mol.GetFrameCount() - 1);
Tobias WEBER's avatar
Tobias WEBER committed
791

Tobias WEBER's avatar
Tobias WEBER committed
792

Tobias WEBER's avatar
Tobias WEBER committed
793
794
795
796
797
		// 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();

Tobias WEBER's avatar
Tobias WEBER committed
798
		m_crystA = tl2::create<t_mat>({
Tobias WEBER's avatar
Tobias WEBER committed
799
800
801
802
803
			_a[0],	_b[0],	_c[0],
			_a[1],	_b[1],	_c[1],
			_a[2], 	_b[2],	_c[2] });

		bool ok = true;
Tobias WEBER's avatar
Tobias WEBER committed
804
		std::tie(m_crystB, ok) = tl2::inv(m_crystA);
Tobias WEBER's avatar
Tobias WEBER committed
805
806
		if(!ok)
		{
Tobias WEBER's avatar
Tobias WEBER committed
807
			m_crystB = tl2::unit<t_mat>();
Tobias WEBER's avatar
Tobias WEBER committed
808
			QMessageBox::critical(this, PROG_NAME, "Error: Cannot invert A matrix.");
Tobias WEBER's avatar
Tobias WEBER committed
809
810
		}

Tobias WEBER's avatar
Tobias WEBER committed
811
		m_crystB /= t_real_gl(2)*tl2::pi<t_real_gl>;
Tobias WEBER's avatar
Tobias WEBER committed
812
		t_mat_gl matA{m_crystA};
Tobias WEBER's avatar
Tobias WEBER committed
813
		m_plot->GetRenderer()->SetBTrafo(m_crystB, &matA);
Tobias WEBER's avatar
Tobias WEBER committed
814
815
816
817
818

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


Tobias WEBER's avatar
Tobias WEBER committed
819
		// atom colors
Tobias WEBER's avatar
Tobias WEBER committed
820
821
		std::vector<t_vec> cols =
		{
Tobias WEBER's avatar
Tobias WEBER committed
822
823
824
825
826
827
			tl2::create<t_vec>({1, 0, 0}),
			tl2::create<t_vec>({0, 0, 1}),
			tl2::create<t_vec>({0, 0.5, 0}),
			tl2::create<t_vec>({0, 0.5, 0.5}),
			tl2::create<t_vec>({0.5, 0.5, 0}),
			tl2::create<t_vec>({0, 0, 0}),
Tobias WEBER's avatar
Tobias WEBER committed
828
829
		};

Tobias WEBER's avatar
Tobias WEBER committed
830
		// add atoms to 3d view
Tobias WEBER's avatar
Tobias WEBER committed
831
832
833
		if(m_mol.GetFrameCount())
		{
			const auto& frame = m_mol.GetFrame(0);
Tobias WEBER's avatar
Tobias WEBER committed
834
			m_sphereHandles.reserve(frame.GetNumAtomTypes());
Tobias WEBER's avatar
Tobias WEBER committed
835

836
			for(std::size_t atomtypeidx=0; atomtypeidx<frame.GetNumAtomTypes(); ++atomtypeidx)
Tobias WEBER's avatar
Tobias WEBER committed
837
			{
838
839
840
				const auto& coords = frame.GetCoords(atomtypeidx);

				int atomidx = 0;
Tobias WEBER's avatar
Tobias WEBER committed
841
				for(const t_vec& vec : coords)
842
				{
Tobias WEBER's avatar
Tobias WEBER committed
843
					t_real atomscale = m_spinScale->value();
844

845
					std::size_t handle = Add3DAtom(vec, cols[atomtypeidx % cols.size()], atomscale,
846
						m_mol.GetAtomName(atomtypeidx), atomidx);
847
					m_sphereHandles.push_back(handle);
848
849

					++atomidx;
850
				}
Tobias WEBER's avatar
Tobias WEBER committed
851
852
853
854
855
			}
		}
	}
	catch(const std::exception& ex)
	{
Tobias WEBER's avatar
Tobias WEBER committed
856
		QMessageBox::critical(this, PROG_NAME, ex.what());
Tobias WEBER's avatar
Tobias WEBER committed
857
858
	}

Tobias WEBER's avatar
Tobias WEBER committed
859

Tobias WEBER's avatar
Tobias WEBER committed
860
	UpdateAtomsStatusMsg();
Tobias WEBER's avatar
Tobias WEBER committed
861
	m_plot->update();
Tobias WEBER's avatar
Tobias WEBER committed
862
863
864
}


Tobias WEBER's avatar
Tobias WEBER committed
865
void MolDynDlg::SaveAs()
Tobias WEBER's avatar
Tobias WEBER committed
866
{
Tobias WEBER's avatar
Tobias WEBER committed
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
	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()))
		{
Tobias WEBER's avatar
Tobias WEBER committed
891
			QMessageBox::critical(this, PROG_NAME, "Error saving file.");
Tobias WEBER's avatar
Tobias WEBER committed
892
893
894
895
896
897
898
		}


		m_mol.UnsubscribeFromSaveProgress(&progressHandler);
	}
	catch(const std::exception& ex)
	{
Tobias WEBER's avatar
Tobias WEBER committed
899
		QMessageBox::critical(this, PROG_NAME, ex.what());
Tobias WEBER's avatar
Tobias WEBER committed
900
	}
Tobias WEBER's avatar
Tobias WEBER committed
901
902
903
904
905
906
907
908
909
910
911
}
// ----------------------------------------------------------------------------



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

Tobias WEBER's avatar
Tobias WEBER committed
914
915
916
917
918
919
920
921
	if(pos)
		m_curPickedObj = long(objIdx);
	else
		m_curPickedObj = -1;


	if(m_curPickedObj > 0)
	{
Tobias WEBER's avatar
Tobias WEBER committed
922
		const auto [typelabel, atomidx] = SplitDataString(m_plot->GetRenderer()->GetObjectDataString(m_curPickedObj));
923
924
925

		std::ostringstream ostrLabel;
		ostrLabel << "Current Atom: " << typelabel << " #" << (atomidx+1);
Tobias WEBER's avatar
Tobias WEBER committed
926

927
		m_statusCurAtom->setText(ostrLabel.str().c_str());
Tobias WEBER's avatar
Tobias WEBER committed
928
		//SetStatusMsg(label);
Tobias WEBER's avatar
Tobias WEBER committed
929
930
931
	}
	else
	{
Tobias WEBER's avatar
Tobias WEBER committed
932
933
		m_statusCurAtom->setText("");
		//SetStatusMsg("");
Tobias WEBER's avatar
Tobias WEBER committed
934
935
936
937
938
939
940
941
	}
}



/**
 * set status label text in 3d dialog
 */
Tobias WEBER's avatar
Tobias WEBER committed
942
void MolDynDlg::SetStatusMsg(const std::string& msg)
Tobias WEBER's avatar
Tobias WEBER committed
943
944
{
	if(!m_status) return;
945
	m_status->showMessage(msg.c_str(), 2000);
Tobias WEBER's avatar
Tobias WEBER committed
946
947
948
949
}



Tobias WEBER's avatar
Tobias WEBER committed
950
951
void  MolDynDlg::UpdateAtomsStatusMsg()
{
Tobias WEBER's avatar
Tobias WEBER committed
952
	if(!m_statusAtoms || !m_sliderFrame) return;
Tobias WEBER's avatar
Tobias WEBER committed
953
954
955
956
957
958
959
960
961
962

	// Atoms
	std::string numAtoms = std::to_string(m_mol.GetNumAtomsTotal()) + " atoms.";

	// Selected
	if(m_plot)
	{
		std::size_t numSelected = 0;

		for(auto handle : m_sphereHandles)
963
        {
Tobias WEBER's avatar
Tobias WEBER committed
964
			if(m_plot->GetRenderer()->GetObjectHighlight(handle))
Tobias WEBER's avatar
Tobias WEBER committed
965
				++numSelected;
966
        }
Tobias WEBER's avatar
Tobias WEBER committed
967
968
969
970
971

		numAtoms += " " + std::to_string(numSelected) + " selected.";
	}

	// Frames
Tobias WEBER's avatar
Tobias WEBER committed
972
	numAtoms += " Frame " + std::to_string(m_sliderFrame->value()+1) + " of " + std::to_string(m_mol.GetFrameCount()) + ".";
Tobias WEBER's avatar
Tobias WEBER committed
973
974
975
976
977
978

	m_statusAtoms->setText(numAtoms.c_str());
}



Tobias WEBER's avatar
Tobias WEBER committed
979
980
981
982
983
/**
 * mouse button pressed
 */
void MolDynDlg::PlotMouseDown(bool left, bool mid, bool right)
{
Tobias WEBER's avatar
Tobias WEBER committed
984
985
	if(!m_plot) return;

Tobias WEBER's avatar
Tobias WEBER committed
986
987
	if(left && m_curPickedObj > 0)
	{
Tobias WEBER's avatar
Tobias WEBER committed
988
		m_plot->GetRenderer()->SetObjectHighlight(m_curPickedObj, !m_plot->GetRenderer()->GetObjectHighlight(m_curPickedObj));
Tobias WEBER's avatar
Tobias WEBER committed
989
		UpdateAtomsStatusMsg();
Tobias WEBER's avatar
Tobias WEBER committed
990
		m_plot->update();
Tobias WEBER's avatar
Tobias WEBER committed
991
992
993
994
995
996
997
998
999
1000
	}
}


/**
 * mouse button released
 */
void MolDynDlg::PlotMouseUp(bool left, bool mid, bool right)
{
}
Tobias WEBER's avatar
Tobias WEBER committed
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010


/**
 * mouse button clicked
 */
void MolDynDlg::PlotMouseClick(bool left, bool mid, bool right)
{
	// show context menu
	if(right && m_curPickedObj > 0)
	{
Tobias WEBER's avatar
Tobias WEBER committed
1011
		const auto [typelabel, atomidx] = SplitDataString(m_plot->GetRenderer()->GetObjectDataString(m_curPickedObj));
1012
1013

		QString atomLabel = typelabel.c_str();
Tobias WEBER's avatar
Tobias WEBER committed
1014
		m_atomContextMenu->actions()[0]->setText("Delete This \"" + atomLabel + "\" Atom");
1015
1016
1017
		m_atomContextMenu->actions()[5]->setText("Delete All \"" + atomLabel + "\" Atoms");
		m_atomContextMenu->actions()[6]->setText("Delete All But \"" + atomLabel + "\" Atoms");
		m_atomContextMenu->actions()[8]->setText("Select All \"" + atomLabel + "\" Atoms");
Tobias WEBER's avatar
Tobias WEBER committed
1018
1019
1020
1021
1022
1023

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


Tobias WEBER's avatar
Tobias WEBER committed
1026
1027
1028
// ----------------------------------------------------------------------------


Tobias WEBER's avatar
Tobias WEBER committed
1029
1030
1031
1032
1033
1034
1035
1036
/**
 * select all atoms
 */
void MolDynDlg::SelectAll()
{
	if(!m_plot) return;

	for(auto handle : m_sphereHandles)
Tobias WEBER's avatar
Tobias WEBER committed
1037
		m_plot->GetRenderer()->SetObjectHighlight(handle, 1);
Tobias WEBER's avatar
Tobias WEBER committed
1038

Tobias WEBER's avatar
Tobias WEBER committed
1039
	UpdateAtomsStatusMsg();
Tobias WEBER's avatar
Tobias WEBER committed
1040
1041
1042
1043
	m_plot->update();
}


Tobias WEBER's avatar
Tobias WEBER committed
1044
1045
1046
1047
1048
1049
1050
1051
/**
 * unselect all atoms
 */
void MolDynDlg::SelectNone()
{
	if(!m_plot) return;

	for(auto handle : m_sphereHandles)
Tobias WEBER's avatar
Tobias WEBER committed
1052
		m_plot->GetRenderer()->SetObjectHighlight(handle, 0);
Tobias WEBER's avatar
Tobias WEBER committed
1053

Tobias WEBER's avatar
Tobias WEBER committed
1054
	UpdateAtomsStatusMsg();
Tobias WEBER's avatar
Tobias WEBER committed
1055
1056
1057
	m_plot->update();
}

Tobias WEBER's avatar
Tobias WEBER committed
1058

Tobias WEBER's avatar
Tobias WEBER committed
1059
1060
void MolDynDlg::SliderValueChanged(int val)
{
1061
1062
1063
1064
1065
1066
1067
1068
	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
1069
	t_real atomscale = m_spinScale->value();
1070
1071

	std::size_t counter = 0;
1072
	for(std::size_t atomtypeidx=0; atomtypeidx<frame.GetNumAtomTypes(); ++atomtypeidx)
1073
	{
1074
1075
1076
		const auto& coords = frame.GetCoords(atomtypeidx);

		int atomidx = 0;
1077
1078
1079
		for(const t_vec& vec : coords)
		{
			std::size_t obj = m_sphereHandles[counter];
Tobias WEBER's avatar
Tobias WEBER committed
1080
			Change3DAtom(obj, &vec, nullptr, &atomscale, nullptr, atomidx);
1081
1082

			++counter;
1083
			++atomidx;
1084
1085
1086
		}
	}

Tobias WEBER's avatar
Tobias WEBER committed
1087
#ifdef USE_QHULL
1088
	CalculateConvexHulls();
Tobias WEBER's avatar
Tobias WEBER committed
1089
#endif
Tobias WEBER's avatar
Tobias WEBER committed
1090
	UpdateAtomsStatusMsg();
1091
	m_plot->update();
Tobias WEBER's avatar
Tobias WEBER committed
1092
1093
1094
}


Tobias WEBER's avatar
Tobias WEBER committed
1095
1096
// ----------------------------------------------------------------------------

1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
/**
 * extract [typelabel, atomindex] from the atom data strings
 */
std::tuple<std::string, int> MolDynDlg::SplitDataString(const std::string& data) const
{
	std::size_t idx = data.rfind(DATA_SEP);

	// no separator found, just return atom type string
	if(idx == std::string::npos)
		return std::make_tuple(data, -1);

	// split string
	std::string atomtype = data.substr(0, idx);
	std::string atomidx = data.substr(idx + std::strlen(DATA_SEP));

	return std::make_tuple(atomtype, std::stoi(atomidx));
}

Tobias WEBER's avatar
Tobias WEBER committed
1115

Tobias WEBER's avatar
Tobias WEBER committed
1116
/**
Tobias WEBER's avatar
Tobias WEBER committed
1117
1118
 * 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
1119
 */
1120
std::tuple<bool, std::size_t, std::size_t, std::size_t>
Tobias WEBER's avatar
Tobias WEBER committed
1121
MolDynDlg::GetAtomIndexFromHandle(std::size_t handle) const
Tobias WEBER's avatar
Tobias WEBER committed
1122
{
Tobias WEBER's avatar
Tobias WEBER committed
1123
	// find handle in sphere handle vector
Tobias WEBER's avatar
Tobias WEBER committed
1124
	auto iter = std::find(m_sphereHandles.begin(), m_sphereHandles.end(), handle);
Tobias WEBER's avatar
Tobias WEBER committed
1125
	if(iter == m_sphereHandles.end())
Tobias WEBER's avatar
Tobias WEBER committed
1126
		return std::make_tuple(0, 0, 0, 0);
Tobias WEBER's avatar
Tobias WEBER committed
1127
1128

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

Tobias WEBER's avatar
Tobias WEBER committed
1130
1131
	std::size_t atomCountsSoFar = 0;
	std::size_t atomTypeIdx = 0;
Tobias WEBER's avatar
Tobias WEBER committed
1132
	for(atomTypeIdx=0; atomTypeIdx<m_mol.GetNumAtomTypes(); ++atomTypeIdx)
Tobias WEBER's avatar
Tobias WEBER committed
1133
1134
1135
1136
1137
1138
1139
1140
	{
		std::size_t numAtoms = m_mol.GetAtomNum(atomTypeIdx);
		if(atomCountsSoFar + numAtoms > sphereIdx)
			break;

		atomCountsSoFar += numAtoms;
	}

Tobias WEBER's avatar
Tobias WEBER committed
1141
1142
1143
1144
1145
1146
	std::size_t atomSubTypeIdx = sphereIdx-atomCountsSoFar;

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


Tobias WEBER's avatar
Tobias WEBER committed
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
/**
 * select all atoms of the same type as the one under the cursor
 */
void MolDynDlg::SelectAtomsOfSameType()
{
	// nothing under cursor
	if(m_curPickedObj <= 0)
		return;

	// atom type to be selected
Tobias WEBER's avatar
Tobias WEBER committed
1157
	const auto [atomLabel, atomidx] = SplitDataString(m_plot->GetRenderer()->GetObjectDataString(m_curPickedObj));
Tobias WEBER's avatar
Tobias WEBER committed
1158
1159

	for(auto handle : m_sphereHandles)
1160
    {
Tobias WEBER's avatar
Tobias WEBER committed
1161
		const auto [typelabel, atomidx] = SplitDataString(m_plot->GetRenderer()->GetObjectDataString(handle));
1162
		if(typelabel == atomLabel)
Tobias WEBER's avatar
Tobias WEBER committed
1163
			m_plot->GetRenderer()->SetObjectHighlight(handle, 1);
1164
    }
Tobias WEBER's avatar
Tobias WEBER committed
1165

Tobias WEBER's avatar
Tobias WEBER committed