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.

Creating an Engine with Multiple Inputs

Examples 6-3 and 6-4 show the header and source files for SoComposeVec2f SoComposeVec2f SoComposeVec2f . This engine has two inputs, x and y, of type SoMFFloat SoMFFloat SoMFFloat , and one output, an SoEngineOutput SoEngineOutput SoEngineOutput of type SoMFVec2f SoMFVec2f SoMFVec2f .

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 SoComposeVec2f SoComposeVec2f 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 .

Replicating Values in Fields with Fewer Values

Figure 6.1.  Replicating Values in Fields with Fewer Values



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();

   // 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");
}

// 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));
   }
}


Creating an Engine with Multiple Outputs

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]));
   }
}