Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Plot limit with transform #731

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

Merged
merged 11 commits into from
Aug 21, 2012
Merged
48 changes: 48 additions & 0 deletions doc/api/api_changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,54 @@ Changes in 1.2.x
original keyword arguments will override any value provided by
*capthick*.

* Transform subclassing behaviour is now subtly changed. If your transform
implements a non-affine transformation, then it should override the
``transform_non_affine`` method, rather than the generic ``transform`` method.
Previously transforms would define ``transform`` and then copy the
method into ``transform_non_affine``:

class MyTransform(mtrans.Transform):
def transform(self, xy):
...
transform_non_affine = transform

This approach will no longer function correctly and should be changed to:

class MyTransform(mtrans.Transform):
def transform_non_affine(self, xy):
...

* Artists no longer have ``x_isdata`` or ``y_isdata`` attributes; instead
any artist's transform can be interrogated with
``artist_instance.get_transform().contains_branch(ax.transData)``

* Lines added to an axes now take into account their transform when updating the
data and view limits. This means transforms can now be used as a pre-transform.
For instance:

>>> import matplotlib.pyplot as plt
>>> import matplotlib.transforms as mtrans
>>> ax = plt.axes()
>>> ax.plot(range(10), transform=mtrans.Affine2D().scale(10) + ax.transData)
>>> print(ax.viewLim)
Bbox('array([[ 0., 0.],\n [ 90., 90.]])')

* One can now easily get a transform which goes from one transform's coordinate system
to another, in an optimized way, using the new subtract method on a transform. For instance,
to go from data coordinates to axes coordinates::

>>> import matplotlib.pyplot as plt
>>> ax = plt.axes()
>>> data2ax = ax.transData - ax.transAxes
>>> print(ax.transData.depth, ax.transAxes.depth)
3, 1
>>> print(data2ax.depth)
2

for versions before 1.2 this could only be achieved in a sub-optimal way, using
``ax.transData + ax.transAxes.inverted()`` (depth is a new concept, but had it existed
it would return 4 for this example).

Changes in 1.1.x
================

Expand Down
2 changes: 0 additions & 2 deletions lib/matplotlib/artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ def __init__(self):
self._remove_method = None
self._url = None
self._gid = None
self.x_isdata = True # False to avoid updating Axes.dataLim with x
self.y_isdata = True # with y
self._snap = None

def remove(self):
Expand Down
69 changes: 51 additions & 18 deletions lib/matplotlib/axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1461,17 +1461,52 @@ def add_line(self, line):

self._update_line_limits(line)
if not line.get_label():
line.set_label('_line%d'%len(self.lines))
line.set_label('_line%d' % len(self.lines))
self.lines.append(line)
line._remove_method = lambda h: self.lines.remove(h)
return line

def _update_line_limits(self, line):
p = line.get_path()
if p.vertices.size > 0:
self.dataLim.update_from_path(p, self.ignore_existing_data_limits,
updatex=line.x_isdata,
updatey=line.y_isdata)
"""Figures out the data limit of the given line, updating self.dataLim."""
path = line.get_path()
if path.vertices.size == 0:
return

line_trans = line.get_transform()

if line_trans == self.transData:
data_path = path

elif any(line_trans.contains_branch_seperately(self.transData)):
# identify the transform to go from line's coordinates
# to data coordinates
trans_to_data = line_trans - self.transData

# if transData is affine we can use the cached non-affine component
# of line's path. (since the non-affine part of line_trans is
# entirely encapsulated in trans_to_data).
if self.transData.is_affine:
line_trans_path = line._get_transformed_path()
na_path, _ = line_trans_path.get_transformed_path_and_affine()
data_path = trans_to_data.transform_path_affine(na_path)
else:
data_path = trans_to_data.transform_path(path)
else:
# for backwards compatibility we update the dataLim with the
# coordinate range of the given path, even though the coordinate
# systems are completely different. This may occur in situations
# such as when ax.transAxes is passed through for absolute
# positioning.
data_path = path

if data_path.vertices.size > 0:
updatex, updatey = line_trans.contains_branch_seperately(
self.transData
)
self.dataLim.update_from_path(data_path,
self.ignore_existing_data_limits,
updatex=updatex,
updatey=updatey)
self.ignore_existing_data_limits = False

def add_patch(self, p):
Expand Down Expand Up @@ -1507,11 +1542,14 @@ def _update_patch_limits(self, patch):
if vertices.size > 0:
xys = patch.get_patch_transform().transform(vertices)
if patch.get_data_transform() != self.transData:
transform = (patch.get_data_transform() +
self.transData.inverted())
xys = transform.transform(xys)
self.update_datalim(xys, updatex=patch.x_isdata,
updatey=patch.y_isdata)
patch_to_data = (patch.get_data_transform() -
self.transData)
xys = patch_to_data.transform(xys)

updatex, updatey = patch.get_transform().\
contains_branch_seperately(self.transData)
self.update_datalim(xys, updatex=updatex,
updatey=updatey)


def add_table(self, tab):
Expand Down Expand Up @@ -1599,13 +1637,13 @@ def _process_unit_info(self, xdata=None, ydata=None, kwargs=None):
if xdata is not None:
# we only need to update if there is nothing set yet.
if not self.xaxis.have_units():
self.xaxis.update_units(xdata)
self.xaxis.update_units(xdata)
#print '\tset from xdata', self.xaxis.units

if ydata is not None:
# we only need to update if there is nothing set yet.
if not self.yaxis.have_units():
self.yaxis.update_units(ydata)
self.yaxis.update_units(ydata)
#print '\tset from ydata', self.yaxis.units

