-
-
Notifications
You must be signed in to change notification settings - Fork 288
/
Copy pathF3DOptionsTools.cxx
622 lines (561 loc) · 25.9 KB
/
F3DOptionsTools.cxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
#include "F3DOptionsTools.h"
#include "F3DConfig.h"
#include "F3DException.h"
#include "F3DPluginsTools.h"
#include "F3DSystemTools.h"
#include "engine.h"
#include "interactor.h"
#include "log.h"
#include "utils.h"
#include <cassert>
#include <filesystem>
#include <iomanip>
#include <set>
#include <sstream>
#include "cxxopts.hpp"
namespace fs = std::filesystem;
namespace
{
/**
* A struct to configure a complete cxxopts CLI options
* F3D uses exclusively string options except for a few true boolean option (eg: `--version`)
* LongName: The long name, eg: `axis`
* ShortName: The short name, eg: `x`
* HelpText: The help text, for display only when using `--help`
* ValueHelper: Used in help but also to discrimate between boolean option or not
* ImplicitValue: The implicit value when option is provided without a value
*/
struct CLIOption
{
std::string_view LongName;
std::string_view ShortName;
std::string_view HelpText;
std::string_view ValueHelper;
std::string_view ImplicitValue;
};
/**
* A struct to group option into categories
*/
struct CLIGroup
{
std::string_view GroupName;
std::vector<CLIOption> Options;
};
/**
* Declaration of all F3D CLI options except `--input` using above structs
* Order of groups matters in the context of `--help`
*/
// clang-format off
#if F3D_MODULE_RAYTRACING
static inline const std::array<CLIGroup, 9> CLIOptions = {{
#else
static inline const std::array<CLIGroup, 8> CLIOptions = {{
#endif
{ "Applicative",
{ { "output", "", "Render to file", "<png file>", "" },
{ "no-background", "", "No background when render to file", "<bool>", "1" },
{ "help", "h", "Print help", "", "" }, { "version", "", "Print version details", "", "" },
{ "list-readers", "", "Print the list of readers", "", "" },
{ "force-reader", "", "Force a specific reader to be used, disrigarding the file extension", "<reader>", "1"},
{ "list-bindings", "", "Print the list of interaction bindings and exits, ignored with `--no-render`, only considers the first file group.", "<bool>", "1" },
{ "config", "", "Specify the configuration file to use. absolute/relative path or filename/filestem to search in configuration file locations", "<filePath/filename/fileStem>", "" },
{ "no-config", "", "Do not read the configuration file", "<bool>", "1" },
{ "no-render", "", "Do not render anything and quit right after loading the first file, use with --verbose to recover information about a file.", "<bool>", "1" },
{ "rendering-backend", "", "Backend to use when rendering (auto|glx|wgl|egl|osmesa)", "<string>", "" },
{ "list-rendering-backends", "", "Print the list of rendering backends available on this system", "", "" },
{ "max-size", "", "Maximum size in Mib of a file to load, leave empty for unlimited", "<size in Mib>", "" },
#if F3D_MODULE_DMON
{ "watch", "", "Watch current file and automatically reload it whenever it is modified on disk", "<bool>", "1" },
#endif
{ "frame-rate", "", "Frame rate used to refresh animation and other repeated tasks (watch, UI). Does not impact rendering frame rate.", "<fps>", "" },
{ "load-plugins", "", "List of plugins to load separated with a comma", "<paths or names>", "" },
{ "scan-plugins", "", "Scan standard directories for plugins and display available plugins (result can be incomplete)", "", "" },
{ "screenshot-filename", "", "Screenshot filename", "<filename>", "" } } },
{ "General",
{ { "verbose", "", "Set verbose level, providing more information about the loaded data in the console output", "{debug, info, warning, error, quiet}", "debug" },
{ "loading-progress", "", "Show loading progress bar", "<bool>", "1" },
{ "animation-progress", "", "Show animation progress bar", "<bool>", "1" },
{ "multi-file-mode", "", R"(Choose the behavior when opening multiple files. "single" will show one file at a time, "all" will show all files in a single scene.)", "<single|all>", "" },
{ "up", "", "Up direction", "<direction>", "" },
{ "axis", "x", "Show axes", "<bool>", "1" }, { "grid", "g", "Show grid", "<bool>", "1" },
{ "grid-absolute", "", "Position grid at the absolute origin instead of below the model", "<bool>", "1" },
{ "grid-unit", "", "Size of grid unit square, automatically computed by default", "<value>", "" },
{ "grid-subdivisions", "", "Number of grid subdivisions", "<value>", "" },
{ "grid-color", "", "Color of main grid lines", "<color>", "" },
{ "edges", "e", "Show cell edges", "<bool>", "1" },
{ "armature", "", "Enable armature visualization", "<bool>", "1" },
{ "camera-index", "", "Select the camera to use", "<index>", "" },
{ "interaction-trackball", "k", "Enable trackball interaction", "<bool>", "1" },
{ "invert-zoom", "", "Invert zoom direction with right mouse click", "<bool>", "1" },
{ "animation-autoplay", "", "Automatically start animation", "<bool>", "1" },
{ "animation-index", "", "Select animation to show", "<index>", "" },
{ "animation-speed-factor", "", "Set animation speed factor", "<ratio>", "" },
{ "animation-time", "", "Set animation time to load", "<time>", "" },
{ "font-file", "", "Path to a FreeType compatible font file", "<file_path>", ""},
{ "font-scale", "", "Scale fonts", "<ratio>", ""},
{ "command-script", "", "Path to a script file containing commands to execute", "<file_path>", "" } } },
{ "Material",
{ {"point-sprites", "o", "Show sphere sprites instead of surfaces", "<bool>", "1" },
{"point-sprites-type", "", "Point sprites type", "<sphere|gaussian>", ""},
{"point-sprites-size", "", "Point sprites size", "<size>", ""},
{"point-size", "", "Point size when showing vertices, model specified by default", "<size>", ""},
{"line-width", "", "Line width when showing edges, model specified by default", "<width>", ""},
{"backface-type", "", "Backface type, can be visible or hidden, model specified by default", "<visible|hidden>", ""},
{"color", "", "Solid color", "<color>", ""},
{"opacity", "", "Opacity", "<opacity>", ""},
{"roughness", "", "Roughness coefficient (0.0-1.0)", "<roughness>", ""},
{"metallic", "", "Metallic coefficient (0.0-1.0)", "<metallic>", ""},
{"hdri-file", "", "Path to an image file that can be used as a light source and skybox", "<file path>", ""},
{"hdri-ambient", "f", "Enable HDRI ambient lighting", "<bool>", "1"},
{"hdri-skybox", "j", "Enable HDRI skybox background", "<bool>", "1"},
{"texture-matcap", "", "Path to a texture file containing a material capture", "<file path>", ""},
{"texture-base-color", "", "Path to a texture file that sets the color of the object", "<file path>", ""},
{"texture-material", "", "Path to a texture file that sets the Occlusion, Roughness and Metallic values of the object", "<file path>", ""},
{"texture-emissive", "", "Path to a texture file that sets the emitted light of the object", "<file path>", ""},
{"emissive-factor", "", "Emissive factor. This value is multiplied with the emissive color when an emissive texture is present", "<color>", ""},
{"texture-normal", "", "Path to a texture file that sets the normal map of the object", "<file path>", ""},
{"normal-scale", "", "Normal scale affects the strength of the normal deviation from the normal texture", "<normalScale>", ""} } },
{"Window",
{ {"background-color", "", "Background color", "<color>", ""},
{"resolution", "", "Window resolution", "<width,height>", ""},
{"position", "", "Window position", "<x,y>", ""},
{"fps", "z", "Display rendering frame per second", "<bool>", "1"},
{"filename", "n", "Display filename", "<bool>", "1"},
{"metadata", "m", "Display file metadata", "<bool>", "1"},
{"blur-background", "u", "Blur background", "<bool>", "1" },
{"blur-coc", "", "Blur circle of confusion radius", "<value>", ""},
{"light-intensity", "", "Light intensity", "<value>", ""} } },
{"Scientific visualization",
{ {"scalar-coloring", "s", "Color by a scalar array", "<bool>", "1" },
{"coloring-array", "", "Name of the array to color with", "<array_name>", "" },
{"coloring-component", "y", "Component from the array to color with. -1 means magnitude, -2 or the short option, -y, means direct scalars", "<comp_index>", "-2"},
{"coloring-by-cells", "c", "Use an array from the cells", "<bool>", "1"},
{"coloring-range", "", "Custom range for the coloring by array, automatically computed by default", "<min,max>", ""},
{"coloring-scalar-bar", "b", "Show scalar bar", "<bool>", "1" },
{"colormap-file", "", "Specify a colormap image", "<filePath/filename/fileStem>", ""},
{"colormap", "", "Specify a custom colormap (ignored if \"colormap-file\" is specified)", "<color_list>", ""},
{"volume", "v", "Show volume if the file is compatible", "<bool>", "1"},
{"volume-inverse", "i", "Inverse opacity function for volume rendering", "<bool>", "1"} } },
{"Camera",
{ {"camera-position", "", "Camera position (overrides camera direction and camera zoom factor if any)", "<X,Y,Z>", ""},
{"camera-focal-point", "", "Camera focal point", "<X,Y,Z>", ""},
{"camera-view-up", "", "Camera view up", "<direction>", ""},
{"camera-view-angle", "", "Camera view angle (non-zero, in degrees)", "<angle>", ""},
{"camera-direction", "", "Camera direction", "<direction>", ""},
{"camera-zoom-factor", "", "Camera zoom factor (non-zero)", "<factor>", ""},
{"camera-azimuth-angle", "", "Camera azimuth angle (in degrees), performed after other camera options", "<angle>", ""},
{"camera-elevation-angle", "", "Camera elevation angle (in degrees), performed after other camera options", "<angle>", ""},
{"camera-orthographic", "", "Use an orthographic camera", "<bool>", "1"} } },
#if F3D_MODULE_RAYTRACING
{"Raytracing",
{ {"raytracing", "r", "Enable raytracing", "<bool>", "1"},
{"raytracing-samples", "", "Number of samples per pixel", "<samples>", ""},
{"raytracing-denoise", "d", "Denoise the image", "<bool>", "1"} } },
#endif
{"PostFX (OpenGL)",
{ {"translucency-support", "p", "Enable translucency support, implemented using depth peeling", "<bool>", "1"},
{"ambient-occlusion", "q", "Enable ambient occlusion providing approximate shadows for better depth perception, implemented using SSAO", "<bool>", "1"},
{"anti-aliasing", "a", "Enable anti-aliasing, implemented using FXAA", "<bool>", "1"},
{"tone-mapping", "t", "Enable Tone Mapping, providing balanced coloring", "<bool>", "1"},
{"final-shader", "", "Execute the final shader at the end of the rendering pipeline", "<GLSL code>", ""} } },
{"Testing",
{ {"reference", "", "Reference", "<png file>", ""},
{"reference-threshold", "", "Testing threshold", "<threshold>", ""},
{"interaction-test-record", "", "Path to an interaction log file to record interactions events to", "<file_path>", ""},
{"interaction-test-play", "", "Path to an interaction log file to play interaction events from when loading a file", "<file_path>", ""} } }
}};
/**
* True boolean options need to be filtered out in ParseCLIOptions
* Also filter out special options like `define` and `reset`
* This is the easiest, compile time way to do it
*/
constexpr std::array CLIBooleans = {"version", "help", "list-readers", "scan-plugins", "list-rendering-backends", "define", "reset"};
//----------------------------------------------------------------------------
/**
* Collapse a longName and shortName into the expected syntax of cxxopts for name declaration
*/
std::string CollapseName(const std::string_view& longName, const std::string_view& shortName)
{
std::stringstream ss;
if (shortName != "")
{
ss << shortName << ",";
}
ss << longName;
return ss.str();
}
//----------------------------------------------------------------------------
void PrintHelp(const std::string& execName, const cxxopts::Options& cxxOptions)
{
const std::array<std::pair<std::string, std::string>, 4> examples = {{
{ execName + " file.vtu -xtgans",
"View a unstructured mesh in a typical nice looking sciviz style" },
{ execName + " file.glb -tuqap --hdri-file=file.hdr --hdri-ambient --hdri-skybox",
"View a gltf file in a realistic environment" },
{ execName + " file.ply -so --point-size=0 --coloring-component=-2",
"View a point cloud file with direct scalars rendering" },
{ execName + " folder", "View all files in folder" },
}};
f3d::log::setUseColoring(false);
std::vector<std::string> orderedCLIGroupNames;
orderedCLIGroupNames.reserve(::CLIOptions.size());
for (const ::CLIGroup& optionGroup : ::CLIOptions)
{
// This ensure help is provided in the expected group order
orderedCLIGroupNames.emplace_back(optionGroup.GroupName);
}
f3d::log::info(cxxOptions.help(orderedCLIGroupNames));
f3d::log::info("\nExamples:");
for (const auto& [cmd, desc] : examples)
{
F3DOptionsTools::PrintHelpPair(cmd, desc, 50);
}
f3d::log::info("\nReport bugs to https://github.com/f3d-app/f3d/issues");
f3d::log::setUseColoring(true);
}
//----------------------------------------------------------------------------
void PrintPluginsScan()
{
#if F3D_MACOS_BUNDLE
f3d::log::error("option not supported with the macOS bundle");
#else
auto appPath = F3DSystemTools::GetApplicationPath();
appPath = appPath.parent_path().parent_path();
appPath /= "share/f3d/plugins";
auto plugins = f3d::engine::getPluginsList(appPath);
f3d::log::info("Found ", plugins.size(), " plugins:");
for (const std::string& p : plugins)
{
f3d::log::info(" - ", p);
}
#endif
}
//----------------------------------------------------------------------------
void PrintVersion()
{
f3d::log::setUseColoring(false);
f3d::log::info(F3D::AppName + " " + F3D::AppVersion + "\n");
f3d::log::info(F3D::AppTitle);
auto libInfo = f3d::engine::getLibInfo();
f3d::log::info("Version: " + libInfo.VersionFull + ".");
f3d::log::info("Build date: " + libInfo.BuildDate + ".");
f3d::log::info("Build system: " + libInfo.BuildSystem + ".");
f3d::log::info("Compiler: " + libInfo.Compiler + ".");
for (const auto& [name, enabled] : libInfo.Modules)
{
f3d::log::info("Module " + name + ": " + (enabled ? "ON." : "OFF."));
}
f3d::log::info("VTK version: " + libInfo.VTKVersion + ".");
for (const auto& cr : libInfo.Copyrights)
{
f3d::log::info("Copyright (C) " + cr + ".");
}
f3d::log::info("License " + libInfo.License + ".");
f3d::log::setUseColoring(true);
}
//----------------------------------------------------------------------------
void PrintRenderingBackendList()
{
auto backends = f3d::engine::getRenderingBackendList();
f3d::log::setUseColoring(false);
f3d::log::info("Rendering backends:");
for (const auto& [name, available] : backends)
{
f3d::log::info(name + ": " + (available ? "available" : "unavailable"));
}
}
//----------------------------------------------------------------------------
void PrintReadersList()
{
size_t nameColSize = 0;
size_t extsColSize = 0;
size_t mimeColSize = 0;
size_t descColSize = 0;
size_t plugColSize = 0;
std::vector<f3d::engine::readerInformation> readersInfo = f3d::engine::getReadersInfo();
if (readersInfo.empty())
{
f3d::log::warn("No registered reader found!");
return;
}
// Compute the size of the 5 columns
for (const auto& reader : readersInfo)
{
// There is at least one MIME type for each extension
assert(reader.Extensions.size() >= reader.MimeTypes.size());
nameColSize = std::max(nameColSize, reader.Name.length());
descColSize = std::max(descColSize, reader.Description.length());
plugColSize = std::max(plugColSize, reader.PluginName.length());
for (const auto& ext : reader.Extensions)
{
extsColSize = std::max(extsColSize, ext.length());
}
for (const auto& mime : reader.MimeTypes)
{
mimeColSize = std::max(mimeColSize, mime.length());
}
}
const size_t colGap = 4;
nameColSize += colGap;
extsColSize += colGap;
mimeColSize += colGap;
descColSize += colGap;
plugColSize += colGap;
std::string separator =
std::string(nameColSize + extsColSize + descColSize + mimeColSize + plugColSize - colGap, '-');
// Print the rows split in 3 columns
std::stringstream headerLine;
headerLine << std::left << std::setw(nameColSize) << "Name" << std::setw(plugColSize) << "Plugin"
<< std::setw(descColSize) << "Description" << std::setw(extsColSize) << "Exts"
<< std::setw(mimeColSize - colGap) << "Mime-types";
f3d::log::info(headerLine.str());
f3d::log::info(separator);
for (const auto& reader : readersInfo)
{
for (size_t i = 0; i < reader.Extensions.size(); i++)
{
std::stringstream readerLine;
readerLine << std::left;
readerLine << std::setw(nameColSize) << (i == 0 ? reader.Name : "");
readerLine << std::setw(plugColSize) << (i == 0 ? reader.PluginName : "");
readerLine << std::setw(descColSize) << (i == 0 ? reader.Description : "");
readerLine << std::setw(extsColSize)
<< (i < reader.Extensions.size() ? reader.Extensions[i] : "");
readerLine << std::setw(mimeColSize - colGap)
<< (i < reader.MimeTypes.size() ? reader.MimeTypes[i] : "");
f3d::log::info(readerLine.str());
}
}
}
}
//----------------------------------------------------------------------------
std::pair<std::string, int> F3DOptionsTools::GetClosestOption(const std::string& option, bool checkLibAndReaders)
{
std::pair<std::string, int> ret = { "", std::numeric_limits<int>::max() };
auto checkDistance = [](const std::string& key, const std::string& name, std::pair<std::string, int>& ref) {
int distance = f3d::utils::textDistance(key, name);
if (distance < ref.second)
{
ref = { key, distance };
}
};
// Check positional arg `--input`
checkDistance("input", option, ret);
// Check true boolean options
for (std::string_view key : ::CLIBooleans)
{
checkDistance(std::string(key), option, ret);
}
// Check cli names in app options
for (const auto& [key, value] : F3DOptionsTools::DefaultAppOptions)
{
checkDistance(key, option, ret);
}
// Check cli names for libf3d options
for (const auto& [key, value] : F3DOptionsTools::LibOptionsNames)
{
checkDistance(std::string(key), option, ret);
}
// Check libf3d and reader option names
if (checkLibAndReaders)
{
for (const std::string& key : f3d::options::getAllNames())
{
checkDistance(key, option, ret);
}
for (const std::string& key : f3d::engine::getAllReaderOptionNames())
{
checkDistance(key, option, ret);
}
}
return ret;
}
//----------------------------------------------------------------------------
F3DOptionsTools::OptionsDict F3DOptionsTools::ParseCLIOptions(
int argc, char** argv, std::vector<std::string>& positionals)
{
std::string execName = argc > 0 && argv[0][0] ? fs::path(argv[0]).filename().string() : "f3d";
// cxxopts values need to live somewhere until parsing is done
std::vector<std::shared_ptr<cxxopts::Value>> cxxoptsValues;
auto cxxoptsInputPositionals = cxxopts::value<std::vector<std::string>>(positionals);
std::vector<std::string> defines;
auto cxxoptsDefines = cxxopts::value<std::vector<std::string>>(defines);
std::vector<std::string> resets;
auto cxxoptsResets = cxxopts::value<std::vector<std::string>>(resets);
try
{
cxxopts::Options cxxOptions(execName, F3D::AppTitle);
cxxOptions.custom_help("[OPTIONS...]");
for (const ::CLIGroup& optionGroup : ::CLIOptions)
{
auto group = cxxOptions.add_options(std::string(optionGroup.GroupName));
// Positional option, `--input` require a custom implementation
if (std::string(optionGroup.GroupName) == "Applicative")
{
group("input", "Input files", cxxoptsInputPositionals, "<files>");
group("D,define", "Define libf3d options", cxxoptsDefines, "libf3d.option=value");
group("R,reset", "Reset libf3d options", cxxoptsResets, "libf3d.option");
}
// Add each option to cxxopts
for (const ::CLIOption& cliOption : optionGroup.Options)
{
if (cliOption.ValueHelper.empty())
{
// No ValueHelper means its a true boolean option like `--help` or `--version`
group(::CollapseName(cliOption.LongName, cliOption.ShortName), std::string(cliOption.HelpText));
}
else
{
// Add the default value to the help text if any
std::string defaultValue;
std::string helpText(cliOption.HelpText);
// Recover default value from app options
auto appIter = F3DOptionsTools::DefaultAppOptions.find(std::string(cliOption.LongName));
if (appIter != F3DOptionsTools::DefaultAppOptions.end())
{
defaultValue = appIter->second;
}
else
{
// Recover default value from lib options
auto libIter = F3DOptionsTools::LibOptionsNames.find(cliOption.LongName);
if (libIter != F3DOptionsTools::LibOptionsNames.end())
{
f3d::options opt;
std::string name = std::string(libIter->second);
// let default value empty for unset options
defaultValue = opt.hasValue(name) ? opt.getAsString(name) : "";
}
}
// Add default value to the help text directly
// Do not add it as a default value in cxxopts
// As it would add it to the parseResults
// which we do not want
if (!defaultValue.empty())
{
helpText += " (default: " + defaultValue + ")";
}
// Recover the implicit value and set it if any
cxxoptsValues.emplace_back(cxxopts::value<std::string>());
auto& val = cxxoptsValues.back();
if (!cliOption.ImplicitValue.empty())
{
val->implicit_value(std::string(cliOption.ImplicitValue));
}
// Add the cxxopts option
group(::CollapseName(cliOption.LongName, cliOption.ShortName), helpText, val,
std::string(cliOption.ValueHelper));
}
}
}
// Parse using cxxopts
cxxOptions.allow_unrecognised_options();
cxxOptions.positional_help("file1 file2 ...");
cxxOptions.parse_positional({ "input" });
cxxOptions.show_positional_help();
auto result = cxxOptions.parse(argc, argv);
// Check boolean options and log them if any
if (result.count("help") > 0)
{
::PrintHelp(execName, cxxOptions);
throw F3DExNoProcess("help requested");
}
if (result.count("version") > 0)
{
::PrintVersion();
throw F3DExNoProcess("version requested");
}
if (result.count("list-rendering-backends") > 0)
{
::PrintRenderingBackendList();
throw F3DExNoProcess("rendering backend list requested");
}
if (result.count("scan-plugins") > 0)
{
::PrintPluginsScan();
throw F3DExNoProcess("scan plugins requested");
}
if (result.count("list-readers") > 0)
{
// `--list-readers` needs plugin to be loaded to be useful
// Load them manually
std::vector<std::string> plugins;
if (result.count("load-plugins") > 0)
{
plugins = f3d::options::parse<std::vector<std::string>>(result["load-plugins"].as<std::string>());
}
F3DPluginsTools::LoadPlugins(plugins);
::PrintReadersList();
throw F3DExNoProcess("reader list requested");
}
// Check for unknown options and log them
auto unmatched = result.unmatched();
bool foundUnknownOption = false;
for (const std::string& unknownOption : unmatched)
{
f3d::log::error("Unknown option '", unknownOption, "'");
foundUnknownOption = true;
// check if it's a long option
if (unknownOption.substr(0, 2) == "--")
{
const size_t equalPos = unknownOption.find('=');
// remove "--" and everything after the first "=" (if any)
const std::string unknownName =
unknownOption.substr(2, equalPos != std::string::npos ? equalPos - 2 : equalPos);
auto [closestName, dist] = F3DOptionsTools::GetClosestOption(unknownName);
const std::string closestOption =
equalPos == std::string::npos ? closestName : closestName + unknownOption.substr(equalPos);
f3d::log::error("Did you mean '--", closestOption, "'?");
}
}
if (foundUnknownOption)
{
throw F3DExFailure("unknown options");
}
// Add each CLI options into a vector of string/string and return it
F3DOptionsTools::OptionsDict cliOptionsDict;
for (const auto& res : result)
{
// Discard boolean option like `--version` or `--help`
if (std::find(::CLIBooleans.begin(), ::CLIBooleans.end(), res.key()) == ::CLIBooleans.end())
{
cliOptionsDict[res.key()] = res.value();
}
}
// Handle defines and add them as proper options
for (const std::string& define : defines)
{
std::string::size_type sepIdx = define.find_first_of('=');
if (sepIdx == std::string::npos)
{
f3d::log::warn("Could not parse a define '", define, "'");
continue;
}
cliOptionsDict[define.substr(0, sepIdx)] = define.substr(sepIdx + 1);
}
// Handles reset using the dedicated syntax
for (const std::string& reset : resets)
{
cliOptionsDict["reset-" + reset] = "";
}
return cliOptionsDict;
}
catch (const cxxopts::exceptions::exception& ex)
{
f3d::log::error("Error parsing command line arguments: ", ex.what());
throw F3DExFailure("Could not parse command line arguments");
}
}
//----------------------------------------------------------------------------
void F3DOptionsTools::PrintHelpPair(
std::string_view key, std::string_view help, int keyWidth, int helpWidth)
{
std::stringstream ss;
ss << " " << std::left << std::setw(keyWidth) << key;
if (key.size() > static_cast<size_t>(keyWidth))
{
ss << "\n " << std::setw(keyWidth) << " ";
}
ss << " " << std::setw(helpWidth) << help;
f3d::log::info(ss.str());
}