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/users/next_whats_new/sketch_seed.rst b/doc/users/next_whats_new/sketch_seed.rst
new file mode 100644
index 000000000000..97ebea87fe2c
--- /dev/null
+++ b/doc/users/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 5c3f6dc5952f..c53c3f88da7c 100644
--- a/lib/matplotlib/artist.py
+++ b/lib/matplotlib/artist.py
@@ -209,6 +209,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
@@ -681,7 +682,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.
@@ -701,12 +703,21 @@ 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 or 128.0, randomness or 16.0,
+ self._sketch_seed)
self.stale = True
def set_path_effects(self, path_effects):
diff --git a/lib/matplotlib/artist.pyi b/lib/matplotlib/artist.pyi
index 101e97a9a072..a9d5d7ed47b6 100644
--- a/lib/matplotlib/artist.pyi
+++ b/lib/matplotlib/artist.pyi
@@ -76,12 +76,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 009410593b34..dbf1788b97af 100644
--- a/lib/matplotlib/backend_bases.py
+++ b/lib/matplotlib/backend_bases.py
@@ -754,6 +754,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."""
@@ -1062,7 +1075,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.
@@ -1076,10 +1090,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 0ae88cf18a42..3d47f39a1dae 100644
--- a/lib/matplotlib/backend_bases.pyi
+++ b/lib/matplotlib/backend_bases.pyi
@@ -136,6 +136,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: ...
@@ -180,6 +184,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 734c188b0048..2c05d1b6e182 100644
--- a/lib/matplotlib/backends/backend_pgf.py
+++ b/lib/matplotlib/backends/backend_pgf.py
@@ -575,7 +575,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 ce263c3d8d1c..244100444507 100644
--- a/lib/matplotlib/figure.py
+++ b/lib/matplotlib/figure.py
@@ -45,6 +45,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
@@ -3141,6 +3142,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 92d55a3fe6ae..f793d1cdf7f6 100644
--- a/lib/matplotlib/lines.py
+++ b/lib/matplotlib/lines.py
@@ -823,7 +823,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 2c53651da3d6..f9ad70f00f24 100644
--- a/lib/matplotlib/mpl-data/matplotlibrc
+++ b/lib/matplotlib/mpl-data/matplotlibrc
@@ -677,6 +677,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 92dc55940b8a..802fa34b79f5 100644
--- a/lib/matplotlib/patches.py
+++ b/lib/matplotlib/patches.py
@@ -563,7 +563,9 @@ def _draw_paths_with_artist_properties(
gc.set_hatch_color(self._hatch_color)
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 a687db923c3c..f907e27dc362 100644
--- a/lib/matplotlib/path.py
+++ b/lib/matplotlib/path.py
@@ -382,8 +382,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 c96c5a0ba9e4..91256e7fd1cd 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 415d5c042241..618cc1bb35e3 100644
--- a/lib/matplotlib/pyplot.py
+++ b/lib/matplotlib/pyplot.py
@@ -701,8 +701,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. This will
only have effect on things drawn after this function is called.
@@ -718,6 +718,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
-----
@@ -738,6 +740,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 276bb9f812a9..09e5ebbd1eb9 100644
--- a/lib/matplotlib/rcsetup.py
+++ b/lib/matplotlib/rcsetup.py
@@ -565,6 +565,15 @@ def validate_sketch(s):
raise ValueError("Expected a (scale, length, randomness) triplet")
+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:
@@ -1288,6 +1297,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
diff --git a/lib/matplotlib/rcsetup.pyi b/lib/matplotlib/rcsetup.pyi
index 8a8a9e71d666..340f131670a0 100644
--- a/lib/matplotlib/rcsetup.pyi
+++ b/lib/matplotlib/rcsetup.pyi
@@ -140,6 +140,7 @@ def _validate_linestyle(s: Any) -> LineStyleType: ...
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 a866916c58c6..6e15265f95c0 100644
--- a/lib/matplotlib/tests/test_backend_pgf.py
+++ b/lib/matplotlib/tests/test_backend_pgf.py
@@ -384,7 +384,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 0a1d6c6b5e52..56ec4ecd258d 100644
--- a/lib/matplotlib/tests/test_path.py
+++ b/lib/matplotlib/tests/test_path.py
@@ -8,10 +8,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():
@@ -241,13 +242,23 @@ 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
+
+
@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)
@@ -261,7 +272,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)
@@ -269,6 +279,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/src/_backend_agg.h b/src/_backend_agg.h
index f15fa05dd5fd..6ed5d519b52b 100644
--- a/src/_backend_agg.h
+++ b/src/_backend_agg.h
@@ -483,7 +483,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);
}
diff --git a/src/_backend_agg_basic_types.h b/src/_backend_agg_basic_types.h
index 21a84bb6a5ae..ad0254ae04ab 100644
--- a/src/_backend_agg_basic_types.h
+++ b/src/_backend_agg_basic_types.h
@@ -23,6 +23,7 @@ struct SketchParams
double scale;
double length;
double randomness;
+ int seed;
};
class Dashes
diff --git a/src/_path.h b/src/_path.h
index 61c4ed07d0d2..0a86139135c0 100644
--- a/src/_path.h
+++ b/src/_path.h
@@ -1051,7 +1051,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);
}
}
@@ -1216,7 +1216,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 8583d84855aa..bf5d5a51aca8 100644
--- a/src/path_converters.h
+++ b/src/path_converters.h
@@ -991,6 +991,7 @@ class PathSimplifier : protected EmbeddedQueue<9>
}
};
+
template
class Sketch
{
@@ -1004,8 +1005,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),
@@ -1015,9 +1018,9 @@ class Sketch
m_last_y(0.0),
m_has_last(false),
m_p(0.0),
- m_rand(0)
+ m_rand(0),
+ m_seed(seed)
{
- rewind(0);
const double d_M_PI = 3.14159265358979323846;
m_p_scale = (2.0 * d_M_PI) / (m_length * m_randomness);
m_log_randomness = 2.0 * log(m_randomness);
@@ -1081,7 +1084,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);
@@ -1101,6 +1104,7 @@ class Sketch
RandomNumberGenerator m_rand;
double m_p_scale;
double m_log_randomness;
+ int m_seed;
};
#endif // MPL_PATH_CONVERTERS_H
diff --git a/src/py_converters.cpp b/src/py_converters.cpp
index 04382c5f94d0..8dff06939d9a 100644
--- a/src/py_converters.cpp
+++ b/src/py_converters.cpp
@@ -451,13 +451,16 @@ int convert_sketch_params(PyObject *obj, void *sketchp)
{
SketchParams *sketch = (SketchParams *)sketchp;
+ sketch->seed=0; // default
+
if (obj == NULL || obj == Py_None) {
sketch->scale = 0.0;
} else if (!PyArg_ParseTuple(obj,
- "ddd:sketch_params",
+ "ddd|i:sketch_params",
&sketch->scale,
&sketch->length,
- &sketch->randomness)) {
+ &sketch->randomness,
+ &sketch->seed)) {
return 0;
}