XtalConfigSpace.cpp 15.7 KB
Newer Older
Tobias WEBER's avatar
Tobias WEBER committed
1
2
3
4
5
6
7
8
9
10
11
/**
 * xtal configuration space
 * @author Tobias Weber <tweber@ill.fr>
 * @date aug-2021
 * @license GPLv3, see 'LICENSE' file
 *
 * References:
 *   - https://www.qcustomplot.com/documentation/classQCustomPlot.html
 *   - https://www.qcustomplot.com/documentation/classQCPColorMap.html
 *   - https://www.qcustomplot.com/documentation/classQCPGraph.html
 *   - https://www.qcustomplot.com/documentation/classQCPCurve.html
12
13
14
 *
 * ----------------------------------------------------------------------------
 * TAS-Paths (part of the Takin software suite)
Tobias WEBER's avatar
Tobias WEBER committed
15
 * Copyright (C) 2021  Tobias WEBER (Institut Laue-Langevin (ILL),
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 *                     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
30
31
32
33
 */

#include "XtalConfigSpace.h"

34
#include <QtGui/QClipboard>
Tobias WEBER's avatar
Tobias WEBER committed
35
36
37
38
39
40
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QMenu>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QFileDialog>
Tobias WEBER's avatar
Tobias WEBER committed
41
#include <QtWidgets/QProgressDialog>
Tobias WEBER's avatar
Tobias WEBER committed
42

43
#include "settings_variables.h"
Tobias WEBER's avatar
Tobias WEBER committed
44

Tobias WEBER's avatar
Tobias WEBER committed
45
#include "src/core/mingw_hacks.h"
46
47
48
49
50
51
#include <boost/asio.hpp>
namespace asio = boost::asio;

using t_task = std::packaged_task<void()>;
using t_taskptr = std::shared_ptr<t_task>;

Tobias WEBER's avatar
Tobias WEBER committed
52
53
54
55
56

XtalConfigSpaceDlg::XtalConfigSpaceDlg(QWidget* parent, QSettings *sett)
	: QDialog{parent}, m_sett{sett}
{
	setWindowTitle("Crystal Configuration Space");
Tobias WEBER's avatar
Tobias WEBER committed
57
	setSizeGripEnabled(true);
Tobias WEBER's avatar
Tobias WEBER committed
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

	// restore dialog geometry
	if(m_sett && m_sett->contains("xtalconfigspace/geo"))
		restoreGeometry(m_sett->value("xtalconfigspace/geo").toByteArray());
	else
		resize(800, 600);

	// plotter
	m_plot = std::make_shared<QCustomPlot>(this);
	m_plot->xAxis->setLabel("x * Orientation Vector 1");
	m_plot->yAxis->setLabel("y * Orientation Vector 2");
	m_plot->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
	m_plot->setInteraction(QCP::iSelectPlottablesBeyondAxisRect, false);

	// wall contours
	m_colourMap = new QCPColorMap(m_plot->xAxis, m_plot->yAxis);
Tobias WEBER's avatar
Tobias WEBER committed
74
	m_colourMap->setGradient(QCPColorGradient::gpJet /*gpGrayscale*/);
Tobias WEBER's avatar
Tobias WEBER committed
75
76
77
78
79
80
81
82
83
84
85
86
87
88
	m_colourMap->setDataRange(QCPRange{0, 1});
	m_colourMap->setDataScaleType(QCPAxis::stLinear);
	m_colourMap->setInterpolate(false);
	m_colourMap->setAntialiased(false);

	// status label
	m_status = new QLabel(this);
	m_status->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
	m_status->setFrameStyle(QFrame::Sunken);
	m_status->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);

	// spin boxes
	m_spinVec1Start = new QDoubleSpinBox(this);
	m_spinVec1End = new QDoubleSpinBox(this);
89
90
91
92
93
	m_spinVec2Start = new QDoubleSpinBox(this);
	m_spinVec2End = new QDoubleSpinBox(this);
	m_spinVec1Delta = new QDoubleSpinBox(this);
	m_spinVec2Delta = new QDoubleSpinBox(this);
	m_spinE = new QDoubleSpinBox(this);
Tobias WEBER's avatar
Tobias WEBER committed
94
95

	m_spinVec1Start->setPrefix("x_start = ");
