Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@
- [Attributes](./api/advanced-scripting/attributes.md)
- [Boolean Math](./api/advanced-scripting/boolean-math.md)
- [Item Nodes](./api/advanced-scripting/item-nodes.md)
- [Repeat Zones](./api/advanced-scripting/repeat-zones.md)
- [Simulation Zones](./api/advanced-scripting/simulation-zones.md)
- [For Each Element Zones](./api/advanced-scripting/for-each-zones.md)
- [Closure Zones](./api/advanced-scripting/closure-zones.md)
- [Zones](./api/advanced-scripting/zones.md)
- [Repeat Zones](./api/advanced-scripting/repeat-zones.md)
- [Simulation Zones](./api/advanced-scripting/simulation-zones.md)
- [For Each Element Zones](./api/advanced-scripting/for-each-zones.md)
- [Closure Zones](./api/advanced-scripting/closure-zones.md)
- [Shaders and Compositing](./api/advanced-scripting/shaders-and-compositing.md)

# Tutorials
Expand Down
59 changes: 59 additions & 0 deletions book/src/api/advanced-scripting/zones.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Zones

A zone is a region of a node tree between a pair of input and output nodes, such as *Repeat Input* and *Repeat Output*. The nodes inside the zone are evaluated differently from the rest of the tree: repeated, run once per frame, run once per element, or packaged up to be evaluated later.

Geometry Script has a decorator for each kind of zone:

| Zone | Decorator | Runs its nodes | Works in |
|---|---|---|---|
| [Repeat](./repeat-zones.md) | `@repeat_zone` | A set number of times, feeding each result into the next iteration | Geometry Nodes, shaders |
| [Simulation](./simulation-zones.md) | `@simulation_zone` | Once per frame, starting from the previous frame's result | Geometry Nodes |
| [For Each Element](./for-each-zones.md) | `@for_each_element(domain=...)` | Once for each point, face, or other element of a geometry | Geometry Nodes |
| [Closure](./closure-zones.md) | `@closure_zone` | Whenever the closure is evaluated | Geometry Nodes, shaders |

## Writing a Zone

The decorated function is the body of the zone. Its parameters become the zone's items, and the values it returns are connected to the zone's output node:

```python
@repeat_zone
def grow(geometry: Geometry, size: float):
bigger = transform_geometry(geometry=geometry, scale=(1.1, 1.1, 1.1)).geometry
return bigger, size * 1.1
```

