Writing a C++ Process ===================== .. highlight:: c++ From C++, a process is a single class inheriting one of three classes: ``StackProcess``, ``MeshProcess`` or ``GlobalProcess``. Which class the process inherits from will decide where in the GUI it will appear and what other processes it can call. In short, if inheriting from ``StackProcess`` or ``MeshProcess`` only processes of the same type can be called, while inheriting from ``GlobalProcess`` allows a process to call any other process. A process's class should follow the following pattern, in which ``Some`` must be replaced by ``Stack``, ``Mesh`` or ``Global`` :: class ProcessName : public SomeProcess { public: ProcessName(const SomeProcess& process) : Process(process) , SomeProcess(process) { } // The two following methods are mandatory bool operator()(const ParmList& parms) override { // Implementation of the process } QString name() const override { return "[Process Name]"; } // All other methods are optional bool initialize(ParmList& parms, QWidget* parent) override { // Provide user interface to adjust the list of parameters // Note: the number of parameters cannot change! } QString description() const override { return "[Process Description]"; } QString folder() const override { return "[Folder in which the process is stored]"; } QStringList parmNames() const override { return QStringList() << "Parm1" << "Parm2" << ...; } QStringList parmDescs() const override { return QStringList() << "Description Parm1" << "Description Parm2" << ...; } ParmList parmDefaults() const override { return ParmList() << value1 << value2 << ...; } ParmChoiceMap parmChoice() const override { ParmChoiceMap map; map[ParmNum1] = QStringList() << "Value 1" << "Value 2" << ...; map[ParmNum2] = QStringList() << "Value 1" << "Value 2" << ...; // ... return map; } QIcon icon() const override { return QIcon(":/path/to/resource"); } } When writing the process, the first task is to parse the parameters. Then, it is customary to provide another ``operator()`` method for direct call from C++ with the parameters already parsed. Process Execution ----------------- Processes are executed in a separate thread, from which **you cannot run any GUI-related events**. This means you must never try to provide a user interface from within a process main method. If you want a user interface, it must be implemented in the ``Process::initialize(...)`` method. During the process execution, there are two ways to show the user the progress of the algorithm: #. Use the ``lgx::Progress`` class #. Use the ``lgx::process::Process::updateState()`` method. This will pause the process while LithoGraphX updates the view, showing the current state. Be careful for the mesh to be in a proper state when you call this function. Also, all ``changed`` states will be reset after that. Parameter Parsing ----------------- The ``ParmList`` type is an alias for a ``QList`` or ``QVariant``. When using types, remember that the user must be able to type values as a string. ``QVariant`` are quite good at converting from string to various values, except for boolean values. So if you need boolean values, You should use the ``parmToBool`` function, which will convert the strings "yes", "y", "true", "t", "on" or "1" as true and any other value as false. Calling other processes ----------------------- There are two ways to call other processes, depending on the availability of the C++ definition or not. The more general method consist in using the ``runProcess`` method of the ``Process`` class. The signation of the function is: .. cpp:function:: bool Process::runProcess(const QString& processType, const QString& processName, const ParmList& parms) Launch a process by name Note that any exception launched by the process will be filtered and converted into an error message and the process returning false. If the C++ implementation is available (through a library for exemple), then it is possible to directly instanciate the process, which allows to call the internal ``operator()`` method. Say you want to use the ``SieveFilter``, you can write (see ``include/SieveFilter/SieveFilter.hpp`` in the system processes folder for details):: SieveFilter filter(*this); filter(input, output, "Median", 100.f, false); Modifying the State ------------------- When a process modifies the state (e.g. image, mesh, transformation, ...), it needs to inform the system. For performance reasons, it would be impractical to either track all changes or to re-analyse all the data structures when coming back from a process. This section will explain first how images and meshes are represented and then how to inform the system about what has been changed. .. toctree:: image_structure mesh_structure Image Modifications ^^^^^^^^^^^^^^^^^^^ Images are modified through the ``lgx::Store`` objects. If the image is modified, you can call one of two functions: .. cpp:function:: void lgx::Store::changed() Call this function for large-scale changed in the image, requiring the whole image to be updated. .. cpp:function:: void lgx::Store::changed(const BoundingBox3i& bbox) Call this function for localised changes. The bounding box should include all the voxel changed. Other aspects of image rendering (size, transfer function, ...) are small and simple enough for their modifications to be tracked automatically. Meshes Modifications ^^^^^^^^^^^^^^^^^^^^ There are five different functions to mark what changed in a mesh: .. cpp:function:: void lgx::Mesh::updateTriangles() Call this function if triangles properties have change, which means label or heat map. .. cpp:function:: void lgx::Mesh::updateLines() Call this function if the "lines" change. This means mostly cell or mesh edges. .. cpp:function:: void lgx::Mesh::updatePositions() Call this function if the position of the vertices have changed. .. cpp:function:: void lgx::Mesh::updateSelection() Call this function if the selection has changed. .. cpp:function:: void lgx::Mesh::updateAll() Call this function if you would have called all the others or if you added/removed vertices. Useful Utilities ---------------- Here are a few features you might want to look at in the developer documentation: * File ``src/Dir.hpp`` for path manipulations you really need to use functions here to manage paths, in particular to save paths in files. * File ``src/Geometry.hpp`` and class templates ``lgx::util::Vector`` and ``lgx::util::Matrix`` for geometry * Class ``lgx::util::CSVStream`` to parse CSV files * Class ``lgx::util::PlyFile`` to parse PLY files (e.g. mesh files) * To run a process over a whole collection use the functions in ``src/Algorithms.hpp``. * Use the class ``lgx::Progress`` to provide a progress bar, possibly with a mean for the user to cancel the process. * Use the class ``lgx::Image5D`` and associated functions to load/save images.