# process kwargs 2nd since these will override default units
Expand Down Expand Up @@ -3424,7 +3462,6 @@ def axhline(self, y=0, xmin=0, xmax=1, **kwargs):
trans = mtransforms.blended_transform_factory(
self.transAxes, self.transData)
l = mlines.Line2D([xmin,xmax], [y,y], transform=trans, **kwargs)
l.x_isdata = False
self.add_line(l)
self.autoscale_view(scalex=False, scaley=scaley)
return l
Expand Down Expand Up @@ -3489,7 +3526,6 @@ def axvline(self, x=0, ymin=0, ymax=1, **kwargs):
trans = mtransforms.blended_transform_factory(
self.transData, self.transAxes)
l = mlines.Line2D([x,x], [ymin,ymax] , transform=trans, **kwargs)
l.y_isdata = False
self.add_line(l)
self.autoscale_view(scalex=scalex, scaley=False)
return l
Expand Down Expand Up @@ -3546,7 +3582,6 @@ def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs):
verts = (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)
p = mpatches.Polygon(verts, **kwargs)
p.set_transform(trans)
p.x_isdata = False
self.add_patch(p)
self.autoscale_view(scalex=False)
return p
Expand Down Expand Up @@ -3603,7 +3638,6 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs):
verts = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)]
p = mpatches.Polygon(verts, **kwargs)
p.set_transform(trans)
p.y_isdata = False
self.add_patch(p)
self.autoscale_view(scaley=False)
return p
Expand Down Expand Up @@ -3909,7 +3943,6 @@ def plot(self, *args, **kwargs):
self.add_line(line)
lines.append(line)


self.autoscale_view(scalex=scalex, scaley=scaley)
return lines

Expand Down
31 changes: 22 additions & 9 deletions lib/matplotlib/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
# TODO: expose cap and join style attrs
from __future__ import division, print_function

import warnings

import numpy as np
from numpy import ma
from matplotlib import verbose
Expand Down Expand Up @@ -249,17 +251,15 @@ def contains(self, mouseevent):
if len(self._xy)==0: return False,{}

# Convert points to pixels
if self._transformed_path is None:
self._transform_path()
path, affine = self._transformed_path.get_transformed_path_and_affine()
path, affine = self._get_transformed_path().get_transformed_path_and_affine()
path = affine.transform_path(path)
xy = path.vertices
xt = xy[:, 0]
yt = xy[:, 1]

# Convert pick radius from points to pixels
if self.figure == None:
warning.warn('no figure set when check if mouse is on line')
if self.figure is None:
warnings.warn('no figure set when check if mouse is on line')
pixels = self.pickradius
else:
pixels = self.figure.dpi/72. * self.pickradius
Expand Down Expand Up @@ -446,13 +446,26 @@ def recache(self, always=False):
self._invalidy = False

def _transform_path(self, subslice=None):
"""
Puts a TransformedPath instance at self._transformed_path,
all invalidation of the transform is then handled by the
TransformedPath instance.
"""
# Masked arrays are now handled by the Path class itself
if subslice is not None:
_path = Path(self._xy[subslice,:])
else:
_path = self._path
self._transformed_path = TransformedPath(_path, self.get_transform())

def _get_transformed_path(self):
"""
Return the :class:`~matplotlib.transforms.TransformedPath` instance
of this line.
"""
if self._transformed_path is None:
self._transform_path()
return self._transformed_path

def set_transform(self, t):
"""
Expand Down Expand Up @@ -482,8 +495,8 @@ def draw(self, renderer):
subslice = slice(max(i0-1, 0), i1+1)
self.ind_offset = subslice.start
self._transform_path(subslice)
if self._transformed_path is None:
self._transform_path()

transformed_path = self._get_transformed_path()

if not self.get_visible(): return

Expand All @@ -507,7 +520,7 @@ def draw(self, renderer):

funcname = self._lineStyles.get(self._linestyle, '_draw_nothing')
if funcname != '_draw_nothing':
tpath, affine = self._transformed_path.get_transformed_path_and_affine()
tpath, affine = transformed_path.get_transformed_path_and_affine()
if len(tpath.vertices):
self._lineFunc = getattr(self, funcname)
funcname = self.drawStyles.get(self._drawstyle, '_draw_lines')
Expand All @@ -528,7 +541,7 @@ def draw(self, renderer):
gc.set_linewidth(self._markeredgewidth)
gc.set_alpha(self._alpha)
marker = self._marker
tpath, affine = self._transformed_path.get_transformed_points_and_affine()
tpath, affine = transformed_path.get_transformed_points_and_affine()
if len(tpath.vertices):
# subsample the markers if markevery is not None
markevery = self.get_markevery()
Expand Down
12 changes: 12 additions & 0 deletions lib/matplotlib/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,21 @@ def get_transform(self):
return self.get_patch_transform() + artist.Artist.get_transform(self)

def get_data_transform(self):
"""
Return the :class:`~matplotlib.transforms.Transform` instance which
maps data coordinates to physical coordinates.
"""
return artist.Artist.get_transform(self)

def get_patch_transform(self):
"""
Return the :class:`~matplotlib.transforms.Transform` instance which
takes patch coordinates to data coordinates.

For example, one may define a patch of a circle which represents a
radius of 5 by providing coordinates for a unit circle, and a
transform which scales the coordinates (the patch coordinate) by 5.
"""
return transforms.IdentityTransform()

def get_antialiased(self):
Expand Down
Loading