-
-
Notifications
You must be signed in to change notification settings - Fork 7.6k
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
Changes from 8 commits
5c26a7e
99e2b5e
05bec2c
b7b3272
f157c97
e1e0848
65544a1
c288df2
edb1526
cc61700
9652004
a274a41
4a61f50
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,18 @@ revision, see the :ref:`github-stats`. | |
|
||
new in matplotlib-1.3 | ||
===================== | ||
`xkcd`-style sketch plotting | ||
---------------------------- | ||
|
||
To gives your plots a sense of authority that they may be missing, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "gives" typo |
||
Michael Droettboom (inspired by the work of many others in `issue | ||
#1329 <https://github.com/matplotlib/matplotlib/pull/1329>`_) has | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have a role to make the linking easier (ghissue I think) - see |
||
added an `xkcd-style <xkcd.com>`_ sketch plotting mode. To use it, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This link is local - so results in a 404. |
||
simply call `pyplot.xkcd` before creating your plot. | ||
|
||
.. plot:: mpl_examples/showcase/xkcd.py | ||
|
||
|
||
``axes.xmargin`` and ``axes.ymargin`` added to rcParams | ||
------------------------------------------------------- | ||
``rcParam`` values (``axes.xmargin`` and ``axes.ymargin``) were added | ||
|
@@ -142,8 +154,8 @@ the bottom of the text bounding box. | |
|
||
``savefig.jpeg_quality`` added to rcParams | ||
------------------------------------------------------------------------------ | ||
``rcParam`` value ``savefig.jpeg_quality`` was added so that the user can | ||
configure the default quality used when a figure is written as a JPEG. The | ||
``rcParam`` value ``savefig.jpeg_quality`` was added so that the user can | ||
configure the default quality used when a figure is written as a JPEG. The | ||
default quality is 95; previously, the default quality was 75. This change | ||
minimizes the artifacting inherent in JPEG images, particularly with images | ||
that have sharp changes in color as plots often do. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,10 +17,23 @@ | |
foreground="w"), | ||
PathEffects.Normal()]) | ||
|
||
ax1.grid(True, linestyle="-") | ||
|
||
pe = [PathEffects.withStroke(linewidth=3, | ||
foreground="w")] | ||
for l in ax1.get_xgridlines() + ax1.get_ygridlines(): | ||
l.set_path_effects(pe) | ||
|
||
ax2 = plt.subplot(132) | ||
arr = np.arange(25).reshape((5,5)) | ||
ax2.imshow(arr) | ||
cntr = ax2.contour(arr, colors="k") | ||
|
||
plt.setp(cntr.collections, | ||
path_effects=[PathEffects.withStroke(linewidth=3, | ||
foreground="w")]) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Picky: too many newlines |
||
|
||
clbls = ax2.clabel(cntr, fmt="%2.0f", use_clabeltext=True) | ||
plt.setp(clbls, | ||
path_effects=[PathEffects.withStroke(linewidth=3, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,5 +30,4 @@ | |
X.shape = h, w, 3 | ||
|
||
im = Image.fromstring( "RGB", (w,h), s) | ||
im.show() | ||
|
||
# im.show() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Intentional? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes -- though I should probably remove the line altogether -- this causes ImageMagick's "display" utility to be called during the documentation build. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh! That annoys me everytime and I've never got to the bottom of it! 😄 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from matplotlib import pyplot as plt | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It'd be nice to reference the XKCD chart that is being reproduced. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Picky: its fewer characters (and more consistent?) to type |
||
import numpy as np | ||
|
||
plt.xkcd() | ||
|
||
fig = plt.figure() | ||
ax = fig.add_subplot(1, 1, 1) | ||
ax.spines['right'].set_color('none') | ||
ax.spines['top'].set_color('none') | ||
plt.xticks([]) | ||
plt.yticks([]) | ||
ax.set_ylim([-30, 10]) | ||
|
||
data = np.ones(100) | ||
data[70:] -= np.arange(30) | ||
|
||
plt.annotate( | ||
'THE DAY I REALIZED\nI COULD COOK BACON\nWHENEVER I WANTED', | ||
xy=(70, 1), arrowprops=dict(arrowstyle='->'), xytext=(15, -10)) | ||
|
||
plt.plot(data) | ||
|
||
plt.xlabel('time') | ||
plt.ylabel('my overall health') | ||
|
||
fig = plt.figure() | ||
ax = fig.add_subplot(1, 1, 1) | ||
ax.bar([-0.125, 1.0-0.125], [0, 100], 0.25) | ||
ax.spines['right'].set_color('none') | ||
ax.spines['top'].set_color('none') | ||
ax.xaxis.set_ticks_position('bottom') | ||
ax.set_xticks([0, 1]) | ||
ax.set_xlim([-0.5, 1.5]) | ||
ax.set_ylim([0, 110]) | ||
ax.set_xticklabels(['CONFIRMED BY\nEXPERIMENT', 'REFUTED BY\nEXPERIMENT']) | ||
plt.yticks([]) | ||
|
||
plt.title("CLAIMS OF SUPERNATURAL POWERS") | ||
|
||
plt.show() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,6 +101,8 @@ def __init__(self): | |
self._url = None | ||
self._gid = None | ||
self._snap = None | ||
self._sketch = rcParams['path.sketch'] | ||
self._path_effects = rcParams['path.effects'] | ||
|
||
def __getstate__(self): | ||
d = self.__dict__.copy() | ||
|
@@ -456,6 +458,52 @@ def set_snap(self, snap): | |
""" | ||
self._snap = snap | ||
|
||
def get_sketch_params(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be keen to use numpydoc form here (https://github.com/matplotlib/matplotlib/wiki/Mep10) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, sure -- I didn't notice that. This PR predates MEP10, so I forgot to update it. |
||
""" | ||
Returns the sketch parameters, which is a tuple with three 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. | ||
|
||
May return `None` if no sketch parameters were set. | ||
""" | ||
return self._sketch | ||
|
||
def set_sketch_params(self, scale=None, length=None, randomness=None): | ||
""" | ||
Sets the the sketch parameters: | ||
|
||
* *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) | ||
|
||
If *scale* is None, no wiggling will be set. | ||
""" | ||
if scale is None: | ||
self._sketch = None | ||
else: | ||
self._sketch = (scale, length or 128.0, randomness or 16.0) | ||
|
||
def set_path_effects(self, path_effects): | ||
""" | ||
set path_effects, which should be a list of instances of | ||
matplotlib.patheffect._Base class or its derivatives. | ||
""" | ||
self._path_effects = path_effects | ||
|
||
def get_path_effects(self): | ||
return self._path_effects | ||
|
||
def get_figure(self): | ||
""" | ||
Return the :class:`~matplotlib.figure.Figure` instance the | ||
|
@@ -672,7 +720,7 @@ def update(self, props): | |
store = self.eventson | ||
self.eventson = False | ||
changed = False | ||
|
||
for k, v in props.iteritems(): | ||
func = getattr(self, 'set_' + k, None) | ||
if func is None or not callable(func): | ||
|
@@ -728,6 +776,8 @@ def update_from(self, other): | |
self._clippath = other._clippath | ||
self._lod = other._lod | ||
self._label = other._label | ||
self._sketch = other._sketch | ||
self._path_effects = other._path_effects | ||
self.pchanged() | ||
|
||
def properties(self): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -701,6 +701,7 @@ def __init__(self): | |
self._url = None | ||
self._gid = None | ||
self._snap = None | ||
self._sketch = None | ||
|
||
def copy_properties(self, gc): | ||
'Copy properties from gc to self' | ||
|
@@ -720,6 +721,7 @@ def copy_properties(self, gc): | |
self._url = gc._url | ||
self._gid = gc._gid | ||
self._snap = gc._snap | ||
self._sketch = gc._sketch | ||
|
||
def restore(self): | ||
""" | ||
|
@@ -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): | ||
""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
Returns the sketch parameters, which is a tuple with three 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. | ||
|
||
May return `None` if no sketch parameters were set. | ||
""" | ||
return self._sketch | ||
|
||
def set_sketch_params(self, scale=None, length=None, randomness=None): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically I don't think these need to be keywords as they are always given (in the draw method). But I agree that keeping the signature consistent is useful. |
||
""" | ||
Sets the the sketch parameters: | ||
|
||
* *scale*: The amplitude of the wiggle perpendicular to the | ||
source line. | ||
|
||
* *length*: The length of the wiggle along the line, in | ||
pixels. | ||
|
||
* *randomness*: The scale factor by which the length is | ||
shrunken or expanded, in pixels. | ||
|
||
If *scale* is None, no wiggling will be set. | ||
""" | ||
if scale is None: | ||
self._sketch = None | ||
else: | ||
self._sketch = (scale, length or 128.0, randomness or 16.0) | ||
|
||
|
||
class TimerBase(object): | ||
''' | ||
|
@@ -1937,7 +1975,7 @@ def print_jpg(self, filename_or_obj, *args, **kwargs): | |
|
||
*quality*: The image quality, on a scale from 1 (worst) to | ||
95 (best). The default is 95, if not given in the | ||
matplotlibrc file in the savefig.jpeg_quality parameter. | ||
matplotlibrc file in the savefig.jpeg_quality parameter. | ||
Values above 95 should be avoided; 100 completely | ||
disables the JPEG quantization stage. | ||
|
||
|
@@ -1957,7 +1995,7 @@ def print_jpg(self, filename_or_obj, *args, **kwargs): | |
options = cbook.restrict_dict(kwargs, ['quality', 'optimize', | ||
'progressive']) | ||
|
||
if 'quality' not in options: | ||
if 'quality' not in options: | ||
options['quality'] = rcParams['savefig.jpeg_quality'] | ||
|
||
return image.save(filename_or_obj, format='jpeg', **options) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -91,6 +91,7 @@ def __init__(self, | |
urls=None, | ||
offset_position='screen', | ||
zorder=1, | ||
path_effects=None, | ||
**kwargs | ||
): | ||
""" | ||
|
@@ -123,6 +124,7 @@ def __init__(self, | |
else: | ||
self._uniform_offsets = offsets | ||
|
||
self._path_effects = None | ||
self.update(kwargs) | ||
self._paths = None | ||
|
||
|
@@ -258,11 +260,24 @@ def draw(self, renderer): | |
if self._hatch: | ||
gc.set_hatch(self._hatch) | ||
|
||
renderer.draw_path_collection( | ||
gc, transform.frozen(), paths, self.get_transforms(), | ||
offsets, transOffset, self.get_facecolor(), self.get_edgecolor(), | ||
self._linewidths, self._linestyles, self._antialiaseds, self._urls, | ||
self._offset_position) | ||
if self.get_sketch_params() is not None: | ||
gc.set_sketch_params(*self.get_sketch_params()) | ||
|
||
if self.get_path_effects(): | ||
#from patheffects import PathEffectsRenderer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rogue comment. |
||
for pe in self.get_path_effects(): | ||
pe.draw_path_collection(renderer, | ||
gc, transform.frozen(), paths, self.get_transforms(), | ||
offsets, transOffset, self.get_facecolor(), self.get_edgecolor(), | ||
self._linewidths, self._linestyles, self._antialiaseds, self._urls, | ||
self._offset_position) | ||
else: | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unnecessary newline. |
||
renderer.draw_path_collection( | ||
gc, transform.frozen(), paths, self.get_transforms(), | ||
offsets, transOffset, self.get_facecolor(), self.get_edgecolor(), | ||
self._linewidths, self._linestyles, self._antialiaseds, self._urls, | ||
self._offset_position) | ||
|
||
gc.restore() | ||
renderer.close_group(self.__class__.__name__) | ||
|
@@ -642,6 +657,7 @@ def update_from(self, other): | |
self.cmap = other.cmap | ||
# self.update_dict = other.update_dict # do we need to copy this? -JJL | ||
|
||
|
||
# these are not available for the object inspector until after the | ||
# class is built so we define an initial set here for the init | ||
# function and they will be overridden after object defn | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Path effects on lines deserves to go in here too.