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

Skip to content

Commit fcfe8b1

Browse files
committed
Convert existing mplot3d functions to use transforms
1 parent eb7676c commit fcfe8b1

File tree

3 files changed

+76
-98
lines changed

3 files changed

+76
-98
lines changed

lib/mpl_toolkits/mplot3d/art3d.py

Lines changed: 53 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
Collection, LineCollection, PolyCollection, PatchCollection, PathCollection)
2121
from matplotlib.colors import Normalize
2222
from matplotlib.patches import Patch
23-
from . import proj3d
2423

2524

2625
def _norm_angle(a):
@@ -148,12 +147,11 @@ def set_3d_properties(self, z=0, zdir='z'):
148147
@artist.allow_rasterization
149148
def draw(self, renderer):
150149
position3d = np.array((self._x, self._y, self._z))
151-
proj = proj3d._proj_trans_points(
152-
[position3d, position3d + self._dir_vec], self.axes.M)
153-
dx = proj[0][1] - proj[0][0]
154-
dy = proj[1][1] - proj[1][0]
150+
proj = self.axes.M.transform([position3d, position3d + self._dir_vec])
151+
dx = proj[1][0] - proj[0][0]
152+
dy = proj[1][1] - proj[0][1]
155153
angle = math.degrees(math.atan2(dy, dx))
156-
with cbook._setattr_cm(self, _x=proj[0][0], _y=proj[1][0],
154+
with cbook._setattr_cm(self, _x=proj[0][0], _y=proj[0][1],
157155
_rotation=_norm_text_angle(angle)):
158156
mtext.Text.draw(self, renderer)
159157
self.stale = False
@@ -267,8 +265,8 @@ def get_data_3d(self):
267265
@artist.allow_rasterization
268266
def draw(self, renderer):
269267
xs3d, ys3d, zs3d = self._verts3d
270-
xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)
271-
self.set_data(xs, ys)
268+
points = self.axes.M.transform(np.column_stack((xs3d, ys3d, zs3d)))
269+
self.set_data(points[:, 0], points[:, 1])
272270
super().draw(renderer)
273271
self.stale = False
274272

@@ -349,11 +347,11 @@ class Collection3D(Collection):
349347

350348
def do_3d_projection(self):
351349
"""Project the points according to renderer matrix."""
352-
xyzs_list = [proj3d.proj_transform(*vs.T, self.axes.M)
353-
for vs, _ in self._3dverts_codes]
354-
self._paths = [mpath.Path(np.column_stack([xs, ys]), cs)
355-
for (xs, ys, _), (_, cs) in zip(xyzs_list, self._3dverts_codes)]
356-
zs = np.concatenate([zs for _, _, zs in xyzs_list])
350+
path_vertices = [self.axes.M.transform(vs) for vs, _ in self._3dverts_codes]
351+
self._paths = [mpath.Path(vertices[:, :2], codes)
352+
for (vertices, (_, codes))
353+
in zip(path_vertices, self._3dverts_codes)]
354+
zs = np.concatenate(path_vertices)[:, 2]
357355
return zs.min() if len(zs) else 1e9
358356

359357

@@ -390,15 +388,14 @@ def do_3d_projection(self):
390388
"""
391389
Project the points according to renderer matrix.
392390
"""
393-
xyslist = [proj3d._proj_trans_points(points, self.axes.M)
394-
for points in self._segments3d]
395-
segments_2d = [np.column_stack([xs, ys]) for xs, ys, zs in xyslist]
391+
segments_3d = [self.axes.M.transform(segment) for segment in self._segments3d]
392+
segments_2d = [segment[:, :2] for segment in segments_3d]
396393
LineCollection.set_segments(self, segments_2d)
397394

398395
# FIXME
399396
minz = 1e9
400-
for xs, ys, zs in xyslist:
401-
minz = min(minz, min(zs))
397+
for segment in segments_3d:
398+
minz = min(minz, segment[0][2], segment[1][2])
402399
return minz
403400

