PathsTool.cpp 63.2 KB
Newer Older
Tobias WEBER's avatar
Tobias WEBER committed
1
/**
Tobias WEBER's avatar
Tobias WEBER committed
2
 * TAS path tool, main window
Tobias WEBER's avatar
Tobias WEBER committed
3
 * @author Tobias Weber <tweber@ill.fr>
4
 * @date February-November 2021
Tobias WEBER's avatar
comment    
Tobias WEBER committed
5
 * @note some code forked on 15-nov-2021 from my private "qm" project: https://github.com/t-weber/qm
Tobias WEBER's avatar
Tobias WEBER committed
6
 * @license GPLv3, see 'LICENSE' file
7
8
9
 *
 * ----------------------------------------------------------------------------
 * TAS-Paths (part of the Takin software suite)
Tobias WEBER's avatar
Tobias WEBER committed
10
 * Copyright (C) 2021  Tobias WEBER (Institut Laue-Langevin (ILL),
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *                     Grenoble, France).
 *
 * 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.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * ----------------------------------------------------------------------------
Tobias WEBER's avatar
Tobias WEBER committed
25
26
 */

Tobias WEBER's avatar
Tobias WEBER committed
27
28
#include "PathsTool.h"

29
30
#include <QtCore/QMetaObject>
#include <QtCore/QThread>
Tobias WEBER's avatar
Tobias WEBER committed
31
32
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QMessageBox>
Tobias WEBER's avatar
Tobias WEBER committed
33
#include <QtWidgets/QFileDialog>
34
#include <QtGui/QDesktopServices>
Tobias WEBER's avatar
Tobias WEBER committed
35

Tobias WEBER's avatar
Tobias WEBER committed
36
#include <boost/scope_exit.hpp>
Tobias WEBER's avatar
Tobias WEBER committed
37
38
39
40
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
namespace pt = boost::property_tree;

41
#include "settings_variables.h"
Tobias WEBER's avatar
Tobias WEBER committed
42
#include "src/libs/proc.h"
Tobias WEBER's avatar
Tobias WEBER committed
43
#include "tlibs2/libs/maths.h"
Tobias WEBER's avatar
Tobias WEBER committed
44
#include "tlibs2/libs/str.h"
Tobias WEBER's avatar
Tobias WEBER committed
45
46
#include "tlibs2/libs/file.h"
#include "tlibs2/libs/algos.h"
Tobias WEBER's avatar
Tobias WEBER committed
47
48
49
#include "tlibs2/libs/helper.h"


Tobias WEBER's avatar
Tobias WEBER committed
50
51
52
53
54
55
56
#if defined(__MINGW32__) || defined(__MINGW64__)
	#define EXEC_EXTENSION ".exe"
#else
	#define EXEC_EXTENSION ""
#endif


57
58
59
/**
 * event signalling that the crystal UB matrix needs an update
 */
Tobias WEBER's avatar
Tobias WEBER committed
60
61
void PathsTool::UpdateUB()
{
62
	m_tascalc.UpdateUB();
63
64

	if(m_xtalInfos)
65
		m_xtalInfos->GetWidget()->SetUB(m_tascalc.GetB(), m_tascalc.GetUB());
Tobias WEBER's avatar
Tobias WEBER committed
66
}
Tobias WEBER's avatar
Tobias WEBER committed
67
68


69
70
71
/**
 * the window is being shown
 */
Tobias WEBER's avatar
Tobias WEBER committed
72
73
74
void PathsTool::showEvent(QShowEvent *evt)
{
	m_renderer->EnableTimer(true);
75

Tobias WEBER's avatar
Tobias WEBER committed
76
77
78
79
	QMainWindow::showEvent(evt);
}


80
81
82
/**
 * the window is being hidden
 */
Tobias WEBER's avatar
Tobias WEBER committed
83
84
85
void PathsTool::hideEvent(QHideEvent *evt)
{
	m_renderer->EnableTimer(false);
86

Tobias WEBER's avatar
Tobias WEBER committed
87
88
89
90
	QMainWindow::hideEvent(evt);
}


91
92
93
/**
 * the window is being closed
 */
Tobias WEBER's avatar
Tobias WEBER committed
94
void PathsTool::closeEvent(QCloseEvent *evt)
Tobias WEBER's avatar
Tobias WEBER committed
95
96
97
98
{
	// save window size, position, and state
	m_sett.setValue("geo", saveGeometry());
	m_sett.setValue("state", saveState());
Tobias WEBER's avatar
Tobias WEBER committed
99

100
101
	m_recent.TrimEntries();
	m_sett.setValue("recent_files", m_recent.GetRecentFiles());
Tobias WEBER's avatar
Tobias WEBER committed
102
103

	QMainWindow::closeEvent(evt);
Tobias WEBER's avatar
Tobias WEBER committed
104
}
Tobias WEBER's avatar
Tobias WEBER committed
105

Tobias WEBER's avatar
Tobias WEBER committed
106

107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/**
 * accept a file dropped onto the main window
 * @see https://doc.qt.io/qt-5/dnd.html
 */
void PathsTool::dragEnterEvent(QDragEnterEvent *evt)
{
	// accept urls
	if(evt->mimeData()->hasUrls())
		evt->accept();

	QMainWindow::dragEnterEvent(evt);
}


/**
 * accept a file dropped onto the main window
 * @see https://doc.qt.io/qt-5/dnd.html
 */
void PathsTool::dropEvent(QDropEvent *evt)
{
	// get mime data dropped on the main window
	if(const QMimeData* dat = evt->mimeData(); dat && dat->hasUrls())
	{
		// get the list of urls dropped on the main window
		if(QList<QUrl> urls = dat->urls(); urls.size() > 0)
		{
			// use the first url for the file name
			QString filename = urls.begin()->path();

			// load the dropped file
			if(QFile::exists(filename))
				OpenFile(filename);
		}
	}

	QMainWindow::dropEvent(evt);
}



Tobias WEBER's avatar
Tobias WEBER committed
147
148
149
150
151
152
153
/**
 * File -> New
 */
void PathsTool::NewFile()
{
	SetCurrentFile("");
	m_instrspace.Clear();
154
	ValidatePathMesh(false);
155
156
157
158
159

	if(m_dlgGeoBrowser)
		m_dlgGeoBrowser->UpdateGeoTree(m_instrspace);
	if(m_renderer)
		m_renderer->LoadInstrument(m_instrspace);
Tobias WEBER's avatar
Tobias WEBER committed
160
}
Tobias WEBER's avatar
Tobias WEBER committed
161

Tobias WEBER's avatar
Tobias WEBER committed
162

Tobias WEBER's avatar
Tobias WEBER committed
163
164
165
166
167
/**
 * File -> Open
 */
