On 15/07/2016 at 02:22, xxxxxxxx wrote:
Hi,
sorry, it took me a bit longer to come up with an example. It's not supposed to compete with Niklas' great stuff, but will eventually be added to the SDK examples.
The .res file with a few explanations:
CONTAINER Gvoperatordataexample
{
NAME Gvoperatordataexample;
INCLUDE GVbase; // use if no dynamic ports are involved
GROUP ID_GVPROPERTIES
{
BOOL GVOPERATORDATAEXAMPLE_PROP_BOOL { }
}
GROUP ID_GVPORTS
{
// CREATEPORT [n]: Creates the port on node creation, port is added by default, n may be used with MULTIPLE
// STATICPORT: User can not remove the port
// EDITPORT: The port value can be edited in Attribute Manager, even if the port is not added to the node
// PORTONLY: No parameter in Attribute Manager (overrides EDITPORT)
// MULTIPLE: Multiple instances of the port can be added to the node
// NEEDCONNECTION: Currently not used
// NOTMOVEABLE: Currently not used
LONG GVOPERATORDATAEXAMPLE_INDEX {INPORT; STATICPORT; CREATEPORT;}
LONG GVOPERATORDATAEXAMPLE_LONG_1 {INPORT; PORTONLY; NEEDCONNECTION; CREATEPORT;}
LONG GVOPERATORDATAEXAMPLE_LONG_2 {INPORT; EDITPORT;}
VECTOR GVOPERATORDATAEXAMPLE_VECTOR_MULTI {INPORT; MULTIPLE;}
LONG GVOPERATORDATAEXAMPLE_OUT_LONG {OUTPORT; STATICPORT; CREATEPORT;}
VECTOR GVOPERATORDATAEXAMPLE_OUT_VECTOR {OUTPORT; STATICPORT; CREATEPORT;}
}
}
A few private members used in this example:
GvValuesInfo _ports; // Stores the input and output ports during a calculation
Bool _calcVector; // To avoid reading property from BaseContainer during calculation
Int32 _iterVal; // used to store temporary result in case of iteration
And these are all the function (and Alloc of course) you really need to implement:
Bool GvOperatorDataExample::iCreateOperator(GvNode *bn)
{
BaseContainer* data = bn->GetOpContainerInstance();
if (!data)
return false;
// Set general node parameters (these are located in the node's BaseContainer, not the OpContainer)
bn->SetParameter(ID_GVBASE_COLOR, Vector(0.8, 0.8, 0.8), DESCFLAGS_SET_0); // set a title color
// Init node properties (description params in ID_GVPROPERTIES) here
data->SetBool(GVOPERATORDATAEXAMPLE_PROP_BOOL, true);
return SUPER::iCreateOperator(bn);
}
// Use for custom selection or assuring a certain order of values/ports in GvBuildValuesTable()
// The values info table will be sorted and indexed like this array (see the enums below, defining the indexes for later use in Calculate())
static Int32 inputIds[] = { GVOPERATORDATAEXAMPLE_INDEX, GVOPERATORDATAEXAMPLE_LONG_1, GVOPERATORDATAEXAMPLE_LONG_2, GVOPERATORDATAEXAMPLE_VECTOR_MULTI, 0 };
enum { IDX_GVOPERATORDATAEXAMPLE_INDEX, IDX_GVOPERATORDATAEXAMPLE_LONG_1, IDX_GVOPERATORDATAEXAMPLE_LONG_2, IDX_GVOPERATORDATAEXAMPLE_VECTOR_MULTI };
enum { IDX_GVOPERATORDATAEXAMPLE_OUT_LONG, IDX_GVOPERATORDATAEXAMPLE_OUT_VECTOR };
Bool GvOperatorDataExample::InitCalculation(GvNode *bn, GvCalc *calc, GvRun *run)
{
if (!GvBuildValuesTable(bn, _ports, calc, run, inputIds)) // or GV_EXISTING_PORTS or GV_DEFINED_PORTS instead of input_ids
return false;
BaseContainer* const bc = bn->GetOpContainerInstance();
_calcVector = bc->GetBool(GVOPERATORDATAEXAMPLE_PROP_BOOL);
_iterVal = 0; // reset temp storage for iterator path
return true;
}
void GvOperatorDataExample::FreeCalculation(GvNode *bn, GvCalc *calc)
{
// Don't forget to free the values table(s) and dynamic data at the end of the calculation!
GvFreeValuesTable(bn, _ports);
}
Bool GvOperatorDataExample::Calculate(GvNode *bn, GvPort *port, GvRun *run, GvCalc *calc)
{
Bool result = false;
// First get all input values calculated
// Note: In-values may also be calculated separately,
// for example if one needs only a few of them calculated depending on a mode parameter.
// In this case use _ports.in_values[idx]->Calculate()
if (!GvCalculateInValuesTable(bn, run, calc, _ports))
return false;
// With multiple output ports, Calculate() may be called multiple times per calculation
// port == nullptr means all ports requested
if (!port || (port->GetMainID() == GVOPERATORDATAEXAMPLE_OUT_LONG))
{
// Now get all ports needed for calculation
// Note: Another option would be to do a switch (_ports.in_values[idxPort]->GetMainID()),
// be warned though, this won't work with ports defined with MULTIPLE
GvPort* const portIdx = _ports.in_values[IDX_GVOPERATORDATAEXAMPLE_INDEX]->GetPort();
GvPort* const portL1 = _ports.in_values[IDX_GVOPERATORDATAEXAMPLE_LONG_1]->GetPort();
GvPort* const portL2 = _ports.in_values[IDX_GVOPERATORDATAEXAMPLE_LONG_2]->GetPort();
// Ports defined with MULTIPLE are handled a bit differently, see a bit further down the vector output calculation
if (!portIdx || !portL1 || !portL2)
{
GePrint("Error: There's a port missing for sum calculation");
return false;
}
// Get port values
Int32 idx, l1, l2;
if (!portIdx->GetInteger(&idx, run))
return false;
if (!portL1->GetInteger(&l1, run))
return false;
if (!portL2->GetInteger(&l2, run))
return false;
// Do the sum calculation for the long output
//if (run->IsIterationPath())
// GePrint("Node is connected to an iterator"); // maybe want to act special here
_iterVal += (l1 + l2) * (idx + 1); // storing in a member variable to use the result in next iteration (if any)
result = _ports.out_ports[IDX_GVOPERATORDATAEXAMPLE_OUT_LONG]->SetInteger(_iterVal, run);
if (!result)
{
GePrint("Error: Failed to set sum output port");
return false;
}
}
if (!port || (port->GetMainID() == GVOPERATORDATAEXAMPLE_OUT_VECTOR))
{
// Now turn to the MULTIPLE ports and calculate the vector output
Vector vecResult = Vector(42.0);
if (_calcVector)
{
GvValue* const valMulti = _ports.in_values[IDX_GVOPERATORDATAEXAMPLE_VECTOR_MULTI];
if (valMulti)
{
Int32 numMultiPorts = valMulti->NrOfPorts();
vecResult = Vector(0.0);
for (Int32 idxVal = 0; idxVal < numMultiPorts; ++idxVal)
{
// In this example GvCalculateInValuesTable() at the beginning already did the work,
// but in case in-values are requested separately, one would do it like so:
//if (!valMulti->Calculate(bn, GV_PORT_INPUT, run, calc, idxVal))
// return false;
Vector vec;
if (!valMulti->GetPort(idxVal)->GetVector(&vec, run))
return false;
vecResult += vec;
}
vecResult = vecResult / numMultiPorts;
}
}
result = _ports.out_ports[IDX_GVOPERATORDATAEXAMPLE_OUT_VECTOR]->SetVector(vecResult, run);
if (!result)
{
GePrint("Error: Failed to set vector output port");
return false;
}
}
if (!result)
GePrint("Error: Either got calculation request for an unknown out port or failed to set an output!");
return result;
}
Note: AddToCalculationTable() and SetRecalculate() are not needed for "normal" operators.
AddToCalculationTable() is used to have your operator being calculated regardless of any output port requests.
SetRecalculate() is mainly used for iterators and TP operators.