Description
Describe the issue
Summary
Make the behaviour for setting offsets in collections.Collection
and subclasses more consistent and predictable.
Update 1: 2021-07-21 09:34: Added second fix proposal
Update 2: 2021-07-21 15:55: Added bug with lc.get_paths()
This is my second issue here, so if I'm proposing too much in a single issue, just close this and I'll make several smaller ones. If this looks okay, I'll make a PR with the proposed changes.
- Setting
offsets
during initialization has a slightly different behavior from callingcoll.set_offsets()
after initialization.- If set during intialization, but without the
transOffset
parameter, thencoll._uniform_offsets
are set, otherwise this isNone
. Callingcoll.set_offsets()
whencoll._uniform_offsets
isNone
will updatecoll._offsets
instead. This affects the results ofcoll.get_datalim()
. coll._offsetsNone
is set during initialization, but never updated if.set_offsets
is used. (I could only find this used incoll.get_datalim()
, but I couldn't produce any side effects.)
- If set during intialization, but without the
transOffset
can only be set ifoffset
is included during initialization, and is otherwise ignored without warning.- There is a
coll.get_offset_transform()
, but nocoll.set_offset_transform()
.
Unrelated to collections:
- One of the functions called in
coll.get_datalim()
has a typo:_BlendedMixin.contains_branch_seperately
andTransform.contains_branch_seperately
are misspelled. (It should be separately.) (Separate issue?)
Different behaviour for setting offsets during and after initialization:
import matplotlib.patches as mpatches
import matplotlib.collections as mcollections
import matplotlib.transforms as mtransforms
patch = mpatches.Circle((0, 0), radius=5)
good = mcollections.PatchCollection([patch], offsets=(10, 10))
bad = mcollections.PatchCollection([patch])
bad.set_offsets((10, 10))
ugly = mcollections.PatchCollection([patch])
ugly._uniform_offsets = (10, 10)
identity = mtransforms.IdentityTransform()
print("good", good.get_datalim(identity))
print("bad", bad.get_datalim(identity))
print("ugly", ugly.get_datalim(identity))
output:
>>> good: Bbox(x0=-5.0, y0=-5.0, x1=5.0, y1=5.0)
>>> bad: Bbox(x0=5.0, y0=5.0, x1=15.0, y1=15.0)
>>> ugly: Bbox(x0=-5.0, y0=-5.0, x1=5.0, y1=5.0)
Proposed fix
- Update the behavior of
coll.set_offsets()
:- set
coll._offsetsNone
toFalse
when called. - set
coll._uniform_offsets
instead ofcoll._offsets
ifcoll._transOffset
isNone
.
- set
- Either add a warning, or set
transOffset
during initialization if passed withoutoffsets
. - Add
coll.set_offset_transform()
:- If
coll._uniform_offsets
is notNone
, thencoll._offsets
must be set to this value, andcoll._uniform_offsets
must be set toNone
.
- If
I don't know what to do with the misspelled function names. Keep them, but make an alias?
Update 1: After a bit of digging, it appears that self._uniform_offsets
are a remnant of LineCollection
that was left behind after merging with Collection
. (It first appeared here). So a second proposal would be to merge coll._offsets
and coll._uniform_offsets
. This would simplify coll.set_offsets
and coll.get_offsets
. The only other usage of coll._uniform_offsets
in collections
is in LineCollection._add_offsets
.
I admit that I'm not certain what the use case for this is, but here is a possible issue with _uniform_offsets
. lc.get_paths()
(and lc.get_segments()
). It will show different output depending on whether offsets are set during initialization or after. I'm not sure what the intended behaviour is, because lc._add_offsets
is unclear to me.
import matplotlib.collections as mcollections
coll1 = mcollections.LineCollection([[[0, 0], [1, 1], [2, 2]], [[0, 0], [1, 2]]])
coll1.set_offsets([(1, 1)])
coll2 = mcollections.LineCollection([[[0, 0], [1, 1], [2, 2]], [[0, 0], [1, 2]]], offsets=[(1, 1)])
print(coll1.get_paths()[1])
print(coll2.get_paths()[1])
output
Path(array([[0., 0.],
[1., 2.]]), None)
Path(array([[1., 1.],
[2., 3.]]), None)