Graph View Calculation (XPresso) Manual

There are several ways to classify operators in the GV graph. The first distinction is between operators with and without side-effects. Operators with side-effects are those who directly affect the world outside GV, for example an Object node with input ports or the Result node. We will call these initiators. On the other side, the large majority of operators do not have side-effects. These are just calculators of values inside the GV system, like the Math node or the Range Mapper node.

If a graph has only calculators there is nothing that needs to be calculated, since it would not make any difference to the outside world anyway. It is only when an initiator has been added that the calculation can start.

In practice this is done by the node master calling GvOperatorData::AddToCalculationTable() for all nodes. Pure calculators will not have this function, so they just return false, but all initiators will call GvRun::AddNodeToCalculationTable() here. These initiators then get a call from the node master to GvOperatorData::Calculate(), with port set to nullptr.

During this initial call the initiators can evaluate which of their input ports they need to be able to compute their side-effect. Then they effectively call GvValue::Calculate() for each of these input ports, normally using one of the helper functions like GvCalculateInValuesTable().

It is only when one of these initiators calculates its input ports that the calculators get a call to GvOperatorData::Calculate(), this time with port set to the specific output port that they need to calculate. They in turn might need to evaluate their input ports to deliver this value, and so the recursive calculation has started.

Of course, some operators can act like a combination of calculators and initiators. For example, the Object node is like an initiator for its input ports but like a calculator for its output ports.

A special case of initiators is iterators. During their initial call from the node master they may also call GvPort::SetRecalculate() for their output ports. This invalidates any connected input ports so that when the iterator then calls GvRun::CalculateTable() it can expect another call to GvOperatorData::Calculate() from the connected operators, to retrieve the iteration value. The iterator repeats this for as many values as it likes.

Technically what happens when the iterator calls GvPort::SetRecalculate() then GvOperatorData::SetRecalculate() is called for all connected input ports' nodes. Initiators will have overridden this function, just like GvOperatorData::AddToCalculationTable(), and call GvRun::AddNodeToCalculationTable() to tell GV that they will want to retrieve a new value and update their side-effect. On the other hand, the default behavior for calculators is to just pass the recalculation call along to their output ports' connections. So there is a recursive chain of recalculations. In the end there will be an initiator that starts a calculation back through that chain, so that eventually the iterator gets a call to GvOperatorData::Calculate() for the port that started it all.

To sum this up, here is a table with some typical examples of different kinds of operators:

Input: N/AResultN/A
Output: ConstantN/AHierarchy
Both: MathObject