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/_primitives.py ADDED
@@ -0,0 +1,2198 @@
1
+ """Primitive grob constructors and ``grid_*`` drawing functions.
2
+
3
+ This module is the Python port of R's ``grid/R/primitives.R``,
4
+ ``grid/R/roundRect.R``, and ``grid/R/function.R`` (~1699 lines combined).
5
+ It provides every primitive grob type available in the *grid* graphics
6
+ system together with convenience ``grid_*`` functions that optionally
7
+ draw them immediately.
8
+
9
+ Each primitive follows a consistent three-part pattern:
10
+
11
+ 1. A :class:`~grid_py._grob.Grob` instance with a specific ``_grid_class``.
12
+ 2. A *constructor* function (e.g. :func:`rect_grob`) that builds the grob.
13
+ 3. A ``grid_*`` wrapper (e.g. :func:`grid_rect`) with a ``draw`` parameter.
14
+
15
+ Notes
16
+ -----
17
+ When ``draw=True`` the grob is passed to :func:`grid_draw` for immediate
18
+ rendering. When ``draw=False`` the grob is simply returned.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from typing import (
24
+ Any,
25
+ Callable,
26
+ List,
27
+ Optional,
28
+ Sequence,
29
+ Union,
30
+ )
31
+
32
+ from ._arrow import Arrow
33
+ from ._gpar import Gpar
34
+ from ._grob import Grob, GTree
35
+ from ._just import valid_just
36
+ from ._units import Unit, is_unit
37
+
38
+ __all__ = [
39
+ # move.to
40
+ "move_to_grob",
41
+ "grid_move_to",
42
+ # line.to
43
+ "line_to_grob",
44
+ "grid_line_to",
45
+ # lines
46
+ "lines_grob",
47
+ "grid_lines",
48
+ # polyline
49
+ "polyline_grob",
50
+ "grid_polyline",
51
+ # segments
52
+ "segments_grob",
53
+ "grid_segments",
54
+ # arrows (defunct, kept for API completeness)
55
+ "arrows_grob",
56
+ "grid_arrows",
57
+ # points
58
+ "points_grob",
59
+ "grid_points",
60
+ # rect
61
+ "rect_grob",
62
+ "grid_rect",
63
+ # roundrect
64
+ "roundrect_grob",
65
+ "grid_roundrect",
66
+ # circle
67
+ "circle_grob",
68
+ "grid_circle",
69
+ # polygon
70
+ "polygon_grob",
71
+ "grid_polygon",
72
+ # path
73
+ "path_grob",
74
+ "grid_path",
75
+ # text
76
+ "text_grob",
77
+ "grid_text",
78
+ # raster
79
+ "raster_grob",
80
+ "grid_raster",
81
+ # clip
82
+ "clip_grob",
83
+ "grid_clip",
84
+ # null
85
+ "null_grob",
86
+ "grid_null",
87
+ # function
88
+ "function_grob",
89
+ "grid_function",
90
+ ]
91
+
92
+ def _grid_draw(grob: Grob) -> None:
93
+ """Draw *grob* via the rendering back-end and record it.
94
+
95
+ Parameters
96
+ ----------
97
+ grob : Grob
98
+ The graphical object to draw and record.
99
+ """
100
+ from ._draw import grid_draw # lazy import to avoid circular dependency
101
+
102
+ grid_draw(grob, recording=True)
103
+
104
+
105
+ # ---------------------------------------------------------------------------
106
+ # Helper: ensure a value is a Unit
107
+ # ---------------------------------------------------------------------------
108
+
109
+
110
+ def _ensure_unit(
111
+ x: Any,
112
+ default_units: str,
113
+ ) -> Unit:
114
+ """Convert *x* to a :class:`Unit` if it is not already one.
115
+
116
+ Parameters
117
+ ----------
118
+ x : Any
119
+ A numeric scalar, sequence of numerics, or an existing ``Unit``.
120
+ default_units : str
121
+ The unit string to use when *x* is not already a ``Unit``
122
+ (e.g. ``"npc"``, ``"inches"``).
123
+
124
+ Returns
125
+ -------
126
+ Unit
127
+ The value wrapped as a ``Unit``.
128
+ """
129
+ if is_unit(x):
130
+ return x
131
+ return Unit(x, default_units)
132
+
133
+
134
+ # ===================================================================== #
135
+ # move.to primitive #
136
+ # ===================================================================== #
137
+
138
+
139
+ def move_to_grob(
140
+ x: Any = 0,
141
+ y: Any = 0,
142
+ default_units: str = "npc",
143
+ name: Optional[str] = None,
144
+ vp: Optional[Any] = None,
145
+ ) -> Grob:
146
+ """Create a *move.to* grob.
147
+
148
+ A *move.to* grob sets the current drawing position without producing
149
+ any visible output. It is the counterpart to :func:`line_to_grob`.
150
+
151
+ Parameters
152
+ ----------
153
+ x : Unit or numeric
154
+ Horizontal coordinate. Converted using *default_units* when not
155
+ already a ``Unit``.
156
+ y : Unit or numeric
157
+ Vertical coordinate.
158
+ default_units : str
159
+ Unit type applied to bare numeric *x* / *y*.
160
+ name : str or None
161
+ Grob name. Auto-generated when ``None``.
162
+ vp : viewport or None
163
+ Optional viewport.
164
+
165
+ Returns
166
+ -------
167
+ Grob
168
+ A grob with ``_grid_class="move.to"``.
169
+ """
170
+ x = _ensure_unit(x, default_units)
171
+ y = _ensure_unit(y, default_units)
172
+ return Grob(x=x, y=y, name=name, vp=vp, _grid_class="move.to")
173
+
174
+
175
+ def grid_move_to(
176
+ x: Any = 0,
177
+ y: Any = 0,
178
+ default_units: str = "npc",
179
+ name: Optional[str] = None,
180
+ draw: bool = True,
181
+ vp: Optional[Any] = None,
182
+ ) -> Grob:
183
+ """Create and optionally draw a *move.to* grob.
184
+
185
+ Parameters
186
+ ----------
187
+ x : Unit or numeric
188
+ Horizontal coordinate.
189
+ y : Unit or numeric
190
+ Vertical coordinate.
191
+ default_units : str
192
+ Default unit type for bare numerics.
193
+ name : str or None
194
+ Grob name.
195
+ draw : bool
196
+ If ``True`` (default), record the grob for drawing.
197
+ vp : viewport or None
198
+ Optional viewport.
199
+
200
+ Returns
201
+ -------
202
+ Grob
203
+ The created grob (returned invisibly when *draw* is ``True``).
204
+ """
205
+ grob = move_to_grob(x=x, y=y, default_units=default_units, name=name, vp=vp)
206
+ if draw:
207
+ _grid_draw(grob)
208
+ return grob
209
+
210
+
211
+ # ===================================================================== #
212
+ # line.to primitive #
213
+ # ===================================================================== #
214
+
215
+
216
+ def line_to_grob(
217
+ x: Any = 1,
218
+ y: Any = 1,
219
+ default_units: str = "npc",
220
+ arrow: Optional[Arrow] = None,
221
+ name: Optional[str] = None,
222
+ gp: Optional[Gpar] = None,
223
+ vp: Optional[Any] = None,
224
+ ) -> Grob:
225
+ """Create a *line.to* grob.
226
+
227
+ A *line.to* grob draws a straight line from the current drawing
228
+ position (set by :func:`move_to_grob`) to the specified location.
229
+
230
+ Parameters
231
+ ----------
232
+ x : Unit or numeric
233
+ Horizontal endpoint.
234
+ y : Unit or numeric
235
+ Vertical endpoint.
236
+ default_units : str
237
+ Unit type for bare numerics.
238
+ arrow : Arrow or None
239
+ Optional arrow-head specification.
240
+ name : str or None
241
+ Grob name.
242
+ gp : Gpar or None
243
+ Graphical parameters.
244
+ vp : viewport or None
245
+ Optional viewport.
246
+
247
+ Returns
248
+ -------
249
+ Grob
250
+ A grob with ``_grid_class="line.to"``.
251
+ """
252
+ x = _ensure_unit(x, default_units)
253
+ y = _ensure_unit(y, default_units)
254
+ return Grob(
255
+ x=x, y=y, arrow=arrow,
256
+ name=name, gp=gp, vp=vp, _grid_class="line.to",
257
+ )
258
+
259
+
260
+ def grid_line_to(
261
+ x: Any = 1,
262
+ y: Any = 1,
263
+ default_units: str = "npc",
264
+ arrow: Optional[Arrow] = None,
265
+ name: Optional[str] = None,
266
+ gp: Optional[Gpar] = None,
267
+ draw: bool = True,
268
+ vp: Optional[Any] = None,
269
+ ) -> Grob:
270
+ """Create and optionally draw a *line.to* grob.
271
+
272
+ Parameters
273
+ ----------
274
+ x : Unit or numeric
275
+ Horizontal endpoint.
276
+ y : Unit or numeric
277
+ Vertical endpoint.
278
+ default_units : str
279
+ Default unit type for bare numerics.
280
+ arrow : Arrow or None
281
+ Optional arrow-head specification.
282
+ name : str or None
283
+ Grob name.
284
+ gp : Gpar or None
285
+ Graphical parameters.
286
+ draw : bool
287
+ If ``True`` (default), record the grob for drawing.
288
+ vp : viewport or None
289
+ Optional viewport.
290
+
291
+ Returns
292
+ -------
293
+ Grob
294
+ The created grob.
295
+ """
296
+ grob = line_to_grob(
297
+ x=x, y=y, default_units=default_units, arrow=arrow,
298
+ name=name, gp=gp, vp=vp,
299
+ )
300
+ if draw:
301
+ _grid_draw(grob)
302
+ return grob
303
+
304
+
305
+ # ===================================================================== #
306
+ # lines primitive #
307
+ # ===================================================================== #
308
+
309
+
310
+ def lines_grob(
311
+ x: Optional[Any] = None,
312
+ y: Optional[Any] = None,
313
+ default_units: str = "npc",
314
+ arrow: Optional[Arrow] = None,
315
+ name: Optional[str] = None,
316
+ gp: Optional[Gpar] = None,
317
+ vp: Optional[Any] = None,
318
+ ) -> Grob:
319
+ """Create a *lines* grob.
320
+
321
+ A *lines* grob draws a single connected polyline through the given
322
+ coordinates.
323
+
324
+ Parameters
325
+ ----------
326
+ x : Unit, numeric, or None
327
+ Horizontal coordinates. Defaults to ``Unit([0, 1], "npc")``
328
+ when ``None``.
329
+ y : Unit, numeric, or None
330
+ Vertical coordinates. Defaults to ``Unit([0, 1], "npc")``
331
+ when ``None``.
332
+ default_units : str
333
+ Unit type for bare numerics.
334
+ arrow : Arrow or None
335
+ Optional arrow-head specification.
336
+ name : str or None
337
+ Grob name.
338
+ gp : Gpar or None
339
+ Graphical parameters.
340
+ vp : viewport or None
341
+ Optional viewport.
342
+
343
+ Returns
344
+ -------
345
+ Grob
346
+ A grob with ``_grid_class="lines"``.
347
+ """
348
+ if x is None:
349
+ x = Unit([0, 1], "npc")
350
+ else:
351
+ x = _ensure_unit(x, default_units)
352
+ if y is None:
353
+ y = Unit([0, 1], "npc")
354
+ else:
355
+ y = _ensure_unit(y, default_units)
356
+ return Grob(
357
+ x=x, y=y, arrow=arrow,
358
+ name=name, gp=gp, vp=vp, _grid_class="lines",
359
+ )
360
+
361
+
362
+ def grid_lines(
363
+ x: Optional[Any] = None,
364
+ y: Optional[Any] = None,
365
+ default_units: str = "npc",
366
+ arrow: Optional[Arrow] = None,
367
+ name: Optional[str] = None,
368
+ gp: Optional[Gpar] = None,
369
+ draw: bool = True,
370
+ vp: Optional[Any] = None,
371
+ ) -> Grob:
372
+ """Create and optionally draw a *lines* grob.
373
+
374
+ Parameters
375
+ ----------
376
+ x : Unit, numeric, or None
377
+ Horizontal coordinates. Defaults to ``Unit([0, 1], "npc")``.
378
+ y : Unit, numeric, or None
379
+ Vertical coordinates. Defaults to ``Unit([0, 1], "npc")``.
380
+ default_units : str
381
+ Default unit type for bare numerics.
382
+ arrow : Arrow or None
383
+ Optional arrow-head specification.
384
+ name : str or None
385
+ Grob name.
386
+ gp : Gpar or None
387
+ Graphical parameters.
388
+ draw : bool
389
+ If ``True`` (default), record the grob for drawing.
390
+ vp : viewport or None
391
+ Optional viewport.
392
+
393
+ Returns
394
+ -------
395
+ Grob
396
+ The created grob.
397
+ """
398
+ grob = lines_grob(
399
+ x=x, y=y, default_units=default_units, arrow=arrow,
400
+ name=name, gp=gp, vp=vp,
401
+ )
402
+ if draw:
403
+ _grid_draw(grob)
404
+ return grob
405
+
406
+
407
+ # ===================================================================== #
408
+ # polyline primitive #
409
+ # ===================================================================== #
410
+
411
+
412
+ def polyline_grob(
413
+ x: Any = None,
414
+ y: Any = None,
415
+ id: Optional[Sequence[int]] = None,
416
+ id_lengths: Optional[Sequence[int]] = None,
417
+ default_units: str = "npc",
418
+ arrow: Optional[Arrow] = None,
419
+ name: Optional[str] = None,
420
+ gp: Optional[Gpar] = None,
421
+ vp: Optional[Any] = None,
422
+ ) -> Grob:
423
+ """Create a *polyline* grob.
424
+
425
+ A *polyline* grob draws one or more disconnected polylines through
426
+ the given coordinates. Sub-polylines are identified either by *id*
427
+ (a per-point group label) or *id_lengths* (lengths of consecutive
428
+ runs). Only one of *id* and *id_lengths* may be specified.
429
+
430
+ Parameters
431
+ ----------
432
+ x : Unit or numeric
433
+ Horizontal coordinates. Defaults to ``Unit([0, 1], "npc")``.
434
+ y : Unit or numeric
435
+ Vertical coordinates. Defaults to ``Unit([0, 1], "npc")``.
436
+ id : sequence of int or None
437
+ Per-point group identifier.
438
+ id_lengths : sequence of int or None
439
+ Lengths of consecutive sub-polylines.
440
+ default_units : str
441
+ Unit type for bare numerics.
442
+ arrow : Arrow or None
443
+ Optional arrow-head specification.
444
+ name : str or None
445
+ Grob name.
446
+ gp : Gpar or None
447
+ Graphical parameters.
448
+ vp : viewport or None
449
+ Optional viewport.
450
+
451
+ Returns
452
+ -------
453
+ Grob
454
+ A grob with ``_grid_class="polyline"``.
455
+
456
+ Raises
457
+ ------
458
+ ValueError
459
+ If both *id* and *id_lengths* are specified.
460
+ """
461
+ if x is None:
462
+ x = Unit([0, 1], "npc")
463
+ else:
464
+ x = _ensure_unit(x, default_units)
465
+ if y is None:
466
+ y = Unit([0, 1], "npc")
467
+ else:
468
+ y = _ensure_unit(y, default_units)
469
+ if id is not None and id_lengths is not None:
470
+ raise ValueError(
471
+ "it is invalid to specify both 'id' and 'id_lengths'"
472
+ )
473
+ return Grob(
474
+ x=x, y=y, id=id, id_lengths=id_lengths, arrow=arrow,
475
+ name=name, gp=gp, vp=vp, _grid_class="polyline",
476
+ )
477
+
478
+
479
+ def grid_polyline(
480
+ x: Any = None,
481
+ y: Any = None,
482
+ id: Optional[Sequence[int]] = None,
483
+ id_lengths: Optional[Sequence[int]] = None,
484
+ default_units: str = "npc",
485
+ arrow: Optional[Arrow] = None,
486
+ name: Optional[str] = None,
487
+ gp: Optional[Gpar] = None,
488
+ draw: bool = True,
489
+ vp: Optional[Any] = None,
490
+ ) -> Grob:
491
+ """Create and optionally draw a *polyline* grob.
492
+
493
+ Parameters
494
+ ----------
495
+ x : Unit or numeric
496
+ Horizontal coordinates.
497
+ y : Unit or numeric
498
+ Vertical coordinates.
499
+ id : sequence of int or None
500
+ Per-point group identifier.
501
+ id_lengths : sequence of int or None
502
+ Lengths of consecutive sub-polylines.
503
+ default_units : str
504
+ Default unit type for bare numerics.
505
+ arrow : Arrow or None
506
+ Optional arrow-head specification.
507
+ name : str or None
508
+ Grob name.
509
+ gp : Gpar or None
510
+ Graphical parameters.
511
+ draw : bool
512
+ If ``True`` (default), record the grob for drawing.
513
+ vp : viewport or None
514
+ Optional viewport.
515
+
516
+ Returns
517
+ -------
518
+ Grob
519
+ The created grob.
520
+ """
521
+ grob = polyline_grob(
522
+ x=x, y=y, id=id, id_lengths=id_lengths,
523
+ default_units=default_units, arrow=arrow,
524
+ name=name, gp=gp, vp=vp,
525
+ )
526
+ if draw:
527
+ _grid_draw(grob)
528
+ return grob
529
+
530
+
531
+ # ===================================================================== #
532
+ # segments primitive #
533
+ # ===================================================================== #
534
+
535
+
536
+ def segments_grob(
537
+ x0: Any = 0,
538
+ y0: Any = 0,
539
+ x1: Any = 1,
540
+ y1: Any = 1,
541
+ default_units: str = "npc",
542
+ arrow: Optional[Arrow] = None,
543
+ name: Optional[str] = None,
544
+ gp: Optional[Gpar] = None,
545
+ vp: Optional[Any] = None,
546
+ ) -> Grob:
547
+ """Create a *segments* grob.
548
+
549
+ A *segments* grob draws one or more independent line segments, each
550
+ defined by start ``(x0, y0)`` and end ``(x1, y1)`` coordinates.
551
+
552
+ Parameters
553
+ ----------
554
+ x0 : Unit or numeric
555
+ Horizontal start coordinate(s).
556
+ y0 : Unit or numeric
557
+ Vertical start coordinate(s).
558
+ x1 : Unit or numeric
559
+ Horizontal end coordinate(s).
560
+ y1 : Unit or numeric
561
+ Vertical end coordinate(s).
562
+ default_units : str
563
+ Unit type for bare numerics.
564
+ arrow : Arrow or None
565
+ Optional arrow-head specification.
566
+ name : str or None
567
+ Grob name.
568
+ gp : Gpar or None
569
+ Graphical parameters.
570
+ vp : viewport or None
571
+ Optional viewport.
572
+
573
+ Returns
574
+ -------
575
+ Grob
576
+ A grob with ``_grid_class="segments"``.
577
+ """
578
+ x0 = _ensure_unit(x0, default_units)
579
+ y0 = _ensure_unit(y0, default_units)
580
+ x1 = _ensure_unit(x1, default_units)
581
+ y1 = _ensure_unit(y1, default_units)
582
+ return Grob(
583
+ x0=x0, y0=y0, x1=x1, y1=y1, arrow=arrow,
584
+ name=name, gp=gp, vp=vp, _grid_class="segments",
585
+ )
586
+
587
+
588
+ def grid_segments(
589
+ x0: Any = 0,
590
+ y0: Any = 0,
591
+ x1: Any = 1,
592
+ y1: Any = 1,
593
+ default_units: str = "npc",
594
+ arrow: Optional[Arrow] = None,
595
+ name: Optional[str] = None,
596
+ gp: Optional[Gpar] = None,
597
+ draw: bool = True,
598
+ vp: Optional[Any] = None,
599
+ ) -> Grob:
600
+ """Create and optionally draw a *segments* grob.
601
+
602
+ Parameters
603
+ ----------
604
+ x0 : Unit or numeric
605
+ Horizontal start coordinate(s).
606
+ y0 : Unit or numeric
607
+ Vertical start coordinate(s).
608
+ x1 : Unit or numeric
609
+ Horizontal end coordinate(s).
610
+ y1 : Unit or numeric
611
+ Vertical end coordinate(s).
612
+ default_units : str
613
+ Default unit type for bare numerics.
614
+ arrow : Arrow or None
615
+ Optional arrow-head specification.
616
+ name : str or None
617
+ Grob name.
618
+ gp : Gpar or None
619
+ Graphical parameters.
620
+ draw : bool
621
+ If ``True`` (default), record the grob for drawing.
622
+ vp : viewport or None
623
+ Optional viewport.
624
+
625
+ Returns
626
+ -------
627
+ Grob
628
+ The created grob.
629
+ """
630
+ grob = segments_grob(
631
+ x0=x0, y0=y0, x1=x1, y1=y1,
632
+ default_units=default_units, arrow=arrow,
633
+ name=name, gp=gp, vp=vp,
634
+ )
635
+ if draw:
636
+ _grid_draw(grob)
637
+ return grob
638
+
639
+
640
+ # ===================================================================== #
641
+ # arrows primitive (defunct in R -- kept for API completeness) #
642
+ # ===================================================================== #
643
+
644
+
645
+ def arrows_grob(
646
+ x: Any = None,
647
+ y: Any = None,
648
+ default_units: str = "npc",
649
+ arrow: Optional[Arrow] = None,
650
+ name: Optional[str] = None,
651
+ gp: Optional[Gpar] = None,
652
+ vp: Optional[Any] = None,
653
+ ) -> Grob:
654
+ """Create an *arrows* grob.
655
+
656
+ .. deprecated::
657
+ **Defunct** since R >= 2.3.0. Use the ``arrow`` argument on
658
+ line-drawing primitives (e.g. ``lines_grob(..., arrow=...)``)
659
+ instead.
660
+
661
+ Raises
662
+ ------
663
+ NotImplementedError
664
+ Always. Matches R's ``.Defunct()`` (``primitives.R:539``).
665
+ """
666
+ # R: .Defunct(msg="'arrowsGrob' is defunct.\n
667
+ # Use 'arrow' arguments to line-drawing primitives.")
668
+ raise NotImplementedError(
669
+ "'arrows_grob' is defunct. "
670
+ "Use the 'arrow' argument on line-drawing primitives instead."
671
+ )
672
+
673
+
674
+ def grid_arrows(
675
+ x: Any = None,
676
+ y: Any = None,
677
+ default_units: str = "npc",
678
+ arrow: Optional[Arrow] = None,
679
+ name: Optional[str] = None,
680
+ gp: Optional[Gpar] = None,
681
+ draw: bool = True,
682
+ vp: Optional[Any] = None,
683
+ ) -> Grob:
684
+ """Create and optionally draw an *arrows* grob.
685
+
686
+ .. deprecated::
687
+ **Defunct** since R >= 2.3.0. Use the ``arrow`` argument on
688
+ line-drawing primitives instead.
689
+
690
+ Raises
691
+ ------
692
+ NotImplementedError
693
+ Always. Matches R's ``.Defunct()`` (``primitives.R:543``).
694
+ """
695
+ # R: .Defunct(msg="'grid.arrows' is defunct.\n
696
+ # Use 'arrow' arguments to line-drawing primitives.")
697
+ raise NotImplementedError(
698
+ "'grid_arrows' is defunct. "
699
+ "Use the 'arrow' argument on line-drawing primitives instead."
700
+ )
701
+
702
+
703
+ # ===================================================================== #
704
+ # points primitive #
705
+ # ===================================================================== #
706
+
707
+
708
+ def points_grob(
709
+ x: Any = None,
710
+ y: Any = None,
711
+ size: Optional[Any] = None,
712
+ default_units: str = "native",
713
+ pch: Union[int, str] = 1,
714
+ name: Optional[str] = None,
715
+ gp: Optional[Gpar] = None,
716
+ vp: Optional[Any] = None,
717
+ ) -> Grob:
718
+ """Create a *points* grob.
719
+
720
+ A *points* grob draws symbols (plotting characters) at the given
721
+ locations, analogous to R's ``points()``.
722
+
723
+ Parameters
724
+ ----------
725
+ x : Unit, numeric, or None
726
+ Horizontal coordinates. When ``None`` a default is chosen.
727
+ y : Unit, numeric, or None
728
+ Vertical coordinates. When ``None`` a default is chosen.
729
+ size : Unit, numeric, or None
730
+ Symbol size. Defaults to ``Unit(1, "char")`` when ``None``.
731
+ default_units : str
732
+ Unit type for bare numerics (default ``"native"``,
733
+ matching R's ``pointsGrob``).
734
+ pch : int or str
735
+ Plotting character / symbol code.
736
+ name : str or None
737
+ Grob name.
738
+ gp : Gpar or None
739
+ Graphical parameters.
740
+ vp : viewport or None
741
+ Optional viewport.
742
+
743
+ Returns
744
+ -------
745
+ Grob
746
+ A grob with ``_grid_class="points"``.
747
+ """
748
+ if x is None:
749
+ x = Unit(0.5, "npc")
750
+ else:
751
+ x = _ensure_unit(x, default_units)
752
+ if y is None:
753
+ y = Unit(0.5, "npc")
754
+ else:
755
+ y = _ensure_unit(y, default_units)
756
+ if size is None:
757
+ size = Unit(1, "char")
758
+ else:
759
+ size = _ensure_unit(size, default_units)
760
+ return Grob(
761
+ x=x, y=y, pch=pch, size=size,
762
+ name=name, gp=gp, vp=vp, _grid_class="points",
763
+ )
764
+
765
+
766
+ def grid_points(
767
+ x: Any = None,
768
+ y: Any = None,
769
+ size: Optional[Any] = None,
770
+ default_units: str = "npc",
771
+ pch: Union[int, str] = 1,
772
+ name: Optional[str] = None,
773
+ gp: Optional[Gpar] = None,
774
+ draw: bool = True,
775
+ vp: Optional[Any] = None,
776
+ ) -> Grob:
777
+ """Create and optionally draw a *points* grob.
778
+
779
+ Parameters
780
+ ----------
781
+ x : Unit, numeric, or None
782
+ Horizontal coordinates.
783
+ y : Unit, numeric, or None
784
+ Vertical coordinates.
785
+ size : Unit, numeric, or None
786
+ Symbol size.
787
+ default_units : str
788
+ Default unit type for bare numerics.
789
+ pch : int or str
790
+ Plotting character / symbol code.
791
+ name : str or None
792
+ Grob name.
793
+ gp : Gpar or None
794
+ Graphical parameters.
795
+ draw : bool
796
+ If ``True`` (default), record the grob for drawing.
797
+ vp : viewport or None
798
+ Optional viewport.
799
+
800
+ Returns
801
+ -------
802
+ Grob
803
+ The created grob.
804
+ """
805
+ grob = points_grob(
806
+ x=x, y=y, size=size, default_units=default_units, pch=pch,
807
+ name=name, gp=gp, vp=vp,
808
+ )
809
+ if draw:
810
+ _grid_draw(grob)
811
+ return grob
812
+
813
+
814
+ # ===================================================================== #
815
+ # rect primitive #
816
+ # ===================================================================== #
817
+
818
+
819
+ def rect_grob(
820
+ x: Any = 0.5,
821
+ y: Any = 0.5,
822
+ width: Any = 1,
823
+ height: Any = 1,
824
+ default_units: str = "npc",
825
+ just: Union[str, Sequence[str]] = "centre",
826
+ hjust: Optional[float] = None,
827
+ vjust: Optional[float] = None,
828
+ name: Optional[str] = None,
829
+ gp: Optional[Gpar] = None,
830
+ vp: Optional[Any] = None,
831
+ ) -> Grob:
832
+ """Create a *rect* grob.
833
+
834
+ A *rect* grob draws one or more axis-aligned rectangles.
835
+
836
+ Parameters
837
+ ----------
838
+ x : Unit or numeric
839
+ Horizontal anchor.
840
+ y : Unit or numeric
841
+ Vertical anchor.
842
+ width : Unit or numeric
843
+ Rectangle width(s).
844
+ height : Unit or numeric
845
+ Rectangle height(s).
846
+ default_units : str
847
+ Unit type for bare numerics.
848
+ just : str or sequence of str
849
+ Justification specification (e.g. ``"centre"``, ``"left"``,
850
+ ``["left", "bottom"]``).
851
+ hjust : float or None
852
+ Explicit horizontal justification override.
853
+ vjust : float or None
854
+ Explicit vertical justification override.
855
+ name : str or None
856
+ Grob name.
857
+ gp : Gpar or None
858
+ Graphical parameters.
859
+ vp : viewport or None
860
+ Optional viewport.
861
+
862
+ Returns
863
+ -------
864
+ Grob
865
+ A grob with ``_grid_class="rect"``.
866
+ """
867
+ x = _ensure_unit(x, default_units)
868
+ y = _ensure_unit(y, default_units)
869
+ width = _ensure_unit(width, default_units)
870
+ height = _ensure_unit(height, default_units)
871
+ return Grob(
872
+ x=x, y=y, width=width, height=height,
873
+ just=just, hjust=hjust, vjust=vjust,
874
+ name=name, gp=gp, vp=vp, _grid_class="rect",
875
+ )
876
+
877
+
878
+ def grid_rect(
879
+ x: Any = 0.5,
880
+ y: Any = 0.5,
881
+ width: Any = 1,
882
+ height: Any = 1,
883
+ default_units: str = "npc",
884
+ just: Union[str, Sequence[str]] = "centre",
885
+ hjust: Optional[float] = None,
886
+ vjust: Optional[float] = None,
887
+ name: Optional[str] = None,
888
+ gp: Optional[Gpar] = None,
889
+ draw: bool = True,
890
+ vp: Optional[Any] = None,
891
+ ) -> Grob:
892
+ """Create and optionally draw a *rect* grob.
893
+
894
+ Parameters
895
+ ----------
896
+ x : Unit or numeric
897
+ Horizontal anchor.
898
+ y : Unit or numeric
899
+ Vertical anchor.
900
+ width : Unit or numeric
901
+ Rectangle width(s).
902
+ height : Unit or numeric
903
+ Rectangle height(s).
904
+ default_units : str
905
+ Default unit type for bare numerics.
906
+ just : str or sequence of str
907
+ Justification specification.
908
+ hjust : float or None
909
+ Horizontal justification override.
910
+ vjust : float or None
911
+ Vertical justification override.
912
+ name : str or None
913
+ Grob name.
914
+ gp : Gpar or None
915
+ Graphical parameters.
916
+ draw : bool
917
+ If ``True`` (default), record the grob for drawing.
918
+ vp : viewport or None
919
+ Optional viewport.
920
+
921
+ Returns
922
+ -------
923
+ Grob
924
+ The created grob.
925
+ """
926
+ grob = rect_grob(
927
+ x=x, y=y, width=width, height=height,
928
+ default_units=default_units, just=just,
929
+ hjust=hjust, vjust=vjust,
930
+ name=name, gp=gp, vp=vp,
931
+ )
932
+ if draw:
933
+ _grid_draw(grob)
934
+ return grob
935
+
936
+
937
+ # ===================================================================== #
938
+ # roundrect primitive #
939
+ # ===================================================================== #
940
+
941
+
942
+ def roundrect_grob(
943
+ x: Any = 0.5,
944
+ y: Any = 0.5,
945
+ width: Any = 1,
946
+ height: Any = 1,
947
+ default_units: str = "npc",
948
+ r: Optional[Any] = None,
949
+ just: Union[str, Sequence[str]] = "centre",
950
+ name: Optional[str] = None,
951
+ gp: Optional[Gpar] = None,
952
+ vp: Optional[Any] = None,
953
+ ) -> Grob:
954
+ """Create a *roundrect* grob.
955
+
956
+ A *roundrect* grob draws a single rectangle with rounded corners.
957
+ Corner radius *r* is best specified as an absolute unit or ``"snpc"``
958
+ to avoid distortion.
959
+
960
+ Parameters
961
+ ----------
962
+ x : Unit or numeric
963
+ Horizontal anchor.
964
+ y : Unit or numeric
965
+ Vertical anchor.
966
+ width : Unit or numeric
967
+ Rectangle width.
968
+ height : Unit or numeric
969
+ Rectangle height.
970
+ default_units : str
971
+ Unit type for bare numerics.
972
+ r : Unit, numeric, or None
973
+ Corner radius. Defaults to ``Unit(0.1, "snpc")`` when ``None``.
974
+ just : str or sequence of str
975
+ Justification specification.
976
+ name : str or None
977
+ Grob name.
978
+ gp : Gpar or None
979
+ Graphical parameters.
980
+ vp : viewport or None
981
+ Optional viewport.
982
+
983
+ Returns
984
+ -------
985
+ Grob
986
+ A grob with ``_grid_class="roundrect"``.
987
+ """
988
+ x = _ensure_unit(x, default_units)
989
+ y = _ensure_unit(y, default_units)
990
+ width = _ensure_unit(width, default_units)
991
+ height = _ensure_unit(height, default_units)
992
+ if r is None:
993
+ r = Unit(0.1, "snpc")
994
+ elif not is_unit(r):
995
+ r = Unit(r, default_units)
996
+ return Grob(
997
+ x=x, y=y, width=width, height=height, r=r, just=just,
998
+ name=name, gp=gp, vp=vp, _grid_class="roundrect",
999
+ )
1000
+
1001
+
1002
+ def grid_roundrect(
1003
+ x: Any = 0.5,
1004
+ y: Any = 0.5,
1005
+ width: Any = 1,
1006
+ height: Any = 1,
1007
+ default_units: str = "npc",
1008
+ r: Optional[Any] = None,
1009
+ just: Union[str, Sequence[str]] = "centre",
1010
+ name: Optional[str] = None,
1011
+ gp: Optional[Gpar] = None,
1012
+ draw: bool = True,
1013
+ vp: Optional[Any] = None,
1014
+ ) -> Grob:
1015
+ """Create and optionally draw a *roundrect* grob.
1016
+
1017
+ Parameters
1018
+ ----------
1019
+ x : Unit or numeric
1020
+ Horizontal anchor.
1021
+ y : Unit or numeric
1022
+ Vertical anchor.
1023
+ width : Unit or numeric
1024
+ Rectangle width.
1025
+ height : Unit or numeric
1026
+ Rectangle height.
1027
+ default_units : str
1028
+ Default unit type for bare numerics.
1029
+ r : Unit, numeric, or None
1030
+ Corner radius. Defaults to ``Unit(0.1, "snpc")``.
1031
+ just : str or sequence of str
1032
+ Justification specification.
1033
+ name : str or None
1034
+ Grob name.
1035
+ gp : Gpar or None
1036
+ Graphical parameters.
1037
+ draw : bool
1038
+ If ``True`` (default), record the grob for drawing.
1039
+ vp : viewport or None
1040
+ Optional viewport.
1041
+
1042
+ Returns
1043
+ -------
1044
+ Grob
1045
+ The created grob.
1046
+ """
1047
+ grob = roundrect_grob(
1048
+ x=x, y=y, width=width, height=height,
1049
+ default_units=default_units, r=r, just=just,
1050
+ name=name, gp=gp, vp=vp,
1051
+ )
1052
+ if draw:
1053
+ _grid_draw(grob)
1054
+ return grob
1055
+
1056
+
1057
+ # ===================================================================== #
1058
+ # circle primitive #
1059
+ # ===================================================================== #
1060
+
1061
+
1062
+ def circle_grob(
1063
+ x: Any = 0.5,
1064
+ y: Any = 0.5,
1065
+ r: Any = 0.5,
1066
+ default_units: str = "npc",
1067
+ name: Optional[str] = None,
1068
+ gp: Optional[Gpar] = None,
1069
+ vp: Optional[Any] = None,
1070
+ ) -> Grob:
1071
+ """Create a *circle* grob.
1072
+
1073
+ Parameters
1074
+ ----------
1075
+ x : Unit or numeric
1076
+ Horizontal centre.
1077
+ y : Unit or numeric
1078
+ Vertical centre.
1079
+ r : Unit or numeric
1080
+ Radius.
1081
+ default_units : str
1082
+ Unit type for bare numerics.
1083
+ name : str or None
1084
+ Grob name.
1085
+ gp : Gpar or None
1086
+ Graphical parameters.
1087
+ vp : viewport or None
1088
+ Optional viewport.
1089
+
1090
+ Returns
1091
+ -------
1092
+ Grob
1093
+ A grob with ``_grid_class="circle"``.
1094
+ """
1095
+ x = _ensure_unit(x, default_units)
1096
+ y = _ensure_unit(y, default_units)
1097
+ r = _ensure_unit(r, default_units)
1098
+ return Grob(
1099
+ x=x, y=y, r=r,
1100
+ name=name, gp=gp, vp=vp, _grid_class="circle",
1101
+ )
1102
+
1103
+
1104
+ def grid_circle(
1105
+ x: Any = 0.5,
1106
+ y: Any = 0.5,
1107
+ r: Any = 0.5,
1108
+ default_units: str = "npc",
1109
+ name: Optional[str] = None,
1110
+ gp: Optional[Gpar] = None,
1111
+ draw: bool = True,
1112
+ vp: Optional[Any] = None,
1113
+ ) -> Grob:
1114
+ """Create and optionally draw a *circle* grob.
1115
+
1116
+ Parameters
1117
+ ----------
1118
+ x : Unit or numeric
1119
+ Horizontal centre.
1120
+ y : Unit or numeric
1121
+ Vertical centre.
1122
+ r : Unit or numeric
1123
+ Radius.
1124
+ default_units : str
1125
+ Default unit type for bare numerics.
1126
+ name : str or None
1127
+ Grob name.
1128
+ gp : Gpar or None
1129
+ Graphical parameters.
1130
+ draw : bool
1131
+ If ``True`` (default), record the grob for drawing.
1132
+ vp : viewport or None
1133
+ Optional viewport.
1134
+
1135
+ Returns
1136
+ -------
1137
+ Grob
1138
+ The created grob.
1139
+ """
1140
+ grob = circle_grob(
1141
+ x=x, y=y, r=r, default_units=default_units,
1142
+ name=name, gp=gp, vp=vp,
1143
+ )
1144
+ if draw:
1145
+ _grid_draw(grob)
1146
+ return grob
1147
+
1148
+
1149
+ # ===================================================================== #
1150
+ # polygon primitive #
1151
+ # ===================================================================== #
1152
+
1153
+
1154
+ def polygon_grob(
1155
+ x: Any = None,
1156
+ y: Any = None,
1157
+ id: Optional[Sequence[int]] = None,
1158
+ id_lengths: Optional[Sequence[int]] = None,
1159
+ default_units: str = "npc",
1160
+ name: Optional[str] = None,
1161
+ gp: Optional[Gpar] = None,
1162
+ vp: Optional[Any] = None,
1163
+ ) -> Grob:
1164
+ """Create a *polygon* grob.
1165
+
1166
+ A *polygon* grob draws one or more filled polygons. Sub-polygons
1167
+ are identified by *id* or *id_lengths* (mutually exclusive).
1168
+
1169
+ Parameters
1170
+ ----------
1171
+ x : Unit or numeric
1172
+ Horizontal coordinates. Defaults to a diamond shape.
1173
+ y : Unit or numeric
1174
+ Vertical coordinates. Defaults to a diamond shape.
1175
+ id : sequence of int or None
1176
+ Per-point group identifier.
1177
+ id_lengths : sequence of int or None
1178
+ Lengths of consecutive sub-polygons.
1179
+ default_units : str
1180
+ Unit type for bare numerics.
1181
+ name : str or None
1182
+ Grob name.
1183
+ gp : Gpar or None
1184
+ Graphical parameters.
1185
+ vp : viewport or None
1186
+ Optional viewport.
1187
+
1188
+ Returns
1189
+ -------
1190
+ Grob
1191
+ A grob with ``_grid_class="polygon"``.
1192
+
1193
+ Raises
1194
+ ------
1195
+ ValueError
1196
+ If both *id* and *id_lengths* are specified.
1197
+ """
1198
+ if x is None:
1199
+ x = Unit([0, 0.5, 1, 0.5], "npc")
1200
+ else:
1201
+ x = _ensure_unit(x, default_units)
1202
+ if y is None:
1203
+ y = Unit([0.5, 1, 0.5, 0], "npc")
1204
+ else:
1205
+ y = _ensure_unit(y, default_units)
1206
+ if id is not None and id_lengths is not None:
1207
+ raise ValueError(
1208
+ "it is invalid to specify both 'id' and 'id_lengths'"
1209
+ )
1210
+ return Grob(
1211
+ x=x, y=y, id=id, id_lengths=id_lengths,
1212
+ name=name, gp=gp, vp=vp, _grid_class="polygon",
1213
+ )
1214
+
1215
+
1216
+ def grid_polygon(
1217
+ x: Any = None,
1218
+ y: Any = None,
1219
+ id: Optional[Sequence[int]] = None,
1220
+ id_lengths: Optional[Sequence[int]] = None,
1221
+ default_units: str = "npc",
1222
+ name: Optional[str] = None,
1223
+ gp: Optional[Gpar] = None,
1224
+ draw: bool = True,
1225
+ vp: Optional[Any] = None,
1226
+ ) -> Grob:
1227
+ """Create and optionally draw a *polygon* grob.
1228
+
1229
+ Parameters
1230
+ ----------
1231
+ x : Unit or numeric
1232
+ Horizontal coordinates.
1233
+ y : Unit or numeric
1234
+ Vertical coordinates.
1235
+ id : sequence of int or None
1236
+ Per-point group identifier.
1237
+ id_lengths : sequence of int or None
1238
+ Lengths of consecutive sub-polygons.
1239
+ default_units : str
1240
+ Default unit type for bare numerics.
1241
+ name : str or None
1242
+ Grob name.
1243
+ gp : Gpar or None
1244
+ Graphical parameters.
1245
+ draw : bool
1246
+ If ``True`` (default), record the grob for drawing.
1247
+ vp : viewport or None
1248
+ Optional viewport.
1249
+
1250
+ Returns
1251
+ -------
1252
+ Grob
1253
+ The created grob.
1254
+ """
1255
+ grob = polygon_grob(
1256
+ x=x, y=y, id=id, id_lengths=id_lengths,
1257
+ default_units=default_units,
1258
+ name=name, gp=gp, vp=vp,
1259
+ )
1260
+ if draw:
1261
+ _grid_draw(grob)
1262
+ return grob
1263
+
1264
+
1265
+ # ===================================================================== #
1266
+ # path primitive #
1267
+ # ===================================================================== #
1268
+
1269
+
1270
+ def path_grob(
1271
+ x: Any,
1272
+ y: Any,
1273
+ id: Optional[Sequence[int]] = None,
1274
+ id_lengths: Optional[Sequence[int]] = None,
1275
+ path_id: Optional[Sequence[int]] = None,
1276
+ path_id_lengths: Optional[Sequence[int]] = None,
1277
+ rule: str = "winding",
1278
+ default_units: str = "npc",
1279
+ name: Optional[str] = None,
1280
+ gp: Optional[Gpar] = None,
1281
+ vp: Optional[Any] = None,
1282
+ ) -> Grob:
1283
+ """Create a *path* grob.
1284
+
1285
+ A *path* grob draws a complex filled shape defined by one or more
1286
+ sub-paths. The fill rule (*rule*) determines how overlapping
1287
+ sub-paths interact.
1288
+
1289
+ Parameters
1290
+ ----------
1291
+ x : Unit or numeric
1292
+ Horizontal coordinates.
1293
+ y : Unit or numeric
1294
+ Vertical coordinates.
1295
+ id : sequence of int or None
1296
+ Per-point sub-path identifier.
1297
+ id_lengths : sequence of int or None
1298
+ Lengths of consecutive sub-paths.
1299
+ path_id : sequence of int or None
1300
+ Identifier for grouping sub-paths into separate compound paths.
1301
+ path_id_lengths : sequence of int or None
1302
+ Lengths of consecutive compound path groups.
1303
+ rule : {"winding", "evenodd"}
1304
+ Fill rule.
1305
+ default_units : str
1306
+ Unit type for bare numerics.
1307
+ name : str or None
1308
+ Grob name.
1309
+ gp : Gpar or None
1310
+ Graphical parameters.
1311
+ vp : viewport or None
1312
+ Optional viewport.
1313
+
1314
+ Returns
1315
+ -------
1316
+ Grob
1317
+ A grob with ``_grid_class="pathgrob"``.
1318
+
1319
+ Raises
1320
+ ------
1321
+ ValueError
1322
+ If both *id* and *id_lengths* are specified, or if *rule* is
1323
+ invalid.
1324
+ """
1325
+ x = _ensure_unit(x, default_units)
1326
+ y = _ensure_unit(y, default_units)
1327
+ if id is not None and id_lengths is not None:
1328
+ raise ValueError(
1329
+ "it is invalid to specify both 'id' and 'id_lengths'"
1330
+ )
1331
+ if rule not in ("winding", "evenodd"):
1332
+ raise ValueError(f"'rule' must be 'winding' or 'evenodd', got {rule!r}")
1333
+ return Grob(
1334
+ x=x, y=y, id=id, id_lengths=id_lengths,
1335
+ path_id=path_id, path_id_lengths=path_id_lengths,
1336
+ rule=rule,
1337
+ name=name, gp=gp, vp=vp, _grid_class="pathgrob",
1338
+ )
1339
+
1340
+
1341
+ def grid_path(
1342
+ x: Any = None,
1343
+ y: Any = None,
1344
+ id: Optional[Sequence[int]] = None,
1345
+ id_lengths: Optional[Sequence[int]] = None,
1346
+ path_id: Optional[Sequence[int]] = None,
1347
+ path_id_lengths: Optional[Sequence[int]] = None,
1348
+ rule: str = "winding",
1349
+ default_units: str = "npc",
1350
+ name: Optional[str] = None,
1351
+ gp: Optional[Gpar] = None,
1352
+ draw: bool = True,
1353
+ vp: Optional[Any] = None,
1354
+ ) -> Grob:
1355
+ """Create and optionally draw a *path* grob.
1356
+
1357
+ Parameters
1358
+ ----------
1359
+ x : Unit or numeric
1360
+ Horizontal coordinates.
1361
+ y : Unit or numeric
1362
+ Vertical coordinates.
1363
+ id : sequence of int or None
1364
+ Per-point sub-path identifier.
1365
+ id_lengths : sequence of int or None
1366
+ Lengths of consecutive sub-paths.
1367
+ path_id : sequence of int or None
1368
+ Identifier for compound path groups.
1369
+ path_id_lengths : sequence of int or None
1370
+ Lengths of compound path groups.
1371
+ rule : {"winding", "evenodd"}
1372
+ Fill rule.
1373
+ default_units : str
1374
+ Default unit type for bare numerics.
1375
+ name : str or None
1376
+ Grob name.
1377
+ gp : Gpar or None
1378
+ Graphical parameters.
1379
+ draw : bool
1380
+ If ``True`` (default), record the grob for drawing.
1381
+ vp : viewport or None
1382
+ Optional viewport.
1383
+
1384
+ Returns
1385
+ -------
1386
+ Grob
1387
+ The created grob.
1388
+ """
1389
+ if x is None:
1390
+ x = Unit([0, 0.5, 1, 0.5], "npc")
1391
+ if y is None:
1392
+ y = Unit([0.5, 1, 0.5, 0], "npc")
1393
+ grob = path_grob(
1394
+ x=x, y=y, id=id, id_lengths=id_lengths,
1395
+ path_id=path_id, path_id_lengths=path_id_lengths,
1396
+ rule=rule, default_units=default_units,
1397
+ name=name, gp=gp, vp=vp,
1398
+ )
1399
+ if draw:
1400
+ _grid_draw(grob)
1401
+ return grob
1402
+
1403
+
1404
+ # ===================================================================== #
1405
+ # text primitive #
1406
+ # ===================================================================== #
1407
+
1408
+
1409
+ def text_grob(
1410
+ label: Any,
1411
+ x: Any = 0.5,
1412
+ y: Any = 0.5,
1413
+ default_units: str = "npc",
1414
+ just: Union[str, Sequence[str]] = "centre",
1415
+ hjust: Optional[float] = None,
1416
+ vjust: Optional[float] = None,
1417
+ rot: float = 0,
1418
+ check_overlap: bool = False,
1419
+ name: Optional[str] = None,
1420
+ gp: Optional[Gpar] = None,
1421
+ vp: Optional[Any] = None,
1422
+ ) -> Grob:
1423
+ """Create a *text* grob.
1424
+
1425
+ A *text* grob renders one or more text strings at the given
1426
+ positions.
1427
+
1428
+ Parameters
1429
+ ----------
1430
+ label : str or sequence of str
1431
+ Text string(s) to display.
1432
+ x : Unit or numeric
1433
+ Horizontal position(s).
1434
+ y : Unit or numeric
1435
+ Vertical position(s).
1436
+ default_units : str
1437
+ Unit type for bare numerics.
1438
+ just : str or sequence of str
1439
+ Justification specification.
1440
+ hjust : float or None
1441
+ Horizontal justification override.
1442
+ vjust : float or None
1443
+ Vertical justification override.
1444
+ rot : float
1445
+ Rotation angle in degrees.
1446
+ check_overlap : bool
1447
+ If ``True``, labels that overlap previously drawn labels are
1448
+ suppressed.
1449
+ name : str or None
1450
+ Grob name.
1451
+ gp : Gpar or None
1452
+ Graphical parameters.
1453
+ vp : viewport or None
1454
+ Optional viewport.
1455
+
1456
+ Returns
1457
+ -------
1458
+ Grob
1459
+ A grob with ``_grid_class="text"``.
1460
+ """
1461
+ x = _ensure_unit(x, default_units)
1462
+ y = _ensure_unit(y, default_units)
1463
+ return Grob(
1464
+ label=label, x=x, y=y,
1465
+ just=just, hjust=hjust, vjust=vjust,
1466
+ rot=float(rot), check_overlap=bool(check_overlap),
1467
+ name=name, gp=gp, vp=vp, _grid_class="text",
1468
+ )
1469
+
1470
+
1471
+ def grid_text(
1472
+ label: Any,
1473
+ x: Any = 0.5,
1474
+ y: Any = 0.5,
1475
+ default_units: str = "npc",
1476
+ just: Union[str, Sequence[str]] = "centre",
1477
+ hjust: Optional[float] = None,
1478
+ vjust: Optional[float] = None,
1479
+ rot: float = 0,
1480
+ check_overlap: bool = False,
1481
+ name: Optional[str] = None,
1482
+ gp: Optional[Gpar] = None,
1483
+ draw: bool = True,
1484
+ vp: Optional[Any] = None,
1485
+ ) -> Grob:
1486
+ """Create and optionally draw a *text* grob.
1487
+
1488
+ Parameters
1489
+ ----------
1490
+ label : str or sequence of str
1491
+ Text string(s) to display.
1492
+ x : Unit or numeric
1493
+ Horizontal position(s).
1494
+ y : Unit or numeric
1495
+ Vertical position(s).
1496
+ default_units : str
1497
+ Default unit type for bare numerics.
1498
+ just : str or sequence of str
1499
+ Justification specification.
1500
+ hjust : float or None
1501
+ Horizontal justification override.
1502
+ vjust : float or None
1503
+ Vertical justification override.
1504
+ rot : float
1505
+ Rotation angle in degrees.
1506
+ check_overlap : bool
1507
+ Whether to suppress overlapping labels.
1508
+ name : str or None
1509
+ Grob name.
1510
+ gp : Gpar or None
1511
+ Graphical parameters.
1512
+ draw : bool
1513
+ If ``True`` (default), record the grob for drawing.
1514
+ vp : viewport or None
1515
+ Optional viewport.
1516
+
1517
+ Returns
1518
+ -------
1519
+ Grob
1520
+ The created grob.
1521
+ """
1522
+ grob = text_grob(
1523
+ label=label, x=x, y=y, default_units=default_units,
1524
+ just=just, hjust=hjust, vjust=vjust,
1525
+ rot=rot, check_overlap=check_overlap,
1526
+ name=name, gp=gp, vp=vp,
1527
+ )
1528
+ if draw:
1529
+ _grid_draw(grob)
1530
+ return grob
1531
+
1532
+
1533
+ # ===================================================================== #
1534
+ # raster primitive #
1535
+ # ===================================================================== #
1536
+
1537
+
1538
+ def raster_grob(
1539
+ image: Any,
1540
+ x: Any = 0.5,
1541
+ y: Any = 0.5,
1542
+ width: Optional[Any] = None,
1543
+ height: Optional[Any] = None,
1544
+ default_units: str = "npc",
1545
+ just: Union[str, Sequence[str]] = "centre",
1546
+ hjust: Optional[float] = None,
1547
+ vjust: Optional[float] = None,
1548
+ interpolate: bool = True,
1549
+ name: Optional[str] = None,
1550
+ gp: Optional[Gpar] = None,
1551
+ vp: Optional[Any] = None,
1552
+ ) -> Grob:
1553
+ """Create a *raster* grob.
1554
+
1555
+ A *raster* grob draws a raster image (2-D pixel array) at the
1556
+ specified location. When *width* or *height* is ``None``, the
1557
+ missing dimension is inferred from the image aspect ratio at draw
1558
+ time.
1559
+
1560
+ Parameters
1561
+ ----------
1562
+ image : array-like
1563
+ The raster image data (e.g. a NumPy array or PIL Image).
1564
+ x : Unit or numeric
1565
+ Horizontal anchor.
1566
+ y : Unit or numeric
1567
+ Vertical anchor.
1568
+ width : Unit, numeric, or None
1569
+ Image width. ``None`` means infer from aspect ratio.
1570
+ height : Unit, numeric, or None
1571
+ Image height. ``None`` means infer from aspect ratio.
1572
+ default_units : str
1573
+ Unit type for bare numerics.
1574
+ just : str or sequence of str
1575
+ Justification specification.
1576
+ hjust : float or None
1577
+ Horizontal justification override.
1578
+ vjust : float or None
1579
+ Vertical justification override.
1580
+ interpolate : bool
1581
+ If ``True`` (default), the image is linearly interpolated when
1582
+ scaled.
1583
+ name : str or None
1584
+ Grob name.
1585
+ gp : Gpar or None
1586
+ Graphical parameters.
1587
+ vp : viewport or None
1588
+ Optional viewport.
1589
+
1590
+ Returns
1591
+ -------
1592
+ Grob
1593
+ A grob with ``_grid_class="rastergrob"``.
1594
+ """
1595
+ x = _ensure_unit(x, default_units)
1596
+ y = _ensure_unit(y, default_units)
1597
+ if width is not None:
1598
+ width = _ensure_unit(width, default_units)
1599
+ if height is not None:
1600
+ height = _ensure_unit(height, default_units)
1601
+ return Grob(
1602
+ raster=image, x=x, y=y, width=width, height=height,
1603
+ just=just, hjust=hjust, vjust=vjust,
1604
+ interpolate=bool(interpolate),
1605
+ name=name, gp=gp, vp=vp, _grid_class="rastergrob",
1606
+ )
1607
+
1608
+
1609
+ def grid_raster(
1610
+ image: Any,
1611
+ x: Any = 0.5,
1612
+ y: Any = 0.5,
1613
+ width: Optional[Any] = None,
1614
+ height: Optional[Any] = None,
1615
+ default_units: str = "npc",
1616
+ just: Union[str, Sequence[str]] = "centre",
1617
+ hjust: Optional[float] = None,
1618
+ vjust: Optional[float] = None,
1619
+ interpolate: bool = True,
1620
+ name: Optional[str] = None,
1621
+ gp: Optional[Gpar] = None,
1622
+ draw: bool = True,
1623
+ vp: Optional[Any] = None,
1624
+ ) -> Grob:
1625
+ """Create and optionally draw a *raster* grob.
1626
+
1627
+ Parameters
1628
+ ----------
1629
+ image : array-like
1630
+ The raster image data.
1631
+ x : Unit or numeric
1632
+ Horizontal anchor.
1633
+ y : Unit or numeric
1634
+ Vertical anchor.
1635
+ width : Unit, numeric, or None
1636
+ Image width.
1637
+ height : Unit, numeric, or None
1638
+ Image height.
1639
+ default_units : str
1640
+ Default unit type for bare numerics.
1641
+ just : str or sequence of str
1642
+ Justification specification.
1643
+ hjust : float or None
1644
+ Horizontal justification override.
1645
+ vjust : float or None
1646
+ Vertical justification override.
1647
+ interpolate : bool
1648
+ Whether to interpolate when scaling.
1649
+ name : str or None
1650
+ Grob name.
1651
+ gp : Gpar or None
1652
+ Graphical parameters.
1653
+ draw : bool
1654
+ If ``True`` (default), record the grob for drawing.
1655
+ vp : viewport or None
1656
+ Optional viewport.
1657
+
1658
+ Returns
1659
+ -------
1660
+ Grob
1661
+ The created grob.
1662
+ """
1663
+ grob = raster_grob(
1664
+ image=image, x=x, y=y, width=width, height=height,
1665
+ default_units=default_units, just=just,
1666
+ hjust=hjust, vjust=vjust, interpolate=interpolate,
1667
+ name=name, gp=gp, vp=vp,
1668
+ )
1669
+ if draw:
1670
+ _grid_draw(grob)
1671
+ return grob
1672
+
1673
+
1674
+ # ===================================================================== #
1675
+ # clip primitive #
1676
+ # ===================================================================== #
1677
+
1678
+
1679
+ def clip_grob(
1680
+ x: Any = 0.5,
1681
+ y: Any = 0.5,
1682
+ width: Any = 1,
1683
+ height: Any = 1,
1684
+ default_units: str = "npc",
1685
+ just: Union[str, Sequence[str]] = "centre",
1686
+ hjust: Optional[float] = None,
1687
+ vjust: Optional[float] = None,
1688
+ name: Optional[str] = None,
1689
+ vp: Optional[Any] = None,
1690
+ ) -> Grob:
1691
+ """Create a *clip* grob.
1692
+
1693
+ A *clip* grob sets the clipping region to the given rectangle.
1694
+ Subsequent drawing is clipped to this area until the viewport is
1695
+ popped or the clipping region is reset.
1696
+
1697
+ Parameters
1698
+ ----------
1699
+ x : Unit or numeric
1700
+ Horizontal anchor.
1701
+ y : Unit or numeric
1702
+ Vertical anchor.
1703
+ width : Unit or numeric
1704
+ Clip rectangle width.
1705
+ height : Unit or numeric
1706
+ Clip rectangle height.
1707
+ default_units : str
1708
+ Unit type for bare numerics.
1709
+ just : str or sequence of str
1710
+ Justification specification.
1711
+ hjust : float or None
1712
+ Horizontal justification override.
1713
+ vjust : float or None
1714
+ Vertical justification override.
1715
+ name : str or None
1716
+ Grob name.
1717
+ vp : viewport or None
1718
+ Optional viewport.
1719
+
1720
+ Returns
1721
+ -------
1722
+ Grob
1723
+ A grob with ``_grid_class="clip"``.
1724
+
1725
+ Notes
1726
+ -----
1727
+ The *clip* grob does **not** accept a *gp* argument -- graphical
1728
+ parameters are irrelevant for clipping.
1729
+ """
1730
+ x = _ensure_unit(x, default_units)
1731
+ y = _ensure_unit(y, default_units)
1732
+ width = _ensure_unit(width, default_units)
1733
+ height = _ensure_unit(height, default_units)
1734
+ return Grob(
1735
+ x=x, y=y, width=width, height=height,
1736
+ just=just, hjust=hjust, vjust=vjust,
1737
+ name=name, vp=vp, _grid_class="clip",
1738
+ )
1739
+
1740
+
1741
+ def grid_clip(
1742
+ x: Any = 0.5,
1743
+ y: Any = 0.5,
1744
+ width: Any = 1,
1745
+ height: Any = 1,
1746
+ default_units: str = "npc",
1747
+ just: Union[str, Sequence[str]] = "centre",
1748
+ hjust: Optional[float] = None,
1749
+ vjust: Optional[float] = None,
1750
+ name: Optional[str] = None,
1751
+ draw: bool = True,
1752
+ vp: Optional[Any] = None,
1753
+ ) -> Grob:
1754
+ """Create and optionally draw a *clip* grob.
1755
+
1756
+ Parameters
1757
+ ----------
1758
+ x : Unit or numeric
1759
+ Horizontal anchor.
1760
+ y : Unit or numeric
1761
+ Vertical anchor.
1762
+ width : Unit or numeric
1763
+ Clip rectangle width.
1764
+ height : Unit or numeric
1765
+ Clip rectangle height.
1766
+ default_units : str
1767
+ Default unit type for bare numerics.
1768
+ just : str or sequence of str
1769
+ Justification specification.
1770
+ hjust : float or None
1771
+ Horizontal justification override.
1772
+ vjust : float or None
1773
+ Vertical justification override.
1774
+ name : str or None
1775
+ Grob name.
1776
+ draw : bool
1777
+ If ``True`` (default), record the grob for drawing.
1778
+ vp : viewport or None
1779
+ Optional viewport.
1780
+
1781
+ Returns
1782
+ -------
1783
+ Grob
1784
+ The created grob.
1785
+ """
1786
+ grob = clip_grob(
1787
+ x=x, y=y, width=width, height=height,
1788
+ default_units=default_units, just=just,
1789
+ hjust=hjust, vjust=vjust,
1790
+ name=name, vp=vp,
1791
+ )
1792
+ if draw:
1793
+ _grid_draw(grob)
1794
+ return grob
1795
+
1796
+
1797
+ # ===================================================================== #
1798
+ # null primitive #
1799
+ # ===================================================================== #
1800
+
1801
+
1802
+ def null_grob(
1803
+ x: Any = 0.5,
1804
+ y: Any = 0.5,
1805
+ default_units: str = "npc",
1806
+ name: Optional[str] = None,
1807
+ vp: Optional[Any] = None,
1808
+ ) -> Grob:
1809
+ """Create a *null* grob.
1810
+
1811
+ A *null* grob has guaranteed zero width and height and draws
1812
+ nothing. It is useful as a positional anchor for other grobs.
1813
+
1814
+ Parameters
1815
+ ----------
1816
+ x : Unit or numeric
1817
+ Horizontal position.
1818
+ y : Unit or numeric
1819
+ Vertical position.
1820
+ default_units : str
1821
+ Unit type for bare numerics.
1822
+ name : str or None
1823
+ Grob name.
1824
+ vp : viewport or None
1825
+ Optional viewport.
1826
+
1827
+ Returns
1828
+ -------
1829
+ Grob
1830
+ A grob with ``_grid_class="null"``.
1831
+ """
1832
+ x = _ensure_unit(x, default_units)
1833
+ y = _ensure_unit(y, default_units)
1834
+ return Grob(x=x, y=y, name=name, vp=vp, _grid_class="null")
1835
+
1836
+
1837
+ def grid_null(
1838
+ x: Any = 0.5,
1839
+ y: Any = 0.5,
1840
+ default_units: str = "npc",
1841
+ name: Optional[str] = None,
1842
+ draw: bool = True,
1843
+ vp: Optional[Any] = None,
1844
+ ) -> Grob:
1845
+ """Create and optionally draw a *null* grob.
1846
+
1847
+ Parameters
1848
+ ----------
1849
+ x : Unit or numeric
1850
+ Horizontal position.
1851
+ y : Unit or numeric
1852
+ Vertical position.
1853
+ default_units : str
1854
+ Default unit type for bare numerics.
1855
+ name : str or None
1856
+ Grob name.
1857
+ draw : bool
1858
+ If ``True`` (default), record the grob for drawing.
1859
+ vp : viewport or None
1860
+ Optional viewport.
1861
+
1862
+ Returns
1863
+ -------
1864
+ Grob
1865
+ The created grob.
1866
+ """
1867
+ grob = null_grob(x=x, y=y, default_units=default_units, name=name, vp=vp)
1868
+ if draw:
1869
+ _grid_draw(grob)
1870
+ return grob
1871
+
1872
+
1873
+ # ===================================================================== #
1874
+ # function grob #
1875
+ # ===================================================================== #
1876
+
1877
+
1878
+ class _FunctionGrob(Grob):
1879
+ """A grob that evaluates a function and renders as a lines grob."""
1880
+
1881
+ def __init__(self, fn, n, range, units, **kwargs):
1882
+ super().__init__(f=fn, n=n, range=range, units=units,
1883
+ _grid_class="functiongrob", **kwargs)
1884
+
1885
+ def make_content(self):
1886
+ """Evaluate the function and return a lines grob.
1887
+
1888
+ Mirrors R's ``makeContent.functiongrob`` (function.R:43-47).
1889
+ """
1890
+ fn = self.f
1891
+ n = self.n
1892
+ rng = self.range
1893
+ units = getattr(self, "units", "native")
1894
+
1895
+ if isinstance(rng, str) and rng == "x":
1896
+ from ._viewport import current_viewport
1897
+ vp = current_viewport()
1898
+ xscale = getattr(vp, "_xscale", None) or getattr(vp, "xscale", [0, 1])
1899
+ t = [xscale[0] + i * (xscale[1] - xscale[0]) / n for i in range(n + 1)]
1900
+ elif isinstance(rng, str) and rng == "y":
1901
+ from ._viewport import current_viewport
1902
+ vp = current_viewport()
1903
+ yscale = getattr(vp, "_yscale", None) or getattr(vp, "yscale", [0, 1])
1904
+ t = [yscale[0] + i * (yscale[1] - yscale[0]) / n for i in range(n + 1)]
1905
+ else:
1906
+ rng = list(rng)
1907
+ t = [rng[0] + i * (rng[1] - rng[0]) / n for i in range(n + 1)]
1908
+
1909
+ results = [fn(ti) for ti in t]
1910
+ x_vals = t
1911
+ y_vals = results
1912
+
1913
+ return lines_grob(
1914
+ x=Unit(x_vals, units),
1915
+ y=Unit(y_vals, units),
1916
+ name=self._name,
1917
+ gp=self.gp,
1918
+ vp=self.vp,
1919
+ )
1920
+
1921
+
1922
+ def function_grob(
1923
+ fn: Callable[..., Any],
1924
+ n: int = 101,
1925
+ range: Union[str, Sequence[float]] = "x",
1926
+ units: str = "native",
1927
+ name: Optional[str] = None,
1928
+ gp: Optional[Gpar] = None,
1929
+ vp: Optional[Any] = None,
1930
+ ) -> Grob:
1931
+ """Create a *function* grob.
1932
+
1933
+ A *function* grob evaluates a user-supplied function over a
1934
+ regularly-spaced grid and draws the resulting curve as a *lines*
1935
+ grob at draw time.
1936
+
1937
+ Parameters
1938
+ ----------
1939
+ fn : callable
1940
+ A function that accepts a 1-D numeric input and returns a dict
1941
+ (or object) with ``"x"`` and ``"y"`` keys/attributes giving the
1942
+ output coordinates.
1943
+ n : int
1944
+ Number of evaluation points (default 101).
1945
+ range : {"x", "y"} or sequence of float
1946
+ Input range. ``"x"`` or ``"y"`` means use the corresponding
1947
+ viewport scale; a 2-element numeric sequence gives explicit
1948
+ bounds.
1949
+ units : str
1950
+ Unit type for the coordinates produced by *fn*.
1951
+ name : str or None
1952
+ Grob name.
1953
+ gp : Gpar or None
1954
+ Graphical parameters.
1955
+ vp : viewport or None
1956
+ Optional viewport.
1957
+
1958
+ Returns
1959
+ -------
1960
+ Grob
1961
+ A grob with ``_grid_class="functiongrob"``.
1962
+
1963
+ Raises
1964
+ ------
1965
+ ValueError
1966
+ If *n* < 1.
1967
+ TypeError
1968
+ If *fn* is not callable.
1969
+ """
1970
+ if n < 1:
1971
+ raise ValueError("'n' must be >= 1")
1972
+ if not callable(fn):
1973
+ raise TypeError("'fn' must be callable")
1974
+ return _FunctionGrob(
1975
+ fn=fn, n=n, range=range, units=units,
1976
+ name=name, gp=gp, vp=vp,
1977
+ )
1978
+
1979
+
1980
+ def grid_function(
1981
+ fn: Callable[..., Any],
1982
+ n: int = 101,
1983
+ range: Union[str, Sequence[float]] = "x",
1984
+ units: str = "native",
1985
+ name: Optional[str] = None,
1986
+ gp: Optional[Gpar] = None,
1987
+ draw: bool = True,
1988
+ vp: Optional[Any] = None,
1989
+ ) -> Grob:
1990
+ """Create and optionally draw a *function* grob.
1991
+
1992
+ Parameters
1993
+ ----------
1994
+ fn : callable
1995
+ Evaluation function (see :func:`function_grob`).
1996
+ n : int
1997
+ Number of evaluation points.
1998
+ range : {"x", "y"} or sequence of float
1999
+ Input range.
2000
+ units : str
2001
+ Unit type for output coordinates.
2002
+ name : str or None
2003
+ Grob name.
2004
+ gp : Gpar or None
2005
+ Graphical parameters.
2006
+ draw : bool
2007
+ If ``True`` (default), record the grob for drawing.
2008
+ vp : viewport or None
2009
+ Optional viewport.
2010
+
2011
+ Returns
2012
+ -------
2013
+ Grob
2014
+ The created grob.
2015
+ """
2016
+ grob = function_grob(
2017
+ fn=fn, n=n, range=range, units=units,
2018
+ name=name, gp=gp, vp=vp,
2019
+ )
2020
+ if draw:
2021
+ _grid_draw(grob)
2022
+ return grob
2023
+
2024
+
2025
+ # ===================================================================== #
2026
+ # Path fill / stroke primitives (R 4.2+, grid/R/path.R) #
2027
+ # ===================================================================== #
2028
+
2029
+
2030
+ def as_path(
2031
+ x: Any,
2032
+ gp: Optional[Gpar] = None,
2033
+ rule: str = "winding",
2034
+ ) -> dict:
2035
+ """Mark a grob as a single path.
2036
+
2037
+ Mirrors R's ``as.path()`` (``path.R:3-9``).
2038
+
2039
+ Parameters
2040
+ ----------
2041
+ x : Grob
2042
+ The grob to convert.
2043
+ gp : Gpar or None
2044
+ Graphical parameters.
2045
+ rule : str
2046
+ Fill rule: ``"winding"`` (default) or ``"evenodd"``.
2047
+
2048
+ Returns
2049
+ -------
2050
+ dict
2051
+ A ``GridPath`` descriptor with keys ``grob``, ``gp``, ``rule``.
2052
+ """
2053
+ return {
2054
+ "grob": x,
2055
+ "gp": gp if gp is not None else Gpar(),
2056
+ "rule": rule,
2057
+ "_class": "GridPath",
2058
+ }
2059
+
2060
+
2061
+ def stroke_grob(
2062
+ x: Any,
2063
+ name: Optional[str] = None,
2064
+ gp: Optional[Gpar] = None,
2065
+ vp: Optional[Any] = None,
2066
+ ) -> Grob:
2067
+ """Create a stroke-only path grob.
2068
+
2069
+ Draws the outline of the nested grob without filling.
2070
+ Mirrors R's ``strokeGrob()`` (``path.R:20-35``).
2071
+
2072
+ Parameters
2073
+ ----------
2074
+ x : Grob or GridPath dict
2075
+ The grob (or ``as_path`` result) defining the path.
2076
+ name, gp, vp
2077
+ Standard grob parameters.
2078
+
2079
+ Returns
2080
+ -------
2081
+ Grob
2082
+ A grob with ``_grid_class="GridStroke"``.
2083
+ """
2084
+ if isinstance(x, dict) and x.get("_class") == "GridPath":
2085
+ return Grob(
2086
+ path=x["grob"], name=name, gp=x["gp"], vp=vp,
2087
+ _grid_class="GridStroke",
2088
+ )
2089
+ return Grob(
2090
+ path=x, name=name, gp=gp if gp is not None else Gpar(), vp=vp,
2091
+ _grid_class="GridStroke",
2092
+ )
2093
+
2094
+
2095
+ def grid_stroke(x: Any, **kwargs: Any) -> Grob:
2096
+ """Create and draw a stroke-only path grob.
2097
+
2098
+ Mirrors R's ``grid.stroke()``.
2099
+ """
2100
+ grob = stroke_grob(x, **kwargs)
2101
+ _grid_draw(grob)
2102
+ return grob
2103
+
2104
+
2105
+ def fill_grob(
2106
+ x: Any,
2107
+ rule: str = "winding",
2108
+ name: Optional[str] = None,
2109
+ gp: Optional[Gpar] = None,
2110
+ vp: Optional[Any] = None,
2111
+ ) -> Grob:
2112
+ """Create a fill-only path grob.
2113
+
2114
+ Fills the interior of the nested grob without stroking the outline.
2115
+ Mirrors R's ``fillGrob()`` (``path.R:49-64``).
2116
+
2117
+ Parameters
2118
+ ----------
2119
+ x : Grob or GridPath dict
2120
+ The grob (or ``as_path`` result) defining the path.
2121
+ rule : str
2122
+ Fill rule: ``"winding"`` or ``"evenodd"``.
2123
+ name, gp, vp
2124
+ Standard grob parameters.
2125
+
2126
+ Returns
2127
+ -------
2128
+ Grob
2129
+ A grob with ``_grid_class="GridFill"``.
2130
+ """
2131
+ if isinstance(x, dict) and x.get("_class") == "GridPath":
2132
+ return Grob(
2133
+ path=x["grob"], rule=x["rule"], name=name, gp=x["gp"], vp=vp,
2134
+ _grid_class="GridFill",
2135
+ )
2136
+ return Grob(
2137
+ path=x, rule=rule, name=name,
2138
+ gp=gp if gp is not None else Gpar(), vp=vp,
2139
+ _grid_class="GridFill",
2140
+ )
2141
+
2142
+
2143
+ def grid_fill(x: Any, **kwargs: Any) -> Grob:
2144
+ """Create and draw a fill-only path grob.
2145
+
2146
+ Mirrors R's ``grid.fill()``.
2147
+ """
2148
+ grob = fill_grob(x, **kwargs)
2149
+ _grid_draw(grob)
2150
+ return grob
2151
+
2152
+
2153
+ def fill_stroke_grob(
2154
+ x: Any,
2155
+ rule: str = "winding",
2156
+ name: Optional[str] = None,
2157
+ gp: Optional[Gpar] = None,
2158
+ vp: Optional[Any] = None,
2159
+ ) -> Grob:
2160
+ """Create a fill-and-stroke path grob.
2161
+
2162
+ Fills and then strokes the nested grob as a single combined path.
2163
+ Mirrors R's ``fillStrokeGrob()`` (``path.R:80-95``).
2164
+
2165
+ Parameters
2166
+ ----------
2167
+ x : Grob or GridPath dict
2168
+ The grob (or ``as_path`` result) defining the path.
2169
+ rule : str
2170
+ Fill rule: ``"winding"`` or ``"evenodd"``.
2171
+ name, gp, vp
2172
+ Standard grob parameters.
2173
+
2174
+ Returns
2175
+ -------
2176
+ Grob
2177
+ A grob with ``_grid_class="GridFillStroke"``.
2178
+ """
2179
+ if isinstance(x, dict) and x.get("_class") == "GridPath":
2180
+ return Grob(
2181
+ path=x["grob"], rule=x["rule"], name=name, gp=x["gp"], vp=vp,
2182
+ _grid_class="GridFillStroke",
2183
+ )
2184
+ return Grob(
2185
+ path=x, rule=rule, name=name,
2186
+ gp=gp if gp is not None else Gpar(), vp=vp,
2187
+ _grid_class="GridFillStroke",
2188
+ )
2189
+
2190
+
2191
+ def grid_fill_stroke(x: Any, **kwargs: Any) -> Grob:
2192
+ """Create and draw a fill-and-stroke path grob.
2193
+
2194
+ Mirrors R's ``grid.fillStroke()``.
2195
+ """
2196
+ grob = fill_stroke_grob(x, **kwargs)
2197
+ _grid_draw(grob)
2198
+ return grob