404401

@@ -456,12 +453,10 @@ def get_path(self):
456453
return self._path2d
457454

458455
def do_3d_projection(self):
459-
s = self._segment3d
460-
xs, ys, zs = zip(*s)
461-
vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs,
462-
self.axes.M)
463-
self._path2d = mpath.Path(np.column_stack([vxs, vys]))
464-
return min(vzs)
456+
segments = self.axes.M.transform(self._segment3d)
457+
self._path2d = mpath.Path(segments[:, :2])
458+
459+
return min(segments[:, 2])
465460

466461

467462
class PathPatch3D(Patch3D):
@@ -503,12 +498,10 @@ def set_3d_properties(self, path, zs=0, zdir='z'):
503498
self._code3d = path.codes
504499

505500
def do_3d_projection(self):
506-
s = self._segment3d
507-
xs, ys, zs = zip(*s)
508-
vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs,
509-
self.axes.M)
510-
self._path2d = mpath.Path(np.column_stack([vxs, vys]), self._code3d)
511-
return min(vzs)
501+
segments = self.axes.M.transform(self._segment3d)
502+
self._path2d = mpath.Path(segments[:, :2], self._code3d)
503+
504+
return min(segments[:, 2])
512505

513506

514507
def _get_patch_verts(patch):
@@ -610,14 +603,13 @@ def set_3d_properties(self, zs, zdir):
610603
self.stale = True
611604

612605
def do_3d_projection(self):
613-
xs, ys, zs = self._offsets3d
614-
vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs,
615-
self.axes.M)
616-
self._vzs = vzs
617-
super().set_offsets(np.column_stack([vxs, vys]))
606+
points = self.axes.M.transform(np.column_stack(self._offsets3d))
607+
super().set_offsets(points[:, :2])
618608

619-
if vzs.size > 0:
620-
return min(vzs)
609+
self._vzs = points[:, 2]
610+
611+
if self._vzs.size > 0:
612+
return min(self._vzs)
621613
else:
622614
return np.nan
623615

@@ -751,37 +743,31 @@ def set_depthshade(self, depthshade):
751743
self.stale = True
752744

753745
def do_3d_projection(self):
754-
xs, ys, zs = self._offsets3d
755-
vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs,
756-
self.axes.M)
757746
# Sort the points based on z coordinates
758747
# Performance optimization: Create a sorted index array and reorder
759748
# points and point properties according to the index array
760-
z_markers_idx = self._z_markers_idx = np.argsort(vzs)[::-1]
761-
self._vzs = vzs
749+
points = self.axes.M.transform(np.column_stack(self._offsets3d))
750+
z_markers_idx = self._z_markers_idx = np.argsort(points[:, 2])[::-1]
751+
self._vzs = points[:, 2]
762752

763753
# we have to special case the sizes because of code in collections.py
764754
# as the draw method does
765755
# self.set_sizes(self._sizes, self.figure.dpi)
766756
# so we cannot rely on doing the sorting on the way out via get_*
767-
768757
if len(self._sizes3d) > 1:
769758
self._sizes = self._sizes3d[z_markers_idx]
770759

771760
if len(self._linewidths3d) > 1:
772761
self._linewidths = self._linewidths3d[z_markers_idx]
773762

774-
PathCollection.set_offsets(self, np.column_stack((vxs, vys)))
763+
PathCollection.set_offsets(self, points[:, :2])
775764

776765
# Re-order items
777-
vzs = vzs[z_markers_idx]
778-
vxs = vxs[z_markers_idx]
779-
vys = vys[z_markers_idx]
766+
points = points[z_markers_idx]
780767

781768
# Store ordered offset for drawing purpose
782-
self._offset_zordered = np.column_stack((vxs, vys))
783-
784-
return np.min(vzs) if vzs.size else np.nan
769+
self._offset_zordered = points[:, :2]
770+
return np.min(self._vzs) if self._vzs.size else np.nan
785771

