magstructfact.cpp 73.6 KB
Newer Older
1
2
3
4
5
/**
 * magnetic structure factor tool
 * @author Tobias Weber <tweber@ill.fr>
 * @date Jan-2019
 * @license GPLv3, see 'LICENSE' file
Tobias WEBER's avatar
Tobias WEBER committed
6
 * @desc The present version was forked on 28-Dec-2018 from my privately developed "misc" project (https://github.com/t-weber/misc).
7
8
9
10
 */

#include "magstructfact.h"

Tobias WEBER's avatar
Tobias WEBER committed
11
#include <QtCore/QDir>
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <QtWidgets/QApplication>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QTabWidget>
#include <QtWidgets/QLabel>
#include <QtWidgets/QToolButton>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QMessageBox>

#include <iostream>
#include <fstream>
#include <random>
#include <chrono>
#include <tuple>

#include <boost/version.hpp>
#include <boost/config.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/units/systems/si/codata/electron_constants.hpp>
#include <boost/units/systems/si/codata/neutron_constants.hpp>
#include <boost/units/systems/si/codata/electromagnetic_constants.hpp>
namespace algo = boost::algorithm;
namespace pt = boost::property_tree;
namespace si = boost::units::si;
namespace consts = si::constants;

#include "../structfact/loadcif.h"
42
43
#include "tlibs2/libs/algos.h"
#include "tlibs2/libs/helper.h"
44

Tobias WEBER's avatar
Tobias WEBER committed
45
using namespace tl2_ops;
46
47
48
49
50

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


51
// columns of Fourier components table
52
53
54
enum : int
{
	COL_NAME = 0,
Tobias WEBER's avatar
Tobias WEBER committed
55
56
57
	COL_X, COL_Y, COL_Z,				// position
	COL_M_MAG,							// scale factor of FC
	COL_ReM_X, COL_ReM_Y, COL_ReM_Z,	// fourier components
58
	COL_ImM_X, COL_ImM_Y, COL_ImM_Z,
Tobias WEBER's avatar
Tobias WEBER committed
59
60
	COL_RAD,							// drawing radius
	COL_COL,							// colour
61
62
63
64
65

	NUM_COLS
};


66
67
68
69
// columns of propagation vectors table
enum : int
{
	PROP_COL_NAME = 0,
Tobias WEBER's avatar
Tobias WEBER committed
70
71
	PROP_COL_X, PROP_COL_Y, PROP_COL_Z,	// propagation direction
	PROP_COL_CONJ,						// conjugate fourier component for this propagation vector?
72
73
74
75
76

	PROP_NUM_COLS
};


77
78
struct PowderLine
{
79
80
81
	t_real Q{};
	t_real I{};
	std::size_t num_peaks = 0;
82
83
84
85
86
87
	std::string peaks;
};


// ----------------------------------------------------------------------------
MagStructFactDlg::MagStructFactDlg(QWidget* pParent) : QDialog{pParent},
Tobias WEBER's avatar
Tobias WEBER committed
88
	m_sett{new QSettings{"takin", "magstructfact"}}
89
90
91
92
93
94
95
{
	setWindowTitle("Magnetic Structure Factors");
	setSizeGripEnabled(true);
	setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));


	auto tabs = new QTabWidget(this);
96
97
98


	{	// fourier components panel
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
		m_nucleipanel = new QWidget(this);

		m_nuclei = new QTableWidget(m_nucleipanel);
		m_nuclei->setShowGrid(true);
		m_nuclei->setSortingEnabled(true);
		m_nuclei->setMouseTracking(true);
		m_nuclei->setSelectionBehavior(QTableWidget::SelectRows);
		m_nuclei->setSelectionMode(QTableWidget::ContiguousSelection);
		m_nuclei->setContextMenuPolicy(Qt::CustomContextMenu);

		m_nuclei->verticalHeader()->setDefaultSectionSize(fontMetrics().lineSpacing() + 4);
		m_nuclei->verticalHeader()->setVisible(false);

		m_nuclei->setColumnCount(NUM_COLS);
		m_nuclei->setHorizontalHeaderItem(COL_NAME, new QTableWidgetItem{"Name"});
		m_nuclei->setHorizontalHeaderItem(COL_X, new QTableWidgetItem{"x (frac.)"});
		m_nuclei->setHorizontalHeaderItem(COL_Y, new QTableWidgetItem{"y (frac.)"});
		m_nuclei->setHorizontalHeaderItem(COL_Z, new QTableWidgetItem{"z (frac.)"});
117
		m_nuclei->setHorizontalHeaderItem(COL_M_MAG, new QTableWidgetItem{"|M|"});
118
119
120
121
122
123
		m_nuclei->setHorizontalHeaderItem(COL_ReM_X, new QTableWidgetItem{"Re{FC_x}"});
		m_nuclei->setHorizontalHeaderItem(COL_ReM_Y, new QTableWidgetItem{"Re{FC_y}"});
		m_nuclei->setHorizontalHeaderItem(COL_ReM_Z, new QTableWidgetItem{"Re{FC_z}"});
		m_nuclei->setHorizontalHeaderItem(COL_ImM_X, new QTableWidgetItem{"Im{FC_x}"});
		m_nuclei->setHorizontalHeaderItem(COL_ImM_Y, new QTableWidgetItem{"Im{FC_y}"});
		m_nuclei->setHorizontalHeaderItem(COL_ImM_Z, new QTableWidgetItem{"Im{FC_z}"});
Tobias WEBER's avatar
Tobias WEBER committed
124
		m_nuclei->setHorizontalHeaderItem(COL_RAD, new QTableWidgetItem{"Scale"});
125
126
127
128
129
130
		m_nuclei->setHorizontalHeaderItem(COL_COL, new QTableWidgetItem{"Colour"});

		m_nuclei->setColumnWidth(COL_NAME, 90);
		m_nuclei->setColumnWidth(COL_X, 75);
		m_nuclei->setColumnWidth(COL_Y, 75);
		m_nuclei->setColumnWidth(COL_Z, 75);
131
		m_nuclei->setColumnWidth(COL_M_MAG, 75);
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
		m_nuclei->setColumnWidth(COL_ReM_X, 75);
		m_nuclei->setColumnWidth(COL_ReM_Y, 75);
		m_nuclei->setColumnWidth(COL_ReM_Z, 75);
		m_nuclei->setColumnWidth(COL_ImM_X, 75);
		m_nuclei->setColumnWidth(COL_ImM_Y, 75);
		m_nuclei->setColumnWidth(COL_ImM_Z, 75);
		m_nuclei->setColumnWidth(COL_RAD, 75);
		m_nuclei->setColumnWidth(COL_COL, 75);

		QToolButton *pTabBtnAdd = new QToolButton(m_nucleipanel);
		QToolButton *pTabBtnDel = new QToolButton(m_nucleipanel);
		QToolButton *pTabBtnUp = new QToolButton(m_nucleipanel);
		QToolButton *pTabBtnDown = new QToolButton(m_nucleipanel);
		QToolButton *pTabBtnSG = new QToolButton(m_nucleipanel);

		m_nuclei->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Expanding});
		pTabBtnAdd->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnDel->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnUp->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnDown->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnSG->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});

154
155
156
157
		pTabBtnAdd->setText("Add Fourier Comp.");
		pTabBtnDel->setText("Delete Fourier Comp.");
		pTabBtnUp->setText("Move Fourier Comp. Up");
		pTabBtnDown->setText("Move Fourier Comp. Down");
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
		pTabBtnSG->setText("Generate");

		m_editA = new QLineEdit("5", m_nucleipanel);
		m_editB = new QLineEdit("5", m_nucleipanel);
		m_editC = new QLineEdit("5", m_nucleipanel);
		m_editAlpha = new QLineEdit("90", m_nucleipanel);
		m_editBeta = new QLineEdit("90", m_nucleipanel);
		m_editGamma = new QLineEdit("90", m_nucleipanel);

		m_comboSG = new QComboBox(m_nucleipanel);


		// get space groups and symops
		for(auto [sgnum, descr, ops] : get_sgs<t_mat>())
		{
			m_comboSG->addItem(descr.c_str(), m_comboSG->count());
			m_SGops.emplace_back(std::move(ops));
		}


		auto pTabGrid = new QGridLayout(m_nucleipanel);
		pTabGrid->setSpacing(2);
		pTabGrid->setContentsMargins(4,4,4,4);
