Accessing node data

Read an MTG file

Let's first read a simple MTG file:

using MultiScaleTreeGraph

file = joinpath(dirname(dirname(pathof(MultiScaleTreeGraph))),"test","files","simple_plant.mtg")
mtg = read_mtg(file)
/ 1: Scene
└─ / 2: Individual
   └─ / 3: Axis
      └─ / 4: Internode
         ├─ + 5: Leaf
         └─ < 6: Internode
            └─ + 7: Leaf
Node

If you print a node, it will always print its subtree. So if we print the root node it will print the entire MTG.

read_mtg returns a Node object.

typeof(mtg)
Node{MutableNodeMTG, Dict{Symbol, Any}}

This node is the root node of the MTG, meaning the first node of the MTG, the one without any parent.

Note

Root node and leaf node mean a node without any parent or children respectively. These terms are used in the sense of a tree data structure.

The Node type

What's a node?

As we learned in the previous section, the node is used as the elementary object to build the MTG. In this package, a node is a data structure used to hold these informations (i.e. fields). See the MTG implementation section for more details.

Access the fields of a node

The first node of the whole MTG is all we need to access every other nodes in the MTG, because they are all linked together.

You can list the fields in a node like so:

fieldnames(typeof(mtg))
(:id, :parent, :children, :MTG, :attributes, :traversal_cache)

These fields are considered internal to the package, and are not meant to be accessed directly like so. In any case, you can still access them using the getfield function, e.g.:

getfield(mtg, :MTG)
MutableNodeMTG("/", "Scene", 0, 0)

But the preferred way for accessing such values is to use the accessor functions provided by the package: parent, children, node_attributes, node_mtg, node_id.

So to get the values of the attributes (i.e. the variables), you can use:

node_attributes(mtg)
Dict{Symbol, Any} with 3 entries:
  :scales      => [0, 1, 2, 3, 3]
  :description => 2×4 DataFrame…
  :symbols     => SubString{String}["Scene", "Individual", "Axis", "Internode",…

The MTG field from the node helps us describe the node within the MTG. Let's see what's in it:

node_mtg(mtg) |> typeof |> fieldnames
(:link, :symbol, :index, :scale)

We see that it holds the MTG fields: the scale, symbol, index and link to its parent.

The package also provide helper functions to access the MTG encoding of the node directly: symbol, scale, index and link.

Update the node

Similarly, we can update the fields of a node using the setting functions provided by the package: reparent!, rechildren!.

The package also provides updating functions for the MTG encoding of the node directly: symbol!, scale!, index! and link!.

Note

There is no setting function for the attributes or the node id because they are not meant to be updated directly. The attribute structure shouldn't change, only its content should (see next paragraph). The node id is unique in the MTG, so it should never change.

Get other nodes

We can move from node to node in the MTG graph because we know every node parent and children.

Get children nodes

To get the children of a node, you can use the children function:

children(mtg)
1-element Vector{Node{MutableNodeMTG, Dict{Symbol, Any}}}:
 / 2: Individual
└─ / 3: Axis
   └─ / 4: Internode
      ├─ + 5: Leaf
      └─ < 6: Internode
         └─ + 7: Leaf

You can also also index the node using an integer, and it will return the corresponding child (1 for the first child, 2 for the second, etc.):

mtg[1]
/ 2: Individual
└─ / 3: Axis
   └─ / 4: Internode
      ├─ + 5: Leaf
      └─ < 6: Internode
         └─ + 7: Leaf

We can iteratively index into the nodes to access the descendants of a node. For example if we need to access the 6th node (the 2nd Internode), we would do:

node_6 = mtg[1][1][1][2]
< 6: Internode
└─ + 7: Leaf

Get the parent node

To get the parent you can use:

parent(mtg)

Note that it returns nothing here because the root node has no parent.

Get the node siblings

To get the node siblings:

siblings(mtg[1][1][1][1])
1-element Vector{Node{MutableNodeMTG, Dict{Symbol, Any}}}:
 < 6: Internode
└─ + 7: Leaf
Note

We repeatedly index into the MTG (mtg[1][1][1][1]) to get the fourth generation descendant of the root node, because it is the only one with a sibling in our example MTG.

Get any node

To get any node in the MTG, you can get it by its id:

get_node(mtg, 3)
/ 3: Axis
└─ / 4: Internode
   ├─ + 5: Leaf
   └─ < 6: Internode
      └─ + 7: Leaf

You can list all node ids using list_nodes:

list_nodes(mtg)
7-element Vector{Int64}:
 1
 2
 3
 4
 5
 6
 7

Get the root node

To get the root node from any other node, simply use get_root:

node_5 = get_node(mtg, 5)

get_root(node_5)
/ 1: Scene
└─ / 2: Individual
   └─ / 3: Axis
      └─ / 4: Internode
         ├─ + 5: Leaf
         └─ < 6: Internode
            └─ + 7: Leaf

Get the attributes

This section has its own tutorial! Head over the next page to learn how to get the nodes attributes.