diff --git a/doc/api/next_api_changes/behavior/26050-AS.rst b/doc/api/next_api_changes/behavior/26050-AS.rst
new file mode 100644
index 000000000000..0f8424f4d047
--- /dev/null
+++ b/doc/api/next_api_changes/behavior/26050-AS.rst
@@ -0,0 +1,13 @@
+Seed for ``path.sketch`` will have a rolling (auto incrementing) behaviour
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The seed for the internal Pseudo number generator will now have an auto changing behavior.
+This means that the C code of every artist will get a different seed every time it is called
+and this will be done in a deterministic manner.
+
+Two figures sketched with the same parameters and different seed will look different from one another.
+
+``Artist.get_sketch_params()`` will now return a 4-tuple instead of a 3-tuple consisting of
+(scale, length, randomness, seed) of the form (float, float, float, int).
+
+See 'What's new' on how to set a value to the seed and its behaviour.
diff --git a/doc/release/next_whats_new/sketch_seed.rst b/doc/release/next_whats_new/sketch_seed.rst
new file mode 100644
index 000000000000..97ebea87fe2c
--- /dev/null
+++ b/doc/release/next_whats_new/sketch_seed.rst
@@ -0,0 +1,67 @@
+``sketch_seed`` parameter for rcParams
+--------------------------------------
+
+`~matplotlib.rcParams` now has a new parameter ``path.sketch_seed``.
+Its default value is 0 and accepted values are any non negative integer.
+This allows the user to set the seed for the internal pseudo random number generator in one of three ways.
+
+1) Directly changing the rcParam:
+
+ rcParams['path.sketch_seed'] = 20
+
+2) Passing a value to the new *seed* parameter of `~matplotlib.pyplot.xkcd` function:
+
+ plt.xkcd(seed=20)
+
+3) Passing a value to the new *seed* parameter of matplotlib.artist.set_sketch_params function:
+
+ ln = plt.plot(x, y)
+ ln[0].set_sketch_params(seed = 20)
+
+The seed will also have a changing characteristic for every artist which will be done in a deterministic manner.
+
+
+.. plot::
+ :include-source: true
+
+ import matplotlib.pyplot as plt
+ from matplotlib import rcParams
+
+ with plt.xkcd():
+ rcParams['path.sketch_seed']=0
+ rcParams['path.sketch']=(2,120,40)
+ pat,txt=plt.pie([10,20,30,40],wedgeprops={'edgecolor':'black'})
+ plt.legend(pat,['first','second','third','fourth'],loc='best')
+ plt.title("seed = 0")
+ plt.show()
+
+.. plot::
+ :include-source: true
+
+ import matplotlib.pyplot as plt
+ from matplotlib import rcParams
+
+ fig, ax = plt.subplots()
+ x = np.linspace(0.7, 1.42, 100)
+ y = x ** 2
+ ln = ax.plot(x, y, color='black')
+ ln[0].set_sketch_params(100, 100, 20, 40)
+ plt.title("seed = 40")
+ plt.show()
+
+.. plot::
+ :include-source: true
+
+ import matplotlib.pyplot as plt
+ from matplotlib import rcParams
+
+ with plt.xkcd(seed=19680801):
+ import matplotlib
+ from matplotlib import gridspec
+
+ rcParams['path.sketch']=(2,120,40)
+
+ pat,txt=plt.pie([10,20,30,40],wedgeprops={'edgecolor':'black'})
+ plt.legend(pat,['first','second','third','fourth'],loc='best')
+ plt.title("seed = 19680801")
+ plt.show()
diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py
index cb6e650a94cc..50d25f11b700 100644
--- a/lib/matplotlib/artist.py
+++ b/lib/matplotlib/artist.py
@@ -222,6 +222,7 @@ def __init__(self):
self._gid = None
self._snap = None
self._sketch = mpl.rcParams['path.sketch']
+ self._sketch_seed = mpl.rcParams['path.sketch_seed']
self._path_effects = mpl.rcParams['path.effects']
self._sticky_edges = _XYPair([], [])
self._in_layout = True
@@ -713,7 +714,8 @@ def get_sketch_params(self):
"""
return self._sketch
- def set_sketch_params(self, scale=None, length=None, randomness=None):
+ def set_sketch_params(self, scale=None, length=None, randomness=None,
+ seed=None):
"""
Set the sketch parameters.
@@ -733,12 +735,22 @@ def set_sketch_params(self, scale=None, length=None, randomness=None):
The PGF backend uses this argument as an RNG seed and not as
described above. Using the same seed yields the same random shape.
- .. ACCEPTS: (scale: float, length: float, randomness: float)
+ seed : int, optional
+ Seed for the internal pseudo-random number generator.
+
+ .. versionadded:: 3.8
+
+ .. ACCEPTS: (scale: float, length: float, randomness: float, seed: int)
"""
+ if seed is not None:
+ self._sketch_seed = seed
+
if scale is None:
self._sketch = None
else:
- self._sketch = (scale, length or 128.0, randomness or 16.0)
+ self._sketch = (scale,length if length is not None else 128.0,
+ randomness if randomness is not None else 16.0)
+
self.stale = True
def set_path_effects(self, path_effects):
diff --git a/lib/matplotlib/artist.pyi b/lib/matplotlib/artist.pyi
index 7b8b0c36be69..b90b9be22d8c 100644
--- a/lib/matplotlib/artist.pyi
+++ b/lib/matplotlib/artist.pyi
@@ -77,12 +77,13 @@ class Artist:
def set_gid(self, gid: str | None) -> None: ...
def get_snap(self) -> bool | None: ...
def set_snap(self, snap: bool | None) -> None: ...
- def get_sketch_params(self) -> tuple[float, float, float] | None: ...
+ def get_sketch_params(self) -> tuple[float, float, float, int] | None: ...
def set_sketch_params(
self,
scale: float | None = ...,
length: float | None = ...,
randomness: float | None = ...,
+ seed: int | None = ...,
) -> None: ...
def set_path_effects(self, path_effects: list[AbstractPathEffect]) -> None: ...
def get_path_effects(self) -> list[AbstractPathEffect]: ...
diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py
index fc19f6108d13..59020a844041 100644
--- a/lib/matplotlib/backend_bases.py
+++ b/lib/matplotlib/backend_bases.py
@@ -691,6 +691,19 @@ def _draw_disabled(self):
return _setattr_cm(self, **no_ops)
+ @property
+ def _seed_increment(self):
+ """
+ seed increment for renderer.
+ It is used to implement the rolling characteristic for seed
+ """
+ self.__seed_increment += 1
+ return self.__seed_increment
+
+ @_seed_increment.setter
+ def _seed_increment(self, value):
+ self.__seed_increment = value
+
class GraphicsContextBase:
"""An abstract base class that provides color, line styles, etc."""
@@ -1003,7 +1016,8 @@ def get_sketch_params(self):
"""
return self._sketch
- def set_sketch_params(self, scale=None, length=None, randomness=None):
+ def set_sketch_params(self, scale=None, length=None, randomness=None,
+ seed=None):
"""
Set the sketch parameters.
@@ -1017,10 +1031,19 @@ def set_sketch_params(self, scale=None, length=None, randomness=None):
The length of the wiggle along the line, in pixels.
randomness : float, default: 16
The scale factor by which the length is shrunken or expanded.
+ seed : int, optional
+ Seed for the internal pseudo-random number generator.
+
+ .. versionadded:: 3.8
"""
+
self._sketch = (
None if scale is None
- else (scale, length or 128., randomness or 16.))
+ else (scale,
+ length or rcParams['path.sketch'][1],
+ randomness or rcParams['path.sketch'][2],
+ seed or rcParams['path.sketch_seed'])
+ )
class TimerBase:
diff --git a/lib/matplotlib/backend_bases.pyi b/lib/matplotlib/backend_bases.pyi
index a69b36093839..a3598ef83763 100644
--- a/lib/matplotlib/backend_bases.pyi
+++ b/lib/matplotlib/backend_bases.pyi
@@ -142,6 +142,10 @@ class RendererBase:
def stop_rasterizing(self) -> None: ...
def start_filter(self) -> None: ...
def stop_filter(self, filter_func) -> None: ...
+ @property
+ def _seed_increment(self) -> int: ...
+ @_seed_increment.setter
+ def _seed_increment(self, value: int) -> None: ...
class GraphicsContextBase:
def __init__(self) -> None: ...
@@ -187,6 +191,7 @@ class GraphicsContextBase:
scale: float | None = ...,
length: float | None = ...,
randomness: float | None = ...,
+ seed:int | None = ...,
) -> None: ...
class TimerBase:
diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py
index 2d2e24c3286c..dfa39f73ba86 100644
--- a/lib/matplotlib/backends/backend_pgf.py
+++ b/lib/matplotlib/backends/backend_pgf.py
@@ -598,7 +598,7 @@ def _print_pgf_path(self, gc, path, transform, rgbFace=None):
# and has a separate "scale" argument for the amplitude.
# -> Use "randomness" as PRNG seed to allow the user to force the
# same shape on multiple sketched lines
- scale, length, randomness = sketch_params
+ scale, length, randomness, seed = sketch_params
if scale is not None:
# make matplotlib and PGF rendering visually similar
length *= 0.5
diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py
index 501b3e32ebf4..9525bc2521ec 100644
--- a/lib/matplotlib/figure.py
+++ b/lib/matplotlib/figure.py
@@ -42,6 +42,7 @@
Artist, allow_rasterization, _finalize_rasterization)
from matplotlib.backend_bases import (
DrawEvent, FigureCanvasBase, NonGuiException, MouseButton, _get_renderer)
+
import matplotlib._api as _api
import matplotlib.cbook as cbook
import matplotlib.colorbar as cbar
@@ -3263,6 +3264,7 @@ def draw(self, renderer):
artists = self._get_draw_artists(renderer)
try:
+ renderer._seed_increment = 0
renderer.open_group('figure', gid=self.get_gid())
if self.axes and self.get_layout_engine() is not None:
try:
diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py
index 0cfda07dd627..677378a3e346 100644
--- a/lib/matplotlib/lines.py
+++ b/lib/matplotlib/lines.py
@@ -838,7 +838,8 @@ def draw(self, renderer):
gc.set_foreground(ec_rgba, isRGBA=True)
if self.get_sketch_params() is not None:
scale, length, randomness = self.get_sketch_params()
- gc.set_sketch_params(scale/2, length/2, 2*randomness)
+ seed = self._sketch_seed
+ gc.set_sketch_params(scale/2, length/2, 2*randomness, seed)
marker = self._marker
diff --git a/lib/matplotlib/mpl-data/matplotlibrc b/lib/matplotlib/mpl-data/matplotlibrc
index e934109ee492..9e5903891f06 100644
--- a/lib/matplotlib/mpl-data/matplotlibrc
+++ b/lib/matplotlib/mpl-data/matplotlibrc
@@ -714,6 +714,7 @@
# - *randomness* is the factor by which the length is
# randomly scaled.
#path.effects:
+#path.sketch_seed: 0 # seed for the internal pseudo number generator.
## ***************************************************************************
diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py
index 4a4bd698db04..346e81ec7a78 100644
--- a/lib/matplotlib/patches.py
+++ b/lib/matplotlib/patches.py
@@ -703,7 +703,9 @@ def _draw_paths_with_artist_properties(
gc.set_hatch_linewidth(self._hatch_linewidth)
if self.get_sketch_params() is not None:
- gc.set_sketch_params(*self.get_sketch_params())
+ scale, length, randomness = self.get_sketch_params()
+ gc.set_sketch_params(scale, length, randomness,
+ self._sketch_seed+renderer._seed_increment)
if self.get_path_effects():
from matplotlib.patheffects import PathEffectRenderer
diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py
index f65ade669167..6123027000f7 100644
--- a/lib/matplotlib/path.py
+++ b/lib/matplotlib/path.py
@@ -401,8 +401,8 @@ def iter_segments(self, transform=None, remove_nans=True, clip=None,
If True, curve segments will be returned as curve segments.
If False, all curves will be converted to line segments.
sketch : None or sequence, optional
- If not None, must be a 3-tuple of the form
- (scale, length, randomness), representing the sketch parameters.
+ If not None, must be a 4-tuple of the form
+ (scale, length, randomness, seed), representing the sketch parameters.
"""
if not len(self):
return
diff --git a/lib/matplotlib/path.pyi b/lib/matplotlib/path.pyi
index 8a5a5c03792e..7073d9117da0 100644
--- a/lib/matplotlib/path.pyi
+++ b/lib/matplotlib/path.pyi
@@ -61,7 +61,7 @@ class Path:
stroke_width: float = ...,
simplify: bool | None = ...,
curves: bool = ...,
- sketch: tuple[float, float, float] | None = ...,
+ sketch: tuple[float, float, float, int] | None = ...,
) -> Generator[tuple[np.ndarray, np.uint8], None, None]: ...
def iter_bezier(self, **kwargs) -> Generator[BezierSegment, None, None]: ...
def cleaned(
@@ -74,7 +74,7 @@ class Path:
curves: bool = ...,
stroke_width: float = ...,
snap: bool | None = ...,
- sketch: tuple[float, float, float] | None = ...
+ sketch: tuple[float, float, float, int] | None = ...
) -> Path: ...
def transformed(self, transform: Transform) -> Path: ...
def contains_point(
diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py
index 483bc031336c..e413c6ceeebe 100644
--- a/lib/matplotlib/pyplot.py
+++ b/lib/matplotlib/pyplot.py
@@ -828,8 +828,8 @@ def setp(obj, *args, **kwargs):
def xkcd(
- scale: float = 1, length: float = 100, randomness: float = 2
-) -> ExitStack:
+ scale: float = 1, length: float = 100, randomness: float = 2,
+ seed: int | None = None) -> ExitStack:
"""
Turn on `xkcd `_ sketch-style drawing mode.
@@ -846,6 +846,8 @@ def xkcd(
The length of the wiggle along the line.
randomness : float, optional
The scale factor by which the length is shrunken or expanded.
+ seed: int, optional
+ Seed for the internal pseudo-random number generator.
Notes
-----
@@ -865,6 +867,9 @@ def xkcd(
# This cannot be implemented in terms of contextmanager() or rc_context()
# because this needs to work as a non-contextmanager too.
+ if seed is not None:
+ rcParams['path.sketch_seed'] = seed
+
if rcParams['text.usetex']:
raise RuntimeError(
"xkcd mode is not compatible with text.usetex = True")
diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py
index e56da5200386..01d3fdb32ea7 100644
--- a/lib/matplotlib/rcsetup.py
+++ b/lib/matplotlib/rcsetup.py
@@ -636,6 +636,15 @@ def validate_sketch(s):
raise ValueError("Expected a (scale, length, randomness) tuple") from exc
+def validate_sketch_seed(s):
+ s = validate_int(s)
+
+ if s >= 0:
+ return s
+ else:
+ raise ValueError("seed must be a non negative integer")
+
+
def _validate_greaterthan_minushalf(s):
s = validate_float(s)
if s > -0.5:
@@ -1388,6 +1397,7 @@ def _convert_validator_spec(key, conv):
"path.simplify_threshold": _validate_greaterequal0_lessequal1,
"path.snap": validate_bool,
"path.sketch": validate_sketch,
+ "path.sketch_seed": validate_sketch_seed,
"path.effects": validate_anylist,
"agg.path.chunksize": validate_int, # 0 to disable chunking
@@ -3004,6 +3014,13 @@ class _Subsection:
"- *randomness* is the factor by which the length is randomly "
" scaled."
),
+ _Param(
+ "path.sketch_seed",
+ default=0,
+ validator=validate_sketch_seed,
+ description="Seed for the random number generator used in sketch mode. "
+ "The seed is auto-incremented after each path is drawn."
+ ),
_Param(
"path.effects",
default=[],
diff --git a/lib/matplotlib/rcsetup.pyi b/lib/matplotlib/rcsetup.pyi
index 120c0c93bec9..de1a9d219113 100644
--- a/lib/matplotlib/rcsetup.pyi
+++ b/lib/matplotlib/rcsetup.pyi
@@ -143,6 +143,7 @@ def _validate_linestyle_or_None(s: Any) -> LineStyleType | None: ...
def validate_markeverylist(s: Any) -> list[MarkEveryType]: ...
def validate_bbox(s: Any) -> Literal["tight", "standard"] | None: ...
def validate_sketch(s: Any) -> None | tuple[float, float, float]: ...
+def validate_sketch_seed(s: Any) -> int: ...
def validate_hatch(s: Any) -> str: ...
def validate_hatchlist(s: Any) -> list[str]: ...
def validate_dashlist(s: Any) -> list[list[float]]: ...
diff --git a/lib/matplotlib/tests/baseline_images/test_path/xkcd.png b/lib/matplotlib/tests/baseline_images/test_path/xkcd.png
index fd486c42305f..f57d07aab8f2 100644
Binary files a/lib/matplotlib/tests/baseline_images/test_path/xkcd.png and b/lib/matplotlib/tests/baseline_images/test_path/xkcd.png differ
diff --git a/lib/matplotlib/tests/baseline_images/test_path/xkcd_marker.png b/lib/matplotlib/tests/baseline_images/test_path/xkcd_marker.png
index c4224f74c1ec..a163a2254217 100644
Binary files a/lib/matplotlib/tests/baseline_images/test_path/xkcd_marker.png and b/lib/matplotlib/tests/baseline_images/test_path/xkcd_marker.png differ
diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py
index e218a81cdceb..f0aa8413e18c 100644
--- a/lib/matplotlib/tests/test_backend_pgf.py
+++ b/lib/matplotlib/tests/test_backend_pgf.py
@@ -358,7 +358,7 @@ def test_sketch_params():
ax.set_yticks([])
ax.set_frame_on(False)
handle, = ax.plot([0, 1])
- handle.set_sketch_params(scale=5, length=30, randomness=42)
+ handle.set_sketch_params(scale=5, length=30, randomness=42, seed=0)
with BytesIO() as fd:
fig.savefig(fd, format='pgf')
diff --git a/lib/matplotlib/tests/test_path.py b/lib/matplotlib/tests/test_path.py
index a61f01c0d48a..ba1642bb2f05 100644
--- a/lib/matplotlib/tests/test_path.py
+++ b/lib/matplotlib/tests/test_path.py
@@ -9,10 +9,11 @@
from matplotlib import patches
from matplotlib.path import Path
from matplotlib.patches import Polygon
-from matplotlib.testing.decorators import image_comparison
+from matplotlib.testing.decorators import image_comparison, check_figures_equal
import matplotlib.pyplot as plt
from matplotlib import transforms
from matplotlib.backend_bases import MouseEvent
+from matplotlib import rcParams
def test_empty_closed_path():
@@ -242,13 +243,45 @@ def test_make_compound_path_stops():
assert np.sum(compound_path.codes == Path.STOP) == 0
+def test_path_sketch_seed():
+ # the default value of path.sketch_seed should be 0
+ assert rcParams['path.sketch_seed'] == 0
+
+
+def test_xkcd_seed_update():
+ # when passing a seed to xkcd, the global rcParam should be updated
+ with plt.xkcd(seed=2000):
+ assert rcParams['path.sketch_seed'] == 2000
+
+
+def test_sketch_rolling_seed():
+ """Same seed should produce identical output every time (deterministic)"""
+
+ import io
+ x = np.linspace(0, 5, 200)
+ y = 20 * np.sin(x)
+
+ with plt.xkcd(seed=100):
+ fig, ax = plt.subplots()
+ ax.plot(x, y)
+ ax.plot(x, y + 5)
+ buf1 = io.BytesIO()
+ buf2 = io.BytesIO()
+ fig.savefig(buf1, format='png')
+ fig.savefig(buf2, format='png')
+
+ buf1.seek(0)
+ buf2.seek(0)
+ assert buf1.read() == buf2.read()
+ plt.close('all')
+
+
@image_comparison(['xkcd.png'], remove_text=True)
def test_xkcd():
np.random.seed(0)
x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x)
-
with plt.xkcd():
fig, ax = plt.subplots()
ax.plot(x, y)
@@ -262,7 +295,6 @@ def test_xkcd_marker():
y1 = x
y2 = 5 - x
y3 = 2.5 * np.ones(8)
-
with plt.xkcd():
fig, ax = plt.subplots()
ax.plot(x, y1, '+', ms=10)
@@ -270,6 +302,77 @@ def test_xkcd_marker():
ax.plot(x, y3, '^', ms=10)
+@check_figures_equal(extensions=['png'])
+def test_xkcd_override(fig_test, fig_ref):
+ x = np.linspace(0.7, 1.42, 100)
+ y = x ** 2
+
+ ln = fig_ref.add_subplot().plot(x, y)
+ ln_test = fig_test.add_subplot().plot(x, y)
+
+ with plt.xkcd():
+ ln[0].set_sketch_params(3, 120, 40, 420)
+
+ # set_sketch should override seed set by xkcd
+ with plt.xkcd(seed=5885):
+ ln_test[0].set_sketch_params(3, 120, 40, 420)
+
+
+@check_figures_equal(extensions=['png'])
+def test_artist_seed(fig_test, fig_ref):
+ x = np.linspace(0.7, 1.42, 100)
+ y = [0.7]*100
+
+ ax_ref = fig_ref.add_subplot()
+ ax_test = fig_test.add_subplot()
+
+ ln = ax_ref.plot(x, y)
+ ln_test = ax_test.plot(x, y)
+
+ ln[0].set_sketch_params(3, 120, 40, 19680801)
+
+ # set_sketch_params seed should override seed set by rcParam
+ # when seed is passed in set_sketch, it should be used
+ # else rcParam should be used as seed
+ rcParams['path.sketch_seed'] = 59856
+ ln_test[0].set_sketch_params(3, 120, 40, 19680801)
+
+
+@check_figures_equal(extensions=['png'])
+def test_path_seed(fig_test, fig_ref):
+ x = x = np.linspace(0, 5, 200)
+ y = 20*np.sin(x)
+
+ ax_ref = fig_ref.add_subplot()
+ ax_test = fig_test.add_subplot()
+
+ rcParams['path.sketch_seed'] = 645
+ rcParams['path.sketch'] = 3, 120, 40
+
+ ln = ax_ref.plot(x, y)
+
+ ln_test = ax_test.plot(x, y)
+ ln_test[0].set_sketch_params(3, 120, 40, 645)
+
+
+@check_figures_equal(extensions=['png'])
+def test_xkcd_seed(fig_test, fig_ref):
+ x = x = np.linspace(0, 5, 200)
+ y = 20*np.sin(x)
+
+ ax_ref = fig_ref.add_subplot()
+ ax_test = fig_test.add_subplot()
+
+ with plt.xkcd(seed=20):
+ ln = ax_ref.plot(x, y)
+ ln[0].set_sketch_params(3, 120, 40)
+
+ with plt.xkcd(seed=40):
+ rcParams['path.sketch_seed'] = 20
+ ln_test = ax_test.plot(x, y)
+ ln_test[0].set_sketch_params(3, 120, 40)
+
+
@image_comparison(['marker_paths.pdf'], remove_text=True)
def test_marker_paths_pdf():
N = 7
diff --git a/lib/matplotlib/typing.py b/lib/matplotlib/typing.py
index 87016984da12..ce4dcfc17554 100644
--- a/lib/matplotlib/typing.py
+++ b/lib/matplotlib/typing.py
@@ -418,6 +418,7 @@
"path.simplify",
"path.simplify_threshold",
"path.sketch",
+ "path.sketch_seed",
"path.snap",
"pcolor.shading",
"pcolormesh.snap",
diff --git a/src/_backend_agg.h b/src/_backend_agg.h
index 1ac3d4c06b13..ce7584c11426 100644
--- a/src/_backend_agg.h
+++ b/src/_backend_agg.h
@@ -481,7 +481,7 @@ RendererAgg::draw_path(GCAgg &gc, PathIterator &path, agg::trans_affine &trans,
snapped_t snapped(clipped, gc.snap_mode, path.total_vertices(), snapping_linewidth);
simplify_t simplified(snapped, simplify, path.simplify_threshold());
curve_t curve(simplified);
- sketch_t sketch(curve, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness);
+ sketch_t sketch(curve, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness, gc.sketch.seed);
_draw_path(sketch, has_clippath, face, gc);
}
@@ -1004,19 +1004,18 @@ inline void RendererAgg::_draw_path_collection_generic(GCAgg &gc,
clipped, gc.snap_mode, path.total_vertices(), points_to_pixels(gc.linewidth));
if (has_codes) {
snapped_curve_t curve(snapped);
- sketch_snapped_curve_t sketch(curve, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness);
- _draw_path(sketch, has_clippath, face, gc);
+sketch_snapped_curve_t sketch(curve, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness, gc.sketch.seed); _draw_path(sketch, has_clippath, face, gc);
} else {
- sketch_snapped_t sketch(snapped, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness);
+ sketch_snapped_t sketch(snapped, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness, gc.sketch.seed);
_draw_path(sketch, has_clippath, face, gc);
}
} else {
if (has_codes) {
curve_t curve(clipped);
- sketch_curve_t sketch(curve, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness);
+ sketch_curve_t sketch(curve, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness, gc.sketch.seed);
_draw_path(sketch, has_clippath, face, gc);
} else {
- sketch_clipped_t sketch(clipped, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness);
+ sketch_clipped_t sketch(clipped, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness, gc.sketch.seed);
_draw_path(sketch, has_clippath, face, gc);
}
}
diff --git a/src/_backend_agg_basic_types.h b/src/_backend_agg_basic_types.h
index b424419ec99e..41c610a73e51 100644
--- a/src/_backend_agg_basic_types.h
+++ b/src/_backend_agg_basic_types.h
@@ -29,6 +29,7 @@ struct SketchParams
double scale;
double length;
double randomness;
+ int seed;
};
class Dashes
@@ -219,8 +220,10 @@ namespace PYBIND11_NAMESPACE { namespace detail {
return true;
}
- auto params = src.cast>();
- std::tie(value.scale, value.length, value.randomness) = params;
+
+
+ auto params = src.cast>();
+ std::tie(value.scale, value.length, value.randomness, value.seed) = params;
return true;
}
diff --git a/src/_path.h b/src/_path.h
index 226d60231682..0772f4790969 100644
--- a/src/_path.h
+++ b/src/_path.h
@@ -1033,7 +1033,7 @@ void cleanup_path(PathIterator &path,
__cleanup_path(simplified, vertices, codes);
} else {
curve_t curve(simplified);
- sketch_t sketch(curve, sketch_params.scale, sketch_params.length, sketch_params.randomness);
+ sketch_t sketch(curve, sketch_params.scale, sketch_params.length, sketch_params.randomness, sketch_params.seed);
__cleanup_path(sketch, vertices, codes);
}
}
@@ -1197,7 +1197,7 @@ bool convert_to_string(PathIterator &path,
return __convert_to_string(simplified, precision, codes, postfix, buffer);
} else {
curve_t curve(simplified);
- sketch_t sketch(curve, sketch_params.scale, sketch_params.length, sketch_params.randomness);
+ sketch_t sketch(curve, sketch_params.scale, sketch_params.length, sketch_params.randomness, sketch_params.seed);
return __convert_to_string(sketch, precision, codes, postfix, buffer);
}
}
diff --git a/src/path_converters.h b/src/path_converters.h
index 1482aeed95f8..e89d048fc90c 100644
--- a/src/path_converters.h
+++ b/src/path_converters.h
@@ -1041,6 +1041,7 @@ class PathSimplifier : protected EmbeddedQueue<9>
}
};
+
template
class Sketch
{
@@ -1054,8 +1055,10 @@ class Sketch
randomness: the factor that the sketch length will randomly
shrink and expand.
+
+ seed: seed for the built-in pseudo-random number generator.
*/
- Sketch(VertexSource &source, double scale, double length, double randomness)
+ Sketch(VertexSource &source, double scale, double length, double randomness, int seed)
: m_source(&source),
m_scale(scale),
m_length(length),
@@ -1065,9 +1068,9 @@ class Sketch
m_last_y(0.0),
m_has_last(false),
m_p(0.0),
- m_rand(0)
+ m_rand(seed),
+ m_seed(seed)
{
- rewind(0);
const double d_M_PI = 3.14159265358979323846;
// Set derived values to zero if m_length or m_randomness are zero to
// avoid divide-by-zero errors when a sketch is created but not used.
@@ -1141,7 +1144,7 @@ class Sketch
m_has_last = false;
m_p = 0.0;
if (m_scale != 0.0) {
- m_rand.seed(0);
+ m_rand.seed(m_seed);
m_segmented.rewind(path_id);
} else {
m_source->rewind(path_id);
@@ -1161,6 +1164,7 @@ class Sketch
RandomNumberGenerator m_rand;
double m_p_scale;
double m_log_randomness;
+ int m_seed;
};
#endif // MPL_PATH_CONVERTERS_H