ComponentCreation
From SOFAWiki
A SOFA Component is a class deriving, directly or not, from sofa::core::objectmodel::BaseObject.
Contents |
Where to put your files
- The best way to write new components is to integrate them in a new Plugin
- Conversely you can to place your files next to other existing components, located in Sofa/modules/sofa/components/, in the subdirectory corresponding to the type of your component (forcefield, constraint, linearsolver, ...).
Creating a new component
- Your components must derive from the sofa::core::objectmodel::BaseObject class or an existing component (e.g: sofa::core::visual::VisualModel ).
In your header file :
#include <sofa/core/objectmodel/BaseObject.h> class MyComponent : public sofa::core::objectmodel::BaseObject { public: SOFA_CLASS(MyComponent ,sofa::core::objectmodel::BaseObject); MyComponent (); virtual ~MyComponent (); };
- The SOFA_CLASS macro is required otherwise you will get an error message at the runtime. It enables some kind of reflection mechanism for each component registered in the ObjectFactory.
- You must register your component in the ObjectFactory so that it is known by SOFA at the runtime.
Modifications of the .pro file
- If you chose to put your components directly inside sofa directories (fast and dirty), open the .pro file in the same directory as your component
- If you have a separated project, then you should already have a .pro file (take example on one of our projects), open this .pro file
- Modify the opened .pro file by
- Adding in the HEADERS section, the list of the new .h and .inl files
- Adding in the SOURCES section, the list of the new .cpp files
Recreate the project
- For windows users, you have to run projectVC8.bat or projectVC9.bat depending on your version of Microsoft Visual Studio C++.
- For the others, simply run qmake at the Sofa main directory
Build Sofa
Launch the compilation. A good way to verify that your component's creation went well, is to launch the Modeler, and try to find it in the Library. If it doesn't appear, it means probably that you failed to register your component properly
Documentation
A scene has to be created, with the same name as your component, and placed inside Sofa/examples/Components and its good subdirectory. Finally, you can write a small paragraph for the sofa documentation placed in Sofa/doc, and a link to a publication, if any exists.
Basic Component Concepts
We will introduce some of the basic concepts of building a component by creating two example components, ComponentA and ComponentB.
Data
The type Data provides the link between your Component's code and your scene graph. The Data type acts like a wrapper around whatever data type you want to describe in your scene graph and manipulate in your code. Image that in ComponentA, we want to have a string that we can initialize in the scene graph. In our header file, we would define it as follows:
#include <sofa/core/objectmodel/BaseObject.h> #include <string> class ComponentA : public sofa::core::objectmodel::BaseObject { public: SOFA_CLASS(ComponentA ,sofa::core::objectmodel::BaseObject); ComponentA (); virtual ~ComponentA (); Data<std::string> stringA; };
Now, we want it to be a parameter in our scene graph, so do not forget to register it in the object constructor, which should look like this:
ComponentA::ComponentA() : stringA(initData(&stringA, "defaultString", "myString", "Our string parameter" )) {}
The initData function sets the link between the scene graph and our stringup for us. The second argument, "defaultString", is the default value that stringA will be set to if nothing is specified in the scene graph. "myString" is the parameter name in the scene graph. "Our string parameter" is a description of our parameter.
Now, we create a scene graph to use our brand new component and parameter.
<Node name="Root" gravity="0 0 0" dt="1" > < ComponentA name="myComponentA" myInt="test string" \> </Node >
Now, when the scene graph is loaded, an instant of the class ComponentA will be created, and its Data StringA will hold the value "test string".
Accessing Data
Now that we have our parameter initialized from the scene graph, we want to do something with it. Because of the Data wrapper, we can't access our string directly. The getValue() function allows us to do read only computations with our Data.
void ComponentA::myOutputFunction() { std::string tempString = stringA.getValue(); std::cout << tempString << std::endl; }
When myOutputFunction() is called on the myComponentA instance of ComponentA, it will output test string.
