1 #include "F3DOptions.h"
2
3 #include "F3DLog.h"
4 #include "F3DException.h"
5 #include "F3DReaderFactory.h"
6
7 #include <vtk_jsoncpp.h>
8 #include <vtksys/SystemTools.hxx>
9 #include <vtkVersion.h>
10
11 #include <fstream>
12 #include <regex>
13 #include <sstream>
14 #include <utility>
15 #include <vector>
16
17 #include "cxxopts.hpp"
18
19 //----------------------------------------------------------------------------
20 class ConfigurationOptions
21 {
22 public:
ConfigurationOptions(int argc,char ** argv)23 ConfigurationOptions(int argc, char** argv)
24 : Argc(argc)
25 , Argv(argv)
26 {
27 }
28
29 F3DOptions GetOptionsFromArgs(std::vector<std::string>& inputs);
30 bool InitializeDictionaryFromConfigFile(const std::string& userConfigFile);
31
SetFilePathForConfigBlock(const std::string & filePath)32 void SetFilePathForConfigBlock(const std::string& filePath) { this->FilePathForConfigBlock = filePath; }
33
34 protected:
GetOptionConfig(const std::string & option,std::string & configValue) const35 bool GetOptionConfig(const std::string& option, std::string& configValue) const
36 {
37 bool ret = false;
38 for (auto const& it : this->ConfigDic)
39 {
40 std::regex re(it.first);
41 std::smatch matches;
42 if (std::regex_match(this->FilePathForConfigBlock, matches, re))
43 {
44 auto localIt = it.second.find(option);
45 if (localIt != it.second.end())
46 {
47 configValue = localIt->second;
48 ret = true;
49 }
50 }
51 }
52 return ret;
53 }
54
55 template<class T>
ToString(T currValue)56 static std::string ToString(T currValue)
57 {
58 std::stringstream ss;
59 ss << currValue;
60 return ss.str();
61 }
62
ToString(bool currValue)63 static std::string ToString(bool currValue)
64 {
65 return currValue ? "true" : "false";
66 }
67
68 template<class T>
ToString(const std::vector<T> & currValue)69 static std::string ToString(const std::vector<T>& currValue)
70 {
71 std::stringstream ss;
72 for (size_t i = 0; i < currValue.size(); i++)
73 {
74 ss << currValue[i];
75 if (i != currValue.size() - 1)
76 {
77 ss << ",";
78 }
79 }
80 return ss.str();
81 }
82
CollapseName(const std::string & longName,const std::string & shortName) const83 std::string CollapseName(const std::string& longName, const std::string& shortName) const
84 {
85 std::stringstream ss;
86 if (shortName != "")
87 {
88 ss << shortName << ",";
89 }
90 ss << longName;
91 return ss.str();
92 }
93
DeclareOption(cxxopts::OptionAdder & group,const std::string & longName,const std::string & shortName,const std::string & doc) const94 void DeclareOption(cxxopts::OptionAdder& group, const std::string& longName,
95 const std::string& shortName, const std::string& doc) const
96 {
97 group(this->CollapseName(longName, shortName), doc);
98 }
99
100 template<class T>
DeclareOption(cxxopts::OptionAdder & group,const std::string & longName,const std::string & shortName,const std::string & doc,T & var,bool hasDefault=true,bool mayHaveConfig=true,const std::string & argHelp="") const101 void DeclareOption(cxxopts::OptionAdder& group, const std::string& longName,
102 const std::string& shortName, const std::string& doc, T& var, bool hasDefault = true, bool mayHaveConfig = true,
103 const std::string& argHelp = "") const
104 {
105 auto val = cxxopts::value<T>(var);
106 std::string defaultVal;
107 if (hasDefault)
108 {
109 defaultVal = ConfigurationOptions::ToString(var);
110 }
111
112 if (mayHaveConfig)
113 {
114 hasDefault |= this->GetOptionConfig(longName, defaultVal);
115 }
116
117 if (hasDefault)
118 {
119 val = val->default_value(defaultVal);
120 }
121 var = {};
122 group(this->CollapseName(longName, shortName), doc, val, argHelp);
123 }
124
125 template<class T>
DeclareOptionWithImplicitValue(cxxopts::OptionAdder & group,const std::string & longName,const std::string & shortName,const std::string & doc,T & var,const std::string & implicitValue,bool hasDefault=true,bool mayHaveConfig=true,const std::string & argHelp="") const126 void DeclareOptionWithImplicitValue(cxxopts::OptionAdder& group, const std::string& longName,
127 const std::string& shortName, const std::string& doc, T& var, const std::string& implicitValue,
128 bool hasDefault = true, bool mayHaveConfig = true, const std::string& argHelp = "") const
129 {
130 auto val = cxxopts::value<T>(var)->implicit_value(implicitValue);
131 std::string defaultVal;
132 if (hasDefault)
133 {
134 defaultVal = ConfigurationOptions::ToString(var);
135 }
136
137 if (mayHaveConfig)
138 {
139 hasDefault |= this->GetOptionConfig(longName, defaultVal);
140 }
141
142 if (hasDefault)
143 {
144 val = val->default_value(defaultVal);
145 }
146
147 var = {};
148 group(this->CollapseName(longName, shortName), doc, val, argHelp);
149 }
150
151 std::string GetBinarySettingsDirectory();
152 std::string GetSettingsFilePath();
153
154 static std::string GetSystemSettingsDirectory();
155 static std::string GetUserSettingsDirectory();
156
157 void PrintHelpPair(const std::string& key, const std::string& help, int keyWidth = 10, int helpWidth = 70);
158 void PrintHelp(cxxopts::Options& cxxOptions);
159 void PrintVersion();
160 void PrintReadersList();
161 void PrintExtensionsList();
162
163 private:
164 int Argc;
165 char** Argv;
166
167 std::string FilePathForConfigBlock;
168
169 using Dictionnary = std::map<std::string, std::map<std::string, std::string>>;
170 Dictionnary ConfigDic;
171 };
172
173 //----------------------------------------------------------------------------
GetOptionsFromArgs(std::vector<std::string> & inputs)174 F3DOptions ConfigurationOptions::GetOptionsFromArgs(std::vector<std::string>& inputs)
175 {
176 F3DOptions options;
177 try
178 {
179 cxxopts::Options cxxOptions(f3d::AppName, f3d::AppTitle);
180 cxxOptions.positional_help("file1 file2 ...");
181
182 // clang-format off
183 auto grp1 = cxxOptions.add_options();
184 this->DeclareOption(grp1, "input", "", "Input file", inputs, false, false, "<files>");
185 this->DeclareOption(grp1, "output", "", "Render to file", options.Output, false, false,"<png file>");
186 this->DeclareOption(grp1, "no-background", "", "No background when render to file", options.NoBackground);
187 this->DeclareOption(grp1, "help", "h", "Print help");
188 this->DeclareOption(grp1, "version", "", "Print version details");
189 this->DeclareOption(grp1, "readers-list", "", "Print the list of file types");
190 this->DeclareOption(grp1, "extensions-list", "", "Print the list of supported extensions");
191 this->DeclareOption(grp1, "verbose", "", "Enable verbose mode, providing more information about the loaded data in the console output", options.Verbose);
192 this->DeclareOption(grp1, "no-render", "", "Verbose mode without any rendering, only for the first file", options.NoRender);
193 this->DeclareOption(grp1, "quiet", "", "Enable quiet mode, which superseed any verbose options and prevent any console output to be generated at all", options.Quiet);
194 this->DeclareOption(grp1, "axis", "x", "Show axes", options.Axis);
195 this->DeclareOption(grp1, "grid", "g", "Show grid", options.Grid);
196 this->DeclareOption(grp1, "edges", "e", "Show cell edges", options.Edges);
197 this->DeclareOption(grp1, "trackball", "k", "Enable trackball interaction", options.Trackball);
198 this->DeclareOption(grp1, "progress", "", "Show progress bar", options.Progress);
199 this->DeclareOption(grp1, "up", "", "Up direction", options.Up, true, "[-X|+X|-Y|+Y|-Z|+Z]");
200 this->DeclareOption(grp1, "animation-index", "", "Select animation to show", options.AnimationIndex, true, true, "<index>");
201 #if VTK_VERSION_NUMBER > VTK_VERSION_CHECK(9, 0, 20210228)
202 this->DeclareOption(grp1, "camera-index", "", "Select the camera to use", options.CameraIndex, true, true, "<index>");
203 #endif
204 this->DeclareOption(grp1, "geometry-only", "", "Do not read materials, cameras and lights from file", options.GeometryOnly);
205 this->DeclareOption(grp1, "dry-run", "", "Do not read the configuration file", options.DryRun, true, false);
206 this->DeclareOption(grp1, "config", "", "Read a provided configuration file instead of default one", options.UserConfigFile, false, false, "<file path>");
207
208 auto grp2 = cxxOptions.add_options("Material");
209 this->DeclareOption(grp2, "point-sprites", "o", "Show sphere sprites instead of geometry", options.PointSprites);
210 this->DeclareOption(grp2, "point-size", "", "Point size when showing vertices or point sprites", options.PointSize, true, true, "<size>");
211 this->DeclareOption(grp2, "line-width", "", "Line width when showing edges", options.LineWidth, true, true, "<width>");
212 this->DeclareOption(grp2, "color", "", "Solid color", options.SolidColor, true, true, "<R,G,B>");
213 this->DeclareOption(grp2, "opacity", "", "Opacity", options.Opacity, true, true, "<opacity>");
214 this->DeclareOption(grp2, "roughness", "", "Roughness coefficient (0.0-1.0)", options.Roughness, true, true, "<roughness>");
215 this->DeclareOption(grp2, "metallic", "", "Metallic coefficient (0.0-1.0)", options.Metallic, true, true, "<metallic>");
216 this->DeclareOption(grp2, "hdri", "", "Path to an image file that will be used as a light source", options.HDRIFile, false, true, "<file path>");
217 this->DeclareOption(grp2, "texture-base-color", "", "Path to a texture file that sets the color of the object", options.BaseColorTex, false, true, "<file path>");
218 this->DeclareOption(grp2, "texture-material", "", "Path to a texture file that sets the Occlusion, Roughness and Metallic values of the object", options.ORMTex, false, true, "<file path>");
219 this->DeclareOption(grp2, "texture-emissive", "", "Path to a texture file that sets the emitted light of the object", options.EmissiveTex, false, true, "<file path>");
220 this->DeclareOption(grp2, "emissive-factor", "", "Emissive factor. This value is multiplied with the emissive color when an emissive texture is present", options.EmissiveFactor, true, true, "<R,G,B>");
221 this->DeclareOption(grp2, "texture-normal", "", "Path to a texture file that sets the normal map of the object", options.NormalTex, false, true, "<file path>");
222 this->DeclareOption(grp2, "normal-scale", "", "Normal scale affects the strength of the normal deviation from the normal texture", options.NormalScale, true, true, "<normalScale>");
223
224 auto grp3 = cxxOptions.add_options("Window");
225 this->DeclareOption(grp3, "bg-color", "", "Background color", options.BackgroundColor, true, true, "<R,G,B>");
226 this->DeclareOption(grp3, "resolution", "", "Window resolution", options.WindowSize, true, true, "<width,height>");
227 this->DeclareOption(grp3, "fps", "z", "Display frame per second", options.FPS);
228 this->DeclareOption(grp3, "filename", "n", "Display filename", options.Filename);
229 this->DeclareOption(grp3, "metadata", "m", "Display file metadata", options.MetaData);
230 this->DeclareOption(grp3, "fullscreen", "f", "Full screen", options.FullScreen);
231 this->DeclareOption(grp3, "blur-background", "u", "Blur background", options.BlurBackground);
232
233 auto grp4 = cxxOptions.add_options("Scientific visualization");
234 this->DeclareOptionWithImplicitValue(grp4, "scalars", "s", "Color by scalars", options.Scalars, std::string(""), true, true, "<array_name>");
235 this->DeclareOptionWithImplicitValue(grp4, "comp", "y", "Component from the scalar array to color with. -1 means magnitude, -2 or the short option, -y, means direct scalars", options.Component, "-2", true, true, "<comp_index>");
236 this->DeclareOption(grp4, "cells", "c", "Use a scalar array from the cells", options.Cells);
237 this->DeclareOption(grp4, "range", "", "Custom range for the coloring by array", options.Range, false, true, "<min,max>");
238 this->DeclareOption(grp4, "bar", "b", "Show scalar bar", options.Bar);
239 this->DeclareOption(grp4, "colormap", "", "Specify a custom colormap", options.LookupPoints,
240 true, "<color_list>");
241 this->DeclareOption(grp4, "volume", "v", "Show volume if the file is compatible", options.Volume);
242 this->DeclareOption(grp4, "inverse", "i", "Inverse opacity function for volume rendering", options.InverseOpacityFunction);
243
244 auto grpCamera = cxxOptions.add_options("Camera");
245 this->DeclareOption(grpCamera, "camera-position", "", "Camera position", options.CameraPosition, false, true, "<X,Y,Z>");
246 this->DeclareOption(grpCamera, "camera-focal-point", "", "Camera focal point", options.CameraFocalPoint, false, true, "<X,Y,Z>");
247 this->DeclareOption(grpCamera, "camera-view-up", "", "Camera view up", options.CameraViewUp, false, true, "<X,Y,Z>");
248 this->DeclareOption(grpCamera, "camera-view-angle", "", "Camera view angle (non-zero, in degrees)", options.CameraViewAngle, false, true, "<angle>");
249 this->DeclareOption(grpCamera, "camera-azimuth-angle", "", "Camera azimuth angle (in degrees)", options.CameraAzimuthAngle, true, true, "<angle>");
250 this->DeclareOption(grpCamera, "camera-elevation-angle", "", "Camera elevation angle (in degrees)", options.CameraElevationAngle, true, true, "<angle>");
251
252 #if F3D_MODULE_RAYTRACING
253 auto grp5 = cxxOptions.add_options("Raytracing");
254 this->DeclareOption(grp5, "raytracing", "r", "Enable raytracing", options.Raytracing);
255 this->DeclareOption(grp5, "samples", "", "Number of samples per pixel", options.Samples, true, true, "<samples>");
256 this->DeclareOption(grp5, "denoise", "d", "Denoise the image", options.Denoise);
257 #endif
258
259 auto grp6 = cxxOptions.add_options("PostFX (OpenGL)");
260 this->DeclareOption(grp6, "depth-peeling", "p", "Enable depth peeling", options.DepthPeeling);
261 this->DeclareOption(grp6, "ssao", "q", "Enable Screen-Space Ambient Occlusion", options.SSAO);
262 this->DeclareOption(grp6, "fxaa", "a", "Enable Fast Approximate Anti-Aliasing", options.FXAA);
263 this->DeclareOption(grp6, "tone-mapping", "t", "Enable Tone Mapping", options.ToneMapping);
264
265 auto grp7 = cxxOptions.add_options("Testing");
266 this->DeclareOption(grp7, "ref", "", "Reference", options.Reference, false, false, "<png file>");
267 this->DeclareOption(grp7, "ref-threshold", "", "Testing threshold", options.RefThreshold, true, false, "<threshold>");
268 this->DeclareOption(grp7, "interaction-test-record", "", "Path to an interaction log file to record interactions events to", options.InteractionTestRecordFile, false, false, "<file_path>");
269 this->DeclareOption(grp7, "interaction-test-play", "", "Path to an interaction log file to play interaction events from when loading a file", options.InteractionTestPlayFile, false, false,"<file_path>");
270 // clang-format on
271
272 cxxOptions.parse_positional({ "input" });
273
274 int argc = this->Argc;
275 auto result = cxxOptions.parse(argc, this->Argv);
276
277 if (result.count("help") > 0)
278 {
279 this->PrintHelp(cxxOptions);
280 throw F3DExNoProcess();
281 }
282
283 if (result.count("version") > 0)
284 {
285 this->PrintVersion();
286 throw F3DExNoProcess();
287 }
288
289 if (result.count("readers-list") > 0)
290 {
291 this->PrintReadersList();
292 throw F3DExNoProcess();
293 }
294
295 if (result.count("extensions-list") > 0)
296 {
297 this->PrintExtensionsList();
298 throw F3DExNoProcess();
299 }
300 }
301 catch (const cxxopts::OptionException& e)
302 {
303 F3DLog::Print(F3DLog::Severity::Error, "Error parsing options: ", e.what());
304 throw;
305 }
306 return options;
307 }
308
309 //----------------------------------------------------------------------------
PrintHelpPair(const std::string & key,const std::string & help,int keyWidth,int helpWidth)310 void ConfigurationOptions::PrintHelpPair(const std::string& key, const std::string& help, int keyWidth, int helpWidth)
311 {
312 std::stringstream ss;
313 ss << " " << std::left << std::setw(keyWidth) << key << " " << std::setw(helpWidth) << help;
314 F3DLog::Print(F3DLog::Severity::Info, ss.str());
315 }
316
317 //----------------------------------------------------------------------------
PrintHelp(cxxopts::Options & cxxOptions)318 void ConfigurationOptions::PrintHelp(cxxopts::Options& cxxOptions)
319 {
320 const std::vector<std::pair<std::string, std::string> > keys =
321 {
322 { "C", "Cycle point/cell data coloring" },
323 { "S", "Cycle array to color with" },
324 { "Y", "Cycle array component to color with" },
325 { "B", "Toggle the scalar bar display" },
326 { "V", "Toggle volume rendering" },
327 { "I", "Toggle inverse volume opacity" },
328 { "O", "Toggle point sprites rendering" },
329 { "P", "Toggle depth peeling" },
330 { "Q", "Toggle SSAO" },
331 { "A", "Toggle FXAA" },
332 { "T", "Toggle tone mapping" },
333 { "E", "Toggle the edges display" },
334 { "X", "Toggle the axes display" },
335 { "G", "Toggle the grid display" },
336 { "N", "Toggle the filename display" },
337 { "M", "Toggle the metadata display" },
338 { "Z", "Toggle the FPS counter display" },
339 { "R", "Toggle raytracing rendering" },
340 { "D", "Toggle denoising when raytracing" },
341 { "F", "Toggle full screen" },
342 { "U", "Toggle blur background" },
343 { "K", "Toggle trackball interaction" },
344 { "H", "Toggle cheat sheet display" },
345 { "?", "Dump camera state to the terminal" },
346 { "Escape", "Quit" },
347 { "Enter", "Reset camera to initial parameters" },
348 { "Space", "Play animation if any" },
349 { "Left", "Previous file" },
350 { "Right", "Next file" },
351 { "Up", "Reload current file" }
352 };
353
354 const std::vector<std::pair<std::string, std::string> > examples =
355 {
356 { "f3d file.vtu -xtgans", "View a unstructured mesh in a typical nice looking sciviz style" },
357 { "f3d file.glb -tuqap --hdri=file.hdr", "View a gltf file in a realistic environment" },
358 { "f3d file.ply -so --point-size=0 --comp=-2", "View a point cloud file with direct scalars rendering" },
359 { "f3d folder", "View all files in folder" },
360 };
361
362 F3DLog::SetUseColoring(false);
363 F3DLog::Print(F3DLog::Severity::Info, cxxOptions.help());
364 F3DLog::Print(F3DLog::Severity::Info, "Keys:");
365 for (const auto& key : keys)
366 {
367 this->PrintHelpPair(key.first, key.second);
368 }
369
370 F3DLog::Print(F3DLog::Severity::Info, "\nExamples:");
371 for (const auto& example : examples)
372 {
373 this->PrintHelpPair(example.first, example.second, 50);
374 }
375 F3DLog::Print(F3DLog::Severity::Info, "\nReport bugs to https://github.com/f3d-app/f3d/issues");
376 F3DLog::SetUseColoring(true);
377 }
378
379 //----------------------------------------------------------------------------
PrintVersion()380 void ConfigurationOptions::PrintVersion()
381 {
382 std::string version = f3d::AppName + " " + f3d::AppVersion + "\n\n";
383
384 version += f3d::AppTitle;
385 version += "\nVersion: ";
386 version += f3d::AppVersion;
387 version += "\nBuild date: ";
388 version += f3d::AppBuildDate;
389 version += "\nSystem: ";
390 version += f3d::AppBuildSystem;
391 version += "\nCompiler: ";
392 version += f3d::AppCompiler;
393 version += "\nRayTracing module: ";
394 #if F3D_MODULE_RAYTRACING
395 version += "ON";
396 #else
397 version += "OFF";
398 #endif
399 version += "\nExodus module: ";
400 #if F3D_MODULE_EXODUS
401 version += "ON";
402 #else
403 version += "OFF";
404 #endif
405 version += "\nOpenCASCADE module: ";
406 #if F3D_MODULE_OCCT
407 version += F3D_OCCT_VERSION;
408 #if F3D_MODULE_OCCT_XCAF
409 version += " (full support)";
410 #else
411 version += " (no metadata)";
412 #endif
413 #else
414 version += "OFF";
415 #endif
416 version += "\nAssimp module: ";
417 #if F3D_MODULE_ASSIMP
418 version += F3D_ASSIMP_VERSION;
419 #else
420 version += "OFF";
421 #endif
422 version += "\nVTK version: ";
423 version += std::string(VTK_VERSION) + std::string(" (build ") +
424 std::to_string(VTK_BUILD_VERSION) + std::string(")");
425
426 version += "\n\nCopyright (C) 2019-2021 Kitware SAS.";
427 version += "\nCopyright (C) 2021-2022 Michael Migliore, Mathieu Westphal.";
428 version += "\nLicense BSD-3-Clause.";
429 version += "\nWritten by Michael Migliore, Mathieu Westphal and Joachim Pouderoux.";
430
431 F3DLog::SetUseColoring(false);
432 F3DLog::Print(F3DLog::Severity::Info, version);
433 F3DLog::SetUseColoring(true);
434 }
435
436 //----------------------------------------------------------------------------
PrintReadersList()437 void ConfigurationOptions::PrintReadersList()
438 {
439 size_t nameColSize = 0;
440 size_t extsColSize = 0;
441 size_t descColSize = 0;
442
443 const auto& readers = F3DReaderFactory::GetInstance()->GetReaders();
444 if (readers.empty())
445 {
446 F3DLog::Print(F3DLog::Severity::Warning, "No registered reader found!");
447 return;
448 }
449 // Compute the size of the 3 columns
450 for (const auto& reader : readers)
451 {
452 nameColSize = std::max(nameColSize, reader->GetName().length());
453 descColSize = std::max(descColSize, reader->GetLongDescription().length());
454 auto exts = reader->GetExtensions();
455 size_t extLen = 0;
456 int cnt = 0;
457 for (const auto& ext : exts)
458 {
459 if (cnt++ > 0)
460 {
461 extLen++;
462 }
463 extLen += ext.length();
464 }
465 extsColSize = std::max(extsColSize, extLen);
466 }
467 nameColSize++;
468 extsColSize++;
469 descColSize++;
470
471 // Print the rows split in 3 columns
472 std::stringstream headerLine;
473 headerLine << std::left << std::setw(nameColSize) << "Name" << std::setw(extsColSize)
474 << "Extensions" << std::setw(descColSize) << "Description";
475 F3DLog::Print(F3DLog::Severity::Info, headerLine.str());
476 F3DLog::Print(F3DLog::Severity::Info, std::string(nameColSize + extsColSize + descColSize, '-'));
477
478 for (const auto& reader : readers)
479 {
480 std::stringstream readerLine;
481 readerLine << std::left << std::setw(nameColSize) << reader->GetName()
482 << std::setw(extsColSize);
483 auto exts = reader->GetExtensions();
484 unsigned int cnt = 0;
485 std::string extLine;
486 for (const auto& ext : exts)
487 {
488 if (cnt++ > 0)
489 {
490 extLine += ";";
491 }
492 extLine += ext;
493 }
494 readerLine << extLine << std::setw(descColSize) << reader->GetLongDescription();
495 F3DLog::Print(F3DLog::Severity::Info, readerLine.str());
496 }
497 }
498
499 //----------------------------------------------------------------------------
PrintExtensionsList()500 void ConfigurationOptions::PrintExtensionsList()
501 {
502 const auto& readers = F3DReaderFactory::GetInstance()->GetReaders();
503 if (readers.size() == 0)
504 {
505 F3DLog::Print(F3DLog::Severity::Warning, "No registered reader found!");
506 return;
507 }
508 std::stringstream extList;
509 unsigned int cnt = 0;
510 for (const auto& reader : readers)
511 {
512 auto exts = reader->GetExtensions();
513 for (const auto& ext : exts)
514 {
515 if (cnt++ > 0)
516 {
517 extList << ";";
518 }
519 extList << ext;
520 }
521 }
522 F3DLog::Print(F3DLog::Severity::Info, extList.str());
523 }
524
525 //----------------------------------------------------------------------------
InitializeDictionaryFromConfigFile(const std::string & userConfigFile)526 bool ConfigurationOptions::InitializeDictionaryFromConfigFile(const std::string& userConfigFile)
527 {
528 this->ConfigDic.clear();
529
530 std::string configFilePath;
531 if (!userConfigFile.empty())
532 {
533 configFilePath = userConfigFile;
534 }
535 else
536 {
537 configFilePath = this->GetSettingsFilePath();
538 }
539 if (configFilePath.empty())
540 {
541 return false;
542 }
543
544 std::ifstream file;
545 file.open(configFilePath.c_str());
546
547 if (!file.is_open())
548 {
549 F3DLog::Print(
550 F3DLog::Severity::Error, "Unable to open the configuration file ", configFilePath);
551 return false;
552 }
553
554 Json::Value root;
555 Json::CharReaderBuilder builder;
556 builder["collectComments"] = false;
557 std::string errs;
558 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
559 bool success = Json::parseFromStream(builder, file, &root, &errs);
560 if (!success)
561 {
562 F3DLog::Print(
563 F3DLog::Severity::Error, "Unable to parse the configuration file ", configFilePath);
564 F3DLog::Print(F3DLog::Severity::Error, errs);
565 return false;
566 }
567
568 for (auto const& id : root.getMemberNames())
569 {
570 const Json::Value node = root[id];
571 std::map<std::string, std::string> localDic;
572 for (auto const& nl : node.getMemberNames())
573 {
574 const Json::Value v = node[nl];
575 localDic[nl] = v.asString();
576 }
577 this->ConfigDic[id] = localDic;
578 }
579
580 return true;
581 }
582
583 //----------------------------------------------------------------------------
GetUserSettingsDirectory()584 std::string ConfigurationOptions::GetUserSettingsDirectory()
585 {
586 std::string applicationName = "f3d";
587 #if defined(_WIN32)
588 const char* appData = vtksys::SystemTools::GetEnv("APPDATA");
589 if (!appData)
590 {
591 return std::string();
592 }
593 std::string separator("\\");
594 std::string directoryPath(appData);
595 if (directoryPath[directoryPath.size() - 1] != separator[0])
596 {
597 directoryPath.append(separator);
598 }
599 directoryPath += applicationName + separator;
600 #else
601 std::string directoryPath;
602 std::string separator("/");
603
604 // Implementing XDG specifications
605 const char* xdgConfigHome = vtksys::SystemTools::GetEnv("XDG_CONFIG_HOME");
606 if (xdgConfigHome && strlen(xdgConfigHome) > 0)
607 {
608 directoryPath = xdgConfigHome;
609 if (directoryPath[directoryPath.size() - 1] != separator[0])
610 {
611 directoryPath += separator;
612 }
613 }
614 else
615 {
616 const char* home = vtksys::SystemTools::GetEnv("HOME");
617 if (!home)
618 {
619 return std::string();
620 }
621 directoryPath = home;
622 if (directoryPath[directoryPath.size() - 1] != separator[0])
623 {
624 directoryPath += separator;
625 }
626 directoryPath += ".config/";
627 }
628 directoryPath += applicationName + separator;
629 #endif
630 return directoryPath;
631 }
632
633 //----------------------------------------------------------------------------
GetSystemSettingsDirectory()634 std::string ConfigurationOptions::GetSystemSettingsDirectory()
635 {
636 std::string directoryPath = "";
637 // No support implemented for system wide settings on Windows yet
638 #ifndef _WIN32
639 #ifdef __APPLE__
640 // Implementing simple /usr/local/etc/ system wide config
641 directoryPath = "/usr/local/etc/f3d/";
642 #else
643 // Implementing simple /etc/ system wide config
644 directoryPath = "/etc/f3d/";
645 #endif
646 #endif
647 return directoryPath;
648 }
649
650 //----------------------------------------------------------------------------
GetBinarySettingsDirectory()651 std::string ConfigurationOptions::GetBinarySettingsDirectory()
652 {
653 std::string directoryPath = "";
654 std::string errorMsg, programFilePath;
655 if (vtksys::SystemTools::FindProgramPath(this->Argv[0], programFilePath, errorMsg))
656 {
657 // resolve symlinks
658 programFilePath = vtksys::SystemTools::GetRealPath(programFilePath);
659 directoryPath = vtksys::SystemTools::GetProgramPath(programFilePath);
660 std::string separator;
661 #if defined(_WIN32)
662 separator = "\\";
663 if (directoryPath[directoryPath.size() - 1] != separator[0])
664 {
665 directoryPath.append(separator);
666 }
667 #else
668 separator = "/";
669 directoryPath += separator;
670 #endif
671 directoryPath += "..";
672 #if F3D_MACOS_BUNDLE
673 if (vtksys::SystemTools::FileExists(directoryPath + "/Resources"))
674 {
675 directoryPath += "/Resources";
676 }
677 #endif
678 directoryPath = vtksys::SystemTools::CollapseFullPath(directoryPath);
679 directoryPath += separator;
680 }
681 return directoryPath;
682 }
683
684 //----------------------------------------------------------------------------
GetSettingsFilePath()685 std::string ConfigurationOptions::GetSettingsFilePath()
686 {
687 std::string fileName = "config.json";
688 std::string filePath = ConfigurationOptions::GetUserSettingsDirectory() + fileName;
689 if (!vtksys::SystemTools::FileExists(filePath))
690 {
691 filePath = this->GetBinarySettingsDirectory() + fileName;
692 if (!vtksys::SystemTools::FileExists(filePath))
693 {
694 filePath = ConfigurationOptions::GetSystemSettingsDirectory() + fileName;
695 if (!vtksys::SystemTools::FileExists(filePath))
696 {
697 filePath = "";
698 }
699 }
700 }
701 return filePath;
702 }
703
704 //----------------------------------------------------------------------------
705 F3DOptionsParser::F3DOptionsParser() = default;
706
707 //----------------------------------------------------------------------------
708 F3DOptionsParser::~F3DOptionsParser() = default;
709
710 //----------------------------------------------------------------------------
Initialize(int argc,char ** argv)711 void F3DOptionsParser::Initialize(int argc, char** argv)
712 {
713 this->ConfigOptions = std::unique_ptr<ConfigurationOptions>(new ConfigurationOptions(argc, argv));
714 }
715
716 //----------------------------------------------------------------------------
InitializeDictionaryFromConfigFile(const std::string & userConfigFile)717 void F3DOptionsParser::InitializeDictionaryFromConfigFile(const std::string& userConfigFile)
718 {
719 this->ConfigOptions->InitializeDictionaryFromConfigFile(userConfigFile);
720 }
721
722 //----------------------------------------------------------------------------
GetOptionsFromConfigFile(const std::string & filePath)723 F3DOptions F3DOptionsParser::GetOptionsFromConfigFile(const std::string& filePath)
724 {
725 this->ConfigOptions->SetFilePathForConfigBlock(filePath);
726 std::vector<std::string> dummy;
727 return this->ConfigOptions->GetOptionsFromArgs(dummy);
728 }
729
730 //----------------------------------------------------------------------------
GetOptionsFromCommandLine(std::vector<std::string> & files)731 F3DOptions F3DOptionsParser::GetOptionsFromCommandLine(std::vector<std::string>& files)
732 {
733 this->ConfigOptions->SetFilePathForConfigBlock("");
734 return this->ConfigOptions->GetOptionsFromArgs(files);
735 }
736