8000 Added path.sketch_seed to rcParams · matplotlib/matplotlib@eb546b4 · GitHub
[go: up one dir, main page]

Skip to content

Commit eb546b4

Browse files
eudoxososcargus
authored andcommitted
Added path.sketch_seed to rcParams
Seed can be manually set for path.sketch by modifying the value of rcParams path.sketch_seed or by passing a seed value to xkcd or Artist.set_sketch . Seed will also have a rolling(auto incrementing) behaviour. Co-Authored-By: Oscar Gustafsson <8114497+oscargus@users.noreply.github.com> Co-Authored-By: eudoxos <1029876+eudoxos@users.noreply.github.com>
1 parent 835220b commit eb546b4

File tree

25 files changed

+259
-28
lines changed

25 files changed

+259
-28
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Seed for ``path.sketch`` will have a rolling (auto incrementing) behaviour
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
The seed for the internal Pseudo number generator will now have an auto changing behavior.
5+
This means that the C code of every artist will get a different seed every time it is called
6+
and this will be done in a deterministic manner.
7+
8+
Two figures sketched with the same parameters and different seed will look different from one another.
9+
10+
``Artist.get_sketch_params()`` will now return a 4-tuple instead of a 3-tuple consisting of
11+
(scale, length, randomness, seed) of the form (float, float, float, int).
12+
13+
See 'What's new' on how to set a value to the seed and its behaviour.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
``sketch_seed`` parameter for rcParams
2+
--------------------------------------
3+
4+
`~matplotlib.rcParams` now has a new parameter ``path.sketch_seed``.
5+
Its default value is 0 and accepted values are any non negative integer.
6+
This allows the user to set the seed for the internal pseudo random number generator in one of three ways.
7+
8+
1) Directly changing the rcParam:
9+
10+
rcParams['path.sketch_seed'] = 20
11+
12+
2) Passing a value to the new *seed* parameter of `~matplotlib.pyplot.xkcd` function:
13+
14+
plt.xkcd(seed=20)
15+
16+
3) Passing a value to the new *seed* parameter of matplotlib.artist.set_sketch_params function:
17+
18+
ln = plt.plot(x, y)
19+
ln[0].set_sketch_params(seed = 20)
20+
21+
The seed will also have a changing characteristic for every artist which will be done in a deterministic manner.
22+
23+
24+
.. plot::
25+
:include-source: true
26+
27+
import matplotlib.pyplot as plt
28+
from matplotlib import rcParams
29+
30+
with plt.xkcd():
31+
rcParams['path.sketch_seed']=0
32+
rcParams['path.sketch']=(2,120,40)
33+
pat,txt=plt.pie([10,20,30,40],wedgeprops={'edgecolor':'black'})
34+
plt.legend(pat,['first','second','third','fourth'],loc='best')
35+
plt.title("seed = 0")
36+
plt.show()
37+
38+
.. plot::
39+
:include-source: true
40+
41+
import matplotlib.pyplot as plt
42+
from matplotlib import rcParams
43+
44+
fig, ax = plt.subplots()
45+
x = np.linspace(0.7, 1.42, 100)
46+
y = x ** 2
47+
ln = ax.plot(x, y, color='black')
48+
ln[0].set_sketch_params(100, 100, 20, 40)
49+
plt.title("seed = 40")
50+
plt.show()
51+
52+
.. plot::
53+
:include-source: true
54+
55+
import matplotlib.pyplot as plt
56+
from matplotlib import rcParams
57+
58+
with plt.xkcd(seed=19680801):
59+
import matplotlib
60+
from matplotlib import gridspec
61+
62+
rcParams['path.sketch']=(2,120,40)
63+
64+
pat,txt=plt.pie([10,20,30,40],wedgeprops={'edgecolor':'black'})
65+
plt.legend(pat,['first','second','third','fourth'],loc='best')
66+
plt.title("seed = 19680801")
67+
plt.show()

