Skip to content

Commit 05ef043

Browse files
committed
Merge branch 'main' of https://github.com/compas-dev/compas
2 parents 09adc5d + 8392844 commit 05ef043

File tree

3 files changed

+189
-2
lines changed

3 files changed

+189
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
* Added `compas_rhino8` as starting point for Rhino8 support.
13+
* Added tutorial for `compas.datastructures.Tree`.
1314

1415
### Changed
1516

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,189 @@
11
********************************************************************************
22
Trees
33
********************************************************************************
4+
5+
.. rst-class:: lead
6+
7+
A :class:`compas.datastructures.Tree` is a data structure that can be used to
8+
represent hierarchical relationships between data. It starts with a single root
9+
node, and each node can have a number of children nodes. Each node can
10+
store arbitrary attributes.
11+
12+
13+
.. note::
14+
15+
Please refer to the API for a complete overview of all functionality:
16+
17+
* :class:`compas.datastructures.Tree`
18+
19+
Tree Construction
20+
=================
21+
22+
Trees can be constructed in a number of ways:
23+
* by adding nodes to a tree instance, specifiying parent node,
24+
* by adding nodes directly to another node instance that belongs to a tree, or
25+
* directly loaded from a serilised json file.
26+
27+
Adding To Tree
28+
--------------
29+
30+
>>> from compas.datastructures import Tree
31+
>>> from compas.datastructures import TreeNode
32+
>>> tree = Tree()
33+
>>> root_node = TreeNode('root')
34+
>>> tree.add(root_node)
35+
>>> branch_node = TreeNode('branch')
36+
>>> tree.add(branch_node, parent=root_node)
37+
>>> leaf_node1 = TreeNode('leaf1')
38+
>>> tree.add(leaf_node1, parent=branch_node)
39+
>>> leaf_node2 = TreeNode('leaf2')
40+
>>> tree.add(leaf_node2, parent=branch_node)
41+
>>> tree.print_hierarchy()
42+
└── <TreeNode root>
43+
└── <TreeNode branch>
44+
├── <TreeNode leaf1>
45+
└── <TreeNode leaf2>
46+
47+
Adding To Node
48+
--------------
49+
50+
>>> from compas.datastructures import Tree
51+
>>> from compas.datastructures import TreeNode
52+
>>> tree = Tree()
53+
>>> root_node = TreeNode('root')
54+
>>> tree.add(root_node)
55+
>>> branch_node = TreeNode('branch')
56+
>>> root_node.add(branch_node)
57+
>>> leaf_node1 = TreeNode('leaf1')
58+
>>> branch_node.add(leaf_node1)
59+
>>> leaf_node2 = TreeNode('leaf2')
60+
>>> branch_node.add(leaf_node2)
61+
└── <TreeNode root>
62+
└── <TreeNode branch>
63+
├── <TreeNode leaf1>
64+
└── <TreeNode leaf2>
65+
66+
Loading From File
67+
-----------------
68+
69+
>>> from compas.data import json_load
70+
>>> tree = json_load('tree.json')
71+
>>> tree.print_hierarchy()
72+
└── <TreeNode root>
73+
└── <TreeNode branch>
74+
├── <TreeNode leaf1>
75+
└── <TreeNode leaf2>
76+
77+
Remove Node
78+
-----------
79+
80+
>>> tree.remove(leaf_node2) # Or branch_node.remove(leaf_node2)
81+
>>> tree.print_hierarchy()
82+
└── <TreeNode root>
83+
└── <TreeNode branch>
84+
└── <TreeNode leaf1>
85+
86+
87+
Tree Traversal
88+
==============
89+
The tree can be traversed in a number of ways, including depth-first and breadth-first.
90+
For depth-first traversal, there are additional options for pre-order and post-order.
91+
92+
Depth-First
93+
-----------
94+
The algorithm starts at the root node and explores as far as possible along each branch before backtracking.
95+
The traversal can be done in pre-orderor post-order. In pre-order, the parent node is visited before its children. In post-order,
96+
the parent node is visited after its children.
97+
98+
>>> from compas.datastructures import Tree
99+
>>> from compas.datastructures import TreeNode
100+
>>> tree = Tree()
101+
>>> root_node = TreeNode('root')
102+
>>> tree.add(root_node)
103+
>>> branch_node1 = TreeNode('branch1')
104+
>>> root_node.add(branch_node1)
105+
>>> leaf_node1 = TreeNode('leaf1')
106+
>>> branch_node1.add(leaf_node1)
107+
>>> leaf_node2 = TreeNode('leaf2')
108+
>>> branch_node1.add(leaf_node2)
109+
>>> branch_node2 = TreeNode('branch2')
110+
>>> root_node.add(branch_node2)
111+
>>> leaf_node3 = TreeNode('leaf3')
112+
>>> branch_node2.add(leaf_node3)
113+
>>> leaf_node4 = TreeNode('leaf4')
114+
>>> branch_node2.add(leaf_node4)
115+
>>> tree.print_hierarchy()
116+
└── <TreeNode root>
117+
├── <TreeNode branch1>
118+
│ ├── <TreeNode leaf1>
119+
│ └── <TreeNode leaf2>
120+
└── <TreeNode branch2>
121+
├── <TreeNode leaf3>
122+
└── <TreeNode leaf4>
123+
124+
>>> for node in tree.traverse(strategy='depthfirst', order='preorder'):
125+
... print(node)
126+
<TreeNode root>
127+
<TreeNode branch1>
128+
<TreeNode leaf1>
129+
<TreeNode leaf2>
130+
<TreeNode branch2>
131+
<TreeNode leaf3>
132+
<TreeNode leaf4>
133+
134+
>>> for node in tree.traverse(strategy='depthfirst', order='postorder'):
135+
... print(node)
136+
<TreeNode leaf1>
137+
<TreeNode leaf2>
138+
<TreeNode branch1>
139+
<TreeNode leaf3>
140+
<TreeNode leaf4>
141+
<TreeNode branch2>
142+
<TreeNode root>
143+
144+
145+
Breadth-First
146+
-------------
147+
The algorithm starts at the root node and explores the neighbour nodes first, before moving to the next level neighbours.
148+
149+
>>> for node in tree.traverse(strategy='breadthfirst'):
150+
... print(node)
151+
<TreeNode root>
152+
<TreeNode branch1>
153+
<TreeNode branch2>
154+
<TreeNode leaf1>
155+
<TreeNode leaf2>
156+
<TreeNode leaf3>
157+
<TreeNode leaf4>
158+
159+
160+
Node Attributes
161+
===============
162+
163+
>>> nodes = tree.get_nodes_by_name('branch1')
164+
>>> nodes
165+
[<TreeNode branch1>]
166+
167+
>>> node = tree.get_node_by_name('branch1')
168+
>>> node
169+
<TreeNode branch1>
170+
171+
>>> node.is_root
172+
False
173+
>>> node.is_leaf
174+
False
175+
>>> node.is_branch
176+
True
177+
178+
>>> node.parent
179+
<TreeNode root>
180+
>>> node.children
181+
[<TreeNode leaf1>, <TreeNode leaf2>]
182+
183+
>>> leaf1 = node.children[0]
184+
>>> list(leaf1.ancestors)
185+
[<TreeNode branch1>, <TreeNode root>]
186+
>>> root = leaf1.ancestors[-1]
187+
>>> list(root.descendants)
188+
[<TreeNode branch1>, <TreeNode leaf1>, <TreeNode leaf2>, <TreeNode branch2>, <TreeNode leaf3>, <TreeNode leaf4>]
189+

src/compas/datastructures/tree/tree.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ def remove(self, node):
151151
@property
152152
def ancestors(self):
153153
this = self
154-
while this:
155-
yield this
154+
while this.parent:
155+
yield this.parent
156156
this = this.parent
157157

158158
@property

0 commit comments

Comments
 (0)