ConfigSpace.cpp 34.5 KB
Newer Older
1
/**
Tobias WEBER's avatar
Tobias WEBER committed
2
 * angular configuration space dialog
3
4
5
 * @author Tobias Weber <tweber@ill.fr>
 * @date may-2021
 * @license GPLv3, see 'LICENSE' file
Tobias WEBER's avatar
Tobias WEBER committed
6
 *
7
 * References:
8
 *   - https://www.qcustomplot.com/documentation/classQCustomPlot.html
9
 *   - https://www.qcustomplot.com/documentation/classQCPColorMap.html
10
11
 *   - 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/>.
 * ----------------------------------------------------------------------------
30
31
32
 */

#include "ConfigSpace.h"
33

34
35
#include <QtCore/QMetaObject>
#include <QtCore/QThread>
Tobias WEBER's avatar
Tobias WEBER committed
36

37
38
#include <QtGui/QClipboard>

39
40
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QMenu>
41
#include <QtWidgets/QGridLayout>
42
43
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
44
#include <QtWidgets/QFileDialog>
45

Tobias WEBER's avatar
Tobias WEBER committed
46
47
48
49
50
51
#if QT_VERSION >= 0x060000
	#include <QtGui/QActionGroup>
#else
	#include <QtWidgets/QActionGroup>
#endif

52
#include "settings_variables.h"
53

54
55
56
57

