Fréchet View  1.6.0
A Tool for Exploring Fréchet Distance Algorithms
main.cpp
Go to the documentation of this file.
1 
3 #include <concurrency.h>
4 #include <iostream>
5 #include <QScreen>
6 #include <QProcess>
7 #include <QCommandLineParser>
8 
9 #define QT_SCALE_FACTOR "QT_SCALE_FACTOR"
10 #define QT_AUTO_SCREEN_SCALE_FACTOR "QT_AUTO_SCREEN_SCALE_FACTOR"
11 #define QT_SCREEN_SCALE_FACTORS "QT_SCREEN_SCALE_FACTORS"
12 
13 #ifdef Q_OS_WIN
14 void m4ri_init();
15 #endif
16 
17 using namespace frechet;
18 using namespace app;
19 
20 QStringList argsList(int argc, char* argv[]);
21 bool calculateScaleFactors(qreal minDpi, qreal targetDpi, qreal maxDpi, QByteArray& factors);
22 
23 #ifdef HAS_OPENCL
24 int printCLInfo();
25 #endif
26 
27 bool restart(QStringList args, QByteArray scaleFactors)
28 {
29  QString program = args[0];
30  args.removeAt(0);
31  args.append("--scale");
32  args.append(scaleFactors);
33 
34 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
35  QProcess proc;
36  proc.setProgram(args[0]);
37  proc.setArguments(args);
38  return proc.startDetached();
39 #else
40  // backward compatibility with Qt 5.9...
41  return QProcess::startDetached(program,args);
42 #endif
43 }
44 
45 int main(int argc, char *argv[])
46 {
47 #if defined(Q_OS_WIN) && defined(QT_DEBUG)
48  // Get current flag
49  int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
50 
51  // Turn on leak-checking bit.
52  tmpFlag |= _CRTDBG_ALLOC_MEM_DF;
53  tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
54  tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF;
55  tmpFlag &= ~_CRTDBG_REPORT_FLAG;
56 
57  // Set flag to the new value.
58  _CrtSetDbgFlag( tmpFlag );
59 
60 // m4ri_init();
61 #endif
62 // std::cout << " L1 Cache Size = " << frechet::ConcurrencyContext::cacheSize(1) << std::endl;
63 // std::cout << " L2 Cache Size = " << frechet::ConcurrencyContext::cacheSize(2) << std::endl;
64 // std::cout << " L3 Cache Size = " << frechet::ConcurrencyContext::cacheSize(3) << std::endl;
65 
66  QCoreApplication::setOrganizationName("ti.fu-hagen.de");
67  QCoreApplication::setApplicationName("Frechet View");
68  // TODO Gnome does not recognize UTF-8 characters correctly. Is there a work-around?
69  // this is also the key for QSettings. Don't change it.
70  QCoreApplication::setApplicationVersion("1.6.0");
71 
72  //QCoreApplication::setAttribute(Qt::AA_Use96Dpi); // influences Font scaling
73  //QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
74  // works in a way, but scaling factors are too large (why?)
75 
76  //QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
77  QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
78 
79  //QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
80  //QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
81 
82  QCommandLineParser parser;
83  parser.setApplicationDescription("Fréchet Distance Free-Space Utility");
84  QCommandLineOption optVersion = parser.addVersionOption();
85  QCommandLineOption optHelp = parser.addHelpOption();
86 
87  QCommandLineOption screenScale("scale","gui scale", "scale-value");
88  parser.addPositionalArgument("input","input file (optional)", "[*.svg,ipe,js,jscript]");
89 
90  //<file> (-curve|-polygon|-kfrechet) (-optimize|-decide=<eps>) [-cores=1,2,4,...] [-gpu|-nogpu]
91  /* Command Line Interface */
92  QCommandLineOption optCurve({"curve","c"}, "Fréchet distance for polygonal curves");
93  QCommandLineOption optPoly({"polygon","p"}, "Fréchet distance for simple polygons");
94  QCommandLineOption optKFrechet({"kfrechet","k"}, "k-Fréchet distance\n");
95 
96  QCommandLineOption optOptimize({"optimize","opt"}, "calculate Fréchet distance (exact)");
97  QCommandLineOption optDecide({"decide","dec"}, "decide whether Fréchet distance <= epsilon", "epsilon");
98  QCommandLineOption optApprox({"approx","app"}, "approximate to limited accuracy\n", "accuracy");
99 
100  QCommandLineOption optCores({"cores","cpu"}, "run parallel on multiple cores", "cores");
101  QCommandLineOption optGpu("gpu", "GPU support");
102  QCommandLineOption optTile("tile", "matrix tile size (n,m)", "tile");
103  QCommandLineOption optTopo("large", "large polygons\n");
104 
105  // Algorithm
106  parser.addOption(optCurve);
107  parser.addOption(optPoly);
108  parser.addOption(optKFrechet);
109  // Variant
110  parser.addOption(optDecide);
111  parser.addOption(optOptimize);
112  parser.addOption(optApprox);
113  // Concurrency
114  parser.addOption(optCores);
115  parser.addOption(optGpu);
116  parser.addOption(optTile);
117  parser.addOption(optTopo);
118  parser.addOption(screenScale);
119 
120  QStringList args = argsList(argc,argv);
121  bool optOk = parser.parse(args);
122 
123  if (!optOk) {
124  QCoreApplication qtApp(argc, argv);
125  std::cout << parser.errorText().toStdString() << std::endl << std::endl;
126  parser.showHelp();
127  return -1;
128  }
129 
130  if (parser.isSet(optVersion)) {
131  QCoreApplication qtApp(argc, argv);
132  parser.showVersion();
133  return -1;
134  }
135 
136  if (parser.isSet(optHelp)) {
137  QCoreApplication qtApp(argc, argv);
138  parser.showHelp();
139  return -1;
140  }
141 
142  // Concurrency parameters
143  int cores=0;
144  if (parser.isSet("cores")) {
145  bool intok;
146  cores = parser.value(optCores).toInt(&intok);
147  if (!intok) throw std::runtime_error("Number of cores expected.");
148  }
149 
151 
152  bool gpu = parser.isSet(optGpu);
153  bool topo = parser.isSet(optTopo); // only headless!
154 
155  // Input file parameter
156  QString inputFile = "";
157  if (! parser.positionalArguments().isEmpty())
158  inputFile = parser.positionalArguments().first();
159 
160  // Algorithm parameters
161  if ( parser.isSet(optCurve) || parser.isSet(optKFrechet) || parser.isSet(optPoly)
162  || parser.isSet(optOptimize) || parser.isSet(optDecide))
163  {
164  // run headless
165  QCoreApplication qtApp(argc,argv);
166  FrechetViewApplication frApp(argv[0],false);
167  parser.process(args);
168 
169  if (gpu) {
170  size2_t max_tile = { 0,0 };
171  if (parser.isSet(optTile)) {
172  QString tile_str = parser.value(optTile);
173  QStringList tile_strs = tile_str.split(QRegExp("\\D+"), QString::SkipEmptyParts);
174  if (tile_strs.size() >= 1) max_tile[0] = tile_strs[0].toUInt();
175  if (tile_strs.size() >= 2) max_tile[1] = tile_strs[1].toUInt();
176  }
178  }
179 
180  try {
181  if (inputFile.isEmpty()) throw std::runtime_error("Input file expected.");
182  if (! QFileInfo::exists(inputFile)) throw std::runtime_error("File "+inputFile.toStdString()+" not found.");
183 
185 
186  if ((parser.isSet(optCurve) + parser.isSet(optPoly) + parser.isSet(optKFrechet)) > 1)
187  throw std::runtime_error("Please choose only one algorithm.");
188 
189  if (parser.isSet(optCurve)) algo = FrechetViewApplication::Algorithm::ALGORITHM_CURVE;
190  if (parser.isSet(optPoly)) algo = FrechetViewApplication::Algorithm::ALGORITHM_POLYGON;
191  if (parser.isSet(optKFrechet)) algo = FrechetViewApplication::Algorithm::ALGORITHM_K_FRECHET;
192  if (algo==0) algo = FrechetViewApplication::Algorithm::ALGORITHM_CURVE;
193 
194  if (topo && (algo != FrechetViewApplication::Algorithm::ALGORITHM_POLYGON))
195  throw std::runtime_error("Option --large is only applicable to -p.");
196 
197  double accuracy=NAN; // == decide
198  double epsilon;
199  bool ok=true;
200 
201  if (gpu && !ConcurrencyContext::hasGpuSupport())
202  throw std::runtime_error("No compatible GPU found. Please install an OpenCL driver.");
203 
204  if ( (parser.isSet(optDecide)+parser.isSet(optOptimize)+parser.isSet(optApprox)) >= 2 )
205  throw std::runtime_error("Please choose either -decide or -optimize or -approx, not both.");
206  if (parser.isSet(optDecide)) {
207  accuracy=NAN;
208  epsilon = parser.value(optDecide).toDouble(&ok);
209  if (!ok) throw std::runtime_error("Epsilon expected for decision variant.");
210  }
211  if (parser.isSet(optOptimize)) accuracy=0.0; // == critical values
212  if (parser.isSet(optApprox)) accuracy = parser.value(optApprox).toDouble(&ok);
213  if (!ok || (!std::isnan(accuracy) && (accuracy < 0.0)))
214  throw std::runtime_error("Accuracy expected for approximation variant.");
215 
216  // Set up concurrency. Applies only to Poly algorithm.
217  if (gpu==FrechetViewApplication::TriState::YES && !ConcurrencyContext::hasGpuSupport())
218  throw std::runtime_error(">>> ERROR: no viable GPU device found. Use -nogpu instead.");
219 
220  if ((cores > 0) && (gpu != FrechetViewApplication::TriState::DEFAULT)
222  std::cout << ">>> Info: core and gpu settings do not apply for this algorithm. Running on one core instead." << std::endl;
223  else if (ConcurrencyContext::countThreads() > 1)
224  std::cout << " Info: using "
225  <<ConcurrencyContext::countThreads()<<" threads on "
226  <<ConcurrencyContext::countCores()<<" CPU cores."<<std::endl;
227 
229  size2_t max_tile;
231  std::cout << " Info: using "
233  << " with "
235  << " units." << std::endl
236  << " "
237  << "matrix tile = " << max_tile[0] << "," << max_tile[1]
238  << std::endl;
239  }
240 
241  return frApp.commandLineInterface(inputFile, algo, topo, accuracy, epsilon);
242 
243  } catch (std::exception& error) {
244  std::cout << ">>> ERROR: " << error.what() << std::endl << std::endl << std::endl << std::endl;
245  parser.showHelp();
246  return -1;
247  }
248  }
249 
250  // Run with GUI. Adjust scale factor as necessary.
251 
252  /*
253  * Hi-DPI scaling
254  *
255  * ===Linux===
256  * the following environment variables work well:
257  *
258  * export QT_SCALE_FACTOR=1
259  * export QT_AUTO_SCREEN_SCALE_FACTOR=0
260  * export QT_SCREEN_SCALE_FACTORS=2
261  *
262  * other mechanisms (like Qt::AA_EnableHiDPIScaling, etc.) are sub-optimal
263  * The tricky part comes now:
264  * - environment variables must be set before QApplication is constructed
265  * - actual pixel ratios can only be calculated *after* QApplication is constructed
266  * ... a dilemma. We solve it by storing the scale factors in preferences. If stored
267  * preferences do not match actual scale factors, the application is *restarted* with
268  * adjusted settings (but this will only happen once after the screen resolution changed).
269  *
270  * The actual scale factors are retrieved from three locations
271  * (1) process Environment variables
272  * (2) application preferences (QSettings)
273  * (3) command line arguments (overrides settings)
274  * */
275 #ifndef Q_OS_MAC
276  // (1) process Environment variables
277  QByteArray scaleFactors;
278  {
279  QSettings settings;
280  if (parser.isSet(screenScale)) {
281  // (3) command line arguments (override settings)
282  scaleFactors = parser.value("scale").toLocal8Bit();
283  }
284  else
285  { // (1) Environment variable
286  scaleFactors = qgetenv(QT_SCREEN_SCALE_FACTORS);
287 
288  if (scaleFactors.isEmpty()) // fall back to (2) application preferences (QSettings)
289  scaleFactors = settings.value(QT_SCREEN_SCALE_FACTORS).toByteArray();
290  }
291  if (!scaleFactors.isEmpty())
292  settings.setValue(QT_SCREEN_SCALE_FACTORS,scaleFactors);
293  }
294 
295  // qputenv MUST be done BEFORE QApplication is constructed
296  if (!scaleFactors.isEmpty()) {
297  qputenv(QT_SCALE_FACTOR,"1");
298  qputenv(QT_AUTO_SCREEN_SCALE_FACTOR,"0");
299  qputenv(QT_SCREEN_SCALE_FACTORS,scaleFactors);
300  //std::cout << "QT_SCREEN_SCALE_FACTORS=" << scaleFactors.constData() << std::endl;
301  }
302 
303  //std::cout << "Hello from " << QCoreApplication::applicationName().toStdString() << std::endl;
304  QApplication qtApp(argc,argv);
305 
306 
307  // re-assess scale factors
308  // scale factors should be chosen so that the logical resolution
309  // is equal (or close) to 96 dpi. If not, we re-start the whole application
310  // with corrected settings
311  if(! calculateScaleFactors(90,96,120, scaleFactors)
312  && !parser.isSet("scale"))
313  {
314  // relaunch application with corrected scale factors
315 
316  if (restart(args,scaleFactors)) {
317  std::cout << "Relaunch with --scale " << scaleFactors.toStdString() << std::endl;
318  return 0;
319  }
320  else {
321  std::cout << "Relaunch failed " << std::endl;
322  }
323  }
324 #endif // Q_OS_MAC
325 #ifdef Q_OS_MAC
326  QApplication qtApp(argc,argv);
327  //QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
328 #endif
329  if (topo)
330  std::cout << "WARNING: option --large is ignored. Use it only with command line option -p." << std::endl;
331  if (gpu)
332  std::cout << "WARNING: option --gpu is ignored. Use it only with command line option -p." << std::endl;
333 
334  FrechetViewApplication frApp(argv[0], true);
335  frApp.init(inputFile);
336  return qtApp.exec();
337 }
338 
339 QStringList argsList(int argc, char* argv[])
340 {
341  QStringList result;
342  for(int i=0; i < argc; ++i)
343  result += QString::fromLocal8Bit(argv[i]);
344  return result;
345 }
346 
347 
348 
349 bool calculateScaleFactors(qreal minDpi, qreal targetDpi, qreal maxDpi, QByteArray& factors)
350 {
351  bool ok=true;
352  QList<QScreen*> screens = QGuiApplication::screens();
353  foreach(QScreen* screen, screens)
354  {
355 /* std::cout << "screen="<<screen->name().toStdString() << std::endl
356  << "size="<<screen->size().width()<<" x " << screen->size().height() << std::endl
357  << "virtual size="<<screen->virtualSize().width()<<" x " << screen->virtualSize().height() << std::endl
358  << "physical size="<<screen->physicalSize().width()<<" x " << screen->physicalSize().height() << std::endl
359  << "pixel ratio="<<screen->devicePixelRatio() << std::endl
360  << "logical dpi="<<screen->logicalDotsPerInch() << std::endl
361  << "physical dpi="<<screen->physicalDotsPerInch() << std::endl;
362 */
363  qreal screenDpi = screen->logicalDotsPerInch();
364  if ((screenDpi < minDpi) || (screenDpi > maxDpi))
365  ok = false;
366 
367  if (!factors.isEmpty()) factors += ";";
368  factors += QByteArray::number(screen->devicePixelRatio() * screenDpi/targetDpi,'g',2);
369  }
370  return ok;
371 }
QStringList argsList(int argc, char *argv[])
Definition: main.cpp:339
size_t size2_t[2]
tow-dimensional size; used for various OpenCL parameters
Definition: clm4rm.h:67
global definitions for all algorithms.
#define QT_SCALE_FACTOR
Definition: main.cpp:9
static void maxMaxtrixTile(size2_t &)
set tile size for cubic matrix multiplication. Tiles adapt to local memory and/or compute units....
Definition: concurrency.cpp:81
#define QT_SCREEN_SCALE_FACTORS
Definition: main.cpp:11
Algorithm
we have three Fréchet Distance algorithms
static ConcurrencyContext instance
singleton instance
Definition: concurrency.h:61
bool calculateScaleFactors(qreal minDpi, qreal targetDpi, qreal maxDpi, QByteArray &factors)
Definition: main.cpp:349
bool setupGpu(size2_t max_tile)
set up OpenCL context
Definition: concurrency.cpp:87
static std::string gpuName()
Definition: concurrency.cpp:63
Definition: grid.h:18
bool restart(QStringList args, QByteArray scaleFactors)
Definition: main.cpp:27
#define QT_AUTO_SCREEN_SCALE_FACTOR
Definition: main.cpp:10
int main(int argc, char *argv[])
Definition: main.cpp:45
void setup(int max_threads)
set up TBB thread count
Definition: concurrency.cpp:39
compute k-Fréchet distance between two curves