Tobias WEBER's avatar
Tobias WEBER committed
181
		int y = 0;
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
		//pTabGrid->addWidget(m_plot.get(), y,0,1,4);
		pTabGrid->addWidget(m_nuclei, y,0,1,4);
		pTabGrid->addWidget(pTabBtnAdd, ++y,0,1,1);
		pTabGrid->addWidget(pTabBtnDel, y,1,1,1);
		pTabGrid->addWidget(pTabBtnUp, y,2,1,1);
		pTabGrid->addWidget(pTabBtnDown, y,3,1,1);

		pTabGrid->addWidget(new QLabel("Space Groups:"), ++y,0,1,1);
		pTabGrid->addWidget(m_comboSG, y,1,1,2);
		pTabGrid->addWidget(pTabBtnSG, y,3,1,1);


		auto sep1 = new QFrame(m_nucleipanel); sep1->setFrameStyle(QFrame::HLine);
		pTabGrid->addWidget(sep1, ++y,0, 1,4);

		pTabGrid->addWidget(new QLabel("Lattice (A):"), ++y,0,1,1);
		pTabGrid->addWidget(m_editA, y,1,1,1);
		pTabGrid->addWidget(m_editB, y,2,1,1);
		pTabGrid->addWidget(m_editC, y,3,1,1);
		pTabGrid->addWidget(new QLabel("Angles (deg):"), ++y,0,1,1);
		pTabGrid->addWidget(m_editAlpha, y,1,1,1);
		pTabGrid->addWidget(m_editBeta, y,2,1,1);
		pTabGrid->addWidget(m_editGamma, y,3,1,1);


		// table CustomContextMenu
208
		QMenu *pTabContextMenu = new QMenu(m_nuclei);
209
210
211
212
		pTabContextMenu->addAction("Add Fourier Component Before", this, [this]() { this->AddTabItem(-2); });
		pTabContextMenu->addAction("Add Fourier Component After", this, [this]() { this->AddTabItem(-3); });
		pTabContextMenu->addAction("Clone Fourier Component", this, [this]() { this->AddTabItem(-4); });
		pTabContextMenu->addAction("Delete Fourier Component", this, [this]() { this->DelTabItem(); });
213
214
215


		// table CustomContextMenu in case nothing is selected
216
		QMenu *pTabContextMenuNoItem = new QMenu(m_nuclei);
217
218
		pTabContextMenuNoItem->addAction("Add Fourier Component", this, [this]() { this->AddTabItem(); });
		pTabContextMenuNoItem->addAction("Delete Fourier Component", this, [this]() { this->DelTabItem(); });
219
		//pTabContextMenuNoItem->addSeparator();
220
221
222
223
224
225
226


		// signals
		for(auto* edit : std::vector<QLineEdit*>{{ m_editA, m_editB, m_editC, m_editAlpha, m_editBeta, m_editGamma }})
			connect(edit, &QLineEdit::textEdited, this, [this]() { this->CalcB(); });

		connect(pTabBtnAdd, &QToolButton::clicked, this, [this]() { this->AddTabItem(-1); });
227
228
229
		connect(pTabBtnDel, &QToolButton::clicked, this, [this]() { this->DelTabItem(); });
		connect(pTabBtnUp, &QToolButton::clicked, this, [this]() { this->MoveTabItemUp(m_nuclei); });
		connect(pTabBtnDown, &QToolButton::clicked, this, [this]() { this->MoveTabItemDown(m_nuclei); });
230
231
232
233
234
		connect(pTabBtnSG, &QToolButton::clicked, this, &MagStructFactDlg::GenerateFromSG);

		connect(m_nuclei, &QTableWidget::currentCellChanged, this, &MagStructFactDlg::TableCurCellChanged);
		connect(m_nuclei, &QTableWidget::entered, this, &MagStructFactDlg::TableCellEntered);
		connect(m_nuclei, &QTableWidget::itemChanged, this, &MagStructFactDlg::TableItemChanged);
235
		connect(m_nuclei, &QTableWidget::customContextMenuRequested, this,
236
237
238
239
240
241
242
			[this, pTabContextMenu, pTabContextMenuNoItem](const QPoint& pt)
			{ this->ShowTableContextMenu(m_nuclei, pTabContextMenu, pTabContextMenuNoItem, pt); });

		tabs->addTab(m_nucleipanel, "Fourier Components");
	}


243
	{	// propagation vectors panel
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
		m_propvecpanel = new QWidget(this);

		m_propvecs = new QTableWidget(m_propvecpanel);
		m_propvecs->setShowGrid(true);
		m_propvecs->setSortingEnabled(true);
		m_propvecs->setMouseTracking(true);
		m_propvecs->setSelectionBehavior(QTableWidget::SelectRows);
		m_propvecs->setSelectionMode(QTableWidget::ContiguousSelection);
		m_propvecs->setContextMenuPolicy(Qt::CustomContextMenu);

		m_propvecs->verticalHeader()->setDefaultSectionSize(fontMetrics().lineSpacing() + 4);
		m_propvecs->verticalHeader()->setVisible(false);

		m_propvecs->setColumnCount(PROP_NUM_COLS);
		m_propvecs->setHorizontalHeaderItem(PROP_COL_NAME, new QTableWidgetItem{"Name"});
		m_propvecs->setHorizontalHeaderItem(PROP_COL_X, new QTableWidgetItem{"x (frac.)"});
		m_propvecs->setHorizontalHeaderItem(PROP_COL_Y, new QTableWidgetItem{"y (frac.)"});
		m_propvecs->setHorizontalHeaderItem(PROP_COL_Z, new QTableWidgetItem{"z (frac.)"});
Tobias WEBER's avatar
Tobias WEBER committed
262
		m_propvecs->setHorizontalHeaderItem(PROP_COL_CONJ, new QTableWidgetItem{"FC*"});
263
264
265
266
267

		m_propvecs->setColumnWidth(PROP_COL_NAME, 90);
		m_propvecs->setColumnWidth(PROP_COL_X, 90);
		m_propvecs->setColumnWidth(PROP_COL_Y, 90);
		m_propvecs->setColumnWidth(PROP_COL_Z, 90);
Tobias WEBER's avatar
Tobias WEBER committed
268
		m_propvecs->setColumnWidth(PROP_COL_CONJ, 75);
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285

		QToolButton *pTabBtnAdd = new QToolButton(m_propvecpanel);
		QToolButton *pTabBtnDel = new QToolButton(m_propvecpanel);
		QToolButton *pTabBtnUp = new QToolButton(m_propvecpanel);
		QToolButton *pTabBtnDown = new QToolButton(m_propvecpanel);

		m_propvecs->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Expanding});
		pTabBtnAdd->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnDel->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnUp->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnDown->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});

		pTabBtnAdd->setText("Add Vector");
		pTabBtnDel->setText("Delete Vector");
		pTabBtnUp->setText("Move Vector Up");
		pTabBtnDown->setText("Move Vector Down");

286

287
288
289
		auto pTabGrid = new QGridLayout(m_propvecpanel);
		pTabGrid->setSpacing(2);
		pTabGrid->setContentsMargins(4,4,4,4);
Tobias WEBER's avatar
Tobias WEBER committed
290
		int y = 0;
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
		pTabGrid->addWidget(m_propvecs, y,0,1,4);
		pTabGrid->addWidget(pTabBtnAdd, ++y,0,1,1);
		pTabGrid->addWidget(pTabBtnDel, y,1,1,1);
		pTabGrid->addWidget(pTabBtnUp, y,2,1,1);
		pTabGrid->addWidget(pTabBtnDown, y,3,1,1);


		// table CustomContextMenu
		QMenu *pPropContextMenu = new QMenu(m_propvecs);
		pPropContextMenu->addAction("Add Vector Before", this, [this]() { this->AddPropItem(-2); });
		pPropContextMenu->addAction("Add Vector After", this, [this]() { this->AddPropItem(-3); });
		pPropContextMenu->addAction("Clone Vector", this, [this]() { this->AddPropItem(-4); });
		pPropContextMenu->addAction("Delete Vector", this, [this]() { this->DelPropItem(); });


		// table CustomContextMenu in case nothing is selected
		QMenu *pPropContextMenuNoItem = new QMenu(m_propvecs);
		pPropContextMenuNoItem->addAction("Add Vector", this, [this]() { this->AddPropItem(); });
		pPropContextMenuNoItem->addAction("Delete Vector", this, [this]() { this->DelPropItem(); });



		connect(pTabBtnAdd, &QToolButton::clicked, this, [this]() { this->AddPropItem(-1); });
		connect(pTabBtnDel, &QToolButton::clicked, this, [this]() { this->DelPropItem(); });
		connect(pTabBtnUp, &QToolButton::clicked, this,  [this]() { this->MoveTabItemUp(m_propvecs); });
		connect(pTabBtnDown, &QToolButton::clicked, this,  [this]() { this->MoveTabItemUp(m_propvecs); });

		//connect(m_propvecs, &QTableWidget::currentCellChanged, this, &MagStructFactDlg::PropCurCellChanged);
		//connect(m_propvecs, &QTableWidget::entered, this, &MagStructFactDlg::PropCellEntered);
		connect(m_propvecs, &QTableWidget::itemChanged, this, &MagStructFactDlg::PropItemChanged);
321
		connect(m_propvecs, &QTableWidget::customContextMenuRequested, this,
322
323
324
325
			[this, pPropContextMenu, pPropContextMenuNoItem](const QPoint& pt)
			{ this->ShowTableContextMenu(m_propvecs, pPropContextMenu, pPropContextMenuNoItem, pt); });

		tabs->addTab(m_propvecpanel, "Propagation Vectors");
326
327
328
329
330
331
332
333
334
335
336
337
	}


	{	// structure factors panel
		auto sfactpanel = new QWidget(this);
		auto pGrid = new QGridLayout(sfactpanel);
		pGrid->setSpacing(4);
		pGrid->setContentsMargins(4,4,4,4);

		m_structfacts = new QPlainTextEdit(sfactpanel);
		m_structfacts->setReadOnly(true);
		m_structfacts->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
338
		m_structfacts->setLineWrapMode(QPlainTextEdit::NoWrap);
339
340
341
342
343
344

		m_maxBZ = new QSpinBox(sfactpanel);
		m_maxBZ->setMinimum(0);
		m_maxBZ->setMaximum(99);
		m_maxBZ->setValue(4);

345
346
347
348
		m_RemoveZeroes = new QCheckBox("Remove Zeroes", sfactpanel);
		m_RemoveZeroes->setChecked(true);


349
		pGrid->addWidget(m_structfacts, 0,0, 1,4);
350
		pGrid->addWidget(new QLabel("Max. Order:"), 1,0,1,1);
351
		pGrid->addWidget(m_maxBZ, 1,1, 1,1);
352
353
		pGrid->addWidget(m_RemoveZeroes, 1,2, 1,2);

354
355
356

		// signals
		connect(m_maxBZ, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this]() { this->Calc(); });
357
		connect(m_RemoveZeroes, static_cast<void (QCheckBox::*)(int)>(&QCheckBox::stateChanged), this, [this]() { this->Calc(); });
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379

		tabs->addTab(sfactpanel, "Structure Factors");
	}


	{	// powder lines panel
		auto powderpanel = new QWidget(this);
		auto pGrid = new QGridLayout(powderpanel);
		pGrid->setSpacing(4);
		pGrid->setContentsMargins(4,4,4,4);

		m_powderlines = new QPlainTextEdit(powderpanel);
		m_powderlines->setReadOnly(true);
		m_powderlines->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
		m_powderlines->setLineWrapMode(QPlainTextEdit::NoWrap);

		pGrid->addWidget(m_powderlines, 0,0, 1,4);

		tabs->addTab(powderpanel, "Powder Lines");
	}


380
381
382
383
384
385
386
387
388
389
390
	{	// real magnetic moments
		auto mmpanel = new QWidget(this);
		auto pGrid = new QGridLayout(mmpanel);
		pGrid->setSpacing(4);
		pGrid->setContentsMargins(4,4,4,4);

		m_moments = new QPlainTextEdit(mmpanel);
		m_moments->setReadOnly(true);
		m_moments->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
		m_moments->setLineWrapMode(QPlainTextEdit::NoWrap);

Tobias WEBER's avatar
Tobias WEBER committed
391
392
393
394
395
396
397
398
399
400
401
		for(auto*& spin : m_maxSC)
		{
			spin = new QSpinBox(mmpanel);
			spin->setMinimum(0);
			spin->setMaximum(999);
			spin->setValue(4);
		}

		m_maxSC[0]->setPrefix("x = ");
		m_maxSC[1]->setPrefix("y = ");
		m_maxSC[2]->setPrefix("z = ");
402
403
404

		pGrid->addWidget(m_moments, 0,0, 1,4);
		pGrid->addWidget(new QLabel("Max. Supercell Order:"), 1,0,1,1);
Tobias WEBER's avatar
Tobias WEBER committed
405
406
407
		pGrid->addWidget(m_maxSC[0], 1,1, 1,1);
		pGrid->addWidget(m_maxSC[1], 1,2, 1,1);
		pGrid->addWidget(m_maxSC[2], 1,3, 1,1);
408
409
410


		// signals
Tobias WEBER's avatar
Tobias WEBER committed
411
412
		for(auto* spin : m_maxSC)
			connect(spin, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this]() { this->Calc(); });
413
414
415
416
417

		tabs->addTab(mmpanel, "Magnetic Moments");
	}


