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) { /*...*/ }
          
          
          
          © 2025 MeVis Medical Solutions AG