Skip to content

Commit e887548

Browse files
committed
add tutorial to docs
1 parent 656154c commit e887548

File tree

5 files changed

+85
-4
lines changed

5 files changed

+85
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3232
* Added `compas.geometry.curves.curve.Curve.from_native`.
3333
* Added `compas_rhino.geometry.curves.curve.Curve.from_native`.
3434
* Added `compas_rhino.geometry.curves.nurbs.NurbsCurve.from_native`.
35+
* Added `compas.datastructures.HashTree` and `compas.datastructures.HashNode`.
3536

3637
### Changed
3738

@@ -165,7 +166,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
165166
* Added `compas.datastructures.CellNetwork.face_plane`
166167
* Added `compas.datastructures.CellNetwork.cell_volume`
167168
* Added `compas.datastructures.CellNetwork.cell_neighbors`
168-
* Added `compas.datastructures.HashTree` and `compas.datastructures.HashNode`.
169169

170170
### Changed
171171

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
********************************************************************************
2+
Hash Tree
3+
********************************************************************************
4+
5+
Hash tree (or Merkle tree) is a tree data structure in which every leaf node is labelled with the hash of a data block and every non-leaf node is labelled with the cryptographic hash of the labels of its child nodes.
6+
Hash trees are useful because they allow efficient and secure verification of the contents of large data structures. It is widly used in modern distributed version control systems like Git as well as peer-to-peer systems like Blockchain.
7+
COMPAS provides a simple implementation of a hash tree that can be used for detecting and locating changes in a complex data structure. In context of AEC, this feature can also be useful for many real-world applications,
8+
such as detecting changes in a complicated Building Information Model, tracking minor deformation in structural assessments, or even detecting robot joint movements in a digital fabracation process, and many more.
9+
10+
Hash Tree From Dict
11+
===================
12+
13+
A COMPAS hash tree can be created from any raw python dictionary using the `HashTree.from_dict` method.
14+
15+
>>> from compas.datastructures import HashTree
16+
>>> data = {'a': 1, 'b': 2, 'c': {'d': 3, 'e': 4}}
17+
>>> tree = HashTree.from_dict(data)
18+
19+
The structure of the hash tree and crypo hash on each node can be visualised using the `print` function.
20+
21+
>>> print(tree)
22+
<Tree with 6 nodes>
23+
└── ROOT @ b2e1c
24+
├── .a:1 @ 4d9a8
25+
├── .b:2 @ 82b86
26+
└── .c @ 664a3
27+
├── .d:3 @ 76d82
28+
└── .e:4 @ ebe84
29+
30+
Once the original data is modified, a new hash tree can be created from the modified data and the changes can be detected by comparing the two hash trees.
31+
32+
>>> data['c']['d'] = 5
33+
>>> del data["b"]
34+
>>> data["f"] = True
35+
>>> new_tree = HashTree.from_dict(data)
36+
>>> print(new_tree)
37+
<Tree with 6 nodes>
38+
└── ROOT @ a8c1b
39+
├── .a:1 @ 4d9a8
40+
├── .c @ e1701
41+
│ ├── .d:5 @ 98b1e
42+
│ └── .e:4 @ ebe84
43+
└── .f:True @ 753e5
44+
45+
>>> new_tree.diff(tree)
46+
{'added': [{'path': '.f', 'value': True}], 'removed': [{'path': '.b', 'value': 2}], 'modified': [{'path': '.c.d', 'old': 3, 'new': 5}]}
47+
48+
Hash Tree From COMPAS Data
49+
==========================
50+
51+
A COMPAS hash tree can also be created from any classes that inherit from the base `Data` class in COMPAS, such as `Mesh`, `Graph`, `Shape`, `Geometry`, etc.
52+
This is done by hashing the serilised data of the object.
53+
54+
>>> from compas.datastructures import Mesh
55+
>>> mesh = Mesh.from_polyhedron(6)
56+
>>> tree = HashTree.from_object(mesh)
57+
>>> print(tree)
58+
<Tree with 58 nodes>
59+
└── ROOT @ 44cc1
60+
├── .attributes @ 3370c
61+
├── .default_vertex_attributes @ 84700
62+
│ ├── .x:0.0 @ 5bc2d
63+
│ ├── .y:0.0 @ 1704b
64+
│ └── .z:0.0 @ 6199e
65+
├── .default_edge_attributes @ 5e834
66+
├── .default_face_attributes @ 5a8d9
67+
├── .vertex @ ff6d0
68+
│ ├── .0 @ 84ec1
69+
│ │ ├── .x:-1.1547005383792517 @ 874f4
70+
│ │ ├── .y:-1.1547005383792517 @ d2b16
71+
│ │ └── .z:-1.1547005383792517 @ bd9f0
72+
│ ├── .1 @ 316d3
73+
...
74+
75+
>>> mesh.vertex_attribute(0, "x", 1.0)
76+
>>> mesh.delete_face(3)
77+
>>> new_tree = HashTree.from_object(mesh)
78+
>>> new_tree.diff(tree)
79+
{'added': [], 'removed': [{'path': '.face.3', 'value': [4, 2, 3, 5]}, {'path': '.facedata.3', 'value': None}], 'modified': [{'path': '.vertex.0.x', 'old': -1.1547005383792517, 'new': 1.0}]}
80+

docs/userguide/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ User Guide
3232
advanced.tolerance
3333
advanced.serialisation
3434
advanced.rpc
35+
advanced.hashtree
3536

3637

3738
.. toctree::

src/compas/datastructures/tree/hashtree.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def from_object(cls, obj):
170170
"""Construct a HashTree from a COMPAS data object."""
171171
if not isinstance(obj, Data):
172172
raise TypeError("The object must be a COMPAS data object.")
173-
return cls.from_dict(obj.data)
173+
return cls.from_dict(obj.__data__)
174174

175175
def node_signature(self, node, parent_path=""):
176176
"""Compute the SHA256 signature of a node. The computed nodes are cached in `self.signatures` dictionary.

tests/compas/datastructures/test_hashtree.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ def test_hashtree_from_dict():
1515

1616
def test_hashtree_from_mesh():
1717
mesh = Mesh.from_polyhedron(4)
18-
tree1 = HashTree.from_dict(mesh.__data__)
18+
tree1 = HashTree.from_object(mesh)
1919
mesh.vertex_attribute(0, "x", 1.0)
2020
mesh.delete_face(3)
21-
tree2 = HashTree.from_dict(mesh.__data__)
21+
tree2 = HashTree.from_object(mesh)
2222
diff = tree2.diff(tree1)
2323

2424
assert diff["added"] == []

0 commit comments

Comments
 (0)