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

Skip to content

Commit 9d20dd8

Browse files
authored
Merge pull request #20717 from fourpoints/fix-collection-offsets
Fix collection offsets
2 parents e9b9f59 + adeb59c commit 9d20dd8

File tree

3 files changed

+63
-43
lines changed

3 files changed

+63
-43
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Setting collection offset transform after initialization
2+
--------------------------------------------------------
3+
`.collections.Collection.set_offset_transform()` was added.
4+
5+
Previously the offset transform could not be set after initialization. This can be helpful when creating a `.collections.Collection` outside an axes object and later adding it with `.Axes.add_collection()` and settings the offset transform to `.Axes.transData`.

lib/matplotlib/collections.py

Lines changed: 27 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ class Collection(artist.Artist, cm.ScalarMappable):
6262
ignoring those that were manually passed in.
6363
"""
6464
_offsets = np.zeros((0, 2))
65-
_transOffset = transforms.IdentityTransform()
6665
#: Either a list of 3x3 arrays or an Nx3x3 array (representing N
6766
#: transforms), suitable for the `all_transforms` argument to
6867
#: `~matplotlib.backend_bases.RendererBase.draw_path_collection`;
@@ -194,20 +193,17 @@ def __init__(self,
194193
else:
195194
self._joinstyle = None
196195

196+
# default to zeros
197197
self._offsets = np.zeros((1, 2))
198-
# save if offsets passed in were none...
199-
self._offsetsNone = offsets is None
200-
self._uniform_offsets = None
198+
201199
if offsets is not None:
202200
offsets = np.asanyarray(offsets, float)
203201
# Broadcast (2,) -> (1, 2) but nothing else.
204202
if offsets.shape == (2,):
205203
offsets = offsets[None, :]
206-
if transOffset is not None:
207-
self._offsets = offsets
208-
self._transOffset = transOffset
209-
else:
210-
self._uniform_offsets = offsets
204+
self._offsets = offsets
205+
206+
self._transOffset = transOffset
211207

212208
self._path_effects = None
213209
self.update(kwargs)
@@ -223,11 +219,23 @@ def get_transforms(self):
223219
return self._transforms
224220

225221
def get_offset_transform(self):
226-
t = self._transOffset
227-
if (not isinstance(t, transforms.Transform)
228-
and hasattr(t, '_as_mpl_transform')):
229-
t = t._as_mpl_transform(self.axes)
230-
return t
222+
"""Return the `.Transform` instance used by this artist offset."""
223+
if self._transOffset is None:
224+
self._transOffset = transforms.IdentityTransform()
225+
elif (not isinstance(self._transOffset, transforms.Transform)
226+
and hasattr(self._transOffset, '_as_mpl_transform')):
227+
self._transOffset = self._transOffset._as_mpl_transform(self.axes)
228+
return self._transOffset
229+
230+
def set_offset_transform(self, transOffset):
231+
"""
232+
Set the artist offset transform.
233+
234+
Parameters
235+
----------
236+
transOffset : `.Transform`
237+
"""
238+
self._transOffset = transOffset
231239

232240
def get_datalim(self, transData):
233241
# Calculate the data limits and return them as a `.Bbox`.
@@ -248,8 +256,8 @@ def get_datalim(self, transData):
248256

249257
transform = self.get_transform()
250258
transOffset = self.get_offset_transform()
251-
if (not self._offsetsNone and
252-
not transOffset.contains_branch(transData)):
259+
hasOffsets = np.any(self._offsets) # True if any non-zero offsets
260+
if hasOffsets and not transOffset.contains_branch(transData):
253261
# if there are offsets but in some coords other than data,
254262
# then don't use them for autoscaling.
255263
return transforms.Bbox.null()
@@ -279,7 +287,7 @@ def get_datalim(self, transData):
279287
self.get_transforms(),
280288
transOffset.transform_non_affine(offsets),
281289
transOffset.get_affine().frozen())
282-
if not self._offsetsNone:
290+
if hasOffsets:
283291
# this is for collections that have their paths (shapes)
284292
# in physical, axes-relative, or figure-relative units
285293
# (i.e. like scatter). We can't uniquely set limits based on
@@ -542,20 +550,12 @@ def set_offsets(self, offsets):
542550
offsets = np.asanyarray(offsets, float)
543551
if offsets.shape == (2,): # Broadcast (2,) -> (1, 2) but nothing else.
544552
offsets = offsets[None, :]
545-
# This decision is based on how they are initialized above in __init__.
546-
if self._uniform_offsets is None:
547-
self._offsets = offsets
548-
else:
549-
self._uniform_offsets = offsets
553+
self._offsets = offsets
550554
self.stale = True
551555

552556
def get_offsets(self):
553557
"""Return the offsets for the collection."""
554-
# This decision is based on how they are initialized above in __init__.
555-
if self._uniform_offsets is None:
556-
return self._offsets
557-
else:
558-
return self._uniform_offsets
558+
return self._offsets
559559

560560
def _get_default_linewidth(self):
561561
# This may be overridden in a subclass.
@@ -1441,9 +1441,6 @@ def set_segments(self, segments):
14411441
seg = np.asarray(seg, float)
14421442
_segments.append(seg)
14431443

1444-
if self._uniform_offsets is not None:
1445-
_segments = self._add_offsets(_segments)
1446-
14471444
self._paths = [mpath.Path(_seg) for _seg in _segments]
14481445
self.stale = True
14491446

@@ -1474,19 +1471,6 @@ def get_segments(self):
14741471

14751472
return segments
14761473

1477-
def _add_offsets(self, segs):
1478-
offsets = self._uniform_offsets
1479-
Nsegs = len(segs)
1480-
Noffs = offsets.shape[0]
1481-
if Noffs == 1:
1482-
for i in range(Nsegs):
1483-
segs[i] = segs[i] + i * offsets
1484-
else:
1485-
for i in range(Nsegs):
1486-
io = i % Noffs
1487-
segs[i] = segs[i] + offsets[io:io + 1]
1488-
return segs
1489-
14901474
def _get_default_linewidth(self):
14911475
return mpl.rcParams['lines.linewidth']
14921476

lib/matplotlib/tests/test_collections.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,3 +1048,34 @@ def test_get_segments():
10481048
readback, = lc.get_segments()
10491049
# these should comeback un-changed!
10501050
assert np.all(segments == readback)
1051+
1052+
1053+
def test_set_offsets_late():
1054+
identity = mtransforms.IdentityTransform()
1055+
sizes = [2]
1056+
1057+
null = mcollections.CircleCollection(sizes=sizes)
1058+
1059+
init = mcollections.CircleCollection(sizes=sizes, offsets=(10, 10))
1060+
1061+
late = mcollections.CircleCollection(sizes=sizes)
1062+
late.set_offsets((10, 10))
1063+
1064+
# Bbox.__eq__ doesn't compare bounds
1065+
null_bounds = null.get_datalim(identity).bounds
1066+
init_bounds = init.get_datalim(identity).bounds
1067+
late_bounds = late.get_datalim(identity).bounds
1068+
1069+
# offsets and transform are applied when set after initialization
1070+
assert null_bounds != init_bounds
1071+
assert init_bounds == late_bounds
1072+
1073+
1074+
def test_set_offset_transform():
1075+
skew = mtransforms.Affine2D().skew(2, 2)
1076+
init = mcollections.Collection([], transOffset=skew)
1077+
1078+
late = mcollections.Collection([])
1079+
late.set_offset_transform(skew)
1080+
1081+
assert skew == init.get_offset_transform() == late.get_offset_transform()

0 commit comments

Comments
 (0)