786772
@contextmanager
787773
def _use_zordered_offset(self):
@@ -954,8 +940,7 @@ def get_vector(self, segments3d):
954940
xs, ys, zs = np.vstack(segments3d).T
955941
else: # vstack can't stack zero arrays.
956942
xs, ys, zs = [], [], []
957-
ones = np.ones(len(xs))
958-
self._vec = np.array([xs, ys, zs, ones])
943+
self._vec = np.array([xs, ys, zs])
959944

960945
indices = [0, *np.cumsum([len(segment) for segment in segments3d])]
961946
self._segslices = [*map(slice, indices[:-1], indices[1:])]
@@ -1020,27 +1005,28 @@ def do_3d_projection(self):
10201005
self._facecolor3d = self._facecolors
10211006
if self._edge_is_mapped:
10221007
self._edgecolor3d = self._edgecolors
1023-
txs, tys, tzs = proj3d._proj_transform_vec(self._vec, self.axes.M)
1024-
xyzlist = [(txs[sl], tys[sl], tzs[sl]) for sl in self._segslices]
1008+
1009+
verts = self.axes.M.transform(np.column_stack(self._vec))
1010+
verts_slices = [verts[sl] for sl in self._segslices]
10251011

10261012
# This extra fuss is to re-order face / edge colors
10271013
cface = self._facecolor3d
10281014
cedge = self._edgecolor3d
1029-
if len(cface) != len(xyzlist):
1030-
cface = cface.repeat(len(xyzlist), axis=0)
1031-
if len(cedge) != len(xyzlist):
1015+
1016+
if len(cface) != len(verts_slices):
1017+
cface = cface.repeat(len(verts_slices), axis=0)
1018+
if len(cedge) != len(verts_slices):
10321019
if len(cedge) == 0:
10331020
cedge = cface
10341021
else:
1035-
cedge = cedge.repeat(len(xyzlist), axis=0)
1022+
cedge = cedge.repeat(len(verts_slices), axis=0)
10361023

1037-
if xyzlist:
1038-
# sort by depth (furthest drawn first)
1024+
if verts_slices:
10391025
z_segments_2d = sorted(
1040-
((self._zsortfunc(zs), np.column_stack([xs, ys]), fc, ec, idx)
1041-
for idx, ((xs, ys, zs), fc, ec)
1042-
in enumerate(zip(xyzlist, cface, cedge))),
1043-
key=lambda x: x[0], reverse=True)
1026+
((self._zsortfunc(verts[:, 2]), verts[:, :2], fc, ec, idx)
1027+
for idx, (verts, fc, ec)
1028+
in enumerate(zip(verts_slices, cface, cedge))),
1029+
key=lambda x: x[0], reverse=True)
10441030

10451031
_, segments_2d, self._facecolors2d, self._edgecolors2d, idxs = \
10461032
zip(*z_segments_2d)
@@ -1061,14 +1047,12 @@ def do_3d_projection(self):
10611047

10621048
# Return zorder value
10631049
if self._sort_zpos is not None:
1064-
zvec = np.array([[0], [0], [self._sort_zpos], [1]])
1065-
ztrans = proj3d._proj_transform_vec(zvec, self.axes.M)
1066-
return ztrans[2][0]
1067-
elif tzs.size > 0:
1050+
return self.axes.M.transform([0, 0, self._sort_zpos])[2]
1051+
elif len(verts) > 0:
10681052
# FIXME: Some results still don't look quite right.
10691053
# In particular, examine contourf3d_demo2.py
10701054
# with az = -54 and elev = -45.
1071-
return np.min(tzs)
1055+
return np.min(verts[:, 2])
10721056
else:
10731057
return np.nan
10741058

lib/mpl_toolkits/mplot3d/axes3d.py

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from . import art3d
3636
from . import proj3d
3737
from . import axis3d
38+
from . import transform3d
3839

3940

