Gray's Tortuosity Model
Gray's Tortuosity Model (GTM) measures how much a sampled planar path turns at a chosen physical scale.
The core p=2 model is a scale-normalized curvature-energy tortuosity:
T_ell = (T_c / 2 pi) ell int kappa_ell(s)^2 ds
where ell is the object or observation scale and kappa_ell is curvature after scale-dependent smoothing.
Use GTM when you want to compare paths by turning load, local tortuosity density, polygon corner load, or scale response rather than by endpoint displacement alone.
Install
python -m pip install grays-tortuosity-model
The package requires Python 3.10+ and depends on NumPy and Matplotlib.
Quick Example
import numpy as np
from grays_tortuosity import tortuosity_and_average
t = np.linspace(0.0, 4.0 * np.pi, 800)
path = np.column_stack((t, np.sin(t)))
result = tortuosity_and_average(path, ell=0.5)
print(result.total)
print(result.average)
total is accumulated tortuosity over the whole path. average is total tortuosity divided by path length, which is often easier to compare across paths of different lengths.
Polygon And Polyline Inputs
For polygon vertices or sparse polylines, use polygon_tortuosity. Inputs are open by default; pass closed=True for closed polygons.
from grays_tortuosity import polygon_tortuosity
vertices = [
(0.0, 0.0),
(2.0, 0.0),
(2.0, 1.0),
(0.0, 1.0),
]
midpoint = polygon_tortuosity(vertices, ell=0.25, closed=True)
fsad = polygon_tortuosity(vertices, ell=0.25, closed=True, smoothing="fsad")
print(midpoint.total, midpoint.average)
print(fsad.total, fsad.average)
Use smoothing="midpoint" for the default midpoint-quadratic corner smoothing. Use smoothing="fsad" for finite-scale angular density on the original polygon vertices, and smoothing="chord" for derivative-free finite-scale chord density.
Finite-Scale Vector Relaxation
Version 0.3.0 adds the vector relaxation / vector RC diagnostics:
from grays_tortuosity import polygon_vector_rc_state
vr = polygon_vector_rc_state(vertices, ell=0.25, closed=True, source="tangent_piecewise")
print(vr.total)
print(vr.min_radius)
print(vr.radial_total, vr.angular_total)
The canonical model uses zero lookahead, ell * x'(s) = T(s) - x(s). The state is not unit norm;
its magnitude can collapse during opposing-direction transients. The density is
q = (T_c/2*pi) * ell * ||x'||^2 / ||x||^2, with radial/angular diagnostics exposed separately.
Local Density
import numpy as np
from grays_tortuosity import finite_difference_tortuosity_density, plot_density_path
t = np.linspace(0.0, 2.0 * np.pi, 600)
path = np.column_stack((np.cos(t), np.sin(t)))
density = finite_difference_tortuosity_density(path, ell=0.2, closed=True)
fig, ax, collection = plot_density_path(density.total.points, density.density, closed=True)
fig.savefig("density_path.png", dpi=180)
Density functions return sampled fields so you can inspect where the turning load is concentrated, integrate local windows, or plot density along the path.
Command Line
Evaluate a CSV file with x and y columns:
grays-tortuosity path.csv --ell 0.5 --x-column x --y-column y --density-summary --json
Common Entry Points
tortuosity(points, ell, ...): total GTM tortuosity for sampled planar points.tortuosity_and_average(points, ell, ...): total plus length-normalized average.tortuosity_density(points, ell, ...): local unsigned density from headings.finite_difference_tortuosity_density(points, ell, ...): smooth-curve density from coordinate derivatives.polygon_tortuosity(points, ell, closed=False, smoothing="midpoint"): polygon/polyline-friendly wrapper.polygon_finite_scale_angular_density(points, ell, window=None, closed=False): direct FSAD polygon density.polygon_finite_scale_chord_density(points, h, closed=False): direct FSCD polygon density.polygon_vector_rc_state(points, ell, closed=False, source="tangent_piecewise"): vector relaxation / vector RC state and density diagnostics.scale_sweep(points, ells, ...): evaluate a path across multiple scales.plot_density_path(points, density, ...): Matplotlib helper for path-colored density plots.
Notes
ellis the most important parameter. It should represent the physical object scale, sensor scale, or observation scale relevant to the path.- Open paths and closed paths are treated differently. Pass
closed=Trueexplicitly when the final point should connect back to the first. - The package is research-facing and currently marked alpha. APIs are usable, tested, and documented, but the model is still evolving.
- Riemann/zeta experiments live outside the distributed package under the repository's
research/tree and are not part of the PyPI API.
More Documentation
The source repository contains a fuller README, API quick reference, examples, and theory notes. The PyPI page intentionally avoids embedded images so it renders cleanly without relying on repository-hosted assets.
Metadata
Release files for grays-tortuosity-model 0.3.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 | |
|---|---|---|---|
| grays_tortuosity_model-0.3.0.tar.gz | 207.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| grays_tortuosity_model-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 276.4 kB
Release files / grays_tortuosity_model-0.3.0.tar.gz
| Download URL | grays_tortuosity_model-0.3.0.tar.gz |
|---|---|
| Size | 207.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ac71f21ddd7ad4fa6d3a405a2ad080fdaa864059d8f6e132ed9fe54806b5fe5a
|
|
BLAKE2b-256 checksum How to use checksums |
9567cc4a61f76bfb990ab8299017036e37f7a5f5f6c5af73f20a4e4e2744fb53
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / grays_tortuosity_model-0.3.0-py3-none-any.whl
| Download URL | grays_tortuosity_model-0.3.0-py3-none-any.whl |
|---|---|
| Size | 69.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9cabd74bb49658daf53d1732f09cc9e1652d97794e2c5f8931d6ee3a55af9b4d
|
|
BLAKE2b-256 checksum How to use checksums |
c6c5e1412c7d06c2426a97f31411645a4de88ff73f6aa40e1a6f59433793f802
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|