Create your engine
Create DataEngine Components
Basic components such as the example above that inherits BaseObject
, only allow for “Parameter” data fields (values that can be changed but only affect the component itself, internally).
Sometimes, you might want to create input / output components.
Engines can be “chained”, by giving an engine an input data that is the output of another, previously declared engine:
<?xml version="1.0"?>
<Node name="Root">
<RequiredPlugin pluginName="MyPlugin"/>
<MechanicalObject name=”MO” />
<MyEngine name=”engine1” myinput="@MO.position" />
<MyEngine myinput=”@engine1.myoutput” />
</Node>
These engine components will trigger an action when notified of a change in an input data field, and warn other engines, that take as an input the output of this engine, that the data has been modified.
In order to implement such a component, your class must inherit sofa::core::DataEngine
class.
The interface of the DataEngine class inherits BaseObject's, and adds an additional doUpdate()
method. Input and output fields must be added to the engine in the init() method by respectively calling addInput(myInputData)
and addOutput(myOutputData)
:
virtual void init() override
{
addInput(d_input1)
addInput(d_input2)
...
addOutput(d_output1)
addOutput(d_output2)
...
}
doUpdate()
is a pure virtual method of DataEngine. This method will be automatically called when another component in the scene graph tries to access a data field (getValue() / beginEdit() / ReadAccessor / WriteAccessor ...
) that is linked to one of your Engine's output field.
In doUpdate()
your input fields are always "ready", i.e already updated from their parent value, thus holding up-to-date data.
Each engine holds an internal DataTracker
, called m_dataTracker
. DataTrackers can tell you which input field has been changed, during a call to doUpdate(). DataTrackers need to be told which data it should track. this is done by calling:
addInput
automatically adds the given input to the dataTracker.
If you have multiple input fields, and want to perform specific functions depending on which input has been modified, you can use this DataTracker to check which input has changed:
virtual void doUpdate()
{
if (m_dataTracker.hasChanged(d_input1))
{
// Do Stuff With d_input1
...
}
if (m_dataTracker.hasChanged(d_input2))
{
// Do stuff with d_input2
...
}
...
}
Remarks
- Note that you should not modify your inputs in an engine. I do not know of any use cases where this would be a good idea. If you do, it will set the engine's dirty flag, hence forcing a re-call to update().
If for some reason you would need to do so, you will need to call
DDGNode::cleanDirty()
on your engine to prevent the call loop. doUpdate
is actually a delegate function called by theupdate()
method. This method isfinal
, which means that you cannot override it. Here's its implementation:
void DataEngine::update()
{
updateAllInputs(); ///< Updates all input fields to retrieve their parent values if changed
DDGNode::cleanDirty(); ///< Cleans the engine's "dirty value".
/// This dirty value is used to know whether or not the call to update is necessary
doUpdate(); ///< Your engine's implementation is called here.
m_dataTracker.clean(); ///< The dataTracker is cleaned, i.e its internal counters are synced with the input's counters
}