Skip to content
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

Add a "sketch" path filter #1329

Merged
merged 13 commits into from May 14, 2013
Merged

Add a "sketch" path filter #1329

merged 13 commits into from May 14, 2013

Conversation

mdboom
Copy link
Member

@mdboom mdboom commented Oct 5, 2012

Based on the mailing list thread "XKCD style graphs", this adds a sketchy line path filter to matplotlib.

This PR is not complete, but I thought I'd get something up early so we can hack on it together.

To play with this, set the rcParam['path.sketch'] to a tuple (scale, length, randomness), for example (1.0, 128., 16.).

          * *scale*: The amplitude of the wiggle perpendicular to the
            source line, in pixels.

          * *length*: The length of the wiggle along the line, in
             pixels (default 128.0)

          * *randomness*: The scale factor by which the length is
            shrunken or expanded (default 16.0)

This currently only works with the Agg and PDF backends, though it should be easy enough to add to other vector backends by following the same procedure as the PDF backend. The Mac OS-X backend will require more care, but should be possible as it's been added to path_cleanup.cpp.

Beyond this, it would be nice to add a convenience function xkcd_style, or some such, that thickens the lines, removes the north and east spines, possibly changes the font, etc.

@mdboom
Copy link
Member Author

mdboom commented Oct 5, 2012

Also, I invented my own wiggliness algorithm here based on a randomly progressing sine wave. I think Juergen Hasch's filtered noise approach looks better -- we should probably change this to do that.

@pwuertz
Copy link
Contributor

pwuertz commented Oct 5, 2012

Haha, looks great :)
mpl-xkcd

@mdboom
Copy link
Member Author

mdboom commented Oct 5, 2012

That image seems to be a broken link, maybe? No worries -- just curious what you've decided to do with it.

@pwuertz
Copy link
Contributor

pwuertz commented Oct 5, 2012

Oops, my bad. I better put it on github...

@WeatherGod
Copy link
Member

Let's see Matlab match this! :-P

@mdboom
Copy link
Member Author

mdboom commented Oct 5, 2012

Very cool.

@dmcdougall
Copy link
Member

Amazing. Unfortunately, this won't make it in 1.2; it's not a bug fix.

:)

@dmcdougall
Copy link
Member

This is actually great for doing schematic 'big-picture' plots in a talk where a clean looking plot looks 'too scientific'.

@@ -337,7 +337,16 @@ def validate_bbox(s):
return None
raise ValueError("bbox should be 'tight' or 'standard'")


def validate_sketch(s):
if s == 'None' or s is None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s isn't a very explicit variable name. I'm not sure what the matplotlib's coding convention are regarding to this, but I think the code would gain in readibility with a longer variable name such as sketch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In cases like this, however, I think using a short variable name is fine. "def validate_sketch(sketch)" looks worse to my eye.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dunno... Reading the code as a "not matplotlib expert", it didn't seem obvious what s was. I have for rule not to use one letter variables as they are often confusing for beginners, and I want my code to be easily read and modified by anyone. Explicit is better than implicit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your suggestion for a name, "sketch" would not help--it is no more descriptive of what "s" is than "s". "s" is not a sketch, it is an argument of unknown type being checked to see if it takes one of several valid forms.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NelleV @efiring I understand you both have your opinions. They are also both valid opinions. That being said, it seems as though the best course of action here is to agree to disagree.

@WeatherGod
Copy link
Member

Probably should make it clear in any documentation that this parameter has to be set prior to any plotting calls, much like with setting font sizes and such.

@WeatherGod
Copy link
Member

sweet! add the plt.rc('path', sketch=(1.0, 128.0, 16.0)) towards the beginning of the animate_decay.py example for some fun!

http://youtu.be/PRRRvIRWk9M

@WeatherGod
Copy link
Member

And doing it to the subplots.py example is really neat!

http://youtu.be/jx4D2NGqkd0

And it also works for 3D plots as well:
Lorenz Sketch

@certik
Copy link

certik commented Oct 6, 2012

I don't have time to test this now, but I just want to say that I like this a lot!

@glyg
Copy link

glyg commented Oct 6, 2012

That's really nice! Now fetch the Ipython crew so we have a whole xkcd python notebook

@WeatherGod
Copy link
Member

Ok, some bugs:

  1. The GraphicsContextBase doesn't copy the ._sketch attribute when creating a copy of itself.
  2. Artist.update_from() doesn't copy the ._sketch attribute (as well as some others...) (side note, some of the setters in Artist should probably be calling pchanged()).
  3. Even after these changes, I still can not seem to get various collection objects to sketch, so somewhere in chain, the sketch params are not getting propagated.

@takluyver
Copy link
Contributor

@glyg: A notebook for you: view | download

(Not my doing - I just saw a link to it)

@jakevdp
Copy link
Contributor

jakevdp commented Oct 6, 2012

Another notebook with some stabs in this direction (though done at the patch level in matplotlib):
http://nbviewer.ipython.org/url/jakevdp.github.com/downloads/notebooks/XKCD_plots.ipynb
Would it be possible for the sketch filter to automatically add a white background line so that there's the bit of space between line crossings?

@WeatherGod
Copy link
Member