418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
	{	// info panel
		auto infopanel = new QWidget(this);
		auto pGrid = new QGridLayout(infopanel);
		pGrid->setSpacing(4);
		pGrid->setContentsMargins(4,4,4,4);

		// table grid
		for(int i=0; i<4; ++i)
		{
			m_labelGlInfos[i] = new QLabel("", infopanel);
			m_labelGlInfos[i]->setSizePolicy(QSizePolicy::Ignored, m_labelGlInfos[i]->sizePolicy().verticalPolicy());
		}

		auto sep1 = new QFrame(infopanel); sep1->setFrameStyle(QFrame::HLine);
		auto sep2 = new QFrame(infopanel); sep2->setFrameStyle(QFrame::HLine);
		auto sep3 = new QFrame(infopanel); sep3->setFrameStyle(QFrame::HLine);

		std::string strBoost = BOOST_LIB_VERSION;
		algo::replace_all(strBoost, "_", ".");

		auto labelTitle = new QLabel("Magnetic Structure Factor Calculator", infopanel);
		auto fontTitle = labelTitle->font();
		fontTitle.setBold(true);
		labelTitle->setFont(fontTitle);
		labelTitle->setAlignment(Qt::AlignHCenter);

		auto labelAuthor = new QLabel("Written by Tobias Weber <tweber@ill.fr>.", infopanel);
		labelAuthor->setAlignment(Qt::AlignHCenter);

		auto labelDate = new QLabel("January 2019.", infopanel);
		labelDate->setAlignment(Qt::AlignHCenter);

		int y = 0;
		pGrid->addWidget(labelTitle, y++,0, 1,1);
		pGrid->addWidget(labelAuthor, y++,0, 1,1);
		pGrid->addWidget(labelDate, y++,0, 1,1);
		pGrid->addItem(new QSpacerItem(16,16, QSizePolicy::Minimum, QSizePolicy::Fixed), y++,0, 1,1);
		pGrid->addWidget(sep1, y++,0, 1,1);
		pGrid->addWidget(new QLabel(QString("Compiler: ") + QString(BOOST_COMPILER) + ".", infopanel), y++,0, 1,1);
		pGrid->addWidget(new QLabel(QString("C++ Library: ") + QString(BOOST_STDLIB) + ".", infopanel), y++,0, 1,1);
		pGrid->addWidget(new QLabel(QString("Build Date: ") + QString(__DATE__) + ", " + QString(__TIME__) + ".", infopanel), y++,0, 1,1);
		pGrid->addWidget(sep2, y++,0, 1,1);
		pGrid->addWidget(new QLabel(QString("Qt Version: ") + QString(QT_VERSION_STR) + ".", infopanel), y++,0, 1,1);
		pGrid->addWidget(new QLabel(QString("Boost Version: ") + strBoost.c_str() + ".", infopanel), y++,0, 1,1);
		pGrid->addWidget(sep3, y++,0, 1,1);
		for(int i=0; i<4; ++i)
			pGrid->addWidget(m_labelGlInfos[i], y++,0, 1,1);
		pGrid->addItem(new QSpacerItem(16,16, QSizePolicy::Minimum, QSizePolicy::Expanding), y++,0, 1,1);

		tabs->addTab(infopanel, "Infos");
	}


	// main grid
	auto pmainGrid = new QGridLayout(this);
	pmainGrid->setSpacing(4);
	pmainGrid->setContentsMargins(4,4,4,4);
	pmainGrid->addWidget(tabs, 0,0, 1,1);


	// menu bar
	{
		m_menu = new QMenuBar(this);
		m_menu->setNativeMenuBar(m_sett ? m_sett->value("native_gui", false).toBool() : false);

		auto menuFile = new QMenu("File", m_menu);
Tobias WEBER's avatar
Tobias WEBER committed
484
		auto menuView = new QMenu("3D View", m_menu);
485

Tobias WEBER's avatar
Tobias WEBER committed
486
		auto acNew = new QAction("New", menuFile);
487
488
489
		auto acLoad = new QAction("Load...", menuFile);
		auto acSave = new QAction("Save...", menuFile);
		auto acImportCIF = new QAction("Import CIF...", menuFile);
Tobias WEBER's avatar
Tobias WEBER committed
490
		auto acExit = new QAction("Quit", menuFile);
Tobias WEBER's avatar
Tobias WEBER committed
491
492
		auto ac3DView = new QAction("Unit Cell / Fourier Components...", menuFile);
		auto ac3DViewSC = new QAction("Super Cell / Magnetic Moments...", menuFile);
493

Tobias WEBER's avatar
Tobias WEBER committed
494
495
		menuFile->addAction(acNew);
		menuFile->addSeparator();
496
497
498
499
500
501
502
		menuFile->addAction(acLoad);
		menuFile->addAction(acSave);
		menuFile->addSeparator();
		menuFile->addAction(acImportCIF);
		menuFile->addSeparator();
		menuFile->addAction(acExit);
		menuView->addAction(ac3DView);
Tobias WEBER's avatar
Tobias WEBER committed
503
		menuView->addAction(ac3DViewSC);
504

Tobias WEBER's avatar
Tobias WEBER committed
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
		connect(acNew, &QAction::triggered, this,  [this]()
		{
			// clear old tables
			DelTabItem(-1);
			DelPropItem(-1);

			// set some defaults
			m_comboSG->setCurrentIndex(0);
			m_editA->setText("5");
			m_editB->setText("5");
			m_editC->setText("5");
			m_editAlpha->setText("90");
			m_editBeta->setText("90");
			m_editGamma->setText("90");
		});
520
521
522
523
		connect(acLoad, &QAction::triggered, this, &MagStructFactDlg::Load);
		connect(acSave, &QAction::triggered, this, &MagStructFactDlg::Save);
		connect(acImportCIF, &QAction::triggered, this, &MagStructFactDlg::ImportCIF);
		connect(acExit, &QAction::triggered, this, &QDialog::close);
Tobias WEBER's avatar
Tobias WEBER committed
524

Tobias WEBER's avatar
Tobias WEBER committed
525
526

		// unit cell view
527
528
529
530
531
532
		connect(ac3DView, &QAction::triggered, this, [this]()
		{
			// plot widget
			if(!m_dlgPlot)
			{
				m_dlgPlot = new QDialog(this);
Tobias WEBER's avatar
Tobias WEBER committed
533
				m_dlgPlot->setWindowTitle("Unit Cell");
534

Tobias WEBER's avatar
Tobias WEBER committed
535
				m_plot = std::make_shared<tl2::GlPlot>(this);
Tobias WEBER's avatar
Tobias WEBER committed
536
537
538
539
				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
540
					tl2::create<t_vec_gl>({1,0,0,0}), tl2::create<t_vec_gl>({0,0,1,0}));
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562


				auto labCoordSys = new QLabel("Coordinate System:", /*m_dlgPlot*/ this);
				auto comboCoordSys = new QComboBox(/*m_dlgPlot*/ this);
				m_status3D = new QLabel(/*m_dlgPlot*/ this);

				comboCoordSys->addItem("Fractional Units (rlu)");
				comboCoordSys->addItem("Lab Units (A)");


				m_plot->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Expanding});
				labCoordSys->setSizePolicy(QSizePolicy{QSizePolicy::Fixed, QSizePolicy::Fixed});

				auto grid = new QGridLayout(m_dlgPlot);
				grid->setSpacing(2);
				grid->setContentsMargins(4,4,4,4);
				grid->addWidget(m_plot.get(), 0,0,1,2);
				grid->addWidget(labCoordSys, 1,0,1,1);
				grid->addWidget(comboCoordSys, 1,1,1,1);
				grid->addWidget(m_status3D, 2,0,1,2);


Tobias WEBER's avatar
Tobias WEBER committed
563
564
565
566
				connect(m_plot.get(), &tl2::GlPlot::AfterGLInitialisation, this, &MagStructFactDlg::AfterGLInitialisation);
				connect(m_plot->GetRenderer(), &tl2::GlPlotRenderer::PickerIntersection, this, &MagStructFactDlg::PickerIntersection);
				connect(m_plot.get(), &tl2::GlPlot::MouseDown, this, &MagStructFactDlg::PlotMouseDown);
				//connect(m_plot.get(), &tl2::GlPlot::MouseUp, this, [this](bool left, bool mid, bool right) {});
567
568
569
				connect(comboCoordSys, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this](int val)
				{
					if(this->m_plot)
Tobias WEBER's avatar
Tobias WEBER committed
570
						this->m_plot->GetRenderer()->SetCoordSys(val);
571
572
573
574
575
576
577
578
579
580
581
582
583
584
				});


				if(m_sett && m_sett->contains("geo_3dview"))
					m_dlgPlot->restoreGeometry(m_sett->value("geo_3dview").toByteArray());
				else
					m_dlgPlot->resize(500,500);
			}

			m_dlgPlot->show();
			m_dlgPlot->raise();
			m_dlgPlot->focusWidget();
		});

Tobias WEBER's avatar
Tobias WEBER committed
585
586
587
588
589
590
591
592
593
594

		// super cell view
		connect(ac3DViewSC, &QAction::triggered, this, [this]()
		{
			// plot widget
			if(!m_dlgPlotSC)
			{
				m_dlgPlotSC = new QDialog(this);
				m_dlgPlotSC->setWindowTitle("Super Cell");

Tobias WEBER's avatar
Tobias WEBER committed
595
				m_plotSC = std::make_shared<tl2::GlPlot>(this);
Tobias WEBER's avatar
Tobias WEBER committed
596
597
598
599
				m_plotSC->GetRenderer()->SetLight(0, tl2::create<t_vec3_gl>({ 5, 5, 5 }));
				m_plotSC->GetRenderer()->SetLight(1, tl2::create<t_vec3_gl>({ -5, -5, -5 }));
				m_plotSC->GetRenderer()->SetCoordMax(1.);
				m_plotSC->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
600
					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
601
602
603
604


				auto labCoordSys = new QLabel("Coordinate System:", /*m_dlgPlotSC*/ this);
				auto comboCoordSys = new QComboBox(/*m_dlgPlotSC*/ this);
605
				m_status3DSC = new QLabel(/*m_dlgPlotSC*/ this);
Tobias WEBER's avatar
Tobias WEBER committed
606
607
608
609
610
611
612
613
614
615
616
617
618
619

				comboCoordSys->addItem("Fractional Units (rlu)");
				comboCoordSys->addItem("Lab Units (A)");


				m_plotSC->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Expanding});
				labCoordSys->setSizePolicy(QSizePolicy{QSizePolicy::Fixed, QSizePolicy::Fixed});

				auto grid = new QGridLayout(m_dlgPlotSC);
				grid->setSpacing(2);
				grid->setContentsMargins(4,4,4,4);
				grid->addWidget(m_plotSC.get(), 0,0,1,2);
				grid->addWidget(labCoordSys, 1,0,1,1);
				grid->addWidget(comboCoordSys, 1,1,1,1);
620
				grid->addWidget(m_status3DSC, 2,0,1,2);
Tobias WEBER's avatar
Tobias WEBER committed
621
622


Tobias WEBER's avatar
Tobias WEBER committed
623
624
625
626
				connect(m_plotSC.get(), &tl2::GlPlot::AfterGLInitialisation, this, &MagStructFactDlg::AfterGLInitialisationSC);
				connect(m_plotSC->GetRenderer(), &tl2::GlPlotRenderer::PickerIntersection, this, &MagStructFactDlg::PickerIntersectionSC);
				//connect(m_plotSC.get(), &tl2::GlPlot::MouseDown, this, [this](bool left, bool mid, bool right) {});
				//connect(m_plotSC.get(), &tl2::GlPlot::MouseUp, this, [this](bool left, bool mid, bool right) {});
Tobias WEBER's avatar
Tobias WEBER committed
627
628
629
				connect(comboCoordSys, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this](int val)
				{
					if(this->m_plotSC)
Tobias WEBER's avatar
Tobias WEBER committed
630
						this->m_plotSC->GetRenderer()->SetCoordSys(val);
Tobias WEBER's avatar
Tobias WEBER committed
631
632
633
634
635
636
637
638
639
640
641
642
643
644
				});


				if(m_sett && m_sett->contains("geo_3dview_sc"))
					m_dlgPlotSC->restoreGeometry(m_sett->value("geo_3dview_sc").toByteArray());
				else
					m_dlgPlotSC->resize(500,500);
			}

			m_dlgPlotSC->show();
			m_dlgPlotSC->raise();
			m_dlgPlotSC->focusWidget();
		});