ConfigSpaceDlg::ConfigSpaceDlg(QWidget* parent, QSettings *sett)
	: QDialog{parent}, m_sett{sett}
{
Tobias WEBER's avatar
Tobias WEBER committed
58
	setWindowTitle("Angular Configuration Space");
Tobias WEBER's avatar
Tobias WEBER committed
59
	setSizeGripEnabled(true);
60

61
62
63
64

	// --------------------------------------------------------------------
	// get settings
	// --------------------------------------------------------------------
65
	// restore dialog geometry
66
	if(m_sett && m_sett->contains("configspace/geo"))
67
68
69
70
		restoreGeometry(m_sett->value("configspace/geo").toByteArray());
	else
		resize(800, 600);

71

72
73
74
75
76
77
78
79
80
81
82
	// set voronoi calculation backend
	switch(g_voronoi_backend)
	{
		case 0:
			m_voronoibackend = VoronoiBackend::BOOST;
			break;
		case 1:
			m_voronoibackend = VoronoiBackend::CGAL;
			break;
	}

83
84
85
86
87
88
89
90
91
92
	// get global path finding strategy
	switch(g_pathstrategy)
	{
		case 0:
			m_pathstrategy = PathStrategy::SHORTEST;
			break;
		case 1:
			m_pathstrategy = PathStrategy::PENALISE_WALLS;
			break;
	}
93
94
95


	m_use_region_function = (g_use_region_function != 0);
96
97
98
	// --------------------------------------------------------------------


99
100
	// plotter
	m_plot = std::make_shared<QCustomPlot>(this);
101
102
	m_plot->xAxis->setLabel("2θ_S (deg)");
	m_plot->yAxis->setLabel("2θ_M (deg)");
103
	m_plot->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
104
	m_plot->setInteraction(QCP::iSelectPlottablesBeyondAxisRect, false);
105

106
	// wall contours
107
	m_colourMap = new QCPColorMap(m_plot->xAxis, m_plot->yAxis);
Tobias WEBER's avatar
Tobias WEBER committed
108
	m_colourMap->setGradient(QCPColorGradient::gpJet);
109
110
	m_colourMap->setDataRange(QCPRange{0, 1});
	m_colourMap->setDataScaleType(QCPAxis::stLinear);
Tobias WEBER's avatar
Tobias WEBER committed
111
112
	m_colourMap->setInterpolate(false);
	m_colourMap->setAntialiased(false);
113

114
	// instrument position plot
115
116
117
118
119
120
121
122
123
124
125
126
127
128
	{
		m_instrposplot = m_plot->addGraph();
		m_instrposplot->setLineStyle(QCPGraph::lsNone);
		m_instrposplot->setAntialiased(true);

		QPen instrpen = m_instrposplot->pen();
		instrpen.setColor(QColor::fromRgbF(1., 0., 0.));
		m_instrposplot->setPen(instrpen);

		QBrush instrbrush = m_instrposplot->brush();
		instrbrush.setColor(QColor::fromRgbF(1., 0., 0.));
		instrbrush.setStyle(Qt::SolidPattern);
		m_instrposplot->setBrush(instrbrush);

Tobias WEBER's avatar
Tobias WEBER committed
129
		QCPScatterStyle scatterstyle(QCPScatterStyle::ssCircle, 12);
130
131
132
133
		scatterstyle.setPen(instrpen);
		scatterstyle.setBrush(instrbrush);
		m_instrposplot->setScatterStyle(scatterstyle);
	}
134

135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
	// target position plot
	{
		m_targetposplot = m_plot->addGraph();
		m_targetposplot->setLineStyle(QCPGraph::lsNone);
		m_targetposplot->setAntialiased(true);

		QPen instrpen = m_targetposplot->pen();
		instrpen.setColor(QColor::fromRgbF(0., 1., 0.));
		m_targetposplot->setPen(instrpen);

		QBrush instrbrush = m_targetposplot->brush();
		instrbrush.setColor(QColor::fromRgbF(0., 1., 0.));
		instrbrush.setStyle(Qt::SolidPattern);
		m_targetposplot->setBrush(instrbrush);

Tobias WEBER's avatar
Tobias WEBER committed
150
		QCPScatterStyle scatterstyle(QCPScatterStyle::ssCircle, 12);
151
152
153
154
		scatterstyle.setPen(instrpen);
		scatterstyle.setBrush(instrbrush);
		m_targetposplot->setScatterStyle(scatterstyle);
	}
155

Tobias WEBER's avatar
Tobias WEBER committed
156
	UpdatePlotRanges();
157

158
	// status label
Tobias WEBER's avatar
Tobias WEBER committed
159
160
161
162
	m_status = new QLabel(this);
	m_status->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
	m_status->setFrameStyle(QFrame::Sunken);
	m_status->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
163
164
165
166
167
168
169

	// spin boxes
	m_spinDelta2ThS = new QDoubleSpinBox(this);
	m_spinDelta2ThM = new QDoubleSpinBox(this);

	m_spinDelta2ThS->setPrefix("Δθ_S = ");
	m_spinDelta2ThS->setSuffix(" deg");
170
	m_spinDelta2ThS->setValue(g_a4_delta / tl2::pi<t_real>*180.);
171
172
173
174
175
176
	m_spinDelta2ThS->setMinimum(0.001);
	m_spinDelta2ThS->setMaximum(180.);
	m_spinDelta2ThS->setSingleStep(0.1);

	m_spinDelta2ThM->setPrefix("Δθ_M = ");
	m_spinDelta2ThM->setSuffix(" deg");
177
	m_spinDelta2ThM->setValue(g_a2_delta / tl2::pi<t_real>*180.);
178
179
180
181
182
	m_spinDelta2ThM->setMinimum(0.001);
	m_spinDelta2ThM->setMaximum(180.);
	m_spinDelta2ThM->setSingleStep(0.1);

	// buttons
Tobias WEBER's avatar
Tobias WEBER committed
183
	QPushButton *btnCalc = new QPushButton("Calculate Mesh", this);
184
	QPushButton *btnSave = new QPushButton("Save Figure...", this);
185
	QPushButton *btnClose = new QPushButton("OK", this);
186
187
188
189

	// grid
	auto grid = new QGridLayout(this);
	grid->setSpacing(4);
190
	grid->setContentsMargins(12, 12, 12, 12);
191
	int y = 0;
192
	grid->addWidget(m_plot.get(), y++, 0, 1, 5);
193
194
195
	grid->addWidget(m_spinDelta2ThS, y, 0, 1, 1);
	grid->addWidget(m_spinDelta2ThM, y, 1, 1, 1);
	grid->addWidget(btnCalc, y, 2, 1, 1);
196
197
	grid->addWidget(btnSave, y, 3, 1, 1);
	grid->addWidget(btnClose, y++, 4, 1, 1);
Tobias WEBER's avatar
Tobias WEBER committed
198
	grid->addWidget(m_status, y++, 0, 1, 5);
199

200

Tobias WEBER's avatar
Tobias WEBER committed
201
	// ------------------------------------------------------------------------
202
	// menu
Tobias WEBER's avatar
Tobias WEBER committed
203
	// ------------------------------------------------------------------------
204
	QMenu *menuFile = new QMenu("File", this);
205
	QMenu *menuEdit = new QMenu("Edit", this);
Tobias WEBER's avatar
Tobias WEBER committed
206
	QMenu *menuView = new QMenu("View", this);
Tobias WEBER's avatar
Tobias WEBER committed
207
208
	QMenu *menuMeshOptions = new QMenu("Mesh Options", this);
	QMenu *menuPathOptions = new QMenu("Path Options", this);
209
	QMenu *menuCalc = new QMenu("Calculate", this);
210

Tobias WEBER's avatar
Tobias WEBER committed
211
212

	// file
213
	QAction *acSavePDF = new QAction("Save Figure...", menuFile);
214
	acSavePDF->setShortcut(QKeySequence::Save);
215
216
217
218
219
	menuFile->addAction(acSavePDF);

	QAction *acSaveLines = new QAction("Save Contour Lines...", menuFile);
	menuFile->addAction(acSaveLines);

220
221
222
	QAction *acSaveGraph = new QAction("Save Voronoi Graph...", menuFile);
	menuFile->addAction(acSaveGraph);

Tobias WEBER's avatar
Tobias WEBER committed
223
224
225
226
	QMenu *menuExportPath = new QMenu("Export Path", this);

	QAction *acExportRaw = new QAction("To Raw...", menuFile);
	menuExportPath->addAction(acExportRaw);
227

Tobias WEBER's avatar
Tobias WEBER committed
228
229
	QAction *acExportNomad = new QAction("To Nomad...", menuFile);
	menuExportPath->addAction(acExportNomad);
Tobias WEBER's avatar
Tobias WEBER committed
230

Tobias WEBER's avatar
Tobias WEBER committed
231
232
	QAction *acExportNicos = new QAction("To Nicos...", menuFile);
	menuExportPath->addAction(acExportNicos);
Tobias WEBER's avatar
Tobias WEBER committed
233

Tobias WEBER's avatar
Tobias WEBER committed
234
	menuFile->addMenu(menuExportPath);
Tobias WEBER's avatar
Tobias WEBER committed
235
236
	menuFile->addSeparator();

Tobias WEBER's avatar
Tobias WEBER committed
237
238
	QAction *acQuit = new QAction(QIcon::fromTheme("window-close"), "Close", menuFile);
	acQuit->setShortcut(QKeySequence::Close);
239
240
	menuFile->addAction(acQuit);

Tobias WEBER's avatar
Tobias WEBER committed
241

242
243
244
245
246
247
	// edit
	QAction *acCopy = new QAction("Copy Figure", menuEdit);
	acCopy->setShortcut(QKeySequence::Copy);
	menuEdit->addAction(acCopy);


Tobias WEBER's avatar
Tobias WEBER committed
248
	// path mesh options
Tobias WEBER's avatar
Tobias WEBER committed
249
250
251
	QAction *acSimplifyContour = new QAction("Simplify Contour", menuView);
	acSimplifyContour->setCheckable(true);
	acSimplifyContour->setChecked(m_simplifycontour);
Tobias WEBER's avatar
Tobias WEBER committed
252
	menuMeshOptions->addAction(acSimplifyContour);
Tobias WEBER's avatar
Tobias WEBER committed
253

254
255
256
	QAction *acGroupLines = new QAction("Group Line Segments", menuView);
	acGroupLines->setCheckable(true);
	acGroupLines->setChecked(m_grouplines);
Tobias WEBER's avatar
Tobias WEBER committed
257
	menuMeshOptions->addAction(acGroupLines);
258
259
260
261

	QAction *acSplitContour = new QAction("Split Contour into Convex Regions", menuView);
	acSplitContour->setCheckable(true);
	acSplitContour->setChecked(m_splitcontour);
Tobias WEBER's avatar
Tobias WEBER committed
262
	menuMeshOptions->addAction(acSplitContour);
263
264
265
266

	QAction *acCalcVoro = new QAction("Calculate Voronoi Diagram", menuView);
	acCalcVoro->setCheckable(true);
	acCalcVoro->setChecked(m_calcvoronoi);
Tobias WEBER's avatar
Tobias WEBER committed
267
	menuMeshOptions->addAction(acCalcVoro);
268

Tobias WEBER's avatar
Tobias WEBER committed
269
270
271
272
273
	QAction *acSubdivPath = new QAction("Subdivide Path", menuView);
	acSubdivPath->setCheckable(true);
	acSubdivPath->setChecked(m_subdivide_path);
	menuMeshOptions->addAction(acSubdivPath);

Tobias WEBER's avatar
Tobias WEBER committed
274
275
276
277
278
279
	QAction *acUseRegionFunc = new QAction("Use Region Function", menuView);
	acUseRegionFunc->setCheckable(true);
	acUseRegionFunc->setChecked(m_use_region_function);
	menuMeshOptions->addSeparator();
	menuMeshOptions->addAction(acUseRegionFunc);

280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
	// ------------------------------------------------------------------------
	// voronoi diagram calculation backends
	QMenu *menuVoroBackend = new QMenu("Voronoi Backend", this);

	QAction *acBackendBoost = new QAction("Boost.Polygon", menuVoroBackend);
	acBackendBoost->setCheckable(true);
	acBackendBoost->setChecked(m_voronoibackend == VoronoiBackend::BOOST);

	QAction *acBackendCgal = new QAction("CGAL/Segment Delaunay Graph", menuVoroBackend);
	acBackendCgal->setCheckable(true);
	acBackendCgal->setChecked(m_voronoibackend == VoronoiBackend::CGAL);

	QActionGroup *groupVoroBackend = new QActionGroup{this};
	groupVoroBackend->addAction(acBackendBoost);
	groupVoroBackend->addAction(acBackendCgal);

	menuVoroBackend->addAction(acBackendBoost);
	menuVoroBackend->addAction(acBackendCgal);
	menuMeshOptions->addMenu(menuVoroBackend);
	// ------------------------------------------------------------------------

Tobias WEBER's avatar
Tobias WEBER committed
301

Tobias WEBER's avatar
Tobias WEBER committed
302
	// path options
Tobias WEBER's avatar
Tobias WEBER committed
303
304
305
	QAction *acAutocalcPath = new QAction("Automatically Calculate Path", menuView);
	acAutocalcPath->setCheckable(true);
	acAutocalcPath->setChecked(m_autocalcpath);
Tobias WEBER's avatar
Tobias WEBER committed
306
307
	menuPathOptions->addAction(acAutocalcPath);

308
309
310
311
312
	QAction *acSyncPath = new QAction("Sync Path with Main View", menuView);
	acSyncPath->setCheckable(true);
	acSyncPath->setChecked(m_syncpath);
	menuPathOptions->addAction(acSyncPath);

Tobias WEBER's avatar
Tobias WEBER committed
313
	// ------------------------------------------------------------------------
314
	// path-finding strategies
Tobias WEBER's avatar
Tobias WEBER committed
315
316
317
318
319
320
321
322
323
324
325
326
	QMenu *menuPathStrategy = new QMenu("Path Finding Strategy", this);

	QAction *acStrategyShortest = new QAction("Shortest Path", menuPathStrategy);
	acStrategyShortest->setCheckable(true);
	acStrategyShortest->setChecked(m_pathstrategy == PathStrategy::SHORTEST);

	QAction *acStrategyPenaliseWalls = new QAction("Avoid Walls", menuPathStrategy);
	acStrategyPenaliseWalls->setCheckable(true);
	acStrategyPenaliseWalls->setChecked(m_pathstrategy == PathStrategy::PENALISE_WALLS);

	QActionGroup *groupPathStrategy = new QActionGroup{this};
	groupPathStrategy->addAction(acStrategyShortest);
Tobias WEBER's avatar
Tobias WEBER committed
327
	groupPathStrategy->addAction(acStrategyPenaliseWalls);
Tobias WEBER's avatar
Tobias WEBER committed
328
329
330
331
332

	menuPathStrategy->addAction(acStrategyShortest);
	menuPathStrategy->addAction(acStrategyPenaliseWalls);
	menuPathOptions->addMenu(menuPathStrategy);
	// ------------------------------------------------------------------------
Tobias WEBER's avatar
Tobias WEBER committed
333

Tobias WEBER's avatar
Tobias WEBER committed
334
335
336
	QAction *acMoveTarget = new QAction("Move Target Point", menuView);
	acMoveTarget->setCheckable(true);
	acMoveTarget->setChecked(m_movetarget);
Tobias WEBER's avatar
Tobias WEBER committed
337
338
	menuPathOptions->addSeparator();
	menuPathOptions->addAction(acMoveTarget);
Tobias WEBER's avatar
Tobias WEBER committed
339

340
341
342
343
344
345
	QAction *acCalcMesh = new QAction("Calculate Path Mesh", menuView);
	menuCalc->addAction(acCalcMesh);

	QAction *acCalcPath = new QAction("Calculate Path", menuView);
	menuCalc->addAction(acCalcPath);

Tobias WEBER's avatar
Tobias WEBER committed
346
347

	// view
348
349
350
351
352
353
354
355
	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);