void PathsTool::OpenFile()
{
168
	QString dirLast = m_sett.value("cur_dir", "~/").toString();
Tobias WEBER's avatar
Tobias WEBER committed
169

Tobias WEBER's avatar
Tobias WEBER committed
170
171
172
173
174
175
176
177
178
179
180
	QFileDialog filedlg(this, "Open File", dirLast,
		"TAS-Paths Files (*.taspaths)");
	filedlg.setAcceptMode(QFileDialog::AcceptOpen);
	filedlg.setDefaultSuffix("taspaths");
	filedlg.setFileMode(QFileDialog::AnyFile);

	if(!filedlg.exec())
		return;

	QStringList files = filedlg.selectedFiles();
	if(!files.size() || files[0]=="" || !QFile::exists(files[0]))
Tobias WEBER's avatar
Tobias WEBER committed
181
		return;
Tobias WEBER's avatar
Tobias WEBER committed
182

Tobias WEBER's avatar
Tobias WEBER committed
183
184
	if(OpenFile(files[0]))
		m_sett.setValue("cur_dir", QFileInfo(files[0]).path());
Tobias WEBER's avatar
Tobias WEBER committed
185
}
Tobias WEBER's avatar
Tobias WEBER committed
186
187


Tobias WEBER's avatar
Tobias WEBER committed
188
189
190
191
192
/**
 * File -> Save
 */
void PathsTool::SaveFile()
{
Tobias WEBER's avatar
Tobias WEBER committed
193
	if(m_recent.GetCurFile() == "")
Tobias WEBER's avatar
Tobias WEBER committed
194
195
		SaveFileAs();
	else
Tobias WEBER's avatar
Tobias WEBER committed
196
		SaveFile(m_recent.GetCurFile());
Tobias WEBER's avatar
Tobias WEBER committed
197
}
Tobias WEBER's avatar
Tobias WEBER committed
198

Tobias WEBER's avatar
Tobias WEBER committed
199

Tobias WEBER's avatar
Tobias WEBER committed
200
201
202
203
204
/**
 * File -> Save As
 */
void PathsTool::SaveFileAs()
{
205
	QString dirLast = m_sett.value("cur_dir", "~/").toString();
Tobias WEBER's avatar
Tobias WEBER committed
206

Tobias WEBER's avatar
Tobias WEBER committed
207
208
209
210
211
212
213
214
215
216
	QFileDialog filedlg(this, "Open File", dirLast,
		"TAS-Paths Files (*.taspaths)");
	filedlg.setAcceptMode(QFileDialog::AcceptSave);
	filedlg.setDefaultSuffix("taspaths");
	filedlg.setFileMode(QFileDialog::AnyFile);

	if(!filedlg.exec())
		return;

	QStringList files = filedlg.selectedFiles();
217
	if(!files.size() || files[0]=="")
Tobias WEBER's avatar
Tobias WEBER committed
218
		return;
Tobias WEBER's avatar
Tobias WEBER committed
219

Tobias WEBER's avatar
Tobias WEBER committed
220
221
	if(SaveFile(files[0]))
		m_sett.setValue("cur_dir", QFileInfo(files[0]).path());
Tobias WEBER's avatar
Tobias WEBER committed
222
}
Tobias WEBER's avatar
Tobias WEBER committed
223
224


225
226
227
228
229
230
231
/**
 * File -> Save Screenshot
 */
void PathsTool::SaveScreenshot()
{
	QString dirLast = m_sett.value("cur_dir", "~/").toString();

Tobias WEBER's avatar
Tobias WEBER committed
232
233
234
235
236
237
238
239
240
241
	QFileDialog filedlg(this, "Save Screenshot", dirLast,
		"PNG Images (*.png);;JPEG Images (*.jpg)");
	filedlg.setAcceptMode(QFileDialog::AcceptSave);
	filedlg.setDefaultSuffix("png");
	filedlg.setFileMode(QFileDialog::AnyFile);

	if(!filedlg.exec())
		return;

	QStringList files = filedlg.selectedFiles();
242
	if(!files.size() || files[0]=="")
243
244
245
246
		return;

	bool ok = false;
	if(g_combined_screenshots)
Tobias WEBER's avatar
Tobias WEBER committed
247
		ok = SaveCombinedScreenshot(files[0]);
248
	else
Tobias WEBER's avatar
Tobias WEBER committed
249
		ok = SaveScreenshot(files[0]);
250
251

	if(ok)
Tobias WEBER's avatar
Tobias WEBER committed
252
		m_sett.setValue("cur_dir", QFileInfo(files[0]).path());
253
254
255
}


256
257
258
259
260
261
262
263
/**
 * File -> Export Path
 */
bool PathsTool::ExportPath(PathsExporterFormat fmt)
{
	std::shared_ptr<PathsExporterBase> exporter;

	QString dirLast = m_sett.value("cur_dir", "~/").toString();
Tobias WEBER's avatar
Tobias WEBER committed
264
265
266
267
268
269
270
271
272
273
274

	QFileDialog filedlg(this, "Export Path", dirLast,
		"Text Files (*.txt)");
	filedlg.setAcceptMode(QFileDialog::AcceptSave);
	filedlg.setDefaultSuffix("txt");
	filedlg.setFileMode(QFileDialog::AnyFile);

	if(!filedlg.exec())
		return false;

	QStringList files = filedlg.selectedFiles();
275
	if(!files.size() || files[0]=="")
276
277
278
279
280
		return false;

	switch(fmt)
	{
		case PathsExporterFormat::RAW:
Tobias WEBER's avatar
Tobias WEBER committed
281
			exporter = std::make_shared<PathsExporterRaw>(files[0].toStdString());
282
283
			break;
		case PathsExporterFormat::NOMAD:
Tobias WEBER's avatar
Tobias WEBER committed
284
			exporter = std::make_shared<PathsExporterNomad>(files[0].toStdString());
285
286
			break;
		case PathsExporterFormat::NICOS:
Tobias WEBER's avatar
Tobias WEBER committed
287
			exporter = std::make_shared<PathsExporterNicos>(files[0].toStdString());
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
			break;
	}

	if(!exporter)
	{
		QMessageBox::critical(this, "Error", "No path is available.");
		return false;
	}

	if(!m_pathsbuilder.AcceptExporter(exporter.get(), m_pathvertices, true))
	{
		QMessageBox::critical(this, "Error", "path could not be exported.");
		return false;
	}

Tobias WEBER's avatar
Tobias WEBER committed
303
	m_sett.setValue("cur_dir", QFileInfo(files[0]).path());
304
305
306
307
	return true;
}


Tobias WEBER's avatar
Tobias WEBER committed
308
309
310
311
312
313
/**
 * load file
 */
