@@ -124,6 +124,30 @@ def _viewlim_mask(xs, ys, zs, axes):
124124 return mask
125125
126126
127+ def _apply_viewlim_mask_with_copy (xs , ys , zs , axes ):
128+ """
129+ Mask points outside view limits by setting to nan. Allocates new arrays.
130+
131+ Parameters
132+ ----------
133+ xs, ys, zs : array-like
134+ The points to mask. These should be in data coordinates.
135+ axes : Axes3D
136+ The axes to use for the view limits.
137+
138+ Returns
139+ -------
140+ xs, ys, zs : array-like
141+ The coordinates with out-of-bounds points set to nan.
142+ """
143+ mask = _viewlim_mask (xs , ys , zs , axes )
144+ if np .any (mask ):
145+ xs = np .where (mask , np .nan , xs )
146+ ys = np .where (mask , np .nan , ys )
147+ zs = np .where (mask , np .nan , zs )
148+ return xs , ys , zs
149+
150+
127151class Text3D (mtext .Text ):
128152 """
129153 Text object with 3D position and direction.
@@ -222,7 +246,7 @@ def draw(self, renderer):
222246 if self ._axlim_clip :
223247 mask = _viewlim_mask (self ._x , self ._y , self ._z , self .axes )
224248 if np .any (mask ):
225- pos3d = np .where ( mask , np . nan , pos3d )
249+ pos3d [ mask ] = np .nan
226250
227251 # Apply scale transforms before projection
228252 pos3d_scaled = np .array (_apply_scale_transforms (
@@ -362,11 +386,8 @@ def get_data_3d(self):
362386 def draw (self , renderer ):
363387 xs3d , ys3d , zs3d = self ._verts3d
364388 if self ._axlim_clip :
365- mask = _viewlim_mask (xs3d , ys3d , zs3d , self .axes )
366- if np .any (mask ):
367- xs3d = np .where (mask , np .nan , xs3d )
368- ys3d = np .where (mask , np .nan , ys3d )
369- zs3d = np .where (mask , np .nan , zs3d )
389+ xs3d , ys3d , zs3d = _apply_viewlim_mask_with_copy (xs3d , ys3d , zs3d ,
390+ self .axes )
370391 # Apply scale transforms before projection
371392 xs3d , ys3d , zs3d = _apply_scale_transforms (xs3d , ys3d , zs3d , self .axes )
372393 xs , ys , zs , tis = proj3d ._proj_transform_clip (xs3d , ys3d , zs3d ,
@@ -650,23 +671,16 @@ def get_path(self):
650671
651672 def do_3d_projection (self ):
652673 s = self ._segment3d
653- mask = False
654674 xs , ys , zs = zip (* s )
655675 if self ._axlim_clip :
656- mask = _viewlim_mask (xs , ys , zs , self .axes )
657- if np .any (mask ):
658- xs = np .where (mask , np .nan , xs )
659- ys = np .where (mask , np .nan , ys )
660- zs = np .where (mask , np .nan , zs )
676+ xs , ys , zs = _apply_viewlim_mask_with_copy (xs , ys , zs , self .axes )
661677 # Apply scale transforms before projection
662678 xs , ys , zs = _apply_scale_transforms (xs , ys , zs , self .axes )
663679 vxs , vys , vzs , vis = proj3d ._proj_transform_clip (xs , ys , zs ,
664680 self .axes .M ,
665681 self .axes ._focal_length )
666682 self ._path2d = mpath .Path (np .column_stack ([vxs , vys ]))
667- if mask is False :
668- return np .min (vzs )
669- return np .min (vzs [~ mask ])
683+ return np .nanmin (vzs )
670684
671685
672686class PathPatch3D (Patch3D ):
@@ -718,24 +732,16 @@ def set_3d_properties(self, path, zs=0, zdir='z', axlim_clip=False):
718732
719733 def do_3d_projection (self ):
720734 s = self ._segment3d
721- mask = False
722735 xs , ys , zs = zip (* s )
723736 if self ._axlim_clip :
724- mask = _viewlim_mask (xs , ys , zs , self .axes )
725- if np .any (mask ):
726- xs = np .where (mask , np .nan , xs )
727- ys = np .where (mask , np .nan , ys )
728- zs = np .where (mask , np .nan , zs )
737+ xs , ys , zs = _apply_viewlim_mask_with_copy (xs , ys , zs , self .axes )
729738 # Apply scale transforms before projection
730739 xs , ys , zs = _apply_scale_transforms (xs , ys , zs , self .axes )
731740 vxs , vys , vzs , vis = proj3d ._proj_transform_clip (xs , ys , zs ,
732741 self .axes .M ,
733742 self .axes ._focal_length )
734743 self ._path2d = mpath .Path (np .column_stack ([vxs , vys ]), self ._code3d )
735-
736- if mask is False :
737- return np .min (vzs )
738- return np .min (vzs [~ mask ])
744+ return np .nanmin (vzs )
739745
740746
741747def _get_patch_verts (patch ):
@@ -872,13 +878,8 @@ def set_3d_properties(self, zs, zdir, axlim_clip=False):
872878
873879 def do_3d_projection (self ):
874880 xs , ys , zs = self ._offsets3d
875- mask = False
876881 if self ._axlim_clip :
877- mask = _viewlim_mask (xs , ys , zs , self .axes )
878- if np .any (mask ):
879- xs = np .where (mask , np .nan , xs )
880- ys = np .where (mask , np .nan , ys )
881- zs = np .where (mask , np .nan , zs )
882+ xs , ys , zs = _apply_viewlim_mask_with_copy (xs , ys , zs , self .axes )
882883 # Apply scale transforms before projection
883884 xs , ys , zs = _apply_scale_transforms (xs , ys , zs , self .axes )
884885 vxs , vys , vzs , vis = proj3d ._proj_transform_clip (xs , ys , zs ,
@@ -887,12 +888,7 @@ def do_3d_projection(self):
887888 self ._vzs = vzs
888889 super ().set_offsets (np .column_stack ([vxs , vys ]))
889890
890- if vzs .size > 0 :
891- if mask is False :
892- return np .min (vzs )
893- return np .min (vzs [~ mask ])
894- else :
895- return np .nan
891+ return np .nanmin (vzs ) if vzs .size > 0 else np .nan
896892
897893 def _maybe_depth_shade_and_sort_colors (self , color_array ):
898894 color_array = (
0 commit comments