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

Skip to content

Commit f19ae31

Browse files
committed
FIX: change autolim exclude shapes not in data co-ordinates
1 parent 4728e70 commit f19ae31

File tree

13 files changed

+3845
-3771
lines changed

13 files changed

+3845
-3771
lines changed

doc/api/next_api_changes/2019-03-04-AL.rst

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,41 @@ without an explicit call to `Axes.autoscale_view`.
1313
In some cases, this can result in different limits being reported. If this is
1414
an issue, consider triggering a draw with `fig.canvas.draw`.
1515

16+
Autoscaling changes for Collections
17+
```````````````````````````````````
18+
19+
Autoscaling has also changed for artists that are based on the `.Collection`
20+
class. Previously, the method that calculates the automatic limits
21+
`.Collection.get_datalim` tried to take into account the size of objects
22+
in the collection and make the limits large enough to not clip any of the
23+
object, i.e., for `.Axes.scatter` it would make the limits large enough to not
24+
clip any markers in the scatter. This is problematic when the object size is
25+
specified in physical space, or figure-relative space, because the transform
26+
from physical units to data limits requires knowing the data limits, and
27+
becomes invalid when the new limits are applied. This is an inverse
28+
problem that is theoretically solvable (if the object is physically smaller
29+
than the axes), but the extra complexity was not deemed worth it, particularly
30+
as the most common use case is for markers in scatter that are usually small
31+
enough to be accommodated by the default data limit margins.
32+
33+
While the new behavior is algorithmically simpler, it is conditional on
34+
properties of the `.Collection` object:
35+
36+
1. ``offsets = None``, ``transform`` is a child of `.Axes.transData`: use the paths
37+
for the automatic limits (i.e. for `.LineCollection` in `Axes.streamplot`).
38+
2. ``offsets != None``, and ``offset_transform`` is child of `.Axes.transData`:
39+
40+
a) ``transform`` is child of `.Axes.transData`: use the ``path + offset`` for
41+
limits (i.e., for `.Axes.bar`).
42+
b) ``transform`` is not a child of `.Axes.transData`: just use the offsets
43+
for the limits (i.e. for scatter)
44+
45+
3. otherwise return a null `.Bbox`.
46+
47+
While this seems complicated, the logic is simply to use the information from
48+
the object that are in data space for the limits, but not information that is
49+
in physical units.
50+
1651
LogLocator.nonsingular now maintains the orders of its arguments
1752
````````````````````````````````````````````````````````````````
1853

lib/matplotlib/collections.py

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ def __init__(self,
146146
self._joinstyle = None
147147

148148
self._offsets = np.zeros((1, 2))
149+
# save if offsets passed in were none...
150+
self._offsetsNone = offsets is None
149151
self._uniform_offsets = None
150152
if offsets is not None:
151153
offsets = np.asanyarray(offsets, float)
@@ -179,9 +181,30 @@ def get_offset_transform(self):
179181
return t
180182

181183
def get_datalim(self, transData):
184+
185+
# Get the automatic datalim of the collection.
186+
#
187+
# This operation depends on the transforms for the data in the
188+
# collection and whether the collection has offsets.
189+
#
190+
# 1) offsets = None, transform child of transData: use the paths for
191+
# the automatic limits (i.e. for LineCollection in streamline).
192+
# 2) offsets != None: offset_transform is child of transData:
193+
# a) transform is child of transData: use the path + offset for
194+
# limits (i.e for bar).
195+
# b) transform is not a child of transData: just use the offsets
196+
# for the limits (i.e. for scatter)
197+
# 3) otherwise return a null Bbox.
198+
182199
transform = self.get_transform()
183200
transOffset = self.get_offset_transform()
201+
if (not self._offsetsNone and
202+
not transOffset.contains_branch(transData)):
203+
# if there are offsets but in some co-ords other than data,
204+
# then don't use them for autoscaling.
205+
return transforms.Bbox.null()
184206
offsets = self._offsets
207+
185208
paths = self.get_paths()
186209

187210
if not transform.is_affine:
@@ -196,13 +219,30 @@ def get_datalim(self, transData):
196219
# get_path_collection_extents handles nan but not masked arrays
197220

198221
if len(paths) and len(offsets):
199-
result = mpath.get_path_collection_extents(
200-
transform.frozen(), paths, self.get_transforms(),
201-
offsets, transOffset.frozen())
202-
result = result.inverse_transformed(transData)
203-
else:
204-
result = transforms.Bbox.null()
205-
return result
222+
if transform.contains_branch(transData):
223+
# collections that are just in data units (like quiver)
224+
# can properly have the axes limits set by their shape +
225+
# offset. LineCollections that have no offsets can
226+
# also use this algorithm (like streamplot).
227+
result = mpath.get_path_collection_extents(
228+
transform.frozen(), paths, self.get_transforms(),
229+
offsets, transOffset.frozen())
230+
return result.inverse_transformed(transData)
231+
if not self._offsetsNone:
232+
# this is for collections that have their paths (shapes)
233+
# in physical, axes-relative, or figure-relative units
234+
# (i.e. like scatter). We can't uniquely set limits based on
235+
# those shapes, so we just set the limits based on their
236+
# location.
237+
# Finish the transform:
238+
offsets = (transOffset +
239+
transData.inverted()).transform(offsets)
240+
offsets = np.ma.masked_invalid(offsets)
241+
if not offsets.mask.all():
242+
points = np.row_stack((offsets.min(axis=0),
243+
offsets.max(axis=0)))
244+
return transforms.Bbox(points)
245+
return transforms.Bbox.null()
206246

207247
def get_window_extent(self, renderer):
208248
# TODO: check to ensure that this does not fail for
@@ -1299,7 +1339,6 @@ def __init__(self, segments, # Can be None.
12991339
antialiaseds = (mpl.rcParams['lines.antialiased'],)
13001340

13011341
colors = mcolors.to_rgba_array(colors)
1302-
13031342
Collection.__init__(
13041343
self,
13051344
edgecolors=colors,
Binary file not shown.
Loading

0 commit comments

Comments
 (0)