bool PathsTool::OpenFile(const QString &file)
{
	try
Tobias WEBER's avatar
Tobias WEBER committed
314
	{
315
		NewFile();
316
317

		// get property tree
Tobias WEBER's avatar
Tobias WEBER committed
318
		if(file == "" || !QFile::exists(file))
319
		{
320
			QMessageBox::critical(this, "Error",
Tobias WEBER's avatar
Tobias WEBER committed
321
				"Instrument file \"" + file + "\" does not exist.");
322
323
324
325
			return false;
		}

		// open xml
Tobias WEBER's avatar
Tobias WEBER committed
326
		std::string filename = file.toStdString();
327
328
329
		std::ifstream ifstr{filename};
		if(!ifstr)
		{
330
			QMessageBox::critical(this, "Error",
331
332
333
334
335
336
337
338
339
340
341
				("Could not read instrument file \"" + filename + "\".").c_str());
			return false;
		}

		// read xml
		pt::ptree prop;
		pt::read_xml(ifstr, prop);
		// check format and version
		if(auto opt = prop.get_optional<std::string>(FILE_BASENAME "ident");
			!opt || *opt != PROG_IDENT)
		{
342
343
			QMessageBox::critical(this, "Error",
				("Instrument file \"" + filename +
344
345
346
347
				"\" has invalid identifier.").c_str());
			return false;
		}

Tobias WEBER's avatar
Tobias WEBER committed
348

349
		// load instrument definition file
Tobias WEBER's avatar
Tobias WEBER committed
350
		if(auto [instrok, msg] =
351
			InstrumentSpace::load(prop, m_instrspace, &filename); !instrok)
Tobias WEBER's avatar
Tobias WEBER committed
352
		{
353
			QMessageBox::critical(this, "Error", msg.c_str());
Tobias WEBER's avatar
Tobias WEBER committed
354
355
356
			return false;
		}
		else
Tobias WEBER's avatar
Tobias WEBER committed
357
		{
358
359
360
			std::ostringstream ostr;
			ostr << "Loaded \"" << QFileInfo{file}.fileName().toStdString() << "\" "
				<< "dated " << msg << ".";
361
362

			SetTmpStatus(ostr.str());
Tobias WEBER's avatar
Tobias WEBER committed
363
364
		}

365
366
367
368
369
370
371
372
373
374
375
376
377
378

		// load dock window settings
		if(auto prop_dock = prop.get_child_optional(FILE_BASENAME "configuration.tas"); prop_dock)
			m_tasProperties->GetWidget()->Load(*prop_dock);
		if(auto prop_dock = prop.get_child_optional(FILE_BASENAME "configuration.crystal"); prop_dock)
			m_xtalProperties->GetWidget()->Load(*prop_dock);
		if(auto prop_dock = prop.get_child_optional(FILE_BASENAME "configuration.coordinates"); prop_dock)
			m_coordProperties->GetWidget()->Load(*prop_dock);
		if(auto prop_dock = prop.get_child_optional(FILE_BASENAME "configuration.path"); prop_dock)
			m_pathProperties->GetWidget()->Load(*prop_dock);
		if(auto prop_dock = prop.get_child_optional(FILE_BASENAME "configuration.camera"); prop_dock)
			m_camProperties->GetWidget()->Load(*prop_dock);


Tobias WEBER's avatar
Tobias WEBER committed
379
		SetCurrentFile(file);
380
		m_recent.AddRecentFile(file);
Tobias WEBER's avatar
Tobias WEBER committed
381

382
383
384
385
		if(m_dlgGeoBrowser)
			m_dlgGeoBrowser->UpdateGeoTree(m_instrspace);
		if(m_renderer)
			m_renderer->LoadInstrument(m_instrspace);
Tobias WEBER's avatar
Tobias WEBER committed
386

387

Tobias WEBER's avatar
Tobias WEBER committed
388
389
390
391
		// update slot for instrument space (e.g. walls) changes
		m_instrspace.AddUpdateSlot(
			[this](const InstrumentSpace& instrspace)
			{
392
393
394
				// invalidate the mesh
				ValidatePathMesh(false);

Tobias WEBER's avatar
Tobias WEBER committed
395
396
397
398
399
				if(m_renderer)
					m_renderer->UpdateInstrumentSpace(instrspace);
			});

		// update slot for instrument movements
Tobias WEBER's avatar
Tobias WEBER committed
400
		m_instrspace.GetInstrument().AddUpdateSlot(
Tobias WEBER's avatar
Tobias WEBER committed
401
			[this](const Instrument& instr)
Tobias WEBER's avatar
Tobias WEBER committed
402
			{
Tobias WEBER's avatar
Tobias WEBER committed
403
404
405
406
407
408
				if(&instr != &m_instrspace.GetInstrument())
				{
					std::cerr << "Error: Received update from unknown instrument." << std::endl;
					return;
				}

Tobias WEBER's avatar
Tobias WEBER committed
409
410
411
412
413
				// is ki or kf fixed?
				bool kf_fixed = true;
				if(!std::get<1>(m_tascalc.GetKfix()))
					kf_fixed = false;

414
415
				// old angles
				t_real oldA6 = m_tasProperties->GetWidget()->GetAnaScatteringAngle()/t_real{180}*tl2::pi<t_real>;
416
				t_real oldA2 = m_tasProperties->GetWidget()->GetMonoScatteringAngle()/t_real{180}*tl2::pi<t_real>;
417

Tobias WEBER's avatar
Tobias WEBER committed
418
				// get scattering angles
Tobias WEBER's avatar
Tobias WEBER committed
419
420
421
				t_real monoScAngle = instr.GetMonochromator().GetAxisAngleOut();
				t_real sampleScAngle = instr.GetSample().GetAxisAngleOut();
				t_real anaScAngle = instr.GetAnalyser().GetAxisAngleOut();
422
423

				// set scattering angles
Tobias WEBER's avatar
Tobias WEBER committed
424
425
426
427
				m_tasProperties->GetWidget()->SetMonoScatteringAngle(monoScAngle*t_real{180}/tl2::pi<t_real>);
				m_tasProperties->GetWidget()->SetSampleScatteringAngle(sampleScAngle*t_real{180}/tl2::pi<t_real>);
				m_tasProperties->GetWidget()->SetAnaScatteringAngle(anaScAngle*t_real{180}/tl2::pi<t_real>);

428
				// get crystal rocking angles
Tobias WEBER's avatar
Tobias WEBER committed
429
430
431
				t_real monoXtalAngle = instr.GetMonochromator().GetAxisAngleInternal();
				t_real sampleXtalAngle = instr.GetSample().GetAxisAngleInternal();
				t_real anaXtalAngle = instr.GetAnalyser().GetAxisAngleInternal();
432
433

				// set crystal rocking angles
Tobias WEBER's avatar
Tobias WEBER committed
434
435
436
				m_tasProperties->GetWidget()->SetMonoCrystalAngle(monoXtalAngle*t_real{180}/tl2::pi<t_real>);
				m_tasProperties->GetWidget()->SetSampleCrystalAngle(sampleXtalAngle*t_real{180}/tl2::pi<t_real>);
				m_tasProperties->GetWidget()->SetAnaCrystalAngle(anaXtalAngle*t_real{180}/tl2::pi<t_real>);
Tobias WEBER's avatar
Tobias WEBER committed
437

438
				auto [Qrlu, E] = m_tascalc.GetQE(monoXtalAngle, anaXtalAngle,
439
					sampleXtalAngle, sampleScAngle);
440

Tobias WEBER's avatar
Tobias WEBER committed
441
442
443
				bool in_angular_limits = m_instrspace.CheckAngularLimits();
				bool colliding = m_instrspace.CheckCollision2D();

Tobias WEBER's avatar
Tobias WEBER committed
444
				SetInstrumentStatus(Qrlu, E,
Tobias WEBER's avatar
Tobias WEBER committed
445
					in_angular_limits, colliding);
446

447
448
449
450
				// if the analyser or monochromator angle changes, the mesh also needs to be updated
				if(kf_fixed && !tl2::equals<t_real>(oldA6, anaScAngle, g_eps))
					ValidatePathMesh(false);
				if(!kf_fixed && !tl2::equals<t_real>(oldA2, monoScAngle, g_eps))
451
452
					ValidatePathMesh(false);

453
				if(this->m_dlgConfigSpace)
Tobias WEBER's avatar
Tobias WEBER committed
454
				{
455
456
					this->m_dlgConfigSpace->UpdateInstrument(
						instr, m_tascalc.GetScatteringSenses());
Tobias WEBER's avatar
Tobias WEBER committed
457
				}
458
459

				if(this->m_renderer)
Tobias WEBER's avatar
Tobias WEBER committed
460
461
462
				{
					this->m_renderer->SetInstrumentStatus(
						in_angular_limits, colliding);
463
					this->m_renderer->UpdateInstrument(instr);
Tobias WEBER's avatar
Tobias WEBER committed
464
				}
Tobias WEBER's avatar
Tobias WEBER committed
465
			});
Tobias WEBER's avatar
Tobias WEBER committed
466

Tobias WEBER's avatar
Tobias WEBER committed
467
468
469
470
471
472
473
		m_instrspace.GetInstrument().EmitUpdate();
	}
	catch(const std::exception& ex)
	{
		QMessageBox::critical(this, "Error",
			QString{"Instrument configuration error: "} + ex.what() + QString{"."});
		return false;
Tobias WEBER's avatar
Tobias WEBER committed
474
	}
Tobias WEBER's avatar
Tobias WEBER committed
475
476
	return true;
}
Tobias WEBER's avatar
Tobias WEBER committed
477
478


