gsplot 0.0.5__tar.gz → 0.1.0__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 (33) hide show
  1. {gsplot-0.0.5 → gsplot-0.1.0}/PKG-INFO +7 -4
  2. {gsplot-0.0.5 → gsplot-0.1.0}/README.md +6 -3
  3. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/__init__.py +5 -1
  4. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/config/config.py +179 -2
  5. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/figure/axes.py +2 -2
  6. gsplot-0.1.0/gsplot/figure/axes_base.py +288 -0
  7. gsplot-0.0.5/gsplot/figure/axes_base.py → gsplot-0.1.0/gsplot/figure/axes_range_base.py +108 -390
  8. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/logger.py +2 -0
  9. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/plot/line.py +12 -15
  10. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/plot/line_base.py +41 -70
  11. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/plot/line_colormap_dashed.py +22 -25
  12. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/plot/line_colormap_solid.py +13 -22
  13. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/plot/scatter.py +18 -23
  14. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/plot/scatter_colormap.py +16 -24
  15. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/style/graph.py +44 -49
  16. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/style/label.py +73 -70
  17. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/style/legend.py +26 -44
  18. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/style/legend_colormap.py +13 -22
  19. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/style/ticks.py +22 -28
  20. gsplot-0.1.0/gsplot/version.py +2 -0
  21. {gsplot-0.0.5 → gsplot-0.1.0}/pyproject.toml +1 -1
  22. gsplot-0.0.5/gsplot/version.py +0 -2
  23. {gsplot-0.0.5 → gsplot-0.1.0}/LICENSE +0 -0
  24. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/base/base.py +0 -0
  25. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/base/base_alias_validator.py +0 -0
  26. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/color/colormap.py +0 -0
  27. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/data/load_file.py +0 -0
  28. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/figure/figure_tools.py +0 -0
  29. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/figure/show.py +0 -0
  30. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/figure/store.py +0 -0
  31. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/hello_world/hello_world.py +0 -0
  32. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/path/path.py +0 -0
  33. {gsplot-0.0.5 → gsplot-0.1.0}/gsplot/plot/line_colormap_base.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gsplot
3
- Version: 0.0.5
3
+ Version: 0.1.0
4
4
  Summary: General-scientific plot based on matplotlib
5
5
  Author: Giordano Mattoni
6
6
  Author-email: mattoni@scphys.kyoto-u.ac.jp
@@ -32,13 +32,16 @@ Description-Content-Type: text/markdown
32
32
  ----------------
33
33
 
34
34
  <p align="center" style="font-weight: bold; font-size: 1.2em; margin: 20px 0;">
35
- <a href="https://soichiroyamane.github.io/gsplot/" style="text-decoration: none;">Docs</a> |
35
+ <a href="https://soichiroyamane.github.io/gsplot/stable" style="text-decoration: none;">Docs</a> |
36
36
  <a href="#authors" style="text-decoration: none;">Authors</a> |
37
37
  <a href="#license" style="text-decoration: none;">License</a>
38
38
  </p>
39
39
 
