Reading and Modifying Node Graphs

Provides an overview of modifying a graph. The graph stores selection, connection, and muting information for which both read and write access is being provided.

Transaction

The graph access relies on a database access model. To modify a graph, a transaction has to be started, bundling a set of operations. The modification operations are not being carried out until the transaction has been finalized. This finalization operation is called a commit. Transactions can be validated and rolled back, reversing all changes made within a transaction. The transaction logic also ensures a well-formed graph, as when an operation error occurs, the transaction will not be committed and therefore the previously made operations are not going to be applied. Important functions for modifying a graph under this model are:

A change list in this database model is a collection of operations carried out under a transaction. Change lists can be stored to apply changes again to same graph or to a completely different graph.

Access Selections

Information about selection states in a graph is being stored in a DataDictionary. The information can be stored on the root or group level.

Get the Selected Elements

// Return all the selected node in the graph
{
ApplicationOutput("this node @ is selected", node);
return true;
// Return all the selected ports in the graph
{
ApplicationOutput("this port @ is selected", port);
return true;
// Get the selected wire from the graph.
{
ApplicationOutput("this wire @ is selected, it connects port @ and port @", wire, source, dest);
return true;
static MAXON_METHOD Result< void > GetSelectedConnections(const GraphModelRef &graphModel, const ValueReceiver< const GraphNode &, const GraphNode &, const Wires & > &callback)
static MAXON_METHOD Result< void > GetSelectedNodes(const GraphModelRef &graphModel, NODE_KIND kind, const ValueReceiver< const GraphNode & > &callback)
Definition: graph.h:1950
Definition: resultbase.h:766
const Py_UNICODE * source
Definition: unicodeobject.h:54
#define ApplicationOutput(formatString,...)
Definition: debugdiagnostics.h:210
@ NODE
Indicates that the g-node is a true node.
@ PORT_MASK
Mask to check a NODE_KIND if it stands for a port.
#define iferr_return
Definition: resultbase.h:1519
Definition: node.h:10
Definition: graph.h:104

Set the Selected Elements

// Select an element.
// As the Graph is going to be modified, we need to encapsulate our modification inside a transaction.
graphNodeTransaction = nodeGraph.BeginTransaction() iferr_return;
// Select a True Node
// Select a Port
// Select a Wire
// Commit the transaction and make all modification permanent
graphNodeTransaction.Commit() iferr_return;
static MAXON_METHOD Result< void > SelectConnection(const GraphNode &srcPort, const GraphNode &dstPort)
static MAXON_METHOD Result< void > SelectNode(const GraphNode &node)

Handling a Connection

Two ports can be connected with different wire types, representing a connection. These wires convey information between ports. For more information on wires see maxon::Wires.

Adding a Connection

Add a connection between two ports.

// Connect ports is as easy as defining the first and the second port.
colorOutPort.Connect(colorBlendIn1) iferr_return;

Removing a Connection

// Disconnect some ports
// Remove all connections
// Remove all connections in one direction
color2OutPort.RemoveConnections(maxon::PORT_DIR::OUTPUT, maxon::Wires::All()) iferr_return;
static MAXON_METHOD Result< void > RemoveAllConnections(const GraphNode &node)
@ OUTPUT
Output direction, i.e., an output port or an outgoing connection.
static constexpr Wires All(WIRE_MODE mode=WIRE_MODE::ALL)
Definition: graph.h:185

Bypassing and Muting

Information can disabled or hidden without completely removing it with bypassing and muting.

Bypassing a Node

A node can be bypassed.

// Find the port that will contain the Boolean to bypass a node.
const maxon::GraphNode port = colorBlendOut.GetInputs().FindChild(maxon::NODE::BYPASSABLE::BYPASS) iferr_return;
if (port.IsValid())
{
maxon::GraphTransaction transaction = port.GetGraph().BeginTransaction() iferr_return;
transaction.Commit() iferr_return;
}
Result< Bool > SetDefaultValue(T &&value) const
Definition: graph.h:1785
Result< typename SFINAEHelper< GraphNode, BASE >::type > GetInputs() const
Definition: graph.h:1193
Bool IsValid() const
Definition: graph.h:1963
const GraphModelRef & GetGraph() const
Definition: graph.h:1992
Definition: graph.h:979
Result< void > Commit(const DataDictionary &userData=GetPtrSizedZeroRef< DataDictionary >(), Bool validate=true)

Muting a Connection

A connection can be muted.

// Mute All connections for the color node
// As we are changing the node itself, we need to begin a transaction
maxon::GraphTransaction transaction = nodeGraph.BeginTransaction() iferr_return;
// MuteConnectionWith can be used with one node or a port.
transaction.Commit() iferr_return;
static MAXON_METHOD Result< void > MuteConnection(const GraphNode &srcNode, const GraphNode &dstPort)

Muting a Port

To mute a port, it's required to mute all the connection of this port.

// Mute all connection for all port on a node.
// Mute all connection for one port.
static MAXON_METHOD Result< void > MuteAllConnections(const GraphNode &node, PORT_DIR direction)
@ INPUT
Input direction, i.e., an input port or an incoming connection.

Animation Data

Animation data for a graph is not stored in the graph itself, but with the CTrack objects attached to a BaseList2D object associated with the graph. That BaseList2D can be obtained from the NodeMaterial class.

Reading Animation Data

To read animation data, first the special BaseList2D object has to be retrieved which contains the animation tracks. To access those tracks, it's required to know their DescID. See DescID Manual for more information about DescID.

static maxon::Result<void> ReadAnimationData(BaseDocument* doc)
{
// Retrieve the first material
BaseMaterial* material = doc->GetFirstMaterial();
if (!material->IsNodeBased())
return maxon::UnknownError(MAXON_SOURCE_LOCATION);
NodeMaterial* myMaterial = static_cast<NodeMaterial*>(material);
const maxon::Id currentNodeSpaceID = GetActiveNodeSpaceId();
const maxon::NimbusBaseRef nimbusRef = myMaterial->GetNimbusRef(currentNodeSpaceID);
if (nimbusRef == nullptr)
return maxon::NullptrError(MAXON_SOURCE_LOCATION);
const maxon::nodes::NodesGraphModelRef& nodeGraph = nimbusRef.GetGraph();
// Retrieve the end node of this material
const maxon::NodePath endNodePath = nimbusRef.GetPath(maxon::NIMBUS_PATH::MATERIALENDNODE);
if (endNodePath.IsEmpty())
return maxon::UnknownError(MAXON_SOURCE_LOCATION);
const maxon::GraphNode endNode = nodeGraph.GetNode(endNodePath);
if (!endNode.IsValid())
return maxon::UnknownError(MAXON_SOURCE_LOCATION);
const maxon::NodePath transparencyRelativePath = maxon::NodePath::FromCString("<transparency") iferr_return;
maxon::NodePath transparencyPortPath = (endNodePath + transparencyRelativePath) iferr_return;
const maxon::GraphNode transparencyPort = nodeGraph.GetNode(transparencyPortPath);
if (!transparencyPort.IsValid())
return maxon::UnknownError(MAXON_SOURCE_LOCATION);
// Transparency.
// Get base DescID
DescID portDescID;
nimbusRef.GetDescID(transparencyPort, portDescID) iferr_return;
// Get BaseList2D object storing the animation tracks. Note that this is the true node where the
// port is attached not the baselist2D corresponding to the port.
const BaseList2D* const listElement = myMaterial->GetBaseListForNode(currentNodeSpaceID, endNodePath) iferr_return;
BaseList2D* const listElementNonConst = const_cast<BaseList2D*>(listElement);
if (!listElementNonConst)
return maxon::NullptrError(MAXON_SOURCE_LOCATION);
CTrack* track = listElementNonConst->GetFirstCTrack();
while (track)
{
CCurve* cc = track->GetCurve(CCURVE::CURVE, false);
if (cc)
ApplicationOutput("track @ : number of key in the track @", track->GetName(), cc->GetKeyCount());
track = track->GetNext();
}
// Get track type
const maxon::Int32 type = portDescID[-1].dtype;
// If COLORA, access sub-tracks
if (type == DTYPE_COLOR)
{
// Add the sub-track Desc Level.
const DescID fullDescID = portDescID + DescLevel(COLORA_B, DTYPE_REAL, 0);
CTrack* const colorTrack = listElementNonConst->FindCTrack(fullDescID);
if (colorTrack != nullptr)
{
// Get current time
const BaseTime now = doc->GetTime();
// Get track name and current value
const maxon::String name = colorTrack->GetName();
const maxon::Float value = colorTrack->GetValue(doc, now);
ApplicationOutput(" Track: @, Value: @", name, value);
}
}
return maxon::OK;
}
PyObject * value
Definition: abstract.h:715
const char const char * name
Definition: abstract.h:195
maxon::Id GetActiveNodeSpaceId()
Definition: c4d_basedocument.h:498
Definition: c4d_baselist.h:2208
String GetName() const
Definition: c4d_baselist.h:2381
Bool IsNodeBased() const
maxon::NimbusForwardRef GetNimbusRef(const maxon::Id &spaceId) const
CTrack * FindCTrack(const DescID &id)
Definition: c4d_basematerial.h:28
Definition: c4d_basetime.h:25
Definition: c4d_canimation.h:366
Int32 GetKeyCount() const
Definition: c4d_canimation.h:381
Definition: c4d_canimation.h:671
Float GetValue(BaseDocument *doc, const BaseTime &time)
Definition: c4d_canimation.h:855
Definition: lib_description.h:330
Definition: c4d_basematerial.h:391
Definition: apibaseid.h:253
Definition: string.h:1235
@ CURVE
Standard curve.
@ COLORA_B
B component.
Definition: lib_description.h:232
Float64 Float
Definition: apibase.h:197
int32_t Int32
32 bit signed integer datatype.
Definition: apibase.h:176
return OK
Definition: apibase.h:2690
@ DTYPE_REAL
Float
Definition: lib_description.h:68
@ DTYPE_COLOR
Color.
Definition: lib_description.h:57
#define MAXON_SOURCE_LOCATION
Definition: memoryallocationbase.h:67
GvNode * GetNode(GeListNode *bn)
Definition: c4d_graphview.h:2795
The maxon namespace contains all declarations of the MAXON API.
Definition: autoweight.h:14
@ MATERIALENDNODE
Path of the material end node.
PyObject ** type
Definition: pycore_pyerrors.h:34
const char * doc
Definition: pyerrors.h:226
#define iferr_scope
Definition: resultbase.h:1384
Represents a level within a DescID.
Definition: lib_description.h:289

Writing Animation Data

Writing animation data follows the same principle as reading animation data from a node system. Once it has been retrieved the Baselist2D object and its tracks, keyframes can be modified or added as with tracks for any other Baselist2D. See CTrack Manual for more information.

Getting and Setting Values

For each attribute, a GraphNode stores two values: one for the default values and another to store the modified values. For ports, the values of that data are being stored in the default parameter. This is why the value of data stored inside a port has to be accessed with the function maxon::GraphNodeFunctions::SetDefaultValue or maxon::GraphNodeFunctions::GetDefaultValue.

Next example reads the values of all attributes of a node.

Note
This will not read any default values for attributes, but only values that have been actively set.
root.GetChildren([](maxon::GraphNode child) -> maxon::Result<Bool>
{
maxon::GraphAttributeMap graphAttributeMap;
child.GetValues(mask, graphAttributeMap) iferr_return;
for (const auto& value : graphAttributeMap)
ApplicationOutput("Attribute in node @, @ have value @", child, value.GetKey(), value.GetValue());
return true;
FLAGS
Definition: graphattribs.h:90
@ TRANSIENT
The attribute is transient (non-persistent, i.e., not stored by serialization).
@ IMMUTABLE
The attribute value can only be set once on creation of the node or port.
@ MESSAGE_MASK
Use this mask to test for WARNING or ERROR.
@ TYPE_MASK
Use this mask to test for the type (one of DIRECT, DERIVED, USER_STATE or DERIVED|USER_STATE).
Result< void > GetValues(GraphAttributeInterface::FLAGS mask, GraphAttributeMap &map) const
Definition: graph.h:1533
Definition: hashmap.h:1119

Next example sets the name of a GraphNode. To retrieve the value of an attribute, use maxon::GraphNodeFunctions::GetValue.

Note
GetValue can return an empty value if the parameter has not been set.
// Access different attributes values.
const maxon::String name = endNode.GetValue<decltype(maxon::NODE::BASE::NAME)>().GetValueOrNull() iferr_return;
const maxon::IdAndVersion assetId = endNode.GetValue<decltype(maxon::NODE::ATTRIBUTE::ASSETID)>().GetValueOrNull() iferr_return;
const maxon::Id repositoryID = endNode.GetValue<decltype(maxon::NODE::ATTRIBUTE::REPOSITORYID)>().GetValueOrNull() iferr_return;
const maxon::Id assetVersion = endNode.GetValue<decltype(maxon::NODE::BASE::ASSETVERSION)>().GetValueOrNull() iferr_return;
const maxon::String comment = endNode.GetValue<decltype(maxon::NODE::BASE::COMMENT)>().GetValueOrNull() iferr_return;
const maxon::InternedId category = endNode.GetValue<decltype(maxon::NODE::BASE::CATEGORY)>().GetValueOrNull() iferr_return;
ApplicationOutput(" name: @", name);
// Start a transaction to be able to modify a node attributes.
maxon::GraphTransaction graphNodeTransaction = nodeGraph.BeginTransaction() iferr_return;
endNode.SetValue<decltype(maxon::NODE::BASE::NAME)>("My name is !!"_s) iferr_return;
graphNodeTransaction.Commit() iferr_return;
Result< ConstDataPtr > GetValue(const InternedId &attr, const DataType &expectedType) const
Definition: graph.h:1569
Result< Bool > SetValue(const InternedId &attr, ForwardingDataPtr &&value, Bool checkAndInvalidate=true) const
Definition: graph.h:1698
Definition: datatypelib.h:31

Next example defines the data value stored inside a port, using maxon::GraphNodeFunctions::SetDefaultValue

// Begin a transaction to change the data value of a port.
graphNodeTransaction = nodeGraph.BeginTransaction() iferr_return;
// Find the ports.
maxon::GraphNode enableTransparency = endNode.GetInputs().FindChild(maxon::Id("net.maxon.render.node.material.enabletransparency")) iferr_return;
maxon::GraphNode transparency = endNode.GetInputs().FindChild(maxon::Id("net.maxon.render.node.material.transparency")) iferr_return;
// Set the value of the data.
enableTransparency.SetDefaultValue(true) iferr_return;
transparency.SetDefaultValue(maxon::Color(1, 0, 0)) iferr_return;
graphNodeTransaction.Commit() iferr_return;
// Retrieve the data value of the port.
const Bool transparencyIsEnable = enableTransparency.GetDefaultValue(false) iferr_return;
ApplicationOutput("The transparency parameter enable is set to @", transparencyIsEnable);
Result< const T & > GetDefaultValue(const T &def=maxon::NullValue< const T & >()) const
Definition: graph.h:1801
maxon::Bool Bool
Definition: ge_sys_math.h:55