Tobias WEBER's avatar
Tobias WEBER committed
356

357
	// shortcuts
Tobias WEBER's avatar
Tobias WEBER committed
358
359
360
	acMoveTarget->setShortcut(int(Qt::CTRL) | int(Qt::Key_T));
	acCalcMesh->setShortcut(int(Qt::ALT) | int(Qt::Key_M));
	acCalcPath->setShortcut(int(Qt::ALT) | int(Qt::Key_P));
361
362


Tobias WEBER's avatar
Tobias WEBER committed
363
	// menu bar
364
365
	auto* menuBar = new QMenuBar(this);
	menuBar->addMenu(menuFile);
366
	menuBar->addMenu(menuEdit);
Tobias WEBER's avatar
Tobias WEBER committed
367
	menuBar->addMenu(menuView);
Tobias WEBER's avatar
Tobias WEBER committed
368
369
	menuBar->addMenu(menuMeshOptions);
	menuBar->addMenu(menuPathOptions);
370
	menuBar->addMenu(menuCalc);
371
	grid->setMenuBar(menuBar);
Tobias WEBER's avatar
Tobias WEBER committed
372
	// ------------------------------------------------------------------------
373
374


Tobias WEBER's avatar
Tobias WEBER committed
375
376
377
	// ------------------------------------------------------------------------
	// output functions
	// ------------------------------------------------------------------------
378
	// export obstacle line segments
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
	auto saveLines = [this]()
	{
		if(!this->m_pathsbuilder)
			return;

		QString dirLast = this->m_sett->value("configspace/cur_dir", "~/").toString();

		QString filename = QFileDialog::getSaveFileName(
			this, "Save Line Segments", dirLast, "XML Files (*.xml)");
		if(filename=="")
			return;

		std::ofstream ofstr{filename.toStdString()};
		if(this->m_pathsbuilder->SaveToLinesTool(ofstr))
			this->m_sett->setValue("configspace/cur_dir", QFileInfo(filename).path());
	};

