This second engine has a slightly more complex evaluate() method. Before creating the output, it determines the longest input field, and replicates the last value in short fields that need to be filled out to match the number of values in the longest field. The SO_ENGINE_OUTPUT() macro is used to set the number of values in the fields connected from the output, as well as to set the values.
Examples 6-3 and 6-4 show the header and source files for SoComposeVec2f( C++ | Java | .NET ). This engine has two inputs, x and y, of type SoMFFloat( C++ | Java | .NET ), and one output, an SoEngineOutput( C++ | Java | .NET ) of type SoMFVec2f( C++ | Java | .NET ).
This engine illustrates a general policy for handling multiple-value inputs that is followed by all arithmetic engines in Inventor. This policy is useful for engines that process multiple-value inputs as “parallel” independent data sets, but it is not required. Because the x and y inputs of the SoComposeVec2f( C++ | Java | .NET ) engine are multiple-value fields, they can each contain any number of values. Figure 6.1, “ Replicating Values in Fields with Fewer Values ” illustrates a case where the last value of y is replicated to fill out the field with values to match up with the number of values provided in x .
Note that the constructor sets the default value for both input fields to 0.0.
Example 6.3. SoComposeVec2f.h
#include <Inventor/engines/SoSubEngine.h> #include <Inventor/fields/SoMFFloat.h> #include <Inventor/fields/SoMFVec2f.h> class SoComposeVec2f : public SoEngine { SO_ENGINE_HEADER(SoComposeVec2f); public: // Inputs: SoMFFloat x; SoMFFloat y; // Output: SoEngineOutput vector; // (SoMFVec2f) // Initialization static void initClass(); static void exitClass(); // Constructor SoComposeVec2f(); private: // Destructor virtual ~SoComposeVec2f(); // Evaluation method virtual void evaluate(); };
Example 6.4. SoComposeVec2f.c++
#include "SoComposeVec2f.h" SO_ENGINE_SOURCE(SoComposeVec2f); // Initializes the SoComposeVec2f class. void SoComposeVec2f::initClass() { SO_ENGINE_INIT_CLASS(SoComposeVec2f, SoEngine, "Engine"); } void SoComposeVec2f::exitClass() { SO_ENGINE_EXIT_CLASS(SoComposeVec2f); } // Constructor SoComposeVec2f::SoComposeVec2f() { // Do standard constructor tasks SO_ENGINE_CONSTRUCTOR(SoComposeVec2f); // Define input fields and their default values SO_ENGINE_ADD_INPUT(x, (0.0)); SO_ENGINE_ADD_INPUT(y, (0.0)); // Define the output, specifying its type SO_ENGINE_ADD_OUTPUT(vector, SoMFVec2f); } // Destructor. Does nothing. SoComposeVec2f::~SoComposeVec2f() { } // This is the evaluation routine. void SoComposeVec2f::evaluate() { // Figure out how many input values we have int numX = x.getNum(); int numY = y.getNum(); // We will output as many values as there are in the input // with the greater number of values int numToOutput = (numX > numY ? numX : numY); // Make sure that all of the fields connected from the output // have enough room for the results. The SoMField::setNum() // method does this. SO_ENGINE_OUTPUT(vector, SoMFVec2f, setNum(numToOutput)); // Now output the vectors composed from the input values float xValue, yValue; int i; for (i = 0; i < numToOutput; i++) { // If there are different numbers of values in the input // fields, repeat the last value as necessary. xValue = (i < numX ? x[i] : x[numX - 1]); yValue = (i < numY ? y[i] : y[numY - 1]); // Set the vector value in the indexed slot in all // connected fields SO_ENGINE_OUTPUT(vector, SoMFVec2f, set1Value(i, xValue, yValue)); } }
This engine class shows creating an engine with more than one output. Examples 6-5 and 6-6 illustrate an engine that decomposes a vector into its individual float values. The evaluate() method uses the getNum() method to determine how many input values there are. Then it uses the SoMField::setNum() method to ensure that the fields connected from this engine have enough room for the results, as in the previous example.
Example 6.5. SoDecomposeVec2f.h
#include <Inventor/engines/SoSubEngine.h> #include <Inventor/fields/SoMFFloat.h> #include <Inventor/fields/SoMFVec2f.h> class SoDecomposeVec2f : public SoEngine { SO_ENGINE_HEADER(SoDecomposeVec2f); public: // Input: SoMFVec2f vector; // Outputs: SoEngineOutput x; // (SoMFFloat) SoEngineOutput y; // (SoMFFloat) // Initialization static void initClass(); // Constructor SoDecomposeVec2f(); private: // Destructor virtual ~SoDecomposeVec2f(); // Evaluation method virtual void evaluate(); };
Example 6.6. SoDecomposeVec2f.c++
#include "SoDecomposeVec2f.h" SO_ENGINE_SOURCE(SoDecomposeVec2f); // Initializes the SoDecomposeVec2f class. void SoDecomposeVec2f::initClass() { SO_ENGINE_INIT_CLASS(SoDecomposeVec2f, SoEngine, "Engine"); } // Constructor SoDecomposeVec2f::SoDecomposeVec2f() { // Do standard constructor tasks SO_ENGINE_CONSTRUCTOR(SoDecomposeVec2f); // Define input field and its default value SO_ENGINE_ADD_INPUT(vector, (0.0, 0.0)); // Define the outputs, specifying their types SO_ENGINE_ADD_OUTPUT(x, SoMFFloat); SO_ENGINE_ADD_OUTPUT(y, SoMFFloat); } // Destructor. Does nothing. SoDecomposeVec2f::~SoDecomposeVec2f() { } // This is the evaluation routine. void SoDecomposeVec2f::evaluate() { // Figure out how many input values we have int numToOutput = vector.getNum(); // Make sure that all of the fields connected from the // outputs have enough room for the results. The // SoMField::setNum() method does this. SO_ENGINE_OUTPUT(x, SoMFFloat, setNum(numToOutput)); SO_ENGINE_OUTPUT(y, SoMFFloat, setNum(numToOutput)); // Now output the values extracted from the input vectors for (int i = 0; i < numToOutput; i++) { SO_ENGINE_OUTPUT(x, SoMFFloat, set1Value(i, vector[i][0])); SO_ENGINE_OUTPUT(y, SoMFFloat, set1Value(i, vector[i][1])); } }