Skip to content

Commit

Permalink
Add a command API
Browse files Browse the repository at this point in the history
  • Loading branch information
mwestphal committed Nov 7, 2024
1 parent cdcb2cd commit f6f3fc0
Show file tree
Hide file tree
Showing 32 changed files with 851 additions and 189 deletions.
16 changes: 11 additions & 5 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,42 +125,48 @@ defaults:
values:
parent: User Documentation
nav_order: 6
-
scope:
path: "doc/user/COMMANDS.md"
values:
parent: User Documentation
nav_order: 7

-
scope:
path: "doc/user/COLOR_MAPS.md"
values:
parent: User Documentation
nav_order: 7
nav_order: 8

-
scope:
path: "doc/user/FINAL_SHADER.md"
values:
parent: User Documentation
nav_order: 8
nav_order: 9

-
scope:
path: "doc/user/DESKTOP_INTEGRATION.md"
values:
parent: User Documentation
nav_order: 9
nav_order: 10

-
scope:
path: "doc/user/PLUGINS.md"
values:
parent: User Documentation
nav_order: 10
nav_order: 11

-
scope:
path: "doc/user/LIMITATIONS_AND_TROUBLESHOOTING.md"
values:
title: Limitations and Troubleshooting
parent: User Documentation
nav_order: 11
nav_order: 12

# libf3d doc
-
Expand Down
70 changes: 53 additions & 17 deletions application/F3DStarter.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -798,45 +798,81 @@ int F3DStarter::Start(int argc, char** argv)
this->Internals->ApplyPositionAndResolution();

f3d::interactor& interactor = this->Internals->Engine->getInteractor();

interactor.addCommandCallback("load_previous_file_group",
[this](const std::vector<std::string>&) { return this->LoadRelativeFileGroup(-1); });

interactor.addCommandCallback("load_next_file_group",
[this](const std::vector<std::string>&) { return this->LoadRelativeFileGroup(+1); });

interactor.addCommandCallback("reload_current_file_group",
[this](const std::vector<std::string>&)
{ return this->LoadRelativeFileGroup(0, true, true); });

interactor.addCommandCallback("add_current_directories",
[this](const std::vector<std::string>&) -> bool
{
if (this->Internals->LoadedFiles.size() > 0)
{
for (const auto& parentPath : F3DInternals::ParentPaths(this->Internals->LoadedFiles))
{
this->AddFile(parentPath, true);
}
return this->LoadRelativeFileGroup(0);
}
return true;
});

interactor.addCommandCallback("take_screenshot",
[this](const std::vector<std::string>& args) -> bool
{
// XXX: Add a test for this one this can be reached with a non empty filename
std::string filename =
args.empty() ? this->Internals->AppOptions.ScreenshotFilename : args[0];
this->SaveScreenshot(filename);
return true;
});

interactor.addCommandCallback("take_minimal_screenshot",
[this](const std::vector<std::string>& args) -> bool
{
// XXX: Add a test for this one this can be reached with a non empty filename
std::string filename =
args.empty() ? this->Internals->AppOptions.ScreenshotFilename : args[0];
this->SaveScreenshot(filename, true);
return true;
});

interactor.setKeyPressCallBack(
[this](int, const std::string& keySym) -> bool
{
if (keySym == "Left")
{
return this->LoadRelativeFileGroup(-1);
return this->Internals->Engine->getInteractor().triggerCommand(
"load_previous_file_group");
}
if (keySym == "Right")
{
return this->LoadRelativeFileGroup(+1);
return this->Internals->Engine->getInteractor().triggerCommand("load_next_file_group");
}
if (keySym == "Up")
{
return this->LoadRelativeFileGroup(0, true, true);
return this->Internals->Engine->getInteractor().triggerCommand(
"reload_current_file_group");
}
if (keySym == "Down")
{
if (this->Internals->LoadedFiles.size() > 0)
{
for (const auto& parentPath : F3DInternals::ParentPaths(this->Internals->LoadedFiles))
{
this->AddFile(parentPath, true);
}
return this->LoadRelativeFileGroup(0);
}
return true;
return this->Internals->Engine->getInteractor().triggerCommand("add_current_directories");
}

if (keySym == "F12")
{
this->SaveScreenshot(this->Internals->AppOptions.ScreenshotFilename);
return true;
return this->Internals->Engine->getInteractor().triggerCommand("take_screenshot");
}
if (keySym == "F11")
{
this->SaveScreenshot(this->Internals->AppOptions.ScreenshotFilename, true);
return true;
return this->Internals->Engine->getInteractor().triggerCommand("take_minimal_screenshot");
}

return false;
});