396
397

	// export figure as pdf file
398
399
400
401
	auto savePDF = [this]()
	{
		QString dirLast = this->m_sett->value("configspace/cur_dir", "~/").toString();
		QString filename = QFileDialog::getSaveFileName(
402
			this, "Save PDF Figure", dirLast, "PDF Files (*.pdf)");
403
404
405
		if(filename=="")
			return;

406
		if(this->SaveFigure(filename))
407
408
409
410
			this->m_sett->setValue("configspace/cur_dir", QFileInfo(filename).path());
	};


411
412
413
414
415
416
417
418
419
420
421
422
423
	// export voronoi graph as dot file
	auto saveGraph = [this]()
	{
		if(!this->m_pathsbuilder)
			return;

		QString dirLast = this->m_sett->value("configspace/cur_dir", "~/").toString();
		QString filename = QFileDialog::getSaveFileName(
			this, "Save DOT Graph", dirLast, "DOT Files (*.dot)");
		if(filename=="")
			return;

		std::ofstream ofstr(filename.toStdString());
Tobias WEBER's avatar
Tobias WEBER committed
424
425
		bool ok = geo::print_graph(this->m_pathsbuilder->
			GetVoronoiResults().GetVoronoiGraph(), ofstr);
426
427
428
429
430
431
		ofstr << std::endl;

		if(ok)
			this->m_sett->setValue("configspace/cur_dir", QFileInfo(filename).path());
	};

Tobias WEBER's avatar
Tobias WEBER committed
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
	// export the current path to an external format
	auto exportPath = [this](PathsExporterFormat fmt) -> bool
	{
		std::shared_ptr<PathsExporterBase> exporter;

		QString dirLast = this->m_sett->value("configspace/cur_dir", "~/").toString();
		QString filename = QFileDialog::getSaveFileName(
			this, "Export Path", dirLast, "Text Files (*.txt)");
		if(filename=="")
			return false;

		switch(fmt)
		{
			case PathsExporterFormat::RAW:
				exporter = std::make_shared<PathsExporterRaw>(filename.toStdString());
				break;
			case PathsExporterFormat::NOMAD:
				exporter = std::make_shared<PathsExporterNomad>(filename.toStdString());
				break;
			case PathsExporterFormat::NICOS:
				exporter = std::make_shared<PathsExporterNicos>(filename.toStdString());
				break;
		}

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

Tobias WEBER's avatar
Tobias WEBER committed
462
		if(!m_pathsbuilder->AcceptExporter(exporter.get(), m_pathvertices))
Tobias WEBER's avatar
Tobias WEBER committed
463
464
465
466
467
468
469
470
471
472
		{
			QMessageBox::critical(this, "Error", "path could not be exported.");
			return false;
		}

		this->m_sett->setValue("configspace/cur_dir", QFileInfo(filename).path());
		return true;
	};
	// ------------------------------------------------------------------------

473

Tobias WEBER's avatar
Tobias WEBER committed
474
	// ------------------------------------------------------------------------
475
	// connections
Tobias WEBER's avatar
Tobias WEBER committed
476
	// ------------------------------------------------------------------------
Tobias WEBER's avatar
Tobias WEBER committed
477

478
	// mouse button down
479
	connect(m_plot.get(), &QCustomPlot::mousePress,
480
481
482
483
	[this](QMouseEvent* evt)
	{
		if(!this->m_plot || !m_moveInstr)
			return;
484

Tobias WEBER's avatar
Tobias WEBER committed
485
486
		const t_real _a4 = this->m_plot->xAxis->pixelToCoord(evt->pos().x());
		const t_real _a2 = this->m_plot->yAxis->pixelToCoord(evt->pos().y());
487
488
489
490

		std::optional<t_real> a1 = _a2 * t_real(0.5) / t_real(180) * tl2::pi<t_real>;
		std::optional<t_real> a4 = _a4 / t_real(180) * tl2::pi<t_real>;

491
492
493
494
495
496
497
498
		bool kf_fixed = true;
		if(m_pathsbuilder && m_pathsbuilder->GetTasCalculator())
		{
			// move monochromator if kf=fixed and analyser otherwise
			if(!std::get<1>(m_pathsbuilder->GetTasCalculator()->GetKfix()))
				kf_fixed = false;
		}

499
		// move instrument
500
501
502
503
		if(kf_fixed)
			this->EmitGotoAngles(a1, std::nullopt, a4, std::nullopt);
		else
			this->EmitGotoAngles(std::nullopt, std::nullopt, a4, a1);
504
505
	});

506
	// mouse move
