diff --git a/doc/api/api_changes/2018-12-20-AL-deprecations.rst b/doc/api/api_changes/2018-12-20-AL-deprecations.rst new file mode 100644 index 000000000000..56bd23bc85dd --- /dev/null +++ b/doc/api/api_changes/2018-12-20-AL-deprecations.rst @@ -0,0 +1,30 @@ +Deprecations +```````````` + +Multiple internal functions that were exposed as part of the public API +of ``mpl_toolkits.mplot3d`` are deprecated, + +**mpl_toolkits.mplot3d.art3d** + +- :func:`mpl_toolkits.mplot3d.art3d.norm_angle` +- :func:`mpl_toolkits.mplot3d.art3d.norm_text_angle` +- :func:`mpl_toolkits.mplot3d.art3d.path_to_3d_segment` +- :func:`mpl_toolkits.mplot3d.art3d.paths_to_3d_segments` +- :func:`mpl_toolkits.mplot3d.art3d.path_to_3d_segment_with_codes` +- :func:`mpl_toolkits.mplot3d.art3d.paths_to_3d_segments_with_codes` +- :func:`mpl_toolkits.mplot3d.art3d.get_patch_verts` +- :func:`mpl_toolkits.mplot3d.art3d.get_colors` +- :func:`mpl_toolkits.mplot3d.art3d.zalpha` + +**mpl_toolkits.mplot3d.proj3d** + +- :func:`mpl_toolkits.mplot3d.proj3d.line2d` +- :func:`mpl_toolkits.mplot3d.proj3d.line2d_dist` +- :func:`mpl_toolkits.mplot3d.proj3d.line2d_seg_dist` +- :func:`mpl_toolkits.mplot3d.proj3d.mod` +- :func:`mpl_toolkits.mplot3d.proj3d.proj_transform_vec` +- :func:`mpl_toolkits.mplot3d.proj3d.proj_transform_vec_clip` +- :func:`mpl_toolkits.mplot3d.proj3d.vec_pad_ones` +- :func:`mpl_toolkits.mplot3d.proj3d.proj_trans_clip_points` + +If your project relies on these functions, consider vendoring them. diff --git a/lib/mpl_toolkits/mplot3d/art3d.py b/lib/mpl_toolkits/mplot3d/art3d.py index 808ee5a95f7c..d38fa862b69e 100644 --- a/lib/mpl_toolkits/mplot3d/art3d.py +++ b/lib/mpl_toolkits/mplot3d/art3d.py @@ -20,7 +20,7 @@ from . import proj3d -def norm_angle(a): +def _norm_angle(a): """Return the given angle normalized to -180 < *a* <= 180 degrees.""" a = (a + 360) % 360 if a > 180: @@ -28,7 +28,13 @@ def norm_angle(a): return a -def norm_text_angle(a): +@cbook.deprecated("3.1") +def norm_angle(a): + """Return the given angle normalized to -180 < *a* <= 180 degrees.""" + return _norm_angle(a) + + +def _norm_text_angle(a): """Return the given angle normalized to -90 < *a* <= 90 degrees.""" a = (a + 180) % 180 if a > 90: @@ -36,6 +42,12 @@ def norm_text_angle(a): return a +@cbook.deprecated("3.1") +def norm_text_angle(a): + """Return the given angle normalized to -90 < *a* <= 90 degrees.""" + return _norm_text_angle(a) + + def get_dir_vector(zdir): """ Return a direction vector. @@ -109,7 +121,7 @@ def draw(self, renderer): dy = proj[1][1] - proj[1][0] angle = math.degrees(math.atan2(dy, dx)) self.set_position((proj[0][0], proj[1][0])) - self.set_rotation(norm_text_angle(angle)) + self.set_rotation(_norm_text_angle(angle)) mtext.Text.draw(self, renderer) self.stale = False @@ -200,7 +212,7 @@ def line_2d_to_3d(line, zs=0, zdir='z'): line.set_3d_properties(zs, zdir) -def path_to_3d_segment(path, zs=0, zdir='z'): +def _path_to_3d_segment(path, zs=0, zdir='z'): """Convert a path to a 3D segment.""" zs = np.broadcast_to(zs, len(path)) @@ -210,16 +222,28 @@ def path_to_3d_segment(path, zs=0, zdir='z'): return seg3d -def paths_to_3d_segments(paths, zs=0, zdir='z'): +@cbook.deprecated("3.1") +def path_to_3d_segment(path, zs=0, zdir='z'): + """Convert a path to a 3D segment.""" + return _path_to_3d_segment(path, zs=zs, zdir=zdir) + + +def _paths_to_3d_segments(paths, zs=0, zdir='z'): """Convert paths from a collection object to 3D segments.""" zs = np.broadcast_to(zs, len(paths)) - segs = [path_to_3d_segment(path, pathz, zdir) + segs = [_path_to_3d_segment(path, pathz, zdir) for path, pathz in zip(paths, zs)] return segs -def path_to_3d_segment_with_codes(path, zs=0, zdir='z'): +@cbook.deprecated("3.1") +def paths_to_3d_segments(paths, zs=0, zdir='z'): + """Convert paths from a collection object to 3D segments.""" + return _paths_to_3d_segments(paths, zs=zs, zdir=zdir) + + +def _path_to_3d_segment_with_codes(path, zs=0, zdir='z'): """Convert a path to a 3D segment with path codes.""" zs = np.broadcast_to(zs, len(path)) @@ -234,13 +258,19 @@ def path_to_3d_segment_with_codes(path, zs=0, zdir='z'): return seg3d, list(codes) -def paths_to_3d_segments_with_codes(paths, zs=0, zdir='z'): +@cbook.deprecated("3.1") +def path_to_3d_segment_with_codes(path, zs=0, zdir='z'): + """Convert a path to a 3D segment with path codes.""" + return _path_to_3d_segment_with_codes(path, zs=zs, zdir=zdir) + + +def _paths_to_3d_segments_with_codes(paths, zs=0, zdir='z'): """ Convert paths from a collection object to 3D segments with path codes. """ zs = np.broadcast_to(zs, len(paths)) - segments_codes = [path_to_3d_segment_with_codes(path, pathz, zdir) + segments_codes = [_path_to_3d_segment_with_codes(path, pathz, zdir) for path, pathz in zip(paths, zs)] if segments_codes: segments, codes = zip(*segments_codes) @@ -249,6 +279,14 @@ def paths_to_3d_segments_with_codes(paths, zs=0, zdir='z'): return list(segments), list(codes) +@cbook.deprecated("3.1") +def paths_to_3d_segments_with_codes(paths, zs=0, zdir='z'): + """ + Convert paths from a collection object to 3D segments with path codes. + """ + return _paths_to_3d_segments_with_codes(paths, zs=zs, zdir=zdir) + + class Line3DCollection(LineCollection): """ A collection of 3D lines. @@ -291,7 +329,7 @@ def draw(self, renderer, project=False): def line_collection_2d_to_3d(col, zs=0, zdir='z'): """Convert a LineCollection to a Line3DCollection object.""" - segments3d = paths_to_3d_segments(col.get_paths(), zs, zdir) + segments3d = _paths_to_3d_segments(col.get_paths(), zs, zdir) col.__class__ = Line3DCollection col.set_segments(segments3d) @@ -350,7 +388,7 @@ def do_3d_projection(self, renderer): return min(vzs) -def get_patch_verts(patch): +def _get_patch_verts(patch): """Return a list of vertices for the path of a patch.""" trans = patch.get_patch_transform() path = patch.get_path() @@ -361,9 +399,15 @@ def get_patch_verts(patch): return [] +@cbook.deprecated("3.1") +def get_patch_verts(patch): + """Return a list of vertices for the path of a patch.""" + return _get_patch_verts(patch) + + def patch_2d_to_3d(patch, z=0, zdir='z'): """Convert a Patch to a Patch3D object.""" - verts = get_patch_verts(patch) + verts = _get_patch_verts(patch) patch.__class__ = Patch3D patch.set_3d_properties(verts, z, zdir) @@ -427,12 +471,12 @@ def do_3d_projection(self, renderer): xs, ys, zs = self._offsets3d vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs, renderer.M) - fcs = (zalpha(self._facecolor3d, vzs) if self._depthshade else + fcs = (_zalpha(self._facecolor3d, vzs) if self._depthshade else self._facecolor3d) fcs = mcolors.to_rgba_array(fcs, self._alpha) self.set_facecolors(fcs) - ecs = (zalpha(self._edgecolor3d, vzs) if self._depthshade else + ecs = (_zalpha(self._edgecolor3d, vzs) if self._depthshade else self._edgecolor3d) ecs = mcolors.to_rgba_array(ecs, self._alpha) self.set_edgecolors(ecs) @@ -493,12 +537,12 @@ def do_3d_projection(self, renderer): xs, ys, zs = self._offsets3d vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs, renderer.M) - fcs = (zalpha(self._facecolor3d, vzs) if self._depthshade else + fcs = (_zalpha(self._facecolor3d, vzs) if self._depthshade else self._facecolor3d) fcs = mcolors.to_rgba_array(fcs, self._alpha) self.set_facecolors(fcs) - ecs = (zalpha(self._edgecolor3d, vzs) if self._depthshade else + ecs = (_zalpha(self._edgecolor3d, vzs) if self._depthshade else self._edgecolor3d) ecs = mcolors.to_rgba_array(ecs, self._alpha) self.set_edgecolors(ecs) @@ -643,7 +687,7 @@ def do_3d_projection(self, renderer): self.update_scalarmappable() self._facecolors3d = self._facecolors - txs, tys, tzs = proj3d.proj_transform_vec(self._vec, renderer.M) + txs, tys, tzs = proj3d._proj_transform_vec(self._vec, renderer.M) xyzlist = [(txs[si:ei], tys[si:ei], tzs[si:ei]) for si, ei in self._segis] @@ -681,7 +725,7 @@ def do_3d_projection(self, renderer): # Return zorder value if self._sort_zpos is not None: zvec = np.array([[0], [0], [self._sort_zpos], [1]]) - ztrans = proj3d.proj_transform_vec(zvec, renderer.M) + ztrans = proj3d._proj_transform_vec(zvec, renderer.M) return ztrans[2][0] elif tzs.size > 0: # FIXME: Some results still don't look quite right. @@ -734,8 +778,8 @@ def get_edgecolor(self): def poly_collection_2d_to_3d(col, zs=0, zdir='z'): """Convert a PolyCollection to a Poly3DCollection object.""" - segments_3d, codes = paths_to_3d_segments_with_codes(col.get_paths(), - zs, zdir) + segments_3d, codes = _paths_to_3d_segments_with_codes( + col.get_paths(), zs, zdir) col.__class__ = Poly3DCollection col.set_verts_and_codes(segments_3d, codes) col.set_3d_properties() @@ -777,14 +821,20 @@ def rotate_axes(xs, ys, zs, zdir): return xs, ys, zs -def get_colors(c, num): +def _get_colors(c, num): """Stretch the color argument to provide the required number *num*.""" return np.broadcast_to( mcolors.to_rgba_array(c) if len(c) else [0, 0, 0, 0], (num, 4)) -def zalpha(colors, zs): +@cbook.deprecated("3.1") +def get_colors(c, num): + """Stretch the color argument to provide the required number *num*.""" + return _get_colors(c, num) + + +def _zalpha(colors, zs): """Modify the alphas of the color list according to depth.""" # FIXME: This only works well if the points for *zs* are well-spaced # in all three dimensions. Otherwise, at certain orientations, @@ -796,3 +846,9 @@ def zalpha(colors, zs): sats = 1 - norm(zs) * 0.7 rgba = np.broadcast_to(mcolors.to_rgba_array(colors), (len(zs), 4)) return np.column_stack([rgba[:, :3], rgba[:, 3] * sats]) + + +@cbook.deprecated("3.1") +def zalpha(colors, zs): + """Modify the alphas of the color list according to depth.""" + return _zalpha(colors, zs) diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index 8f2857ee4dfc..468b3aa4cc0e 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -1027,7 +1027,7 @@ def get_proj(self): self.eye = E self.vvec = R - E - self.vvec = self.vvec / proj3d.mod(self.vvec) + self.vvec = self.vvec / proj3d._mod(self.vvec) if abs(relev) > np.pi/2: # upside down @@ -1162,7 +1162,7 @@ def format_coord(self, xd, yd): # nearest edge p0, p1 = min(self.tunit_edges(), - key=lambda edge: proj3d.line2d_seg_dist( + key=lambda edge: proj3d._line2d_seg_dist( edge[0], edge[1], (xd, yd))) # scale the z value to match @@ -1209,8 +1209,8 @@ def _on_move(self, event): # get the x and y pixel coords if dx == 0 and dy == 0: return - self.elev = art3d.norm_angle(self.elev - (dy/h)*180) - self.azim = art3d.norm_angle(self.azim - (dx/w)*180) + self.elev = art3d._norm_angle(self.elev - (dy/h)*180) + self.azim = art3d._norm_angle(self.azim - (dx/w)*180) self.get_proj() self.stale = True self.figure.canvas.draw_idle() @@ -1762,8 +1762,8 @@ def _shade_colors(self, color, normals, lightsource=None): # chosen for backwards-compatibility lightsource = LightSource(azdeg=225, altdeg=19.4712) - shade = np.array([np.dot(n / proj3d.mod(n), lightsource.direction) - if proj3d.mod(n) else np.nan + shade = np.array([np.dot(n / proj3d._mod(n), lightsource.direction) + if proj3d._mod(n) else np.nan for n in normals]) mask = ~np.isnan(shade) @@ -2029,8 +2029,8 @@ def _3d_extend_contour(self, cset, stride=5): paths = linec.get_paths() if not paths: continue - topverts = art3d.paths_to_3d_segments(paths, z - dz) - botverts = art3d.paths_to_3d_segments(paths, z + dz) + topverts = art3d._paths_to_3d_segments(paths, z - dz) + botverts = art3d._paths_to_3d_segments(paths, z + dz) color = linec.get_color()[0] @@ -2394,7 +2394,7 @@ def bar(self, left, height, zs=0, zdir='z', *args, **kwargs): verts = [] verts_zs = [] for p, z in zip(patches, zs): - vs = art3d.get_patch_verts(p) + vs = art3d._get_patch_verts(p) verts += vs.tolist() verts_zs += [z] * len(vs) art3d.patch_2d_to_3d(p, z, zdir) diff --git a/lib/mpl_toolkits/mplot3d/axis3d.py b/lib/mpl_toolkits/mplot3d/axis3d.py index ba43685b2282..4fb5ad6ae68a 100644 --- a/lib/mpl_toolkits/mplot3d/axis3d.py +++ b/lib/mpl_toolkits/mplot3d/axis3d.py @@ -298,7 +298,7 @@ def draw(self, renderer): renderer.M) self.label.set_position((tlx, tly)) if self.get_rotate_label(self.label.get_text()): - angle = art3d.norm_text_angle(np.rad2deg(np.arctan2(dy, dx))) + angle = art3d._norm_text_angle(np.rad2deg(np.arctan2(dy, dx))) self.label.set_rotation(angle) self.label.set_va(info['label']['va']) self.label.set_ha(info['label']['ha']) @@ -321,7 +321,7 @@ def draw(self, renderer): pos[0], pos[1], pos[2], renderer.M) self.offsetText.set_text(self.major.formatter.get_offset()) self.offsetText.set_position((olx, oly)) - angle = art3d.norm_text_angle(np.rad2deg(np.arctan2(dy, dx))) + angle = art3d._norm_text_angle(np.rad2deg(np.arctan2(dy, dx))) self.offsetText.set_rotation(angle) # Must set rotation mode to "anchor" so that # the alignment point is used as the "fulcrum" for rotation. diff --git a/lib/mpl_toolkits/mplot3d/proj3d.py b/lib/mpl_toolkits/mplot3d/proj3d.py index 64df8db655f1..41a5df3845f8 100644 --- a/lib/mpl_toolkits/mplot3d/proj3d.py +++ b/lib/mpl_toolkits/mplot3d/proj3d.py @@ -6,7 +6,10 @@ import numpy as np import numpy.linalg as linalg +from matplotlib import cbook + +@cbook.deprecated("3.1") def line2d(p0, p1): """ Return 2D equation of line in the form ax+by+c = 0 @@ -30,6 +33,7 @@ def line2d(p0, p1): return a, b, c +@cbook.deprecated("3.1") def line2d_dist(l, p): """ Distance from line to point @@ -40,7 +44,7 @@ def line2d_dist(l, p): return abs((a*x0 + b*y0 + c) / np.hypot(a, b)) -def line2d_seg_dist(p1, p2, p0): +def _line2d_seg_dist(p1, p2, p0): """distance(s) from line defined by p1 - p2 to point(s) p0 p0[0] = x(s) @@ -62,11 +66,30 @@ def line2d_seg_dist(p1, p2, p0): return d -def mod(v): +@cbook.deprecated("3.1") +def line2d_seg_dist(p1, p2, p0): + """distance(s) from line defined by p1 - p2 to point(s) p0 + + p0[0] = x(s) + p0[1] = y(s) + + intersection point p = p1 + u*(p2-p1) + and intersection point lies within segment if u is between 0 and 1 + """ + return _line2d_seg_dist(p1, p2, p0) + + +def _mod(v): """3d vector length""" return np.sqrt(v[0]**2+v[1]**2+v[2]**2) +@cbook.deprecated("3.1") +def mod(v): + """3d vector length""" + return _mod(v) + + def world_transformation(xmin, xmax, ymin, ymax, zmin, zmax): @@ -91,9 +114,9 @@ def view_transformation(E, R, V): ## end new ## old - n = n / mod(n) + n = n / _mod(n) u = np.cross(V, n) - u = u / mod(u) + u = u / _mod(u) v = np.cross(n, u) Mr = [[u[0], u[1], u[2], 0], [v[0], v[1], v[2], 0], @@ -128,7 +151,7 @@ def ortho_transformation(zfront, zback): [0, 0, a, b]]) -def proj_transform_vec(vec, M): +def _proj_transform_vec(vec, M): vecw = np.dot(M, vec) w = vecw[3] # clip here.. @@ -136,7 +159,12 @@ def proj_transform_vec(vec, M): return txs, tys, tzs -def proj_transform_vec_clip(vec, M): +@cbook.deprecated("3.1") +def proj_transform_vec(vec, M): + return _proj_transform_vec(vec, M) + + +def _proj_transform_vec_clip(vec, M): vecw = np.dot(M, vec) w = vecw[3] # clip here. @@ -147,9 +175,14 @@ def proj_transform_vec_clip(vec, M): return txs, tys, tzs, tis +@cbook.deprecated("3.1") +def proj_transform_vec_clip(vec, M): + return _proj_transform_vec_clip(vec, M) + + def inv_transform(xs, ys, zs, M): iM = linalg.inv(M) - vec = vec_pad_ones(xs, ys, zs) + vec = _vec_pad_ones(xs, ys, zs) vecr = np.dot(iM, vec) try: vecr = vecr / vecr[3] @@ -158,16 +191,21 @@ def inv_transform(xs, ys, zs, M): return vecr[0], vecr[1], vecr[2] -def vec_pad_ones(xs, ys, zs): +def _vec_pad_ones(xs, ys, zs): return np.array([xs, ys, zs, np.ones_like(xs)]) +@cbook.deprecated("3.1") +def vec_pad_ones(xs, ys, zs): + return _vec_pad_ones(xs, ys, zs) + + def proj_transform(xs, ys, zs, M): """ Transform the points by the projection matrix """ - vec = vec_pad_ones(xs, ys, zs) - return proj_transform_vec(vec, M) + vec = _vec_pad_ones(xs, ys, zs) + return _proj_transform_vec(vec, M) transform = proj_transform @@ -179,8 +217,8 @@ def proj_transform_clip(xs, ys, zs, M): and return the clipping result returns txs,tys,tzs,tis """ - vec = vec_pad_ones(xs, ys, zs) - return proj_transform_vec_clip(vec, M) + vec = _vec_pad_ones(xs, ys, zs) + return _proj_transform_vec_clip(vec, M) def proj_points(points, M): @@ -192,6 +230,7 @@ def proj_trans_points(points, M): return proj_transform(xs, ys, zs, M) +@cbook.deprecated("3.1") def proj_trans_clip_points(points, M): xs, ys, zs = zip(*points) return proj_transform_clip(xs, ys, zs, M) diff --git a/lib/mpl_toolkits/tests/test_mplot3d.py b/lib/mpl_toolkits/tests/test_mplot3d.py index a1d3fd6653e9..23986f5b1ec3 100644 --- a/lib/mpl_toolkits/tests/test_mplot3d.py +++ b/lib/mpl_toolkits/tests/test_mplot3d.py @@ -2,7 +2,9 @@ from mpl_toolkits.mplot3d import Axes3D, axes3d, proj3d, art3d from matplotlib import cm +from matplotlib import path as mpath from matplotlib.testing.decorators import image_comparison, check_figures_equal +from matplotlib.cbook.deprecation import MatplotlibDeprecationWarning from matplotlib.collections import LineCollection, PolyCollection from matplotlib.patches import Circle import matplotlib.pyplot as plt @@ -618,8 +620,8 @@ def test_lines_dists(): ys = (100, 150, 30, 200) ax.scatter(xs, ys) - dist = proj3d.line2d_seg_dist(p0, p1, (xs[0], ys[0])) - dist = proj3d.line2d_seg_dist(p0, p1, np.array((xs, ys))) + dist = proj3d._line2d_seg_dist(p0, p1, (xs[0], ys[0])) + dist = proj3d._line2d_seg_dist(p0, p1, np.array((xs, ys))) for x, y, d in zip(xs, ys, dist): c = Circle((x, y), d, fill=0) ax.add_patch(c) @@ -842,3 +844,58 @@ def test_inverted_cla(): assert not ax.xaxis_inverted() assert not ax.yaxis_inverted() assert not ax.zaxis_inverted() + + +def test_art3d_deprecated(): + + with pytest.warns(MatplotlibDeprecationWarning): + art3d.norm_angle(0.0) + + with pytest.warns(MatplotlibDeprecationWarning): + art3d.norm_text_angle(0.0) + + path = mpath.Path(np.empty((0, 2))) + + with pytest.warns(MatplotlibDeprecationWarning): + art3d.path_to_3d_segment(path) + + with pytest.warns(MatplotlibDeprecationWarning): + art3d.paths_to_3d_segments([path]) + + with pytest.warns(MatplotlibDeprecationWarning): + art3d.path_to_3d_segment_with_codes(path) + + with pytest.warns(MatplotlibDeprecationWarning): + art3d.paths_to_3d_segments_with_codes([path]) + + with pytest.warns(MatplotlibDeprecationWarning): + art3d.get_colors([], 1) + + with pytest.warns(MatplotlibDeprecationWarning): + art3d.zalpha([], []) + + +def test_proj3d_deprecated(): + with pytest.warns(MatplotlibDeprecationWarning): + proj3d.line2d([0, 1], [0, 1]) + + with pytest.warns(MatplotlibDeprecationWarning): + proj3d.line2d_dist([0, 1, 3], [0, 1]) + + with pytest.warns(MatplotlibDeprecationWarning): + proj3d.mod([1, 1, 1]) + + vec = np.arange(4) + M = np.ones((4, 4)) + + with pytest.warns(MatplotlibDeprecationWarning): + proj3d.proj_transform_vec(vec, M) + + with pytest.warns(MatplotlibDeprecationWarning): + proj3d.proj_transform_vec_clip(vec, M) + + with pytest.warns(MatplotlibDeprecationWarning): + proj3d.vec_pad_ones(np.ones(3), np.ones(3), np.ones(3)) + + with pytest.warns(MatplotlibDeprecationWarning): + proj3d.proj_trans_clip_points(np.ones((4, 3)), M)