Fréchet View  1.6.0
A Tool for Exploring Fréchet Distance Algorithms
controlpanel.cpp
Go to the documentation of this file.
1 
2 #include <controlpanel.h>
3 #include <QSettings>
4 #include <QMovie>
5 #include <QWheelEvent>
6 
8 #include <k_frechet/kalgorithm.h>
9 
10 using namespace frechet;
11 using namespace poly;
12 using namespace view;
13 using namespace app;
14 
15 const int ControlPanel::DECIMALS = 8;
16 QMovie* ControlPanel::loaderMovie = nullptr;
17 
18 ControlPanel::ControlPanel(QWidget *parent)
19  : QWidget(parent),
20  locale(), movieLabel(nullptr), curve_was_ok(false)
21 {
22  ui.setupUi(this);
23  eps = 0.0;
24  eps_step = 1;
25 
26  ui.toolBox->setCurrentIndex(FrechetViewApplication::ALGORITHM_K_FRECHET);
27  // TODO select algorithm
28 
29  ui.epsSlider->setMinimum(0);
30  ui.epsSlider->setMaximum(100);
31  ui.epsSlider->setTickInterval(5);
32 
33  epsValidator = new QDoubleValidator;
34  epsValidator->setDecimals(DECIMALS);
35  epsValidator->setLocale(locale);
36  ui.epsEdit->setValidator(epsValidator);
37 
38  //connect(ui.kBF, SIGNAL(valueChanged(int)), this, SLOT(clearResults()));
39  connect(ui.showGreedy, SIGNAL(clicked()), this, SLOT(onGreedyButton()), Qt::QueuedConnection);
40  //connect(ui.startBF, SIGNAL(clicked()), this, SIGNAL(startBruteForce()));
41  connect(ui.showBF, SIGNAL(clicked()), this, SLOT(onBFButton()), Qt::QueuedConnection);
42 
43  FrechetViewApplication* app = FrechetViewApplication::instance();
44  connect(ui.optCurve, SIGNAL(clicked()), app, SLOT(startStopOptimiseCurve()), Qt::QueuedConnection);
45  connect(ui.approxCurve, SIGNAL(clicked()), app, SLOT(startStopApproximateCurve()), Qt::QueuedConnection);
46 
47  connect(ui.decidePoly, SIGNAL(clicked()), app, SLOT(startStopDecidePolygon()), Qt::QueuedConnection);
48  connect(ui.optPoly, SIGNAL(clicked()), app, SLOT(startStopOptimisePolygon()), Qt::QueuedConnection);
49  connect(ui.approxPoly, SIGNAL(clicked()), app, SLOT(startStopApproximatePolygon()), Qt::QueuedConnection);
50 
51  // forward signal AlgorithmChanged
52  connect(ui.toolBox, SIGNAL(currentChanged(int)), this, SLOT(onAlgorithmChanged(int)));
53 
54  clearResults();
55 }
56 
57 void ControlPanel::saveSettings(QSettings& settings, QString group)
58 {
59  settings.beginGroup(group);
60 
61  settings.setValue("epsilon", getEpsilon());
62  //settings.setValue("k", ui.kBF->value());
63  settings.setValue("algorithm", FrechetViewApplication::instance()->currentAlgorithm());
64 
65  settings.endGroup();
66 }
67 
68 void ControlPanel::restoreSettings(QSettings& settings, QString group)
69 {
70  settings.beginGroup(group);
71 
72  setEpsilonWithNotify(settings.value("epsilon",30.0).toDouble());
73  //ui.kBF->setValue(settings.value("k",5).toInt());
74  int algo = settings.value("algorithm", FrechetViewApplication::ALGORITHM_K_FRECHET).toInt();
75  FrechetViewApplication::instance()->setCurrentAlgorithm((FrechetViewApplication::Algorithm)algo);
76  ui.toolBox->setCurrentIndex(algo);
77 
78  settings.endGroup();
79 }
80 
81 void ControlPanel::setEpsilon(double eps, bool notify)
82 {
83  // don't trigger signals; respect notify
84  ui.epsSlider->blockSignals(true);
85  ui.epsSlider->setValue(toSliderValue(eps));
86  ui.epsEdit->setText(toString(eps));
87  ui.epsSlider->blockSignals(false);
88 
89  if (eps != this->eps) {
90  clearResults();
91  this->eps = eps;
92  if (notify)
93  emit epsilonChanged(this->eps);
94  }
95 }
96 
97 QString ControlPanel::toString(double number)
98 {
99  // format according to Locale.
100  // sadly, Qt has no such function
101  QString s = QString::number(number);
102  return s.replace('.',locale.decimalPoint());
103 }
104 
105 
106 double ControlPanel::toDouble(QString text)
107 {
108  bool ok;
109  double eps = locale.toDouble(text,&ok);
110  return ok ? eps : NAN;
111 }
112 
114 {
115  return round(x / eps_step);
116 }
117 
119 {
120  return i * eps_step;
121 }
122 
123 void ControlPanel::setEpsilonMax(double eps_max)
124 {
125  epsValidator->setRange(0.0,eps_max,DECIMALS);
126 
127  // find "reasonable" values for eps_step ...
128 
129  if (eps_max <= 10.0) {
130  eps_step = 0.1;
131  }
132  else if (eps_max <= 50.0) {
133  eps_step = 0.5;
134  }
135  else if (eps_max <= 100.0) {
136  eps_step = 1.0;
137  }
138  else {
139  eps_step = round(eps_max/100.0);
140  }
141 
142  ui.epsSlider->setMaximum(eps_max/eps_step);
143  ui.epsSlider->setSingleStep(1);
144  ui.epsSlider->setPageStep(3);
145  ui.epsSlider->setTickInterval(1);
146 
147  double current = toDouble(ui.epsEdit->text());
148  if (std::isnan(current) || (current < 0.0))
149  setEpsilon(0.0,false);
150  else if (current > eps_max)
151  setEpsilon(eps_max,false);
152  else
153  setEpsilon(current,false);
154 }
155 
157 {
158  k::kAlgorithm::ptr alg = FrechetViewApplication::instance()->getKAlgorithm();
159  updateGreedyResult(alg->greedyResult());
160  updateOptimalResult(alg->optimalResult());
161 
163 
164  // button activation and check state
165  if (ui.showGreedy->isChecked())
166  emit showGreedyResult();
167  else if (ui.showBF->isChecked())
168  emit showOptimalResult();
169  else
170  emit hideResult();
171 }
172 
174 {
176 
177  QString text;
178  if (bf.k_optimal > 0) {
179  text = "k="+QVariant(bf.k_optimal).toString();
180  }
181  else if (bf.k_optimal==k::kAlgorithm::NO_SOLUTION) {
182  text = "--";
183  }
184  else {
185  if (bf.k_min > 0)
186  text += QString::number(bf.k_min)+" <= ";
187 
188  switch(bf.k_optimal)
189  {
191  text += "?";
192  break;
194  text += "...";
195  break;
196  }
197 
198  if (bf.k_max > 0)
199  text += " <= "+QString::number(bf.k_max);
200  }
201  ui.resultBF->setText(text);
202 }
203 
205 {
206  switch(k)
207  {
209  ui.resultBF->setText("--");
210  break;
212  ui.resultBF->setText("?");
213  break;
215  ui.resultBF->setText("...");
216  break;
217  default:
218  // see updateOptimalResult(const k::kAlgorithm::BruteForce& bf)
219  break;
220  }
221 
222  showIcon(ui.statusBF,k);
223 
224  switch(k)
225  {
227  ui.showBF->setEnabled(false);
228  ui.showBF->setCheckable(false);
229  ui.showBF->setChecked(false);
230  ui.showBF->setText("Show");
231  break;
233  ui.showBF->setEnabled(true);
234  ui.showBF->setCheckable(false);
235  ui.showBF->setChecked(false);
236  ui.showBF->setText("Go");
237  break;
239  ui.showBF->setEnabled(true);
240  ui.showBF->setCheckable(false);
241  ui.showBF->setChecked(false);
242  ui.showBF->setText("Stop");
243  break;
244  default:
245  ui.showBF->setEnabled(true);
246  ui.showBF->setCheckable(true);
247  ui.showBF->setText("Show");
248  break;
249  }
250 }
251 
253 {
257 }
258 
260 {
261  showIcon(ui.statusGreedy,k);
262 
263  QString text;
264  switch(k)
265  {
267  text="?";
268  break;
270  text="--";
271  break;
273  text="...";
274  break;
275  default:
276  Q_ASSERT(k > 0);
277  Q_ASSERT(k2 > 0);
278  if (k > k2) std::swap(k,k2);
279 
280  text = "k="+QString::number(k);
281  if (k != k2)
282  text += ", "+QString::number(k2);
283  break;
284  }
285 
286  ui.resultGreedy->setText(text);
287 
288  switch(k)
289  {
292  ui.showGreedy->setEnabled(false);
293  ui.showGreedy->setChecked(false);
294  break;
296  ui.showGreedy->setEnabled(false);
297  break;
298  default:
299  Q_ASSERT(k > 0);
300  ui.showGreedy->setEnabled(true);
301  break;
302  }
303 }
304 
306 {
310 }
311 
312 QString ControlPanel::POLY_STATUS [] = {
313  "OK", "no solution",
314  "NOT_SET_UP", // not supposed to happen
315  "",
316  "P is empty", "Q is empty",
317  "P is not closed", "Q is not closed",
318  // beide Kurven müssen einfache Polygone seine
319  "P is not simple", "Q is not simple",
320  // beide Polygone müssen gegen den Uhrzeigersinn orientiert sein
321  "P_NOT_COUNTER_CLOCKWISE", "Q_NOT_COUNTER_CLOCKWISE", // not supposed to happen
322  // für konvexe Polygone könenn wir einen einfacheren Algorithmus verwenden
323  "P is convex", "Q is convex"
324 };
325 
326 
328 {
329  Algorithm::ptr poly = FrechetViewApplication::instance()->getPolyAlgorithm();
330  if (!poly) {
331  ui.resultPoly->setText("?");
332  showIcon(ui.statusPoly, k::kAlgorithm::UNKNOWN);
333  return;
334  }
335 
336  // show poly status and results
337  int status = FrechetViewApplication::instance()->getPolyAlgorithm()->status();
338  if (status<=Algorithm::RUNNING) {
339  ui.resultPoly->setText("...");
340  ui.resultCurve->setText("...");
341  }
342  else {
343  ui.resultPoly->setText(POLY_STATUS[status]);
344  }
345 
346  if (status <= Algorithm::RUNNING) {
347  if (status <= Algorithm::RUNNING_APPROX_POLY)
348  showIcon(ui.statusPoly, k::kAlgorithm::RUNNING);
349  else if (status <= Algorithm::RUNNING_APPROX_CURVE)
350  showIcon(ui.statusCurve, k::kAlgorithm::RUNNING);
351  }
352  else if (FrechetViewApplication::instance()->currentAlgorithm()==FrechetViewApplication::ALGORITHM_CURVE) {
354  ui.resultCurve->setText(curve_was_ok ? "OK":"no solution");
355  }
356 
357  ui.decidePoly->setEnabled (poly->canDecidePoly());
358  ui.optPoly->setEnabled (poly->canOptimisePoly());
359  ui.approxPoly->setEnabled (poly->canOptimisePoly());
360  ui.optCurve->setEnabled (poly->canOptimiseCurve());
361  ui.approxCurve->setEnabled (poly->canOptimiseCurve());
362 
363  ui.decidePoly->setText("Decide");
364  ui.optPoly->setText("Optimise");
365  ui.approxPoly->setText("Approximate");
366  ui.optCurve->setText("Optimise");
367  ui.approxCurve->setText("Approximate");
368 
369  switch(status)
370  {
371  case Algorithm::YES:
372  showIcon(ui.statusPoly, 1);
373  ui.decidePoly->setEnabled(false);
374  break;
375  default:
376  case Algorithm::NO:
378  ui.decidePoly->setEnabled(false);
379  break;
380  case Algorithm::SET_UP:
381  showIcon(ui.statusPoly, k::kAlgorithm::UNKNOWN);
382  ui.decidePoly->setEnabled(true);
383  break;
384  case Algorithm::RUNNING_OPT_CURVE:
385  ui.optCurve->setText("Stop");
386  ui.optCurve->setEnabled(true);
387  break;
388  case Algorithm::RUNNING_OPT_POLY:
389  ui.optPoly->setText("Stop");
390  ui.optPoly->setEnabled(true);
391  break;
392  case Algorithm::RUNNING_APPROX_CURVE:
393  ui.approxCurve->setText("Stop");
394  ui.approxCurve->setEnabled(true);
395  break;
396  case Algorithm::RUNNING_APPROX_POLY:
397  ui.approxPoly->setText("Stop");
398  ui.approxPoly->setEnabled(true);
399  break;
400  case Algorithm::RUNNING_DECIDE_POLY:
401  ui.decidePoly->setText("Stop");
402  ui.decidePoly->setEnabled(true);
403  break;
404  }
405  ui.decidePoly->setChecked(false);
406 }
407 
409 {
410  // when the show-bounds parameter changes.
411  // force update of result sets (if necessary)
412  if (ui.showBF->isChecked())
413  emit showOptimalResult();
414  if (ui.showGreedy->isChecked())
415  emit showGreedyResult();
416 }
417 
419 {
420  if (FrechetViewApplication::instance()->currentAlgorithm()==
421  FrechetViewApplication::ALGORITHM_CURVE)
422  {
423  curve_was_ok = ok;
424  showIcon(ui.statusCurve, ok ? 1 : k::kAlgorithm::NO_SOLUTION);
425  ui.resultCurve->setText(ok ? "OK":"no solution");
426  }
427 
428  if (FrechetViewApplication::instance()->currentAlgorithm()==
429  FrechetViewApplication::ALGORITHM_POLYGON)
430  {
431  // TODO integrate result code and error code (like k-Frechet)
433  }
434 }
435 
437  double eps = toDouble(ui.epsEdit->text());
438  if (! std::isnan(eps))
440 }
441 
442 void ControlPanel::onAlgorithmChanged(int algorithm) {
443  clearResults();
444  FrechetViewApplication::instance()->setCurrentAlgorithm((FrechetViewApplication::Algorithm)algorithm);
445  // force redraw
446  emit epsilonChanged(this->eps);
447 }
448 
450  setEpsilonWithNotify(fromSliderValue(ui.epsSlider->value()));
451 }
452 
454 {
455  // if we have valid Greedy result, show it in FreeSpaceView
456  k::kAlgorithm::ptr alg = FrechetViewApplication::instance()->getKAlgorithm();
457  if (alg->greedyResult().valid() && ui.showGreedy->isChecked()) {
458  ui.showBF->setChecked(false);
459  emit showGreedyResult();
460  }
461  else {
462  emit hideResult();
463  }
464 }
465 
467 {
468  // Four States: Disabled, Go, Stop, Show
469  k::kAlgorithm::ptr alg = FrechetViewApplication::instance()->getKAlgorithm();
470  int k = alg->optimalResult().k_optimal;
471  switch(k)
472  {
474  Q_ASSERT(false); //button must not be enabled
475  break;
477  FrechetViewApplication::instance()->startKBruteForce();
478  break;
480  FrechetViewApplication::instance()->cancelBackgroundJob();
481  break;
482  default:
483  Q_ASSERT(k > 0);
484  if (ui.showBF->isChecked()) {
485  ui.showGreedy->setChecked(false);
486  emit showOptimalResult();
487  }
488  else {
489  emit hideResult();
490  }
491  break;
492  }
493 
494  }
495 
496 
497 void ControlPanel::showIcon(QLabel *label, int k)
498 {
499  if (loaderMovie && (label==movieLabel) && (k!=k::kAlgorithm::RUNNING))
500  loaderMovie->stop();
501 
502  switch(k)
503  {
505  setIcon(label,":/awesome/question-circle.svg", "solution not yet calculated");
506  break;
507  default:
508  Q_ASSERT(k > 0);
509  setIcon(label,":/awesome/check-circle.svg", "found a solution");
510  break;
512  setIcon(label,":/awesome/times-circle.svg", "there is no solution");
513  break;
515  if (!loaderMovie)
516  loaderMovie = new QMovie(":/ajax-loader.gif");
517  movieLabel=label;
518  label->setMovie(loaderMovie);
519  label->setToolTip("calculation in progress");
520  loaderMovie->start();
521  break;
522  }
523 }
524 
525 void ControlPanel::setIcon(QLabel *label, QString file, QString tooltip)
526 {
527  QIcon icon(file);
528  label->setPixmap(icon.pixmap(24,24));
529  label->setToolTip(tooltip);
530 }
531 
532 void ControlPanel::wheelEvent(QWheelEvent* wh)
533 {
534  // forward to slider
535  singleStep(ui.epsSlider, (wh->delta() > 0) ? +1 : -1);
536 }
537 
538 void ControlPanel::keyPressEvent(QKeyEvent* event)
539 {
540  switch(event->key())
541  {
542  case Qt::Key_Right:
543  case Qt::Key_Up:
544  case Qt::Key_Plus: singleStep(ui.epsSlider,+1); break;
545  case Qt::Key_Left:
546  case Qt::Key_Down:
547  case Qt::Key_Minus: singleStep(ui.epsSlider,-1); break;
548 
549  case Qt::Key_PageUp: pageStep(ui.epsSlider,+1); break;
550  case Qt::Key_PageDown: pageStep(ui.epsSlider,-1); break;
551  }
552 }
553 
static const int DECIMALS
number of decimals in the epsilon control
Definition: controlpanel.h:165
void onCurveFinished(bool)
called when the curve algorithm finishes; update the "curve" panel to show latest results
void singleStep(QAbstractSlider *, double factor)
perform a single step on a slider
Definition: baseview.cpp:158
initial status; the algorithm has not ye been run
Definition: kalgorithm.h:132
static QString POLY_STATUS[]
texts to display for algorithm results
Definition: controlpanel.h:220
void swap(gpuword **A, gpuword **B)
void updateGreedyResult(const k::kAlgorithm::Greedy &kalg)
update results of the k-Frechet greedy algorithm
int k_optimal
optimal value so far
Definition: kalgorithm.h:271
void onBFButton()
called when the user clicks the "Show" button in the "brute-force" section. Triggers showOptimalResul...
global definitions for all algorithms.
int toSliderValue(double x)
map an epsilon value to a slider location
QString toString(double x)
format a number for display, with appropriate locale
Results of the Greedy algorithm.
Definition: kalgorithm.h:207
void onEdit()
called after the user has edited the value of epsilon. Triggers epsilonChanged(), which in turn trigg...
QLabel * movieLabel
label that display the rotating icon
Definition: controlpanel.h:215
void showIcon(QLabel *label, int status)
show a status icon in the control panel
void onGreedyButton()
called when the user clicks the "Show" button in the "greedy" section. Triggers showGreedyResult(),...
double fromSliderValue(int i)
map a slider location to a value of epsilon
indicates that the algorithm is currently running
Definition: kalgorithm.h:133
void pageStep(QAbstractSlider *, double factor)
perform a page step on a slider
Definition: baseview.cpp:163
boost::shared_ptr< Algorithm > ptr
(smart) pointer to an Algorithm object
Definition: algorithm.h:236
void updatePolyResult()
update the "Polygon" pabel to show the lastest results of the poly-algorithm
void onShowBounds(bool)
called when the user clicks the "Show Bounds" button
double eps
the current value of epsilon
Definition: controlpanel.h:168
void onAlgorithmChanged(int algorithm)
called when the user selects a algorithm panel. Calls on FrechetViewApplication to update the current...
double eps_step
step size for epsilon slider control
Definition: controlpanel.h:174
static QMovie * loaderMovie
displays a rotatin icon (during a long-running algorithm)
Definition: controlpanel.h:213
int k_yx
number of components, when scanning the y-axis first. 0 if the axes can no be covered.
Definition: kalgorithm.h:215
void setEpsilon(double eps, bool notify)
updates the value of epsilon
void setEpsilonWithNotify(double eps)
update the value of epsilon and raise signals
Definition: controlpanel.h:126
bool curve_was_ok
latest result of the decision algorithm for curves
Definition: controlpanel.h:217
int k_max
upper bound derived from greedy results. k_max = min{greedy.k_xy,greedy.k_yx}
Definition: kalgorithm.h:265
void saveSettings(QSettings &settings, QString group)
store settings to application preferences
void setIcon(QLabel *label, QString text, QString tooltip="")
show a status icon in the control panel
void updateOptimalResult(const k::kAlgorithm::BruteForce &bfres)
update results of the k-Frechet brute-force algorithm
void restoreSettings(QSettings &settings, QString group)
load settings from application preferences
Ui::ControlPanel ui
UI elements (auto-generated by Qt)
Definition: controlpanel.h:177
results of the brute-force algorithm
Definition: kalgorithm.h:258
void clearResults()
reset the k-Frechet panel
void showOptimalResult()
raised when the user clicks the "Show" button in the k-Frechet panel. Updates the free-space diagram ...
QDoubleValidator * epsValidator
validator for epsilon input
Definition: controlpanel.h:172
void wheelEvent(QWheelEvent *) override
Result
negative values for k indicate the special conditions of the computation
Definition: kalgorithm.h:131
QLocale locale
locale used for number formatting
Definition: controlpanel.h:170
int k_min
lower bound derived from greedy results. k_min = max{greedy.k_x,greedy.k_y}
Definition: kalgorithm.h:263
void epsilonChanged(double eps)
raised when the value of epsilon changes (e.g. when the user operates the slider control)....
boost::shared_ptr< kAlgorithm > ptr
smart pointer to an kAlgorithm object
Definition: kalgorithm.h:320
int k_xy
number of components, when scanning the x-axis first. 0 if the axes can no be covered.
Definition: kalgorithm.h:213
void onSlider(int value)
called when the user moves the slider control for epsilon. Triggers epsilonChanged(),...
void keyPressEvent(QKeyEvent *event) override
void hideResult()
raised when the user clicks the "Hide" button in the k-Frechet panel. Resets the free-space diagram t...
double toDouble(QString text)
parse a number input
there is no solution at all
Definition: kalgorithm.h:134
void updateResults()
update the k-Frechet panel to show the latest results of the k-Frechet algorithm
void setEpsilonMax(double eps_max)
update the maximum value of epsilon; adjusts the slider control
void showGreedyResult()
raised when the user clicks the "Show" button in the k-Frechet panel. Updates the free-space diagram ...