Skip to content

Commit

Permalink
Use std::optional for options (#1617)
Browse files Browse the repository at this point in the history
  • Loading branch information
mwestphal authored Sep 18, 2024
1 parent 743f4f7 commit 80b86cc
Show file tree
Hide file tree
Showing 40 changed files with 462 additions and 363 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ F3D (pronounced `/fɛd/`) is a fast and minimalist 3D viewer desktop application

It is fully controllable from the command line and support configuration files. It can provide thumbnails, support interactive hotkeys, drag&drop and integration into file managers.

F3D also contains the libf3d, a simple library to render meshes, with C++ and Python Bindings, as well as experimental Java and Javascript bindings.
F3D also contains the libf3d, a simple library to render meshes, with a C++17 API, Python Bindings, and experimental Java and Javascript bindings.

<img src="https://user-images.githubusercontent.com/3129530/194735416-3f386437-456c-4145-9b5e-6bb6451d7e9a.png" width="640">

Expand Down
20 changes: 11 additions & 9 deletions application/F3DOptionsTools.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ static inline const std::array<CLIGroup, 8> CLIOptions = {{
{ "up", "", "Up direction", "{-X, +X, -Y, +Y, -Z, +Z}", "" },
{ "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, set to a non-positive value for automatic computation", "<value>", "" },
{ "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", "<R,G,B>", "" },
{ "edges", "e", "Show cell edges", "<bool>", "1" },
Expand All @@ -97,10 +97,11 @@ static inline const std::array<CLIGroup, 8> CLIOptions = {{
{"font-file", "", "Path to a FreeType compatible font file", "<file_path>", ""} } },
{ "Material",
{ {"point-sprites", "o", "Show sphere sprites instead of geometry", "<bool>", "1" },
{"point-type", "", "Point sprites type when showing point sprites", "<sphere|gaussian>", ""},
{"point-size", "", "Point size when showing vertices or point sprites", "<size>", ""},
{"line-width", "", "Line width when showing edges", "<width>", ""},
{"backface-type", "", "Backface type, can be default (usually visible), visible or hidden", "<default|visible|hidden>", ""},
{"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", "<R,G,B>", ""},
{"opacity", "", "Opacity", "<opacity>", ""},
{"roughness", "", "Roughness coefficient (0.0-1.0)", "<roughness>", ""},
Expand Down Expand Up @@ -130,7 +131,7 @@ static inline const std::array<CLIGroup, 8> CLIOptions = {{
{"coloring-array", "", "Name of the array to color with", "<array_name>", "" },
{"comp", "y", "Component from the array to color with. -1 means magnitude, -2 or the short option, -y, means direct scalars", "<comp_index>", "-2"},
{"cells", "c", "Use an array from the cells", "<bool>", "1"},
{"range", "", "Custom range for the coloring by array", "<min,max>", ""},
{"range", "", "Custom range for the coloring by array, automatically computed by default", "<min,max>", ""},
{"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>", ""},
Expand Down Expand Up @@ -390,8 +391,7 @@ std::pair<std::string, int> F3DOptionsTools::GetClosestOption(const std::string&
// Check libf3d option names
if (checkLib)
{
f3d::options opt;
for (const std::string& key : opt.getNames())
for (const std::string& key : f3d::options::getAllNames())
{
checkDistance(key, option, ret);
}
Expand Down Expand Up @@ -450,7 +450,9 @@ F3DOptionsTools::OptionsDict F3DOptionsTools::ParseCLIOptions(
if (libIter != F3DOptionsTools::LibOptionsNames.end())
{
f3d::options opt;
defaultValue = opt.getAsString(std::string(libIter->second));
std::string name = std::string(libIter->second);
// let default value empty for unset options
defaultValue = opt.hasValue(name) ? opt.getAsString(name) : "";
}
}

Expand Down
3 changes: 2 additions & 1 deletion application/F3DOptionsTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ static inline const std::map<std::string_view, std::string_view> LibOptionsNames
{ "animation-frame-rate", "scene.animation.frame_rate" },
{ "font-file", "ui.font_file" },
{ "point-sprites", "model.point_sprites.enable" },
{ "point-type", "model.point_sprites.type" },
{ "point-sprites-type", "model.point_sprites.type" },
{ "point-sprites-size", "model.point_sprites.size" },
{ "point-size", "render.point_size" },
{ "line-width", "render.line_width" },
{ "backface-type", "render.backface_type" },
Expand Down
5 changes: 4 additions & 1 deletion application/F3DStarter.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -957,11 +957,14 @@ void F3DStarter::LoadFile(int index, bool relativeIndex)
// Detect interactively changed options and store them into the dynamic options dict
// options names are shared between options instance
F3DOptionsTools::OptionsDict dynamicOptionsDict;
std::vector<std::string> optionNames = this->Internals->LibOptions.getNames();
std::vector<std::string> optionNames = dynamicOptions.getNames();
for (const auto& name : optionNames)
{
if (!dynamicOptions.isSame(this->Internals->LibOptions, name))
{
// XXX Currently an assert is enough but it should be a proper try/catch once
// we add a mechanism to unset an option
assert(dynamicOptions.hasValue(name));
dynamicOptionsDict[name] = dynamicOptions.getAsString(name);
}
}
Expand Down
25 changes: 17 additions & 8 deletions application/testing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ f3d_test(NAME TestVTS DATA bluntfin.vts)
f3d_test(NAME TestVTM DATA mb.vtm)
f3d_test(NAME TestVTK DATA cow.vtk)
f3d_test(NAME TestNRRD DATA beach.nrrd ARGS -s)
f3d_test(NAME TestSPLAT DATA small.splat ARGS -osy --up=-Y --point-size=1)
f3d_test(NAME TestSPLAT DATA small.splat ARGS -osy --up=-Y --point-sprites-size=1)
f3d_test(NAME TestGridX DATA suzanne.ply ARGS -g --up=+X)
f3d_test(NAME TestGridY DATA suzanne.ply ARGS -g --up=+Y)
f3d_test(NAME TestGridZ DATA suzanne.ply ARGS -g --up=+Z)
Expand All @@ -137,9 +137,9 @@ f3d_test(NAME TestGridColor DATA suzanne.ply ARGS -g --grid-color=1,1,1)
f3d_test(NAME TestAxis DATA suzanne.ply ARGS -x)
f3d_test(NAME TestBackfaceVisible DATA backface.vtp ARGS --backface-type=visible)
f3d_test(NAME TestBackfaceHidden DATA backface.vtp ARGS --backface-type=hidden)
f3d_test(NAME TestPointCloud DATA pointsCloud.vtp ARGS -o --point-size=20)
f3d_test(NAME TestPointCloudBar DATA pointsCloud.vtp ARGS -sob --point-size=20)
f3d_test(NAME TestPointCloudUG DATA pointsCloud.vtu ARGS -o --point-size=20)
f3d_test(NAME TestPointCloud DATA pointsCloud.vtp ARGS -o --point-sprites-size=20)
f3d_test(NAME TestPointCloudBar DATA pointsCloud.vtp ARGS -sob --point-sprites-size=20)
f3d_test(NAME TestPointCloudUG DATA pointsCloud.vtu ARGS -o --point-sprites-size=20)
f3d_test(NAME TestPointCloudVolume DATA bluntfin.vts ARGS -sob)
f3d_test(NAME TestPointCloudDefaultScene DATA pointsCloud.vtp ARGS --point-size=20)
f3d_test(NAME Test3DSImporter DATA iflamigm.3ds ARGS --up=+Z)
Expand Down Expand Up @@ -287,7 +287,7 @@ endif()
# Needs splat sorting with compute shaders
if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240203)
if(NOT APPLE) # MacOS does not support compute shaders
f3d_test(NAME Test3DGaussiansSplatting DATA small.splat ARGS -osy --up=-Y --point-size=1 --point-type=gaussian --camera-position=-3.6,0.5,-4.2)
f3d_test(NAME Test3DGaussiansSplatting DATA small.splat ARGS -osy --up=-Y --point-sprites-size=1 --point-sprites-type=gaussian --camera-position=-3.6,0.5,-4.2)
f3d_test(NAME TestDefaultConfigFileSPLAT DATA small.splat CONFIG config_build LONG_TIMEOUT)
f3d_test(NAME TestThumbnailConfigFileSPLAT DATA small.splat CONFIG thumbnail_build LONG_TIMEOUT)
endif()
Expand Down Expand Up @@ -612,8 +612,11 @@ if(F3D_PLUGIN_BUILD_EXODUS)
f3d_test(NAME TestAnimationTimeLimitsHigh DATA small.ex2 ARGS ARGS --load-plugins=exodus --animation-time=10)
f3d_test(NAME TestAnimationTimeLimitsLow DATA small.ex2 ARGS ARGS --load-plugins=exodus --animation-time=-5)

# Test if negative range is respected when loading a file without specifying the animation time
f3d_test(NAME TestTimeRangeLessThanZeroNoAnimationTime DATA negative_range_animated.e ARGS -s --load-plugins=exodus)

# Test if the animation-time works when set to zero and time range[0] is less than zero
f3d_test(NAME TestTimeRangeLessThanZero DATA negative_range_animated.e ARGS -s --load-plugins=exodus --animation-time=0)
f3d_test(NAME TestTimeRangeLessThanZeroWithAnimationTime DATA negative_range_animated.e ARGS -s --load-plugins=exodus --animation-time=0)

# Test if a negative animation-time works when time range[0] is less than zero
f3d_test(NAME TestTimeRangeLessThanZeroNegativeAnimationTime DATA negative_range_animated.e ARGS -s --load-plugins=exodus --animation-time=-3)
Expand Down Expand Up @@ -721,7 +724,7 @@ f3d_test(NAME TestInteractionCycleCell DATA waveletArrays.vti INTERACTION) #VCCC
f3d_test(NAME TestInteractionCycleComp DATA dragon.vtu INTERACTION) #SYYYY
f3d_test(NAME TestInteractionCycleScalars DATA dragon.vtu INTERACTION) #BSSSS
f3d_test(NAME TestInteractionVolumeInverse DATA HeadMRVolume.mhd ARGS --camera-position=127.5,-400,127.5 --camera-view-up=0,0,1 INTERACTION) #VI
f3d_test(NAME TestInteractionPointCloud DATA pointsCloud.vtp ARGS --point-size=20 INTERACTION) #O
f3d_test(NAME TestInteractionPointCloud DATA pointsCloud.vtp ARGS --point-sprites-size=20 INTERACTION) #O
f3d_test(NAME TestInteractionDirectory DATA mb INTERACTION ARGS --scalar-coloring) #Right;Right;Right;Left;Up;
f3d_test(NAME TestInteractionDirectoryLoop DATA mb INTERACTION ARGS --scalar-coloring) #Left;Left;Left;
f3d_test(NAME TestInteractionDirectoryEmpty DATA mb INTERACTION NO_DATA_FORCE_RENDER) #Right;Right;Right;
Expand Down Expand Up @@ -821,7 +824,7 @@ f3d_test(NAME TestVerboseDebug DATA dragon.vtu ARGS --verbose REGEXP "Number of
f3d_test(NAME TestVerboseInvalid DATA dragon.vtu ARGS --verbose=invalid REGEXP "Unrecognized verbose level" NO_BASELINE)

# Unknown scalar array verbosity test
f3d_test(NAME TestVerboseWrongArray DATA dragon.vtu ARGS -s --coloring-array=dummy --verbose REGEXP "Unknown scalar array: dummy" NO_BASELINE)
f3d_test(NAME TestVerboseWrongArray DATA dragon.vtu ARGS -s --coloring-array=dummy --verbose REGEXP "Unknown scalar array: \"dummy\"" NO_BASELINE)

# Default scalar array verbosity test
f3d_test(NAME TestVerboseDefaultScalar DATA HeadMRVolume.mhd ARGS -s --verbose REGEXP "Coloring using point array named MetaImage, Magnitude" NO_BASELINE)
Expand Down Expand Up @@ -864,6 +867,12 @@ f3d_test(NAME TestVerboseVolumeNoArray DATA cow.vtp ARGS -v REGEXP "Cannot use v
# Test scalar rendering without any array
f3d_test(NAME TestVerboseNoArray DATA cow.vtp ARGS -s --verbose=debug REGEXP "No array to color with" NO_BASELINE)

# Test invalid scalar range
f3d_test(NAME TestInvalidScalarsRange DATA suzanne.ply ARGS -s --coloring-array=Normals --comp=1 --range=0,1,2 REGEXP "Invalid scalar range provided, using automatic range" NO_BASELINE)

# Test invalid backface type
f3d_test(NAME TestInvalidBackface DATA backface.vtp ARGS --backface-type=invalid REGEXP "is not a valid backface type, assuming it is not set" NO_BASELINE)

# Test non existent file, do not create nonExistentFile.vtp
f3d_test(NAME TestVerboseNonExistentFile DATA nonExistentFile.vtp REGEXP "File .*nonExistentFile.vtp does not exist" NO_RENDER)

Expand Down
27 changes: 21 additions & 6 deletions cmake/f3dOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,22 @@ function(_parse_json_option _top_json)
# Read the json element
string(JSON _cur_json GET ${_top_json} ${_member_name})
# Recover its type and default if it is a leaf option
string(JSON _option_type ERROR_VARIABLE _type_error GET ${_cur_json} "type")
string(JSON _option_default_value ERROR_VARIABLE _default_value_error GET ${_cur_json} "default_value")
if(_type_error STREQUAL "NOTFOUND" AND _default_value_error STREQUAL "NOTFOUND")
string(JSON _option_type ERROR_VARIABLE _option_type_error GET ${_cur_json} "type")
string(JSON _option_type_type ERROR_VARIABLE _option_type_type_error TYPE ${_cur_json} "type")
if(_option_type_error STREQUAL "NOTFOUND" AND ${_option_type_type} STREQUAL "STRING")
# Leaf option found!

# Recover default_value if any
string(JSON _option_default_value ERROR_VARIABLE _default_value_error GET ${_cur_json} "default_value")

set(_option_name "${_option_basename}${_member_name}")

# Identify types
set(_option_actual_type ${_option_type})
set(_option_variant_type ${_option_type})
set(_option_default_value_start "")
set(_option_default_value_end "")

if(_option_type STREQUAL "double_vector")
set(_option_actual_type "std::vector<double>")
set(_option_variant_type "std::vector<double>")
Expand All @@ -125,11 +130,21 @@ function(_parse_json_option _top_json)
endif()

# Add option to struct and methods
string(APPEND _options_struct "${_option_indent} ${_option_actual_type} ${_member_name} = ${_option_default_value_start}${_option_default_value}${_option_default_value_end};\n")

if(_default_value_error STREQUAL "NOTFOUND")
# Use default_value
string(APPEND _options_struct "${_option_indent} ${_option_actual_type} ${_member_name} = ${_option_default_value_start}${_option_default_value}${_option_default_value_end};\n")
set(_optional_getter "")
else()
# No default_value, it is an std::optional
string(APPEND _options_struct "${_option_indent} std::optional<${_option_actual_type}> ${_member_name};\n")
set(_optional_getter ".value()")
endif()

list(APPEND _options_setter "if (name == \"${_option_name}\") opt.${_option_name} = std::get<${_option_variant_type}>(value)")
list(APPEND _options_getter "if (name == \"${_option_name}\") return opt.${_option_name}")
list(APPEND _options_getter "if (name == \"${_option_name}\") return opt.${_option_name}${_optional_getter}")
list(APPEND _options_string_setter "if (name == \"${_option_name}\") opt.${_option_name} = options_tools::parse<${_option_actual_type}>(str)")
list(APPEND _options_string_getter "if (name == \"${_option_name}\") return options_tools::format(opt.${_option_name})")
list(APPEND _options_string_getter "if (name == \"${_option_name}\") return options_tools::format(opt.${_option_name}${_optional_getter})")
list(APPEND _options_lister "\"${_option_name}\"")

else()
Expand Down
2 changes: 1 addition & 1 deletion doc/GALLERY.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Images and videos displayed below use public datasets, you can download them [he

<video src='https://media.githubusercontent.com/media/f3d-app/f3d-media/main/media/counter.webm' autoplay="autoplay" loop="loop" width="700"></video>

*3D Gaussians Splatting*: `f3d counter.splat --point-size=1 --point-type=gaussian -soynxz --up=-Y --camera-position=0,1,-5.2 --camera-focal-point=0,1,0`
*3D Gaussians Splatting*: `f3d counter.splat --point-sprites-size=1 --point-sprites-type=gaussian -soynxz --up=-Y --camera-position=0,1,-5.2 --camera-focal-point=0,1,0`

<img src="https://user-images.githubusercontent.com/3129530/194735272-5bcd3e7c-a333-41f5-8066-9b0bec9885e8.png" width="700">

Expand Down
Loading

0 comments on commit 80b86cc

Please sign in to comment.