gsplot 0.0.1__py3-none-any.whl
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.
- gsplot/__init__.py +98 -0
- gsplot/base/base.py +518 -0
- gsplot/base/base_alias_validator.py +155 -0
- gsplot/color/colormap.py +218 -0
- gsplot/config/config.py +422 -0
- gsplot/data/load_file.py +188 -0
- gsplot/figure/axes.py +361 -0
- gsplot/figure/axes_base.py +952 -0
- gsplot/figure/figure_tools.py +66 -0
- gsplot/figure/show.py +205 -0
- gsplot/figure/store.py +119 -0
- gsplot/hello_world/hello_world.py +23 -0
- gsplot/logger.py +155 -0
- gsplot/path/path.py +227 -0
- gsplot/plot/line.py +328 -0
- gsplot/plot/line_base.py +272 -0
- gsplot/plot/line_colormap_base.py +120 -0
- gsplot/plot/line_colormap_dashed.py +540 -0
- gsplot/plot/line_colormap_solid.py +289 -0
- gsplot/plot/scatter.py +228 -0
- gsplot/plot/scatter_colormap.py +296 -0
- gsplot/style/graph.py +466 -0
- gsplot/style/label.py +866 -0
- gsplot/style/legend.py +469 -0
- gsplot/style/legend_colormap.py +381 -0
- gsplot/style/ticks.py +167 -0
- gsplot/version.py +2 -0
- gsplot-0.0.1.dist-info/LICENSE +21 -0
- gsplot-0.0.1.dist-info/METADATA +82 -0
- gsplot-0.0.1.dist-info/RECORD +31 -0
- gsplot-0.0.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,952 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import threading
|
|
4
|
+
from typing import Any, Callable, TypeVar, cast
|
|
5
|
+
|
|
6
|
+
import matplotlib.pyplot as plt
|
|
7
|
+
import numpy as np
|
|
8
|
+
from matplotlib.axes import Axes
|
|
9
|
+
from matplotlib.transforms import Bbox
|
|
10
|
+
from numpy.typing import NDArray
|
|
11
|
+
|
|
12
|
+
from .figure_tools import FigureLayout
|
|
13
|
+
|
|
14
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
15
|
+
|
|
16
|
+
__all__: list[str] = []
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AxesResolver:
|
|
20
|
+
"""
|
|
21
|
+
Resolves an axis target to a Matplotlib `Axes` object or its index.
|
|
22
|
+
|
|
23
|
+
This class provides a mechanism to convert an axis target, which can be either
|
|
24
|
+
an integer (index of the axis) or an `Axes` object, into a consistent representation
|
|
25
|
+
including the corresponding `Axes` object and its index within the current figure.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
--------------------
|
|
29
|
+
axis_target : int or matplotlib.axes.Axes
|
|
30
|
+
The target axis to resolve. Can be an integer representing the index of the
|
|
31
|
+
axis in the current figure or a specific `Axes` object.
|
|
32
|
+
|
|
33
|
+
Attributes
|
|
34
|
+
--------------------
|
|
35
|
+
axis_target : int or matplotlib.axes.Axes
|
|
36
|
+
The input target axis (as provided by the user).
|
|
37
|
+
_axis_index : int or None
|
|
38
|
+
The resolved index of the target axis in the current figure.
|
|
39
|
+
_axis : matplotlib.axes.Axes or None
|
|
40
|
+
The resolved `Axes` object corresponding to the target.
|
|
41
|
+
|
|
42
|
+
Methods
|
|
43
|
+
--------------------
|
|
44
|
+
_resolve_type()
|
|
45
|
+
Resolves the type of the axis target and retrieves the corresponding
|
|
46
|
+
`Axes` object and its index.
|
|
47
|
+
axis_index
|
|
48
|
+
Returns the resolved index of the axis.
|
|
49
|
+
axis
|
|
50
|
+
Returns the resolved `Axes` object.
|
|
51
|
+
|
|
52
|
+
Raises
|
|
53
|
+
--------------------
|
|
54
|
+
IndexError
|
|
55
|
+
If the provided axis index is out of range for the current figure.
|
|
56
|
+
ValueError
|
|
57
|
+
If the axis target is neither an integer nor an `Axes` object.
|
|
58
|
+
|
|
59
|
+
Examples
|
|
60
|
+
--------------------
|
|
61
|
+
>>> import matplotlib.pyplot as plt
|
|
62
|
+
>>> fig, axs = plt.subplots(2, 2)
|
|
63
|
+
>>> resolver = AxesResolver(1) # Resolves the second axis (index 1)
|
|
64
|
+
>>> print(resolver.axis)
|
|
65
|
+
AxesSubplot(0.5,0.5;0.352273x0.352273)
|
|
66
|
+
|
|
67
|
+
>>> resolver = AxesResolver(axs[0, 0]) # Resolves an Axes object directly
|
|
68
|
+
>>> print(resolver.axis_index)
|
|
69
|
+
0
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def __init__(self, axis_target: int | Axes) -> None:
|
|
73
|
+
self.axis_target: int | Axes = axis_target
|
|
74
|
+
|
|
75
|
+
self._axis_index: int | None = None
|
|
76
|
+
self._axis: Axes | None = None
|
|
77
|
+
|
|
78
|
+
self._resolve_type()
|
|
79
|
+
|
|
80
|
+
def _resolve_type(self) -> None:
|
|
81
|
+
"""
|
|
82
|
+
Resolves the type of the axis target and retrieves the corresponding
|
|
83
|
+
`Axes` object and its index.
|
|
84
|
+
|
|
85
|
+
Raises
|
|
86
|
+
--------------------
|
|
87
|
+
IndexError
|
|
88
|
+
If the provided axis index is out of range for the current figure.
|
|
89
|
+
ValueError
|
|
90
|
+
If the axis target is neither an integer nor an `Axes` object.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
def ordinal_suffix(n: int) -> str:
|
|
94
|
+
if 11 <= n % 100 <= 13:
|
|
95
|
+
suffix = "th"
|
|
96
|
+
else:
|
|
97
|
+
suffix = {1: "st", 2: "nd", 3: "rd"}.get(n % 10, "th")
|
|
98
|
+
return f"{n}{suffix}"
|
|
99
|
+
|
|
100
|
+
if isinstance(self.axis_target, int):
|
|
101
|
+
self._axis_index = self.axis_target
|
|
102
|
+
axes = plt.gcf().axes
|
|
103
|
+
try:
|
|
104
|
+
self._axis = axes[self._axis_index]
|
|
105
|
+
except IndexError:
|
|
106
|
+
error_message = f"Axes out of range: {self._axis_index} => Number of axes: {len(axes)}, but requested {ordinal_suffix(self._axis_index + 1)} axis."
|
|
107
|
+
raise IndexError(error_message)
|
|
108
|
+
|
|
109
|
+
elif isinstance(self.axis_target, Axes):
|
|
110
|
+
self._axis = self.axis_target
|
|
111
|
+
self._axis_index = plt.gcf().axes.index(self._axis)
|
|
112
|
+
else:
|
|
113
|
+
raise ValueError(
|
|
114
|
+
"Invalid axis target. Please provide an integer or Axes object."
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def axis_index(self) -> int:
|
|
119
|
+
"""
|
|
120
|
+
Returns the resolved index of the target axis.
|
|
121
|
+
|
|
122
|
+
Returns
|
|
123
|
+
--------------------
|
|
124
|
+
int
|
|
125
|
+
The index of the resolved axis.
|
|
126
|
+
|
|
127
|
+
Raises
|
|
128
|
+
--------------------
|
|
129
|
+
ValueError
|
|
130
|
+
If the axis index is not resolved.
|
|
131
|
+
"""
|
|
132
|
+
if isinstance(self._axis_index, int):
|
|
133
|
+
return self._axis_index
|
|
134
|
+
else:
|
|
135
|
+
raise ValueError("Axis index not resolved. Please check the AxisResolver")
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def axis(self) -> Axes:
|
|
139
|
+
"""
|
|
140
|
+
Returns the resolved `Axes` object.
|
|
141
|
+
|
|
142
|
+
Returns
|
|
143
|
+
--------------------
|
|
144
|
+
matplotlib.axes.Axes
|
|
145
|
+
The resolved `Axes` object.
|
|
146
|
+
|
|
147
|
+
Raises
|
|
148
|
+
--------------------
|
|
149
|
+
ValueError
|
|
150
|
+
If the axis is not resolved.
|
|
151
|
+
"""
|
|
152
|
+
if isinstance(self._axis, Axes):
|
|
153
|
+
return self._axis
|
|
154
|
+
else:
|
|
155
|
+
raise ValueError("Axis not resolced. Please check the AxisResolver")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class AxesRangeSingleton:
|
|
159
|
+
"""
|
|
160
|
+
A thread-safe singleton class for managing axis ranges in Matplotlib figures.
|
|
161
|
+
|
|
162
|
+
This class provides functionality to store and update axis ranges for all axes
|
|
163
|
+
in a figure, ensuring consistency across different parts of the application.
|
|
164
|
+
It maintains axis ranges for each axis and can dynamically extend or reset the stored ranges.
|
|
165
|
+
|
|
166
|
+
Attributes
|
|
167
|
+
--------------------
|
|
168
|
+
_instance : AxesRangeSingleton or None
|
|
169
|
+
The singleton instance of the `AxesRangeSingleton` class.
|
|
170
|
+
_lock : threading.Lock
|
|
171
|
+
A lock to ensure thread-safe access to the singleton instance.
|
|
172
|
+
_axes_ranges : list of list[Any]
|
|
173
|
+
A list storing ranges for each axis, where each range is represented as `[xrange, yrange]`.
|
|
174
|
+
|
|
175
|
+
Methods
|
|
176
|
+
--------------------
|
|
177
|
+
axes_ranges
|
|
178
|
+
Retrieves the list of axis ranges, ensuring its size matches the number of axes in the current figure.
|
|
179
|
+
add_range(axis_index, xrange, yrange)
|
|
180
|
+
Adds or updates the range for a specific axis.
|
|
181
|
+
get_max_wo_inf(array)
|
|
182
|
+
Returns the maximum value in an array, ignoring infinities.
|
|
183
|
+
get_min_wo_inf(array)
|
|
184
|
+
Returns the minimum value in an array, ignoring infinities.
|
|
185
|
+
reset(axes)
|
|
186
|
+
Resets the stored ranges to match the provided list of axes.
|
|
187
|
+
update(func)
|
|
188
|
+
A decorator to update axis ranges based on data and ensure consistency.
|
|
189
|
+
|
|
190
|
+
Examples
|
|
191
|
+
--------------------
|
|
192
|
+
>>> axes_ranges = AxesRangeSingleton()
|
|
193
|
+
>>> print(axes_ranges.axes_ranges)
|
|
194
|
+
[[None, None]]
|
|
195
|
+
|
|
196
|
+
>>> axes_ranges.add_range(0, np.array([0, 10]), np.array([0, 20]))
|
|
197
|
+
>>> print(axes_ranges.axes_ranges)
|
|
198
|
+
[array([0, 10]), array([0, 20])]
|
|
199
|
+
|
|
200
|
+
>>> axes_ranges.reset(plt.gcf().axes)
|
|
201
|
+
>>> print(axes_ranges.axes_ranges)
|
|
202
|
+
[[None, None], [None, None]] # Resets to the number of current figure axes
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
_instance: AxesRangeSingleton | None = None
|
|
206
|
+
_lock: threading.Lock = threading.Lock() # Lock to ensure thread safety
|
|
207
|
+
|
|
208
|
+
def __new__(cls) -> "AxesRangeSingleton":
|
|
209
|
+
with cls._lock: # Ensure thread safety
|
|
210
|
+
if cls._instance is None:
|
|
211
|
+
cls._instance = super(AxesRangeSingleton, cls).__new__(cls)
|
|
212
|
+
cls._instance._initialize_axes_ranges()
|
|
213
|
+
return cls._instance
|
|
214
|
+
|
|
215
|
+
def _initialize_axes_ranges(self) -> None:
|
|
216
|
+
"""
|
|
217
|
+
Initializes the axis ranges storage with a default value.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
# Explicitly initialize the instance variable with a type hint
|
|
221
|
+
self._axes_ranges: list[list[Any]] = [[None, None]]
|
|
222
|
+
|
|
223
|
+
def ensure_size_of_axes_ranges(self) -> None:
|
|
224
|
+
"""
|
|
225
|
+
Ensures the `_axes_ranges` list matches the number of axes in the current figure.
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
axes = plt.gcf().axes
|
|
229
|
+
axes_length = len(axes)
|
|
230
|
+
num_elements_to_append = max(0, axes_length - len(self._axes_ranges))
|
|
231
|
+
self._axes_ranges.extend([[None, None]] * num_elements_to_append)
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def axes_ranges(self) -> list[list[Any]]:
|
|
235
|
+
"""
|
|
236
|
+
Retrieves the list of axis ranges, ensuring its size matches the number of axes.
|
|
237
|
+
|
|
238
|
+
Returns
|
|
239
|
+
--------------------
|
|
240
|
+
list of list[Any]
|
|
241
|
+
The list of axis ranges.
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
self.ensure_size_of_axes_ranges()
|
|
245
|
+
return self._axes_ranges
|
|
246
|
+
|
|
247
|
+
@axes_ranges.setter
|
|
248
|
+
def axes_ranges(
|
|
249
|
+
self,
|
|
250
|
+
axes_ranges: list[list[Any]],
|
|
251
|
+
) -> None:
|
|
252
|
+
"""
|
|
253
|
+
Sets the axis ranges to a new list.
|
|
254
|
+
|
|
255
|
+
Parameters
|
|
256
|
+
--------------------
|
|
257
|
+
axes_ranges : list of list[Any]
|
|
258
|
+
The new axis ranges to set.
|
|
259
|
+
|
|
260
|
+
Raises
|
|
261
|
+
--------------------
|
|
262
|
+
TypeError
|
|
263
|
+
If the provided value is not iterable.
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
try:
|
|
267
|
+
iter(axes_ranges)
|
|
268
|
+
except TypeError:
|
|
269
|
+
raise TypeError(f"Expected an iterable, got {type(axes_ranges).__name__}")
|
|
270
|
+
self._axes_ranges = axes_ranges
|
|
271
|
+
|
|
272
|
+
def add_range(
|
|
273
|
+
self, axis_index: int, xrange: NDArray[Any], yrange: NDArray[Any]
|
|
274
|
+
) -> None:
|
|
275
|
+
"""
|
|
276
|
+
Adds or updates the range for a specific axis.
|
|
277
|
+
|
|
278
|
+
Parameters
|
|
279
|
+
--------------------
|
|
280
|
+
axis_index : int
|
|
281
|
+
The index of the axis to update.
|
|
282
|
+
xrange : numpy.ndarray
|
|
283
|
+
The range for the x-axis.
|
|
284
|
+
yrange : numpy.ndarray
|
|
285
|
+
The range for the y-axis.
|
|
286
|
+
|
|
287
|
+
Examples
|
|
288
|
+
--------------------
|
|
289
|
+
>>> axes_ranges = AxesRangeSingleton()
|
|
290
|
+
>>> axes_ranges.add_range(0, np.array([0, 10]), np.array([0, 20]))
|
|
291
|
+
>>> print(axes_ranges.axes_ranges)
|
|
292
|
+
[[array([ 0, 10]), array([ 0, 20])]]
|
|
293
|
+
"""
|
|
294
|
+
while len(self._axes_ranges) <= axis_index:
|
|
295
|
+
self._axes_ranges.append([None, None])
|
|
296
|
+
self._axes_ranges[axis_index] = [xrange, yrange]
|
|
297
|
+
|
|
298
|
+
def _get_wider_range(
|
|
299
|
+
self, range1: NDArray[Any], range2: NDArray[Any]
|
|
300
|
+
) -> NDArray[Any]:
|
|
301
|
+
"""
|
|
302
|
+
Computes the wider range encompassing two given ranges.
|
|
303
|
+
|
|
304
|
+
Parameters
|
|
305
|
+
--------------------
|
|
306
|
+
range1 : numpy.ndarray
|
|
307
|
+
The first range.
|
|
308
|
+
range2 : numpy.ndarray
|
|
309
|
+
The second range.
|
|
310
|
+
|
|
311
|
+
Returns
|
|
312
|
+
--------------------
|
|
313
|
+
numpy.ndarray
|
|
314
|
+
The wider range encompassing both inputs.
|
|
315
|
+
|
|
316
|
+
Examples
|
|
317
|
+
--------------------
|
|
318
|
+
>>> wider_range = AxesRangeSingleton()._get_wider_range(
|
|
319
|
+
... np.array([0, 5]),
|
|
320
|
+
... np.array([3, 10])
|
|
321
|
+
... )
|
|
322
|
+
>>> print(wider_range)
|
|
323
|
+
array([0, 10])
|
|
324
|
+
"""
|
|
325
|
+
new_range = np.array([min(range1[0], range2[0]), max(range1[1], range2[1])])
|
|
326
|
+
return new_range
|
|
327
|
+
|
|
328
|
+
def get_max_wo_inf(self, array: NDArray[Any]) -> float:
|
|
329
|
+
"""
|
|
330
|
+
Returns the maximum value in an array, ignoring infinities.
|
|
331
|
+
|
|
332
|
+
Parameters
|
|
333
|
+
--------------------
|
|
334
|
+
array : numpy.ndarray
|
|
335
|
+
The input array.
|
|
336
|
+
|
|
337
|
+
Returns
|
|
338
|
+
--------------------
|
|
339
|
+
float
|
|
340
|
+
The maximum value excluding infinities.
|
|
341
|
+
|
|
342
|
+
Examples
|
|
343
|
+
--------------------
|
|
344
|
+
>>> max_value = AxesRangeSingleton().get_max_wo_inf(
|
|
345
|
+
... np.array([1, 2, np.inf, 3])
|
|
346
|
+
... )
|
|
347
|
+
>>> print(max_value)
|
|
348
|
+
3.0
|
|
349
|
+
"""
|
|
350
|
+
array = np.array(array)
|
|
351
|
+
array = array[array != np.inf]
|
|
352
|
+
return float(np.nanmax(array))
|
|
353
|
+
|
|
354
|
+
def get_min_wo_inf(self, array: NDArray[Any]) -> float:
|
|
355
|
+
"""
|
|
356
|
+
Returns the minimum value in an array, ignoring negative infinities.
|
|
357
|
+
|
|
358
|
+
Parameters
|
|
359
|
+
--------------------
|
|
360
|
+
array : numpy.ndarray
|
|
361
|
+
The input array.
|
|
362
|
+
|
|
363
|
+
Returns
|
|
364
|
+
--------------------
|
|
365
|
+
float
|
|
366
|
+
The minimum value excluding negative infinities.
|
|
367
|
+
|
|
368
|
+
Examples
|
|
369
|
+
--------------------
|
|
370
|
+
>>> min_value = AxesRangeSingleton().get_min_wo_inf(
|
|
371
|
+
... np.array([1, 2, -np.inf, 3])
|
|
372
|
+
... )
|
|
373
|
+
>>> print(min_value)
|
|
374
|
+
1.0
|
|
375
|
+
"""
|
|
376
|
+
array = np.array(array)
|
|
377
|
+
array = array[array != -np.inf]
|
|
378
|
+
return float(np.nanmin(array))
|
|
379
|
+
|
|
380
|
+
@classmethod
|
|
381
|
+
def update(cls, func: F) -> F:
|
|
382
|
+
"""
|
|
383
|
+
A decorator to update axis ranges based on data and ensure consistency.
|
|
384
|
+
|
|
385
|
+
The decorator dynamically adjusts axis ranges by considering the current axis
|
|
386
|
+
data and adding it to the stored ranges.
|
|
387
|
+
|
|
388
|
+
Parameters
|
|
389
|
+
--------------------
|
|
390
|
+
func : callable
|
|
391
|
+
The function to wrap.
|
|
392
|
+
|
|
393
|
+
Returns
|
|
394
|
+
--------------------
|
|
395
|
+
callable
|
|
396
|
+
The wrapped function.
|
|
397
|
+
|
|
398
|
+
Examples
|
|
399
|
+
--------------------
|
|
400
|
+
>>> @AxesRangeSingleton.update
|
|
401
|
+
... def draw_plot(self, *args, **kwargs):
|
|
402
|
+
... pass
|
|
403
|
+
"""
|
|
404
|
+
|
|
405
|
+
def wrapper(self, *args: Any, **kwargs: Any) -> Any:
|
|
406
|
+
axis_index: int = self.axis_index
|
|
407
|
+
x: NDArray[Any] = self.x
|
|
408
|
+
y: NDArray[Any] = self.y
|
|
409
|
+
|
|
410
|
+
num_elements_to_append = max(0, axis_index + 1 - len(cls().axes_ranges))
|
|
411
|
+
cls().axes_ranges.extend([[None, None]] * num_elements_to_append)
|
|
412
|
+
|
|
413
|
+
xrange, yrange = AxisRangeHandler(axis_index, x, y).get_new_axis_range()
|
|
414
|
+
xrange = np.array([cls().get_min_wo_inf(x), cls().get_max_wo_inf(x)])
|
|
415
|
+
yrange = np.array([cls().get_min_wo_inf(y), cls().get_max_wo_inf(y)])
|
|
416
|
+
|
|
417
|
+
xrange_singleton = cls().axes_ranges[axis_index][0]
|
|
418
|
+
yrange_singleton = cls().axes_ranges[axis_index][1]
|
|
419
|
+
|
|
420
|
+
if xrange_singleton is not None:
|
|
421
|
+
new_xrange = cls()._get_wider_range(xrange, xrange_singleton)
|
|
422
|
+
else:
|
|
423
|
+
new_xrange = xrange
|
|
424
|
+
|
|
425
|
+
if yrange_singleton is not None:
|
|
426
|
+
new_yrange = cls()._get_wider_range(yrange, yrange_singleton)
|
|
427
|
+
else:
|
|
428
|
+
new_yrange = yrange
|
|
429
|
+
|
|
430
|
+
cls().add_range(axis_index, new_xrange, new_yrange)
|
|
431
|
+
|
|
432
|
+
result = func(self, *args, **kwargs)
|
|
433
|
+
return result
|
|
434
|
+
|
|
435
|
+
return cast(F, wrapper)
|
|
436
|
+
|
|
437
|
+
def reset(self, axes: list[Axes]):
|
|
438
|
+
axes_length = len(axes)
|
|
439
|
+
self._axes_ranges = [[None, None]] * axes_length
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
class AxisLayout:
|
|
443
|
+
"""
|
|
444
|
+
A utility class for managing axis layout properties in a Matplotlib figure.
|
|
445
|
+
|
|
446
|
+
This class provides methods to retrieve an axis's position and size, both in
|
|
447
|
+
normalized figure coordinates and in physical units (inches). It integrates
|
|
448
|
+
with the `AxesResolver` and `FigureLayout` classes to ensure consistent layout
|
|
449
|
+
calculations.
|
|
450
|
+
|
|
451
|
+
Parameters
|
|
452
|
+
--------------------
|
|
453
|
+
axis_index : int
|
|
454
|
+
The index of the target axis in the current figure.
|
|
455
|
+
|
|
456
|
+
Attributes
|
|
457
|
+
--------------------
|
|
458
|
+
axis_index : int
|
|
459
|
+
The index of the target axis.
|
|
460
|
+
axis : matplotlib.axes.Axes
|
|
461
|
+
The resolved `Axes` object corresponding to the target index.
|
|
462
|
+
fig_size : numpy.ndarray
|
|
463
|
+
The size of the figure in inches as a NumPy array.
|
|
464
|
+
|
|
465
|
+
Methods
|
|
466
|
+
--------------------
|
|
467
|
+
get_axis_position()
|
|
468
|
+
Returns the position of the axis in normalized figure coordinates.
|
|
469
|
+
get_axis_size()
|
|
470
|
+
Returns the size of the axis in normalized figure coordinates.
|
|
471
|
+
get_axis_position_inches()
|
|
472
|
+
Returns the position of the axis in physical units (inches).
|
|
473
|
+
get_axis_size_inches()
|
|
474
|
+
Returns the size of the axis in physical units (inches).
|
|
475
|
+
|
|
476
|
+
Examples
|
|
477
|
+
--------------------
|
|
478
|
+
>>> layout = AxisLayout(axis_index=0)
|
|
479
|
+
>>> axis_position = layout.get_axis_position()
|
|
480
|
+
>>> print(axis_position)
|
|
481
|
+
Bbox(x0=0.1, y0=0.1, x1=0.9, y1=0.9)
|
|
482
|
+
|
|
483
|
+
>>> axis_size = layout.get_axis_size()
|
|
484
|
+
>>> print(axis_size)
|
|
485
|
+
array([0.8, 0.8])
|
|
486
|
+
|
|
487
|
+
>>> axis_position_inches = layout.get_axis_position_inches()
|
|
488
|
+
>>> print(axis_position_inches)
|
|
489
|
+
Bbox(x0=1.6, y0=1.6, x1=14.4, y1=14.4)
|
|
490
|
+
|
|
491
|
+
>>> axis_size_inches = layout.get_axis_size_inches()
|
|
492
|
+
>>> print(axis_size_inches)
|
|
493
|
+
array([12.8, 12.8])
|
|
494
|
+
"""
|
|
495
|
+
|
|
496
|
+
def __init__(self, axis_index: int) -> None:
|
|
497
|
+
self.axis_index = axis_index
|
|
498
|
+
self.axis: Axes = AxesResolver(self.axis_index).axis
|
|
499
|
+
|
|
500
|
+
self.fig_size: NDArray[Any] = FigureLayout().get_figure_size()
|
|
501
|
+
|
|
502
|
+
def get_axis_position(self) -> Bbox:
|
|
503
|
+
"""
|
|
504
|
+
Retrieves the position of the axis in normalized figure coordinates.
|
|
505
|
+
|
|
506
|
+
Returns
|
|
507
|
+
--------------------
|
|
508
|
+
matplotlib.transforms.Bbox
|
|
509
|
+
The position of the axis as a bounding box in normalized coordinates.
|
|
510
|
+
|
|
511
|
+
Examples
|
|
512
|
+
--------------------
|
|
513
|
+
>>> layout = AxisLayout(axis_index=0)
|
|
514
|
+
>>> position = layout.get_axis_position()
|
|
515
|
+
>>> print(position)
|
|
516
|
+
Bbox(x0=0.1, y0=0.1, x1=0.9, y1=0.9)
|
|
517
|
+
"""
|
|
518
|
+
axis_position = self.axis.get_position()
|
|
519
|
+
return axis_position
|
|
520
|
+
|
|
521
|
+
def get_axis_size(self) -> NDArray[Any]:
|
|
522
|
+
"""
|
|
523
|
+
Retrieves the size of the axis in normalized figure coordinates.
|
|
524
|
+
|
|
525
|
+
Returns
|
|
526
|
+
--------------------
|
|
527
|
+
numpy.ndarray
|
|
528
|
+
The width and height of the axis as a NumPy array.
|
|
529
|
+
|
|
530
|
+
Examples
|
|
531
|
+
--------------------
|
|
532
|
+
>>> layout = AxisLayout(axis_index=0)
|
|
533
|
+
>>> size = layout.get_axis_size()
|
|
534
|
+
>>> print(size)
|
|
535
|
+
array([0.8, 0.8])
|
|
536
|
+
"""
|
|
537
|
+
axis_position_size = np.array(self.get_axis_position().size)
|
|
538
|
+
return axis_position_size
|
|
539
|
+
|
|
540
|
+
def get_axis_position_inches(self) -> Bbox:
|
|
541
|
+
"""
|
|
542
|
+
Retrieves the position of the axis in physical units (inches).
|
|
543
|
+
|
|
544
|
+
Returns
|
|
545
|
+
--------------------
|
|
546
|
+
matplotlib.transforms.Bbox
|
|
547
|
+
The position of the axis as a bounding box in inches.
|
|
548
|
+
|
|
549
|
+
Examples
|
|
550
|
+
--------------------
|
|
551
|
+
>>> layout = AxisLayout(axis_index=0)
|
|
552
|
+
>>> position_inches = layout.get_axis_position_inches()
|
|
553
|
+
>>> print(position_inches)
|
|
554
|
+
Bbox(x0=1.6, y0=1.6, x1=14.4, y1=14.4)
|
|
555
|
+
"""
|
|
556
|
+
|
|
557
|
+
axis_position = self.get_axis_position()
|
|
558
|
+
|
|
559
|
+
axis_position_inches = Bbox.from_bounds(
|
|
560
|
+
axis_position.x0 * self.fig_size[0],
|
|
561
|
+
axis_position.y0 * self.fig_size[1],
|
|
562
|
+
axis_position.width * self.fig_size[0],
|
|
563
|
+
axis_position.height * self.fig_size[1],
|
|
564
|
+
)
|
|
565
|
+
return axis_position_inches
|
|
566
|
+
|
|
567
|
+
def get_axis_size_inches(self) -> NDArray[Any]:
|
|
568
|
+
"""
|
|
569
|
+
Retrieves the size of the axis in physical units (inches).
|
|
570
|
+
|
|
571
|
+
Returns
|
|
572
|
+
--------------------
|
|
573
|
+
numpy.ndarray
|
|
574
|
+
The width and height of the axis in inches as a NumPy array.
|
|
575
|
+
|
|
576
|
+
Examples
|
|
577
|
+
--------------------
|
|
578
|
+
>>> layout = AxisLayout(axis_index=0)
|
|
579
|
+
>>> size_inches = layout.get_axis_size_inches()
|
|
580
|
+
>>> print(size_inches)
|
|
581
|
+
array([12.8, 12.8])
|
|
582
|
+
"""
|
|
583
|
+
axis_position_size_inches = np.array(self.get_axis_position_inches().size)
|
|
584
|
+
return axis_position_size_inches
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
class AxisRangeController:
|
|
588
|
+
"""
|
|
589
|
+
A controller for managing the x and y ranges of a specific Matplotlib axis.
|
|
590
|
+
|
|
591
|
+
This class provides methods to get and set the x-axis and y-axis ranges for
|
|
592
|
+
a given axis in a Matplotlib figure.
|
|
593
|
+
|
|
594
|
+
Parameters
|
|
595
|
+
--------------------
|
|
596
|
+
axis_index : int
|
|
597
|
+
The index of the target axis in the current figure.
|
|
598
|
+
|
|
599
|
+
Attributes
|
|
600
|
+
--------------------
|
|
601
|
+
axis_index : int
|
|
602
|
+
The index of the target axis.
|
|
603
|
+
axis : matplotlib.axes.Axes
|
|
604
|
+
The resolved `Axes` object corresponding to the target index.
|
|
605
|
+
|
|
606
|
+
Methods
|
|
607
|
+
--------------------
|
|
608
|
+
get_axis_xrange()
|
|
609
|
+
Retrieves the x-axis range of the target axis.
|
|
610
|
+
get_axis_yrange()
|
|
611
|
+
Retrieves the y-axis range of the target axis.
|
|
612
|
+
set_axis_xrange(xrange)
|
|
613
|
+
Sets the x-axis range of the target axis.
|
|
614
|
+
set_axis_yrange(yrange)
|
|
615
|
+
Sets the y-axis range of the target axis.
|
|
616
|
+
|
|
617
|
+
Examples
|
|
618
|
+
--------------------
|
|
619
|
+
>>> controller = AxisRangeController(axis_index=0)
|
|
620
|
+
>>> x_range = controller.get_axis_xrange()
|
|
621
|
+
>>> print(x_range)
|
|
622
|
+
array([0.0, 1.0])
|
|
623
|
+
|
|
624
|
+
>>> controller.set_axis_xrange(np.array([0.5, 1.5]))
|
|
625
|
+
>>> print(controller.get_axis_xrange())
|
|
626
|
+
array([0.5, 1.5])
|
|
627
|
+
|
|
628
|
+
>>> y_range = controller.get_axis_yrange()
|
|
629
|
+
>>> print(y_range)
|
|
630
|
+
array([0.0, 1.0])
|
|
631
|
+
|
|
632
|
+
>>> controller.set_axis_yrange(np.array([0.2, 0.8]))
|
|
633
|
+
>>> print(controller.get_axis_yrange())
|
|
634
|
+
array([0.2, 0.8])
|
|
635
|
+
"""
|
|
636
|
+
|
|
637
|
+
def __init__(self, axis_index: int):
|
|
638
|
+
self.axis_index = axis_index
|
|
639
|
+
self.axis: Axes = AxesResolver(self.axis_index).axis
|
|
640
|
+
|
|
641
|
+
def get_axis_xrange(self) -> NDArray[Any]:
|
|
642
|
+
"""
|
|
643
|
+
Retrieves the x-axis range of the target axis.
|
|
644
|
+
|
|
645
|
+
Returns
|
|
646
|
+
--------------------
|
|
647
|
+
numpy.ndarray
|
|
648
|
+
The x-axis range as a NumPy array.
|
|
649
|
+
|
|
650
|
+
Examples
|
|
651
|
+
--------------------
|
|
652
|
+
>>> controller = AxisRangeController(axis_index=0)
|
|
653
|
+
>>> x_range = controller.get_axis_xrange()
|
|
654
|
+
>>> print(x_range)
|
|
655
|
+
array([0.0, 1.0])
|
|
656
|
+
"""
|
|
657
|
+
axis_xrange: NDArray[Any] = np.array(self.axis.get_xlim())
|
|
658
|
+
return axis_xrange
|
|
659
|
+
|
|
660
|
+
def get_axis_yrange(self) -> NDArray[Any]:
|
|
661
|
+
"""
|
|
662
|
+
Retrieves the y-axis range of the target axis.
|
|
663
|
+
|
|
664
|
+
Returns
|
|
665
|
+
--------------------
|
|
666
|
+
numpy.ndarray
|
|
667
|
+
The y-axis range as a NumPy array.
|
|
668
|
+
|
|
669
|
+
Examples
|
|
670
|
+
--------------------
|
|
671
|
+
>>> controller = AxisRangeController(axis_index=0)
|
|
672
|
+
>>> y_range = controller.get_axis_yrange()
|
|
673
|
+
>>> print(y_range)
|
|
674
|
+
array([0.0, 1.0])
|
|
675
|
+
"""
|
|
676
|
+
axis_yrange: NDArray[Any] = np.array(self.axis.get_ylim())
|
|
677
|
+
return axis_yrange
|
|
678
|
+
|
|
679
|
+
def set_axis_xrange(self, xrange: NDArray[Any]) -> None:
|
|
680
|
+
"""
|
|
681
|
+
Sets the x-axis range of the target axis.
|
|
682
|
+
|
|
683
|
+
Parameters
|
|
684
|
+
--------------------
|
|
685
|
+
xrange : numpy.ndarray
|
|
686
|
+
The new x-axis range as a NumPy array.
|
|
687
|
+
|
|
688
|
+
Examples
|
|
689
|
+
--------------------
|
|
690
|
+
>>> controller = AxisRangeController(axis_index=0)
|
|
691
|
+
>>> controller.set_axis_xrange(np.array([0.5, 1.5]))
|
|
692
|
+
>>> print(controller.get_axis_xrange())
|
|
693
|
+
array([0.5, 1.5])
|
|
694
|
+
"""
|
|
695
|
+
xrange_tuple = tuple(xrange)
|
|
696
|
+
self.axis.set_xlim(xrange_tuple)
|
|
697
|
+
|
|
698
|
+
def set_axis_yrange(self, yrange: NDArray[Any]) -> None:
|
|
699
|
+
"""
|
|
700
|
+
Sets the y-axis range of the target axis.
|
|
701
|
+
|
|
702
|
+
Parameters
|
|
703
|
+
--------------------
|
|
704
|
+
yrange : numpy.ndarray
|
|
705
|
+
The new y-axis range as a NumPy array.
|
|
706
|
+
|
|
707
|
+
Examples
|
|
708
|
+
--------------------
|
|
709
|
+
>>> controller = AxisRangeController(axis_index=0)
|
|
710
|
+
>>> controller.set_axis_yrange(np.array([0.2, 0.8]))
|
|
711
|
+
>>> print(controller.get_axis_yrange())
|
|
712
|
+
array([0.2, 0.8])
|
|
713
|
+
"""
|
|
714
|
+
yrange_tuple = tuple(yrange)
|
|
715
|
+
self.axis.set_ylim(yrange_tuple)
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
class AxisRangeManager:
|
|
719
|
+
"""
|
|
720
|
+
A manager for handling axis range-related operations in Matplotlib.
|
|
721
|
+
|
|
722
|
+
This class provides functionality to determine whether a given axis is initialized
|
|
723
|
+
or has any existing plots (lines) drawn on it.
|
|
724
|
+
|
|
725
|
+
Parameters
|
|
726
|
+
--------------------
|
|
727
|
+
axis_index : int
|
|
728
|
+
The index of the target axis in the current figure.
|
|
729
|
+
|
|
730
|
+
Attributes
|
|
731
|
+
--------------------
|
|
732
|
+
axis_index : int
|
|
733
|
+
The index of the target axis.
|
|
734
|
+
axis : matplotlib.axes.Axes
|
|
735
|
+
The resolved `Axes` object corresponding to the target index.
|
|
736
|
+
|
|
737
|
+
Methods
|
|
738
|
+
--------------------
|
|
739
|
+
is_init_axis()
|
|
740
|
+
Checks whether the target axis is initialized (has no plots).
|
|
741
|
+
|
|
742
|
+
Examples
|
|
743
|
+
--------------------
|
|
744
|
+
>>> manager = AxisRangeManager(axis_index=0)
|
|
745
|
+
>>> is_initialized = manager.is_init_axis()
|
|
746
|
+
>>> print(is_initialized)
|
|
747
|
+
True # No lines plotted yet
|
|
748
|
+
|
|
749
|
+
>>> plt.plot([1, 2, 3], [4, 5, 6])
|
|
750
|
+
>>> is_initialized = manager.is_init_axis()
|
|
751
|
+
>>> print(is_initialized)
|
|
752
|
+
False # A line plot exists on the axis
|
|
753
|
+
"""
|
|
754
|
+
|
|
755
|
+
def __init__(self, axis_index: int):
|
|
756
|
+
self.axis_index = axis_index
|
|
757
|
+
|
|
758
|
+
self.axis: Axes = AxesResolver(self.axis_index).axis
|
|
759
|
+
|
|
760
|
+
def is_init_axis(self) -> bool:
|
|
761
|
+
"""
|
|
762
|
+
Checks whether the target axis is initialized (has no plots).
|
|
763
|
+
|
|
764
|
+
This method determines if the axis has no lines plotted, indicating that it is in
|
|
765
|
+
its initial state.
|
|
766
|
+
|
|
767
|
+
Returns
|
|
768
|
+
--------------------
|
|
769
|
+
bool
|
|
770
|
+
`True` if the axis has no plots (lines), `False` otherwise.
|
|
771
|
+
|
|
772
|
+
Examples
|
|
773
|
+
--------------------
|
|
774
|
+
>>> manager = AxisRangeManager(axis_index=0)
|
|
775
|
+
>>> is_initialized = manager.is_init_axis()
|
|
776
|
+
>>> print(is_initialized)
|
|
777
|
+
True # No lines plotted yet
|
|
778
|
+
|
|
779
|
+
>>> plt.plot([1, 2, 3], [4, 5, 6])
|
|
780
|
+
>>> is_initialized = manager.is_init_axis()
|
|
781
|
+
>>> print(is_initialized)
|
|
782
|
+
False # A line plot exists on the axis
|
|
783
|
+
"""
|
|
784
|
+
num_lines = len(self.axis.lines)
|
|
785
|
+
|
|
786
|
+
if num_lines:
|
|
787
|
+
return False
|
|
788
|
+
else:
|
|
789
|
+
return True
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
class AxisRangeHandler:
|
|
793
|
+
"""
|
|
794
|
+
Handles the computation and updating of axis ranges for a specific Matplotlib axis.
|
|
795
|
+
|
|
796
|
+
This class calculates new axis ranges by considering existing ranges and new data,
|
|
797
|
+
ensuring that the axis ranges encompass all relevant data. It also determines whether
|
|
798
|
+
an axis is in its initial state or has been previously modified.
|
|
799
|
+
|
|
800
|
+
Parameters
|
|
801
|
+
--------------------
|
|
802
|
+
axis_index : int
|
|
803
|
+
The index of the target axis in the current figure.
|
|
804
|
+
xdata : numpy.ndarray
|
|
805
|
+
The x-axis data to consider for range calculation.
|
|
806
|
+
ydata : numpy.ndarray
|
|
807
|
+
The y-axis data to consider for range calculation.
|
|
808
|
+
|
|
809
|
+
Attributes
|
|
810
|
+
--------------------
|
|
811
|
+
axis_index : int
|
|
812
|
+
The index of the target axis.
|
|
813
|
+
xdata : numpy.ndarray
|
|
814
|
+
The x-axis data to consider for range calculation.
|
|
815
|
+
ydata : numpy.ndarray
|
|
816
|
+
The y-axis data to consider for range calculation.
|
|
817
|
+
axis : matplotlib.axes.Axes
|
|
818
|
+
The resolved `Axes` object corresponding to the target index.
|
|
819
|
+
_is_init_axis : bool
|
|
820
|
+
Indicates whether the axis is in its initial state (no plots or data).
|
|
821
|
+
|
|
822
|
+
Methods
|
|
823
|
+
--------------------
|
|
824
|
+
get_new_axis_range()
|
|
825
|
+
Calculates the new axis range, combining existing ranges and new data ranges.
|
|
826
|
+
|
|
827
|
+
Examples
|
|
828
|
+
--------------------
|
|
829
|
+
>>> xdata = np.array([0, 1, 2, 3])
|
|
830
|
+
>>> ydata = np.array([4, 5, 6, 7])
|
|
831
|
+
>>> handler = AxisRangeHandler(axis_index=0, xdata=xdata, ydata=ydata)
|
|
832
|
+
>>> new_xrange, new_yrange = handler.get_new_axis_range()
|
|
833
|
+
>>> print(new_xrange)
|
|
834
|
+
array([0, 3])
|
|
835
|
+
>>> print(new_yrange)
|
|
836
|
+
array([4, 7])
|
|
837
|
+
"""
|
|
838
|
+
|
|
839
|
+
def __init__(self, axis_index: int, xdata: NDArray[Any], ydata: NDArray[Any]):
|
|
840
|
+
self.axis_index = axis_index
|
|
841
|
+
self.xdata = xdata
|
|
842
|
+
self.ydata = ydata
|
|
843
|
+
|
|
844
|
+
self.axis: Axes = AxesResolver(self.axis_index).axis
|
|
845
|
+
|
|
846
|
+
self._is_init_axis: bool = AxisRangeManager(self.axis_index).is_init_axis()
|
|
847
|
+
|
|
848
|
+
def _get_axis_range(
|
|
849
|
+
self,
|
|
850
|
+
) -> tuple[NDArray | None, NDArray | None] | None:
|
|
851
|
+
"""
|
|
852
|
+
Retrieves the current axis ranges (x and y) if the axis is not in its initial state.
|
|
853
|
+
|
|
854
|
+
Returns
|
|
855
|
+
--------------------
|
|
856
|
+
tuple of (numpy.ndarray or None, numpy.ndarray or None)
|
|
857
|
+
The x-axis and y-axis ranges. Returns `(None, None)` if the axis is in its initial state.
|
|
858
|
+
|
|
859
|
+
Examples
|
|
860
|
+
--------------------
|
|
861
|
+
>>> handler = AxisRangeHandler(axis_index=0, xdata=np.array([]), ydata=np.array([]))
|
|
862
|
+
>>> axis_range = handler._get_axis_range()
|
|
863
|
+
>>> print(axis_range)
|
|
864
|
+
(array([0.0, 1.0]), array([0.0, 1.0]))
|
|
865
|
+
"""
|
|
866
|
+
if self._is_init_axis:
|
|
867
|
+
return None, None
|
|
868
|
+
else:
|
|
869
|
+
axis_xrange = AxisRangeController(self.axis_index).get_axis_xrange()
|
|
870
|
+
axis_yrange = AxisRangeController(self.axis_index).get_axis_yrange()
|
|
871
|
+
return axis_xrange, axis_yrange
|
|
872
|
+
|
|
873
|
+
def _calculate_data_range(self, data: NDArray[Any]) -> NDArray[Any]:
|
|
874
|
+
"""
|
|
875
|
+
Calculates the minimum and maximum range for the given data.
|
|
876
|
+
|
|
877
|
+
Parameters
|
|
878
|
+
--------------------
|
|
879
|
+
data : numpy.ndarray
|
|
880
|
+
The data for which to calculate the range.
|
|
881
|
+
|
|
882
|
+
Returns
|
|
883
|
+
--------------------
|
|
884
|
+
numpy.ndarray
|
|
885
|
+
The range of the data as a NumPy array `[min, max]`.
|
|
886
|
+
|
|
887
|
+
Examples
|
|
888
|
+
--------------------
|
|
889
|
+
>>> handler = AxisRangeHandler(axis_index=0, xdata=np.array([0, 1, 2]), ydata=np.array([]))
|
|
890
|
+
>>> data_range = handler._calculate_data_range(np.array([1, 2, 3]))
|
|
891
|
+
>>> print(data_range)
|
|
892
|
+
array([1, 3])
|
|
893
|
+
"""
|
|
894
|
+
min_data = np.min(data)
|
|
895
|
+
max_data = np.max(data)
|
|
896
|
+
return np.array([min_data, max_data])
|
|
897
|
+
|
|
898
|
+
def get_new_axis_range(
|
|
899
|
+
self,
|
|
900
|
+
) -> tuple[NDArray | None, NDArray | None]:
|
|
901
|
+
"""
|
|
902
|
+
Calculates the new axis ranges based on existing ranges and new data.
|
|
903
|
+
|
|
904
|
+
If the axis is in its initial state, it returns the range of the new data.
|
|
905
|
+
Otherwise, it computes the wider range encompassing both the existing range
|
|
906
|
+
and the new data range.
|
|
907
|
+
|
|
908
|
+
Returns
|
|
909
|
+
--------------------
|
|
910
|
+
tuple of (numpy.ndarray or None, numpy.ndarray or None)
|
|
911
|
+
The new x-axis and y-axis ranges.
|
|
912
|
+
|
|
913
|
+
Examples
|
|
914
|
+
--------------------
|
|
915
|
+
>>> xdata = np.array([0, 1, 2, 3])
|
|
916
|
+
>>> ydata = np.array([4, 5, 6, 7])
|
|
917
|
+
>>> handler = AxisRangeHandler(axis_index=0, xdata=xdata, ydata=ydata)
|
|
918
|
+
>>> new_xrange, new_yrange = handler.get_new_axis_range()
|
|
919
|
+
>>> print(new_xrange)
|
|
920
|
+
array([0, 3])
|
|
921
|
+
>>> print(new_yrange)
|
|
922
|
+
array([4, 7])
|
|
923
|
+
"""
|
|
924
|
+
axis_range = self._get_axis_range()
|
|
925
|
+
if axis_range is None:
|
|
926
|
+
return None, None
|
|
927
|
+
|
|
928
|
+
xrange, yrange = axis_range
|
|
929
|
+
xrange_data, yrange_data = (
|
|
930
|
+
self._calculate_data_range(self.xdata),
|
|
931
|
+
self._calculate_data_range(self.ydata),
|
|
932
|
+
)
|
|
933
|
+
|
|
934
|
+
if xrange is None:
|
|
935
|
+
new_xrange = xrange_data
|
|
936
|
+
else:
|
|
937
|
+
new_xrange = np.array([xrange[0], xrange[1]])
|
|
938
|
+
|
|
939
|
+
if yrange is None:
|
|
940
|
+
new_yrange = yrange_data
|
|
941
|
+
else:
|
|
942
|
+
new_yrange = np.array([yrange[0], yrange[1]])
|
|
943
|
+
|
|
944
|
+
if xrange is not None and yrange is not None:
|
|
945
|
+
new_xrange = np.array(
|
|
946
|
+
[min(xrange[0], xrange_data[0]), max(xrange[1], xrange_data[1])]
|
|
947
|
+
)
|
|
948
|
+
new_yrange = np.array(
|
|
949
|
+
[min(yrange[0], yrange_data[0]), max(yrange[1], yrange_data[1])]
|
|
950
|
+
)
|
|
951
|
+
|
|
952
|
+
return new_xrange, new_yrange
|