Skip to content

Sketch seed #25796

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions lib/matplotlib/artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,19 +671,21 @@ def get_sketch_params(self):
-------
tuple or None

A 3-tuple with the following elements:
A 4-tuple with the following elements:

- *scale*: The amplitude of the wiggle perpendicular to the
source line.
- *length*: The length of the wiggle along the line.
- *randomness*: The scale factor by which the length is
shrunken or expanded.
- *seed*: Seed for the internal pseudo-random number
generator.

Returns *None* if no sketch parameters were set.
"""
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=0):
"""
Set the sketch parameters.

Expand All @@ -702,13 +704,18 @@ 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.
seed : int, optional
Seed for the internal pseudo-random number generator.
For the same seed, the result will be exactly the same.

.. ACCEPTS: (scale: float, length: float, randomness: float)
.. versionadded:: 3.8

.. ACCEPTS: (scale: float, length: float, randomness: float, seed: int)
"""
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, seed)
self.stale = True

def set_path_effects(self, path_effects):
Expand Down
6 changes: 4 additions & 2 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -1062,7 +1062,7 @@ 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=0):
"""
Set the sketch parameters.

Expand All @@ -1076,10 +1076,12 @@ 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.
"""
self._sketch = (
None if scale is None
else (scale, length or 128., randomness or 16.))
else (scale, length or 128., randomness or 16., seed))


class TimerBase:
Expand Down
4 changes: 2 additions & 2 deletions lib/matplotlib/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -823,8 +823,8 @@ def draw(self, renderer):
ec_rgba = ec_rgba[:3] + (fc_rgba[3],)
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)
scale, length, randomness, seed = self.get_sketch_params()
gc.set_sketch_params(scale/2, length/2, 2*randomness, seed)

marker = self._marker

Expand Down
8 changes: 6 additions & 2 deletions lib/matplotlib/pyplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ def setp(obj, *args, **kwargs):


def xkcd(
scale: float = 1, length: float = 100, randomness: float = 2
scale: float = 1, length: float = 100, randomness: float = 2, seed: int = -1
) -> ExitStack:
"""
Turn on `xkcd <https://xkcd.com/>`_ sketch-style drawing mode. This will
Expand All @@ -685,6 +685,10 @@ 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. The special
value of -1 will make the randomness different for each object
processed.

Notes
-----
Expand Down Expand Up @@ -717,7 +721,7 @@ def xkcd(
'font.family': ['xkcd', 'xkcd Script', 'Humor Sans', 'Comic Neue',
'Comic Sans MS'],
'font.size': 14.0,
'path.sketch': (scale, length, randomness),
'path.sketch': (scale, length, randomness, seed),
'path.effects': [
patheffects.withStroke(linewidth=4, foreground="w")],
'axes.linewidth': 1.5,
Expand Down
9 changes: 8 additions & 1 deletion lib/matplotlib/rcsetup.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,14 @@ def validate_sketch(s):
try:
return tuple(_listify_validator(validate_float, n=3)(s))
except ValueError:
raise ValueError("Expected a (scale, length, randomness) triplet")
try:
result = tuple(_listify_validator(validate_float, n=4)(s))
# make sure seed is an integer
return (result[0], result[1], result[2], int(result[3]))
except ValueError:
raise ValueError(
"path.sketch must be a 3-tuple (scale, length, randomness) or"
" a 4-tuple (scale, length, randomness, seed)")


def _validate_greaterequal0_lessthan1(s):
Expand Down
1 change: 1 addition & 0 deletions setupext.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ def get_extensions(self):
ext = Extension(
"matplotlib._path", [
"src/py_converters.cpp",
"src/path_converters.cpp",
"src/_path_wrapper.cpp",
])
add_numpy_flags(ext)
Expand Down
2 changes: 1 addition & 1 deletion src/_backend_agg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
1 change: 1 addition & 0 deletions src/_backend_agg_basic_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct SketchParams
double scale;
double length;
double randomness;
int seed;
};

class Dashes
Expand Down
4 changes: 2 additions & 2 deletions src/_path.h
Original file line number Diff line number Diff line change
Expand Up @@ -1067,7 +1067,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);
}
}
Expand Down Expand Up @@ -1232,7 +1232,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);
}

