@@ -97,6 +97,33 @@ def _viewlim_mask(xs, ys, zs, axes):
97
97
return mask
98
98
99
99
100
+ def _clip_to_axes_bbox (axes , paths ):
101
+ """
102
+ Return an Axes-clipped copy of paths.
103
+
104
+ Parameters
105
+ ----------
106
+ axes : Axes3D
107
+ The axes to clip the paths against.
108
+ paths : array-like
109
+ A list of polygons, each comprised of a path of vertices. May also be a 3D NumPy
110
+ array, if all polygons contain paths of the same length.
111
+
112
+ Returns
113
+ -------
114
+ list of np.ndarray
115
+ A copy of *paths* with each polygon clipped to the *axes*.
116
+ """
117
+ result = mpath ._path .clip_paths_to_box (
118
+ paths ,
119
+ [axes .xy_viewLim .intervalx ,
120
+ axes .xy_viewLim .intervaly ,
121
+ axes .zz_viewLim .intervalx ],
122
+ True ) # TODO: Pass in mask?
123
+
124
+ return result
125
+
126
+
100
127
class Text3D (mtext .Text ):
101
128
"""
102
129
Text object with 3D position and direction.
@@ -1339,22 +1366,28 @@ def do_3d_projection(self):
1339
1366
if self ._edge_is_mapped :
1340
1367
self ._edgecolor3d = self ._edgecolors
1341
1368
1342
- needs_masking = np .any (self ._invalid_vertices )
1343
- num_faces = len (self ._faces )
1344
- mask = self ._invalid_vertices
1369
+ if self ._axlim_clip :
1370
+ # TODO: Apply `self._invalid_vertices` for proper masking.
1371
+ pfaces = _clip_to_axes_bbox (self .axes , self ._faces )
1372
+ num_faces = len (pfaces )
1373
+ num_verts = np .fromiter (map (len , pfaces ), dtype = np .intp )
1374
+ max_verts = num_verts .max (initial = 0 )
1375
+ segments = np .empty ((num_faces , max_verts , 3 ))
1376
+ for i , face in enumerate (pfaces ):
1377
+ segments [i , :len (face )] = face
1378
+ pfaces = segments
1379
+ mask = np .arange (max_verts ) >= num_verts [:, None ]
1380
+ needs_masking = np .any (mask )
1381
+ else :
1382
+ pfaces = self ._faces
1383
+ needs_masking = np .any (self ._invalid_vertices )
1384
+ num_faces = len (self ._faces )
1385
+ mask = self ._invalid_vertices
1345
1386
1346
1387
# Some faces might contain masked vertices, so we want to ignore any
1347
1388
# errors that those might cause
1348
1389
with np .errstate (invalid = 'ignore' , divide = 'ignore' ):
1349
- pfaces = proj3d ._proj_transform_vectors (self ._faces , self .axes .M )
1350
-
1351
- if self ._axlim_clip :
1352
- viewlim_mask = _viewlim_mask (self ._faces [..., 0 ], self ._faces [..., 1 ],
1353
- self ._faces [..., 2 ], self .axes )
1354
- if np .any (viewlim_mask ):
1355
- needs_masking = True
1356
- mask = mask | viewlim_mask
1357
-
1390
+ pfaces = proj3d ._proj_transform_vectors (pfaces , self .axes .M )
1358
1391
pzs = pfaces [..., 2 ]
1359
1392
if needs_masking :
1360
1393
pzs = np .ma .MaskedArray (pzs , mask = mask )
@@ -1385,8 +1418,7 @@ def do_3d_projection(self):
1385
1418
if self ._codes3d is not None and len (self ._codes3d ) > 0 :
1386
1419
if needs_masking :
1387
1420
segment_mask = ~ mask [face_order , :]
1388
- faces_2d = [face [mask , :] for face , mask
1389
- in zip (faces_2d , segment_mask )]
1421
+ faces_2d = [face [mask , :] for face , mask in zip (faces_2d , segment_mask )]
1390
1422
codes = [self ._codes3d [idx ] for idx in face_order ]
1391
1423
PolyCollection .set_verts_and_codes (self , faces_2d , codes )
1392
1424
else :
0 commit comments