645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
		m_menu->addMenu(menuFile);
		m_menu->addMenu(menuView);
		pmainGrid->setMenuBar(m_menu);
	}


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


	m_ignoreChanges = 0;
}



// ----------------------------------------------------------------------------
664
665
666
void MagStructFactDlg::AddTabItem(int row,
	const std::string& name, t_real MMag, t_real x, t_real y, t_real z,
	t_real ReMx, t_real ReMy, t_real ReMz, t_real ImMx, t_real ImMy, t_real ImMz,
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
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
730
731
732
733
734
735
736
	t_real scale, const std::string& col)
{
	bool bclone = 0;
	m_ignoreChanges = 1;

	if(row == -1)	// append to end of table
		row = m_nuclei->rowCount();
	else if(row == -2 && m_iCursorRow >= 0)	// use row from member variable
		row = m_iCursorRow;
	else if(row == -3 && m_iCursorRow >= 0)	// use row from member variable +1
		row = m_iCursorRow + 1;
	else if(row == -4 && m_iCursorRow >= 0)	// use row from member variable +1
	{
		row = m_iCursorRow + 1;
		bclone = 1;
	}

	//bool sorting = m_nuclei->isSortingEnabled();
	m_nuclei->setSortingEnabled(false);
	m_nuclei->insertRow(row);

	if(bclone)
	{
		for(int thecol=0; thecol<NUM_COLS; ++thecol)
			m_nuclei->setItem(row, thecol, m_nuclei->item(m_iCursorRow, thecol)->clone());
	}
	else
	{
		m_nuclei->setItem(row, COL_NAME, new QTableWidgetItem(name.c_str()));
		m_nuclei->setItem(row, COL_M_MAG, new NumericTableWidgetItem<t_real>(MMag));
		m_nuclei->setItem(row, COL_X, new NumericTableWidgetItem<t_real>(x));
		m_nuclei->setItem(row, COL_Y, new NumericTableWidgetItem<t_real>(y));
		m_nuclei->setItem(row, COL_Z, new NumericTableWidgetItem<t_real>(z));
		m_nuclei->setItem(row, COL_ReM_X, new NumericTableWidgetItem<t_real>(ReMx));
		m_nuclei->setItem(row, COL_ReM_Y, new NumericTableWidgetItem<t_real>(ReMy));
		m_nuclei->setItem(row, COL_ReM_Z, new NumericTableWidgetItem<t_real>(ReMz));
		m_nuclei->setItem(row, COL_ImM_X, new NumericTableWidgetItem<t_real>(ImMx));
		m_nuclei->setItem(row, COL_ImM_Y, new NumericTableWidgetItem<t_real>(ImMy));
		m_nuclei->setItem(row, COL_ImM_Z, new NumericTableWidgetItem<t_real>(ImMz));
		m_nuclei->setItem(row, COL_RAD, new NumericTableWidgetItem<t_real>(scale));
		m_nuclei->setItem(row, COL_COL, new QTableWidgetItem(col.c_str()));
	}

	Add3DItem(row);

	m_nuclei->scrollToItem(m_nuclei->item(row, 0));
	m_nuclei->setCurrentCell(row, 0);

	m_nuclei->setSortingEnabled(/*sorting*/ true);

	m_ignoreChanges = 0;
	Calc();
}


/**
 * add 3d object
 */
void MagStructFactDlg::Add3DItem(int row)
{
	if(!m_plot) return;

	// add all items
	if(row < 0)
	{
		for(int row=0; row<m_nuclei->rowCount(); ++row)
			Add3DItem(row);
		return;
	}

Tobias WEBER's avatar
Tobias WEBER committed
737
738
739
740
	auto objSphere = m_plot->GetRenderer()->AddLinkedObject(m_sphere, 0,0,0, 1,1,1,1);
	//auto obj = m_plot->GetRenderer()->AddSphere(0.05, 0,0,0, 1,1,1,1);
	auto objArrowRe = m_plot->GetRenderer()->AddLinkedObject(m_arrow, 0,0,0, 1,1,1,1);
	auto objArrowIm = m_plot->GetRenderer()->AddLinkedObject(m_arrow, 0,0,0, 1,1,1,1);
741

742
743
744
	m_nuclei->item(row, COL_NAME)->setData(Qt::UserRole+0, unsigned(objSphere));	// atomic position
	m_nuclei->item(row, COL_NAME)->setData(Qt::UserRole+1, unsigned(objArrowRe));	// real part of Fourier comp
	m_nuclei->item(row, COL_NAME)->setData(Qt::UserRole+2, unsigned(objArrowIm));	// imag part of Fourier comp
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765

	Sync3DItem(row);
}


/**
 * sync the properties of a 3d object
 */
void MagStructFactDlg::Sync3DItem(int row)
{
	if(!m_plot) return;

	// sync all items
	if(row < 0)
	{
		for(int row=0; row<m_nuclei->rowCount(); ++row)
			Sync3DItem(row);
		return;
	}

	std::size_t objSphere = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+0).toUInt();
766
767
768
	std::size_t objArrowRe = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+1).toUInt();
	std::size_t objArrowIm = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+2).toUInt();
	if(!objSphere || !objArrowRe || !objArrowIm)
769
770
		return;

771
772
773
774
	auto *itemName = m_nuclei->item(row, COL_NAME);
	auto *itemx = m_nuclei->item(row, COL_X);
	auto *itemy = m_nuclei->item(row, COL_Y);
	auto *itemz = m_nuclei->item(row, COL_Z);
775
776
777
778
	auto *itemM = m_nuclei->item(row, COL_M_MAG);
	auto *itemReMX = m_nuclei->item(row, COL_ReM_X);
	auto *itemReMY = m_nuclei->item(row, COL_ReM_Y);
	auto *itemReMZ = m_nuclei->item(row, COL_ReM_Z);
779
780
781
	auto *itemImMX = m_nuclei->item(row, COL_ImM_X);
	auto *itemImMY = m_nuclei->item(row, COL_ImM_Y);
	auto *itemImMZ = m_nuclei->item(row, COL_ImM_Z);
782
783
784
	auto *itemsc = m_nuclei->item(row, COL_RAD);
	auto *itemCol = m_nuclei->item(row, COL_COL);

785
	t_real_gl posx=0, posy=0, posz=0, M=1, ReMX=0, ReMY=0, ReMZ=1, ImMX=0, ImMY=0, ImMZ=1, scale=1;
786
787
788
	std::istringstream{itemx->text().toStdString()} >> posx;
	std::istringstream{itemy->text().toStdString()} >> posy;
	std::istringstream{itemz->text().toStdString()} >> posz;
789
790
791
792
	std::istringstream{itemM->text().toStdString()} >> M;
	std::istringstream{itemReMX->text().toStdString()} >> ReMX;
	std::istringstream{itemReMY->text().toStdString()} >> ReMY;
	std::istringstream{itemReMZ->text().toStdString()} >> ReMZ;
793
794
795
	std::istringstream{itemImMX->text().toStdString()} >> ImMX;
	std::istringstream{itemImMY->text().toStdString()} >> ImMY;
	std::istringstream{itemImMZ->text().toStdString()} >> ImMZ;
796
797
798
799
800
801
	std::istringstream{itemsc->text().toStdString()} >> scale;

	qreal r=1, g=1, b=1;
	QColor col{itemCol->text()};
	col.getRgbF(&r, &g, &b);

Tobias WEBER's avatar
Tobias WEBER committed
802
803
	t_mat_gl matSphere = tl2::hom_translation<t_mat_gl>(posx, posy, posz) *
		tl2::hom_scaling<t_mat_gl>(M*scale, M*scale, M*scale);
804

