Skip to content

Commit 7a52377

Browse files
committed
Improve and document 'capture'
1 parent b1f1edb commit 7a52377

File tree

4 files changed

+71
-3
lines changed

4 files changed

+71
-3
lines changed

api/node_mapper.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ def __ge__(self, other) -> Type: return self
326326
x = Type()
327327
y = Type()
328328
z = Type()
329+
def capture(self, attribute: Type, **kwargs) -> Callable[[], Type]: return transfer_attribute
329330
{(newline + ' ').join(map(lambda x: x.replace('(', '(self, '), filter(lambda x: x.startswith('def'), symbols)))}
330331
331332
{newline.join(map(type_symbol, Type.__subclasses__()))}

api/types.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,10 @@ def y(self):
134134
def z(self):
135135
return self._get_xyz_component(2)
136136

137-
def capture(self, value):
137+
def capture(self, value, **kwargs):
138138
data_type = socket_type_to_data_type(value._socket.type)
139-
captured = self.capture_attribute(data_type=data_type, value=value)
140-
return captured.geometry.transfer_attribute(data_type=data_type, attribute=captured.attribute)
139+
captured = self.capture_attribute(data_type=data_type, value=value, **kwargs)
140+
return lambda **kwargs: captured.geometry.transfer_attribute(data_type=data_type, attribute=captured.attribute, **kwargs)
141141

142142
for standard_socket in list(filter(lambda x: 'NodeSocket' in x, dir(bpy.types))):
143143
name = standard_socket.replace('NodeSocket', '')

book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- [Node Groups](./api/advanced-scripting/node-groups.md)
2020
- [Generators](./api/advanced-scripting/generators.md)
2121
- [Input Groups](./api/advanced-scripting/input-groups.md)
22+
- [Attributes](./api/advanced-scripting/attributes.md)
2223

2324
# Tutorials
2425

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Attributes
2+
3+
An important concept in Geometry Nodes is attributes. Many trees transfer attributes between geometry, using a combination of *Capture Attribute* and *Transfer Attribute*.
4+
5+
Unfortunately, it takes quite a bit of code to use this common pattern.
6+
7+
```python
8+
@tree("Skin")
9+
def skin():
10+
# Create a cube
11+
c = cube()
12+
# Capture the position
13+
cube_position_attribute = c.capture_attribute(
14+
data_type=CaptureAttribute.DataType.FLOAT_VECTOR,
15+
value=position()
16+
)
17+
# Create a sphere
18+
sphere = uv_sphere()
19+
# Transfer the position to the sphere
20+
transferred_position = cube_position_attribute.geometry.transfer_attribute(
21+
data_type=TransferAttribute.DataType.FLOAT_VECTOR,
22+
attribute=cube_position_attribute.attribute
23+
)
24+
# Make the sphere conform to the shape of the cube
25+
return sphere.set_position(position=transferred_position)
26+
```
27+
28+
Thankfully, a convenient `capture(...)` method is available on `Geometry`, which simplifies this function quite a bit.
29+
30+
```python
31+
@tree("Skin")
32+
def skin():
33+
# Create a cube
34+
c = cube()
35+
# Capture the position
36+
cube_position = c.capture(position())
37+
# Create a sphere
38+
sphere = uv_sphere()
39+
# Make the sphere conform to the shape of the cube
40+
return sphere.set_position(position=cube_position())
41+
```
42+
43+
## How it Works
44+
45+
Internally, `capture(...)` works just like the more manual approach.
46+
47+
1. Capture the attribute from the source
48+
49+
In the example above, we capture the `position()` from the cube.
50+
The data type is automatically inferred from the input. If you want to customize other options, simply pass them as keyword arguments to `capture(...)`.
51+
52+
```python
53+
cube_position = c.capture(position())
54+
cube_position = c.capture(position(), domain=CaptureAttribute.Domain.FACE) # Optionally pass other arguments available on `capture_attribute`.
55+
```
56+
57+
2. Transfer the attribute to the target
58+
59+
`capture(...)` returns another function that calls `transfer_attribute` with the correct arguments passed automatically.
60+
Call this returned function (which we store in the variable `cube_position`) to transfer the attribute.
61+
In this example we also set the transferred cube position back onto the sphere.
62+
63+
```python
64+
sphere.set_position(position=cube_position())
65+
sphere.set_position(position=cube_position(mapping=TransferAttribute.Mapping.NEAREST)) # Optionally pass other arguments available on `transfer_attribute`.
66+
```

0 commit comments

Comments
 (0)