Expand Down
3 changes: 2 additions & 1 deletion doc/libf3d/CLASSES.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ When provided by the engine, the interactor class lets you choose how to interac

It contains the animation API to start and stop animation.

Interactor also lets you set your interaction callbacks in order to modify how the interaction with the data is done.
Interactor also lets you set your interaction callbacks in order to modify how the interaction with the data is done,
as well as adding/removing/triggering [command](../user/COMMANDS.md) callbacks.

Of course, you can use `start` and `stop` to control the interactor behavior.

Expand Down
7 changes: 5 additions & 2 deletions doc/libf3d/OPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ scene.animation.time|double<br>optional<br>load|Set the animation time to load.|
scene.animation.frame_rate|double<br>60<br>render|Set the animation frame rate used to play the animation interactively.|\-\-animation-frame-rate
scene.camera.index|int<br>optional<br>load|Select the scene camera to use when available in the file.<br>The default scene always uses automatic camera.|\-\-camera-index
scene.up_direction|string<br>+Y<br>load|Define the Up direction. It impacts the grid, the axis, the HDRI and the camera.|\-\-up
scene.camera.orthographic|bool<br>false<br>load|Toggles between orthographic projection and parallel mode.|\-\-camera\-orthographic
scene.camera.orthographic|bool<br>optional<br>load|Set to true to force orthographic projection. Model specified by default, which is false if not specified.|\-\-camera\-orthographic

## Interactor Options

Expand Down Expand Up @@ -75,7 +75,7 @@ render.effect.ambient_occlusion|bool<br>false<br>render|Enable *ambient occlusio
render.effect.tone_mapping|bool<br>false<br>render|Enable generic filmic *Tone Mapping Pass*. This technique is used to map colors properly to the monitor colors.|\-\-tone-mapping
render.effect.final_shader|string<br>optional<br>render|Add a final shader to the output image|\-\-final-shader. See [user documentation](../user/FINAL_SHADER.md).
render.line_width|double<br>optional<br>render|Set the *width* of lines when showing edges. Model specified by default.|\-\-line-width
render.show_edges|bool<br>false<br>render|Show the *cell edges*|\-\-edges
render.show_edges|bool<br>optional<br>render|Set to true to show the *cell edges*. Model specified by default.|\-\-edges
render.point_size|double<br>optional<br>render|Set the *size* of points when showing vertices. Model specified by default.|\-\-point-size
render.backface_type|string<br>optional<br>render|Set the Backface type, can be `visible` or `hidden`, model specified by default.|\-\-backface-type
render.grid.enable|bool<br>false<br>render|Show *a grid* aligned with the horizontal (orthogonal to the Up direction) plane.|\-\-grid
Expand Down Expand Up @@ -141,6 +141,9 @@ Please note that when accessing optional options, special care must be used, eg:
std::cout << "Line Width: unset" << std::endl;
}
```
It's even more true with the few optional boolean options as std::optional has an implicit boolean cast operator.
## String API
The most generic and flexible API, as it rely on parsing and string generation.
Expand Down
79 changes: 79 additions & 0 deletions doc/user/COMMANDS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Commands

F3D will provide access to commands through interactions configuration and command line widget in the future.
Commands let you trigger specific behavior that may not be available otherwise.
Please note commands are currently experimental and the behaviors, names and arguments may change without deprecation.

Commands have the following syntax:

`action [args]`

## libf3d provided commands

The libf3d provides a few commands, many related to manipulating libf3d (options)[../libf3d/OPTIONS.md].

`set option.name values`: A command to set a libf3d option, eg: `set scene.up.direction +Z` or `set render.hdri.file "/path/to/file with spaces.png"`

`toggle option.name`: A command to toggle a boolean libf3d option, eg: `toggle ui.scalar_bar`.

`reset option.name`: A command to reset a libf3d option to its default values, eg: `reset render.background.blur_coc`.

`print option.name`: A command to print the value of an libf3d option, eg: `print scene.up.direction`.

`cycle_animation`: A specific command to cycle `scene.animation.index` option using model information, No argument.

`cycle_coloring field/array/component`: A specific command to manipulate scivis options using model information.
Supports `field`, `array` or `component` arguments, see [documentation](INTERACTIONS.md#cycling-coloring).
eg: `cycle_coloring array`.

`roll_camera value`: A specific command to roll the camera on its side, takes an angle in degrees as an argument.
eg: `roll_camera 120`.

`increase_light_intensity [negative]`: A specific command to increase/decrease light intensity. Add the optional `negative argument to decrease.
eg: `increase_light_intensity negative`.

