The _node module
This is about the mechanism used for the vertices, edges and faces of
halfedge/Surface objects.
Calling NodeClass will create a class tied to another object s (stored in
surf of nodes), typically a
Surface object. The class name
will be the given name concatenated with id(obj). Attributes that the class
instances should have can be passed as a tuple of strings. The default value is
always None for these.
The created class has a length and is iterable:
>>> import ddg.datastructures.halfedge._node as _node
>>> s = object()
>>> nodelist = _node.NodeClass(s, "Blub", ("one_attribute", "another_attribute"))
>>> nodelist
<class 'ddg.datastructures.halfedge._node.Blub...'>
>>> len(nodelist)
0
>>> list(nodelist)
[]
We can add nodes by simply calling the class. This will return an instance of the class, as well as add it to the list.
>>> node = nodelist()
>>> node
<ddg.datastructures.halfedge._node.Blub... object at 0x...>
>>> node.surf is s
True
>>> len(nodelist)
1
>>> list(nodelist)
[<ddg.datastructures.halfedge._node.Blub... object at 0x...>]
>>> print(node.one_attribute)
None
>>> print(node.another_attribute)
None
The implementation is basically that of a circular doubly linked list: All
nodes have attributes _pred and _succ that point to another node or a
sentinel node, implemented as a SimpleNamespace object, that marks the
start and end of the list. The sentinel node is stored in
nodelist._instance_list.
>>> node._pred
namespace(_succ=<ddg.datastructures.halfedge._node.Blub... object at 0x...>, _pred=<ddg.datastructures.halfedge._node.Blub... object at 0x...>)
>>> node._succ
namespace(_succ=<ddg.datastructures.halfedge._node.Blub... object at 0x...>, _pred=<ddg.datastructures.halfedge._node.Blub... object at 0x...>)
>>> node._pred is node._succ
True
Nodes also have indices:
>>> node2 = nodelist()
>>> node.index
0
>>> node2.index
1
Nodes can be removed from the list. This changes indices. Removing a node
unlinks it from the doubly linked list, sets its surf to None and removes its
other attributes.
>>> nodelist.remove(node)
>>> node2.index
0
>>> node.one_attribute
Traceback (most recent call last):
...
AttributeError: 'Blub...' object has no attribute 'one_attribute'...
>>> print(node.surf)
None
>>> len(nodelist)
1
>>> list(nodelist)
[<ddg.datastructures.halfedge._node.Blub... object at 0x...>]
As an implementation side note, the len is just incremented and decremented
manually when adding and removing nodes.
Attributes can also be added to all nodes simultaneously, with a default value.
This is implemented using a descriptor class NodeAttribute (meaning it
implements __get__ and __set__) that has a dictionary _values with
the nodes as keys. The descriptor na is stored in a class attribute
attribute_name. When accessing node.attribute_name, it actually returns
na._values[node]. The descriptor itself can also be used like a dictionary,
i.e. na[node] also returns the same value.
>>> na = nodelist.add_attribute("new_attribute", 0)
>>> na
<ddg.datastructures.halfedge._node.NodeAttribute object at 0x...>
>>> nodelist.new_attribute is na
True
>>> node2.new_attribute
0
>>> node2.new_attribute = 2
>>> node2.new_attribute
2
>>> na._values[node2]
2
>>> na[node2]
2
The final component of the module is the NodeIter class. This is just a
simple iterator that starts at _instance_list and traverses the list using
_succ until it hits _instance_list again.