The ML module is of the class Module
which is the base class from which all C++-based image processing modules are derived. Usually, it is used to implement new algorithms for processing voxel images. In our example, it will only offer the fields to parameterize the simple scene.
The class Module
is explained in more detail in the ML Guide, chapter “Deriving Your Own Module from Module
”.
Technically, this module owns the object derived from Base we will implement later:
it constructs and deconstructs the Base object in its constructor and destructor, respectively.
it holds the pointer to the Base object.
it parameterizes the Base object according to the values of its fields and touches the BaseField in order to notify the receiving module(s) that the scene needs to be updated.
To create the MLBaseOwner
module in the BaseCommunication project, use the wizard.
First of all, make sure that you have a user package defined as described in Section 8.2, “Creating a User Package for Your Project” or create it now. Then run the Project Wizard and select the option ML Module. This starts the Wizard for C++/ML Modules.
On the dialog Module Properties, enter the following:
Name: BaseOwner
Comment: Module for setting parameters of a Base object via fields
Keyword: Example
See Also: SoBaseReceiver
Target Package: your package, for example “Example/General”
Project: BaseCommunication
The project name is different to the module name here because the project will later include the module MLBaseOwner
and the additional class BaseMessenger
.
Click Next to proceed.
On the dialog Imaging Module Properties, all settings have to be removed as the module has no image input/outputs.
Keep New Style ML Module, as the setting is irrelevant for our example.
Change the settings to 0 inputs and 0 outputs.
Uncheck all options (Add calculateInputSubImageBox and Add voxel loop to calculateOutputSubImage).
Click Next.
On the dialog Additional Module Properties, the following settings are necessary:
Check Add activateAttachements().
Check Add MDL window with fields.
Uncheck everything else.
Click Next.
On the dialog Module Field Interface, add the following five fields (their sequence is not important):
Enter field:
Field Name: outputMessenger
Field Type: Base
Field Comment: Output of the Base object holding the parameters for the Inventor scene.
Enter field:
Field Name: shapeType
Field Type: Enum
Field Comment: Selects the type of the rendered shape.
Field Value: 0
Enum Values: Cube, Sphere
New field:
Field Name: translation
Field Type: Vector3
Field Comment: The translation of the rendered shape.
New field:
Field Name: color
Field Type: Color
Field Comment: The color of the rendered shape.
New field:
Field Name: diameter
Field Type: Double
Field Comment: The diameter of the rendered shape.
Field Value: 1
Click Create to create the project.
In the default file browser of your system, two folders are opened:
folder with the source code: {packagePath}\Sources\ML\MLBaseCommunication
folder with the module's GUI definition: {packagePath}\Modules\ML\MLBaseCommunication
Note | |
---|---|
For a full list of all created files and their contents, see the MeVisLab Reference Manual, chapter “ML Module (Wizard)”. |
Close the Wizard.
The code resulting from the wizard is:
//----------------------------------------------------------------------------------
//! The ML module class BaseOwner.
/*!
// \file
// \author JDoe
// \date 2016-03-03
//
// Module for setting parameters of a Base object via fields.
*/
//----------------------------------------------------------------------------------
#include "mlBaseOwner.h"
ML_START_NAMESPACE
//! Implements code for the runtime type system of the ML
ML_MODULE_CLASS_SOURCE(BaseOwner, Module);
//----------------------------------------------------------------------------------
BaseOwner::BaseOwner() : Module(0, 0)
{
// Suppress calls of handleNotification on field changes to
// avoid side effects during initialization phase.
handleNotificationOff();
// Add fields to the module and set their values.
_outputMessengerFld = addBase("outputMessenger", NULL);
static const char * const shapeTypeValues[] = { "Cube", "Sphere" };
_shapeTypeFld = addEnum("shapeType", shapeTypeValues, 2);
_shapeTypeFld->setEnumValue(0);
_translationFld = addVector3("translation", Vector3());
_colorFld = addColor("color", 1,1,1);
_diameterFld = addDouble("diameter", 1);
// Reactivate calls of handleNotification on field changes.
handleNotificationOn();
}
//----------------------------------------------------------------------------------
void BaseOwner::handleNotification(Field* field)
{
// Handle changes of module parameters and input image fields here.
}
//----------------------------------------------------------------------------------
void BaseOwner::activateAttachments()
{
// Update members to new field state here.
// Call super class functionality to enable notification handling again.
Module::activateAttachments();
}
ML_END_NAMESPACE
Open your folder {packagePath}/Sources/ML/MLBaseCommunication
and add two empty files:
BaseMessenger.h
BaseMessenger.cpp
These will be used for the BaseMessenger
class that transmits the field values from the ML module to the Open Inventor module.
Open CMakeLists.txt
of the project in a text editor.
Add references to the new files. Result:
target_sources(MLBaseCommunication PRIVATE BaseMessenger.cpp BaseMessenger.h MLBaseCommunicationInit.cpp MLBaseCommunicationInit.h MLBaseCommunicationSystem.h mlBaseOwner.cpp mlBaseOwner.h )
Add the include paths for Base objects (MLBase
) to the configuration:
find_package(MeVisLab COMPONENTS ML MLBase HINTS "$ENV{MLAB_ROOT}" REQUIRED)
target_link_libraries(MLBaseCommunication
PUBLIC
MeVisLab::ML
MeVisLab::MLBase
)
Compile the CMakeLists.txt
file and open the project
in your development environment.
Open BaseMessenger.h
and enter the following code:
//--------------------------------------------------------------------------------
//! This class defines merely a parameter container for
//! visualization attributes and a shape enumeration.
/*!
// \file BaseMessenger.h
// \author JDoe
// \date 2016-03-03
*/
//--------------------------------------------------------------------------------
#pragma once
#include <mlModuleIncludes.h>
#include <mlBase.h>
#include <mlLinearAlgebra.h>
// Local includes
#include "MLBaseCommunicationSystem.h"
ML_START_NAMESPACE
//--------------------------------------------------------------------------------
//! This enumeration lists all possible
//! shape types used in the owner and receiver modules.
enum MessengerShapeType
{
ShapeTypeCube = 0,
ShapeTypeSphere = 1
};
//--------------------------------------------------------------------------------
//! This class defines merely a parameter container for
//! visualization attributes and a shape enumeration.
class MLBASECOMMUNICATION_EXPORT BaseMessenger : public Base
{
public:
//! Constructor.
BaseMessenger();
//! Copy constructor.
BaseMessenger(const BaseMessenger& baseMessenger);
//! Standard destructor.
virtual ~BaseMessenger();
//! \name Methods to retrieve attributes.
//@{
inline const Vector3& getPosition() const { return _position; }
inline const Vector3& getColor() const { return _color; }
inline MLdouble getDiameter() const { return _diameter; }
inline MessengerShapeType getShapeType() const { return _shapeType; }
//@}
//! \name Methods to set attributes.
//@{
inline void setPosition(const Vector3& newPosition) { _position = newPosition; }
inline void setColor(const Vector3& newColor) { _color = newColor; }
inline void setDiameter(MLdouble newDiameter) { _diameter = newDiameter; }
inline void setShapeType(MessengerShapeType newType) { _shapeType = newType; }
//@}
private:
//! \name Member variables.
//@{
Vector3 _position;
Vector3 _color;
MLdouble _diameter;
MessengerShapeType _shapeType;
//@}
//! Implements interface for the runtime type system of the ML.
ML_CLASS_HEADER(BaseMessenger)
};
//--------------------------------------------------------------------------------
ML_END_NAMESPACE
Open BaseMessenger.cpp
and enter the following code:
//--------------------------------------------------------------------------------
//!
/*!
// \file BaseMessenger.cpp
// \author JDoe
// \date 2016-03-03
*/
//--------------------------------------------------------------------------------
// Local includes
#include "BaseMessenger.h"
ML_START_NAMESPACE
//! Implements code for the runtime type system of the ML
ML_MODULE_CLASS_SOURCE(BaseMessenger, Base);
//--------------------------------------------------------------------------------
BaseMessenger::BaseMessenger() : Base()
{
// Set default values.
_position.assign(0.0, 0.0, 0.0);
_color.assign(1.0, 0.0, 0.0); // red
_diameter = 1.0;
}
//--------------------------------------------------------------------------------
BaseMessenger::BaseMessenger(const BaseMessenger& baseMessenger) : Base()
{
// Just copy values of the given object.
_position = baseMessenger.getPosition();
_color = baseMessenger.getColor();
_diameter = baseMessenger.getDiameter();
}
//--------------------------------------------------------------------------------
BaseMessenger::~BaseMessenger()
{
// Not needed.
}
//--------------------------------------------------------------------------------
ML_END_NAMESPACE
Add the initialization of the BaseMessenger
class (runtime type system).
Open MLBaseCommunicationInit.cpp
.
Add the include of BaseMessenger.h
. Result:
// Include all module headers
#include "mlBaseOwner.h"
#include "BaseMessenger.h"
Add the initialization of BaseMessenger.h
. Result:
int MLBaseCommunicationInit ()
{
// Add initClass calls from all other modules here...
BaseOwner::initClass();
BaseMessenger::initClass();
return 1;
}
At this point, the project should be compilable.
Open mlBaseOwner.h
.
Add the include of BaseMessenger.h
. Result:
// Local includes
#include "MLBaseCommunicationSystem.h"
#include "BaseMessenger.h"
Add a destructor to the class:
//! Constructor.
BaseOwner();
//! Destructor.
~BaseOwner();
Add a private member variable of type BaseMessenger pointer since this module is the owner of the Base object. Result:
private:
//! \name Member variables.
//@{
BaseMessenger* _baseMessenger;
//@}
Add a private method to set the value in the Messenger object to the module's field values:
// Implements interface for the runtime type system of the ML.
ML_MODULE_CLASS_HEADER(BaseOwner)
//! Set the field values to the output messenger.
void _setFieldValuesToMessenger();
Open mlBaseOwner.cpp
and add the construction of a new BaseMessenger object and its parameterization
to the constructor of the BaseOwner
module. Use setBaseValueAndAddAllowed
to ensure that the base type will be checked when drawing connetions in the user interface.
Also, add the destruction of the _baseMessenger
object to the destructor.
Result:
BaseOwner::BaseOwner () : Module(0, 0) { // Suppress calls of handleNotification on field changes to // avoid side effects during initialization phase. handleNotificationOff(); // Allocate memory for the BaseMessenger object. // Delete the object in this module's destructor. ML_CHECK_NEW(_baseMessenger, BaseMessenger()); // Add fields for the interface. // Set the pointer to the BaseMessenger object to the output field. _outputMessengerFld = addBase("outputMessenger"); _outputMessengerFld->setBaseValueAndAddAllowedType(_baseMessenger);; static const char * const shapeTypeValues[] = { "Cube", "Sphere" }; _shapeTypeFld = addEnum("shapeType", shapeTypeValues, 2); _shapeTypeFld->setEnumValue(0); _translationFld = addVector3("translation"); _translationFld->setVector3Value(Vector3()); _colorFld = addColor("color"); _colorFld->setColorValue(1,1,1); _diameterFld = addDouble("diameter"); _diameterFld->setDoubleValue(1); _setFieldValuesToMessenger(); // Reactivate calls of handleNotification on field changes. handleNotificationOn(); } // ... BaseOwner::~BaseOwner() { ML_DELETE(_baseMessenger); }
Change handleNotification
so that it touches the output Base field after setting the module's field values to the BaseMessenger
object. Result:
void BaseOwner::handleNotification(Field* field)
{
// Handle changes of module parameters and input image fields here.
bool touchOutputs = false;
if ((field == _shapeTypeFld) ||
(field == _translationFld) ||
(field == _colorFld) ||
(field == _diameterFld))
{
touchOutputs = true;
}
if (touchOutputs)
{
// Set the current parameter values to the messenger object
// and touch the output field so the receiver generates its
// scene anew.
_setFieldValuesToMessenger();
_outputMessengerFld->touch();
}
}
After a saved network has been loaded and all the modules and their connection have been regenerated, the activateAttachements
methods are called. We use this to regenerate the Base object with the saved parameters of the BaseOwner
module.
Result:
void BaseOwner::activateAttachments()
{
// Update members to new field state here.
_setFieldValuesToMessenger();
_outputMessengerFld->touch();
// Call super class functionality to enable notification handling again.
Module::activateAttachments();
}
Implement the setting of the parameters in the BaseMessenger according to the module's fields after the module has been loaded in a network (with restored field values) in activateAttachments(). Result:
void BaseOwner::activateAttachments ()
{
// Update members to new field state here.
_setFieldValuesToMessenger();
// Call super class functionality to enable notification handling again.
Module::activateAttachments ();
}
void BaseOwner::_setFieldValuesToMessenger()
{
_baseMessenger->setPosition(_translationFld->getVector3Value());
_baseMessenger->setColor(_colorFld->getVector3Value());
_baseMessenger->setDiameter(_diameterFld->getDoubleValue());
_baseMessenger->setShapeType(
static_cast<MessengerShapeType>(
_shapeTypeFld->getEnumValue()
)
);
}
ML_END_NAMESPACE
Save the file mlBaseOwner.cpp
.
Make the classes of the project MLBaseCommunication (and with it, the BaseMessenger) known to other projects:
Open the file CMakeLists.txt
of the project in a text editor.
Change the last lines of the file, from:
mlab_install(MLBaseCommunication NS MeVisLab)
to
mlab_install(MLBaseCommunication NS MeVisLab EXPORT) mlab_install_headers(MLBaseCommunication)
Compile the project and restart MeVisLab. To check the final module, enter
BaseOwner
in the quick search and add it.
Tip | |
---|---|
This example is delivered with MeVisLab ( |
To use the MLBaseCommunication
in scripting, an object wrapper can be implemented. How this
is done is explained here.
© 2024 MeVis Medical Solutions AG