evenlyspacedstreamlines


Nameevenlyspacedstreamlines JSON
Version 0.1.0 PyPI version JSON
download
home_pagehttp://github.com/jacquemv/evenlyspacedstreamlines
SummaryGenerate evenly-spaced streamlines from an orientation field on a triangulated 3D surface
upload_time2023-11-07 20:44:48
maintainer
docs_urlNone
authorVincent Jacquemet
requires_python
licenseMIT
keywords vector field visualization surface streamline
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            
![Illustration of streamlines](https://github.com/jacquemv/evenlyspacedstreamlines/blob/main/illustration.png?raw=true)

### Objective

The objective is to generate a set of evenly-spaced streamlines tangent to a given vector field (or orientation field) on a smooth triangulated surface. The vector field is assumed to be constant over each triangle. A streamline is defined here as a polygonal line on the triangulated surface such that each segment of that polygonal line lies on a triangle and is parallel to the vector associated with that triangle. The algorithm distributes streamlines over the surface in such a way that the minimal distance between streamlines never becomes smaller than a given radius $r$. The approach is inspired by Jobard and Lefer [3].

The structure of the algorithm, its performance and its limitations are discussed in our papers [1, 2], with applications to the visualization of fiber orientation in the human atria.

### Minimal example

Here is an example of streamline generation on a triangular mesh composed of 3 vertices and 1 triangle, so there is 1 orientation vector.
```python
from evenlyspacedstreamlines import evenly_spaced_streamlines
vertices = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
triangles = [[0, 1, 2]]
orientation = [[1.0, 1.0, 0.0]]
radius = 0.4
list_of_lines, list_of_indices, infos = \
    evenly_spaced_streamlines(vertices, triangles, orientation, radius)
```
The output gives the xyz coordinates of 3 streamlines, the indices of the triangles the streamlines pass through (here always triangle 0), and some geometrical information such as streamline lengths:
```python
>>> list_of_lines
[array([[0.4803, 0.5197, 0.],
        [0.,     0.0394, 0.]]),
 array([[0.7674, 0.2326, 0.],
        [0.5348, 0.,     0.]]),
 array([[0.1963, 0.8037, 0.],
        [0.,     0.6074, 0.]])]
>>> list_of_indices
[array([0], dtype=int32), array([0], dtype=int32), array([0], dtype=int32)]
>>> infos
StreamlinesInfos(lengths=array([0.6793, 0.3289, 0.2776]), min_altitude=0.7071,
max_base=1.4142, neighborhood_size=0.0, euler_characteristic=1, 
random_seed=1698460061)
```


### Syntax

```python
list_of_lines, list_of_indices, infos = \
    evenly_spaced_streamlines(vertices, triangles, orientation, radius,
        orthogonal=False, oriented_streamlines=False,
        seed_points=32, seed_region=None, max_length=0,
        avoid_u_turns=True, max_angle=90, singularity_mask_radius=0.1,
        allow_tweaking_orientation=True,
        random_seed=0, parallel=True, num_threads=-1)
```

### Positional arguments

The arguments **vertices** and **triangles** define the triangulated surface, and **orientation** the vector field on that surface. The parameter **radius** specifies streamlines spacing.
- **vertices** ($n_v$-by-3 float array): x, y, z coordinates of the $n_v$ vertices
- **triangles** ($n_t$-by-3 int array): indices of the vertices of the $n_t$ triangles
- **orientation** ($n_t$-by-3 float array): orientation vector in each triangle
- **radius** (float): the distance between streamlines will be larger than the radius and smaller than 2*radius

### Optional keyword arguments

- **seed_points** (int): number of seed points tested to generate each streamline; the longest streamline is kept (default: 32)
- **seed_region** (int array): list of triangle indices among which seed points are picked (default: None, which means that all triangles are considered)
- **orthogonal** (bool): if True, rotate the orientation by 90 degrees (default: False)
- **oriented_streamlines** (bool): if True, streamlines only follow the vector field in the direction it points to (and not the opposite direction); the outputted streamlines are then oriented according to the vector field. If False (default), the orientation field is defined modulo pi instead of 2pi
- **allow_tweaking_orientation** (bool): if an orientation vector is parallel to an edge of the triangle, a small random perturbation is applied to that vector to satisfy the requirement (default: True); otherwise an exception is raised when the requirement is not satisfied
- **singularity_mask_radius** (float): when the orientation field has a singularity (e.g. focus or node), prevent streamlines from entering a sphere of radius 'singularity_mask_radius' x 'radius' around that singularity (default: 0.1)
- **max_length** (int): maximal number of iterations when tracing streamlines; it is needed because of nearly-periodic streamlines (default: 0, which means equal to the number of triangles)
- **max_angle** (float): stop streamline integration if the angle between two consecutive segments is larger than max_angle in degrees; 0 means straight line and 180 means U-turn (default: 90)
- **avoid_u_turns** (bool): restrict high curvatures by maintaining a lateral (perpendicular) distance of at least 'radius' between a segment of the streamline and the other segments of the same streamline; this automatically sets 'max_angle' to at most 90 degrees (default: True)
- **random_seed** (int): initialize the seed for pseudo-random number generation (default: seed based on clock)
- **parallel** (bool): if True (default), use multithreading wherever implemented
- **num_threads** (int): if possible, use that number of threads for parallel computing (default: let OpenMP choose)

### Outputs

The function ``evenly_spaced_streamlines`` returns a 3-tuple:
- **list_of_lines** (list of $n$-by-3 matrices): xyz coordinates of each of the streamlines generated
- **list_of_indices** (list of ($n$-1)-arrays): vectors of indices indicating for each line segment of the streamline in which triangle they lie
- **infos** (namedtuple): information about streamline generation with the following attributes:
    - 'lengths' ($n$-array of float): length of each streamline;
    - 'min_altitude' (float): minimum altitude over all triangles;
    - 'max_base' (float): maximum length of the base edge over all triangles;
    - 'neighborhood_size' (float): average number of triangles at a distance < 'radius' from any triangle
    - 'euler_characteristic' (int): = #vertices - #edges + #triangles
    - 'random_seed' (int): random seed used for random number generation

### Visualization

A simple way to visualize the output is:
```python
import matplotlib.pyplot as plt
plt.figure().add_subplot(projection='3d')
for line in list_of_lines:
    plt.plot(line[:, 0], line[:, 1], line[:, 2])
plt.show()
```
For more sophisticated visualizations, the module also provides a function ``streamlines_to_tubes`` to represent the streamlines as a set of tubes (described by triangulated surfaces).

### Implementation

The code is implemented in C++, interfaced and compiled using cython, and with a wrapper for python (``evenlyspacedstreamlines/wrapper.py``). It was designed for meshes with a number of vertices of the order of 100k and a radius $r$ not too large as compared to mesh edge length.

### Installation

The package can be installed using the command ``pip install evenlyspacedstreamlines`` (on Windows, a compiler such as Microsoft Visual C++ is required).

If the code is downloaded from github, local installation on Linux is done by running ``make local`` and including the directory 'evenlyspacedstreamlines' in the PYTHONPATH environment variable. The easiest way to check if the installation worked is:
```python
from evenlyspacedstreamlines import test; test()
```
Tested using Anaconda 2023.09 (python 3.11) on Linux and Windows.

### Acknowledgements

This work was supported by the Natural Sciences and Engineering Research
Council of Canada (NSERC grant RGPIN-2020-05252).

### References

1. V. Jacquemet. Improved algorithm for generating evenly-spaced streamlines on a triangulated surface (*in preparation*), 2023.

2. A. Saliani, A. Tsikhanovich, V. Jacquemet. [Visualization of interpolated atrial fiber orientation using evenly-spaced streamlines](https://doi.org/10.1016/j.compbiomed.2019.103349), *Comput. Biol. Med.* 2019, vol. 11, pp. 103349. 

3. B. Jobard, W. Lefer. [Creating evenly-spaced streamlines of arbitrary density](https://link.springer.com/chapter/10.1007/978-3-7091-6876-9_5). In Visualization in Scientific Computing’97, pp. 43–55. Springer, 1997.

            

Raw data

            {
    "_id": null,
    "home_page": "http://github.com/jacquemv/evenlyspacedstreamlines",
    "name": "evenlyspacedstreamlines",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "vector,field,visualization,surface,streamline",
    "author": "Vincent Jacquemet",
    "author_email": "vincent.jacquemet@umontreal.ca",
    "download_url": "https://files.pythonhosted.org/packages/4d/fd/141baa316c33312707aa9166827b0bf3852e2bc1b442d866456eed75a27b/evenlyspacedstreamlines-0.1.0.tar.gz",
    "platform": null,
    "description": "\n![Illustration of streamlines](https://github.com/jacquemv/evenlyspacedstreamlines/blob/main/illustration.png?raw=true)\n\n### Objective\n\nThe objective is to generate a set of evenly-spaced streamlines tangent to a given vector field (or orientation field) on a smooth triangulated surface. The vector field is assumed to be constant over each triangle. A streamline is defined here as a polygonal line on the triangulated surface such that each segment of that polygonal line lies on a triangle and is parallel to the vector associated with that triangle. The algorithm distributes streamlines over the surface in such a way that the minimal distance between streamlines never becomes smaller than a given radius $r$. The approach is inspired by Jobard and Lefer [3].\n\nThe structure of the algorithm, its performance and its limitations are discussed in our papers [1, 2], with applications to the visualization of fiber orientation in the human atria.\n\n### Minimal example\n\nHere is an example of streamline generation on a triangular mesh composed of 3 vertices and 1 triangle, so there is 1 orientation vector.\n```python\nfrom evenlyspacedstreamlines import evenly_spaced_streamlines\nvertices = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]\ntriangles = [[0, 1, 2]]\norientation = [[1.0, 1.0, 0.0]]\nradius = 0.4\nlist_of_lines, list_of_indices, infos = \\\n    evenly_spaced_streamlines(vertices, triangles, orientation, radius)\n```\nThe output gives the xyz coordinates of 3 streamlines, the indices of the triangles the streamlines pass through (here always triangle 0), and some geometrical information such as streamline lengths:\n```python\n>>> list_of_lines\n[array([[0.4803, 0.5197, 0.],\n        [0.,     0.0394, 0.]]),\n array([[0.7674, 0.2326, 0.],\n        [0.5348, 0.,     0.]]),\n array([[0.1963, 0.8037, 0.],\n        [0.,     0.6074, 0.]])]\n>>> list_of_indices\n[array([0], dtype=int32), array([0], dtype=int32), array([0], dtype=int32)]\n>>> infos\nStreamlinesInfos(lengths=array([0.6793, 0.3289, 0.2776]), min_altitude=0.7071,\nmax_base=1.4142, neighborhood_size=0.0, euler_characteristic=1, \nrandom_seed=1698460061)\n```\n\n\n### Syntax\n\n```python\nlist_of_lines, list_of_indices, infos = \\\n    evenly_spaced_streamlines(vertices, triangles, orientation, radius,\n        orthogonal=False, oriented_streamlines=False,\n        seed_points=32, seed_region=None, max_length=0,\n        avoid_u_turns=True, max_angle=90, singularity_mask_radius=0.1,\n        allow_tweaking_orientation=True,\n        random_seed=0, parallel=True, num_threads=-1)\n```\n\n### Positional arguments\n\nThe arguments **vertices** and **triangles** define the triangulated surface, and **orientation** the vector field on that surface. The parameter **radius** specifies streamlines spacing.\n- **vertices** ($n_v$-by-3 float array): x, y, z coordinates of the $n_v$ vertices\n- **triangles** ($n_t$-by-3 int array): indices of the vertices of the $n_t$ triangles\n- **orientation** ($n_t$-by-3 float array): orientation vector in each triangle\n- **radius** (float): the distance between streamlines will be larger than the radius and smaller than 2*radius\n\n### Optional keyword arguments\n\n- **seed_points** (int): number of seed points tested to generate each streamline; the longest streamline is kept (default: 32)\n- **seed_region** (int array): list of triangle indices among which seed points are picked (default: None, which means that all triangles are considered)\n- **orthogonal** (bool): if True, rotate the orientation by 90 degrees (default: False)\n- **oriented_streamlines** (bool): if True, streamlines only follow the vector field in the direction it points to (and not the opposite direction); the outputted streamlines are then oriented according to the vector field. If False (default), the orientation field is defined modulo pi instead of 2pi\n- **allow_tweaking_orientation** (bool): if an orientation vector is parallel to an edge of the triangle, a small random perturbation is applied to that vector to satisfy the requirement (default: True); otherwise an exception is raised when the requirement is not satisfied\n- **singularity_mask_radius** (float): when the orientation field has a singularity (e.g. focus or node), prevent streamlines from entering a sphere of radius 'singularity_mask_radius' x 'radius' around that singularity (default: 0.1)\n- **max_length** (int): maximal number of iterations when tracing streamlines; it is needed because of nearly-periodic streamlines (default: 0, which means equal to the number of triangles)\n- **max_angle** (float): stop streamline integration if the angle between two consecutive segments is larger than max_angle in degrees; 0 means straight line and 180 means U-turn (default: 90)\n- **avoid_u_turns** (bool): restrict high curvatures by maintaining a lateral (perpendicular) distance of at least 'radius' between a segment of the streamline and the other segments of the same streamline; this automatically sets 'max_angle' to at most 90 degrees (default: True)\n- **random_seed** (int): initialize the seed for pseudo-random number generation (default: seed based on clock)\n- **parallel** (bool): if True (default), use multithreading wherever implemented\n- **num_threads** (int): if possible, use that number of threads for parallel computing (default: let OpenMP choose)\n\n### Outputs\n\nThe function ``evenly_spaced_streamlines`` returns a 3-tuple:\n- **list_of_lines** (list of $n$-by-3 matrices): xyz coordinates of each of the streamlines generated\n- **list_of_indices** (list of ($n$-1)-arrays): vectors of indices indicating for each line segment of the streamline in which triangle they lie\n- **infos** (namedtuple): information about streamline generation with the following attributes:\n    - 'lengths' ($n$-array of float): length of each streamline;\n    - 'min_altitude' (float): minimum altitude over all triangles;\n    - 'max_base' (float): maximum length of the base edge over all triangles;\n    - 'neighborhood_size' (float): average number of triangles at a distance < 'radius' from any triangle\n    - 'euler_characteristic' (int): = #vertices - #edges + #triangles\n    - 'random_seed' (int): random seed used for random number generation\n\n### Visualization\n\nA simple way to visualize the output is:\n```python\nimport matplotlib.pyplot as plt\nplt.figure().add_subplot(projection='3d')\nfor line in list_of_lines:\n    plt.plot(line[:, 0], line[:, 1], line[:, 2])\nplt.show()\n```\nFor more sophisticated visualizations, the module also provides a function ``streamlines_to_tubes`` to represent the streamlines as a set of tubes (described by triangulated surfaces).\n\n### Implementation\n\nThe code is implemented in C++, interfaced and compiled using cython, and with a wrapper for python (``evenlyspacedstreamlines/wrapper.py``). It was designed for meshes with a number of vertices of the order of 100k and a radius $r$ not too large as compared to mesh edge length.\n\n### Installation\n\nThe package can be installed using the command ``pip install evenlyspacedstreamlines`` (on Windows, a compiler such as Microsoft Visual C++ is required).\n\nIf the code is downloaded from github, local installation on Linux is done by running ``make local`` and including the directory 'evenlyspacedstreamlines' in the PYTHONPATH environment variable. The easiest way to check if the installation worked is:\n```python\nfrom evenlyspacedstreamlines import test; test()\n```\nTested using Anaconda 2023.09 (python 3.11) on Linux and Windows.\n\n### Acknowledgements\n\nThis work was supported by the Natural Sciences and Engineering Research\nCouncil of Canada (NSERC grant RGPIN-2020-05252).\n\n### References\n\n1. V. Jacquemet. Improved algorithm for generating evenly-spaced streamlines on a triangulated surface (*in preparation*), 2023.\n\n2. A. Saliani, A. Tsikhanovich, V. Jacquemet. [Visualization of interpolated atrial fiber orientation using evenly-spaced streamlines](https://doi.org/10.1016/j.compbiomed.2019.103349), *Comput. Biol. Med.* 2019, vol. 11, pp. 103349. \n\n3. B. Jobard, W. Lefer. [Creating evenly-spaced streamlines of arbitrary density](https://link.springer.com/chapter/10.1007/978-3-7091-6876-9_5). In Visualization in Scientific Computing\u201997, pp. 43\u201355. Springer, 1997.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Generate evenly-spaced streamlines from an orientation field on a triangulated 3D surface",
    "version": "0.1.0",
    "project_urls": {
        "Homepage": "http://github.com/jacquemv/evenlyspacedstreamlines"
    },
    "split_keywords": [
        "vector",
        "field",
        "visualization",
        "surface",
        "streamline"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4dfd141baa316c33312707aa9166827b0bf3852e2bc1b442d866456eed75a27b",
                "md5": "38412b2fb430028d323f86ebacfe8070",
                "sha256": "2f43075a281321ecff6ec37cc0d84546d8610399e5dbbfb4d2070d7e34da6c33"
            },
            "downloads": -1,
            "filename": "evenlyspacedstreamlines-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "38412b2fb430028d323f86ebacfe8070",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 182122,
            "upload_time": "2023-11-07T20:44:48",
            "upload_time_iso_8601": "2023-11-07T20:44:48.832646Z",
            "url": "https://files.pythonhosted.org/packages/4d/fd/141baa316c33312707aa9166827b0bf3852e2bc1b442d866456eed75a27b/evenlyspacedstreamlines-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-11-07 20:44:48",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "jacquemv",
    "github_project": "evenlyspacedstreamlines",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "evenlyspacedstreamlines"
}
        
Elapsed time: 0.14158s