* Each parameter's annotation sets the item's socket type, just like the inputs of a [tree function](../basics/tree-functions.md#group-input). Without an annotation, the type is inferred from the value passed in.
* Return one value per item, in the same order as the parameters. You can also return a dictionary keyed by parameter name.
* Some parameter names are special, and receive a value from the zone instead of being an item: `iteration` in a repeat zone, `delta_time` in a simulation zone, and `index` and `element` in a for-each zone.

## Using a Zone

Call the decorated function inside a tree function to add the zone to the tree. Pass the initial value of each item, positionally or by name, along with any settings of the zone itself, such as `iterations=` or `skip=`:

```python
@tree("Growing Cube")
def growing_cube(steps: int = 4):
result = grow(cube().mesh, 1.0, iterations=steps)
return result.geometry, result.size
```

A zone with a single item returns that socket directly. A zone with several items returns a named tuple with a field per item. For-each and closure zones return their own kinds of results, described on their pages.

Each call adds a new zone, so the same decorated function can be used several times, in one tree or in many. Zones can only be called while a tree is being built.

A zone can also be defined inside the tree function that uses it, which lets its body use the tree's inputs directly:

```python
@tree("Spiral")
def spiral(count: int = 12, turn: float = 0.5):
@repeat_zone
def step(geometry: Geometry, iteration: int):
point = points(position=(math_(operation="COSINE", value=iteration * turn).value,
math_(operation="SINE", value=iteration * turn).value,
iteration * 0.1)).points
return join_geometry(geometry=[geometry, point]).geometry

return step(points(count=0).points, iterations=count)
```

The next pages cover each kind of zone in detail.
Binary file removed book/src/api/basics/cube_tree_size_compare.png
Binary file not shown.
2 changes: 2 additions & 0 deletions book/src/api/basics/tree-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def cube_tree():

Running the script again rebuilds the tree in place. Modifiers using the tree keep their input values, and if the script raises an error, the previous version of the tree is left untouched.

The nodes are laid out for you each time the tree is built, in columns that flow from the *Group Input* on the left to the *Group Output* on the right, with each zone's nodes kept together. Shader and compositor groups, materials and worlds are arranged the same way.

## Group Output
The values you return become the outputs of the tree. Let's try returning a simple cube.

Expand Down
Binary file removed book/src/images/ide_docs.png
Binary file not shown.
Binary file removed book/src/images/vscode_code_completion.png
Binary file not shown.
4 changes: 2 additions & 2 deletions book/src/introduction.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Introduction

**Geometry Script** is a scripting API for Blender's Geometry Nodes.
It makes complicated node trees more managable and easy to share.
It makes complicated node trees more manageable and easy to share.

* [Full coverage of nodes](./api/basics/using-nodes.md) available in your Blender version
* Clean, easy to use [Python API](./api/basics.md)
* Typed node functions generated from your Blender version, with [errors that say what to change](./api/basics/using-nodes.md)
* External [IDE integration](./setup/external-editing.md) for better completions and hot reload
* [Repeat, simulation, for-each and closure zones](./api/advanced-scripting/repeat-zones.md)
* [Repeat, simulation, for-each and closure zones](./api/advanced-scripting/zones.md)

Here's a simple example of what's possible with a short script:

Expand Down
4 changes: 2 additions & 2 deletions book/src/setup/agent-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ agent.run("/path/to/Repeat Grid.py", object="Cube")
```

```
built 'Repeat Grid': 14 nodes; in (Geometry: Geometry, Columns: Int = 3, Rows: Int = 3, Spacing X: Float = 0, Spacing Y: Float = 0) -> out (Result: Geometry)
object 'Cube': instances 9; instance positions (-2, -2, 0)..(2, 2, 0)
built 'Repeat Grid': 14 nodes; in (Geometry: Geometry, Columns: Int = 3, Rows: Int = 3, Spacing X: Float = 0.5, Spacing Y: Float = 0.5) -> out (Result: Geometry)
object 'Cube': instances 9; instance positions (-2.5, -2.5, 0)..(2.5, 2.5, 0)
```

Shader groups, materials, worlds and compositor groups are reported too:
Expand Down
18 changes: 15 additions & 3 deletions book/src/setup/external-editing.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,21 @@ Every node function in Geometry Script is generated with full type hints: its ke

![A screenshot of the Python > Analysis: Extra Paths setting with the path pasted in](../images/vscode_extra_paths.png)

5. Create a new Python file, such as `Repeat Grid.py` and start writing a script. As you type, you should get helpful suggestions for every available node.

![A screenshot of a script with the documentation for `instance_on_points` appearing as the user types.](../images/vscode_code_completion.png)
5. Create a new Python file, such as `Repeat Grid.py` and start writing a script. As you type, you should get helpful suggestions for every available node. Hovering a node function shows its signature and description, for example:

```python
def instance_on_points(
*,
points: Geometry | Socket[Geometry] | None = None,
selection: bool | Field[bool] | Socket[bool | Field[bool]] = True,
instance: Geometry | Socket[Geometry] | None = None,
pick_instance: bool | Field[bool] | Socket[bool | Field[bool]] = False,
instance_index: int | Field[int] | Socket[int | Field[int]] = 0,
rotation: Rotation | Field[Rotation] | Socket[Rotation | Field[Rotation]] = (0.0, 0.0, 0.0),
scale: Vector | Field[Vector] | Socket[Vector | Field[Vector]] = (1.0, 1.0, 1.0),
) -> GeometryNodeInstanceOnPointsOutputs:
'Generate a reference to geometry at each of the input points, without duplicating its underlying data'
```

> For completions of `bpy` itself, install [`fake-bpy-module`](https://github.com/nutti/fake-bpy-module) into the Python environment your editor uses.

Expand Down
5 changes: 0 additions & 5 deletions book/src/setup/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ Blender asks to allow online access if it's off, then offers to add the Geometry
2. Open *Blender* > *Preferences* > *Get Extensions*
3. Open the dropdown at the top right, choose *Install from Disk...* and select the downloaded ZIP file

## From Blender Market
1. After [purchasing the add-on](https://www.blendermarket.com/), download the ZIP file
2. Open *Blender* > *Preferences* > *Get Extensions*
3. Open the dropdown at the top right, choose *Install from Disk...* and select the downloaded ZIP file

## Building it yourself
To build the extension for a Blender version that has no published ZIP, clone the repository and run:

Expand Down
4 changes: 2 additions & 2 deletions examples/Repeat Grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ def repeat_grid(
geometry: Geometry,
columns: int = 3,
rows: int = 3,
spacing_x: float = 0.0,
spacing_y: float = 0.0,
spacing_x: float = 0.5,
spacing_y: float = 0.5,
):
# Measure the geometry's bounds
bounds = bounding_box(geometry=geometry)
Expand Down
Loading