mpl-spaceplot 0.1.1__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.
Files changed (29) hide show
  1. mpl_spaceplot-0.1.1/LICENSE +21 -0
  2. mpl_spaceplot-0.1.1/PKG-INFO +18 -0
  3. mpl_spaceplot-0.1.1/README.md +1 -0
  4. mpl_spaceplot-0.1.1/pyproject.toml +50 -0
  5. mpl_spaceplot-0.1.1/setup.cfg +4 -0
  6. mpl_spaceplot-0.1.1/src/mpl_spaceplot.egg-info/PKG-INFO +18 -0
  7. mpl_spaceplot-0.1.1/src/mpl_spaceplot.egg-info/SOURCES.txt +27 -0
  8. mpl_spaceplot-0.1.1/src/mpl_spaceplot.egg-info/dependency_links.txt +1 -0
  9. mpl_spaceplot-0.1.1/src/mpl_spaceplot.egg-info/requires.txt +2 -0
  10. mpl_spaceplot-0.1.1/src/mpl_spaceplot.egg-info/top_level.txt +1 -0
  11. mpl_spaceplot-0.1.1/src/spaceplot/__init__.py +25 -0
  12. mpl_spaceplot-0.1.1/src/spaceplot/aligner.py +112 -0
  13. mpl_spaceplot-0.1.1/src/spaceplot/appearance/__init__.py +8 -0
  14. mpl_spaceplot-0.1.1/src/spaceplot/appearance/display.py +178 -0
  15. mpl_spaceplot-0.1.1/src/spaceplot/appearance/inline.py +45 -0
  16. mpl_spaceplot-0.1.1/src/spaceplot/appearance/layout.py +237 -0
  17. mpl_spaceplot-0.1.1/src/spaceplot/appearance/palettes.py +402 -0
  18. mpl_spaceplot-0.1.1/src/spaceplot/appearance/styles.py +58 -0
  19. mpl_spaceplot-0.1.1/src/spaceplot/appearance/tools.py +233 -0
  20. mpl_spaceplot-0.1.1/src/spaceplot/decorators/__init__.py +4 -0
  21. mpl_spaceplot-0.1.1/src/spaceplot/decorators/decorators.py +457 -0
  22. mpl_spaceplot-0.1.1/src/spaceplot/decorators/tools.py +88 -0
  23. mpl_spaceplot-0.1.1/src/spaceplot/montage_plot.py +295 -0
  24. mpl_spaceplot-0.1.1/src/spaceplot/plotting/__init__.py +7 -0
  25. mpl_spaceplot-0.1.1/src/spaceplot/plotting/plotting.py +224 -0
  26. mpl_spaceplot-0.1.1/src/spaceplot/plotting/tools.py +75 -0
  27. mpl_spaceplot-0.1.1/src/spaceplot/resources/font_loader.py +11 -0
  28. mpl_spaceplot-0.1.1/src/spaceplot/simulate_data.py +85 -0
  29. mpl_spaceplot-0.1.1/src/spaceplot/utils.py +48 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Florian Raths
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: mpl-spaceplot
3
+ Version: 0.1.1
4
+ Summary: layout wrappers for matplotlib
5
+ Author-email: Florian Raths <raths.f@gmail.com>
6
+ License: MIT
7
+ Keywords: matplotlib,plotting,wrappers
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: matplotlib>=3.7
15
+ Requires-Dist: cmcrameri>=1.9
16
+ Dynamic: license-file
17
+
18
+ # spaceplot
@@ -0,0 +1 @@
1
+ # spaceplot
@@ -0,0 +1,50 @@
1
+ # [build-system]
2
+ # requires = ["uv_build"]
3
+ # build-backend = "uv_build"
4
+
5
+ [build-system]
6
+ requires = ["setuptools"]
7
+ build-backend = "setuptools.build_meta"
8
+
9
+ [project]
10
+ name = "mpl-spaceplot"
11
+ version = "0.1.1"
12
+ description = "layout wrappers for matplotlib"
13
+ keywords = ["matplotlib", "plotting", "wrappers"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+
20
+ authors = [{ name = "Florian Raths", email = "raths.f@gmail.com" }]
21
+ license = { text = "MIT" }
22
+ readme = "README.md"
23
+
24
+ requires-python = ">=3.10"
25
+
26
+ dependencies = ["matplotlib >= 3.7", "cmcrameri >= 1.9"]
27
+
28
+ [dependency-groups]
29
+ dev = [
30
+ "ipykernel >= 6.30.1",
31
+ "ipywidgets >= 8.1.7",
32
+ "pre-commit >= 4.3.0",
33
+ "ruff == 0.13.1",
34
+ "jupytext>=1.18.0",
35
+ ]
36
+ docs = ["mkdocs ~= 1.6", "mkdocs-material ~= 9.6"]
37
+
38
+ [tool.uv]
39
+ required-version = ">=0.7.0"
40
+ default-groups = ["dev", "docs"]
41
+
42
+
43
+ [tool.uv-ship]
44
+ release-branch = "main"
45
+ allow-dirty = false
46
+
47
+
48
+ [tool.setuptools.packages.find]
49
+ where = ["src"]
50
+ include = ["spaceplot*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: mpl-spaceplot
3
+ Version: 0.1.1
4
+ Summary: layout wrappers for matplotlib
5
+ Author-email: Florian Raths <raths.f@gmail.com>
6
+ License: MIT
7
+ Keywords: matplotlib,plotting,wrappers
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: matplotlib>=3.7
15
+ Requires-Dist: cmcrameri>=1.9
16
+ Dynamic: license-file
17
+
18
+ # spaceplot
@@ -0,0 +1,27 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/mpl_spaceplot.egg-info/PKG-INFO
5
+ src/mpl_spaceplot.egg-info/SOURCES.txt
6
+ src/mpl_spaceplot.egg-info/dependency_links.txt
7
+ src/mpl_spaceplot.egg-info/requires.txt
8
+ src/mpl_spaceplot.egg-info/top_level.txt
9
+ src/spaceplot/__init__.py
10
+ src/spaceplot/aligner.py
11
+ src/spaceplot/montage_plot.py
12
+ src/spaceplot/simulate_data.py
13
+ src/spaceplot/utils.py
14
+ src/spaceplot/appearance/__init__.py
15
+ src/spaceplot/appearance/display.py
16
+ src/spaceplot/appearance/inline.py
17
+ src/spaceplot/appearance/layout.py
18
+ src/spaceplot/appearance/palettes.py
19
+ src/spaceplot/appearance/styles.py
20
+ src/spaceplot/appearance/tools.py
21
+ src/spaceplot/decorators/__init__.py
22
+ src/spaceplot/decorators/decorators.py
23
+ src/spaceplot/decorators/tools.py
24
+ src/spaceplot/plotting/__init__.py
25
+ src/spaceplot/plotting/plotting.py
26
+ src/spaceplot/plotting/tools.py
27
+ src/spaceplot/resources/font_loader.py
@@ -0,0 +1,2 @@
1
+ matplotlib>=3.7
2
+ cmcrameri>=1.9
@@ -0,0 +1,25 @@
1
+ import matplotlib.pyplot as plt
2
+
3
+ from . import aligner
4
+ from . import decorators as decs
5
+ from . import utils as ut
6
+ from .appearance import palettes as plts
7
+ from .appearance.display import Theme, display
8
+ from .appearance.layout import layout
9
+ from .montage_plot import montage_plot
10
+ from .plotting import plt_category, plt_continous, plt_image
11
+
12
+ __all__ = [
13
+ 'aligner',
14
+ 'decs',
15
+ 'ut',
16
+ 'plts',
17
+ 'Theme',
18
+ 'display',
19
+ 'layout',
20
+ 'montage_plot',
21
+ 'plt_category',
22
+ 'plt_continous',
23
+ 'plt_image',
24
+ 'plt',
25
+ ]
@@ -0,0 +1,112 @@
1
+ from typing import Literal
2
+
3
+ import numpy as np
4
+
5
+ aligns = Literal[
6
+ 'top_left',
7
+ 'top_right',
8
+ 'bottom_left',
9
+ 'bottom_right',
10
+ 'center_left',
11
+ 'center_right',
12
+ 'center_top',
13
+ 'center_bottom',
14
+ ]
15
+
16
+ align_map = {
17
+ 'c': 0.5,
18
+ 'l': 0,
19
+ 'r': 1,
20
+ 'b': 0,
21
+ 't': 1,
22
+ }
23
+
24
+ align_full_names = {
25
+ 'c': 'center',
26
+ 'l': 'left',
27
+ 'r': 'right',
28
+ 'b': 'bottom',
29
+ 't': 'top',
30
+ }
31
+
32
+
33
+ def translate_align(how: aligns, format: str = 'frac', xfact: float = None, yfact: float = None) -> tuple[float, float]:
34
+ if how is None and (xfact is None or yfact is None):
35
+ raise ValueError("Either 'how' must be provided or both 'xfact' and 'yfact' must be specified.")
36
+
37
+ if how is not None:
38
+ x_a, y_a = parse_alignment(how)
39
+ else:
40
+ x_a, y_a = 'c', 'c'
41
+
42
+ x = xfact if xfact else align_map[x_a]
43
+ y = yfact if yfact else align_map[y_a]
44
+
45
+ if format == 'name':
46
+ x = align_full_names[x_a] if xfact is None else xfact
47
+ y = align_full_names[y_a] if yfact is None else yfact
48
+
49
+ return x, y
50
+
51
+
52
+ def parse_alignment(inpt: aligns) -> tuple[aligns, aligns]:
53
+ """reutrns a tuple of (x, y) alignment based on the input string."""
54
+
55
+ def get_inpt_type(inpt):
56
+ if inpt in ['l', 'r']:
57
+ inpt_type = 'horizontal'
58
+ elif inpt in ['t', 'b']:
59
+ inpt_type = 'vertical'
60
+ else:
61
+ inpt_type = 'centered'
62
+ return inpt_type
63
+
64
+ if len(inpt) > 2:
65
+ if '_' in inpt:
66
+ parts = inpt.split('_')
67
+
68
+ if len(parts) == 2:
69
+ inpt = parts[0][0] + parts[1][0]
70
+ else:
71
+ raise ValueError('Input must be a two-part string separated by an underscore.')
72
+ else:
73
+ inpt = inpt[0]
74
+
75
+ x = y = None
76
+ if len(inpt) == 1:
77
+ in_type = get_inpt_type(inpt)
78
+ if in_type == 'horizontal':
79
+ x, y = inpt, 'c'
80
+ elif in_type == 'vertical':
81
+ x, y = 'c', inpt
82
+ else:
83
+ x = y = 'c'
84
+
85
+ elif len(inpt) == 2:
86
+ in_type_a = get_inpt_type(inpt[0])
87
+ in_type_b = get_inpt_type(inpt[1])
88
+
89
+ types = np.array([in_type_a, in_type_b])
90
+
91
+ if in_type_a == in_type_b:
92
+ raise ValueError('Both inputs cannot be of the same kind.')
93
+
94
+ h_idx = np.where(types == 'horizontal')[0]
95
+ v_idx = np.where(types == 'vertical')[0]
96
+
97
+ if len(h_idx) == 0:
98
+ x = 'c'
99
+ elif len(h_idx) == 1:
100
+ x = inpt[h_idx[0]]
101
+ else:
102
+ raise ValueError('More than one horizontal input provided.')
103
+ if len(v_idx) == 0:
104
+ y = 'c'
105
+ elif len(v_idx) == 1:
106
+ y = inpt[v_idx[0]]
107
+ else:
108
+ raise ValueError('More than one vertical input provided.')
109
+ else:
110
+ raise ValueError('Input must be a single character or a two-character string.')
111
+
112
+ return x, y
@@ -0,0 +1,8 @@
1
+ import importlib.resources as resources
2
+
3
+ from ..resources.font_loader import register_fonts
4
+
5
+ # register fonts supplied with package
6
+ path = resources.files('spaceplot.resources.fonts')
7
+ for cont in path.iterdir():
8
+ register_fonts(cont)
@@ -0,0 +1,178 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+ import matplotlib.pyplot as plt
6
+
7
+ from .. import utils
8
+ from . import inline, styles, tools
9
+
10
+
11
+ def display(
12
+ theme: str | Theme = 'default',
13
+ **kwargs,
14
+ ):
15
+ theme = Theme(source_theme=theme, **kwargs) if isinstance(theme, str) else theme
16
+ theme.apply()
17
+
18
+
19
+ @dataclass
20
+ class Theme:
21
+ source_theme: str = None
22
+ explicit_rcParams: dict = None
23
+ retina: bool = False
24
+ transparent: bool = False
25
+ figsize: tuple[float, float] = None
26
+ dpi: int = None
27
+ palette: list[str] = None
28
+ cmap: str = None
29
+ text_color: str = None
30
+ line_color: str = None
31
+ ticks: list[str] = None
32
+ minor_visible: bool = None
33
+ spines: bool = None
34
+ margins: float = None
35
+ grid: bool = None
36
+ grid_color: str = None
37
+ grid_alpha: float = None
38
+ grid_linestyle: str = None
39
+ grid_linewidth: float = None
40
+ tick_linewidth: tuple[float, float] = (None, None)
41
+ tick_pad: tuple[float, float] = (None, None)
42
+ spine_linewidth: float = None
43
+ tick_size: tuple[float, float] = (None, None)
44
+ font_family: str = None
45
+ font_size: int = None
46
+ fig_facecolor: str = None
47
+ axes_facecolor: str = None
48
+ labelsize: dict = field(default_factory=lambda: {'axes': None, 'figure': None, 'ticks': None})
49
+ titlesize: dict = field(default_factory=lambda: {'axes': None, 'figure': None})
50
+ titleweight: dict = field(default_factory=lambda: {'axes': None, 'figure': None})
51
+ labelweight: dict = field(default_factory=lambda: {'axes': None, 'figure': None})
52
+ axes_labelpad: float = None
53
+ axes_titlepad: float = None
54
+ inline_config: dict = field(default_factory=dict)
55
+
56
+ def parse_source_theme(self):
57
+ if isinstance(self.source_theme, str):
58
+ base_theme = styles.themes.get(self.source_theme, {})
59
+ return Theme(source_theme=base_theme)
60
+
61
+ elif isinstance(self.source_theme, dict):
62
+ return Theme(**self.source_theme)
63
+
64
+ else:
65
+ return self.source_theme
66
+
67
+ def reset_defaults(self):
68
+ plt.rcdefaults()
69
+
70
+ @property
71
+ def rcDict(self):
72
+ from cycler import cycler
73
+
74
+ tick_unset_val = None if self.ticks is None else False
75
+ tick_t, tick_b, tick_l, tick_r = tools.set_position(positions=self.ticks, unset_value=tick_unset_val)
76
+
77
+ spine_unset_val = None if self.spines is None else False
78
+ spine_t, spine_b, spine_l, spine_r = tools.set_position(positions=self.spines, unset_value=spine_unset_val)
79
+
80
+ prop_cycle = None if self.palette is None else cycler('color', self.palette)
81
+
82
+ rc_dict = {
83
+ key: value
84
+ for key, value in {
85
+ 'figure.figsize': self.figsize,
86
+ 'figure.dpi': self.dpi,
87
+ 'axes.prop_cycle': prop_cycle,
88
+ 'image.cmap': self.cmap,
89
+ 'xtick.color': self.line_color,
90
+ 'ytick.color': self.line_color,
91
+ 'axes.grid': self.grid,
92
+ 'axes3d.grid': self.grid,
93
+ 'polaraxes.grid': self.grid,
94
+ 'grid.alpha': self.grid_alpha,
95
+ 'grid.color': self.grid_color,
96
+ 'grid.linestyle': self.grid_linestyle,
97
+ 'grid.linewidth': self.grid_linewidth,
98
+ 'text.color': self.text_color,
99
+ 'axes.labelcolor': self.text_color,
100
+ 'axes.titlecolor': self.text_color,
101
+ 'ytick.labelcolor': self.text_color,
102
+ 'xtick.labelcolor': self.text_color,
103
+ 'axes.edgecolor': self.line_color,
104
+ 'axes.facecolor': self.axes_facecolor,
105
+ 'figure.facecolor': self.fig_facecolor,
106
+ 'font.family': self.font_family,
107
+ 'font.size': self.font_size,
108
+ 'figure.titlesize': self.titlesize.get('figure', None),
109
+ 'axes.titlesize': self.titlesize.get('axes', None),
110
+ 'figure.titleweight': self.titleweight.get('figure', None),
111
+ 'axes.titleweight': self.titleweight.get('axes', None),
112
+ 'figure.labelsize': self.labelsize.get('figure', None),
113
+ 'axes.labelsize': self.labelsize.get('axes', None),
114
+ 'xtick.labelsize': self.labelsize.get('ticks', None),
115
+ 'ytick.labelsize': self.labelsize.get('ticks', None),
116
+ 'axes.labelpad': self.axes_labelpad,
117
+ 'axes.titlepad': self.axes_titlepad,
118
+ 'figure.labelweight': self.labelweight.get('figure', None),
119
+ 'axes.labelweight': self.labelweight.get('axes', None),
120
+ 'axes.linewidth': self.spine_linewidth,
121
+ 'axes.xmargin': self.margins,
122
+ 'axes.ymargin': self.margins,
123
+ 'axes.zmargin': self.margins,
124
+ 'axes.spines.top': spine_t,
125
+ 'axes.spines.bottom': spine_b,
126
+ 'axes.spines.left': spine_l,
127
+ 'axes.spines.right': spine_r,
128
+ 'xtick.top': tick_t,
129
+ 'xtick.bottom': tick_b,
130
+ 'ytick.left': tick_l,
131
+ 'ytick.right': tick_r,
132
+ 'ytick.major.left': tick_l,
133
+ 'ytick.major.right': tick_r,
134
+ 'xtick.major.top': tick_t,
135
+ 'xtick.major.bottom': tick_b,
136
+ 'ytick.minor.left': tick_l,
137
+ 'ytick.minor.right': tick_r,
138
+ 'xtick.minor.top': tick_t,
139
+ 'xtick.minor.bottom': tick_b,
140
+ 'ytick.labelleft': tick_l,
141
+ 'ytick.labelright': tick_r,
142
+ 'xtick.labeltop': tick_t,
143
+ 'xtick.labelbottom': tick_b,
144
+ 'xtick.major.pad': utils.maj_min_args(self.tick_pad)[0],
145
+ 'xtick.minor.pad': utils.maj_min_args(self.tick_pad)[1],
146
+ 'ytick.major.pad': utils.maj_min_args(self.tick_pad)[0],
147
+ 'ytick.minor.pad': utils.maj_min_args(self.tick_pad)[1],
148
+ 'xtick.major.size': utils.maj_min_args(self.tick_size)[0],
149
+ 'xtick.minor.size': utils.maj_min_args(self.tick_size)[1],
150
+ 'ytick.major.size': utils.maj_min_args(self.tick_size)[0],
151
+ 'ytick.minor.size': utils.maj_min_args(self.tick_size)[1],
152
+ 'xtick.major.width': utils.maj_min_args(self.tick_linewidth)[0],
153
+ 'xtick.minor.width': utils.maj_min_args(self.tick_linewidth)[1],
154
+ 'ytick.major.width': utils.maj_min_args(self.tick_linewidth)[0],
155
+ 'ytick.minor.width': utils.maj_min_args(self.tick_linewidth)[1],
156
+ 'ytick.minor.visible': self.minor_visible,
157
+ 'xtick.minor.visible': self.minor_visible,
158
+ }.items()
159
+ if value is not None
160
+ }
161
+
162
+ if self.explicit_rcParams is not None:
163
+ rc_dict.update(self.explicit_rcParams)
164
+
165
+ if self.source_theme is not None:
166
+ source = self.parse_source_theme()
167
+ rc_dict = {**source.rcDict, **rc_dict}
168
+
169
+ return rc_dict
170
+
171
+ def apply(self):
172
+ if self.source_theme == 'default':
173
+ print('Theme: resetting to matplotlib defaults')
174
+ plt.rcdefaults()
175
+
176
+ plt.rcParams.update(self.rcDict)
177
+
178
+ inline.inline_config(retina=self.retina, transparent=self.transparent, **self.inline_config)
@@ -0,0 +1,45 @@
1
+ import matplotlib_inline.backend_inline as mpl_inline
2
+ from matplotlib import rcParams
3
+
4
+ rc_mapping = {
5
+ 'dpi': 'figure.dpi',
6
+ 'pad_inches': 'savefig.pad_inches',
7
+ 'facecolor': 'figure.facecolor',
8
+ 'bbox_inches': 'savefig.bbox',
9
+ }
10
+
11
+
12
+ def from_rc(key):
13
+ return rcParams[rc_mapping[key]] if key in rc_mapping else None
14
+
15
+
16
+ def inline_config(
17
+ retina: bool = None,
18
+ facecolor: str = 'rc',
19
+ dpi: int | str = 'rc',
20
+ pad_inches: float | str = 'rc',
21
+ bbox_inches: float | str = 'tight',
22
+ transparent: bool = False,
23
+ **kwargs,
24
+ ):
25
+ dpi = from_rc('dpi') if dpi == 'rc' else dpi
26
+ pad_inches = from_rc('pad_inches') if pad_inches == 'rc' else pad_inches
27
+ bbox_inches = from_rc('bbox_inches') if bbox_inches == 'rc' else bbox_inches
28
+ facecolor = from_rc('facecolor') if facecolor == 'rc' else facecolor
29
+
30
+ facecolor = 'none' if transparent else facecolor
31
+
32
+ if retina:
33
+ inl_format = 'retina'
34
+ dpi = dpi * 2
35
+ else:
36
+ inl_format = 'png'
37
+
38
+ mpl_inline.set_matplotlib_formats(
39
+ inl_format,
40
+ facecolor=facecolor,
41
+ bbox_inches=bbox_inches,
42
+ dpi=dpi,
43
+ pad_inches=pad_inches,
44
+ **kwargs,
45
+ )