Metadata-Version: 2.4
Name: arrangebpy
Version: 0.1.0
Summary: Automatic layout of Blender node trees
Author-email: Brady Johnston <brady.johnston@me.com>
License: GPL-3.0-or-later
Project-URL: Homepage, https://bradajohnston.github.io/arrangebpy
Project-URL: Repository, https://github.com/BradyAJohnston/arrangebpy
Keywords: blender,nodes,layout,graph
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Requires-Python: ~=3.11.0
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: networkx>=3.4.2
Provides-Extra: bpy
Requires-Dist: bpy>=4.2.0; extra == "bpy"
Provides-Extra: test
Requires-Dist: pytest>=7.4.0; extra == "test"
Requires-Dist: pytest-cov>=4.1.0; extra == "test"
Requires-Dist: tox>=4.8.0; extra == "test"
Provides-Extra: docs
Requires-Dist: quartodoc; extra == "docs"
Dynamic: license-file

# arrangebpy

**Automatic layout of nodes for Blender node trees** - A clean, library-focused Python module implementing multiple graph layout algorithms.

Ported from the excellent [node-arrange](https://github.com/JacquesLucke/node_arrange) Blender add-on, redesigned as a standalone module for use in other add-ons and tools.

## Features

🎨 **Multiple Layout Algorithms**
- **Sugiyama**: Hierarchical layout with minimal edge crossings (default)
- **Topological**: Fast layered layout for quick results
- **Grid**: Regular grid arrangement, great for organization
- **Orthogonal**: Clean orthogonal edge routing for presentations

✨ **Complete Sugiyama Implementation**
- Hierarchical node layout with minimal edge crossings
- Frame-aware layout (respects Blender's node frames)
- Multi-input socket handling
- Reroute node optimization

🎛️ **Highly Configurable**
- **5 layout directions**: Balanced, Left-Up, Right-Down, etc.
- **3 socket alignment modes**: None, Moderate, Full
- **Node stacking**: Automatically stack collapsed math nodes
- **Iterative refinement**: Better layouts for complex frame hierarchies
- **Adjustable spacing**: Control horizontal and vertical spacing

🔧 **Clean API Design**
- Unified `layout()` function for all algorithms
- Settings-based configuration (no global state)
- Type-safe with full type hints
- Well-documented with examples
- Designed for embedding in other add-ons

## Installation

```bash
pip install arrangebpy
```

Or with uv:
```bash
uv add arrangebpy
```

## Quick Start

```python
from arrangebpy import layout, LayoutSettings

# Simple usage with defaults (uses Sugiyama)
layout(node_tree)

# Choose a specific algorithm
layout(node_tree, algorithm="topological")  # Fast layout
layout(node_tree, algorithm="grid")         # Grid layout
layout(node_tree, algorithm="orthogonal")   # Orthogonal edges

# Custom settings
from arrangebpy import LayoutSettings
settings = LayoutSettings(
    direction="BALANCED",
    socket_alignment="MODERATE",
    horizontal_spacing=60.0,
    vertical_spacing=30.0,
)
layout(node_tree, algorithm="sugiyama", settings=settings)
```

### Legacy API (Still Supported)

```python
from arrangebpy import sugiyama_layout, LayoutSettings

# Direct function calls still work
sugiyama_layout(node_tree)
sugiyama_layout(node_tree, LayoutSettings(horizontal_spacing=60.0))
```

## Layout Algorithms

### Sugiyama (Hierarchical)

Best for most node trees. Creates a hierarchical left-to-right layout with minimal edge crossings.

```python
from arrangebpy import layout, LayoutSettings

layout(ntree, algorithm="sugiyama", settings=LayoutSettings(
    direction="BALANCED",
    socket_alignment="FULL",
    stack_collapsed=True,  # Stack collapsed math nodes
    align_top_layer=True   # Align input/output at top
))
```

**Special Feature - Flat Top Layout**:
```python
# Align source and sink nodes (input/output) at Y=0, push others below
layout(ntree, algorithm="sugiyama", settings=LayoutSettings(
    align_top_layer=True  # Clean flat top with I/O aligned
))
```

**Use when**: You want the highest quality layout with minimal crossings (shader trees, geometry nodes, etc.)

### Topological (Fast Layered)

Quick and simple layered layout without crossing reduction. Much faster than Sugiyama.

```python
from arrangebpy import layout, TopologicalSettings

layout(ntree, algorithm="topological", settings=TopologicalSettings(
    horizontal_spacing=60.0,
    sort_by_degree=True  # Sort nodes by connection count
))

# For perfectly flat horizontal layouts (all nodes at Y=0)
layout(ntree, algorithm="topological", settings=TopologicalSettings(
    flatten=True  # Perfect for simple linear chains
))
```

**Use when**: You need quick layouts during development, have very large graphs, or want a perfectly flat horizontal layout.

### Grid

Arranges nodes in a regular grid pattern, optionally grouping by type or cluster.

```python
from arrangebpy import layout, GridSettings

layout(ntree, algorithm="grid", settings=GridSettings(
    columns=5,
    grouping="TYPE",  # Group by node type
    cell_width=250.0
))
```

**Use when**: You want to organize collections of nodes or create inventory-style layouts.

### Orthogonal (Clean Edges)

Uses Sugiyama for node placement but routes edges with only horizontal/vertical segments.

```python
from arrangebpy import layout, OrthogonalSettings

layout(ntree, algorithm="orthogonal", settings=OrthogonalSettings(
    horizontal_spacing=80.0,  # More space for routing
    socket_alignment="FULL"
))
```

**Use when**: You need professional-looking layouts for presentations or documentation.

## Usage Examples

### Default Layout (Sugiyama)

```python
from arrangebpy import layout

# Uses sensible defaults
layout(material.node_tree)
```

### Shader Node Trees (with stacking)

```python
from arrangebpy import LayoutSettings
from arrangebpy.arrange.sugiyama import sugiyama_layout

settings = LayoutSettings(
    # Stack collapsed math nodes
    stack_collapsed=True,
    stack_margin_y_factor=0.4,
    
    # Full socket alignment for cleaner connections
    socket_alignment="FULL",
    
    # Balanced direction
    direction="BALANCED",
    
    # Tight spacing
    horizontal_spacing=50.0,
    vertical_spacing=20.0,
)

sugiyama_layout(shader_node_tree, settings)
```

### Geometry Node Trees

```python
settings = LayoutSettings(
    # More spacing for larger nodes
    horizontal_spacing=70.0,
    vertical_spacing=30.0,
    
    # Left-to-right flow
    direction="RIGHT_DOWN",
    
    # Moderate socket alignment
    socket_alignment="MODERATE",
    
    # High quality layout
    iterations=20,
    crossing_reduction_sweeps=48,
)

sugiyama_layout(geometry_node_tree, settings)
```

## Configuration

All configuration through the `LayoutSettings` dataclass:

```python
from arrangebpy import LayoutSettings

settings = LayoutSettings(
    # Spacing
    horizontal_spacing=50.0,      # Horizontal spacing between columns
    vertical_spacing=25.0,        # Vertical spacing between nodes
    
    # Layout algorithm
    direction="BALANCED",         # Layout direction
    socket_alignment="MODERATE",  # Socket alignment mode
    iterations=20,                # BK algorithm refinement iterations
    crossing_reduction_sweeps=24, # Edge crossing minimization passes
    
    # Features
    add_reroutes=True,           # Add reroute nodes for clean routing
    stack_collapsed=False,       # Stack collapsed math nodes
    stack_margin_y_factor=0.5,   # Spacing factor for stacks (0-1)
)
```

### Direction Options

- `"BALANCED"` - Combines all 4 directions (default, best results)
- `"LEFT_DOWN"` - Top-left alignment  
- `"RIGHT_DOWN"` - Bottom-right alignment
- `"LEFT_UP"` - Bottom-left alignment
- `"RIGHT_UP"` - Top-right alignment

### Socket Alignment Options

- `"NONE"` - Only align node tops (fastest)
- `"MODERATE"` - Smart alignment based on node heights (default)
- `"FULL"` - Always align sockets (cleanest connections)

## Use in Blender Add-ons

```python
import bpy
from arrangebpy import LayoutSettings
from arrangebpy.arrange.sugiyama import sugiyama_layout

class MY_OT_ArrangeNodes(bpy.types.Operator):
    bl_idname = "node.my_arrange"
    bl_label = "Arrange Nodes"
    
    def execute(self, context):
        ntree = context.space_data.edit_tree
        
        # Configure based on node tree type
        if ntree.bl_idname == 'ShaderNodeTree':
            settings = LayoutSettings(
                stack_collapsed=True,
                socket_alignment="FULL",
            )
        else:
            settings = LayoutSettings()
        
        sugiyama_layout(ntree, settings)
        return {'FINISHED'}
```

## How It Works

Implements the Sugiyama hierarchical graph layout framework:

1. **Graph Construction** - Build directed graph from node tree
2. **Ranking** - Assign nodes to horizontal layers
3. **Crossing Minimization** - Reduce edge crossings via median heuristic  
4. **Coordinate Assignment** - Place nodes using Brandes-Köpf algorithm
5. **Edge Routing** - Add bend points for clean connections
6. **Realization** - Apply positions back to Blender

## Architecture

```
arrangebpy/
├── settings.py              # Configuration
├── arrange/
│   ├── sugiyama.py         # Main orchestration
│   ├── graph.py            # Graph construction
│   ├── ranking.py          # Rank assignment
│   ├── ordering.py         # Crossing minimization
│   ├── placement/
│   │   ├── bk.py           # Brandes-Köpf algorithm
│   │   └── linear_segments.py
│   ├── coordinates.py      # X-coordinate assignment  
│   ├── routing.py          # Edge routing
│   ├── stacking.py         # Node stacking
│   └── ...
└── utils.py
```

## Development

```bash
git clone https://github.com/BradyAJohnston/arrangebpy.git
cd arrangebpy

# Install
uv sync

# Run tests
uv run pytest tests/
```

## License

GPL-3.0-or-later

## Credits

Based on [node-arrange](https://github.com/Leonardo-Pike-Excell/node-arrange) by Leonardo Pike-Excell

Implements algorithms from Sugiyama et al., Brandes & Köpf, and others.
