Skip to content

Commit 9ebea41

Browse files
committed
Deprecate offset_position="data".
The `offset_position` property of collections is currently set to "data" only for hexbin(), but one can do away with it by just setting the correct offsetTrans instead -- which this PR does. The advantage of doing so is that the correct offsetTrans only needs to be implemented once, whereas support for `offset_position` needs to be implemented for each renderer class separately (including at the C-level for the Agg backend). Note that the *offset_position* argument to `draw_path_collection` is not pending removal because its removal would require coordination with third-party backends; the plan is to just always set it to "screen" after the deprecation period. If there is demand for it, _VectorTransform could be made a public API.
1 parent 555e1c3 commit 9ebea41

File tree

7 files changed

+84
-30
lines changed

7 files changed

+84
-30
lines changed

doc/api/next_api_changes/deprecations.rst

+9
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,12 @@ The *clear_temp* parameter and attribute of `.FileMovieWriter` is
211211
deprecated. In the future, files placed in a temporary directory (using
212212
``frame_prefix=None``, the default) will be cleared; files placed elsewhere
213213
will not.
214+
215+
``offset_position`` property of `.Collection`
216+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
217+
The ``offset_position`` property of `.Collection` is deprecated. In the future,
218+
`.Collection`\s will always behave as if ``offset_position`` is set to "screen"
219+
(the default).
220+
221+
Support for passing ``offset_position="data"`` to the ``draw_path_collection``
222+
of all renderer classes is deprecated.

lib/matplotlib/axes/_axes.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -4759,8 +4759,7 @@ def reduce_C_function(C: array) -> float
47594759
edgecolors=edgecolors,
47604760
linewidths=linewidths,
47614761
offsets=offsets,
4762-
transOffset=mtransforms.IdentityTransform(),
4763-
offset_position="data"
4762+
transOffset=mtransforms._VectorTransform(self.transData),
47644763
)
47654764

47664765
# Set normalizer if bins is 'log'

lib/matplotlib/backend_bases.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,10 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
202202
*linestyles* and *antialiaseds*. *offsets* is a list of
203203
offsets to apply to each of the paths. The offsets in
204204
*offsets* are first transformed by *offsetTrans* before being
205-
applied. *offset_position* may be either "screen" or "data"
206-
depending on the space that the offsets are in.
205+
applied.
206+
207+
*offset_position* may be either "screen" or "data" depending on the
208+
space that the offsets are in; "data" is deprecated.
207209
208210
This provides a fallback implementation of
209211
:meth:`draw_path_collection` that makes multiple calls to
@@ -381,6 +383,11 @@ def _iter_collection(self, gc, master_transform, all_transforms,
381383
Naa = len(antialiaseds)
382384
Nurls = len(urls)
383385

386+
if offset_position == "data":
387+
cbook.warn_deprecated(
388+
"3.3", message="Support for offset_position='data' is "
389+
"deprecated since %(since)s and will be removed %(removal)s.")
390+
384391
if (Nfacecolors == 0 and Nedgecolors == 0) or Npaths == 0:
385392
return
386393
if Noffsets:

lib/matplotlib/backends/backend_agg.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ def _update_methods(self):
108108
self.draw_gouraud_triangles = self._renderer.draw_gouraud_triangles
109109
self.draw_image = self._renderer.draw_image
110110
self.draw_markers = self._renderer.draw_markers
111-
self.draw_path_collection = self._renderer.draw_path_collection
111+
# This is its own method for the duration of the deprecation of
112+
# offset_position = "data".
113+
# self.draw_path_collection = self._renderer.draw_path_collection
112114
self.draw_quad_mesh = self._renderer.draw_quad_mesh
113115
self.copy_from_bbox = self._renderer.copy_from_bbox
114116
self.get_content_extents = self._renderer.get_content_extents
@@ -152,6 +154,19 @@ def draw_path(self, gc, path, transform, rgbFace=None):
152154
raise OverflowError("Exceeded cell block limit (set "
153155
"'agg.path.chunksize' rcparam)")
154156

157+
def draw_path_collection(self, gc, master_transform, paths, all_transforms,
158+
offsets, offsetTrans, facecolors, edgecolors,
159+
linewidths, linestyles, antialiaseds, urls,
160+
offset_position):
161+
if offset_position == "data":
162+
cbook.warn_deprecated(
163+
"3.3", message="Support for offset_position='data' is "
164+
"deprecated since %(since)s and will be removed %(removal)s.")
165+
return self._renderer.draw_path_collection(
166+
gc, master_transform, paths, all_transforms, offsets, offsetTrans,
167+
facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls,
168+
offset_position)
169+
155170
def draw_mathtext(self, gc, x, y, s, prop, angle):
156171
"""Draw mathtext using :mod:`matplotlib.mathtext`."""
157172
ox, oy, width, height, descent, font_image, used_characters = \

lib/matplotlib/collections.py

+25-20
Original file line numberDiff line numberDiff line change
@@ -41,28 +41,28 @@ class Collection(artist.Artist, cm.ScalarMappable):
4141
4242
Keyword arguments and default values:
4343
44-
* *edgecolors*: None
45-
* *facecolors*: None
46-
* *linewidths*: None
47-
* *capstyle*: None
48-
* *joinstyle*: None
49-
* *antialiaseds*: None
50-
* *offsets*: None
51-
* *transOffset*: transforms.IdentityTransform()
52-
* *offset_position*: 'screen' (default) or 'data'
53-
* *norm*: None (optional for
54-
:class:`matplotlib.cm.ScalarMappable`)
55-
* *cmap*: None (optional for
56-
:class:`matplotlib.cm.ScalarMappable`)
57-
* *hatch*: None
58-
* *zorder*: 1
44+
* *edgecolors*: None
45+
* *facecolors*: None
46+
* *linewidths*: None
47+
* *capstyle*: None
48+
* *joinstyle*: None
49+
* *antialiaseds*: None
50+
* *offsets*: None
51+
* *transOffset*: transforms.IdentityTransform()
52+
* *offset_position* (deprecated): 'screen' (default) or 'data' (deprecated)
53+
* *norm*: None (optional for
54+
:class:`matplotlib.cm.ScalarMappable`)
55+
* *cmap*: None (optional for
56+
:class:`matplotlib.cm.ScalarMappable`)
57+
* *hatch*: None
58+
* *zorder*: 1
5959
6060
*offsets* and *transOffset* are used to translate the patch after
6161
rendering (default no offsets). If offset_position is 'screen'
6262
(default) the offset is applied after the master transform has
6363
been applied, that is, the offsets are in screen coordinates. If
64-
offset_position is 'data', the offset is applied before the master
65-
transform, i.e., the offsets are in data coordinates.
64+
offset_position is 'data' (deprecated), the offset is applied before the
65+
master transform, i.e., the offsets are in data coordinates.
6666
6767
If any of *edgecolors*, *facecolors*, *linewidths*, *antialiaseds*
6868
are None, they default to their :data:`matplotlib.rcParams` patch
@@ -87,6 +87,7 @@ class Collection(artist.Artist, cm.ScalarMappable):
8787
# subclass-by-subclass basis.
8888
_edge_default = False
8989

90+
@cbook._delete_parameter("3.1", "offset_position")
9091
def __init__(self,
9192
edgecolors=None,
9293
facecolors=None,
@@ -132,7 +133,9 @@ def __init__(self,
132133
self.set_pickradius(pickradius)
133134
self.set_urls(urls)
134135
self.set_hatch(hatch)
135-
self.set_offset_position(offset_position)
136+
self._offset_position = "screen"
137+
if offset_position != "screen":
138+
self.set_offset_position(offset_position) # emit deprecation.
136139
self.set_zorder(zorder)
137140

138141
if capstyle:
@@ -407,7 +410,7 @@ def contains(self, mouseevent):
407410
self._picker is not True # the bool, not just nonzero or 1
408411
else self._pickradius)
409412

410-
if self.axes and self.get_offset_position() == "data":
413+
if self.axes:
411414
self.axes._unstale_viewLim()
412415

413416
transform, transOffset, offsets, paths = self._prepare_points()
@@ -416,7 +419,7 @@ def contains(self, mouseevent):
416419
mouseevent.x, mouseevent.y, pickradius,
417420
transform.frozen(), paths, self.get_transforms(),
418421
offsets, transOffset, pickradius <= 0,
419-
self.get_offset_position())
422+
self._offset_position)
420423