40
40
  Welcome to **gsplot** (general-scientific plot), a toolkit designed to enhance the capabilities of data visualization based on [matplotlib](https://matplotlib.org). This package is specifically tailored for creating high-quality figures aimed at the scientific field.
41
41
 
42
+ > [!WARNING]
43
+ > This package is _beta_ quality. Expect breaking changes and many bugs 🐛. Please report any issue you encounter 🤝.
44
+
42
45
  ## ✨ Features
43
46
 
44
47
  - **Better Plot, Less Code**: Simplify the process of creating high-quality figures ⚛️
@@ -48,13 +51,13 @@ Welcome to **gsplot** (general-scientific plot), a toolkit designed to enhance t
48
51
 
49
52
  ### 📈 Example using gsplot
50
53
 
51
- [See more details](https://soichiroyamane.github.io/gsplot/guides/demo/4_paper_plot.html)
54
+ [See more details](https://soichiroyamane.github.io/gsplot/guides/stable/demo/4_paper_plot.html)
52
55
 
53
56
  ![example](docs/_static/tutorial/SC_cal.png)
54
57
 
55
58
  ### 🌃 Example with Python REPL 🐍 and neovim 🌟
56
59
 
57
- [See more details](https://soichiroyamane.github.io/gsplot/guides/demo/13_REPL.html)
60
+ [See more details](https://soichiroyamane.github.io/gsplot/stable/guides/demo/13_REPL.html)
58
61
 
59
62
  ![repl_tutorial](./docs/_static/tutorial/repl_tutorial_sp.gif)
60
63
 
@@ -12,13 +12,16 @@
12
12
  ----------------
13
13
 
14
14
  <p align="center" style="font-weight: bold; font-size: 1.2em; margin: 20px 0;">
15
- <a href="https://soichiroyamane.github.io/gsplot/" style="text-decoration: none;">Docs</a> |
15
+ <a href="https://soichiroyamane.github.io/gsplot/stable" style="text-decoration: none;">Docs</a> |
16
16
  <a href="#authors" style="text-decoration: none;">Authors</a> |
17
17
  <a href="#license" style="text-decoration: none;">License</a>
18
18
  </p>
19
19
 
20
20
  Welcome to **gsplot** (general-scientific plot), a toolkit designed to enhance the capabilities of data visualization based on [matplotlib](https://matplotlib.org). This package is specifically tailored for creating high-quality figures aimed at the scientific field.
21
21
 
22
+ > [!WARNING]
23
+ > This package is _beta_ quality. Expect breaking changes and many bugs 🐛. Please report any issue you encounter 🤝.
24
+
22
25
  ## ✨ Features
23
26
 
24
27
  - **Better Plot, Less Code**: Simplify the process of creating high-quality figures ⚛️
@@ -28,13 +31,13 @@ Welcome to **gsplot** (general-scientific plot), a toolkit designed to enhance t
28
31
 
29
32
  ### 📈 Example using gsplot
30
33
 
31
- [See more details](https://soichiroyamane.github.io/gsplot/guides/demo/4_paper_plot.html)
34
+ [See more details](https://soichiroyamane.github.io/gsplot/guides/stable/demo/4_paper_plot.html)
32
35
 
33
36
  ![example](docs/_static/tutorial/SC_cal.png)
34
37
 
35
38
  ### 🌃 Example with Python REPL 🐍 and neovim 🌟
36
39
 
37
- [See more details](https://soichiroyamane.github.io/gsplot/guides/demo/13_REPL.html)
40
+ [See more details](https://soichiroyamane.github.io/gsplot/stable/guides/demo/13_REPL.html)
38
41
 
39
42
  ![repl_tutorial](./docs/_static/tutorial/repl_tutorial_sp.gif)
40
43
 
@@ -1,6 +1,6 @@
1
1
  from .color.colormap import get_cmap
2
2
  from .config.config import (Config, config_dict, config_entry_option,
3
- config_load)
3
+ config_load, save_metadata)
4
4
  from .data.load_file import load_file
5
5
  from .figure.axes import axes
6
6
  from .figure.figure_tools import get_figure_size
@@ -33,6 +33,10 @@ Config()
33
33
  # ╰──────────────────────────────────────────────────────────╯
34
34
  logger()
35
35
 
36
+ # ╭──────────────────────────────────────────────────────────╮
37
+ # │ Save Metadata │
38
+ # ╰──────────────────────────────────────────────────────────╯
39
+ save_metadata()
36
40
 
37
41
  __version__ = __version__
38
42
  __commit__ = __commit__
@@ -2,13 +2,18 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
  import os
5
+ from datetime import datetime
5
6
  from threading import Lock
6
7
  from typing import Any, cast
7
8
 
8
9
  import matplotlib as mpl
10
+ import yaml
9
11
  from matplotlib import rcParams
10
12
  from rich.traceback import install
11
13
 
14
+ from ..path.path import PathToMain
15
+ from ..version import __commit__, __version__
16
+
12
17
  rcParams["pdf.fonttype"] = 42
13
18
  rcParams["ps.fonttype"] = 42
14
19
 
@@ -148,9 +153,14 @@ class Config:
148
153
  loader.init_load() if config_path else loader.get_config()
149
154
  )
150
155
  self.config_dict = config_dict
156
+
157
+ # Save metadata
158
+ metadata_store = MetadataStore()
159
+ metadata_store.create_metadata()
160
+
151
161
  return config_dict
152
162
 
153
- def get_config_entry_option(self, key: str) -> dict[str, Any]:
163
+ def get_config_entry_option(self, key: str) -> Any | dict[str, Any]:
154
164
  """
155
165
  Retrieves a specific entry from the configuration dictionary.
156
166
 
@@ -161,7 +171,7 @@ class Config:
161
171
 
162
172
  Returns
163
173
  --------------------
164
- dict of str, Any
174
+ Any and dict of str, Any
165
175
  The configuration entry corresponding to the provided key.
166
176
 
167
177
  Examples
@@ -420,3 +430,170 @@ def config_entry_option(key: str) -> dict[str, Any]:
420
430
  _config: Config = Config()
421
431
  entry_option: dict[str, Any] = _config.get_config_entry_option(key)
422
432
  return entry_option
433
+
434
+
435
+ class MetadataHistory:
436
+ def __init__(
437
+ self,
438
+ new_metadata: Any,
439
+ new_config: Any,
440
+ metadata_dir: str,
441
+ ) -> None:
442
+ self.new_metadata = new_metadata.copy()
443
+ self.new_config = new_config.copy()
444
+ self.metadata_dir = metadata_dir
445
+
446
+ self.needs_update = False
447
+
448
+ self.history: Any = {}
449
+ self.new_entry: Any = {}
450
+ self.history_dir = os.path.join(self.metadata_dir, "history")
451
+
452
+ def _get_old_metadata(self) -> None | Any:
453
+ if not os.path.exists(os.path.join(self.metadata_dir, "metadata.yml")):
454
+ return None
455
+
456
+ with open(os.path.join(self.metadata_dir, "metadata.yml"), "r") as file:
457
+ return yaml.safe_load(file)
458
+
459
+ def _get_old_config(self) -> None | Any:
460
+ if not os.path.exists(os.path.join(self.metadata_dir, "config.json")):
461
+ return None
462
+
463
+ with open(os.path.join(self.metadata_dir, "config.json"), "r") as file:
464
+ return json.load(file)
465
+
466
+ def _is_identical(self) -> None:
467
+ old_metadata = self._get_old_metadata()
468
+ old_config = self._get_old_config()
469
+
470
+ if not old_metadata or not old_config:
471
+ self.needs_update = True
472
+ return None
473
+
474
+ exclude_keys = ["date"]
475
+
476
+ def remove_keys(data: dict, keys: list) -> dict:
477
+ return {k: v for k, v in data.items() if k not in keys}
478
+
479
+ filtered_old_metadata = remove_keys(old_metadata, exclude_keys)
480
+ filtered_new_metadata = remove_keys(self.new_metadata, exclude_keys)
481
+
482
+ if filtered_old_metadata != filtered_new_metadata:
483
+ self.needs_update = True
484
+ return None
485
+
486
+ if old_config != self.new_config:
487
+ self.needs_update = True
488
+ return None
489
+
490
+ def _create_history_dir(self) -> None:
491
+ metadata_history_dir = os.path.join(self.history_dir)
492
+
493
+ if not os.path.exists(metadata_history_dir):
494
+ os.makedirs(metadata_history_dir)
495
+
496
+ def _read_history(self) -> Any:
497
+ self._create_history_dir()
498
+ history_file = os.path.join(self.history_dir, "history.txt")
499
+
500
+ if not os.path.exists(history_file):
501
+ return []
502
+
503
+ try:
504
+ with open(history_file, "r") as file:
505
+ return [json.loads(line) for line in file if line.strip()]
506
+ except Exception as e:
507
+ print(f"Error reading history file: {e}")
508
+ return []
509
+
510
+ def _create_new_history(self) -> None:
511
+ if not self.needs_update:
512
+ return None
513
+
514
+ self.history = self._read_history()
515
+
516
+ self.new_entry = self.new_metadata
517
+ self.new_entry["config"] = self.new_config
518
+
519
+ def _write_history(self) -> None:
520
+ history_file = os.path.join(self.history_dir, "history.txt")
521
+
522
+ with open(history_file, "a") as file:
523
+ json.dump(self.new_entry, file)
524
+ file.write("\n")
525
+
526
+ def create_history(self) -> None:
527
+ self._is_identical()
528
+ self._create_new_history()
529
+ if self.new_entry:
530
+ self._write_history()
531
+
532
+
533
+ class MetadataStore:
534
+ def __init__(
535
+ self,
536
+ ) -> None:
537
+ path_to_main = PathToMain()
538
+ self.main_dir = path_to_main.get_executed_file_dir()
539
+ self.meta_data_dir_name = ".gsplot"
540
+ self.meta_data_dir = os.path.join(
541
+ self.main_dir,
542
+ self.meta_data_dir_name,
543
+ )
544
+
545
+ self.date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
546
+ self.version = __version__
547
+ self.commit = __commit__
548
+
549
+ self.new_metadata = {
550
+ "date": self.date,
551
+ "version": self.version,
552
+ "commit": self.commit,
553
+ }
554
+
555
+ # get config dictionary
556
+ config = Config()
557
+ self.new_config_dict = config.config_dict
558
+ self.is_stored = config.get_config_entry_option("metadata")
559
+
560
+ def _create_metadata_dir(self) -> None:
561
+ if not os.path.exists(self.meta_data_dir):
562
+ os.makedirs(self.meta_data_dir)
563
+
564
+ def _create_new_metadata(self) -> None:
565
+
566
+ with open(os.path.join(self.meta_data_dir, "metadata.yml"), "w") as file:
567
+ yaml.dump(
568
+ self.new_metadata,
569
+ file,
570
+ default_flow_style=False,
571
+ sort_keys=False,
572
+ indent=2,
573
+ )
574
+
575
+ def _create_new_config(self) -> None:
576
+ with open(os.path.join(self.meta_data_dir, "config.json"), "w") as file:
577
+ # write config dictionary to file as json
578
+ json.dump(self.new_config_dict, file, indent=2)
579
+
580
+ def create_metadata(self) -> None:
581
+ if not self.is_stored:
582
+ return None
583
+
584
+ self._create_metadata_dir()
585
+
586
+ metadata_history = MetadataHistory(
587
+ new_metadata=self.new_metadata,
588
+ new_config=self.new_config_dict,
589
+ metadata_dir=self.meta_data_dir,
590
+ )
591
+ metadata_history.create_history()
592
+
593
+ self._create_new_metadata()
594
+ self._create_new_config()
595
+
596
+
597
+ def save_metadata() -> None:
598
+ _metadata = MetadataStore()
599
+ _metadata.create_metadata()
@@ -8,7 +8,7 @@ from matplotlib.typing import HashableList
8
8
 
9
9
  from ..base.base import CreateClassParams, ParamsGetter, bind_passed_params
10
10
  from ..plot.line_base import NumLines
11
- from .axes_base import AxesRangeSingleton
11
+ from .axes_range_base import AxesRangeSingleton
12
12
  from .store import StoreSingleton
13
13
 
14
14
  _T = TypeVar("_T")
@@ -285,7 +285,7 @@ class AxesHandler(Generic[_T]):
285
285
  raise ValueError("Mosaic must be specified.")
286
286
 
287
287
  # Initialize the axes range list by the number of axes in the current figure
288
- AxesRangeSingleton().reset(plt.gcf().axes)
288
+ AxesRangeSingleton().reset()
289
289
 
290
290
 
291
291
  @bind_passed_params()
@@ -0,0 +1,288 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable, TypeVar
4
+
5
+ import matplotlib.pyplot as plt
6
+ import numpy as np
7
+ from matplotlib.axes import Axes
8
+ from matplotlib.transforms import Bbox
9
+ from numpy.typing import NDArray
10
+
11
+ from .figure_tools import FigureLayout
12
+
13
+ F = TypeVar("F", bound=Callable[..., Any])
14
+
15
+ __all__: list[str] = []
16
+
17
+
18
+ # class AxesResolver:
19
+ # """
20
+ # Resolves an axis target to a Matplotlib `Axes` object or its index.
21
+ #
22
+ # This class provides a mechanism to convert an axis target, which can be either
23
+ # an integer (index of the axis) or an `Axes` object, into a consistent representation
24
+ # including the corresponding `Axes` object and its index within the current figure.
25
+ #
26
+ # Parameters
27
+ # --------------------
28
+ # axis_target : int or matplotlib.axes.Axes
29
+ # The target axis to resolve. Can be an integer representing the index of the
30
+ # axis in the current figure or a specific `Axes` object.
31
+ #
32
+ # Attributes
33
+ # --------------------
34
+ # axis_target : int or matplotlib.axes.Axes
35
+ # The input target axis (as provided by the user).
36
+ # _axis_index : int or None
37
+ # The resolved index of the target axis in the current figure.
38
+ # _axis : matplotlib.axes.Axes or None
39
+ # The resolved `Axes` object corresponding to the target.
40
+ #
41
+ # Methods
42
+ # --------------------
43
+ # _resolve_type()
44
+ # Resolves the type of the axis target and retrieves the corresponding
45
+ # `Axes` object and its index.
46
+ # axis_index
47
+ # Returns the resolved index of the axis.
48
+ # axis
49
+ # Returns the resolved `Axes` object.
50
+ #
51
+ # Raises
52
+ # --------------------
53
+ # IndexError
54
+ # If the provided axis index is out of range for the current figure.
55
+ # ValueError
56
+ # If the axis target is neither an integer nor an `Axes` object.
57
+ #
58
+ # Examples
59
+ # --------------------
60
+ # >>> import matplotlib.pyplot as plt
61
+ # >>> fig, axs = plt.subplots(2, 2)
62
+ # >>> resolver = AxesResolver(1) # Resolves the second axis (index 1)
63
+ # >>> print(resolver.axis)
64
+ # AxesSubplot(0.5,0.5;0.352273x0.352273)
65
+ #
66
+ # >>> resolver = AxesResolver(axs[0, 0]) # Resolves an Axes object directly
67
+ # >>> print(resolver.axis_index)
68
+ # 0
69
+ # """
70
+ #
71
+ # def __init__(self, axis_target: int | Axes) -> None:
72
+ # self.axis_target: int | Axes = axis_target
73
+ #
74
+ # self._axis_index: int | None = None
75
+ # self._axis: Axes | None = None
76
+ #
77
+ # self._resolve_type()
78
+ #
79
+ # def _resolve_type(self) -> None:
80
+ # """
81
+ # Resolves the type of the axis target and retrieves the corresponding
82
+ # `Axes` object and its index.
83
+ #
84
+ # Raises
85
+ # --------------------
86
+ # IndexError
87
+ # If the provided axis index is out of range for the current figure.
88
+ # ValueError
89
+ # If the axis target is neither an integer nor an `Axes` object.
90
+ # """
91
+ #
92
+ # def ordinal_suffix(n: int) -> str:
93
+ # if 11 <= n % 100 <= 13:
94
+ # suffix = "th"
95
+ # else:
96
+ # suffix = {1: "st", 2: "nd", 3: "rd"}.get(n % 10, "th")
97
+ # return f"{n}{suffix}"
98
+ #
99
+ # if isinstance(self.axis_target, int):
100
+ # self._axis_index = self.axis_target
101
+ # axes = plt.gcf().axes
102
+ # try:
103
+ # self._axis = axes[self._axis_index]
104
+ # except IndexError:
105
+ # error_message = f"Axes out of range: {self._axis_index} => Number of axes: {len(axes)}, but requested {ordinal_suffix(self._axis_index + 1)} axis."
106
+ # raise IndexError(error_message)
107
+ # elif isinstance(self.axis_target, Axes):
108
+ # self._axis = self.axis_target
109
+ # if self.axis_target in plt.gcf().axes:
110
+ # self._axis_index = plt.gcf().axes.index(self._axis)
111
+ # else:
112
+ # # Add the axis to the current figure if it is not present
113
+ # plt.gcf().add_axes(self._axis)
114
+ # self._axis_index = len(plt.gcf().axes) - 1
115
+ # else:
116
+ # raise ValueError(
117
+ # "Invalid axis target. Please provide an integer or Axes object."
118
+ # )
119
+ #
120
+ # @property
121
+ # def axis_index(self) -> int:
122
+ # """
123
+ # Returns the resolved index of the target axis.
124
+ #
125
+ # Returns
126
+ # --------------------
127
+ # int
128
+ # The index of the resolved axis.
129
+ #
130
+ # Raises
131
+ # --------------------
132
+ # ValueError
133
+ # If the axis index is not resolved.
134
+ # """
135
+ # if isinstance(self._axis_index, int):
136
+ # return self._axis_index
137
+ # else:
138
+ # raise ValueError("Axis index not resolved. Please check the AxisResolver")
139
+ #
140
+ # @property
141
+ # def axis(self) -> Axes:
142
+ # """
143
+ # Returns the resolved `Axes` object.
144
+ #
145
+ # Returns
146
+ # --------------------
147
+ # matplotlib.axes.Axes
148
+ # The resolved `Axes` object.
149
+ #
150
+ # Raises
151
+ # --------------------
152
+ # ValueError
153
+ # If the axis is not resolved.
154
+ # """
155
+ # if isinstance(self._axis, Axes):
156
+ # return self._axis
157
+ # else:
158
+ # raise ValueError("Axis not resolced. Please check the AxisResolver")
159
+
160
+
161
+ class AxisLayout:
162
+ """
163
+ A utility class for managing axis layout properties in a Matplotlib figure.
164
+
165
+ This class provides methods to retrieve an axis's position and size, both in
166
+ normalized figure coordinates and in physical units (inches). It integrates
167
+ with the `AxesResolver` and `FigureLayout` classes to ensure consistent layout
168
+ calculations.
169
+
170
+ Parameters
171
+ --------------------
172
+ ax : matplotlib.axes.Axes
173
+ The target `Axes` object for which to manage the
174
+
175
+ Attributes
176
+ --------------------
177
+ ax : matplotlib.axes.Axes
178
+ The target `Axes` object for which to manage the layout.
179
+ fig_size : numpy.ndarray
180
+ The size of the figure in inches as a NumPy array.
181
+
182
+ Methods
183
+ --------------------
184
+ get_axis_position()
185
+ Returns the position of the axis in normalized figure coordinates.
186
+ get_axis_size()
187
+ Returns the size of the axis in normalized figure coordinates.
188
+ get_axis_position_inches()
189
+ Returns the position of the axis in physical units (inches).
190
+ get_axis_size_inches()
191
+ Returns the size of the axis in physical units (inches).
192
+
193
+ Examples
194
+ --------------------
195
+ >>> fig, ax = plt.subplots()
196
+ >>> layout = AxisLayout(ax)
197
+ >>> position = layout.get_axis_position()
198
+ >>> size = layout.get_axis_size()
199
+ >>> position_inches = layout.get_axis_position_inches()
200
+ """
201
+
202
+ def __init__(self, ax: Axes) -> None:
203
+ self.ax: Axes = ax
204
+ self.fig_size: NDArray[Any] = FigureLayout().get_figure_size()
205
+
206
+ def get_axis_position(self) -> Bbox:
207
+ """
208
+ Retrieves the position of the axis in normalized figure coordinates.
209
+
210
+ Returns
211
+ --------------------
212
+ matplotlib.transforms.Bbox
213
+ The position of the axis as a bounding box in normalized coordinates.
214
+
215
+ Examples
216
+ --------------------
217
+ >>> layout = AxisLayout(axis_index=0)
218
+ >>> position = layout.get_axis_position()
219
+ >>> print(position)
220
+ Bbox(x0=0.1, y0=0.1, x1=0.9, y1=0.9)
221
+ """
222
+ axis_position = self.ax.get_position()
223
+ return axis_position
224
+
225
+ def get_axis_size(self) -> NDArray[Any]:
226
+ """
227
+ Retrieves the size of the axis in normalized figure coordinates.
228
+
229
+ Returns
230
+ --------------------
231
+ numpy.ndarray
232
+ The width and height of the axis as a NumPy array.
233
+
234
+ Examples
235
+ --------------------
236
+ >>> layout = AxisLayout(axis_index=0)
237
+ >>> size = layout.get_axis_size()
238
+ >>> print(size)
239
+ array([0.8, 0.8])
240
+ """
241
+ axis_position_size = np.array(self.get_axis_position().size)
242
+ return axis_position_size
243
+
244
+ def get_axis_position_inches(self) -> Bbox:
245
+ """
246
+ Retrieves the position of the axis in physical units (inches).
247
+
248
+ Returns
249
+ --------------------
250
+ matplotlib.transforms.Bbox
251
+ The position of the axis as a bounding box in inches.
252
+
253
+ Examples
254
+ --------------------
255
+ >>> layout = AxisLayout(axis_index=0)
256
+ >>> position_inches = layout.get_axis_position_inches()
257
+ >>> print(position_inches)
258
+ Bbox(x0=1.6, y0=1.6, x1=14.4, y1=14.4)
259
+ """
260
+
261
+ axis_position = self.get_axis_position()
262
+
263
+ axis_position_inches = Bbox.from_bounds(
264
+ axis_position.x0 * self.fig_size[0],
265
+ axis_position.y0 * self.fig_size[1],
266
+ axis_position.width * self.fig_size[0],
267
+ axis_position.height * self.fig_size[1],
268
+ )
269
+ return axis_position_inches
270
+
271
+ def get_axis_size_inches(self) -> NDArray[Any]:
272
+ """
273
+ Retrieves the size of the axis in physical units (inches).
274
+
275
+ Returns
276
+ --------------------
277
+ numpy.ndarray
278
+ The width and height of the axis in inches as a NumPy array.
279
+
280
+ Examples
281
+ --------------------
282
+ >>> layout = AxisLayout(axis_index=0)
283
+ >>> size_inches = layout.get_axis_size_inches()
284
+ >>> print(size_inches)
285
+ array([12.8, 12.8])
286
+ """
287
+ axis_position_size_inches = np.array(self.get_axis_position_inches().size)
288
+ return axis_position_size_inches