On Saturday, October 6, 2012, Jacob Vanderplas wrote:

Another notebook with some stabs in this direction (though done at the
patch level in matplotlib):

http://nbviewer.ipython.org/url/jakevdp.github.com/downloads/notebooks/XKCD_plots.ipynb
Would it be possible for the sketch filter to automatically add a white
background line so that there's the bit of space between line crossings?

No, that is going to have to be done as a path effect or as an offset
transform.

@Nikratio
Copy link

@WeatherGod: In your animation examples, even lines that are not being changed (e.g. x and y axis) are constantly wobbling around. Did you do that deliberately, or is that an unintended side effect? To me it looks very irritating.

@WeatherGod
Copy link
Member

On Saturday, November 17, 2012, Nikolaus Rath wrote:

@WeatherGod https://github.com/WeatherGod: In your animation examples,
even lines that are not being changed (e.g. x and y axis) are constantly
wobbling around. Did you do that deliberately, or is that an unintended
side effect? To me it looks very irritating.

The filter is applied whenever the coordinates of the path is accessed. It
is a natural consequence of the implementation. I rather like it. It
reminds me of some cartoons I remember. We would have to figure out a way
to make the randomness be applied once (maybe on-demand?).

@Tillsten
Copy link
Contributor

Tillsten commented Mar 4, 2013

Any reason not to merge this?

@mdboom
Copy link
Member Author

mdboom commented Mar 5, 2013

The api for using this is horrible. It needs to have proper kwarg or rcparam access before its mergeable. I've sort of dropped the ball on this one.

@WeatherGod
Copy link
Member

Also note the bugs that I have pointed out earlier. I think this was a great and innovative proof-of-concept, but it is probably best to re-work this.

A thought I did have about a possible way to address @Nikratio 's concerns (and would enable proper unit-testing) is an ability to cache a seed number for a path. That way, the random seed would be re-used at each re-draw, resulting in the same sketch every time. This seed could be automatically determined and/or explicitly set.

@pelson
Copy link
Member

pelson commented Mar 6, 2013

FWIW, if my memory serves me, I have a pretty good implementation of this which uses transforms only (with just a couple of bug fixes and if I remember correctly, a single monkey patch). The path filter stuff sounds very useful in this case, but it also sounds like a lot of work and I wonder how widely used it would really be?

@certik
Copy link

certik commented Mar 6, 2013

I just wanted to say that it would be very nice to have this in matplotlib,
I think that a lot of people were waiting for this to get in. But I
understand of course that if the code is hackish, it's not worth the pain.

Ondrej

On Wed, Mar 6, 2013 at 12:34 PM, Phil Elson notifications@github.comwrote:

FWIW I have a pretty good implementation of this which uses transforms
only (with just a couple of bug fixes and if I remember correctly, a single
monkey patch). The path filter stuff sounds very useful in this case, but
it also sounds like a lot of work and I wonder how widely used it would
really be?


Reply to this email directly or view it on GitHubhttps://github.com//pull/1329#issuecomment-14494827
.

@pelson
Copy link
Member

pelson commented Mar 6, 2013

Thanks for the info Ondrej.

I think that a lot of people were waiting for this to get in.

Is it purely for XKCD style plots which show data in a less clinical, semantic form, or are there other usecases which make use of the path filter functionality?

@@ -1003,6 +1005,42 @@ def get_hatch_path(self, density=6.0):
return None
return Path.hatch(self._hatch, density)

def get_sketch_params(self):
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not an action: We should think about removing the duplicated docstings (duplicated in Artist) from here.

@pelson
Copy link
Member

pelson commented May 14, 2013

Mike: This is looking good. Please only add commits on top of c288df2 (rather than rebasing / modifying any history) and I would be happy to merge after some of the actions have been addressed/discussed.

I love the xkcd rendered version of the docs - very nicely done 😄

@mdboom
Copy link
Member Author

mdboom commented May 14, 2013

@pelson: Thanks for the feedback. I believe I have addressed all of your comments. The context manager took some work because the font.* rcParams are "dynamic". I've changed this so that they only affect to-be-created text objects and not ones already created (this brings them in line with most other rcParams). That's a pretty significant change, but it surprisingly doesn't break any tests. Still something to take some caution about.

Out of curiosity, why the rebase warning?

@pelson
Copy link
Member

pelson commented May 14, 2013

Out of curiosity, why the rebase warning?

I didn't want to re-read the whole lot again 😄.

This all looks good to me. Very nice work 😄

pelson added a commit that referenced this pull request May 14, 2013
Add a "sketch" path filter for XKCD style graphics.
@pelson pelson merged commit 6b442b8 into matplotlib:master May 14, 2013
@@ -58,7 +58,7 @@ class _path_module : public Py::ExtensionModule<_path_module>
add_varargs_method("convert_path_to_polygons", &_path_module::convert_path_to_polygons,
"convert_path_to_polygons(path, trans, width, height)");
add_varargs_method("cleanup_path", &_path_module::cleanup_path,
"cleanup_path(path, trans, remove_nans, clip, snap, simplify, curves)");
"cleanup_path(path, trans, remove_nans, clip, snap, simplify, curves, sketch_params)");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mdboom - I don't this this string is correct.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet