6.7. Dealing with Multiple-Value Fields

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.4.  SoComposeVec2f.c++


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.