Tobias WEBER's avatar
Tobias WEBER committed
479
480
481
482
483
484
485
/**
 * save file
 */
bool PathsTool::SaveFile(const QString &file)
{
	if(file=="")
		return false;
Tobias WEBER's avatar
Tobias WEBER committed
486

487
	// save instrument space configuration
Tobias WEBER's avatar
Tobias WEBER committed
488
	pt::ptree prop = m_instrspace.Save();
Tobias WEBER's avatar
Tobias WEBER committed
489

490
491
492
493
494
495
496
	// save dock window settings
	prop.put_child(FILE_BASENAME "configuration.tas", m_tasProperties->GetWidget()->Save());
	prop.put_child(FILE_BASENAME "configuration.crystal", m_xtalProperties->GetWidget()->Save());
	prop.put_child(FILE_BASENAME "configuration.coordinates", m_coordProperties->GetWidget()->Save());
	prop.put_child(FILE_BASENAME "configuration.path", m_pathProperties->GetWidget()->Save());
	prop.put_child(FILE_BASENAME "configuration.camera", m_camProperties->GetWidget()->Save());

Tobias WEBER's avatar
Tobias WEBER committed
497
498
499
	// set format and version
	prop.put(FILE_BASENAME "ident", PROG_IDENT);
	prop.put(FILE_BASENAME "timestamp", tl2::var_to_str(tl2::epoch<t_real>()));
Tobias WEBER's avatar
Tobias WEBER committed
500

Tobias WEBER's avatar
Tobias WEBER committed
501
502
	std::ofstream ofstr{file.toStdString()};
	if(!ofstr)
Tobias WEBER's avatar
Tobias WEBER committed
503
	{
Tobias WEBER's avatar
Tobias WEBER committed
504
505
		QMessageBox::critical(this, "Error", "Could not save file.");
		return false;
Tobias WEBER's avatar
Tobias WEBER committed
506
507
	}

508
	ofstr.precision(g_prec);
Tobias WEBER's avatar
Tobias WEBER committed
509
	pt::write_xml(ofstr, prop, pt::xml_writer_make_settings('\t', 1, std::string{"utf-8"}));
Tobias WEBER's avatar
Tobias WEBER committed
510

Tobias WEBER's avatar
Tobias WEBER committed
511
	SetCurrentFile(file);
512
	m_recent.AddRecentFile(file);
Tobias WEBER's avatar
Tobias WEBER committed
513
514
	return true;
}
Tobias WEBER's avatar
Tobias WEBER committed
515
516


517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
/**
 * save screenshot
 */
bool PathsTool::SaveScreenshot(const QString &file)
{
	if(file=="" || !m_renderer)
		return false;

	QImage img = m_renderer->grabFramebuffer();
	return img.save(file, nullptr, 90);
}


/**
 * save a combined screenshot of the instrument view and config space
 */
bool PathsTool::SaveCombinedScreenshot(const QString& filename)
{
	bool ok1 = SaveScreenshot(filename);

	bool ok2 = false;
	if(m_dlgConfigSpace)
	{
		fs::path file_pdf{filename.toStdString()};
		file_pdf.replace_extension(fs::path{".pdf"});

		ok2 = m_dlgConfigSpace->SaveFigure(file_pdf.string().c_str());
	}

	return ok1 && ok2;
}


Tobias WEBER's avatar
Tobias WEBER committed
550
551
552
553
554
/**
 * remember current file and set window title
 */
void PathsTool::SetCurrentFile(const QString &file)
{
Tobias WEBER's avatar
Tobias WEBER committed
555
	static const QString title(TASPATHS_TITLE);
Tobias WEBER's avatar
Tobias WEBER committed
556
	m_recent.SetCurFile(file);
Tobias WEBER's avatar
Tobias WEBER committed
557

Tobias WEBER's avatar
Tobias WEBER committed
558
559
	this->setWindowFilePath(m_recent.GetCurFile());

Tobias WEBER's avatar
Tobias WEBER committed
560
	if(m_recent.GetCurFile() == "")
Tobias WEBER's avatar
Tobias WEBER committed
561
562
		this->setWindowTitle(title);
	else
563
		this->setWindowTitle(title + " \u2014 " + m_recent.GetCurFile());
Tobias WEBER's avatar
Tobias WEBER committed
564
}
Tobias WEBER's avatar
Tobias WEBER committed
565
566


567
568
569
570
571
/**
 * (in)validates the path mesh if the obstacle configuration has changed
 */
void PathsTool::ValidatePathMesh(bool valid)
{
572
573
	m_pathmeshvalid = valid;
	emit PathMeshValid(m_pathmeshvalid);
574
575
576
}


577
578
579
580
581
/**
 * set the instrument's energy selection mode to either kf=const or ki=const
 */
void PathsTool::SetKfConstMode(bool kf_const)
{
Tobias WEBER's avatar
Tobias WEBER committed
582
583
584
585
586
587
	bool old_kf_const = std::get<1>(m_tascalc.GetKfix());
	if(old_kf_const != kf_const)
	{
		m_tascalc.SetKfix(kf_const);
		ValidatePathMesh(false);
	}
588
589
590
}


Tobias WEBER's avatar
Tobias WEBER committed
591
592
593
/**
 * go to crystal coordinates
 */
594
void PathsTool::GotoCoordinates(
595
	t_real h, t_real k, t_real l,
596
597
	t_real ki, t_real kf,
	bool only_set_target)