96
97
98
	m_spinVec1Start->setMinimum(-999.);
	m_spinVec1Start->setMaximum(999.);
	m_spinVec1Start->setValue(-1.0);
Tobias WEBER's avatar
Tobias WEBER committed
99
100
	m_spinVec1Start->setSingleStep(0.1);
	m_spinVec1End->setPrefix("x_end = ");
101
102
	m_spinVec1End->setMinimum(-999.);
	m_spinVec1End->setMaximum(999.);
Tobias WEBER's avatar
Tobias WEBER committed
103
104
	m_spinVec1End->setValue(1.0);
	m_spinVec1End->setSingleStep(0.1);
105
	m_spinVec1Delta->setPrefix("Δx = ");
Tobias WEBER's avatar
Tobias WEBER committed
106
	m_spinVec1Delta->setDecimals(4);
107
108
109
110
	m_spinVec1Delta->setMinimum(0.0001);
	m_spinVec1Delta->setMaximum(999.);
	m_spinVec1Delta->setValue(0.05);
	m_spinVec1Delta->setSingleStep(0.01);
Tobias WEBER's avatar
Tobias WEBER committed
111
112

	m_spinVec2Start->setPrefix("y_start = ");
113
114
115
	m_spinVec2Start->setMinimum(-999);
	m_spinVec2Start->setMaximum(999);
	m_spinVec2Start->setValue(-1.0);
Tobias WEBER's avatar
Tobias WEBER committed
116
117
	m_spinVec2Start->setSingleStep(0.1);
	m_spinVec2End->setPrefix("y_end = ");
118
119
	m_spinVec2End->setMinimum(-999);
	m_spinVec2End->setMaximum(999);
Tobias WEBER's avatar
Tobias WEBER committed
120
121
	m_spinVec2End->setValue(1.0);
	m_spinVec2End->setSingleStep(0.1);
122
	m_spinVec2Delta->setPrefix("Δy = ");
Tobias WEBER's avatar
Tobias WEBER committed
123
	m_spinVec2Delta->setDecimals(4);
124
125
126
127
128
129
130
131
132
133
134
	m_spinVec2Delta->setMinimum(0.0001);
	m_spinVec2Delta->setMaximum(999.);
	m_spinVec2Delta->setValue(0.05);
	m_spinVec2Delta->setSingleStep(0.01);

	m_spinE->setPrefix("E = ");
	m_spinE->setSuffix(" meV");
	m_spinE->setMinimum(-999.);
	m_spinE->setMaximum(999.);
	m_spinE->setValue(0.);
	m_spinE->setSingleStep(0.1);
Tobias WEBER's avatar
Tobias WEBER committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152

	UpdatePlotRanges();

	// buttons
	QPushButton *btnCalc = new QPushButton("Calculate", this);
	QPushButton *btnSave = new QPushButton("Save Figure...", this);
	QPushButton *btnClose = new QPushButton("OK", this);

	// grid
	auto grid = new QGridLayout(this);
	grid->setSpacing(4);
	grid->setContentsMargins(12, 12, 12, 12);
	int y = 0;
	grid->addWidget(m_plot.get(), y++, 0, 1, 4);
	grid->addWidget(m_spinVec1Start, y, 0, 1, 1);
	grid->addWidget(m_spinVec1End, y, 1, 1, 1);
	grid->addWidget(m_spinVec2Start, y, 2, 1, 1);
	grid->addWidget(m_spinVec2End, y++, 3, 1, 1);
153
154
155
	grid->addWidget(m_spinVec1Delta, y, 0, 1, 1);
	grid->addWidget(m_spinVec2Delta, y, 1, 1, 1);
	grid->addWidget(m_spinE, y++, 3, 1, 1);
Tobias WEBER's avatar
Tobias WEBER committed
156
157
158
159
160
161
162
163
164
165
	grid->addWidget(btnCalc, y, 1, 1, 1);
	grid->addWidget(btnSave, y, 2, 1, 1);
	grid->addWidget(btnClose, y++, 3, 1, 1);
	grid->addWidget(m_status, y++, 0, 1, 4);


	// ------------------------------------------------------------------------
	// menu
	// ------------------------------------------------------------------------
	QMenu *menuFile = new QMenu("File", this);