Expand Down
18 changes: 18 additions & 0 deletions src/_path_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "py_converters.h"
#include "py_adaptors.h"
#include "path_converters.h"

PyObject *convert_polygon_vector(std::vector<Polygon> &polygons)
{
Expand Down Expand Up @@ -842,6 +843,22 @@ static PyObject *Py_is_sorted(PyObject *self, PyObject *obj)
}


const char *Py_sketch_reset_previous_seed__doc__ = "sketch_reset_previous_seed(seed)";

static PyObject *Py_sketch_reset_previous_seed(PyObject *self, PyObject *args, PyObject *kwds)
{
int seed;

if (!PyArg_ParseTuple(args,"i:sketch_reset_previous_seed", &seed)) {
return NULL;
}

CALL_CPP("SketchBase::reset_previous_seed",(SketchBase::reset_previous_seed(seed)));

Py_RETURN_NONE;
}


static PyMethodDef module_functions[] = {
{"point_in_path", (PyCFunction)Py_point_in_path, METH_VARARGS, Py_point_in_path__doc__},
{"points_in_path", (PyCFunction)Py_points_in_path, METH_VARARGS, Py_points_in_path__doc__},
Expand All @@ -861,6 +878,7 @@ static PyMethodDef module_functions[] = {
{"cleanup_path", (PyCFunction)Py_cleanup_path, METH_VARARGS, Py_cleanup_path__doc__},
{"convert_to_string", (PyCFunction)Py_convert_to_string, METH_VARARGS, Py_convert_to_string__doc__},
{"is_sorted", (PyCFunction)Py_is_sorted, METH_O, Py_is_sorted__doc__},
{"sketch_reset_previous_seed", (PyCFunction)Py_sketch_reset_previous_seed, METH_VARARGS, Py_sketch_reset_previous_seed__doc__},
{NULL}
};

Expand Down
8 changes: 8 additions & 0 deletions src/path_converters.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* -*- mode: c++; c-basic-offset: 4 -*- */

#define NO_IMPORT_ARRAY


#include "path_converters.h"

int SketchBase::previous_seed=0;
45 changes: 40 additions & 5 deletions src/path_converters.h
Original file line number Diff line number Diff line change
Expand Up @@ -991,8 +991,36 @@ class PathSimplifier : protected EmbeddedQueue<9>
}
};

// XXX: remove!!!
// #include<iostream>

class SketchBase
{
static int previous_seed;
protected:
/* handle the special-case value -1 which means: use the
previous seed plus one. If not a special value, only
remember it as the last one and return it.
*/
int get_prng_seed(int seed)
{
if (seed==-1) {
previous_seed++;
} else {
previous_seed = seed;
}
// std::cerr<<"Seeding PRNG with "<<previous_seed<<" (@"<<(void*)(&previous_seed)<<"; got seed "<<seed<<")"<<std::endl;
return previous_seed;
};
public:
static void reset_previous_seed(int seed = 0)
{
previous_seed = seed;
}
};

template <class VertexSource>
class Sketch
class Sketch: SketchBase
{
public:
/*
Expand All @@ -1004,8 +1032,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),
Expand All @@ -1015,9 +1045,13 @@ class Sketch
m_last_y(0.0),
m_has_last(false),
m_p(0.0),
m_rand(0)
m_rand(0),
/* if scale==0. (e.g. with test), then seed is 0
that would set previous_seed to 0 which is not what we want.
*/
m_seed0( scale!=0 ? get_prng_seed(seed) : 0)
{
rewind(0);
rewind(0); // re-seeds PRNG with m_seed0 again
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);
Expand Down Expand Up @@ -1081,7 +1115,7 @@ class Sketch
m_has_last = false;
m_p = 0.0;
if (m_scale != 0.0) {
m_rand.seed(0);
m_rand.seed(m_seed0);
m_segmented.rewind(path_id);
} else {
m_source->rewind(path_id);
Expand All @@ -1101,6 +1135,7 @@ class Sketch
RandomNumberGenerator m_rand;
double m_p_scale;
double m_log_randomness;
int m_seed0;
};

#endif // MPL_PATH_CONVERTERS_H
7 changes: 5 additions & 2 deletions src/py_converters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down