Tobias WEBER's avatar
Tobias WEBER committed
598
{
599
	TasAngles angles = m_tascalc.GetAngles(h, k, l, ki, kf);
Tobias WEBER's avatar
Tobias WEBER committed
600

601
	if(!angles.mono_ok)
Tobias WEBER's avatar
Tobias WEBER committed
602
603
604
605
606
	{
		QMessageBox::critical(this, "Error", "Invalid monochromator angle.");
		return;
	}

607
	if(!angles.ana_ok)
Tobias WEBER's avatar
Tobias WEBER committed
608
609
610
611
612
	{
		QMessageBox::critical(this, "Error", "Invalid analyser angle.");
		return;
	}

613
	if(!angles.sample_ok)
Tobias WEBER's avatar
Tobias WEBER committed
614
615
	{
		QMessageBox::critical(this, "Error", "Invalid scattering angles.");
Tobias WEBER's avatar
Tobias WEBER committed
616
		return;
Tobias WEBER's avatar
Tobias WEBER committed
617
618
	}

Tobias WEBER's avatar
Tobias WEBER committed
619
	// set target coordinate angles
620
621
622
623
	if(only_set_target)
	{
		if(!m_pathProperties)
			return;
624

625
626
627
		auto pathwidget = m_pathProperties->GetWidget();
		if(!pathwidget)
			return;
Tobias WEBER's avatar
Tobias WEBER committed
628

629
		const t_real *sensesCCW = m_tascalc.GetScatteringSenses();
630
631
		t_real a2_abs = angles.monoXtalAngle * 2. * sensesCCW[0];
		t_real a4_abs = angles.sampleScatteringAngle * sensesCCW[1];
632

633
		pathwidget->SetTarget(
634
			a2_abs / tl2::pi<t_real> * 180.,
635
636
			a4_abs / tl2::pi<t_real> * 180.);
	}
Tobias WEBER's avatar
Tobias WEBER committed
637
638

	// set instrument angles
639
640
	else
	{
Tobias WEBER's avatar
Tobias WEBER committed
641
		Instrument& instr = m_instrspace.GetInstrument();
Tobias WEBER's avatar
Tobias WEBER committed
642
643
644
645
646
647
648
649
650
651

		// send one update signal at the end
		// and not after each angle change
		instr.SetBlockUpdates(true);
		BOOST_SCOPE_EXIT(&instr)
		{
			instr.SetBlockUpdates(false);
			instr.EmitUpdate();
		} BOOST_SCOPE_EXIT_END

652
		// set scattering angles
Tobias WEBER's avatar
Tobias WEBER committed
653
		instr.GetMonochromator().SetAxisAngleOut(
654
			t_real{2} * angles.monoXtalAngle);
Tobias WEBER's avatar
Tobias WEBER committed
655
		instr.GetSample().SetAxisAngleOut(
656
			angles.sampleScatteringAngle);
Tobias WEBER's avatar
Tobias WEBER committed
657
		instr.GetAnalyser().SetAxisAngleOut(
658
			t_real{2} * angles.anaXtalAngle);
659
660

		// set crystal angles
Tobias WEBER's avatar
Tobias WEBER committed
661
		instr.GetMonochromator().SetAxisAngleInternal(
662
			angles.monoXtalAngle);
Tobias WEBER's avatar
Tobias WEBER committed
663
		instr.GetSample().SetAxisAngleInternal(
664
			angles.sampleXtalAngle);
Tobias WEBER's avatar
Tobias WEBER committed
665
		instr.GetAnalyser().SetAxisAngleInternal(
666
			angles.anaXtalAngle);
667

668
		m_tascalc.SetKfix(kf);
669
	}
670
671
672
673
674
675
676
677
678
}


/**
 * set the instrument angles to the specified ones
 * (angles have to be positive as scattering senses are applied in the function)
 */
void PathsTool::GotoAngles(std::optional<t_real> a1,
	std::optional<t_real> a3, std::optional<t_real> a4,
Tobias WEBER's avatar
Tobias WEBER committed
679
	std::optional<t_real> a5, bool only_set_target)
680
{
Tobias WEBER's avatar
Tobias WEBER committed
681
	// set target coordinate angles
682
	if(only_set_target && (a1 || a5) && a4)
683
	{
Tobias WEBER's avatar
Tobias WEBER committed
684
685
686
687
688
		if(!m_pathProperties)
			return;
		auto pathwidget = m_pathProperties->GetWidget();
		if(!pathwidget)
			return;
Tobias WEBER's avatar
Tobias WEBER committed
689

690
691
692
693
694
695
696
		// is kf or ki fixed?
		bool kf_fixed = true;
		if(!std::get<1>(m_tascalc.GetKfix()))
			kf_fixed = false;

		// move either monochromator or analyser depending if kf=fixed
		t_real _a2 = kf_fixed ? *a1 * 2. : *a5 * 2.;
Tobias WEBER's avatar
Tobias WEBER committed
697
		t_real _a4 = *a4;
Tobias WEBER's avatar
Tobias WEBER committed
698

Tobias WEBER's avatar
Tobias WEBER committed
699
700
701
		pathwidget->SetTarget(
			_a2 / tl2::pi<t_real> * 180.,
			_a4 / tl2::pi<t_real> * 180.);
702
	}
Tobias WEBER's avatar
Tobias WEBER committed
703

Tobias WEBER's avatar
Tobias WEBER committed
704
705
	// set instrument angles
	else
706
	{
Tobias WEBER's avatar
Tobias WEBER committed
707
		Instrument& instr = m_instrspace.GetInstrument();
Tobias WEBER's avatar
Tobias WEBER committed
708
709
710
711
712
713
714
715
716
717

		// send one update signal at the end
		// and not after each angle change
		instr.SetBlockUpdates(true);
		BOOST_SCOPE_EXIT(&instr)
		{
			instr.SetBlockUpdates(false);
			instr.EmitUpdate();
		} BOOST_SCOPE_EXIT_END

718
719
		const t_real *sensesCCW = m_tascalc.GetScatteringSenses();

Tobias WEBER's avatar
Tobias WEBER committed
720
721
722
		// set mono angle
		if(a1)
		{
723
			*a1 *= sensesCCW[0];
Tobias WEBER's avatar
Tobias WEBER committed
724
725
			instr.GetMonochromator().SetAxisAngleOut(t_real{2} * *a1);
			instr.GetMonochromator().SetAxisAngleInternal(*a1);
Tobias WEBER's avatar
Tobias WEBER committed
726
727
728
729
730
		}

		// set sample crystal angle
		if(a3)
		{
731
			*a3 *= sensesCCW[1];
Tobias WEBER's avatar
Tobias WEBER committed
732
			instr.GetSample().SetAxisAngleInternal(*a3);
Tobias WEBER's avatar
Tobias WEBER committed
733
734
735
736
737
		}

		// set sample scattering angle
		if(a4)
		{
738
			*a4 *= sensesCCW[1];
Tobias WEBER's avatar
Tobias WEBER committed
739
			instr.GetSample().SetAxisAngleOut(*a4);
Tobias WEBER's avatar
Tobias WEBER committed
740
741
742
743
744
		}

		// set ana angle
		if(a5)
		{
745
			*a5 *= sensesCCW[2];
Tobias WEBER's avatar
Tobias WEBER committed
746
747
			instr.GetAnalyser().SetAxisAngleOut(t_real{2} * *a5);
			instr.GetAnalyser().SetAxisAngleInternal(*a5);
Tobias WEBER's avatar
Tobias WEBER committed
748
		}
749
	}
Tobias WEBER's avatar
Tobias WEBER committed
750
}
Tobias WEBER's avatar
Tobias WEBER committed
751
752