421424
return len(ind) > 0, dict(ind=ind)
422425

@@ -497,6 +500,7 @@ def get_offsets(self):
497500
else:
498501
return self._uniform_offsets
499502

503+
@cbook.deprecated("3.3")
500504
def set_offset_position(self, offset_position):
501505
"""
502506
Set how offsets are applied. If *offset_position* is 'screen'
@@ -514,6 +518,7 @@ def set_offset_position(self, offset_position):
514518
self._offset_position = offset_position
515519
self.stale = True
516520

521+
@cbook.deprecated("3.3")
517522
def get_offset_position(self):
518523
"""
519524
Returns how offsets are applied for the collection. If

lib/matplotlib/tests/test_backend_bases.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ def check(master_transform, paths, all_transforms,
2525
master_transform, paths, all_transforms))
2626
gc = rb.new_gc()
2727
ids = [path_id for xo, yo, path_id, gc0, rgbFace in
28-
rb._iter_collection(gc, master_transform, all_transforms,
29-
range(len(raw_paths)), offsets,
30-
transforms.IdentityTransform(),
31-
facecolors, edgecolors, [], [], [False],
32-
[], 'data')]
28+
rb._iter_collection(
29+
gc, master_transform, all_transforms,
30+
range(len(raw_paths)), offsets,
31+
transforms._VectorTransform(master_transform),
32+
facecolors, edgecolors, [], [], [False],
33+
[], 'screen')]
3334
uses = rb._iter_collection_uses_per_path(
3435
paths, all_transforms, offsets, facecolors, edgecolors)
3536
if raw_paths:

lib/matplotlib/transforms.py

+18
Original file line numberDiff line numberDiff line change
@@ -2612,6 +2612,24 @@ def get_matrix(self):
26122612
return self._mtx
26132613

26142614

2615+
class _VectorTransform(Affine2DBase):
2616+
"""
2617+
A transform wrapper that ignores the offset component of a base transform.
2618+
"""
2619+
2620+
def __init__(self, transform, **kwargs):
2621+
super().__init__(**kwargs)
2622+
self._base_transform = transform
2623+
2624+
__str__ = _make_str_method("_base_transform")
2625+
2626+
def get_matrix(self):
2627+
if self._invalid:
2628+
self._mtx = self._base_transform.get_matrix().copy()
2629+
self._mtx[:2, -1] = 0
2630+
return self._mtx
2631+
2632+
26152633
class TransformedPath(TransformNode):
26162634
"""
26172635
A `TransformedPath` caches a non-affine transformed copy of the

0 commit comments

Comments
 (0)