diff --git a/api/types.py b/api/types.py index 667d1f1..e477816 100644 --- a/api/types.py +++ b/api/types.py @@ -169,8 +169,11 @@ class Type(metaclass=_TypeMeta): def capture(self, value, **kwargs): data_type = socket_type_to_data_type(value._socket.type) - captured = self.capture_attribute(data_type=data_type, value=value, **kwargs) - return lambda **kwargs: captured.geometry.transfer_attribute(data_type=data_type, attribute=captured.attribute, **kwargs) + geometry, attribute = self.capture_attribute(data_type=data_type, value=value, **kwargs) + return geometry, attribute + def transfer(self, attribute, **kwargs): + data_type = socket_type_to_data_type(attribute._socket.type) + return self.transfer_attribute(data_type=data_type, attribute=attribute, **kwargs) for standard_socket in list(filter(lambda x: 'NodeSocket' in x, dir(bpy.types))): name = standard_socket.replace('NodeSocket', '') diff --git a/book/src/api/advanced-scripting/attributes.md b/book/src/api/advanced-scripting/attributes.md index b90900a..c92b74f 100644 --- a/book/src/api/advanced-scripting/attributes.md +++ b/book/src/api/advanced-scripting/attributes.md @@ -1,66 +1,48 @@ # Attributes -An important concept in Geometry Nodes is attributes. Many trees transfer attributes between geometry, using a combination of *Capture Attribute* and *Transfer Attribute*. +An important concept in Geometry Nodes is attributes. Many trees capture attributes or transfer them from one geometry to another. -Unfortunately, it takes quite a bit of code to use this common pattern. +When using these methods, the `data_type` argument must be correctly specified for the transfer to work as intended. ```python @tree("Skin") def skin(): # Create a cube c = cube() - # Capture the position - cube_position_attribute = c.capture_attribute( - data_type=CaptureAttribute.DataType.FLOAT_VECTOR, - value=position() - ) # Create a sphere sphere = uv_sphere() # Transfer the position to the sphere - transferred_position = cube_position_attribute.geometry.transfer_attribute( + transferred_position = c.transfer_attribute( data_type=TransferAttribute.DataType.FLOAT_VECTOR, - attribute=cube_position_attribute.attribute + attribute=position() ) # Make the sphere conform to the shape of the cube return sphere.set_position(position=transferred_position) ``` -Thankfully, a convenient `capture(...)` method is available on `Geometry`, which simplifies this function quite a bit. +To improve the usability of these nodes, `capture(...)` and `transfer(...)` methods are provided on `Geometry` that simply take the attribute and any other optional arguments. ```python @tree("Skin") def skin(): # Create a cube c = cube() - # Capture the position - cube_position = c.capture(position()) # Create a sphere sphere = uv_sphere() # Make the sphere conform to the shape of the cube - return sphere.set_position(position=cube_position()) + return sphere.set_position(position=c.transfer(position())) ``` -## How it Works - -Internally, `capture(...)` works just like the more manual approach. - -1. Capture the attribute from the source - -In the example above, we capture the `position()` from the cube. -The data type is automatically inferred from the input. If you want to customize other options, simply pass them as keyword arguments to `capture(...)`. +The same is available for `capture(...)`. ```python -cube_position = c.capture(position()) -cube_position = c.capture(position(), domain=CaptureAttribute.Domain.FACE) # Optionally pass other arguments available on `capture_attribute`. +geometry_with_attribute, attribute = c.capture(position()) ``` -2. Transfer the attribute to the target +> You must use the `Geometry` returned from `capture(...)` for the anonymous attribute it creates to be usable. -`capture(...)` returns another function that calls `transfer_attribute` with the correct arguments passed automatically. -Call this returned function (which we store in the variable `cube_position`) to transfer the attribute. -In this example we also set the transferred cube position back onto the sphere. +Any additional keyword arguments can be passed as normal. ```python -sphere.set_position(position=cube_position()) -sphere.set_position(position=cube_position(mapping=TransferAttribute.Mapping.NEAREST)) # Optionally pass other arguments available on `transfer_attribute`. +c.transfer(position(), mapping=TransferAttribute.Mapping.INDEX) ``` \ No newline at end of file