507
508
509
510
511
512
	connect(m_plot.get(), &QCustomPlot::mouseMove,
	[this](QMouseEvent* evt)
	{
		if(!this->m_plot)
			return;

Tobias WEBER's avatar
Tobias WEBER committed
513
514
		const int x = evt->pos().x();
		const int y = evt->pos().y();
515
516
		const t_real _a4 = this->m_plot->xAxis->pixelToCoord(x);
		const t_real _a2 = this->m_plot->yAxis->pixelToCoord(y);
517

518
519
520
521
522
523
524
525
		bool kf_fixed = true;
		if(m_pathsbuilder && m_pathsbuilder->GetTasCalculator())
		{
			// move monochromator if kf=fixed and analyser otherwise
			if(!std::get<1>(m_pathsbuilder->GetTasCalculator()->GetKfix()))
				kf_fixed = false;
		}

526
527
528
		// move instrument
		if(m_moveInstr && (evt->buttons() & Qt::LeftButton))
		{
529
530
			std::optional<t_real> a1 = _a2 * t_real(0.5) / t_real(180) * tl2::pi<t_real>;
			std::optional<t_real> a4 = _a4 / t_real(180) * tl2::pi<t_real>;
531

532
533
534
535
			if(kf_fixed)
				this->EmitGotoAngles(a1, std::nullopt, a4, std::nullopt);
			else
				this->EmitGotoAngles(std::nullopt, std::nullopt, a4, a1);
536
		}
537

538
539
540
		// set status
		std::ostringstream ostr;
		ostr.precision(g_prec_gui);
Tobias WEBER's avatar
Tobias WEBER committed
541
542

		// show angular coordinates
543
544
545
546
547
		ostr << "2θ_S = " << _a4 << " deg";
		if(kf_fixed)
			ostr << ", 2θ_M = " << _a2 << " deg.";
		else
			ostr << ", 2θ_A = " << _a2 << " deg.";
Tobias WEBER's avatar
Tobias WEBER committed
548
549
550
551

		// show pixel coordinates
		if(m_pathsbuilder)
		{
Tobias WEBER's avatar
Tobias WEBER committed
552
			t_vec2 pix = m_pathsbuilder->AngleToPixel(_a4, _a2);
Tobias WEBER's avatar
Tobias WEBER committed
553
554
555
			ostr <<" Pixel: (" << (int)pix[0] << ", " << (int)pix[1] << ").";
		}

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

Tobias WEBER's avatar
Tobias WEBER committed
559
560

	// path mesh options
561
	connect(acSimplifyContour, &QAction::toggled, [this](bool simplify)
562
563
	{ m_simplifycontour = simplify; });

564
	connect(acGroupLines, &QAction::toggled, [this](bool group)
565
	{ m_grouplines = group; });
Tobias WEBER's avatar
Tobias WEBER committed
566

567
	connect(acSplitContour, &QAction::toggled, [this](bool split)
568
	{ m_splitcontour = split; });
569

570
	connect(acCalcVoro, &QAction::toggled, [this](bool calc)
571
	{ m_calcvoronoi = calc; });
572

573
	connect(acUseRegionFunc, &QAction::toggled, [this](bool b)
574
575
	{ m_use_region_function = b; });

576
	connect(acSubdivPath, &QAction::toggled, [this](bool subdiv)
Tobias WEBER's avatar
Tobias WEBER committed
577
578
	{ m_subdivide_path = subdiv; });

579
	connect(acBackendBoost, &QAction::toggled, [this](bool checked)
580
581
582
583
584
	{
		if(checked)
			m_voronoibackend = VoronoiBackend::BOOST;
	});

585
	connect(acBackendCgal, &QAction::toggled, [this](bool checked)
586
587
588
589
590
591
	{
		if(checked)
			m_voronoibackend = VoronoiBackend::CGAL;
	});


Tobias WEBER's avatar
Tobias WEBER committed
592
593

	// path options
594
	connect(acStrategyShortest, &QAction::toggled, [this](bool checked)
Tobias WEBER's avatar
Tobias WEBER committed
595
	{
Tobias WEBER's avatar
Tobias WEBER committed
596
597
598
599
600
601
		if(checked)
		{
			m_pathstrategy = PathStrategy::SHORTEST;
			if(m_autocalcpath)
				CalculatePath();
		}
Tobias WEBER's avatar
Tobias WEBER committed
602
603
	});

604
	connect(acStrategyPenaliseWalls, &QAction::toggled, [this](bool checked)
Tobias WEBER's avatar
Tobias WEBER committed
605
	{
Tobias WEBER's avatar
Tobias WEBER committed
606
607
608
609
610
611
		if(checked)
		{
			m_pathstrategy = PathStrategy::PENALISE_WALLS;
			if(m_autocalcpath)
				CalculatePath();
		}
Tobias WEBER's avatar
Tobias WEBER committed
612
613
	});

614
	connect(acSimplifyContour, &QAction::toggled, [this](bool simplify)
Tobias WEBER's avatar
Tobias WEBER committed
615
616
	{ m_simplifycontour = simplify; });

617
	connect(acAutocalcPath, &QAction::toggled, [this](bool calc)
Tobias WEBER's avatar
Tobias WEBER committed
618
619
	{ m_autocalcpath = calc; });

620
621
622
623
	connect(acSyncPath, &QAction::toggled, [this](bool sync)
	{ m_syncpath = sync; });

	connect(acMoveTarget, &QAction::toggled, [this](bool b)
Tobias WEBER's avatar
Tobias WEBER committed
624
625
	{ m_movetarget = b; });

Tobias WEBER's avatar
Tobias WEBER committed
626
627

	// view
628
	connect(acEnableZoom, &QAction::toggled, [this](bool enableZoom)
629
	{ this->SetInstrumentMovable(!enableZoom); });
630

631
	connect(acResetZoom, &QAction::triggered, [this]()
632
633
634
635
	{
		m_plot->rescaleAxes();
		m_plot->replot();
	});
636

Tobias WEBER's avatar
Tobias WEBER committed
637
638

	// export
Tobias WEBER's avatar
Tobias WEBER committed
639
640
641
642
643
	connect(acExportRaw, &QAction::triggered, this, [exportPath]()
	{
		exportPath(PathsExporterFormat::RAW);
	});

Tobias WEBER's avatar
Tobias WEBER committed
644
645
646
647
648
649
650
651
652
653
	connect(acExportNomad, &QAction::triggered, this, [exportPath]()
	{
		exportPath(PathsExporterFormat::NOMAD);
	});

	connect(acExportNicos, &QAction::triggered, this, [exportPath]()
	{
		exportPath(PathsExporterFormat::NICOS);
	});

Tobias WEBER's avatar
Tobias WEBER committed
654
655

	// file
656
657
	connect(acSaveLines, &QAction::triggered, this, saveLines);
	connect(acSavePDF, &QAction::triggered, this, savePDF);
658
	connect(acSaveGraph, &QAction::triggered, this, saveGraph);
Tobias WEBER's avatar
Tobias WEBER committed
659
660
661
	connect(acQuit, &QAction::triggered, this, &ConfigSpaceDlg::accept);


662
663
664
665
	// edit
	connect(acCopy, &QAction::triggered, this, &ConfigSpaceDlg::CopyFigure);


Tobias WEBER's avatar
Tobias WEBER committed
666
	// calculate
667
668
	connect(acCalcMesh, &QAction::triggered, this, &ConfigSpaceDlg::CalculatePathMesh);
	connect(acCalcPath, &QAction::triggered, this, &ConfigSpaceDlg::CalculatePath);
Tobias WEBER's avatar
Tobias WEBER committed
669
670
671


	// buttons
672
	connect(btnCalc, &QPushButton::clicked, this, &ConfigSpaceDlg::CalculatePathMesh);
Tobias WEBER's avatar
Tobias WEBER committed
673
	connect(btnSave, &QPushButton::clicked, savePDF);
674
	connect(btnClose, &QPushButton::clicked, this, &ConfigSpaceDlg::accept);
Tobias WEBER's avatar
Tobias WEBER committed
675
676
	// ------------------------------------------------------------------------

677
678

	SetInstrumentMovable(m_moveInstr);
679
680
681
682
683
}


ConfigSpaceDlg::~ConfigSpaceDlg()
{
Tobias WEBER's avatar
Tobias WEBER committed
684
	UnsetPathsBuilder();
685
686
687
688
689
690
691
}


void ConfigSpaceDlg::accept()
{
	if(m_sett)
		m_sett->setValue("configspace/geo", saveGeometry());
692
	QDialog::accept();
693
}
694
695


Tobias WEBER's avatar
Tobias WEBER committed
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
void ConfigSpaceDlg::UpdatePlotRanges()
{
	if(m_plot)
	{
		m_plot->xAxis->setRange(
			m_starta4/tl2::pi<t_real>*180.,
			m_enda4/tl2::pi<t_real>*180.);

		m_plot->yAxis->setRange(
			m_starta2/tl2::pi<t_real>*180.,
			m_enda2/tl2::pi<t_real>*180.);
	}

	if(m_colourMap)
	{
		m_colourMap->data()->setRange(
712
			QCPRange{m_starta4/tl2::pi<t_real>*180., m_enda4/tl2::pi<t_real>*180.},
Tobias WEBER's avatar
Tobias WEBER committed
713
714
715
716
717
			QCPRange{m_starta2/tl2::pi<t_real>*180., m_enda2/tl2::pi<t_real>*180.});
	}
}


718
719
720
721
722
/**
 * update the current instrument position indicator if the instrument has moved
 */
void ConfigSpaceDlg::UpdateInstrument(const Instrument& instr, const t_real* sensesCCW)
{
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
	bool kf_fixed = true;
	std::size_t mono_idx = 0;
	if(m_pathsbuilder && m_pathsbuilder->GetTasCalculator())
	{
		// move monochromator if kf=fixed and analyser otherwise
		if(!std::get<1>(m_pathsbuilder->GetTasCalculator()->GetKfix()))
		{
			kf_fixed = false;
			mono_idx = 2;
		}
	}

	if(kf_fixed)
		m_curMonoScatteringAngle = instr.GetMonochromator().GetAxisAngleOut();
	else
		m_curMonoScatteringAngle = instr.GetAnalyser().GetAxisAngleOut();
739
	m_curSampleScatteringAngle = instr.GetSample().GetAxisAngleOut();
740
741
742

	if(sensesCCW)
	{
743
		m_curMonoScatteringAngle *= sensesCCW[mono_idx];
744
		m_curSampleScatteringAngle *= sensesCCW[1];
745
746
747
	}

	QVector<t_real> x, y;
748
749
	x << m_curSampleScatteringAngle / tl2::pi<t_real> * t_real(180);
	y << m_curMonoScatteringAngle / tl2::pi<t_real> * t_real(180);
750
751

	m_instrposplot->setData(x, y);
752
	m_plot->replot();
753
754
755

	if(m_autocalcpath)
		CalculatePath();
756
757
758
759
760
761
}


/**
 * update the current target position indicator
 */
Tobias WEBER's avatar
Tobias WEBER committed
762
void ConfigSpaceDlg::UpdateTarget(t_real monoScAngle, t_real sampleScAngle, const t_real* sensesCCW)
763
{
764
765
766
767
768
769
770
771
	std::size_t mono_idx = 0;
	if(m_pathsbuilder && m_pathsbuilder->GetTasCalculator())
	{
		// move monochromator if kf=fixed and analyser otherwise
		if(!std::get<1>(m_pathsbuilder->GetTasCalculator()->GetKfix()))
			mono_idx = 2;
	}

772
773
774
	m_targetMonoScatteringAngle = monoScAngle;
	m_targetSampleScatteringAngle = sampleScAngle;

Tobias WEBER's avatar
Tobias WEBER committed
775
776
	if(sensesCCW)
	{
777
		m_targetMonoScatteringAngle *= sensesCCW[mono_idx];
Tobias WEBER's avatar
Tobias WEBER committed
778
779
780
		m_targetSampleScatteringAngle *= sensesCCW[1];
	}

781
	QVector<t_real> x, y;
782
783
	x << m_targetSampleScatteringAngle / tl2::pi<t_real> * t_real(180);
	y << m_targetMonoScatteringAngle / tl2::pi<t_real> * t_real(180);
784
785

	m_targetposplot->setData(x, y);
786
	m_plot->replot();
787
788
789

	if(m_autocalcpath)
		CalculatePath();
790
791
792
}


793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
/**
 * either move instrument by clicking in the plot or enable plot zoom mode
 */
void ConfigSpaceDlg::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);
	}
}