Tobias WEBER's avatar
Tobias WEBER committed
805
806
807
808
	auto vecReM = tl2::create<t_vec_gl>({t_real_gl(ReMX), t_real_gl(ReMY), t_real_gl(ReMZ)});
	auto vecImM = tl2::create<t_vec_gl>({t_real_gl(ImMX), t_real_gl(ImMY), t_real_gl(ImMZ)});
	auto normReM = tl2::norm<t_vec_gl>(vecReM);
	auto normImM = tl2::norm<t_vec_gl>(vecImM);
809

Tobias WEBER's avatar
Tobias WEBER committed
810
	t_mat_gl matArrowRe = tl2::get_arrow_matrix<t_vec_gl, t_mat_gl, t_real_gl>(
811
812
		vecReM, 									// to
		1, 											// post-scale
Tobias WEBER's avatar
Tobias WEBER committed
813
814
		tl2::create<t_vec_gl>({0, 0, 0}),				// post-translate
		tl2::create<t_vec_gl>({0, 0, 1}),				// from
815
		M*scale, 									// pre-scale
Tobias WEBER's avatar
Tobias WEBER committed
816
		tl2::create<t_vec_gl>({posx, posy, posz})		// pre-translate
817
818
	);

Tobias WEBER's avatar
Tobias WEBER committed
819
	t_mat_gl matArrowIm = tl2::get_arrow_matrix<t_vec_gl, t_mat_gl, t_real_gl>(
820
		vecImM, 									// to
821
		1, 											// post-scale
Tobias WEBER's avatar
Tobias WEBER committed
822
823
		tl2::create<t_vec_gl>({0, 0, 0}),				// post-translate
		tl2::create<t_vec_gl>({0, 0, 1}),				// from
824
		M*scale, 									// pre-scale
Tobias WEBER's avatar
Tobias WEBER committed
825
		tl2::create<t_vec_gl>({posx, posy, posz})		// pre-translate
826
827
	);

Tobias WEBER's avatar
Tobias WEBER committed
828
829
830
831
832
833
834
835
836
	m_plot->GetRenderer()->SetObjectMatrix(objSphere, matSphere);
	m_plot->GetRenderer()->SetObjectMatrix(objArrowRe, matArrowRe);
	m_plot->GetRenderer()->SetObjectMatrix(objArrowIm, matArrowIm);
	m_plot->GetRenderer()->SetObjectLabel(objSphere, itemName->text().toStdString());
	m_plot->GetRenderer()->SetObjectCol(objSphere, r, g, b, 1);
	m_plot->GetRenderer()->SetObjectCol(objArrowRe, r, g, b, 1);
	m_plot->GetRenderer()->SetObjectCol(objArrowIm, 1.-r, 1.-g, 1.-b, 1);
	m_plot->GetRenderer()->SetObjectVisible(objArrowRe, !tl2::equals<t_real_gl>(normReM, 0, g_eps));
	m_plot->GetRenderer()->SetObjectVisible(objArrowIm, !tl2::equals<t_real_gl>(normImM, 0, g_eps));
837
838
839
840
841
842
843
844
845
846
847
848
849
850
	m_plot->update();
}


void MagStructFactDlg::DelTabItem(int begin, int end)
{
	m_ignoreChanges = 1;

	// if nothing is selected, clear all items
	if(begin == -1 || m_nuclei->selectedItems().count() == 0)
	{
		if(m_plot)
		{
			for(int row=0; row<m_nuclei->rowCount(); ++row)
851
852
			{
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+0).toUInt(); obj)
Tobias WEBER's avatar
Tobias WEBER committed
853
					m_plot->GetRenderer()->RemoveObject(obj);
854
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+1).toUInt(); obj)
Tobias WEBER's avatar
Tobias WEBER committed
855
					m_plot->GetRenderer()->RemoveObject(obj);
856
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+2).toUInt(); obj)
Tobias WEBER's avatar
Tobias WEBER committed
857
					m_plot->GetRenderer()->RemoveObject(obj);
858
			}
859
860
861
862
863
864
865
866
			m_plot->update();
		}

		m_nuclei->clearContents();
		m_nuclei->setRowCount(0);
	}
	else if(begin == -2)	// clear selected
	{
867
		for(int row : GetSelectedRows(m_nuclei, true))
868
869
870
871
		{
			// remove 3d object
			if(m_plot)
			{
872
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+0).toUInt(); obj)
Tobias WEBER's avatar
Tobias WEBER committed
873
					m_plot->GetRenderer()->RemoveObject(obj);
874
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+1).toUInt(); obj)
Tobias WEBER's avatar
Tobias WEBER committed
875
					m_plot->GetRenderer()->RemoveObject(obj);
876
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+2).toUInt(); obj)
Tobias WEBER's avatar
Tobias WEBER committed
877
					m_plot->GetRenderer()->RemoveObject(obj);
878
879
880
881
882
883
884
885
886
887
888
889
890
				m_plot->update();
			}

			m_nuclei->removeRow(row);
		}
	}
	else if(begin >= 0 && end >= 0)		// clear given range
	{
		for(int row=end-1; row>=begin; --row)
		{
			// remove 3d object
			if(m_plot)
			{
891
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+0).toUInt(); obj)
Tobias WEBER's avatar
Tobias WEBER committed
892
					m_plot->GetRenderer()->RemoveObject(obj);
893
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+1).toUInt(); obj)
Tobias WEBER's avatar
Tobias WEBER committed
894
					m_plot->GetRenderer()->RemoveObject(obj);
895
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+2).toUInt(); obj)
Tobias WEBER's avatar
Tobias WEBER committed
896
					m_plot->GetRenderer()->RemoveObject(obj);
897
898
899
900
901
902
903
904
905
906
907
908
				m_plot->update();
			}

			m_nuclei->removeRow(row);
		}
	}

	m_ignoreChanges = 0;
	Calc();
}


909
void MagStructFactDlg::MoveTabItemUp(QTableWidget *pTab)
910
911
{
	m_ignoreChanges = 1;
912
	pTab->setSortingEnabled(false);
913

914
	auto selected = GetSelectedRows(pTab, false);
915
916
917
918
919
	for(int row : selected)
	{
		if(row == 0)
			continue;

920
		auto *item = pTab->item(row, 0);
921
922
923
		if(!item || !item->isSelected())
			continue;

924
925
926
927
		pTab->insertRow(row-1);
		for(int col=0; col<pTab->columnCount(); ++col)
			pTab->setItem(row-1, col, pTab->item(row+1, col)->clone());
		pTab->removeRow(row+1);
928
929
	}

930
	for(int row=0; row<pTab->rowCount(); ++row)
931
	{
932
		if(auto *item = pTab->item(row, 0);
933
934
			item && std::find(selected.begin(), selected.end(), row+1) != selected.end())
		{
935
936
			for(int col=0; col<pTab->columnCount(); ++col)
				pTab->item(row, col)->setSelected(true);
937
938
939
940
941
942
943
		}
	}

	m_ignoreChanges = 0;
}


944
void MagStructFactDlg::MoveTabItemDown(QTableWidget *pTab)
945
946
{
	m_ignoreChanges = 1;
947
	pTab->setSortingEnabled(false);
948

949
	auto selected = GetSelectedRows(pTab, true);
950
951
	for(int row : selected)
	{
952
		if(row == pTab->rowCount()-1)
953
954
			continue;

955
		auto *item = pTab->item(row, 0);
956
957
958
		if(!item || !item->isSelected())
			continue;

959
960
961
962
		pTab->insertRow(row+2);
		for(int col=0; col<pTab->columnCount(); ++col)
			pTab->setItem(row+2, col, pTab->item(row, col)->clone());
		pTab->removeRow(row);
963
964
	}

965
	for(int row=0; row<pTab->rowCount(); ++row)
966
	{
967
		if(auto *item = pTab->item(row, 0);
968
969
			item && std::find(selected.begin(), selected.end(), row-1) != selected.end())
		{
970
971
			for(int col=0; col<pTab->columnCount(); ++col)
				pTab->item(row, col)->setSelected(true);
972
973
974
975
976
977
978
979
980
981
982
		}
	}

	m_ignoreChanges = 0;
}
// ----------------------------------------------------------------------------




// ----------------------------------------------------------------------------
983
std::vector<int> MagStructFactDlg::GetSelectedRows(QTableWidget *pTab, bool sort_reversed) const
984
985
{
	std::vector<int> vec;
986
	vec.reserve(pTab->selectedItems().size());
987

988
	for(int row=0; row<pTab->rowCount(); ++row)
989
	{
990
		if(auto *item = pTab->item(row, 0); item && item->isSelected())
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
			vec.push_back(row);
	}

	if(sort_reversed)
	{
		std::stable_sort(vec.begin(), vec.end(), [](int row1, int row2)
		{ return row1 > row2; });
	}

	return vec;
}