`print_scene_info`: A specific command to print information about the scene, No argument.

`set_camera front/top/right/isometric`: A specific command to position the camera in the specified location relative to the model.
Supports `front`, `top`, `right`, `isometric` arguments. eg: `set_camera top`.

`toggle_volume_rendering`: A specific command to toggle `model.volume.enable` and print coloring information. No argument.

`toggle_ui_fps`: A specific command to toggle `ui.fps` and update it. No argument.

`stop_interactor`: A specific command to stop the interactor hence quitting the application. No argument.

`reset_camera`: A specific command to reset the camera to its original location. No argument.

`toggle_animation`: A specific command to start/stop the animation. No argument.

## F3D provided specific commands

The F3D application provides a few more commands.

`load_previous_file_group`: A specific command to load the previous file or file group. No argument.

`load_next_file_group`: A specific command to load the next file or file group. No argument.

`reload_current_file_group`: A specific command to reload the current file or file group. No argument.

`add_current_directories`: A specific command to add all files from the current file or file group directories. No argument.

`take_screenshot [filename]`: A specific command to [take a screenshot](INTERACTIONS.md#taking-screenshots). If filename is not specified,
rely on the `--screenshot-filename` CLI option. eg: `take_screenshot path/to/file.png`.

`take_minimal_screenshot [filename]`: A specific command to [take a minimal screenshot](INTERACTIONS.md#taking-screenshots). If filename is not specified,
rely on the `--screenshot-filename` CLI option. eg: `take_screenshot path/to/file.png`.

## Command syntax

Command syntax is similar to bash, as in they will be split by "token" to be processed.
Tokens are spaces separated, eg: `set scene.up.direction +Z`.
Tokens can also be quoted to support spaces inside, eg: `set render.hdri.file "/path/to/file with spaces.png"`.
Supported quotes are `` `'" ``, eg: `set render.hdri.file '/path/to/file with spaces.png'`.
Quotes inside quotes are supported as well, eg: `set render.hdri.file "/path/to/file'with'quotes.png"`.
Quotes and spaces can be escaped, eg: `set render.hdri.file /path/to/file\ with\ spaces\ and\ \'quotes\".png`.
Escapes can be escaped too: eg: `set render.hdri.file C:\\path\\to\\windows\\file.png`.
Other escaped character will be processed as if the escape was not present, eg: `set scene.up.direction +\Z`
Unfinished quoted section is invalid, eg: `set scene.up.direction "+Z`
A escape at the end is also invalid, eg: `set scene.up.direction +Z\`
2 changes: 1 addition & 1 deletion doc/user/OPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ As documented, only the `--option=value` syntax is supported. The syntax `--opti

## Filename templating

The destination filename used by `--output` or to save screenshots can use the following template variables:
The destination filename used by `--output` or to save screenshots using `--screenshot-filename` can use the following template variables:

- `{app}`: application name (ie. `F3D`)
- `{version}`: application version (eg. `2.4.0`)
Expand Down
3 changes: 2 additions & 1 deletion doc/user/README_USER.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
- [List of all F3D command line options.](OPTIONS.md)
- [The different interactions in F3D.](INTERACTIONS.md)
- [How to use animations in F3D.](ANIMATIONS.md)
- [How to configure F3D using a configuration file.](CONFIGURATION_FILE.md)
- [The different commands available in F3D.](COMMANDS.md)
- [How to use colormaps in F3D.](COLOR_MAPS.md)
- [How to a use custom final shader in F3D.](FINAL_SHADER.md)
- [How to configure F3D using a configuration file.](CONFIGURATION_FILE.md)
- [How to integrate F3D in your desktop.](DESKTOP_INTEGRATION.md)
- [How to configure plugins in F3D.](PLUGINS.md)
- [Limitations and troubleshooting of F3D.](LIMITATIONS_AND_TROUBLESHOOTING.md)
5 changes: 5 additions & 0 deletions library/private/interactor_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ class interactor_impl : public interactor
interactor& setKeyPressCallBack(std::function<bool(int, std::string)> callBack) override;
interactor& setDropFilesCallBack(std::function<bool(std::vector<std::string>)> callBack) override;

interactor& addCommandCallback(
std::string action, std::function<bool(const std::vector<std::string>&)> callback) override;
interactor& removeCommandCallback(const std::string& action) override;
bool triggerCommand(std::string_view command) override;

unsigned long createTimerCallBack(double time, std::function<void()> callBack) override;
void removeTimerCallBack(unsigned long id) override;

Expand Down
20 changes: 20 additions & 0 deletions library/public/interactor.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,26 @@ class F3D_EXPORT interactor
virtual interactor& setDropFilesCallBack(
std::function<bool(std::vector<std::string>)> callBack) = 0;

///@{ @name Command
/**
* Use this method to add a callback into the command map
* to be called using triggerCommand.
* Adding a commandCallback with an existing action replaces it.
*/
virtual interactor& addCommandCallback(
std::string action, std::function<bool(const std::vector<std::string>&)> callback) = 0;

/**
* Remove a command callback for provided action
*/
virtual interactor& removeCommandCallback(const std::string& action) = 0;

/**
* Trigger provided command, see COMMAND.md for more information
*/
virtual bool triggerCommand(std::string_view command) = 0;
///@}

/**
* Use this method to create your own timer callback. You callback will be called once every time
* ms. Return an id to use in removeTimeCallBack.
Expand Down
2 changes: 1 addition & 1 deletion library/public/options.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ public:

/**
* A boolean option specific method to toggle it.
* If the option has not been set yet, set it to true.
* Throw an options::inexistent_exception if option does not exist.
* Throw an options::incompatible_exception if option is not boolean.
* Throw an options::no_value_exception if option has not been set.
*/
options& toggle(const std::string& name);

Expand Down
35 changes: 35 additions & 0 deletions library/public/utils.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#ifndef f3d_utils_h
#define f3d_utils_h

#include "exception.h"
#include "export.h"

#include <string>
#include <vector>

namespace f3d
{
Expand All @@ -22,6 +24,39 @@ class F3D_EXPORT utils
* Can be useful for spell checking and typo detection.
*/
static unsigned int textDistance(const std::string& strA, const std::string& strB);

// clang-format off
/**
* Tokenize provided string_view into a vector of strings, using the same logic as bash.
* - Split by spaces unless between quotes
* - Split by quoted section and remove the quotes
* - Supported quotes are: '"`
* - Use escaped \ quotes, spaces and escape to add them verbatim
* - Other escaped characters are also added verbatim
* Throw a tokenize_exception if a quoted section is not closed or if finishing with an escape
*
* Examples:
* `set scene.up.direction +Z` -> `set` `scene.up.direction` `+Z`
* `set render.hdri.file "/path/to/file with spaces.png"` -> `set`, `render.hdri.file`,
* `/path/to/file with spaces.png` `set render.hdri.file '/path/to/file with spaces.png'` ->
* `set`, `render.hdri.file`, `/path/to/file with spaces.png` `set render.hdri.file
* "/path/to/file'with'quotes.png"` -> `set`, `render.hdri.file`, `/path/to/file'with'quotes.png`
* `set render.hdri.file /path/to/file\ spaces\ \'quotes\".png` -> `set`, `render.hdri.file`,
* `/path/to/file spaces 'quotes".png` `set render.hdri.file C:\\path\\to\\windows\\file.png` ->
* `set`, `render.hdri.file`, `C:\path\to\windows\file.png` `set scene.up.direction +\Z` -> `set`,
* `scene.up.direction`, `+Z` `set scene.up.direction "+Z` -> tokenize_exception `set
* scene.up.direction +Z\` -> tokenize_exception
*/
static std::vector<std::string> tokenize(std::string_view str);
// clang-format on

/**
* An exception that can be thrown by tokenize
*/
struct tokenize_exception : public exception
{
explicit tokenize_exception(const std::string& what = "");
};
};
}

Expand Down
Loading

0 comments on commit f6f3fc0

Please sign in to comment.