The following paragraphs describe some features for advanced configurations of your ML module. This includes:
a detailed description of the differences between standard, extended and registered voxel types (Section 7.5.1, “About the Difference Between Scalar, Extended and Registered Voxel Types”),
information on how to get and manage metadata about registered voxel types (Section 7.5.2, “Getting and Managing Metadata About Registered Voxel Types”),
information on how to reduce generated code and shorten compile times (Section 7.5.3, “Reducing Generated Code and Compile Times”),
information on how to configure supported voxel types (Section 7.5.4, “Configuration of Supported Voxel Types”),
information on how to implement a new voxel data type(Section 7.5.5, “Implementing a New Voxel Data Type by Deriving from MLTypeInfos”).
There are three different kinds of voxels types you need to distinguish when you want to understand how the ML works in detail.
Scalar Voxel Types
Scalar voxel types are primitive data types. They are available in many programming languages, such as signed and unsigned 8, 16, 32 and/or 64 bit sized integers, float and double types. They are also the most typical types used for image voxels.
In the ML, these types are called
MLuint8
, MLint8
,
MLuint16
, MLint16
,
MLuint32
, MLint32
,
MLint64
, MLfloat
,
and MLdouble
. There are also enumerator
constants called MLuint8Type
,
MLint8Type
,
MLuint16Type
,
MLint16Type
,
MLuint32Type
,
MLint32Type
,
MLfloatType
, and
MLdoubleType
, respectively.
Note | |
---|---|
For compatibility reasons, the
|
Extended Voxel Types
Extended voxel types are all types that are composed of more than one component, e.g. complex, quaternion, vector or matrix types.
There is a set of default extended types that is supported by some macros that are used to instantiate template methods for image calculation: std::complex<float>
, std::complex<double>
, Vector2f
, Vector2d
, Vector3f
, Vector3d
, Vector6f
, Vector6d
, Matrix2f
, Matrix2d
, Matrix3f
and Matrix3d
. Apart from these macro where these types are 'hardcoded', these types have no other special meaning.
Registered Voxel Types
Registered voxel types are loaded to the application code
on runtime. Each registered type provides a function table with
functions for data addition, subtraction, multiplication, shift
and so on. This table can be used to perform operations on this type.They also provide
an MLTypeInfo
data structure describing
their properties, such as name, number of components, size,
etc.
The pre-registered types all have enumerators, type traits descriptions (via the TypeTraits
template class) and type names that can be used in code directly.
The ML provides a number of functions to analyze, convert, process and manage a data type and its values as well as the components of these values.
These functions are useful for building modules that apply abstract operations on arbitrary data types, for example decomposing a voxel of any data type into its components or casting any arbitrary registered data type to another one.
Note | |
---|---|
All these functions are part of the C-API of the ML. Hence
they can also be used for managing voxel data in C programs or in
modules that do not include the You do not have to distinguish between scalar and registered voxel data. The following functionality also works fine on scalar voxel types and data. |
MLSizeOf (MLDataType dt)
,
MLGetDataTypeFromName (const char* dtName)
,
MLIsValidDataType (MLDataType dt)
,
MLIsScalarType (MLDataType dt)
,
MLGetTypeInfosForDataType (MLDataType dt)
,
and
which are often used for module development, are described in Section 7.2.1, “Important Functions For Voxel Types”.
Functions for managing voxel components:
const char* MLGetCDataTypeNameForCharCode(char code);
Returns the basic C/C++ data type name corresponding to a character representing it. On invalid codes "" is returned.
const char* MLGetMLDataTypeNameForCharCode(char code);
Returns an ML type name compatible with a character
representing it. On invalid codes "" is returned. The return
value match for function calls to
MLDataTypeFromName()
.
MLDataType MLGetMLDataTypeForCharCode(char code);
Returns an ML data type compatible with a character representing it. On invalid codes -1 is returned.
MLint32 MLTypeGetComponentProperties(char code, MLint32* isSigned, MLint32* isIntegerType, MLint32* isFloatingPointType, MLint32* isLongType);
Returns 1 (=true)
in
*isSigned
,
*isIntegerType
,
*isFloatingPointType
and
*isLongType
if the component type
represented by code
includes this
features, otherwise set that flag to 0 (=false)
. Invalid code
values return 0 (=false)
in all
parameters. It is explicitly permitted to pass NULL as
isSigned
,
isIntegerType
,
isFloatingPointType
or
isLongType
to ignore these. 1 (=true)
is returned if comp
was a valid component, otherwise the return value is 0 (=false)
.
size_t MLTypeComponentSize(char comp);
Returns the size of a MLTypeComponent denoted by a character code. On invalid character codes, 0 is returned. Valid codes are:
'b' = bool
'c' = unsigned char
'C' = char
's' = unsigned short
'S' = short
'i' = unsigned int
'I' = int
'l' = unsigned long
'L' = long
'6' = MLint64
'f' = float
'd' = double
'D' = long double
void MLTypeSetDoubleComponent(char comp, MLdouble val, MLTypeData *dstPtr);
Interprets the data referenced by
*dstPtr
as data of the type
comp
and sets its value from the passed
MLdouble
value by casting the
val
to it. Invalid character codes are
ignored.
void MLTypeSetIntComponent (char comp, MLCTInt val, MLTypeData *dstPtr);
Same as MLTypeSetDoubleComponent
,
but components are set to integer values.
void MLTypeSetAllDoubleComponents(const MLTypeInfos *infos, MLdouble val, MLTypeData *dstPtr);
All components of the data referenced by
*dstPtr
are set to a value cast from
the MLdouble
value
val
. Casting is performed by the
MLTypeSetComponent
function.
void MLTypeSetAllIntComponent (const MLTypeInfos *infos, MLCTInt val, MLTypeData *dstPtr);
Same as
MLTypeSetAllDoubleComponents
, but
components are set to integer values.
MLdouble MLTypeGetDoubleComponent(char comp, const MLTypeData *dstPtr);
Interprets the data referenced by
*dstPtr
as data of the type
comp
and returns it as an
MLdouble
value. Invalid character codes
are ignored and 0 is returned.
MLCTInt MLTypeGetIntComponent (char comp, const MLTypeData *dstPtr);
Same as MLTypeGetComponent
, but
components are returned as integer values.
void MLTypeShiftLeftComponent(char comp, const MLTypeData *srcPtr, MLCTInt shiftLs, MLTypeData *dstPtr);
Interprets the data referenced by
*dstPtr
as data of the type
comp
and shifts data
shiftLs
times to the left, if it is an
integer component. Floating point components are multiplied
with 2shiftLs. Negative values for
shiftLs
are interpreted as shift right
operations or divisions by
2shiftLs, respectively. Boolean
components become false
on all
shiftLs != 0
. Zero shiftLs
does not change values. Invalid character codes are ignored,
i.e., pointers and values are not changed.
Functions to operate on data of registered voxels:
MLTypeData *MLAllocateVoxelBuffer(MLDataType dataType, size_t numVoxels, const MLTypeData *voxDefault);
Returns a buffer of numVoxels
voxels of data type dataType
. On
failure, NULL is returned. If
voxDefault
is NULL, the allocated
memory is left undefined, otherwise all voxels are filled with
the default value pointed to by
voxDefault
. The allocated buffer must
be removed with MLFree()
.
char *MLGetVoxelValueAsString(const MLTypeData *data, MLDataType dataType, MLErrorCode *errCode);
Interprets the data given by data
as a value of type dataType
and returns
its value as a string. If anything fails, "" is returned.
errCode
may be passed as NULL. If
errCode
is not NULL,
*errCode
is set to the error code on failures;
otherwise it is set to ML_RESULT_OK. Floating point values are
normally printed with maximum precision. The returned pointer
must be freed with MLFree()
.
char *MLGetVoxelValueAsStringLimited(const MLTypeData *data, MLDataType dataType, MLErrorCode *errCode, int maxPrec);
Interprets the data given by data
as a value of type dataType
and returns
its value as a string. If anything fails, "" is returned.
errCode
may be passed as NULL. If
errCode
is not NULL,
*errCode
is set to the error code on failures;
otherwise it is set to ML_RESULT_OK. If
maxPrec
is passed with a negative
value, the maximum precision of floating point numbers is
printed. If passed >= 0, the number of digits is limited to
maxPrec
. It will be not larger than the
maximum default precision, even when it is accordingly
specified. The returned pointer must be freed with
MLFree()
.
char *MLTypeComponentsToString(const MLTypeInfos *infos, const MLTypeData *p);
Converts a data type instance to a string.
infos
point to the type information and
p
points to the data of the type
instance. The return value is a string containing the type
components which are converted to string values that are
separated by spaces. It must be freed with
MLFree()
. Floating point values are
normally printed with maximum precision. On failures (e.g.
infos
==NULL,
p
==NULL), an empty string is returned
which also must be freed.
char *MLTypeComponentsToStringLimited(const MLTypeInfos *infos, const MLTypeData *p, int maxPrec);
Converts a data type instance to a string.
infos
point to the type information and
p
points to the data of the type
instance. The return value is a string containing the type
components which are converted to string values that are
separated by spaces. It must be freed with
MLFree()
. If
maxPrec
is passed with a negative
value, the maximum precision of floating point numbers is
printed. If passed >= 0, the number of digits is limited to
maxPrec
. It will not be larger than the
maximum default precision even if specified so. On failures
(e.g., infos
==NULL,
p
==NULL), an empty string is returned
which also must be freed.
MLint32 MLTypeComponentsFromString(const MLTypeInfos *infos, const char *str, const MLTypeData *defaultVal, MLTypeData *p);
Converts a string of a data type instance to instance
data, i.e., like an sscanf. infos
point
to the type information and p
points to
the data of the type instance to be filled with data scanned
from the string. The return value is 1 if the string could be
scanned successfully. On scan failures or invalid parameters,
0 is returned. If a default value is passed in
defaultVal
, components which could not
be scanned correctly are copied from their corresponding
positions in defaultVal
. If
defaultVal
is passed as NULL, those
components are left unchanged.
MLint32 MLTypeComponentsFromStream(void *iStr, void *iStrStream, void *stdiStr, void *stdiStrStream, const MLTypeInfos *infos, MLTypeData *data);
Reads data type components into different stream
versions (istream
and istrstream
within and outside the standard namespace). Since we have a C
interface here, we need to pass the pointers to the streams as
void*
addresses. Hence be careful to which of the
first parameters the stream is passed. All other can be set to
NULL. On any error, *data
is correctly
set as far as possible, and all unreadable values are set to
the default value. On bad parameters, failures or not
completely readable values, 0 is returned, otherwise 1.
MLdouble MLGetVoxelValueAsDouble(const void *data, MLDataType dataType, MLErrorCode *errCode);
Interprets the data given by data
as a value of type dataType
and return
its value cast to double. If anything fails then 0 is
returned. errCode
may be passed as
NULL. If errCode
is not NULL then
*errCode
is set to the error code on
failures; otherwise it is set to
ML_RESULT_OK
.
MLCTBool MLTypeCastToBool (const MLTypeInfos *infos, const MLTypeData *p);
If p
is identical to default
element, false (= 0)
is returned,
otherwise true (= 1).
MLCTInt MLTypeCastToInt (const MLTypeInfos *infos, const MLTypeData *p);
The first component of the data type
p
is converted to integer and
returned.
MLdouble MLTypeCastToDouble (const MLTypeInfos *infos, const MLTypeData *p);
The first component of the data type
p
is converted to double and
returned.
void MLTypeCastFromBool (const MLTypeInfos *infos, MLCTBool p, MLTypeData *q);
If p == 0
then q
is
set to the type default value given by
infos
. If p != 0
then all
components of the type are cast to their values cast from
1.
void MLTypeCastFromInt (const MLTypeInfos *infos, MLCTInt p, MLTypeData *q);
The integer value of p
is cast to
the types of the components and then written to them.
void MLTypeCastFromDouble (const MLTypeInfos *infos, MLdouble p, MLTypeData *q);
The value p
is cast to the types
of the components and then written to them.
void MLTypeBinaryAndInt (const MLTypeInfos *infos, const MLTypeData *p, MLCTInt q, MLTypeData *r);
Takes all components from p
as
integer values, applies a bitwise 'and' operation with
q
and writes them as (cast from)
integer values back to the corresponding components of
r
.
void MLTypeBinaryOrInt (const MLTypeInfos *infos, const MLTypeData *p, MLCTInt q, MLTypeData *r);
Takes all components from p
as
integer values, applies a bitwise 'or' operation with
q
and writes them as (cast from)
integer values back to the corresponding components of
r
.
void MLTypeBinaryXorInt (const MLTypeInfos *infos, const MLTypeData *p, MLCTInt q, MLTypeData *r);
Takes all components from p
as
integer values, applies a bitwise 'xor' operation with
q
and writes them as (cast from)
integer values back to the corresponding components of
r
.
void MLTypeBinaryAnd (const MLTypeInfos *infos, const MLTypeData *p, const MLTypeData *q, MLTypeData *r);
Takes all components from p
as
integer values, applies a bitwise 'and' operation with
corresponding components from q
(also
as integers) and writes them as (cast from) integer values
back to the corresponding components of
r
.
void MLTypeBinaryOr (const MLTypeInfos *infos, const MLTypeData *p, const MLTypeData *q, MLTypeData *r);
Takes all components from p
as
integer values, applies a bitwise 'or' operation with
corresponding components from q
(also
as integers) and writes them as (cast from) integer values
back to the corresponding components of
r
.
void MLTypeBinaryXor (const MLTypeInfos *infos, const MLTypeData *p, const MLTypeData *q, MLTypeData *r);
Takes all components from p
as
integer values, applies a bitwise 'xor' operation with
corresponding components from q
(also
as integers) and writes them as (cast from) integer values
back to the corresponding components of
r
.
void MLTypeShiftComponentsLeft(const MLTypeInfos *infos, const MLTypeData *p, MLCTInt q, MLTypeData *r);
Takes one data type component after another and shifts
each component left shiftLs
times if it
is an integer component. Floating point components are
multiplied with 2shiftLs. Negative
values for shiftLs
are interpreted as
shift right operations or divisions by
2shiftLs, respectively. Boolean
components become false
on all
shiftLs != 0
. Zero shiftLs
does not change any component.
void MLTypeCastToOtherType(const MLTypeInfos *otherInfos, const MLTypeData *otherData, const MLTypeInfos *myInfos, MLTypeData *myData);
Converts a data instance referenced by
otherData
of a type specified by
otherInfos
to another data instance
referenced by *myData
of a type
specified by myInfos
. As long as
components of any data type in the source exist, the
myData
components are set to the same
values. Components which do not have a counterpart in the
otherData
are filled with the
counterparts from its default value given by the
myInfos
. E.g.: If an (int, char,
double) data type (represented by "ICd") is cast to a four
component float vector (represented by "ffff"), then the first
three components are set from an int cast to double, from an
char cast to double and from an double cast to double. The
fourth component is copied from the fourth component of the
type default value given in the
dstInfos
of type
MLTypeInfo
.
void MLTypeCastFromOtherType(const MLTypeInfos *otherInfos, const MLTypeData *otherData, const MLTypeInfos *myInfos, MLTypeData *myData);
Casts another data element
otherData
with attributes given by
otherInfos
to
myData
of a type given by
myInfos
. See
MLTypeCastToOtherType
for more
infos.
MLint32 MLTypeIsEqualToOtherType(const MLTypeInfos *myInfos, const MLTypeData *myData, const MLTypeInfos *otherInfos, const MLTypeData *otherData);
Casts another data element
otherData
with attributes given by
otherInfos
to a local buffer of a type
given by myInfos
. If that buffer is
equal to myData
then 1 (=true)
is returned, otherwise 0 (=false)
. For the comparison
myInfos->isEqualToType
is used.
MLint32 MLTypeIsSmallerThanOtherType(const MLTypeInfos *myInfos, const MLTypeData *myData, const MLTypeInfos *otherInfos, const MLTypeData *otherData);
Casts another data element
otherData
with attributes given by
otherInfos
to a local buffer of a type
given by myInfos
. If that buffer is
smaller than myData
then 1 (=true)
is returned, otherwise 0 (=false)
. For the comparison
myInfos->isSmallerThanType
is used.
MLint32 MLTypeIsGreaterThanOtherType(const MLTypeInfos *myInfos, const MLTypeData *myData, const MLTypeInfos *otherInfos, const MLTypeData *otherData);
Casts another data element
otherData
with attributes given by
otherInfos
to a local buffer of a type
given by myInfos
. If that buffer is
greater to myData
then 1 (=true
) is returned, otherwise 0 (=false)
. For the comparison
myInfos->isGreaterThanType
is used.
void MLTypeMultWithOtherType(const MLTypeInfos *myInfos, const MLTypeData *myData, const MLTypeInfos *otherInfos, const MLTypeData *otherData, MLTypeData *r);
Casts another data element
otherData
with attributes given by
otherInfos
to a local buffer of a type
given by myInfos
. That buffer is
multiplied with myData
and written into
r
. For the multiplication
myInfos->multWithType
is used.
void MLTypeDivByOtherType(const MLTypeInfos *myInfos, const MLTypeData *myData, const MLTypeInfos *otherInfos, const MLTypeData *otherData, MLTypeData *r);
Casts another data element
otherData
with attributes given by
otherInfos
to a local buffer of a type
given by myInfos
. Then
myData
is divided by the buffer and
written into r
. For the division
myInfos->divByType
is used.
void MLTypeAddOtherType(const MLTypeInfos *myInfos, const MLTypeData *myData, const MLTypeInfos *otherInfos, const MLTypeData *otherData, MLTypeData *r);
Casts another data element
otherData
with attributes given by
otherInfos
to a local buffer of a type
given by myInfos
. That buffer is added
with myData
and written into
r
. For the addition
myInfos->addToType
is used.
void MLTypeSubtractOtherType(const MLTypeInfos *myInfos, const MLTypeData *myData, const MLTypeInfos *otherInfos, const MLTypeData *otherData, MLTypeData *r);
Casts another data element
otherData
with attributes given by
otherInfos
to a local buffer of a type
given by myInfos
. That buffer is
subtracted from myData
and written into
r
. For the subtraction
myInfos->subtractFromType
is used.
void MLTypePowerOfOtherType(const MLTypeInfos *myInfos, const MLTypeData *myData, const MLTypeInfos *otherInfos, const MLTypeData *otherData, MLTypeData *r);
Casts another data element
otherData
with attributes given by
otherInfos
to a local buffer of a type
given by myInfos
. The power of
myData
with the buffer is calculated
and written into r
. For the power
calculation myInfos->powerOfType
is
used.
Sometimes a module programmer knows that a module only makes sense for images with certain voxel types. In this case, the number of potential voxel types can be reduced so that the code is smaller and the compilation times are shortened.
Typical application areas are binary operations on voxels which only work fine on integer voxels; or operations on normalized values which are always between 0 and 1 and consequently require floating point type voxels to avoid information loss. Also, some operations such as gradient calculations or tensor imaging might require operations which only make sense on registered vector or matrix voxels.
A typical ML module uses a ML_CALCULATE_OUTPUTSUBIMAGE
macro to compile the template calculateOutputSubImage
function for all scalar types:
// Implements the call to the typed calculateOutputSubImage method for all potential data types. ML_CALCULATEOUTPUTSUBIMAGE_NUM_INPUTS_1_SCALAR_TYPES_CPP(NormalModule); //! Fill output page with calculated data in a module with one input. template <typename DATATYPE> void NormalModule::calculateOutputSubImage(TSubImage<DATATYPE> *outSubImg, int outIndex, TSubImage<DATATYPE> *inSubImg) { // Calculate contents of outSubImg here. }
The following example shows how to compile the template function
for all available integer types only. It uses a special
ML_CALCULATE_OUTPUTSUBIMAGE
macro which accepts an
additional parameter to determine the set of data type cases to be
compiled:
// Implements the call to the typed calculateOutputSubImage method for all integer types. ML_CALCULATEOUTPUTSUBIMAGE_NUM_INPUTS_1_WITH_CUSTOM_SWITCH_CPP(CalcTest, ML_IMPLEMENT_INT_CASES); //! Fill output page with calculated data in a module with one input. template <typename DATATYPE> void NormalModule::calculateOutputSubImage(TSubImage<DATATYPE> *outSubImg, int outIndex, TSubImage<DATATYPE> *inSubImg) { // Calculate contents of outSubImg here. }
The following predefined type configurations of data type cases can be used:
ML_IMPLEMENT_INT_CASES
- Implements
all integer types.
ML_IMPLEMENT_FLOAT_CASES
- Implements
all floating point types.
ML_IMPLEMENT_INT_FLOAT_CASES
-
Implements all integer and floating point types.
ML_IMPLEMENT_INT_FLOAT_CASES_WO_INT64
- Implements all integer and floating point types without the 64 bit integer types.
ML_IMPLEMENT_COMPLEX_CASES
- Implements the complex types.
ML_IMPLEMENT_VECTOR_CASES
- Implements the default vector types: Vector2
, Vector3
and Vector6
(both with float and double component types).
ML_IMPLEMENT_MATRIX_CASES
- Implements the default matrix types: Matrix2
and Matrix3
(both with float and double component types).
ML_IMPLEMENT_SCALAR_CASES
- Identical to
ML_IMPLEMENT_INT_FLOAT_CASES
which
implements the scalar voxel types used in most ML
modules.
ML_IMPLEMENT_DEFAULT_CASES
- This combines ML_IMPLEMENT_SCALAR_CASES
, ML_IMPLEMENT_COMPLEX_CASES
, ML_IMPLEMENT_VECTOR_CASES
and ML_IMPLEMENT_MATRIX_CASES
.
You can also configure your own combinations using the following constants:
ML_IMPLEMENT_INT8_CASE
and
ML_IMPLEMENT_UINT8_CASE
- Signed and
unsigned 8 bit integers.
ML_IMPLEMENT_INT16_CASE
and
ML_IMPLEMENT_UINT16_CASE
- Signed and
unsigned 16 bit integers.
ML_IMPLEMENT_INT32_CASE
and
ML_IMPLEMENT_UINT32_CASE
- Signed and
unsigned 32 bit integers.
ML_IMPLEMENT_INT64_CASE
- Signed 64
bit integer.
ML_IMPLEMENT_FLOAT_CASE
,
ML_IMPLEMENT_DOUBLE_CASE
- Floating point
types.
MLint64
, the MLdouble
and all complex types are compiled:#define ML_IMPLEMENT_LARGE_AND_COMPLEX_CASES(CLASS_NAME, SWITCH_CODE, DUMMY1, DUMMY2, DUMMY3) \ ML_IMPLEMENT_INT64_CASE( CLASS_NAME, SWITCH_CODE, DUMMY1, DUMMY2, DUMMY3) \ ML_IMPLEMENT_DOUBLE_CASE( CLASS_NAME, SWITCH_CODE, DUMMY1, DUMMY2, DUMMY3) \ ML_IMPLEMENT_COMPLEX_CASES( CLASS_NAME, SWITCH_CODE, DUMMY1, DUMMY2, DUMMY3) ML_CALCULATEOUTPUTSUBIMAGE_NUM_INPUTS_1_WITH_CUSTOM_SWITCH_CPP (CalcTest, ML_IMPLEMENT_LARGE_AND_COMPLEX_CASES); template <typename DATATYPE> void NormalModule::calculateOutputSubImage(TSubImage<DATATYPE> *outSubImg, int outIndex, TSubImage<DATATYPE> *inSubImg){ ... }
There are some advanced options you can use when you activate the support of your ML module for registered data types.
setVoxelDataTypeSupport(ONLY_DEFAULT_TYPE)
lets the module work with the default type set supported by macros like ML_CALCULATEOUTPUTSUBIMAGE_NUM_INPUTS_1_DEFAULT_TYPES_CPP
. So if you use a module macro with DEFAULT
in its name, use this value.
setVoxelDataTypeSupport(ONLY_SCALAR_TYPES)
is the default setting which causes the ML to deactivate all
modules outputs if any of the voxel data is of non-scalar
type. This stops a module from operating on non-scalar data
types, so that a module programmer does not have to care about
them at all.
setVoxelDataTypeSupport(ALL_REGISTERED_TYPES)
is the mode to have the module work with all registered voxel
types which do not provide all operations of a standard type.
Note | |
---|---|
A module can always restrict the types it supports in the |
This mode is useful for modules which handle or just
pass voxel data, but do not calculate explicit output values,
like for example a SubImage
module. It is also useful for modules which support types other than the scalar types when they are not using the module macro for the default type set, e.g. when a module is implemented through typed output handlers.
You can register your own voxel data type. A structure that describes the data type, its properties and a function table with its operations can be registered in the ML to activate a new type. Modules that perform generic operations that use the registered type structures (directly or indirectly) will automatically work with this new type.
There are some steps to take and many functions to implement,
but generally it is not really difficult and does not involve as much
work as one might think. The easiest way is to use the
MLTypeAddExampleInfos
example as a template for integrating a new type; this way you
will not forget any important steps. See MLTypeAddExample for detailed
information.
Take an MLTypeInfos
structure from
mlTypeDefs.h
.
Set all function pointers in that structure to functions that implement your data type operations.
Initialize your MLTypeInfos
(Section 7.5.5.1, “Describing a New Voxel Type with MLTypeInfos”) (see
mlTypeDefs.h
)
by using MLTypeInfosInit()
(see
mlDataTypes.h
).
Create one instance of your initialized
MLTypeInfos
and register it on library
initialization by using MLRegisterTypeInfos()
from
mlDataTypes.h
.
The result of your implementation should be an initialized
MLTypeInfos
structure that describes all data
type properties, features and operations (see Section 7.5.5.1, “Describing a New Voxel Type with MLTypeInfos”). Take a closer look at this structure
now.
The MLTypeInfos
structure describes all
features, properties and operations of a data type. It contains all
data type features and pointers to all functions needed to implement
generic operations on the data type. It is a wrapper for any information and code needed for
any standard or user-defined data type the ML uses. The descriptions
of the scalar ML data types are implemented in
.
The descriptive components of the
MLTypeInfos
structure are permanent and
non-changing values requested by many operations that need data type
information. Most of these values can be initialized with the
function MLTypeInfosInit()
which also performs
checks for valid data type initialization and calculates the more
difficult components of the MLTypeInfos
structure. All other stuff (i.e., the function pointers) should be
initialized by using the macro
ML_TYPE_ASSIGN_FUNCTION_POINTERS()
as explained in the example in Section 7.5.5.2, “The MLTypeAddExample”.
size_t numComps
Number of components of this data type. Equals number of
characters in *structInfoString
. A scalar
value has 1 component, complex numbers have 2 components, and ML
vectors have 6 components (see Section 2.4.1, “ImageVector
, ImageVector” for
details) Each component must be a scalar object as described in
number 13.
size_t typeSize
The sizeof
of the registered data
type, i.e., its size in bytes.
const char *name
The pointer to a null-terminated character string that gives the data type name. It should contain alphanumeric characters only.
MLDataType rangeAndPrecisionEquivalent
Returns a standard data type which has a comparable range and precision behavior.
double dblMin
Double minimum of data type.
double dblMax
Double maximum of data type.
const MLTypeData *typeMinPtr
Minimum value of the data type.
const MLTypeData *typeMaxPtr
Maximum value of the data type.
const MLTypeData *typeDefaultPtr
The default value of the data type, comparable to zero.
size_t numGoodCastTos
Number of data types to which this type can be cast without information or functionality loss.
const char **goodCastTos
Pointer to a table of a null-terminated string of data type names to which this type can be cast without information or functionality loss.
unsigned int compOffsets[ML_MAX_COMPONENTS_EXTENDED_TYPE]
Table of byte offsets from the first component to other
components to directly address any component with a character
pointer. e.g., if a data type consists of a float, a char and
another float component where sizeof(float)
is 4 and sizeof(char)
is 1, the first table
entry must be 0, the second entry must be 4, and the third entry
must be 5.
Pointer to a null-terminated string that describes the
type configuration as explained for the
typeStructInfo
parameter of the function
MLTypeInfoInit
.
int dataTypeId
The MLDataType
id of the
registered type. This should be a constant value. If you want to define your own types you should contact the MeVisLab team to get your own id range assigned.
The operative components of the
MLTypeInfos
structure are function pointers
which are called when operations on a registered data type are
needed. We will forgo the opportunity to list all functions here, simply refer to the definition of MLTypeInfos
in
mlTypeDefs.h
for the required functions.
Many operations can simply be implemented by using convenience functions which are already implemented in the ML, e.g. to cast one extended data type to another.
The parameters of these functions are often pointers of type
MLTypeData
to instances of the data type; the
parameters need to be cast to be able to work on the correct data
type.
The MLTypeInfosInit()
function checks for
valid data type initialization, and calculates the more difficult
components of the MLTypeInfos
structure. It
returns 1(=true)
on success,
0(=false)
on failure.
Please refer to the
MLTypeAddExample
example on how exactly to register your own type.
The following example shows how to implement a new voxel type. The example does not implement all functions in order to keep the example short. However, the implementation of most functions should not be a problem when you look at similar functions for reference.
Note | |
---|---|
|
The initialization of the new voxel data type, typically to be implemented in the library initialization file:
Example 7.9.
MLRegisterTypeInfos
//! Create static instances of all data types to be used in the ML. //! These instances will directly be registered as new ML data types. static MLTypeAddExampleInfos _MLNewVType; int initResult = MLRegisterTypeInfos(&_MLNewVType); return initResult;
The implementation of the
MLTypeAddExampleInfos
class which is used to
create the registered instance in the library initialization
file:
Example 7.10. How to Add Your Own Voxel Data Type
This example is outdated. Please refer to the example code provided with the MeVisLab SDK for the current version.
#ifndef __mlMLGuideTypeAddExampleInfos_H #define __mlMLGuideTypeAddExampleInfos_H // ML-includes #ifndef __MLTypeAddExampleSystem_H #include "MLTypeAddExampleSystem.h" #endif #ifndef __mlDataTypes_H #include "mlDataTypes.h" #endif #ifndef __mlUtils_H #include "mlUtils.h" #endif
ML_START_NAMESPACE //! The data type to be implemented as a new voxel data type. //! For simplification we register a new type which does the //! same as the normal MLdouble type. typedef MLdouble NewVType; //-------------------------------------------------------------------------------------- //! Example class to create a new voxel data types to be registered in the ML. //-------------------------------------------------------------------------------------- class ML_NEW_VTYPEEXTENSION_EXPORT MLTypeAddExampleInfos : public MLTypeInfos { protected: //! Reference to a permanently existing constant instance of NewVType containing the //! minimum data type value. static const NewVType &_typeMin() { static NewVType v=-DBL_MAX; return v; } //! Reference to a permanently existing constant instance of NewVType containing the //! maximum data type value. static const NewVType &_typeMax() { static NewVType v=DBL_MAX; return v; } //! Reference to a permanently existing constant instance of NewVType containing the //! default data type value. static const NewVType &_typeDefault(){ static NewVType v=0; return v; } //! Permanent instance of a pointer to the typeInfos used by this class. It //! will often be used as a kind of this pointer for the static instance of //! this data type information. static MLTypeInfos *_myInfos; //! Number of instances of this class. Only used to avoid that more than one //! instance is created. static size_t _numInstances;
public: //! Constructor. It initializes //! - all data type operations by setting pointers of a //! function table to the data type operations (implemented //! as static functions) by using the macro //! ML_TYPE_ASSIGN_FUNCTION_POINTERS(); //! - all other data type properties, like min/max/default values (as //! MLdouble and as type values) by using the function MLTypeInfosInit(), //! - it checks for at most one instance of this class. MLTypeAddExampleInfos() { // We permit only one instance since most class settings are static constant. if (_numInstances > 0){ mlError("MLTypeAddExampleInfos", ML_PROGRAMMING_ERROR) << "Too many instances of MLTypeAddExampleInfos created."; } _numInstances++; // Store pointer to this. We only have one instance. So we simulate a kind // "this" pointer for this static instance. _myInfos = this; // Assign all pointers to the static functions implementing the operations. // The function names have predefined names beginning with "MLTYPE_" (see // function implementation below). ML_TYPE_ASSIGN_FUNCTION_POINTERS();
// Specify all type names and their number to which this type can be cast // without information loss. size_t numGoodCastTos = 1; static const char *goodCastTos[] = { "NewVType" }; // Initialize the new MLTypeInfos struct. For a parameter description see // discussion of MLTypeInfos structure or the type documentation in the // mlTypeDefs.h file. NewVType buf; void *addr[1]; addr[0] = &buf; MLTypeInfosInit(this, sizeof(NewVType), "NewVType", -DBL_MAX, DBL_MAX, (MLTypeData*)(&_typeMin()), (MLTypeData*)(&_typeMax()), (MLTypeData*)(&_typeDefault()), "d", false, MLdoubleType, addr, numGoodCastTos, goodCastTos ); }
//! Return value as string to be freed by MLFree(). //! Use MLTypeComponentsToString() if possible. static char *MLTYPE_getStringValue(const MLTypeData *p) { return MLTypeComponentsToString(_myInfos, p); } //! Convert string s to value and write result into r. //! Use MLTypeComponentsFromString() if possible. static void MLTYPE_setStringValue(const char *s, MLTypeData *r) { MLTypeComponentsFromString(_myInfos, s, (MLTypeData*)&(_typeDefault()), r); } // IMPLEMENT MINIMUM/MAXIMUM/DEFAULT AND COPY OPERATIONS. //! Sets p to minimum value. Must be implemented. static void MLTYPE_setToMinimum(MLTypeData *p) { memcpy(p, &_typeMin(), sizeof(NewVType)); }
//! Sets p to minimum value. Must be implemented. static void MLTYPE_setToMaximum(MLTypeData *p) { memcpy(p, &_typeMax(), sizeof(NewVType)); } //! Sets p to default value. Must be implemented. static void MLTYPE_setToDefault(MLTypeData *p) { memcpy(p, &_typeDefault(), sizeof(NewVType)); } //! Copy parameter p to second q. static void MLTYPE_copy(const MLTypeData *p, MLTypeData *q) { memcpy(q, p, sizeof(NewVType)); }
// IMPLEMENT CAST OPERATIONS FROM THE NEW TYPE TO BOOL/INT/DOUBLE/OTHER TYPE. //! Return parameter p cast to bool. Typically false when it is identical to //! the default element, otherwise true. static MLCTBool MLTYPE_castToBool(const MLTypeData *p) { return (*((NewVType*)p)) != _typeDefault(); } //! Return parameter p cast to integer. Often implemented as //! the integer cast of the first component. static MLCTInt MLTYPE_castToInt(const MLTypeData *p) { return (MLCTInt)( *((NewVType*)p) ); } //! Return parameter p cast to MLdouble. Often implemented as //! the integer cast of the first component. static MLdouble MLTYPE_castToDouble(const MLTypeData *p) { return (MLdouble)( *((NewVType*)p) ); } //! Cast myData to otherData who has type infos otherInfos. Usually //! implemented by default with function casting componentwise. static void MLTYPE_castToOtherType(const MLTypeData *myData, const MLTypeInfos *otherInfos, MLTypeData *otherData) { MLTypeCastToOtherType(_myInfos, myData, otherInfos, otherData); }
// IMPLEMENT CAST OPERATIONS FROM INT/DOUBLE/OTHER TYPE TO THE NEW TYPE. //! Cast first parameters to data type and write it into second parameter. static void MLTYPE_castFromInt(MLCTInt p, MLTypeData *q) { *((NewVType*)q) = (NewVType)p; } //! Cast first parameters to data type and write it into second parameter. static void MLTYPE_castFromDouble(MLdouble p, MLTypeData *q) { *((NewVType*)q) = (NewVType)p; } //! Cast first parameters to data type and write it into second parameter. static void MLTYPE_castFromOtherType(const MLTypeInfos *otherInfos, const MLTypeData *otherData, MLTypeData *myData) { MLTypeCastToOtherType(otherInfos,otherData, _myInfos, myData); }
static MLCTBool MLTYPE_isEqualToType(const MLTypeData *p, const MLTypeData *q) { return (*((NewVType*)p)) == (*((NewVType*)q)); }
// IMPLEMENT SOME SPECIAL FUNCTIONS //! Negate the value. static void MLTYPE_negate(const MLTypeData *p, MLTypeData *q) { *((NewVType*)q) = -(*((NewVType*)p)); } //! Normalize type. static void MLTYPE_normalize (const MLTypeData * /*p*/, MLTypeData *q) { *((NewVType*)q) = (NewVType)(1); }
// IMPLEMENT MULTIPLICATION FUNCTIONS. THE RESULT IS WRITTEN ALWAYS INTO LAST // FUNCTION PARAMETER R. //! Implement multiplication with integer. Result written into parameter three. static void MLTYPE_multWithInt(const MLTypeData *p, MLCTInt q, MLTypeData *r) { *((NewVType*)r) = (*((NewVType*)p)) * (NewVType)q; } //! Implement multiplication with double. Result written into parameter three. static void MLTYPE_multWithDouble(const MLTypeData *p, MLdouble q, MLTypeData *r) { *((NewVType*)r) = (*((NewVType*)p)) * (NewVType)q; } //! Implement multiplication with its own type. Result written into parameter three. static void MLTYPE_multWithType(const MLTypeData *p, const MLTypeData *q, MLTypeData *r) { *((NewVType*)r) = (*((NewVType*)p)) * (*((NewVType*)q)); } //! Implement multiplication with another type. Result written into parameter three. static void MLTYPE_multWithOtherType(const MLTypeInfos *otherInfos, const MLTypeData *otherData, const MLTypeData *myData, MLTypeData *r) { MLTypeMultWithOtherType(_myInfos, myData, otherInfos, otherData, r); }
//! IMPLEMENT ADDITIONS. SEE MULTIPLICATION FUNCTIONS FOR SIMILAR CODE. static void MLTYPE_plusInt (const MLTypeData *p, MLCTInt q, MLTypeData *r) { /*...*/ } static void MLTYPE_plusDouble(const MLTypeData *p, MLdouble q, MLTypeData *r) { /*...*/ } static void MLTYPE_plusType (const MLTypeData *p, const MLTypeData *q, MLTypeData *r) { /*...*/ }
© 2024 MeVis Medical Solutions AG