rgrid-python 4.5.3__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.
- grid_py/__init__.py +340 -0
- grid_py/_arrow.py +331 -0
- grid_py/_clippath.py +170 -0
- grid_py/_colour.py +815 -0
- grid_py/_coords.py +1534 -0
- grid_py/_curve.py +1668 -0
- grid_py/_display_list.py +507 -0
- grid_py/_draw.py +1397 -0
- grid_py/_edit.py +756 -0
- grid_py/_font_metrics.py +319 -0
- grid_py/_gpar.py +572 -0
- grid_py/_grab.py +501 -0
- grid_py/_grob.py +1377 -0
- grid_py/_group.py +798 -0
- grid_py/_highlevel.py +2176 -0
- grid_py/_just.py +361 -0
- grid_py/_layout.py +593 -0
- grid_py/_ls.py +895 -0
- grid_py/_mask.py +196 -0
- grid_py/_path.py +414 -0
- grid_py/_patterns.py +1049 -0
- grid_py/_primitives.py +2198 -0
- grid_py/_renderer_base.py +1184 -0
- grid_py/_scene_graph.py +248 -0
- grid_py/_size.py +1352 -0
- grid_py/_state.py +683 -0
- grid_py/_transforms.py +448 -0
- grid_py/_typeset.py +384 -0
- grid_py/_units.py +1924 -0
- grid_py/_utils.py +310 -0
- grid_py/_viewport.py +1649 -0
- grid_py/_vp_calc.py +970 -0
- grid_py/py.typed +0 -0
- grid_py/renderer.py +1762 -0
- grid_py/renderer_web.py +764 -0
- grid_py/resources/d3.v7.min.js +2 -0
- grid_py/resources/gridpy.css +80 -0
- grid_py/resources/gridpy.js +813 -0
- rgrid_python-4.5.3.dist-info/METADATA +489 -0
- rgrid_python-4.5.3.dist-info/RECORD +42 -0
- rgrid_python-4.5.3.dist-info/WHEEL +4 -0
- rgrid_python-4.5.3.dist-info/licenses/LICENSE +3 -0
grid_py/_just.py
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"""Justification utilities for grid_py.
|
|
2
|
+
|
|
3
|
+
This module ports the justification logic from R's *grid* package
|
|
4
|
+
(``src/library/grid/R/just.R``) to Python. Justification values
|
|
5
|
+
control where a graphical object is placed relative to its anchor
|
|
6
|
+
point.
|
|
7
|
+
|
|
8
|
+
Numeric convention (matching R):
|
|
9
|
+
|
|
10
|
+
* 0 -- left / bottom
|
|
11
|
+
* 0.5 -- centre
|
|
12
|
+
* 1 -- right / top
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import List, Optional, Sequence, Tuple, Union
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"valid_just",
|
|
23
|
+
"resolve_hjust",
|
|
24
|
+
"resolve_vjust",
|
|
25
|
+
"resolve_raster_size",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
# ------------------------------------------------------------------ #
|
|
29
|
+
# Internal look-up tables #
|
|
30
|
+
# ------------------------------------------------------------------ #
|
|
31
|
+
|
|
32
|
+
# The order matches the R enum in lattice.h:
|
|
33
|
+
# "left"=0, "right"=1, "bottom"=2, "top"=3, "centre"=4, "center"=5
|
|
34
|
+
_JUST_STRINGS: Tuple[str, ...] = (
|
|
35
|
+
"left",
|
|
36
|
+
"right",
|
|
37
|
+
"bottom",
|
|
38
|
+
"top",
|
|
39
|
+
"centre",
|
|
40
|
+
"center",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Mapping from enum index to numeric justification
|
|
44
|
+
_HJUST_MAP = {0: 0.0, 1: 1.0, 4: 0.5, 5: 0.5} # left # right # centre # center
|
|
45
|
+
_VJUST_MAP = {2: 0.0, 3: 1.0, 4: 0.5, 5: 0.5} # bottom # top # centre # center
|
|
46
|
+
|
|
47
|
+
# When a single string is provided, R expands it to (hjust, vjust) as:
|
|
48
|
+
# left -> (left, centre)
|
|
49
|
+
# right -> (right, centre)
|
|
50
|
+
# bottom -> (centre, bottom)
|
|
51
|
+
# top -> (centre, top)
|
|
52
|
+
# centre -> (centre, centre)
|
|
53
|
+
# center -> (centre, centre)
|
|
54
|
+
_SINGLE_EXPAND = {
|
|
55
|
+
0: (0, 4), # left -> (left, centre)
|
|
56
|
+
1: (1, 4), # right -> (right, centre)
|
|
57
|
+
2: (4, 2), # bottom -> (centre, bottom)
|
|
58
|
+
3: (4, 3), # top -> (centre, top)
|
|
59
|
+
4: (4, 4), # centre -> (centre, centre)
|
|
60
|
+
5: (4, 4), # center -> (centre, centre)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Valid enum indices for each axis
|
|
64
|
+
_VALID_HJUST_INDICES = {0, 1, 4, 5} # left, right, centre, center
|
|
65
|
+
_VALID_VJUST_INDICES = {2, 3, 4, 5} # bottom, top, centre, center
|
|
66
|
+
|
|
67
|
+
# Convenient string-to-float shortcuts
|
|
68
|
+
_STR_TO_HJUST = {"left": 0.0, "right": 1.0, "centre": 0.5, "center": 0.5}
|
|
69
|
+
_STR_TO_VJUST = {"bottom": 0.0, "top": 1.0, "centre": 0.5, "center": 0.5}
|
|
70
|
+
|
|
71
|
+
# Type aliases
|
|
72
|
+
JustSpec = Union[str, float, int, Sequence[Union[str, float, int]]]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# ------------------------------------------------------------------ #
|
|
76
|
+
# Internal helpers #
|
|
77
|
+
# ------------------------------------------------------------------ #
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _match_just_string(s: str) -> int:
|
|
81
|
+
"""Return the enum index for a justification string, or raise."""
|
|
82
|
+
try:
|
|
83
|
+
return _JUST_STRINGS.index(s)
|
|
84
|
+
except ValueError:
|
|
85
|
+
raise ValueError(
|
|
86
|
+
f"invalid justification string {s!r}; "
|
|
87
|
+
f"must be one of {_JUST_STRINGS}"
|
|
88
|
+
) from None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _valid_charjust(just: Sequence[str]) -> Tuple[float, float]:
|
|
92
|
+
"""Validate and convert character justification (mirrors ``valid.charjust`` in R).
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
just : sequence of str
|
|
97
|
+
One or two justification strings.
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
tuple of float
|
|
102
|
+
``(hjust, vjust)`` each in [0, 1].
|
|
103
|
+
"""
|
|
104
|
+
n = len(just)
|
|
105
|
+
if n == 0:
|
|
106
|
+
return (0.5, 0.5)
|
|
107
|
+
|
|
108
|
+
if n == 1:
|
|
109
|
+
idx = _match_just_string(just[0])
|
|
110
|
+
h_idx, v_idx = _SINGLE_EXPAND[idx]
|
|
111
|
+
else:
|
|
112
|
+
h_idx = _match_just_string(just[0])
|
|
113
|
+
if h_idx not in _VALID_HJUST_INDICES:
|
|
114
|
+
raise ValueError(
|
|
115
|
+
f"invalid horizontal justification {just[0]!r}; "
|
|
116
|
+
"must be 'left', 'right', 'centre', or 'center'"
|
|
117
|
+
)
|
|
118
|
+
v_idx = _match_just_string(just[1])
|
|
119
|
+
if v_idx not in _VALID_VJUST_INDICES:
|
|
120
|
+
raise ValueError(
|
|
121
|
+
f"invalid vertical justification {just[1]!r}; "
|
|
122
|
+
"must be 'bottom', 'top', 'centre', or 'center'"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
hjust = _HJUST_MAP.get(h_idx)
|
|
126
|
+
vjust = _VJUST_MAP.get(v_idx)
|
|
127
|
+
if hjust is None or vjust is None:
|
|
128
|
+
raise ValueError("invalid justification")
|
|
129
|
+
return (hjust, vjust)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _valid_numjust(
|
|
133
|
+
just: Sequence[Union[int, float]],
|
|
134
|
+
) -> Tuple[float, float]:
|
|
135
|
+
"""Validate and convert numeric justification (mirrors ``valid.numjust`` in R).
|
|
136
|
+
|
|
137
|
+
Parameters
|
|
138
|
+
----------
|
|
139
|
+
just : sequence of int or float
|
|
140
|
+
Zero, one, or two numeric justification values.
|
|
141
|
+
|
|
142
|
+
Returns
|
|
143
|
+
-------
|
|
144
|
+
tuple of float
|
|
145
|
+
``(hjust, vjust)``.
|
|
146
|
+
"""
|
|
147
|
+
n = len(just)
|
|
148
|
+
if n == 0:
|
|
149
|
+
return (0.5, 0.5)
|
|
150
|
+
if n == 1:
|
|
151
|
+
return (float(just[0]), 0.5)
|
|
152
|
+
return (float(just[0]), float(just[1]))
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# ------------------------------------------------------------------ #
|
|
156
|
+
# Public API #
|
|
157
|
+
# ------------------------------------------------------------------ #
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def valid_just(just: JustSpec) -> Tuple[float, float]:
|
|
161
|
+
"""Validate and normalise a justification specification.
|
|
162
|
+
|
|
163
|
+
This mirrors R's ``valid.just`` function. Justification may be
|
|
164
|
+
given as:
|
|
165
|
+
|
|
166
|
+
* a single string (``"left"``, ``"right"``, ``"bottom"``, ``"top"``,
|
|
167
|
+
``"centre"``, ``"center"``),
|
|
168
|
+
* a two-element sequence ``[hjust, vjust]`` where each element is a
|
|
169
|
+
string *or* a number, or
|
|
170
|
+
* a single number (treated as *hjust* with *vjust* defaulting to 0.5).
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
just : str, float, int, or sequence thereof
|
|
175
|
+
Justification specification.
|
|
176
|
+
|
|
177
|
+
Returns
|
|
178
|
+
-------
|
|
179
|
+
tuple of float
|
|
180
|
+
``(hjust, vjust)`` where 0 is left/bottom, 0.5 is centre, and
|
|
181
|
+
1 is right/top.
|
|
182
|
+
|
|
183
|
+
Raises
|
|
184
|
+
------
|
|
185
|
+
ValueError
|
|
186
|
+
If *just* contains an unrecognised string or an invalid
|
|
187
|
+
combination of horizontal/vertical strings.
|
|
188
|
+
|
|
189
|
+
Examples
|
|
190
|
+
--------
|
|
191
|
+
>>> valid_just("centre")
|
|
192
|
+
(0.5, 0.5)
|
|
193
|
+
>>> valid_just("left")
|
|
194
|
+
(0.0, 0.5)
|
|
195
|
+
>>> valid_just(["right", "top"])
|
|
196
|
+
(1.0, 1.0)
|
|
197
|
+
>>> valid_just(0.25)
|
|
198
|
+
(0.25, 0.5)
|
|
199
|
+
>>> valid_just([0.1, 0.9])
|
|
200
|
+
(0.1, 0.9)
|
|
201
|
+
"""
|
|
202
|
+
# Scalar string
|
|
203
|
+
if isinstance(just, str):
|
|
204
|
+
return _valid_charjust([just])
|
|
205
|
+
|
|
206
|
+
# Scalar numeric
|
|
207
|
+
if isinstance(just, (int, float, np.integer, np.floating)):
|
|
208
|
+
return (float(just), 0.5)
|
|
209
|
+
|
|
210
|
+
# Sequence -- coerce to list for uniform handling
|
|
211
|
+
just_list: List[Union[str, float, int]] = list(just)
|
|
212
|
+
if len(just_list) == 0:
|
|
213
|
+
return (0.5, 0.5)
|
|
214
|
+
|
|
215
|
+
# All-string path
|
|
216
|
+
if all(isinstance(j, str) for j in just_list):
|
|
217
|
+
return _valid_charjust(just_list) # type: ignore[arg-type]
|
|
218
|
+
|
|
219
|
+
# All-numeric path
|
|
220
|
+
if all(isinstance(j, (int, float, np.integer, np.floating)) for j in just_list):
|
|
221
|
+
return _valid_numjust(just_list) # type: ignore[arg-type]
|
|
222
|
+
|
|
223
|
+
# Mixed: try to convert everything to float (matches R's as.numeric)
|
|
224
|
+
try:
|
|
225
|
+
nums = [float(j) for j in just_list]
|
|
226
|
+
except (TypeError, ValueError):
|
|
227
|
+
raise ValueError(
|
|
228
|
+
f"invalid justification specification: {just!r}"
|
|
229
|
+
) from None
|
|
230
|
+
return _valid_numjust(nums)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def resolve_hjust(
|
|
234
|
+
just: JustSpec,
|
|
235
|
+
hjust: Optional[float] = None,
|
|
236
|
+
) -> float:
|
|
237
|
+
"""Resolve horizontal justification to a numeric value.
|
|
238
|
+
|
|
239
|
+
If *hjust* is provided it is returned directly; otherwise the
|
|
240
|
+
horizontal component is extracted from *just* via `valid_just`.
|
|
241
|
+
This mirrors R's ``resolveHJust``.
|
|
242
|
+
|
|
243
|
+
Parameters
|
|
244
|
+
----------
|
|
245
|
+
just : str, float, int, or sequence thereof
|
|
246
|
+
General justification specification (see `valid_just`).
|
|
247
|
+
hjust : float, optional
|
|
248
|
+
Explicit horizontal justification override. When not *None*
|
|
249
|
+
this value is returned as-is.
|
|
250
|
+
|
|
251
|
+
Returns
|
|
252
|
+
-------
|
|
253
|
+
float
|
|
254
|
+
Horizontal justification in [0, 1].
|
|
255
|
+
|
|
256
|
+
Examples
|
|
257
|
+
--------
|
|
258
|
+
>>> resolve_hjust("left")
|
|
259
|
+
0.0
|
|
260
|
+
>>> resolve_hjust("centre", hjust=0.3)
|
|
261
|
+
0.3
|
|
262
|
+
"""
|
|
263
|
+
if hjust is not None:
|
|
264
|
+
return float(hjust)
|
|
265
|
+
return valid_just(just)[0]
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def resolve_vjust(
|
|
269
|
+
just: JustSpec,
|
|
270
|
+
vjust: Optional[float] = None,
|
|
271
|
+
) -> float:
|
|
272
|
+
"""Resolve vertical justification to a numeric value.
|
|
273
|
+
|
|
274
|
+
If *vjust* is provided it is returned directly; otherwise the
|
|
275
|
+
vertical component is extracted from *just* via `valid_just`.
|
|
276
|
+
This mirrors R's ``resolveVJust``.
|
|
277
|
+
|
|
278
|
+
Parameters
|
|
279
|
+
----------
|
|
280
|
+
just : str, float, int, or sequence thereof
|
|
281
|
+
General justification specification (see `valid_just`).
|
|
282
|
+
vjust : float, optional
|
|
283
|
+
Explicit vertical justification override. When not *None*
|
|
284
|
+
this value is returned as-is.
|
|
285
|
+
|
|
286
|
+
Returns
|
|
287
|
+
-------
|
|
288
|
+
float
|
|
289
|
+
Vertical justification in [0, 1].
|
|
290
|
+
|
|
291
|
+
Examples
|
|
292
|
+
--------
|
|
293
|
+
>>> resolve_vjust("top")
|
|
294
|
+
1.0
|
|
295
|
+
>>> resolve_vjust("centre", vjust=0.8)
|
|
296
|
+
0.8
|
|
297
|
+
"""
|
|
298
|
+
if vjust is not None:
|
|
299
|
+
return float(vjust)
|
|
300
|
+
return valid_just(just)[1]
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def resolve_raster_size(
|
|
304
|
+
x: np.ndarray,
|
|
305
|
+
default_size: Tuple[float, float],
|
|
306
|
+
target_size: Tuple[Optional[float], Optional[float]],
|
|
307
|
+
) -> Tuple[float, float]:
|
|
308
|
+
"""Compute width and height for a raster grob.
|
|
309
|
+
|
|
310
|
+
When either the target width or height is *None* the missing
|
|
311
|
+
dimension is derived from the raster's aspect ratio and the
|
|
312
|
+
available viewport size, matching R's ``resolveRasterSize``.
|
|
313
|
+
|
|
314
|
+
Parameters
|
|
315
|
+
----------
|
|
316
|
+
x : numpy.ndarray
|
|
317
|
+
The raster data array. Its shape ``(nrow, ncol, ...)`` is used
|
|
318
|
+
to compute the aspect ratio (``nrow / ncol``).
|
|
319
|
+
default_size : tuple of float
|
|
320
|
+
``(viewport_width, viewport_height)`` in inches, used when both
|
|
321
|
+
*width* and *height* are *None*.
|
|
322
|
+
target_size : tuple of float or None
|
|
323
|
+
``(width, height)`` requested by the user. Either or both may
|
|
324
|
+
be *None* to indicate "auto".
|
|
325
|
+
|
|
326
|
+
Returns
|
|
327
|
+
-------
|
|
328
|
+
tuple of float
|
|
329
|
+
``(width, height)`` in the same units as *default_size* /
|
|
330
|
+
*target_size*.
|
|
331
|
+
|
|
332
|
+
Examples
|
|
333
|
+
--------
|
|
334
|
+
>>> import numpy as np
|
|
335
|
+
>>> img = np.zeros((200, 400, 3))
|
|
336
|
+
>>> resolve_raster_size(img, (6.0, 4.0), (None, None))
|
|
337
|
+
(6.0, 3.0)
|
|
338
|
+
"""
|
|
339
|
+
nrow, ncol = x.shape[0], x.shape[1]
|
|
340
|
+
raster_ratio = nrow / ncol # height / width in pixels
|
|
341
|
+
|
|
342
|
+
width, height = target_size
|
|
343
|
+
|
|
344
|
+
if width is None and height is None:
|
|
345
|
+
vp_width, vp_height = default_size
|
|
346
|
+
vp_ratio = vp_height / vp_width
|
|
347
|
+
if raster_ratio > vp_ratio:
|
|
348
|
+
# raster is taller (relative) than viewport
|
|
349
|
+
height = vp_height
|
|
350
|
+
width = vp_height * ncol / nrow
|
|
351
|
+
else:
|
|
352
|
+
width = vp_width
|
|
353
|
+
height = vp_width * nrow / ncol
|
|
354
|
+
elif width is None:
|
|
355
|
+
# height is known; derive width
|
|
356
|
+
width = height * ncol / nrow
|
|
357
|
+
elif height is None:
|
|
358
|
+
# width is known; derive height
|
|
359
|
+
height = width * nrow / ncol
|
|
360
|
+
|
|
361
|
+
return (float(width), float(height))
|