166
	QMenu *menuEdit = new QMenu("Edit", this);
Tobias WEBER's avatar
Tobias WEBER committed
167
168
169
	QMenu *menuView = new QMenu("View", this);

	QAction *acSavePDF = new QAction("Save Figure...", menuFile);
170
	acSavePDF->setShortcut(QKeySequence::Save);
Tobias WEBER's avatar
Tobias WEBER committed
171
172
173
	menuFile->addAction(acSavePDF);
	menuFile->addSeparator();

Tobias WEBER's avatar
Tobias WEBER committed
174
175
	QAction *acQuit = new QAction(QIcon::fromTheme("window-close"), "Close", menuFile);
	acQuit->setShortcut(QKeySequence::Close);
Tobias WEBER's avatar
Tobias WEBER committed
176
177
	menuFile->addAction(acQuit);

178
179
180
	QAction *acCopy = new QAction("Copy Figure", menuEdit);
	acCopy->setShortcut(QKeySequence::Copy);
	menuEdit->addAction(acCopy);
181

Tobias WEBER's avatar
Tobias WEBER committed
182
183
184
185
186
187
188
189
190
191
	QAction *acEnableZoom = new QAction("Enable Zoom", menuView);
	acEnableZoom->setCheckable(true);
	acEnableZoom->setChecked(!m_moveInstr);
	menuView->addAction(acEnableZoom);

	QAction *acResetZoom = new QAction("Reset Zoom", menuView);
	menuView->addAction(acResetZoom);

	auto* menuBar = new QMenuBar(this);
	menuBar->addMenu(menuFile);
192
	menuBar->addMenu(menuEdit);
Tobias WEBER's avatar
Tobias WEBER committed
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
	menuBar->addMenu(menuView);
	grid->setMenuBar(menuBar);
	// ------------------------------------------------------------------------


	// ------------------------------------------------------------------------
	// output functions
	// ------------------------------------------------------------------------
	// export figure as pdf file
	auto savePDF = [this]()
	{
		QString dirLast = this->m_sett->value("xtalconfigspace/cur_dir", "~/").toString();
		QString filename = QFileDialog::getSaveFileName(
			this, "Save PDF Figure", dirLast, "PDF Files (*.pdf)");
		if(filename=="")
			return;

		if(this->m_plot->savePdf(filename))
			this->m_sett->setValue("xtalconfigspace/cur_dir", QFileInfo(filename).path());
	};
213
214
215
216
217
218
219
220
221
222
223
224

	// copy figure to clipboard
	auto copyFigure = [this]()
	{
		QClipboard *clp = QGuiApplication::clipboard();
		if(!m_plot || !clp)
			return;

		QPixmap pix = m_plot->toPixmap();
		QImage img = pix.toImage();
		clp->setImage(img);
	};
