Open Search
    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