lib/matplotlib/artist.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ def __init__(self):
209209
self._gid = None
210210
self._snap = None
211211
self._sketch = mpl.rcParams['path.sketch']
212+
self._sketch_seed = mpl.rcParams['path.sketch_seed']
212213
self._path_effects = mpl.rcParams['path.effects']
213214
self._sticky_edges = _XYPair([], [])
214215
self._in_layout = True
@@ -681,7 +682,8 @@ def get_sketch_params(self):
681682
"""
682683
return self._sketch
683684

684-
def set_sketch_params(self, scale=None, length=None, randomness=None):
685+
def set_sketch_params(self, scale=None, length=None, randomness=None,
686+
seed=None):
685687
"""
686688
Set the sketch parameters.
687689
@@ -701,12 +703,21 @@ def set_sketch_params(self, scale=None, length=None, randomness=None):
701703
The PGF backend uses this argument as an RNG seed and not as
702704
described above. Using the same seed yields the same random shape.
703705
704-
.. ACCEPTS: (scale: float, length: float, randomness: float)
706+
seed : int, optional
707+
Seed for the internal pseudo-random number generator.
708+
709+
.. versionadded:: 3.8
710+
711+
.. ACCEPTS: (scale: float, length: float, randomness: float, seed: int)
705712
"""
713+
if seed is not None:
714+
self._sketch_seed = seed
715+
706716
if scale is None:
707717
self._sketch = None
708718
else:
709-
self._sketch = (scale, length or 128.0, randomness or 16.0)
719+
self._sketch = (scale, length or 128.0, randomness or 16.0,
720+
self._sketch_seed)
710721
self.stale = True
711722

712723
def set_path_effects(self, path_effects):

lib/matplotlib/artist.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,13 @@ class Artist:
7676
def set_gid(self, gid: str | None) -> None: ...
7777
def get_snap(self) -> bool | None: ...
7878
def set_snap(self, snap: bool | None) -> None: ...
79-
def get_sketch_params(self) -> tuple[float, float, float] | None: ...
79+
def get_sketch_params(self) -> tuple[float, float, float, int] | None: ...
8080
def set_sketch_params(
8181
self,
8282
scale: float | None = ...,
8383
length: float | None = ...,
8484
randomness: float | None = ...,
85+
seed: int | None = ...,
8586
) -> None: ...
8687
def set_path_effects(self, path_effects: list[AbstractPathEffect]) -> None: ...
8788
def get_path_effects(self) -> list[AbstractPathEffect]: ...

lib/matplotlib/backend_bases.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,19 @@ def _draw_disabled(self):
754754

755755
return _setattr_cm(self, **no_ops)
756756

757+
@property
758+
def _seed_increment(self):
759+
"""
760+
seed increment for renderer.
761+
It is used to implement the rolling characteristic for seed
762+
"""
763+
self.__seed_increment += 1
764+
return self.__seed_increment
765+
766+
@_seed_increment.setter
767+
def _seed_increment(self, value):
768+
self.__seed_increment = value
769+
757770

758771
class GraphicsContextBase:
759772
"""An abstract base class that provides color, line styles, etc."""
@@ -1062,7 +1075,8 @@ def get_sketch_params(self):
10621075
"""
10631076
return self._sketch
10641077

1065-
def set_sketch_params(self, scale=None, length=None, randomness=None):
1078+
def set_sketch_params(self, scale=None, length=None, randomness=None,
1079+
seed=None):
10661080
"""
10671081
Set the sketch parameters.
10681082
@@ -1076,10 +1090,19 @@ def set_sketch_params(self, scale=None, length=None, randomness=None):
10761090
The length of the wiggle along the line, in pixels.
10771091
randomness : float, default: 16
10781092
The scale factor by which the length is shrunken or expanded.
1093+
seed : int, optional
1094+
Seed for the internal pseudo-random number generator.
1095+
1096+
.. versionadded:: 3.8
10791097
"""
1098+
10801099
self._sketch = (
10811100
None if scale is None
1082-
else (scale, length or 128., randomness or 16.))
1101+
else (scale,
1102+
length or rcParams['path.sketch'][1],
1103+
randomness or rcParams['path.sketch'][2],
1104+
seed or rcParams['path.sketch_seed'])
1105+
)
10831106

10841107

10851108
class TimerBase:

lib/matplotlib/backend_bases.pyi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ class RendererBase:
136136
def stop_rasterizing(self) -> None: ...
137137
def start_filter(self) -> None: ...
138138
def stop_filter(self, filter_func) -> None: ...
139+
@property
140+
def _seed_increment(self) -> int: ...
141+
@_seed_increment.setter
142+
def _seed_increment(self, value: int) -> None: ...
139143

140144
class GraphicsContextBase:
141145
def __init__(self) -> None: ...
@@ -180,6 +184,7 @@ class GraphicsContextBase:
180184
scale: float | None = ...,
181185
length: float | None = ...,
182186
randomness: float | None = ...,
187+
seed:int | None = ...,
183188
) -> None: ...
184189

185190
class TimerBase:

lib/matplotlib/backends/backend_pgf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ def _print_pgf_path(self, gc, path, transform, rgbFace=None):
575575
# and has a separate "scale" argument for the amplitude.
576576
# -> Use "randomness" as PRNG seed to allow the user to force the
577577
# same shape on multiple sketched lines
578-
scale, length, randomness = sketch_params
578+
scale, length, randomness, seed = sketch_params
579579
if scale is not None:
580580
# make matplotlib and PGF rendering visually similar
581581
length *= 0.5

lib/matplotlib/figure.py

Lines changed: 2 additions & 0 deletions
10000
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
Artist, allow_rasterization, _finalize_rasterization)
4646
from matplotlib.backend_bases import (
4747
DrawEvent, FigureCanvasBase, NonGuiException, MouseButton, _get_renderer)
48+
4849
import matplotlib._api as _api
4950
import matplotlib.cbook as cbook
5051
import matplotlib.colorbar as cbar
@@ -3141,6 +3142,7 @@ def draw(self, renderer):
31413142

31423143
artists = self._get_draw_artists(renderer)
31433144
try:
3145+
renderer._seed_increment = 0
31443146
renderer.open_group('figure', gid=self.get_gid())
31453147
if self.axes and self.get_layout_engine() is not None:
31463148
try:

lib/matplotlib/lines.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,8 @@ def draw(self, renderer):
823823
gc.set_foreground(ec_rgba, isRGBA=True)
824824
if self.get_sketch_params() is not None:
825825
scale, length, randomness = self.get_sketch_params()
826-
gc.set_sketch_params(scale/2, length/2, 2*randomness)
826+
seed = self._sketch_seed
827+
gc.set_sketch_params(scale/2, length/2, 2*randomness, seed)
827828

828829
marker = self._marker
829830

lib/matplotlib/mpl-data/matplotlibrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@
677677
# - *randomness* is the factor by which the length is
678678
# randomly scaled.
679679
#path.effects:
680+
#path.sketch_seed: 0 # seed for the internal pseudo number generator.
680681

681682

682683
## ***************************************************************************