/**
 * selected a new row
 */
void MagStructFactDlg::TableCurCellChanged(int rowNew, int colNew, int rowOld, int colOld)
1008
{}
1009
1010
1011
1012
1013
1014


/**
 * hovered over new row
 */
void MagStructFactDlg::TableCellEntered(const QModelIndex& idx)
1015
{}
1016
1017
1018
1019
1020
1021
1022
1023


/**
 * item contents changed
 */
void MagStructFactDlg::TableItemChanged(QTableWidgetItem *item)
{
	// update associated 3d object
1024
	Sync3DItem(item->row());
1025
1026
1027
1028
1029
1030

	if(!m_ignoreChanges)
		Calc();
}


1031
void MagStructFactDlg::ShowTableContextMenu(QTableWidget *pTab, QMenu *pMenu, QMenu *pMenuNoItem, const QPoint& pt)
1032
{
1033
	auto ptGlob = pTab->mapToGlobal(pt);
1034

1035
	if(const auto* item = pTab->itemAt(pt); item)
1036
1037
	{
		m_iCursorRow = item->row();
1038
1039
		ptGlob.setY(ptGlob.y() + pMenu->sizeHint().height()/2);
		pMenu->popup(ptGlob);
1040
1041
1042
	}
	else
	{
1043
1044
1045
1046
1047
1048
1049
1050
1051
		ptGlob.setY(ptGlob.y() + pMenuNoItem->sizeHint().height()/2);
		pMenuNoItem->popup(ptGlob);
	}
}
// ----------------------------------------------------------------------------



// ----------------------------------------------------------------------------
1052
void MagStructFactDlg::AddPropItem(int row,
Tobias WEBER's avatar
Tobias WEBER committed
1053
	const std::string& name, t_real x, t_real y, t_real z, bool bConjFC)
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
{
	bool bclone = 0;
	m_ignoreChanges = 1;

	if(row == -1)	// append to end of table
		row = m_propvecs->rowCount();
	else if(row == -2 && m_iCursorRow >= 0)	// use row from member variable
		row = m_iCursorRow;
	else if(row == -3 && m_iCursorRow >= 0)	// use row from member variable +1
		row = m_iCursorRow + 1;
	else if(row == -4 && m_iCursorRow >= 0)	// use row from member variable +1
	{
		row = m_iCursorRow + 1;
		bclone = 1;
	}

	//bool sorting = m_propvecs->isSortingEnabled();
	m_propvecs->setSortingEnabled(false);
	m_propvecs->insertRow(row);

	if(bclone)
	{
		for(int thecol=0; thecol<PROP_NUM_COLS; ++thecol)
			m_propvecs->setItem(row, thecol, m_propvecs->item(m_iCursorRow, thecol)->clone());
	}
	else
	{
		m_propvecs->setItem(row, PROP_COL_NAME, new QTableWidgetItem(name.c_str()));
		m_propvecs->setItem(row, PROP_COL_X, new NumericTableWidgetItem<t_real>(x));
		m_propvecs->setItem(row, PROP_COL_Y, new NumericTableWidgetItem<t_real>(y));
		m_propvecs->setItem(row, PROP_COL_Z, new NumericTableWidgetItem<t_real>(z));
Tobias WEBER's avatar
Tobias WEBER committed
1085
		m_propvecs->setItem(row, PROP_COL_CONJ, new NumericTableWidgetItem<int>(bConjFC));
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
	}

	//Add3DItem(row);	TODO

	m_propvecs->scrollToItem(m_propvecs->item(row, 0));
	m_propvecs->setCurrentCell(row, 0);

	m_propvecs->setSortingEnabled(/*sorting*/ true);

	m_ignoreChanges = 0;
	Calc();
}


void MagStructFactDlg::DelPropItem(int begin, int end)
{
	m_ignoreChanges = 1;

	// if nothing is selected, clear all items
	if(begin == -1 || m_propvecs->selectedItems().count() == 0)
	{
		m_propvecs->clearContents();
		m_propvecs->setRowCount(0);
	}
	else if(begin == -2)	// clear selected
	{
		for(int row : GetSelectedRows(m_propvecs, true))
			m_propvecs->removeRow(row);
1114
	}
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
	else if(begin >= 0 && end >= 0)		// clear given range
	{
		for(int row=end-1; row>=begin; --row)
			m_propvecs->removeRow(row);
	}

	m_ignoreChanges = 0;
	Calc();
}


/**
 * item contents changed
 */
void MagStructFactDlg::PropItemChanged(QTableWidgetItem *item)
{
	// update associated 3d object
	//Sync3DItem(item->row());

	if(!m_ignoreChanges)
		Calc();
1136
1137
1138
1139
1140
1141
1142
1143
1144
}
// ----------------------------------------------------------------------------




// ----------------------------------------------------------------------------
void MagStructFactDlg::Load()
{
1145
1146
	m_ignoreCalc = 1;

1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
	try
	{
		QString dirLast = m_sett->value("dir", "").toString();
		QString filename = QFileDialog::getOpenFileName(this, "Load File", dirLast, "XML Files (*.xml *.XML)");
		if(filename=="" || !QFile::exists(filename))
			return;
		m_sett->setValue("dir", QFileInfo(filename).path());


		pt::ptree node;

		std::ifstream ifstr{filename.toStdString()};
		pt::read_xml(ifstr, node);

		// check signature
Tobias WEBER's avatar
Tobias WEBER committed
1162
1163
		if(auto optInfo = node.get_optional<std::string>("sfact.meta.info");
			!optInfo || !(*optInfo==std::string{"magsfact_tool"} || *optInfo==std::string{"sfact_tool"}))
1164
1165
1166
1167
		{
			QMessageBox::critical(this, "Structure Factors", "Unrecognised file format.");
			return;
		}
Tobias WEBER's avatar
Tobias WEBER committed
1168
1169
1170
1171
		else if(*optInfo == std::string{"sfact_tool"})
		{
			QMessageBox::warning(this, "Structure Factors", "File only contains nuclear information. Trying to load.");
		}
1172
1173


1174
		// clear old tables
1175
		DelTabItem(-1);
1176
		DelPropItem(-1);
1177

1178

1179
		// lattice
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
		if(auto opt = node.get_optional<t_real>("sfact.xtal.a"); opt)
		{
			std::ostringstream ostr; ostr.precision(g_prec); ostr << *opt;
			m_editA->setText(ostr.str().c_str());
		}
		if(auto opt = node.get_optional<t_real>("sfact.xtal.b"); opt)
		{
			std::ostringstream ostr; ostr.precision(g_prec); ostr << *opt;
			m_editB->setText(ostr.str().c_str());
		}
		if(auto opt = node.get_optional<t_real>("sfact.xtal.c"); opt)
		{
			std::ostringstream ostr; ostr.precision(g_prec); ostr << *opt;
			m_editC->setText(ostr.str().c_str());
		}
		if(auto opt = node.get_optional<t_real>("sfact.xtal.alpha"); opt)
		{
			std::ostringstream ostr; ostr.precision(g_prec); ostr << *opt;
			m_editAlpha->setText(ostr.str().c_str());
		}
		if(auto opt = node.get_optional<t_real>("sfact.xtal.beta"); opt)
		{
			std::ostringstream ostr; ostr.precision(g_prec); ostr << *opt;
			m_editBeta->setText(ostr.str().c_str());
		}
		if(auto opt = node.get_optional<t_real>("sfact.xtal.gamma"); opt)
		{
			std::ostringstream ostr; ostr.precision(g_prec); ostr << *opt;
			m_editGamma->setText(ostr.str().c_str());
		}
		if(auto opt = node.get_optional<int>("sfact.order"); opt)
		{
			m_maxBZ->setValue(*opt);
		}
Tobias WEBER's avatar
Tobias WEBER committed
1214
1215
1216
1217
1218
		if(auto opt = node.get_optional<int>("sfact.scorder_x"); opt)
		{
			m_maxSC[0]->setValue(*opt);
		}
		if(auto opt = node.get_optional<int>("sfact.scorder_y"); opt)
1219
		{
Tobias WEBER's avatar
Tobias WEBER committed
1220
1221
1222
1223
1224
			m_maxSC[1]->setValue(*opt);
		}
		if(auto opt = node.get_optional<int>("sfact.scorder_z"); opt)
		{
			m_maxSC[2]->setValue(*opt);
1225
1226
1227
1228
1229
		}
		if(auto opt = node.get_optional<int>("sfact.removezeroes"); opt)
		{
			m_RemoveZeroes->setChecked(*opt != 0);
		}
1230
1231
1232
1233
1234
		if(auto opt = node.get_optional<int>("sfact.sg_idx"); opt)
		{
			m_comboSG->setCurrentIndex(*opt);
		}

1235
1236

		// fourier components
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
		if(auto nuclei = node.get_child_optional("sfact.nuclei"); nuclei)
		{
			for(const auto &nucl : *nuclei)
			{
				auto optName = nucl.second.get<std::string>("name", "n/a");
				auto optMMag = nucl.second.get<t_real>("M_mag", 1.);
				auto optX = nucl.second.get<t_real>("x", 0.);
				auto optY = nucl.second.get<t_real>("y", 0.);
				auto optZ = nucl.second.get<t_real>("z", 0.);
				auto optReMX = nucl.second.get<t_real>("ReMx", 0.);
				auto optReMY = nucl.second.get<t_real>("ReMy", 0.);
				auto optReMZ = nucl.second.get<t_real>("ReMz", 0.);
				auto optImMX = nucl.second.get<t_real>("ImMx", 0.);
				auto optImMY = nucl.second.get<t_real>("ImMy", 0.);
				auto optImMZ = nucl.second.get<t_real>("ImMz", 0.);
				auto optRad = nucl.second.get<t_real>("rad", 1.);
				auto optCol = nucl.second.get<std::string>("col", "#ff0000");

				AddTabItem(-1, optName, optMMag, optX,  optY, optZ,
1256
					optReMX, optReMY, optReMZ, optImMX, optImMY, optImMZ,
1257
1258
1259
					optRad, optCol);
			}
		}
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270


		// propagation vectors
		if(auto propvecs = node.get_child_optional("sfact.propvecs"); propvecs)
		{
			for(const auto &propvec : *propvecs)
			{
				auto optName = propvec.second.get<std::string>("name", "n/a");
				auto optX = propvec.second.get<t_real>("x", 0.);
				auto optY = propvec.second.get<t_real>("y", 0.);
				auto optZ = propvec.second.get<t_real>("z", 0.);
Tobias WEBER's avatar
Tobias WEBER committed
1271
				auto optConj = propvec.second.get<int>("conjFC", 0);
1272

Tobias WEBER's avatar
Tobias WEBER committed
1273
				AddPropItem(-1, optName, optX,  optY, optZ, optConj!=0);
1274
1275
			}
		}
1276
1277
1278
1279
1280
	}
	catch(const std::exception& ex)
	{
		QMessageBox::critical(this, "Structure Factors", ex.what());
	}
1281
1282
1283
1284
1285


	m_ignoreCalc = 0;
	CalcB(false);
	Calc();
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
}


