Metadata-Version: 2.4
Name: seevol
Version: 0.1.0
Summary: All-pairs visibility analysis on voxel volumes (Amanatides & Woo, C++)
Author-Email: Archie Benn <sj19031@bristol.ac.uk>
License-Expression: MIT
License-File: LICENSE
Classifier: Programming Language :: C++
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Project-URL: Repository, https://github.com/archiebenn/seeVol
Requires-Python: >=3.10
Requires-Dist: numpy
Requires-Dist: pyvista
Description-Content-Type: text/markdown

<p align="center">
  <img src="images/readme_banner.png" alt="3D visualisation of an artificial ant nest volume coloured by voxel visibility" width="800">
</p>

# seeVol

![Build wheels](https://github.com/archiebenn/seeVol/actions/workflows/wheels.yml/badge.svg)

`seevol` analyses line-of-sight visibility in 3D binary volumes, where 0 represents a solid/opaque voxel, and 1 represents open/transparent voxels. 

The core functionality is based on the Amanatides & Woo voxel traversal algorithm <sup>1</sup> and is implemented in C++ to compute straight line visibility between all pairs of open (non solid) voxels in a given volume.  

This tool tests visibility between **all** open pairs throughout the volume, which permits per-voxel visibility analysis metrics to be calculated downstream for every open voxel.

> [!WARNING]
> `seevol` computes all-pairs visibility, which scales as O(open_voxels<sup>2</sup>). This becomes slow or memory-limiting beyond a few hundred thousand open voxels — see [Limitations](#limitations) before running on large volumes. Downsampling the volume is likely to help but will impact results.  

## Dependencies

A C++17 or greater build toolchain is required for source installation, alongside Python >=3.10.  

Pyvista is a dependency for visualisations of the visibility metrics output onto the 3D volume itself (see [this visual example](examples/visualiser_example.py) for use).

## Installation

To install seevol:

```bash
pip install seevol
```

or for uv users:

```bash
uv pip install seevol
```

## Usage

The core function `visible_pairs()` accepts a 3D binary (0 = solid, 1 = open) NumPy array as input and outputs two NumPy arrays:

- `coords` — shape (N, 3), the open voxel coordinates in the volume  
- `edges` — shape (M, 2), pairs of indices into `coords` for voxels that are mutually visible  

For example, if `edges[0]` is `[2, 7]`,the visibility edge connects `coords[2]` and `coords[7]` and these two voxels are visible to each other via direct line of sight.  

Users can load .npy files in Python using `np.load()` and pass the result to `visible_pairs()`.  

Downstream analyses or visualisations are then possible from this output, such as visualising a 'visibility heatmap' using Pyvista (see below) 

## Limitations  

This tool currently uses all-pair calculation using voxel ray traversal. Because of this, the ray traversal algorithm scales as O(open_voxels<sup>2</sup>) and is therefore quickly unusable for large volumes.  

A hard limit is imposed in the C++ source file which limits open voxels to 1,000,000. In reality, volumes with over 500,000 open voxels are likely to significant memory issues as very large visible edges will be stored. Methods to reduce this runtime will hopefully be introduced over time.

## Minimal example

The [quickstart example](examples/quickstart.py) creates a small volume of open voxels with one solid voxel blocking visibility:

```python
import numpy as np
import seevol 

# 1 = open, 0 = solid
volume = np.ones((3, 3, 3), dtype=np.uint8)

# adds an obstruction
volume[1, 1, 1] = 0  

# calculate pairwise voxel visibility
coords, edges = seevol.visible_pairs(volume)

print(f"{len(coords)} open voxels containing {len(edges)} visible pairs")
```

It prints the number of open voxels and visible pairs:

```
26 open voxels containing 214 visible pairs
```

### Using .npy files 

> [!IMPORTANT]
> NumPy array volumes as .npy files must be 3D and contain binary values, with 0 = solid and 1 = open voxels.

To import your own 3D NumPy volume and calculate visibility as with the above example:

```python
import numpy as np
import seevol 

# load your own 3D NumPy volume: 0 = solid, 1 = open
volume = np.load("my_volume.npy")

# calculate pairwise voxel visibility
coords, edges = seevol.visible_pairs(volume)

print(f"{len(coords)} open voxels containing {len(edges)} visible pairs")
```

## Visualisation examples on large artificial volumes

Below shows an example output of the visibility score per voxel as a heatmap within an intricate 150x150x150 volume, generated using `tunnel_excavator()` to mimic an ant-nest structure and visualised with Pyvista using `visibility_visualiser()`:  

![150_cubed](images/realistic_nest.png "150^3 ant excavation example volume")

A visibility scored volume using a generated `porespy` volume:

![150_cubed](images/porespy.png "150^3 ant excavation example volume")


## References

> 1. Amanatides, J., & Woo, A. (1987, August). A fast voxel traversal algorithm for ray tracing. In Eurographics (Vol. 87, No. 3, pp. 3-10).