lib/matplotlib/patches.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,9 @@ def _draw_paths_with_artist_properties(
563563
gc.set_hatch_color(self._hatch_color)
564564

565565
if self.get_sketch_params() is not None:
566-
gc.set_sketch_params(*self.get_sketch_params())
566+
scale, length, randomness = self.get_sketch_params()
567+
gc.set_sketch_params(scale, length, randomness,
568+
self._sketch_seed+renderer._seed_increment)
567569

568570
if self.get_path_effects():
569571
from matplotlib.patheffects import PathEffectRenderer

lib/matplotlib/path.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,8 @@ def iter_segments(self, transform=None, remove_nans=True, clip=None,
382382
If True, curve segments will be returned as curve segments.
383383
If False, all curves will be converted to line segments.
384384
sketch : None or sequence, optional
385-
If not None, must be a 3-tuple of the form
386-
(scale, length, randomness), representing the sketch parameters.
385+
If not None, must be a 4-tuple of the form
386+
(scale, length, randomness, seed), representing the sketch parameters.
387387
"""
388388
if not len(self):
389389
return

lib/matplotlib/path.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class Path:
6161
stroke_width: float = ...,
6262
simplify: bool | None = ...,
6363
curves: bool = ...,
64-
sketch: tuple[float, float, float] | None = ...,
64+
sketch: tuple[float, float, float, int] | None = ...,
6565
) -> Generator[tuple[np.ndarray, np.uint8], None, None]: ...
6666
def iter_bezier(self, **kwargs) -> Generator[BezierSegment, None, None]: ...
6767
def cleaned(
@@ -74,7 +74,7 @@ class Path:
7474
curves: bool = ...,
7575
stroke_width: float = ...,
7676
snap: bool | None = ...,
77-
sketch: tuple[float, float, float] | None = ...
77+
sketch: tuple[float, float, float, int] | None = ...
7878
) -> Path: ...
7979
def transformed(self, transform: Transform) -> Path: ...
8080
def contains_point(

lib/matplotlib/pyplot.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -701,8 +701,8 @@ def setp(obj, *args, **kwargs):
701701

702702

703703
def xkcd(
704-
scale: float = 1, length: float = 100, randomness: float = 2
705-
) -> ExitStack:
704+
scale: float = 1, length: float = 100, randomness: float = 2,
705+
seed: int | None = None) -> ExitStack:
706706
"""
707707
Turn on `xkcd <https://xkcd.com/>`_ sketch-style drawing mode. This will
708708
only have effect on things drawn after this function is called.
@@ -718,6 +718,8 @@ def xkcd(
718718
The length of the wiggle along the line.
719719
randomness : float, optional
720720
The scale factor by which the length is shrunken or expanded.
721+
seed: int, optional
722+
Seed for the internal pseudo-random number generator.
721723
722724
Notes
723725
-----
@@ -738,6 +740,9 @@ def xkcd(
738740
# This cannot be implemented in terms of contextmanager() or rc_context()
739741
# because this needs to work as a non-contextmanager too.
740742

743+
if seed is not None:
744+
rcParams['path.sketch_seed'] = seed
745+
741746
if rcParams['text.usetex']:
742747
raise RuntimeError(
743748
"xkcd mode is not compatible with text.usetex = True")

lib/matplotlib/rcsetup.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,15 @@ def validate_sketch(s):
565565
raise ValueError("Expected a (scale, length, randomness) triplet")
566566

567567

568+
def validate_sketch_seed(s):
569+
s = validate_int(s)
570+
571+
if s >= 0:
572+
return s
573+
else:
574+
raise ValueError("seed must be a non negative integer")
575+
576+
568577
def _validate_greaterthan_minushalf(s):
569578
s = validate_float(s)
570579
if s > -0.5:
@@ -1288,6 +1297,7 @@ def _convert_validator_spec(key, conv):
12881297
"path.simplify_threshold": _validate_greaterequal0_lessequal1,
12891298
"path.snap": validate_bool,
12901299
"path.sketch": validate_sketch,
1300+
"path.sketch_seed": validate_sketch_seed,
12911301
"path.effects": validate_anylist,
12921302
"agg.path.chunksize": validate_int, # 0 to disable chunking
12931303

lib/matplotlib/rcsetup.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ def _validate_linestyle(s: Any) -> LineStyleType: ...
140140
def validate_markeverylist(s: Any) -> list[MarkEveryType]: ...
141141
def validate_bbox(s: Any) -> Literal["tight", "standard"] | None: ...
142142
def validate_sketch(s: Any) -> None | tuple[float, float, float]: ...
143+
def validate_sketch_seed(s: Any) -> int: ...
143144
def validate_hatch(s: Any) -> str: ...
144145
def validate_hatchlist(s: Any) -> list[str]: ...
145146
def validate_dashlist(s: Any) -> list[list[float]]: ...
Loading
Loading

lib/matplotlib/tests/test_backend_pgf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ def test_sketch_params():
384384
ax.set_yticks([])
385385
ax.set_frame_on(False)
386386
handle, = ax.plot([0, 1])
387-
handle.set_sketch_params(scale=5, length=30, randomness=42)
387+
handle.set_sketch_params(scale=5, length=30, randomness=42, seed=0)
388388

389389
with BytesIO() as fd:
390390
fig.savefig(fd, format='pgf')

0 commit comments

Comments
 (0)
0