818
819
820
821
822
823
824
/**
 * set instrument angles to the specified ones
 */
void ConfigSpaceDlg::EmitGotoAngles(std::optional<t_real> a1,
	std::optional<t_real> a3, std::optional<t_real> a4,
	std::optional<t_real> a5)
{
Tobias WEBER's avatar
Tobias WEBER committed
825
826
827
	if(m_autocalcpath)
		CalculatePath();

Tobias WEBER's avatar
Tobias WEBER committed
828
	emit GotoAngles(a1, a3, a4, a5, m_movetarget);
829
830
831
}


832
/**
833
 * calculate the mesh of possible instrument paths
834
 */
835
void ConfigSpaceDlg::CalculatePathMesh()
836
{
Tobias WEBER's avatar
Tobias WEBER committed
837
	if(!m_pathsbuilder || !m_pathsbuilder->GetInstrumentSpace())
838
		return;
Tobias WEBER's avatar
Tobias WEBER committed
839
840
	if(m_block_calc)
		return;
841

842
	m_pathsbuilder->StartPathMeshWorkflow();
Tobias WEBER's avatar
Tobias WEBER committed
843
	const Instrument& instr = m_pathsbuilder->GetInstrumentSpace()->GetInstrument();
844

845
846
847
848
849
850
851
852
	bool kf_fixed = true;
	if(m_pathsbuilder && m_pathsbuilder->GetTasCalculator())
	{
		// move monochromator if kf=fixed and analyser otherwise
		if(!std::get<1>(m_pathsbuilder->GetTasCalculator()->GetKfix()))
			kf_fixed = false;
	}

Tobias WEBER's avatar
Tobias WEBER committed
853
	// plot angular steps
Tobias WEBER's avatar
Tobias WEBER committed
854
855
	t_real da2 = m_spinDelta2ThM->value() / 180. * tl2::pi<t_real>;
	t_real da4 = m_spinDelta2ThS->value() / 180. * tl2::pi<t_real>;
856

Tobias WEBER's avatar
Tobias WEBER committed
857
	// get the angular limits from the instrument model
858
859
860
861
862
863
864
865
866
867
	if(kf_fixed)
	{
		m_starta2 = instr.GetMonochromator().GetAxisAngleOutLowerLimit();
		m_enda2 = instr.GetMonochromator().GetAxisAngleOutUpperLimit();
	}
	else
	{
		m_starta2 = instr.GetAnalyser().GetAxisAngleOutLowerLimit();
		m_enda2 = instr.GetAnalyser().GetAxisAngleOutUpperLimit();
	}
Tobias WEBER's avatar
Tobias WEBER committed
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
	m_starta4 = instr.GetSample().GetAxisAngleOutLowerLimit();
	m_enda4 = instr.GetSample().GetAxisAngleOutUpperLimit();

	// angular padding
	t_real padding = 4;
	m_starta2 -= padding * da2;
	m_enda2 += padding * da2;
	m_starta4 -= padding * da4;
	m_enda4 += padding * da4;

	UpdatePlotRanges();

	m_status->setText("Clearing old paths.");
	m_pathsbuilder->Clear();

883
	m_status->setText("Calculating configuration space.");
Tobias WEBER's avatar
Tobias WEBER committed
884
	if(!m_pathsbuilder->CalculateConfigSpace(da2, da4,
Tobias WEBER's avatar
Tobias WEBER committed
885
		m_starta2, m_enda2, m_starta4, m_enda4))
Tobias WEBER's avatar
Tobias WEBER committed
886
887
	{
		m_status->setText("Error: Configuration space calculation failed.");
888
		m_pathsbuilder->FinishPathMeshWorkflow(false);
Tobias WEBER's avatar
Tobias WEBER committed
889
890
		return;
	}
891

892
893
894
895
	m_status->setText("Calculating wall positions index tree.");
	if(!m_pathsbuilder->CalculateWallsIndexTree())
	{
		m_status->setText("Error: Wall positions index tree calculation failed.");
896
		m_pathsbuilder->FinishPathMeshWorkflow(false);
897
898
899
		return;
	}

900
	m_status->setText("Calculating obstacle contour lines.");
901
	if(!m_pathsbuilder->CalculateWallContours(m_simplifycontour, m_splitcontour))
Tobias WEBER's avatar
Tobias WEBER committed
902
903
	{
		m_status->setText("Error: Obstacle contour lines calculation failed.");
904
		m_pathsbuilder->FinishPathMeshWorkflow(false);
Tobias WEBER's avatar
Tobias WEBER committed
905
906
		return;
	}
Tobias WEBER's avatar
Tobias WEBER committed
907

908
	m_status->setText("Calculating line segments.");
Tobias WEBER's avatar
Tobias WEBER committed
909
	if(!m_pathsbuilder->CalculateLineSegments(m_use_region_function))
Tobias WEBER's avatar
Tobias WEBER committed
910
911
	{
		m_status->setText("Error: Line segment calculation failed.");
912
		m_pathsbuilder->FinishPathMeshWorkflow(false);
Tobias WEBER's avatar
Tobias WEBER committed
913
914
		return;
	}
915

916
	if(m_calcvoronoi)
Tobias WEBER's avatar
Tobias WEBER committed
917
	{
918
		m_status->setText("Calculating Voronoi regions.");
919
920
		if(!m_pathsbuilder->CalculateVoronoi(m_grouplines, m_voronoibackend,
			m_use_region_function))
921
922
		{
			m_status->setText("Error: Voronoi regions calculation failed.");
923
			m_pathsbuilder->FinishPathMeshWorkflow(false);
924
925
			return;
		}
Tobias WEBER's avatar
Tobias WEBER committed
926
	}
927

928
	m_status->setText("Calculation finished.");
Tobias WEBER's avatar
Tobias WEBER committed
929
	RedrawVoronoiPlot();
930

931
	m_pathsbuilder->FinishPathMeshWorkflow(true);
932
933
	// signal the availability of a path mesh
	emit PathMeshAvailable();
Tobias WEBER's avatar
Tobias WEBER committed
934
935
936
}