Tobias WEBER's avatar
Tobias WEBER committed
753
754
755
756
757
758
759
760
/**
 * called after the plotter has initialised
 */
void PathsTool::AfterGLInitialisation()
{
	// GL device info
	std::tie(m_gl_ver, m_gl_shader_ver, m_gl_vendor, m_gl_renderer)
		= m_renderer->GetGlDescr();
Tobias WEBER's avatar
Tobias WEBER committed
761

Tobias WEBER's avatar
Tobias WEBER committed
762
763
764
	// get viewing angle
	t_real viewingAngle = m_renderer ? m_renderer->GetCamViewingAngle() : tl2::pi<t_real>*0.5;
	m_camProperties->GetWidget()->SetViewingAngle(viewingAngle*t_real{180}/tl2::pi<t_real>);
Tobias WEBER's avatar
updates    
Tobias WEBER committed
765

Tobias WEBER's avatar
Tobias WEBER committed
766
767
768
	// get perspective projection flag
	bool persp = m_renderer ? m_renderer->GetPerspectiveProjection() : true;
	m_camProperties->GetWidget()->SetPerspectiveProj(persp);
Tobias WEBER's avatar
updates    
Tobias WEBER committed
769

Tobias WEBER's avatar
Tobias WEBER committed
770
771
772
	// get camera position
	t_vec3_gl campos = m_renderer ? m_renderer->GetCamPosition() : tl2::zero<t_vec3_gl>(3);
	m_camProperties->GetWidget()->SetCamPosition(t_real(campos[0]), t_real(campos[1]), t_real(campos[2]));
Tobias WEBER's avatar
Tobias WEBER committed
773

Tobias WEBER's avatar
Tobias WEBER committed
774
775
776
	// get camera rotation
	t_vec2_gl camrot = m_renderer ? m_renderer->GetCamRotation() : tl2::zero<t_vec2_gl>(2);
	m_camProperties->GetWidget()->SetCamRotation(
777
		t_real(camrot[0])*t_real{180}/tl2::pi<t_real>,
Tobias WEBER's avatar
Tobias WEBER committed
778
		t_real(camrot[1])*t_real{180}/tl2::pi<t_real>);
Tobias WEBER's avatar
Tobias WEBER committed
779

780
	// load an initial instrument definition
Tobias WEBER's avatar
Tobias WEBER committed
781
	if(std::string instrfile = g_res.FindResource(m_initialInstrFile); !instrfile.empty())
782
783
784
785
	{
		if(OpenFile(instrfile.c_str()))
			m_renderer->LoadInstrument(m_instrspace);
	}
Tobias WEBER's avatar
Tobias WEBER committed
786
}
Tobias WEBER's avatar
Tobias WEBER committed
787

Tobias WEBER's avatar
Tobias WEBER committed
788

Tobias WEBER's avatar
Tobias WEBER committed
789
790
791
792
793
794
795
796
797
/**
 * mouse coordinates on base plane
 */
void PathsTool::CursorCoordsChanged(t_real_gl x, t_real_gl y)
{
	m_mouseX = x;
	m_mouseY = y;
	UpdateStatusLabel();
}
Tobias WEBER's avatar
Tobias WEBER committed
798

Tobias WEBER's avatar
Tobias WEBER committed
799

Tobias WEBER's avatar
Tobias WEBER committed
800
801
802
/**
 * mouse is over an object
 */
Tobias WEBER's avatar
Tobias WEBER committed
803
804
void PathsTool::PickerIntersection(const t_vec3_gl* /*pos*/,
	std::string obj_name, const t_vec3_gl* /*posSphere*/)
Tobias WEBER's avatar
Tobias WEBER committed
805
806
807
808
{
	m_curObj = obj_name;
	UpdateStatusLabel();
}
Tobias WEBER's avatar
Tobias WEBER committed
809

Tobias WEBER's avatar
Tobias WEBER committed
810

Tobias WEBER's avatar
Tobias WEBER committed
811
812
813
/**
 * clicked on an object
 */
Tobias WEBER's avatar
Tobias WEBER committed
814
void PathsTool::ObjectClicked(const std::string& obj, bool /*left*/, bool middle, bool right)
Tobias WEBER's avatar
Tobias WEBER committed
815
{
Tobias WEBER's avatar
Tobias WEBER committed
816
817
818
819
820
821
822
823
824
	if(!m_renderer)
		return;

	// show context menu for object
	if(right && obj != "")
	{
		m_curContextObj = obj;

		QPoint pos = m_renderer->GetMousePosition(true);
Tobias WEBER's avatar
Tobias WEBER committed
825
826
		pos.setX(pos.x() + 8);
		pos.setY(pos.y() + 8);
Tobias WEBER's avatar
Tobias WEBER committed
827
828
829
830
831
832
		m_contextMenuObj->popup(pos);
	}

	// centre scene around object
	if(middle)
	{
Tobias WEBER's avatar
Tobias WEBER committed
833
		m_renderer->CentreCam(obj);
Tobias WEBER's avatar
Tobias WEBER committed
834
	}
Tobias WEBER's avatar
Tobias WEBER committed
835
}
Tobias WEBER's avatar
Tobias WEBER committed
836
837


Tobias WEBER's avatar
Tobias WEBER committed
838
839
840
/**
 * dragging an object
 */
841
void PathsTool::ObjectDragged(bool drag_start, const std::string& obj,
Tobias WEBER's avatar
Tobias WEBER committed
842
	t_real_gl x_start, t_real_gl y_start, t_real_gl x, t_real_gl y)
Tobias WEBER's avatar
Tobias WEBER committed
843
{
844
	/*std::cout << "Dragging " << obj
Tobias WEBER's avatar
Tobias WEBER committed
845
846
847
		<< " from (" << x_start << ", " << y_start << ")"
		<< " to (" << x << ", " << y << ")." << std::endl;*/

Tobias WEBER's avatar
Tobias WEBER committed
848
	m_instrspace.DragObject(drag_start, obj, x_start, y_start, x, y);
Tobias WEBER's avatar
Tobias WEBER committed
849
}
Tobias WEBER's avatar
Tobias WEBER committed
850

Tobias WEBER's avatar
Tobias WEBER committed
851

852
/**
853
 * set temporary status message, by default for 2 seconds
854
 */
855
void PathsTool::SetTmpStatus(const std::string& msg, int msg_duration)
856
857
858
859
{
	if(!m_statusbar)
		return;

860
861
862
863
864
865
866
867
868
869
870
871
	if(thread() == QThread::currentThread())
	{
		m_statusbar->showMessage(msg.c_str(), msg_duration);
	}
	else
	{
		// alternate call via meta object when coming from another thread
		QMetaObject::invokeMethod(m_statusbar, "showMessage", Qt::QueuedConnection,
			Q_ARG(QString, msg.c_str()),
			Q_ARG(int, msg_duration)
		);
	}
872
873
874
875
876
877
}


