pysovist
Exact isovists and view volumes from floor plans, meshes and raw point clouds.
An isovist is the region visible from one point. pysovist computes it in two settings:
- 2D, from wall segments. The visible polygon is computed exactly, with no ray count. Area, perimeter, occlusivity, radial statistics, drift and elongation follow in closed form, and so does convex deficiency without a range limit.
- 3D, from a mesh or a point cloud. The view volume is the volume visible from an eye point. Point clouds are used as scanned: every point is a small ball, so furniture, equipment and people occlude as they did on the day of the scan.
Install
pip install pysovist # 2D isovists
pip install "pysovist[pointcloud]" # point clouds (numba, laspy)
pip install "pysovist[mesh]" # meshes (open3d)
pip install "pysovist[ifc]" # BIM models in IFC files (ifcopenshell)
pip install "pysovist[all]" # everything, including e57 files
Python 3.10 or newer. On a Linux machine without a desktop, open3d also needs the system libraries libegl1, libgl1 and libgomp1.
Quick start
import numpy as np
import pysovist
# A 10 m x 10 m room with a 2 m x 2 m pillar, as wall segments [[x0, y0], [x1, y1]].
def rect(x0, y0, x1, y1):
v = np.array([(x0, y0), (x1, y0), (x1, y1), (x0, y1)], float)
return np.stack([v, np.roll(v, -1, axis=0)], axis=1)
plan = pysovist.Plan(np.concatenate([rect(-5, -5, 5, 5), rect(-1, -1, 1, 1)]))
iso = pysovist.isovist(plan, (3.0, 0.0))
iso.area # 70.0, exact
iso.metrics["occlusivity"] # 13.416..., the two edges of the pillar's shadow
# Many observers at once, one row each.
field = pysovist.isovist_field(plan, plan.grid(0.5), max_distance=40.0, n_jobs=-1)
View volumes from a point cloud:
cloud = pysovist.PointCloud.read("scan.las", radius=0.05) # every point is a ball of radius 5 cm
vv = pysovist.view_volume(cloud, (3.0, 0.0, 1.7), n_rays=262_144)
vv.volume, vv.metrics["escape_fraction"]
Plans and meshes from a BIM model, read from an IFC file in any length unit:
plan = pysovist.Plan.from_ifc("model.ifc", 1.2, classes="furnished", storey="Level 1")
mesh = pysovist.Mesh.from_ifc("model.ifc", classes=("walls", "IfcSlab", "IfcCovering"))
Plan.from_ifc cuts the walls and, with "furnished", the furniture of one storey at 1.2 m above its floor.
Units are metres and radians. z points up.
What makes it exact
Seen from the observer, the angles of all wall endpoints, wall crossings and, under a range limit, crossings of walls with the range circle cut the full turn into wedges. Within one wedge the nearest wall never changes, so each wedge is a triangle or, under a range limit, a circular sector. Every metric is built from closed forms over these pieces.
Ray-sampling tools describe the same isovist by the polygon through N hit points. pysovist.sampled_metrics reproduces that description, so its error can be measured against the exact value. The area error falls as N^-2 in convex rooms and as N^-1 where the boundary has occluding edges.
Verification
- 2D: closed-form scenes (square, hallway, regular polygons, L-shaped room, room with a pillar, open room with a range limit, fields of view) agree to 1e-9 (relative, or absolute for values below 1). Random rooms agree with the visilibity library and with a polygon shadow construction to 1e-7, and the results are invariant under rigid motion and scaling.
- 3D: closed-form volumes (cube, pyramid, sphere mesh, convex hulls, a cube clipped by a range sphere) agree to a relative 1e-4 at 65,536 directions (2e-4 for random hulls), and the error on the cube falls faster than N^-1/2. On an extruded floor plan the view volume equals the ceiling height times the exact 2D isovist area, to 1e-3 at 65,536 directions.
- Point clouds: the ball ray caster agrees with a brute-force first hit to 1e-12.
How to cite
pysovist is free to use and modify under the MIT licence. If it helps your work, please cite the
software. GitHub's "Cite this repository" button gives the same reference from CITATION.cff.
@software{pysovist,
author = {Aguilar, Leonel and Tuncay, Bartu},
title = {pysovist: exact isovists and view volumes from floor plans, meshes and point clouds},
version = {0.2.0},
year = {2026},
url = {https://github.com/leaguilar/pysovist}
}
Licence
MIT. Copyright (c) 2025-2026 Bartu Tuncay and Leonel Aguilar.
Metadata
Release files for pysovist 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pysovist-0.2.0.tar.gz | 481.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pysovist-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 583.3 kB
Release files / pysovist-0.2.0.tar.gz
| Download URL | pysovist-0.2.0.tar.gz |
|---|---|
| Size | 481.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
22ec37627b44ccc7c8cc87b76329eba078f0933f72ae666943fbda71fd00f00d
|
|
BLAKE2b-256 checksum How to use checksums |
7d296b21a88014697e07cea0a450bbb3148045470765fe52e3dcabb2066ac30f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Release files / pysovist-0.2.0-py3-none-any.whl
| Download URL | pysovist-0.2.0-py3-none-any.whl |
|---|---|
| Size | 102.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
98e9e09ea0e05a25d9b1ec6322c856d5093cd5bcba95505c4c40b09f9912be1c
|
|
BLAKE2b-256 checksum How to use checksums |
408f3ed65cd07319c4d2561136a29356c71b58c8ef60caedcf181a2f9a7a0c2f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|