void MagStructFactDlg::Save()
{
	QString dirLast = m_sett->value("dir", "").toString();
	QString filename = QFileDialog::getSaveFileName(this, "Save File", dirLast, "XML Files (*.xml *.XML)");
	if(filename=="")
		return;
	m_sett->setValue("dir", QFileInfo(filename).path());


	pt::ptree node;
Tobias WEBER's avatar
Tobias WEBER committed
1299
	node.put<std::string>("sfact.meta.info", "magsfact_tool");
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
	node.put<std::string>("sfact.meta.date", tl2::epoch_to_str<t_real>(tl2::epoch<t_real>()));


	// lattice
	t_real a,b,c, alpha,beta,gamma;
	std::istringstream{m_editA->text().toStdString()} >> a;
	std::istringstream{m_editB->text().toStdString()} >> b;
	std::istringstream{m_editC->text().toStdString()} >> c;
	std::istringstream{m_editAlpha->text().toStdString()} >> alpha;
	std::istringstream{m_editBeta->text().toStdString()} >> beta;
	std::istringstream{m_editGamma->text().toStdString()} >> gamma;

	node.put<t_real>("sfact.xtal.a", a);
	node.put<t_real>("sfact.xtal.b", b);
	node.put<t_real>("sfact.xtal.c", c);
	node.put<t_real>("sfact.xtal.alpha", alpha);
	node.put<t_real>("sfact.xtal.beta", beta);
	node.put<t_real>("sfact.xtal.gamma", gamma);
	node.put<int>("sfact.order", m_maxBZ->value());
Tobias WEBER's avatar
Tobias WEBER committed
1319
1320
1321
	node.put<int>("sfact.scorder_x", m_maxSC[0]->value());
	node.put<int>("sfact.scorder_y", m_maxSC[1]->value());
	node.put<int>("sfact.scorder_z", m_maxSC[2]->value());
1322
	node.put<int>("sfact.removezeroes", m_RemoveZeroes->isChecked());
1323
1324
	node.put<int>("sfact.sg_idx", m_comboSG->currentIndex());

1325
1326

	// fourier component list
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
	for(int row=0; row<m_nuclei->rowCount(); ++row)
	{
		t_real MMag{}, x{},y{},z{}, ReMx{}, ReMy{}, ReMz{}, ImMx{}, ImMy{}, ImMz{}, scale{};
		std::istringstream{m_nuclei->item(row, COL_M_MAG)->text().toStdString()} >> MMag;
		std::istringstream{m_nuclei->item(row, COL_X)->text().toStdString()} >> x;
		std::istringstream{m_nuclei->item(row, COL_Y)->text().toStdString()} >> y;
		std::istringstream{m_nuclei->item(row, COL_Z)->text().toStdString()} >> z;
		std::istringstream{m_nuclei->item(row, COL_ReM_X)->text().toStdString()} >> ReMx;
		std::istringstream{m_nuclei->item(row, COL_ReM_Y)->text().toStdString()} >> ReMy;
		std::istringstream{m_nuclei->item(row, COL_ReM_Z)->text().toStdString()} >> ReMz;
		std::istringstream{m_nuclei->item(row, COL_ImM_X)->text().toStdString()} >> ImMx;
		std::istringstream{m_nuclei->item(row, COL_ImM_Y)->text().toStdString()} >> ImMy;
		std::istringstream{m_nuclei->item(row, COL_ImM_Z)->text().toStdString()} >> ImMz;
		std::istringstream{m_nuclei->item(row, COL_RAD)->text().toStdString()} >> scale;

		pt::ptree itemNode;
		itemNode.put<std::string>("name", m_nuclei->item(row, COL_NAME)->text().toStdString());
		itemNode.put<t_real>("M_mag", MMag);
		itemNode.put<t_real>("x", x);
		itemNode.put<t_real>("y", y);
		itemNode.put<t_real>("z", z);
		itemNode.put<t_real>("ReMx", ReMx);
		itemNode.put<t_real>("ReMy", ReMy);
		itemNode.put<t_real>("ReMz", ReMz);
		itemNode.put<t_real>("ImMx", ImMx);
		itemNode.put<t_real>("ImMy", ImMy);
		itemNode.put<t_real>("ImMz", ImMz);
		itemNode.put<t_real>("rad", scale);
		itemNode.put<std::string>("col", m_nuclei->item(row, COL_COL)->text().toStdString());

		node.add_child("sfact.nuclei.nucleus", itemNode);
	}

1360
1361
1362
1363
1364

	// propagation vectors list
	for(int row=0; row<m_propvecs->rowCount(); ++row)
	{
		t_real x{},y{},z{};
Tobias WEBER's avatar
Tobias WEBER committed
1365
		int iConj{0};
1366
1367
1368
		std::istringstream{m_propvecs->item(row, PROP_COL_X)->text().toStdString()} >> x;
		std::istringstream{m_propvecs->item(row, PROP_COL_Y)->text().toStdString()} >> y;
		std::istringstream{m_propvecs->item(row, PROP_COL_Z)->text().toStdString()} >> z;
Tobias WEBER's avatar
Tobias WEBER committed
1369
		std::istringstream{m_propvecs->item(row, PROP_COL_CONJ)->text().toStdString()} >> iConj;
1370
1371
1372
1373
1374
1375

		pt::ptree itemNode;
		itemNode.put<std::string>("name", m_propvecs->item(row, PROP_COL_NAME)->text().toStdString());
		itemNode.put<t_real>("x", x);
		itemNode.put<t_real>("y", y);
		itemNode.put<t_real>("z", z);
Tobias WEBER's avatar
Tobias WEBER committed
1376
		itemNode.put<int>("conjFC", iConj);