/**
 * update permanent status message
 */
Tobias WEBER's avatar
Tobias WEBER committed
878
879
void PathsTool::UpdateStatusLabel()
{
Tobias WEBER's avatar
checks    
Tobias WEBER committed
880
881
882
883
884
885
886
	const t_real maxRange = 1e6;

	if(!std::isfinite(m_mouseX) || !std::isfinite(m_mouseY))
		return;
	if(std::abs(m_mouseX) >= maxRange || std::abs(m_mouseY) >= maxRange)
		return;

Tobias WEBER's avatar
Tobias WEBER committed
887
	std::ostringstream ostr;
888
	ostr.precision(g_prec_gui);
889
	ostr << std::fixed << std::showpos
Tobias WEBER's avatar
Tobias WEBER committed
890
		<< "Cursor: (" << m_mouseX << ", " << m_mouseY << ") m";
Tobias WEBER's avatar
Tobias WEBER committed
891
892
	if(m_curObj != "")
		ostr << ", object: " << m_curObj;
Tobias WEBER's avatar
Tobias WEBER committed
893
	ostr << ".";
Tobias WEBER's avatar
Tobias WEBER committed
894
895
	m_labelStatus->setText(ostr.str().c_str());
}
Tobias WEBER's avatar
Tobias WEBER committed
896

Tobias WEBER's avatar
Tobias WEBER committed
897

898
899
900
/**
 * set permanent instrumetn status message
 */
Tobias WEBER's avatar
Tobias WEBER committed
901
902
void PathsTool::SetInstrumentStatus(const std::optional<t_vec>& Qopt, t_real E,
	bool in_angular_limits, bool colliding)
903
{
904
905
	using namespace tl2_ops;

906
	std::ostringstream ostr;
907
	ostr.precision(g_prec_gui);
Tobias WEBER's avatar
Tobias WEBER committed
908
	//ostr << "Position: ";
909
910
911
912
913
914
915
916
917
918
919
	if(Qopt)
	{
		t_vec Q = *Qopt;
		tl2::set_eps_0<t_vec>(Q, g_eps_gui);
		ostr << std::fixed << "Q = (" << Q << ") rlu, ";
	}
	else
		ostr << "Q invalid, ";

	tl2::set_eps_0<t_real>(E, g_eps_gui);
	ostr << std::fixed << "E = " << E << " meV, ";
Tobias WEBER's avatar
Tobias WEBER committed
920
921
922

	if(!in_angular_limits)
		ostr << "invalid angles, ";
923
924

	if(colliding)
925
		ostr << "collision detected!";
926
	else
927
		ostr << "no collision.";
928
929
930
931
932

	m_labelCollisionStatus->setText(ostr.str().c_str());
}


Tobias WEBER's avatar
Tobias WEBER committed
933
/**
934
 * constructor, create UI
Tobias WEBER's avatar
Tobias WEBER committed
935
936
937
 */