4041
@_docstring.interpd
@@ -158,8 +159,8 @@ def __init__(
158159
super().set_axis_off()
159160
# Enable drawing of axes by Axes3D class
160161
self.set_axis_on()
161-
self.M = None
162-
self.invM = None
162+
self.M = mtransforms.IdentityTransform(dims=3)
163+
self.invM = mtransforms.IdentityTransform(dims=3)
163164

164165
self._view_margin = 1/48 # default value to match mpl3.8
165166
self.autoscale_view()
@@ -236,7 +237,7 @@ def _transformed_cube(self, vals):
236237
(maxx, miny, maxz),
237238
(maxx, maxy, maxz),
238239
(minx, maxy, maxz)]
239-
return proj3d._proj_points(xyzs, self.M)
240+
return self.M.transform(xyzs)
240241

241242
def set_aspect(self, aspect, adjustable=None, anchor=None, share=False):
242243
"""
@@ -423,7 +424,7 @@ def draw(self, renderer):
423424

424425
# add the projection matrix to the renderer
425426
self.M = self.get_proj()
426-
self.invM = np.linalg.inv(self.M)
427+
self.invM = self.M.inverted()
427428

428429
collections_and_patches = (
429430
artist for artist in self._children
@@ -1200,12 +1201,8 @@ def get_proj(self):
12001201

12011202
# Transform to uniform world coordinates 0-1, 0-1, 0-1
12021203
box_aspect = self._roll_to_vertical(self._box_aspect)
1203-
worldM = proj3d.world_transformation(
1204-
*self.get_xlim3d(),
1205-
*self.get_ylim3d(),
1206-
*self.get_zlim3d(),
1207-
pb_aspect=box_aspect,
1208-
)
1204+
worldM = transform3d.WorldTransform(*self.get_xlim3d(), *self.get_ylim3d(),
1205+
*self.get_zlim3d(), pb_aspect=box_aspect)
12091206

12101207
# Look into the middle of the world coordinates:
12111208
R = 0.5 * box_aspect
@@ -1238,21 +1235,18 @@ def get_proj(self):
12381235
# Generate the view and projection transformation matrices
12391236
if self._focal_length == np.inf:
12401237
# Orthographic projection
1241-
viewM = proj3d._view_transformation_uvw(u, v, w, eye)
1242-
projM = proj3d._ortho_transformation(-self._dist, self._dist)
1238+
viewM = transform3d.ViewTransform(u, v, w, eye)
1239+
projM = transform3d.OrthographicTransform(-self._dist, self._dist)
12431240
else:
12441241
# Perspective projection
12451242
# Scale the eye dist to compensate for the focal length zoom effect
12461243
eye_focal = R + self._dist * ps * self._focal_length
1247-
viewM = proj3d._view_transformation_uvw(u, v, w, eye_focal)
1248-
projM = proj3d._persp_transformation(-self._dist,
1249-
self._dist,
1250-
self._focal_length)
1244+
viewM = transform3d.ViewTransform(u, v, w, eye_focal)
1245+
projM = transform3d.PerspectiveTransform(-self._dist, self._dist,
1246+
self._focal_length)
12511247

12521248
# Combine all the transformation matrices to get the final projection
1253-
M0 = np.dot(viewM, worldM)
1254-
M = np.dot(projM, M0)
1255-
return M
1249+
return worldM + viewM + projM
12561250

12571251
def mouse_init(self, rotate_btn=1, pan_btn=2, zoom_btn=3):
12581252
"""
@@ -1459,7 +1453,7 @@ def _calc_coord(self, xv, yv, renderer=None):
14591453
zv = -1 / self._focal_length
14601454

14611455
# Convert point on view plane to data coordinates
1462-
p1 = np.array(proj3d.inv_transform(xv, yv, zv, self.invM)).ravel()
1456+
p1 = self.invM.transform([xv, yv, zv])
14631457

14641458
# Get the vector from the camera to the point on the view plane
14651459
vec = self._get_camera_loc() - p1

0 commit comments

Comments
 (0)