diff --git a/api/static/attribute.py b/api/static/attribute.py new file mode 100644 index 0000000..8038a5e --- /dev/null +++ b/api/static/attribute.py @@ -0,0 +1,70 @@ +class Attribute: + """ + A class that represents named attributes, providing methods for accessing and storing them. + + Create an attribute with a name, data type, and optional domain. + ```python + height_level = Attribute( + "height_level", + NamedAttribute.DataType.FLOAT, + StoreNamedAttribute.Domain.POINT # optional + ) + ``` + + Access the attribute value by calling the class instance. + ```python + height_level() + ``` + + Store a value for the named attribute on some geometry with `store(...)`. + ```python + height_level.store(geometry, value) + ``` + + Check if the attribute exists on some geometry with `exists()`. + ```python + selection = height_level.exists() + ``` + """ + name: str + data_type: 'NamedAttribute.DataType' + domain: 'StoreNamedAttribute.Domain' + + def __init__( + self, + name: str, + data_type: 'NamedAttribute.DataType', + domain: 'StoreNamedAttribute.Domain' = 'POINT' + ): + self.name = name + self.data_type = data_type + self.domain = domain + + def __call__(self, *args, **kwargs): + """ + Creates a "Named Attribute" node with the correct arguments passed, and returns the "Attribute" socket. + """ + from geometry_script import named_attribute + return named_attribute(data_type=self.data_type, name=self.name, *args, **kwargs).attribute + + def exists(self, *args, **kwargs): + """ + Creates a "Named Attribute" node with the correct arguments passed, and returns the "Exists" socket. + """ + from geometry_script import named_attribute + return named_attribute(data_type=self.data_type, name=self.name, *args, **kwargs).exists + + def store(self, geometry: 'Geometry', value, *args, **kwargs) -> 'Geometry': + """ + Creates a "Store Named Attribute" node with the correct arguments passed, and returns the modified `Geometry`. + """ + from geometry_script import store_named_attribute + return store_named_attribute( + data_type=self.data_type, + domain=self.domain, + geometry=geometry, + name=self.name, + value=value, + *args, + **kwargs + ) \ No newline at end of file diff --git a/api/tree.py b/api/tree.py index d97287d..be1ab38 100644 --- a/api/tree.py +++ b/api/tree.py @@ -7,8 +7,9 @@ except: from .state import State from .types import * from .node_mapper import * -from .static.input_group import * +from .static.attribute import * from .static.expression import * +from .static.input_group import * def _as_iterable(x): try: diff --git a/api/types.py b/api/types.py index 51570b7..54d93b4 100644 --- a/api/types.py +++ b/api/types.py @@ -9,6 +9,8 @@ def map_case_name(i): def socket_type_to_data_type(socket_type): match socket_type: + case 'VALUE': + return 'FLOAT' case 'VECTOR': return 'FLOAT_VECTOR' case 'COLOR': diff --git a/book/src/api/advanced-scripting/attributes.md b/book/src/api/advanced-scripting/attributes.md index c92b74f..196c95a 100644 --- a/book/src/api/advanced-scripting/attributes.md +++ b/book/src/api/advanced-scripting/attributes.md @@ -45,4 +45,24 @@ Any additional keyword arguments can be passed as normal. ```python c.transfer(position(), mapping=TransferAttribute.Mapping.INDEX) +``` + +## Named Attributes + +Custom attributes can be created by name. +The safest way to use named attributes is with the `Attribute` class. + +Create a named attribute with a data type and optional domain, then use the `store(...)`, `exists()`, and `__call__(...)` methods to use it. + +```python +# Create the attribute +my_custom_attribute = Attribute( + "my_custom_attribute", + NamedAttribute.DataType.FLOAT, # declare the data type once + StoreNamedAttribute.Domain.INSTANCE # optional +) +# Store a value +geometry = my_custom_attribute.store(geometry, 0.5) +# Use the value by calling the attribute +geometry = geometry.set_position(offset=my_custom_attribute()) ``` \ No newline at end of file