bsplot 0.0.7__tar.gz → 0.0.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bsplot-0.0.7 → bsplot-0.0.9}/PKG-INFO +2 -1
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/__init__.py +1 -1
- bsplot-0.0.9/bsplot/data/STN_lh.nii.gz +0 -0
- bsplot-0.0.9/bsplot/data/STN_rh.nii.gz +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/graph/edges.py +5 -11
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/streamlines.py +3 -8
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/style.py +188 -28
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/styles/black.mplstyle +0 -1
- bsplot-0.0.9/bsplot/styles/template.mplstyle +83 -0
- bsplot-0.0.9/bsplot/styles/white.mplstyle +77 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/surface.py +67 -4
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/templates.py +1 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/text.py +1 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot.egg-info/SOURCES.txt +5 -1
- {bsplot-0.0.7 → bsplot-0.0.9}/pyproject.toml +3 -1
- {bsplot-0.0.7 → bsplot-0.0.9}/LICENSE +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/MANIFEST.in +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/README.md +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/anat.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/animate.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/bioicons.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/brain.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/colors.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/data/HCP_avg-SC.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/data/MNI152.rh.pial +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/data/__init__.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/data/parcellations/HCP-MMP1.L.label.gii +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/data/parcellations/HCP-MMP1.R.label.gii +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/data/surface.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/figure.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/graph/__init__.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/graph/flowchart.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/graph/layout.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/graph/network.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/graph/nodes.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/panels.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/__init__.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/_sync_from_zenodo_download.sh +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/batlowK.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/batlowW.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/cyclic/bamO.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/cyclic/brocO.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/cyclic/corkO.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/cyclic/romaO.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/cyclic/vikO.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/diverging/bam.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/diverging/berlin.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/diverging/broc.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/diverging/cork.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/diverging/lisbon.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/diverging/managua.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/diverging/roma.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/diverging/tofino.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/diverging/vanimo.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/diverging/vik.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/multisequential/bukavu.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/multisequential/fes.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/multisequential/oleron.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/naviaW.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/acton.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/bamako.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/batlow.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/bilbao.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/buda.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/davos.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/devon.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/glasgow.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/grayC.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/hawaii.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/imola.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/lajolla.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/lapaz.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/lipari.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/navia.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/nuuk.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/oslo.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/tokyo.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/scientific_color_maps/sequential/turku.txt +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/styles/bss.mplstyle +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/styles/bwcomp.mplstyle +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/styles/nature.mplstyle +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/styles/transparent.mplstyle +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/styles/tvbo.mplstyle +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/text2obj.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/timeseries.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/utils.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/bsplot/volume.py +0 -0
- {bsplot-0.0.7 → bsplot-0.0.9}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bsplot
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.9
|
|
4
4
|
Summary: Plotting utilities for neuroscience: styles, palettes, surfaces, glass-brain, and panel helpers.
|
|
5
5
|
License-Expression: EUPL-1.2
|
|
6
6
|
Project-URL: Repository, https://github.com/leon-k-martin/bsplot
|
|
@@ -21,6 +21,7 @@ Requires-Dist: quarto; extra == "docs"
|
|
|
21
21
|
Requires-Dist: quartodoc; extra == "docs"
|
|
22
22
|
Requires-Dist: griffe<1.0; extra == "docs"
|
|
23
23
|
Requires-Dist: jupyter-cache; extra == "docs"
|
|
24
|
+
Requires-Dist: cairosvg; extra == "docs"
|
|
24
25
|
Dynamic: license-file
|
|
25
26
|
|
|
26
27
|
<h1>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Copyright © Charité Universitätsmedizin Berlin. This software is licensed under the terms of the European Union Public Licence (EUPL) version 1.2 or later.
|
|
2
2
|
from os.path import abspath, dirname, join
|
|
3
3
|
|
|
4
|
-
__version__ = "0.0.
|
|
4
|
+
__version__ = "0.0.9"
|
|
5
5
|
|
|
6
6
|
ROOT = abspath(dirname(__file__))
|
|
7
7
|
datadir = join(ROOT, "data")
|
|
Binary file
|
|
Binary file
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# Copyright © Charité Universitätsmedizin Berlin. This software is licensed under the terms of the European Union Public Licence (EUPL) version 1.2 or later.
|
|
2
2
|
import numpy as np
|
|
3
|
-
from matplotlib
|
|
4
|
-
from bsplot.colors import Colormap as _resolve_cmap
|
|
5
|
-
|
|
6
|
-
import numpy as np
|
|
3
|
+
from matplotlib import colormaps
|
|
7
4
|
from matplotlib.colors import Normalize
|
|
8
5
|
from matplotlib.patches import FancyArrowPatch
|
|
9
6
|
|
|
7
|
+
from bsplot.colors import Colormap as _resolve_cmap
|
|
8
|
+
|
|
10
9
|
|
|
11
10
|
def get_edge_color_mapping(G, colormap="viridis", color_by="type"):
|
|
12
11
|
"""
|
|
@@ -197,13 +196,8 @@ def draw_custom_edges(
|
|
|
197
196
|
# if "mutation_scale" not in kwargs.keys():
|
|
198
197
|
# kwargs["mutation_scale"] = 10.0
|
|
199
198
|
|
|
200
|
-
if edge_colors in colormaps
|
|
201
|
-
|
|
202
|
-
edge_colors
|
|
203
|
-
if isinstance(edge_colors, str) and edge_colors in colormaps.keys()
|
|
204
|
-
else "viridis"
|
|
205
|
-
)
|
|
206
|
-
colmap = get_edge_color_mapping(G, colormap=cmap, color_by=color_by)
|
|
199
|
+
if isinstance(edge_colors, str) and edge_colors in colormaps:
|
|
200
|
+
colmap = get_edge_color_mapping(G, colormap=edge_colors, color_by=color_by)
|
|
207
201
|
edge_colors = [
|
|
208
202
|
colmap[data[color_by] if color_by in data else "n.a."]
|
|
209
203
|
for (u, v, data) in G.edges(data=True)
|
|
@@ -136,14 +136,6 @@ def plot_tractogram(
|
|
|
136
136
|
**kwargs,
|
|
137
137
|
):
|
|
138
138
|
from tqdm import tqdm
|
|
139
|
-
import importlib
|
|
140
|
-
|
|
141
|
-
try:
|
|
142
|
-
dipy = importlib.import_module("dipy")
|
|
143
|
-
except ImportError as e:
|
|
144
|
-
raise ImportError(
|
|
145
|
-
"plot_tractogram requires 'dipy'. Please install it: pip install dipy"
|
|
146
|
-
) from e
|
|
147
139
|
from matplotlib.cm import ScalarMappable
|
|
148
140
|
|
|
149
141
|
# Load tractogram from file if path is provided
|
|
@@ -163,6 +155,9 @@ def plot_tractogram(
|
|
|
163
155
|
if datakey not in data:
|
|
164
156
|
raise ValueError(f"Data key '{datakey}' not found in streamlines data.")
|
|
165
157
|
values = data[datakey]
|
|
158
|
+
arr = np.asarray(values)
|
|
159
|
+
if arr.ndim == 2 and arr.shape[1] == 1:
|
|
160
|
+
values = arr.ravel()
|
|
166
161
|
|
|
167
162
|
def _is_pointwise_values(values_in, streamlines_in):
|
|
168
163
|
if values_in is None:
|
|
@@ -9,45 +9,190 @@ import bsplot # Import your package to access its path
|
|
|
9
9
|
from .colors import make_cycler_from_cmap
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
def _styles_dir():
|
|
13
|
+
"""Directory holding the built-in ``.mplstyle`` modules shipped with bsplot."""
|
|
14
|
+
return os.path.join(os.path.dirname(bsplot.__file__), "styles")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _available_styles():
|
|
18
|
+
"""Sorted names of the built-in style modules (without the extension)."""
|
|
19
|
+
return sorted(
|
|
20
|
+
f.removesuffix(".mplstyle")
|
|
21
|
+
for f in os.listdir(_styles_dir())
|
|
22
|
+
if f.endswith(".mplstyle")
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _named_style_path(name):
|
|
27
|
+
"""Resolve a built-in style module *name* to its ``.mplstyle`` path."""
|
|
28
|
+
p = os.path.join(_styles_dir(), f"{name}.mplstyle")
|
|
29
|
+
if not os.path.exists(p):
|
|
30
|
+
raise ValueError(
|
|
31
|
+
f"Style module '{name}' not found. "
|
|
32
|
+
f"Available modules: {_available_styles()}. "
|
|
33
|
+
f"To load a custom style file, pass a path ending in '.mplstyle'."
|
|
34
|
+
)
|
|
35
|
+
return p
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _looks_like_path(token):
|
|
39
|
+
"""True if *token* should be treated as a filesystem path, not a module name."""
|
|
40
|
+
# os.PathLike (e.g. pathlib.Path) is always a filesystem path.
|
|
41
|
+
if isinstance(token, os.PathLike):
|
|
42
|
+
return True
|
|
43
|
+
return (
|
|
44
|
+
os.sep in token
|
|
45
|
+
or (os.altsep is not None and os.altsep in token)
|
|
46
|
+
or token.endswith(".mplstyle")
|
|
47
|
+
or os.path.isfile(token)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _resolve_style_token(token):
|
|
52
|
+
"""Resolve one token to a list of ``.mplstyle`` paths.
|
|
53
|
+
|
|
54
|
+
A token that looks like a path is loaded as a file (never split on ``-``);
|
|
55
|
+
otherwise it is a built-in module name, optionally composed with ``-``.
|
|
56
|
+
"""
|
|
57
|
+
if _looks_like_path(token):
|
|
58
|
+
path = os.path.expanduser(os.fspath(token))
|
|
59
|
+
if not os.path.isfile(path):
|
|
60
|
+
raise ValueError(f"Style file '{token}' not found.")
|
|
61
|
+
return [path]
|
|
62
|
+
return [_named_style_path(m) for m in token.split("-")]
|
|
63
|
+
|
|
64
|
+
|
|
12
65
|
def use(style="bss"):
|
|
13
|
-
"""Apply one or more bsplot
|
|
66
|
+
"""Apply one or more bsplot styles.
|
|
67
|
+
|
|
68
|
+
Styles are layered left-to-right, so later styles override earlier ones.
|
|
69
|
+
Three kinds of style token are accepted and can be freely mixed:
|
|
70
|
+
|
|
71
|
+
* **Built-in modules** — the ``.mplstyle`` files shipped with bsplot,
|
|
72
|
+
referenced by name (e.g. ``"black"``, ``"tvbo"``, ``"transparent"``).
|
|
73
|
+
* **Composed modules** — several built-in modules joined with ``-``
|
|
74
|
+
in a single string (e.g. ``"black-transparent"``).
|
|
75
|
+
* **Filesystem paths** — a path to any ``.mplstyle`` file on disk, so a
|
|
76
|
+
custom style can be shipped alongside a study and combined with a base.
|
|
14
77
|
|
|
15
|
-
|
|
16
|
-
|
|
78
|
+
Custom style files should stay minimal (only the rcParams they override)
|
|
79
|
+
so they layer cleanly on top of a base module.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
style : str, os.PathLike, or list of these
|
|
84
|
+
A single token, a ``-``-composed string of built-in modules, or a
|
|
85
|
+
list mixing module names and filesystem paths. Anything that looks
|
|
86
|
+
like a path (contains a path separator, ends in ``.mplstyle``, is a
|
|
87
|
+
:class:`~os.PathLike`, or names an existing file) is loaded as a file
|
|
88
|
+
and is never split on ``-``.
|
|
17
89
|
|
|
18
90
|
Examples
|
|
19
91
|
--------
|
|
20
|
-
>>> bsplot.style.use("black")
|
|
21
|
-
>>> bsplot.style.use("black-transparent")
|
|
22
|
-
>>> bsplot.style.use("tvbo-transparent")
|
|
92
|
+
>>> bsplot.style.use("black") # single base module
|
|
93
|
+
>>> bsplot.style.use("black-transparent") # base + module
|
|
94
|
+
>>> bsplot.style.use("tvbo-transparent") # works with any base
|
|
95
|
+
>>> bsplot.style.use("study/custom.mplstyle") # a custom style file
|
|
96
|
+
>>> bsplot.style.use(["black", "study/custom.mplstyle"]) # base + custom
|
|
97
|
+
>>> bsplot.style.use(["tvbo", "study/custom.mplstyle"]) # combine freely
|
|
23
98
|
"""
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
available = sorted(
|
|
30
|
-
f.removesuffix(".mplstyle")
|
|
31
|
-
for f in os.listdir(styles_dir)
|
|
32
|
-
if f.endswith(".mplstyle")
|
|
33
|
-
)
|
|
34
|
-
raise ValueError(
|
|
35
|
-
f"Style module '{name}' not found. "
|
|
36
|
-
f"Available modules: {available}"
|
|
37
|
-
)
|
|
38
|
-
return p
|
|
39
|
-
|
|
40
|
-
# Split on "-" to get composable modules: "black-transparent" -> ["black", "transparent"]
|
|
41
|
-
modules = style.split("-")
|
|
42
|
-
paths = [_style_path(m) for m in modules]
|
|
99
|
+
# Normalise to a flat, ordered list of style-file paths.
|
|
100
|
+
tokens = style if isinstance(style, (list, tuple)) else [style]
|
|
101
|
+
paths = []
|
|
102
|
+
for tok in tokens:
|
|
103
|
+
paths.extend(_resolve_style_token(tok))
|
|
43
104
|
plt.style.use(paths)
|
|
44
105
|
|
|
45
106
|
# Turn off legacy layout flags that can conflict
|
|
46
107
|
plt.rcParams["figure.constrained_layout.use"] = False
|
|
47
108
|
plt.rcParams["figure.autolayout"] = False
|
|
48
109
|
|
|
49
|
-
|
|
50
|
-
|
|
110
|
+
|
|
111
|
+
def create_template(path=None, base=None, overwrite=False):
|
|
112
|
+
"""Write a ready-to-edit ``.mplstyle`` style template to disk.
|
|
113
|
+
|
|
114
|
+
Scaffolds a fully functional style file you can customise and ship
|
|
115
|
+
alongside a study or project. The result can be applied on its own or
|
|
116
|
+
combined with a bsplot base style via :func:`use` (e.g.
|
|
117
|
+
``bsplot.style.use(["black", "my_study.mplstyle"])``).
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
path : str, os.PathLike, or None
|
|
122
|
+
Where to write the template. If ``None`` (default), writes
|
|
123
|
+
``bsplot_custom.mplstyle`` in the current directory. A ``.mplstyle``
|
|
124
|
+
extension is appended when missing, and a directory target writes
|
|
125
|
+
``bsplot_custom.mplstyle`` inside it.
|
|
126
|
+
base : str, os.PathLike, or None
|
|
127
|
+
Seed the template from an existing style. May be the name of a
|
|
128
|
+
built-in module (e.g. ``"black"``, ``"tvbo"`` — see
|
|
129
|
+
:func:`_available_styles`) or a path to any ``.mplstyle`` file. When
|
|
130
|
+
``None`` (default), a complete curated starter template is written.
|
|
131
|
+
overwrite : bool
|
|
132
|
+
If ``False`` (default), refuse to clobber an existing file.
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
str
|
|
137
|
+
The path that was written.
|
|
138
|
+
|
|
139
|
+
Examples
|
|
140
|
+
--------
|
|
141
|
+
>>> bsplot.style.create_template() # bsplot_custom.mplstyle
|
|
142
|
+
>>> bsplot.style.create_template("study/theme.mplstyle")
|
|
143
|
+
>>> bsplot.style.create_template("study/dark.mplstyle", base="black")
|
|
144
|
+
>>> bsplot.style.use(["black", "study/dark.mplstyle"]) # combine base + custom
|
|
145
|
+
"""
|
|
146
|
+
if path is None:
|
|
147
|
+
path = "bsplot_custom.mplstyle"
|
|
148
|
+
path = os.path.expanduser(os.fspath(path))
|
|
149
|
+
if os.path.isdir(path):
|
|
150
|
+
path = os.path.join(path, "bsplot_custom.mplstyle")
|
|
151
|
+
if not path.endswith(".mplstyle"):
|
|
152
|
+
path += ".mplstyle"
|
|
153
|
+
|
|
154
|
+
if os.path.exists(path) and not overwrite:
|
|
155
|
+
raise FileExistsError(
|
|
156
|
+
f"'{path}' already exists. Pass overwrite=True to replace it, "
|
|
157
|
+
f"or choose a different path."
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
if base is None:
|
|
161
|
+
# The shipped, self-documenting default style — also available as
|
|
162
|
+
# ``bsplot.style.use("template")``. Copied verbatim; its own header
|
|
163
|
+
# explains how to edit and combine it.
|
|
164
|
+
with open(_named_style_path("template"), encoding="utf-8") as fh:
|
|
165
|
+
content = fh.read()
|
|
166
|
+
else:
|
|
167
|
+
bodies = []
|
|
168
|
+
for src in _resolve_style_token(base):
|
|
169
|
+
with open(src, "r", encoding="utf-8") as fh:
|
|
170
|
+
bodies.append(fh.read().strip())
|
|
171
|
+
body = "\n\n".join(bodies)
|
|
172
|
+
# Show the exact base string in the combine hint when it is a module
|
|
173
|
+
# name (or "-" composition); fall back to a placeholder for file bases.
|
|
174
|
+
combine_base = base if not _looks_like_path(base) else "<base-style>"
|
|
175
|
+
fname = os.path.basename(path)
|
|
176
|
+
header = f"""\
|
|
177
|
+
# Custom bsplot style — seeded from '{base}'.
|
|
178
|
+
#
|
|
179
|
+
# Edit the values below, then apply it directly:
|
|
180
|
+
# bsplot.style.use("{fname}")
|
|
181
|
+
#
|
|
182
|
+
# It also layers on top of any base style (later wins), so you can
|
|
183
|
+
# trim this down to only the keys you override and combine:
|
|
184
|
+
# bsplot.style.use(["{combine_base}", "{fname}"])
|
|
185
|
+
#
|
|
186
|
+
# rcParams reference:
|
|
187
|
+
# https://matplotlib.org/stable/users/explain/customizing.html
|
|
188
|
+
"""
|
|
189
|
+
content = header + "\n" + body + "\n"
|
|
190
|
+
|
|
191
|
+
parent = os.path.dirname(os.path.abspath(path))
|
|
192
|
+
os.makedirs(parent, exist_ok=True)
|
|
193
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
194
|
+
f.write(content)
|
|
195
|
+
return path
|
|
51
196
|
|
|
52
197
|
|
|
53
198
|
def format(obj, **kwargs):
|
|
@@ -125,7 +270,12 @@ def format(obj, **kwargs):
|
|
|
125
270
|
|
|
126
271
|
|
|
127
272
|
def format_fig(
|
|
128
|
-
fig,
|
|
273
|
+
fig,
|
|
274
|
+
colorbar_decimals=None,
|
|
275
|
+
tick_decimals=None,
|
|
276
|
+
add_panel_numbers="auto",
|
|
277
|
+
align_labels=True,
|
|
278
|
+
**kwargs,
|
|
129
279
|
):
|
|
130
280
|
"""Format all axes in a figure.
|
|
131
281
|
|
|
@@ -134,8 +284,11 @@ def format_fig(
|
|
|
134
284
|
If None or "smart", uses automatic detection based on data range.
|
|
135
285
|
- colorbar_decimals: int | None -> number of decimals for colorbar ticks.
|
|
136
286
|
If None, uses automatic detection based on data range.
|
|
287
|
+
- align_labels: bool -> if True, align x- and y-axis labels across subplots
|
|
288
|
+
via ``fig.align_labels()``.
|
|
137
289
|
- other kwargs are passed to format_ax/format_colorbar as appropriate
|
|
138
290
|
"""
|
|
291
|
+
fig.set_layout_engine("compressed")
|
|
139
292
|
i = 1
|
|
140
293
|
for ax in fig.axes:
|
|
141
294
|
# Skip polar (and other non-rectilinear) axes – they lack standard spines
|
|
@@ -164,6 +317,9 @@ def format_fig(
|
|
|
164
317
|
)
|
|
165
318
|
i += 1
|
|
166
319
|
|
|
320
|
+
if align_labels:
|
|
321
|
+
fig.align_labels()
|
|
322
|
+
|
|
167
323
|
|
|
168
324
|
def format_all(**kwargs):
|
|
169
325
|
"""Format every open matplotlib figure.
|
|
@@ -628,6 +784,10 @@ def _apply_axis_offset(ax, axis, offset, pos="top"):
|
|
|
628
784
|
|
|
629
785
|
|
|
630
786
|
def format_colorbar(ax, colorbar_decimals=None):
|
|
787
|
+
# Accept both Colorbar objects and Axes objects
|
|
788
|
+
import matplotlib.colorbar
|
|
789
|
+
if isinstance(ax, matplotlib.colorbar.Colorbar):
|
|
790
|
+
ax = ax.ax
|
|
631
791
|
for spine in ax.spines.values():
|
|
632
792
|
spine.set_visible(False)
|
|
633
793
|
orientation = (
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# bsplot custom style template
|
|
3
|
+
# ============================================================================
|
|
4
|
+
# A complete, ready-to-edit matplotlib style. Change any value below.
|
|
5
|
+
#
|
|
6
|
+
# Apply it directly (standalone), e.g. shipped alongside a study:
|
|
7
|
+
# import bsplot
|
|
8
|
+
# bsplot.style.use("path/to/this.mplstyle")
|
|
9
|
+
#
|
|
10
|
+
# Or layer it on top of a bsplot base style so you only override what you need.
|
|
11
|
+
# Later styles win, so keep just the lines you change and delete the rest:
|
|
12
|
+
# bsplot.style.use(["tvbo", "path/to/this.mplstyle"])
|
|
13
|
+
# bsplot.style.use(["black", "path/to/this.mplstyle"])
|
|
14
|
+
#
|
|
15
|
+
# Full list of options:
|
|
16
|
+
# https://matplotlib.org/stable/users/explain/customizing.html
|
|
17
|
+
# ============================================================================
|
|
18
|
+
|
|
19
|
+
# --- Axes ---
|
|
20
|
+
axes.facecolor: white
|
|
21
|
+
axes.edgecolor: black
|
|
22
|
+
axes.linewidth: 1.5
|
|
23
|
+
axes.spines.top: False
|
|
24
|
+
axes.spines.right: False
|
|
25
|
+
axes.grid: False
|
|
26
|
+
axes.titlesize: 10
|
|
27
|
+
axes.titlepad: 6
|
|
28
|
+
axes.titlecolor: black
|
|
29
|
+
axes.labelsize: 9
|
|
30
|
+
axes.labelpad: 4
|
|
31
|
+
axes.labelcolor: black
|
|
32
|
+
|
|
33
|
+
# --- Font ---
|
|
34
|
+
font.family: sans-serif
|
|
35
|
+
font.sans-serif: Helvetica, Arial, DejaVu Sans
|
|
36
|
+
font.size: 9
|
|
37
|
+
text.color: black
|
|
38
|
+
|
|
39
|
+
# --- Ticks ---
|
|
40
|
+
xtick.direction: out
|
|
41
|
+
ytick.direction: out
|
|
42
|
+
xtick.major.size: 6
|
|
43
|
+
ytick.major.size: 6
|
|
44
|
+
xtick.major.width: 1.5
|
|
45
|
+
ytick.major.width: 1.5
|
|
46
|
+
xtick.labelsize: 8
|
|
47
|
+
ytick.labelsize: 8
|
|
48
|
+
xtick.color: black
|
|
49
|
+
ytick.color: black
|
|
50
|
+
xtick.minor.visible: False
|
|
51
|
+
ytick.minor.visible: False
|
|
52
|
+
|
|
53
|
+
# --- Grid (only shown when axes.grid: True) ---
|
|
54
|
+
grid.color: 0.6
|
|
55
|
+
grid.linestyle: -
|
|
56
|
+
grid.linewidth: 0.5
|
|
57
|
+
grid.alpha: 0.3
|
|
58
|
+
|
|
59
|
+
# --- Lines and markers ---
|
|
60
|
+
lines.linewidth: 1.5
|
|
61
|
+
lines.markersize: 4
|
|
62
|
+
lines.antialiased: True
|
|
63
|
+
|
|
64
|
+
# --- Legend ---
|
|
65
|
+
legend.frameon: True
|
|
66
|
+
legend.fontsize: 8
|
|
67
|
+
legend.loc: best
|
|
68
|
+
|
|
69
|
+
# --- Figure ---
|
|
70
|
+
figure.figsize: 7.2, 5.0
|
|
71
|
+
figure.dpi: 150
|
|
72
|
+
figure.facecolor: white
|
|
73
|
+
figure.edgecolor: white
|
|
74
|
+
|
|
75
|
+
# --- Savefig (export) ---
|
|
76
|
+
savefig.dpi: 300
|
|
77
|
+
savefig.format: pdf
|
|
78
|
+
savefig.bbox: tight
|
|
79
|
+
savefig.pad_inches: 0.05
|
|
80
|
+
savefig.transparent: False
|
|
81
|
+
|
|
82
|
+
# --- Color cycle (colourblind-safe; edit to match your palette) ---
|
|
83
|
+
axes.prop_cycle: cycler(color=["#000000", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7"])
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# --- Axes ---
|
|
2
|
+
axes.linewidth: 1.5
|
|
3
|
+
axes.edgecolor: black
|
|
4
|
+
axes.spines.top: False
|
|
5
|
+
axes.spines.right: False
|
|
6
|
+
axes.autolimit_mode: data
|
|
7
|
+
axes.titlesize: 10
|
|
8
|
+
axes.labelsize: 9
|
|
9
|
+
axes.labelpad: 4
|
|
10
|
+
axes.titlepad: 6
|
|
11
|
+
axes.facecolor: white
|
|
12
|
+
|
|
13
|
+
# --- Font ---
|
|
14
|
+
font.size: 9
|
|
15
|
+
font.family: sans-serif
|
|
16
|
+
font.sans-serif: Helvetica, Arial, DejaVu Sans
|
|
17
|
+
text.color: black
|
|
18
|
+
axes.labelcolor: black
|
|
19
|
+
axes.titlecolor: black
|
|
20
|
+
|
|
21
|
+
# --- Ticks ---
|
|
22
|
+
xtick.direction: out
|
|
23
|
+
ytick.direction: out
|
|
24
|
+
xtick.major.size: 6
|
|
25
|
+
ytick.major.size: 6
|
|
26
|
+
xtick.major.width: 1.5
|
|
27
|
+
ytick.major.width: 1.5
|
|
28
|
+
xtick.minor.visible: False
|
|
29
|
+
ytick.minor.visible: False
|
|
30
|
+
xtick.bottom: True
|
|
31
|
+
ytick.left: True
|
|
32
|
+
xtick.labelsize: 8
|
|
33
|
+
ytick.labelsize: 8
|
|
34
|
+
xtick.color: black
|
|
35
|
+
ytick.color: black
|
|
36
|
+
|
|
37
|
+
# --- Grid ---
|
|
38
|
+
axes.grid: False
|
|
39
|
+
grid.linestyle: -
|
|
40
|
+
grid.linewidth: 0.5
|
|
41
|
+
grid.alpha: 0.3
|
|
42
|
+
grid.color: 0.6
|
|
43
|
+
|
|
44
|
+
# --- Figure ---
|
|
45
|
+
figure.dpi: 150
|
|
46
|
+
figure.figsize: 7.2, 5.0
|
|
47
|
+
figure.facecolor: white
|
|
48
|
+
figure.edgecolor: white
|
|
49
|
+
|
|
50
|
+
# --- Savefig ---
|
|
51
|
+
savefig.dpi: 300
|
|
52
|
+
savefig.bbox: tight
|
|
53
|
+
savefig.pad_inches: 0.05
|
|
54
|
+
savefig.format: pdf
|
|
55
|
+
savefig.facecolor: white
|
|
56
|
+
savefig.edgecolor: white
|
|
57
|
+
savefig.transparent: False
|
|
58
|
+
|
|
59
|
+
# --- Lines and markers ---
|
|
60
|
+
lines.linewidth: 1.5
|
|
61
|
+
lines.markersize: 4
|
|
62
|
+
|
|
63
|
+
# --- Legend ---
|
|
64
|
+
legend.frameon: True
|
|
65
|
+
legend.fontsize: 8
|
|
66
|
+
legend.loc: best
|
|
67
|
+
legend.facecolor: 0.97
|
|
68
|
+
legend.edgecolor: black
|
|
69
|
+
legend.framealpha: 0.8
|
|
70
|
+
legend.labelcolor: black
|
|
71
|
+
|
|
72
|
+
# --- Patches ---
|
|
73
|
+
patch.edgecolor: black
|
|
74
|
+
patch.force_edgecolor: True
|
|
75
|
+
|
|
76
|
+
# --- Color cycle ---
|
|
77
|
+
axes.prop_cycle: cycler(color=["#000000", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7"])
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# Copyright © Charité Universitätsmedizin Berlin. This software is licensed under the terms of the European Union Public Licence (EUPL) version 1.2 or later.
|
|
2
|
+
from __future__ import annotations
|
|
2
3
|
import os
|
|
3
4
|
import matplotlib.pyplot as plt
|
|
4
5
|
import nibabel
|
|
@@ -1043,6 +1044,8 @@ def plot_surf(
|
|
|
1043
1044
|
view="lateral",
|
|
1044
1045
|
cmap="viridis",
|
|
1045
1046
|
color=None, # Base surface color when no overlay
|
|
1047
|
+
shade_mode="auto", # "darken" (multiply), "lighten" (add white), or "auto"
|
|
1048
|
+
shade_intensity=1.0, # strength of the geometric shading (0 = none, 1 = full)
|
|
1046
1049
|
label=False,
|
|
1047
1050
|
vmax=None,
|
|
1048
1051
|
vmin=None,
|
|
@@ -1130,6 +1133,21 @@ def plot_surf(
|
|
|
1130
1133
|
(or *threshold_color*). When ``overlay_volume`` is used and
|
|
1131
1134
|
*sampling_threshold* is not set, *threshold* is also used as the
|
|
1132
1135
|
sampling threshold.
|
|
1136
|
+
shade_mode : {"auto", "darken", "lighten"}, default "auto"
|
|
1137
|
+
How geometric shading is applied to the base colour.
|
|
1138
|
+
``"darken"`` multiplies toward black (the classic behaviour; lit
|
|
1139
|
+
faces keep the base colour, shadowed faces darken). This shows no
|
|
1140
|
+
structure on near-black surfaces. ``"lighten"`` instead adds white
|
|
1141
|
+
toward the *lit* faces, so structure is visible on dark colours
|
|
1142
|
+
(e.g. ``color="k"``) while the apparent light source stays on the
|
|
1143
|
+
same side as ``"darken"``. ``"auto"`` picks ``"lighten"`` only when
|
|
1144
|
+
the base colour is very dark and ``"darken"`` otherwise.
|
|
1145
|
+
shade_intensity : float, default 1.0
|
|
1146
|
+
Strength of the geometric shading. ``0`` disables shading (flat base
|
|
1147
|
+
colour); ``1`` is full strength. In ``"lighten"`` mode it scales how
|
|
1148
|
+
much white is added to lit faces (lower = darker, more of the base
|
|
1149
|
+
colour shows through); in ``"darken"`` mode it scales how much
|
|
1150
|
+
shadowed faces are darkened.
|
|
1133
1151
|
"""
|
|
1134
1152
|
# Handle alpha as alias for transparency
|
|
1135
1153
|
if alpha is not None:
|
|
@@ -1867,8 +1885,35 @@ def plot_surf(
|
|
|
1867
1885
|
# Capture pre-shading face color for legend proxy (before shading darkens it)
|
|
1868
1886
|
_legend_color = C[0].copy() if (label and label is not False) else None
|
|
1869
1887
|
|
|
1870
|
-
# Apply lighting intensity to RGB channels
|
|
1871
|
-
|
|
1888
|
+
# Apply lighting intensity to RGB channels.
|
|
1889
|
+
#
|
|
1890
|
+
# intensity runs from (1 - shading) ≈ 0.3 on faces pointing away from
|
|
1891
|
+
# the light up to 1.0 on faces facing it. Two ways to render that:
|
|
1892
|
+
# "darken" – multiply toward black; shadows darken the base colour.
|
|
1893
|
+
# Looks right for bright colours but does nothing on
|
|
1894
|
+
# black (0 * anything == 0).
|
|
1895
|
+
# "lighten" – add white toward the *lit* faces (high intensity), so
|
|
1896
|
+
# structure shows up on dark surfaces while the light
|
|
1897
|
+
# source stays on the same side as the darken mode.
|
|
1898
|
+
_mode = shade_mode
|
|
1899
|
+
if _mode == "auto":
|
|
1900
|
+
# Lighten when the base colour is too dark for multiply to show
|
|
1901
|
+
# structure; otherwise keep the classic darken behaviour.
|
|
1902
|
+
_mode = "lighten" if float(np.max(C[:, 0:3])) < 0.25 else "darken"
|
|
1903
|
+
if _mode == "lighten":
|
|
1904
|
+
_t_min = float(np.min(intensity))
|
|
1905
|
+
_span = 1.0 - _t_min
|
|
1906
|
+
if _span == 0:
|
|
1907
|
+
_span = 1.0
|
|
1908
|
+
# hl: 0 in deepest shadow, 1 on fully-lit faces (same direction as
|
|
1909
|
+
# the multiply, just applied as a highlight rather than a shadow).
|
|
1910
|
+
# shade_intensity scales how much white is added (1 = full white).
|
|
1911
|
+
hl = ((intensity - _t_min) / _span)[:, None] * shade_intensity
|
|
1912
|
+
C[:, 0:3] = np.clip(C[:, 0:3] + (1.0 - C[:, 0:3]) * hl, 0.0, 1.0)
|
|
1913
|
+
else:
|
|
1914
|
+
# shade_intensity scales how much shadows darken (1 = full).
|
|
1915
|
+
eff = 1.0 - (1.0 - intensity) * shade_intensity
|
|
1916
|
+
C[:, 0:3] *= eff[:, None]
|
|
1872
1917
|
|
|
1873
1918
|
# Perspective MVP for robust drawing
|
|
1874
1919
|
MVP = (
|
|
@@ -2096,8 +2141,26 @@ def plot_surf(
|
|
|
2096
2141
|
is_below = np.any(add_overlay[add_F] <= threshold, axis=1)
|
|
2097
2142
|
add_C[is_below] = grey
|
|
2098
2143
|
|
|
2099
|
-
# Apply shading intensity
|
|
2100
|
-
|
|
2144
|
+
# Apply shading intensity (same darken/lighten logic as main)
|
|
2145
|
+
_add_mode = shade_mode
|
|
2146
|
+
if _add_mode == "auto":
|
|
2147
|
+
_add_mode = (
|
|
2148
|
+
"lighten"
|
|
2149
|
+
if float(np.max(add_C[:, 0:3])) < 0.25
|
|
2150
|
+
else "darken"
|
|
2151
|
+
)
|
|
2152
|
+
if _add_mode == "lighten":
|
|
2153
|
+
_t_min = float(np.min(add_intensity))
|
|
2154
|
+
_span = 1.0 - _t_min
|
|
2155
|
+
if _span == 0:
|
|
2156
|
+
_span = 1.0
|
|
2157
|
+
hl = ((add_intensity - _t_min) / _span)[:, None] * shade_intensity
|
|
2158
|
+
add_C[:, 0:3] = np.clip(
|
|
2159
|
+
add_C[:, 0:3] + (1.0 - add_C[:, 0:3]) * hl, 0.0, 1.0
|
|
2160
|
+
)
|
|
2161
|
+
else:
|
|
2162
|
+
eff = 1.0 - (1.0 - add_intensity) * shade_intensity
|
|
2163
|
+
add_C[:, 0:3] *= eff[:, None]
|
|
2101
2164
|
|
|
2102
2165
|
# Project vertices
|
|
2103
2166
|
add_V_all = np.c_[add_proj_verts, np.ones(len(add_proj_verts))] @ MVP.T
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Copyright © Charité Universitätsmedizin Berlin. This software is licensed under the terms of the European Union Public Licence (EUPL) version 1.2 or later.
|
|
2
2
|
"""Neuroimaging template volumes, downloaded on first access to ~/.bsplot/templates/."""
|
|
3
|
+
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import urllib.request
|
|
5
6
|
from pathlib import Path
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# Copyright © Charité Universitätsmedizin Berlin. This software is licensed under the terms of the European Union Public Licence (EUPL) version 1.2 or later.
|
|
2
|
+
from __future__ import annotations
|
|
2
3
|
import black
|
|
3
4
|
import matplotlib.pyplot as plt
|
|
4
5
|
from pygments.lexers import PythonLexer, get_lexer_by_name
|
|
@@ -21,6 +21,8 @@ bsplot/utils.py
|
|
|
21
21
|
bsplot/volume.py
|
|
22
22
|
bsplot/data/HCP_avg-SC.txt
|
|
23
23
|
bsplot/data/MNI152.rh.pial
|
|
24
|
+
bsplot/data/STN_lh.nii.gz
|
|
25
|
+
bsplot/data/STN_rh.nii.gz
|
|
24
26
|
bsplot/data/__init__.py
|
|
25
27
|
bsplot/data/surface.py
|
|
26
28
|
bsplot/data/parcellations/HCP-MMP1.L.label.gii
|
|
@@ -77,5 +79,7 @@ bsplot/styles/black.mplstyle
|
|
|
77
79
|
bsplot/styles/bss.mplstyle
|
|
78
80
|
bsplot/styles/bwcomp.mplstyle
|
|
79
81
|
bsplot/styles/nature.mplstyle
|
|
82
|
+
bsplot/styles/template.mplstyle
|
|
80
83
|
bsplot/styles/transparent.mplstyle
|
|
81
|
-
bsplot/styles/tvbo.mplstyle
|
|
84
|
+
bsplot/styles/tvbo.mplstyle
|
|
85
|
+
bsplot/styles/white.mplstyle
|
|
@@ -23,7 +23,7 @@ dependencies = [
|
|
|
23
23
|
]
|
|
24
24
|
|
|
25
25
|
[project.optional-dependencies]
|
|
26
|
-
docs = ["quarto", "quartodoc", "griffe<1.0", "jupyter-cache"]
|
|
26
|
+
docs = ["quarto", "quartodoc", "griffe<1.0", "jupyter-cache", "cairosvg"]
|
|
27
27
|
|
|
28
28
|
[project.urls]
|
|
29
29
|
Repository = "https://github.com/leon-k-martin/bsplot"
|
|
@@ -42,6 +42,8 @@ include = ["bsplot*"]
|
|
|
42
42
|
"styles/*.mplstyle",
|
|
43
43
|
"data/HCP_avg-SC.txt",
|
|
44
44
|
"data/MNI152.rh.pial",
|
|
45
|
+
"data/STN_lh.nii.gz",
|
|
46
|
+
"data/STN_rh.nii.gz",
|
|
45
47
|
"data/parcellations/HCP-MMP1.L.label.gii",
|
|
46
48
|
"data/parcellations/HCP-MMP1.R.label.gii",
|
|
47
49
|
"scientific_color_maps/*",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|