PathsTool::PathsTool(QWidget* pParent) : QMainWindow{pParent}
{
Tobias WEBER's avatar
Tobias WEBER committed
938
	setWindowTitle(TASPATHS_TITLE);
Tobias WEBER's avatar
Tobias WEBER committed
939

Tobias WEBER's avatar
Tobias WEBER committed
940
	if(std::string icon_file = g_res.FindResource("res/taspaths.svg"); !icon_file.empty())
Tobias WEBER's avatar
Tobias WEBER committed
941
942
943
944
945
	{
		QIcon icon{icon_file.c_str()};
		setWindowIcon(icon);
	}

Tobias WEBER's avatar
Tobias WEBER committed
946
947
948
	// restore settings
	SettingsDlg::ReadSettings(&m_sett);

Tobias WEBER's avatar
Tobias WEBER committed
949

Tobias WEBER's avatar
Tobias WEBER committed
950
951
952
	// --------------------------------------------------------------------
	// rendering widget
	// --------------------------------------------------------------------
953
954
955
956
	// set gl surface format
	m_renderer->setFormat(tl2::gl_format(true, _GL_MAJ_VER, _GL_MIN_VER,
		m_multisamples, m_renderer->format()));

Tobias WEBER's avatar
Tobias WEBER committed
957
	auto plotpanel = new QWidget(this);
Tobias WEBER's avatar
Tobias WEBER committed
958

Tobias WEBER's avatar
Tobias WEBER committed
959
960
961
962
963
	connect(m_renderer.get(), &PathsRenderer::FloorPlaneCoordsChanged, this, &PathsTool::CursorCoordsChanged);
	connect(m_renderer.get(), &PathsRenderer::PickerIntersection, this, &PathsTool::PickerIntersection);
	connect(m_renderer.get(), &PathsRenderer::ObjectClicked, this, &PathsTool::ObjectClicked);
	connect(m_renderer.get(), &PathsRenderer::ObjectDragged, this, &PathsTool::ObjectDragged);
	connect(m_renderer.get(), &PathsRenderer::AfterGLInitialisation, this, &PathsTool::AfterGLInitialisation);
Tobias WEBER's avatar
Tobias WEBER committed
964

Tobias WEBER's avatar
Tobias WEBER committed
965
966
967
968
969
970
971
	// camera position
	connect(m_renderer.get(), &PathsRenderer::CamPositionChanged,
		[this](t_real_gl _x, t_real_gl _y, t_real_gl _z) -> void
		{
			t_real x = t_real(_x);
			t_real y = t_real(_y);
			t_real z = t_real(_z);
Tobias WEBER's avatar
Tobias WEBER committed
972

Tobias WEBER's avatar
Tobias WEBER committed
973
974
975
			if(m_camProperties)
				m_camProperties->GetWidget()->SetCamPosition(x, y, z);
		});
Tobias WEBER's avatar
Tobias WEBER committed
976

Tobias WEBER's avatar
Tobias WEBER committed
977
978
979
980
981
982
	// camera rotation
	connect(m_renderer.get(), &PathsRenderer::CamRotationChanged,
		[this](t_real_gl _phi, t_real_gl _theta) -> void
		{
			t_real phi = t_real(_phi);
			t_real theta = t_real(_theta);
Tobias WEBER's avatar
Tobias WEBER committed
983

Tobias WEBER's avatar
Tobias WEBER committed
984
985
			if(m_camProperties)
				m_camProperties->GetWidget()->SetCamRotation(
986
					phi*t_real{180}/tl2::pi<t_real>,
Tobias WEBER's avatar
Tobias WEBER committed
987
988
					theta*t_real{180}/tl2::pi<t_real>);
		});
Tobias WEBER's avatar
Tobias WEBER committed
989

Tobias WEBER's avatar
Tobias WEBER committed
990
991
992
	auto pGrid = new QGridLayout(plotpanel);
	pGrid->setSpacing(4);
	pGrid->setContentsMargins(4,4,4,4);
Tobias WEBER's avatar
Tobias WEBER committed
993

Tobias WEBER's avatar
Tobias WEBER committed
994
	pGrid->addWidget(m_renderer.get(), 0,0,1,4);
Tobias WEBER's avatar
Tobias WEBER committed
995

Tobias WEBER's avatar
Tobias WEBER committed
996
997
	setCentralWidget(plotpanel);
	// --------------------------------------------------------------------
Tobias WEBER's avatar
Tobias WEBER committed
998

Tobias WEBER's avatar
Tobias WEBER committed
999

Tobias WEBER's avatar
Tobias WEBER committed
1000
1001
1002
1003
1004
	// --------------------------------------------------------------------
	// dock widgets
	// --------------------------------------------------------------------
	m_tasProperties = std::make_shared<TASPropertiesDockWidget>(this);
	m_xtalProperties = std::make_shared<XtalPropertiesDockWidget>(this);
1005
	m_xtalInfos = std::make_shared<XtalInfoDockWidget>(this);
Tobias WEBER's avatar
Tobias WEBER committed
1006
	m_coordProperties = std::make_shared<CoordPropertiesDockWidget>(this);
Tobias WEBER's avatar
Tobias WEBER committed
1007
1008
	m_pathProperties = std::make_shared<PathPropertiesDockWidget>(this);
	m_camProperties = std::make_shared<CamPropertiesDockWidget>(this);
Tobias WEBER's avatar
Tobias WEBER committed
1009

Tobias WEBER's avatar
cleanup    
Tobias WEBER committed
1010
	for(QDockWidget* dockwidget : std::initializer_list<QDockWidget*>{
Tobias WEBER's avatar
Tobias WEBER committed
1011
		m_tasProperties.get(), m_xtalProperties.get(), m_xtalInfos.get(),
Tobias WEBER's avatar
cleanup    
Tobias WEBER committed
1012
		m_coordProperties.get(), m_pathProperties.get(), m_camProperties.get() })
Tobias WEBER's avatar
Tobias WEBER committed
1013
1014
	{
		dockwidget->setFeatures(
Tobias WEBER's avatar
cleanup    
Tobias WEBER committed
1015
1016
			QDockWidget::DockWidgetClosable |
			QDockWidget::DockWidgetMovable |
Tobias WEBER's avatar
Tobias WEBER committed
1017
1018
1019
1020
			QDockWidget::DockWidgetFloatable /*|
			QDockWidget::DockWidgetVerticalTitleBar*/);
		dockwidget->setAllowedAreas(Qt::AllDockWidgetAreas);
	}
Tobias WEBER's avatar
cleanup    
Tobias WEBER committed
1021

Tobias WEBER's avatar
Tobias WEBER committed
1022
	addDockWidget(Qt::LeftDockWidgetArea, m_tasProperties.get());
Tobias WEBER's avatar
Tobias WEBER committed
1023
	addDockWidget(Qt::LeftDockWidgetArea, m_xtalProperties.get());
Tobias WEBER's avatar
Tobias WEBER committed
1024
	addDockWidget(Qt::RightDockWidgetArea, m_xtalInfos.get());
Tobias WEBER's avatar
Tobias WEBER committed
1025
	addDockWidget(Qt::RightDockWidgetArea, m_coordProperties.get());
Tobias WEBER's avatar
Tobias WEBER committed
1026
	addDockWidget(Qt::RightDockWidgetArea, m_pathProperties.get());
Tobias WEBER's avatar
Tobias WEBER committed
1027
	addDockWidget(Qt::RightDockWidgetArea, m_camProperties.get());
Tobias WEBER's avatar
Tobias WEBER committed
1028

Tobias WEBER's avatar
Tobias WEBER committed
1029
1030
	auto* taswidget = m_tasProperties->GetWidget().get();
	auto* xtalwidget = m_xtalProperties->GetWidget().get();
Tobias WEBER's avatar
Tobias WEBER committed
1031
	auto* coordwidget = m_coordProperties->GetWidget().get();
Tobias WEBER's avatar
Tobias WEBER committed
1032
1033
	auto* pathwidget = m_pathProperties->GetWidget().get();
	auto* camwidget = m_camProperties->GetWidget().get();
Tobias WEBER's avatar
Tobias WEBER committed
1034

Tobias WEBER's avatar
Tobias WEBER committed
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
	// scattering angles
	connect(taswidget, &TASPropertiesWidget::MonoScatteringAngleChanged,
		[this](t_real angle) -> void
		{
			m_instrspace.GetInstrument().GetMonochromator().
				SetAxisAngleOut(angle/t_real{180}*tl2::pi<t_real>);
		});
	connect(taswidget, &TASPropertiesWidget::SampleScatteringAngleChanged,
		[this](t_real angle) -> void
		{
			m_instrspace.GetInstrument().GetSample().
				SetAxisAngleOut(angle/t_real{180}*tl2::pi<t_real>);
		});
	connect(taswidget, &TASPropertiesWidget::AnaScatteringAngleChanged,
		[this](t_real angle) -> void
		{
			m_instrspace.GetInstrument().GetAnalyser().
				SetAxisAngleOut(angle/t_real{180}*tl2::pi<t_real>);
		});
Tobias WEBER's avatar
Tobias WEBER committed
1054

Tobias WEBER's avatar
Tobias WEBER committed
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
	// crystal angles
	connect(taswidget, &TASPropertiesWidget::MonoCrystalAngleChanged,
		[this](t_real angle) -> void
		{
			m_instrspace.GetInstrument().GetMonochromator().
				SetAxisAngleInternal(angle/t_real{180}*tl2::pi<t_real>);
		});
	connect(taswidget, &TASPropertiesWidget::SampleCrystalAngleChanged,
		[this](t_real angle) -> void
		{
			m_instrspace.GetInstrument().GetSample().
				SetAxisAngleInternal(angle/t_real{180}*tl2::pi<t_real>);
		});
	connect(taswidget, &TASPropertiesWidget::AnaCrystalAngleChanged,
		[this](t_real angle) -> void
		{
			m_instrspace.GetInstrument().GetAnalyser().
				SetAxisAngleInternal(angle/t_real{180}*tl2::pi<t_real>);
		});
Tobias WEBER's avatar
Tobias WEBER committed
1074

Tobias WEBER's avatar
Tobias WEBER committed
1075
1076
1077
1078
	// d spacings
	connect(taswidget, &TASPropertiesWidget::DSpacingsChanged,
		[this](t_real dmono, t_real dana) -> void
		{
1079
1080
			m_tascalc.SetMonochromatorD(dmono);
			m_tascalc.SetAnalyserD(dana);
Tobias WEBER's avatar
Tobias WEBER committed
1081
		});
Tobias WEBER's avatar
Tobias WEBER committed
1082

Tobias WEBER's avatar
Tobias WEBER committed
1083
1084
1085
1086
	// scattering senses
	connect(taswidget, &TASPropertiesWidget::ScatteringSensesChanged,
		[this](bool monoccw, bool sampleccw, bool anaccw) -> void
		{
1087
			m_tascalc.SetScatteringSenses(monoccw, sampleccw, anaccw);
Tobias WEBER's avatar
Tobias WEBER committed
1088
		});