937
938
939
940
941
/**
 * calculate the instrument path from the current to the target position
 */
void ConfigSpaceDlg::CalculatePath()
{
Tobias WEBER's avatar
Tobias WEBER committed
942
943
944
	if(m_block_calc)
		return;

Tobias WEBER's avatar
Tobias WEBER committed
945
946
	m_pathvertices.clear();

947
948
949
950
	if(!m_pathsbuilder)
		return;

	// find path from current to target position
Tobias WEBER's avatar
Tobias WEBER committed
951
	InstrumentPath path = m_pathsbuilder->FindPath(
952
		m_curMonoScatteringAngle, m_curSampleScatteringAngle,
Tobias WEBER's avatar
Tobias WEBER committed
953
954
		m_targetMonoScatteringAngle, m_targetSampleScatteringAngle,
		m_pathstrategy);
955

Tobias WEBER's avatar
Tobias WEBER committed
956
957
	if(!path.ok)
	{
958
959
		m_pathvertices.clear();

Tobias WEBER's avatar
Tobias WEBER committed
960
		m_status->setText("Error: No path could be found.");
Tobias WEBER's avatar
Tobias WEBER committed
961
962
		if(!m_autocalcpath)
			QMessageBox::critical(this, "Error", "No path could be found.");
963
964
965
966
967
968
		//return;
	}
	else
	{
		// get the vertices on the path
		m_pathvertices = m_pathsbuilder->GetPathVertices(path, m_subdivide_path, true);
Tobias WEBER's avatar
Tobias WEBER committed
969
970
	}

Tobias WEBER's avatar
Tobias WEBER committed
971
	RedrawPathPlot();
972
973
974

	if(m_syncpath)
		emit PathAvailable(path);
975
976
977
}


978
979
980
/**
 * associate a PathsBuilder and register the progress dialog callback
 */