Tobias WEBER's avatar
Tobias WEBER committed
225
226
227
228
229
230
231
232
233
234
235
236
	// ------------------------------------------------------------------------


	// ------------------------------------------------------------------------
	// connections
	// ------------------------------------------------------------------------
	connect(m_plot.get(), &QCustomPlot::mousePress,
	[this](QMouseEvent* evt)
	{
		if(!this->m_plot || !m_moveInstr)
			return;

Tobias WEBER's avatar
Tobias WEBER committed
237
		// coordinates
Tobias WEBER's avatar
Tobias WEBER committed
238
239
		const t_real x = this->m_plot->xAxis->pixelToCoord(evt->pos().x());
		const t_real y = this->m_plot->yAxis->pixelToCoord(evt->pos().y());
Tobias WEBER's avatar
Tobias WEBER committed
240

Tobias WEBER's avatar
Tobias WEBER committed
241
242
		auto [Q, ki, kf] = GetQkikf(x, y);

Tobias WEBER's avatar
Tobias WEBER committed
243
		// move instrument
Tobias WEBER's avatar
Tobias WEBER committed
244
		emit GotoCoordinates(Q[0], Q[1], Q[2], ki, kf);
Tobias WEBER's avatar
Tobias WEBER committed
245
246
247
248
249
250
251
252
	});

	connect(m_plot.get(), &QCustomPlot::mouseMove,
	[this](QMouseEvent* evt)
	{
		if(!this->m_plot)
			return;

Tobias WEBER's avatar
Tobias WEBER committed
253
		// coordinates
Tobias WEBER's avatar
Tobias WEBER committed
254
255
		const int _x = evt->pos().x();
		const int _y = evt->pos().y();
Tobias WEBER's avatar
Tobias WEBER committed
256

Tobias WEBER's avatar
Tobias WEBER committed
257
258
259
		const t_real x = this->m_plot->xAxis->pixelToCoord(_x);
		const t_real y = this->m_plot->yAxis->pixelToCoord(_y);

Tobias WEBER's avatar
Tobias WEBER committed
260
261
262
		// crystal coordinates
		auto [Q, ki, kf] = GetQkikf(x, y);

Tobias WEBER's avatar
Tobias WEBER committed
263
264
265
		// move instrument
		if(m_moveInstr && (evt->buttons() & Qt::LeftButton))
		{
Tobias WEBER's avatar
Tobias WEBER committed
266
			emit GotoCoordinates(Q[0], Q[1], Q[2], ki, kf);
Tobias WEBER's avatar
Tobias WEBER committed
267
268
269
270
271
272
273
		}

		// set status
		std::ostringstream ostr;
		ostr.precision(g_prec_gui);

		// show coordinates
Tobias WEBER's avatar
Tobias WEBER committed
274
275
276
		ostr << "x = " << x << ", y = " << y << ";";
		ostr << " Q = (" << Q[0] << ", " << Q[1] << ", " << Q[2] << ")"
			<< "; ki = " << ki << ", kf = " << kf << ".";
Tobias WEBER's avatar
Tobias WEBER committed
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292

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


	// connections
	connect(acEnableZoom, &QAction::toggled, [this](bool enableZoom)->void
	{ this->SetInstrumentMovable(!enableZoom); });

	connect(acResetZoom, &QAction::triggered, [this]()->void
	{
		m_plot->rescaleAxes();
		m_plot->replot();
	});

	connect(acSavePDF, &QAction::triggered, this, savePDF);
293
	connect(acCopy, &QAction::triggered, this, copyFigure);
Tobias WEBER's avatar
Tobias WEBER committed
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
	connect(btnSave, &QPushButton::clicked, savePDF);
	connect(btnCalc, &QPushButton::clicked, this, &XtalConfigSpaceDlg::Calculate);
	connect(btnClose, &QPushButton::clicked, this, &XtalConfigSpaceDlg::accept);
	connect(acQuit, &QAction::triggered, this, &XtalConfigSpaceDlg::accept);
	// ------------------------------------------------------------------------


	SetInstrumentMovable(m_moveInstr);
}


XtalConfigSpaceDlg::~XtalConfigSpaceDlg()
{
}


void XtalConfigSpaceDlg::accept()
{
	if(m_sett)
		m_sett->setValue("xtalconfigspace/geo", saveGeometry());
	QDialog::accept();
}


/**
 * either move instrument by clicking in the plot or enable plot zoom mode
 */
void XtalConfigSpaceDlg::SetInstrumentMovable(bool moveInstr)
{
	m_moveInstr = moveInstr;

	if(!m_plot)
		return;

	if(m_moveInstr)
	{
		m_plot->setSelectionRectMode(QCP::srmNone);
		m_plot->setInteraction(QCP::iRangeZoom, false);
		m_plot->setInteraction(QCP::iRangeDrag, false);
	}
	else
	{
		m_plot->setSelectionRectMode(QCP::srmZoom);
		m_plot->setInteraction(QCP::iRangeZoom, true);
		m_plot->setInteraction(QCP::iRangeDrag, true);
	}
}


void XtalConfigSpaceDlg::UpdatePlotRanges()
{
	// ranges
	t_real vec1start = m_spinVec1Start->value();
	t_real vec1end = m_spinVec1End->value();
	t_real vec2start = m_spinVec2Start->value();
	t_real vec2end = m_spinVec2End->value();

	if(m_plot)
	{
		m_plot->xAxis->setRange(vec1start, vec1end);
		m_plot->yAxis->setRange(vec2start, vec2end);
	}

	if(m_colourMap)
	{
		m_colourMap->data()->setRange(
Tobias WEBER's avatar
Tobias WEBER committed
360
			QCPRange{vec1start, vec1end},
Tobias WEBER's avatar
Tobias WEBER committed
361
362
363
364
365
366
			QCPRange{vec2start, vec2end});
	}
}


/**
367
368
369
370
 * redraw plot
 */
void XtalConfigSpaceDlg::RedrawPlot()
{
Tobias WEBER's avatar
Tobias WEBER committed
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
	// print scattering plane vectors as axis labels
	if(m_tascalc)
	{
		const t_vec& vec1 = m_tascalc->GetSampleScatteringPlane(0);
		const t_vec& vec2 = m_tascalc->GetSampleScatteringPlane(1);

		std::ostringstream xlabel, ylabel;
		xlabel << "x * [" << vec1[0] << ", " << vec1[1] << ", " << vec1[2] << "]";
		ylabel << "y * [" << vec2[0] << ", " << vec2[1] << ", " << vec2[2] << "]";

		m_plot->xAxis->setLabel(xlabel.str().c_str());
		m_plot->yAxis->setLabel(ylabel.str().c_str());
	}

	UpdatePlotRanges();

387
388
389
390
391
392
393
394
395
396
397
398
399
400
	// draw wall image
	const std::size_t width = m_img.GetWidth();
	const std::size_t height = m_img.GetHeight();

	m_colourMap->data()->setSize(width, height);

	for(std::size_t y=0; y<height; ++y)
	{
		for(std::size_t x=0; x<width; ++x)
		{
			using t_pixel = typename std::decay_t<decltype(m_img)>::value_type;
			t_pixel pixel_val = m_img.GetPixel(x, y);

			// val > 0 => colliding
Tobias WEBER's avatar
Tobias WEBER committed
401
			t_real val = std::lerp(t_real(0), t_real(1),
402
403
404
405
406
407
408
409
410
411
412
				t_real(pixel_val)/t_real(std::numeric_limits<t_pixel>::max()));
			m_colourMap->data()->setCell(x, y, val);
		}
	}

	// replot
	m_plot->rescaleAxes();
	m_plot->replot();
}


Tobias WEBER's avatar
Tobias WEBER committed
413
414
415
/**
 * calculate crystal coordinates from graph position
 */
Tobias WEBER's avatar
Tobias WEBER committed
416
std::tuple<t_vec, t_real, t_real>
Tobias WEBER's avatar
Tobias WEBER committed
417
XtalConfigSpaceDlg::GetQkikf(t_real x, t_real y) const
Tobias WEBER's avatar
Tobias WEBER committed
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
{
	// orientation vectors
	const t_vec& vec1 = m_tascalc->GetSampleScatteringPlane(0);
	const t_vec& vec2 = m_tascalc->GetSampleScatteringPlane(1);

	// momentum
	t_vec Q = x*vec1 + y*vec2;

	// fixed energy
	t_real E = m_spinE->value();

	// wavenumbers
	t_real ki, kf;
	auto [kfix, fixed_kf] = m_tascalc->GetKfix();

	if(fixed_kf)
	{
		kf = kfix;
		ki = tl2::calc_tas_ki(kf, E);
	}
	else
	{
		ki = kfix;
		kf = tl2::calc_tas_ki(ki, E);
	}

	return std::make_tuple(Q, ki, kf);
}


448
449
/**
 * calculate the obstacle representations in the crystal configuration space
Tobias WEBER's avatar
Tobias WEBER committed
450
451
452
453
454
455
 */
void XtalConfigSpaceDlg::Calculate()
{
	if(!m_instrspace || !m_tascalc)
		return;

456
457
458
	// fixed energy
	t_real E = m_spinE->value();

Tobias WEBER's avatar
Tobias WEBER committed
459
460
461
462
463
	// ranges
	t_real vec1start = m_spinVec1Start->value();
	t_real vec1end = m_spinVec1End->value();
	t_real vec2start = m_spinVec2Start->value();
	t_real vec2end = m_spinVec2End->value();
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
	t_real vec1step = m_spinVec1Delta->value();
	t_real vec2step = m_spinVec2Delta->value();

	// create colour map and image
	std::size_t img_w = (vec1end-vec1start) / vec1step;
	std::size_t img_h = (vec2end-vec2start) / vec2step;

	m_img.Init(img_w, img_h);

	// create thread pool
	asio::thread_pool pool(g_maxnum_threads);

	std::vector<t_taskptr> tasks;
	tasks.reserve(img_h);

	// set image pixels
	for(std::size_t img_row=0; img_row<img_h; ++img_row)
	{
		t_real yparam = std::lerp(vec2start, vec2end, img_row / t_real(img_h));

Tobias WEBER's avatar
Tobias WEBER committed
484
		auto task = [this, img_w, img_row, vec1start, vec1end, yparam, E]()
485
486
		{
			InstrumentSpace instrspace_cpy = *this->m_instrspace;
Tobias WEBER's avatar
Tobias WEBER committed
487
			Instrument& instr = instrspace_cpy.GetInstrument();
488
489
490
491

			for(std::size_t img_col=0; img_col<img_w; ++img_col)
			{
				t_real xparam = std::lerp(vec1start, vec1end, img_col / t_real(img_w));
Tobias WEBER's avatar
Tobias WEBER committed
492
493
494

				// crystal coordinates
				auto [Q, ki, kf] = GetQkikf(xparam, yparam);
495
496
497
498
499

				TasAngles angles = m_tascalc->GetAngles(Q[0], Q[1], Q[2], E);
				if(angles.mono_ok && angles.ana_ok && angles.sample_ok)
				{
					// set scattering angles
Tobias WEBER's avatar
Tobias WEBER committed
500
501
502
					instr.GetMonochromator().SetAxisAngleOut(angles.monoXtalAngle * t_real{2});
					instr.GetSample().SetAxisAngleOut(angles.sampleScatteringAngle);
					instr.GetAnalyser().SetAxisAngleOut(angles.anaXtalAngle * t_real{2});
503
504

					// set crystal angles
Tobias WEBER's avatar
Tobias WEBER committed
505
506
507
					instr.GetMonochromator().SetAxisAngleInternal(angles.monoXtalAngle);
					instr.GetSample().SetAxisAngleInternal(angles.sampleXtalAngle);
					instr.GetAnalyser().SetAxisAngleInternal(angles.anaXtalAngle);
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
				}
				else
				{
					m_img.SetPixel(img_col, img_row, 0xe0);
					continue;
				}

				// set image value
				bool angle_ok = instrspace_cpy.CheckAngularLimits();

				if(!angle_ok)
				{
					m_img.SetPixel(img_col, img_row, 0xf0);
				}
				else
				{
					bool colliding = instrspace_cpy.CheckCollision2D();
					m_img.SetPixel(img_col, img_row, colliding ? 0xff : 0x00);
				}
			}
		};

		t_taskptr taskptr = std::make_shared<t_task>(task);
		tasks.push_back(taskptr);
		asio::post(pool, [taskptr]() { (*taskptr)(); });
	}

Tobias WEBER's avatar
Tobias WEBER committed
535
536
	std::size_t num_tasks = tasks.size();

537
538

	// get results
Tobias WEBER's avatar
Tobias WEBER committed
539
540
541
542
543
544
545
546
547
548
	auto progress = std::make_unique<QProgressDialog>(this);
	progress->setWindowModality(Qt::WindowModal);
	progress->setLabelText(
		QString("Calculating configuration space in %1 threads...")
			.arg(g_maxnum_threads));
	progress->setAutoReset(true);
	progress->setAutoClose(true);
	progress->setMinimumDuration(1000);
	progress->setMinimum(0);
	progress->setMaximum(num_tasks);
549
550
551

	for(std::size_t taskidx=0; taskidx<num_tasks; ++taskidx)
	{
Tobias WEBER's avatar
Tobias WEBER committed
552
553
554
		progress->setValue(taskidx);

		if(progress->wasCanceled())
555
		{
Tobias WEBER's avatar
Tobias WEBER committed
556
557
			pool.stop();
			break;
558
559
560
		}

		tasks[taskidx]->get_future().get();
Tobias WEBER's avatar
Tobias WEBER committed
561
		RedrawPlot();
562
563
564
	}

	pool.join();
Tobias WEBER's avatar
Tobias WEBER committed
565
	progress->setValue(num_tasks);
566
	RedrawPlot();
Tobias WEBER's avatar
Tobias WEBER committed
567
}