Tobias WEBER's avatar
Tobias WEBER committed
981
982
983
984
985
986
void ConfigSpaceDlg::SetPathsBuilder(PathsBuilder* builder)
{
	UnsetPathsBuilder();

	m_pathsbuilder = builder;
	m_pathsbuilderslot = m_pathsbuilder->AddProgressSlot(
987
		[this](CalculationState state, t_real progress, const std::string& message) -> bool
Tobias WEBER's avatar
Tobias WEBER committed
988
		{
989
990
			if(this->thread() == QThread::currentThread())
			{
991
				return this->PathsBuilderProgress(state, progress, message);
992
993
994
995
996
997
998
			}
			else
			{
				// alternate call via meta object when coming from another thread
				bool ok = true;
				QMetaObject::invokeMethod(this, "PathsBuilderProgress", Qt::QueuedConnection,
					//Q_RETURN_ARG(bool, ok),
999
					Q_ARG(CalculationState, state),
1000
1001
1002
1003
1004
					Q_ARG(t_real, progress),
					Q_ARG(const std::string&, message)
				);
				return ok;
			}
Tobias WEBER's avatar
Tobias WEBER committed
1005
		});
1006
1007
1008

	// if the paths builder already has a path mesh, display it
	RedrawVoronoiPlot();
Tobias WEBER's avatar
Tobias WEBER committed
1009
1010
1011
}


1012
1013
1014
/**
 * disassociate a PathsBuilder and unregister the progress dialog callback
 */
Tobias WEBER's avatar
Tobias WEBER committed
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
void ConfigSpaceDlg::UnsetPathsBuilder()
{
	if(m_pathsbuilder)
	{
		m_pathsbuilderslot.disconnect();
		m_pathsbuilder = nullptr;
	}
}


Tobias WEBER's avatar
Tobias WEBER committed
1025
void ConfigSpaceDlg::ClearVoronoiPlotCurves()
1026
{
1027
	// TODO: optimise, as this takes too much time
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
	for(auto* plot : m_vorocurves)
	{
		m_plot->removePlottable(plot);
		//delete plot;
	}

	m_vorocurves.clear();
}


Tobias WEBER's avatar
Tobias WEBER committed
1038
1039
1040
1041
1042
1043
1044
1045
void ConfigSpaceDlg::ClearPathPlotCurve()
{
	m_plot->removePlottable(m_pathcurve);
	//delete m_pathcurve;
}


void ConfigSpaceDlg::AddVoronoiPlotCurve(const QVector<t_real>& x, const QVector<t_real>& y,
Tobias WEBER's avatar
Tobias WEBER committed
1046
	t_real width, QColor colour)
1047
1048
1049
1050
1051
{
	QCPCurve *voroplot = new QCPCurve(m_plot->xAxis, m_plot->yAxis);
	voroplot->setLineStyle(QCPCurve::lsLine);
	voroplot->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssNone, 1));
	voroplot->setAntialiased(true);
Tobias WEBER's avatar
Tobias WEBER committed
1052

1053
	QPen voropen = voroplot->pen();
Tobias WEBER's avatar
Tobias WEBER committed
1054
1055
	voropen.setColor(colour);
	voropen.setWidthF(width);
1056
1057
1058
1059
1060
1061
1062
	voroplot->setPen(voropen);

	voroplot->setData(x, y);
	m_vorocurves.push_back(voroplot);
}


Tobias WEBER's avatar
Tobias WEBER committed
1063
1064
void ConfigSpaceDlg::SetPathPlotCurve(const QVector<t_real>& x, const QVector<t_real>& y,
	t_real width, QColor colour)
Tobias WEBER's avatar
Tobias WEBER committed
1065
{
Tobias WEBER's avatar
Tobias WEBER committed
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
	m_pathcurve = new QCPCurve(m_plot->xAxis, m_plot->yAxis);
	m_pathcurve->setLineStyle(QCPCurve::lsLine);
	m_pathcurve->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssNone, 1));
	m_pathcurve->setAntialiased(true);

	QPen voropen = m_pathcurve->pen();
	voropen.setColor(colour);
	voropen.setWidthF(width);
	m_pathcurve->setPen(voropen);

	m_pathcurve->setData(x, y);
}


Tobias WEBER's avatar
Tobias WEBER committed
1080
1081
1082
/**
 * redraw the path mesh
 */
Tobias WEBER's avatar
Tobias WEBER committed
1083
1084
1085
void ConfigSpaceDlg::RedrawVoronoiPlot()
{
	ClearVoronoiPlotCurves();
1086

Tobias WEBER's avatar
Tobias WEBER committed
1087
	// draw wall image
Tobias WEBER's avatar
Tobias WEBER committed
1088
	const auto& img = m_pathsbuilder->GetImage();
Tobias WEBER's avatar
Tobias WEBER committed
1089
1090
	const std::size_t width = img.GetWidth();
	const std::size_t height = img.GetHeight();
1091

Tobias WEBER's avatar
Tobias WEBER committed
1092
1093
1094
	m_colourMap->data()->setSize(width, height);

	for(std::size_t y=0; y<height; ++y)
1095
	{
Tobias WEBER's avatar
Tobias WEBER committed
1096
		for(std::size_t x=0; x<width; ++x)
1097
		{
Tobias WEBER's avatar
Tobias WEBER committed
1098
1099
			using t_pixel = typename std::decay_t<decltype(img)>::value_type;
			t_pixel pixel_val = img.GetPixel(x, y);
1100

Tobias WEBER's avatar
Tobias WEBER committed
1101
			// val > 0 => colliding
1102
			t_real val = std::lerp(t_real(0), t_real(1),
Tobias WEBER's avatar
Tobias WEBER committed
1103
1104
				t_real(pixel_val)/t_real(std::numeric_limits<t_pixel>::max()));
			m_colourMap->data()->setCell(x, y, val);
1105
		}
1106
1107
	}

Tobias WEBER's avatar
Tobias WEBER committed
1108

Tobias WEBER's avatar
Tobias WEBER committed
1109
	// draw wall contours
Tobias WEBER's avatar
Tobias WEBER committed
1110
	const auto& contours = m_pathsbuilder->GetWallContours(true);
Tobias WEBER's avatar
Tobias WEBER committed
1111

Tobias WEBER's avatar
Tobias WEBER committed
1112
	for(const auto& contour : contours)
1113
1114
		for(const auto& vec : contour)
			m_colourMap->data()->setCell(vec[0], vec[1], 0.5);
Tobias WEBER's avatar
Tobias WEBER committed
1115
1116


1117
1118
1119
	// draw linear voronoi edges
	const t_real edge_eps = m_pathsbuilder->GetVoronoiEdgeEpsilon();

Tobias WEBER's avatar
Tobias WEBER committed
1120
	for(const auto& edge : m_pathsbuilder->GetVoronoiResults().GetLinearEdges())