@@ -2746,8 +2746,10 @@ def calc_arrow(uvw, angle=15):
2746
2746
2747
2747
quiver3D = quiver
2748
2748
2749
- def voxels (self , filled , ** kwargs ):
2749
+ def voxels (self , * args , ** kwargs ):
2750
2750
"""
2751
+ ax.voxels([x, y, z,] /, filled, **kwargs)
2752
+
2751
2753
Plot a set of filled voxels
2752
2754
2753
2755
All voxels are plotted as 1x1x1 cubes on the axis, with filled[0,0,0]
@@ -2758,10 +2760,16 @@ def voxels(self, filled, **kwargs):
2758
2760
2759
2761
Parameters
2760
2762
----------
2761
- filled : np.array
2763
+ filled : 3D np.array of bool
2762
2764
A 3d array of values, with truthy values indicating which voxels
2763
2765
to fill
2764
2766
2767
+ x, y, z : 3D np.array, optional
2768
+ The coordinates of the corners of the voxels. This should broadcast
2769
+ to a shape one larger in every dimension than the shape of `filled`.
2770
+ These arguments can be used to plot non-cubic voxels.
2771
+ If not specified, defaults to increasing integers along each axis.
2772
+
2765
2773
facecolors, edgecolors : array_like
2766
2774
The color to draw the faces and edges of the voxels. This parameter
2767
2775
can be:
@@ -2791,10 +2799,33 @@ def voxels(self, filled, **kwargs):
2791
2799
2792
2800
.. plot:: gallery/mplot3d/voxels.py
2793
2801
.. plot:: gallery/mplot3d/voxels_rgb.py
2802
+ .. plot:: gallery/mplot3d/voxels_torus.py
2803
+ .. plot:: gallery/mplot3d/voxels_numpy_logo.py
2794
2804
"""
2805
+
2806
+ # parse optional x y z arguments
2807
+ if len (args ) >= 3 :
2808
+ xyz , args = args [:3 ], args [3 :]
2809
+ else :
2810
+ xyz = None
2811
+
2812
+ # clever trick to produce the right error message and handle argument
2813
+ # names. `str` is needed to circumvent unicode_literals on py2
2814
+ _get_args = (lambda filled , ** kwargs : (filled , kwargs ))
2815
+ _get_args .__name__ = str ('voxels' )
2816
+ filled , kwargs = _get_args (* args , ** kwargs )
2817
+
2795
2818
# check dimensions
2796
2819
if filled .ndim != 3 :
2797
2820
raise ValueError ("Argument filled must be 3-dimensional" )
2821
+ size = np .array (filled .shape , dtype = np .intp )
2822
+
2823
+ # check xyz coordinates, which are one larger than the filled shape
2824
+ coord_shape = tuple (size + 1 )
2825
+ if xyz is None :
2826
+ x , y , z = np .indices (coord_shape )
2827
+ else :
2828
+ x , y , z = (broadcast_to (c , coord_shape ) for c in xyz )
2798
2829
2799
2830
def _broadcast_color_arg (color , name ):
2800
2831
if np .ndim (color ) in (0 , 1 ):
@@ -2821,25 +2852,21 @@ def _broadcast_color_arg(color, name):
2821
2852
edgecolors = _broadcast_color_arg (edgecolors , 'edgecolors' )
2822
2853
2823
2854
# always scale to the full array, even if the data is only in the center
2824
- self .auto_scale_xyz (
2825
- [0 , filled .shape [0 ]],
2826
- [0 , filled .shape [1 ]],
2827
- [0 , filled .shape [2 ]]
2828
- )
2855
+ self .auto_scale_xyz (x , y , z )
2829
2856
2830
2857
# points lying on corners of a square
2831
2858
square = np .array ([
2832
2859
[0 , 0 , 0 ],
2833
2860
[0 , 1 , 0 ],
2834
2861
[1 , 1 , 0 ],
2835
2862
[1 , 0 , 0 ]
2836
- ])
2863
+ ], dtype = np . intp )
2837
2864
2838
2865
voxel_faces = defaultdict (list )
2839
2866
2840
2867
def permutation_matrices (n ):
2841
2868
""" Generator of cyclic permutation matices """
2842
- mat = np .eye (n , dtype = int )
2869
+ mat = np .eye (n , dtype = np . intp )
2843
2870
for i in range (n ):
2844
2871
yield mat
2845
2872
mat = np .roll (mat , 1 , axis = 0 )
@@ -2848,7 +2875,7 @@ def permutation_matrices(n):
2848
2875
# render
2849
2876
for permute in permutation_matrices (3 ):
2850
2877
# find the set of ranges to iterate over
2851
- pc , qc , rc = permute .T .dot (filled . shape [: 3 ] )
2878
+ pc , qc , rc = permute .T .dot (size )
2852
2879
pinds = np .arange (pc )
2853
2880
qinds = np .arange (qc )
2854
2881
rinds = np .arange (rc )
@@ -2890,7 +2917,20 @@ def permutation_matrices(n):
2890
2917
2891
2918
# iterate over the faces, and generate a Poly3DCollection for each voxel
2892
2919
polygons = {}
2893
- for coord , faces in voxel_faces .items ():
2920
+ for coord , faces_inds in voxel_faces .items ():
2921
+ # convert indices into 3D positions
2922
+ if xyz is None :
2923
+ faces = faces_inds
2924
+ else :
2925
+ faces = []
2926
+ for face_inds in faces_inds :
2927
+ ind = face_inds [:, 0 ], face_inds [:, 1 ], face_inds [:, 2 ]
2928
+ face = np .empty (face_inds .shape )
2929
+ face [:, 0 ] = x [ind ]
2930
+ face [:, 1 ] = y [ind ]
2931
+ face [:, 2 ] = z [ind ]
2932
+ faces .append (face )
2933
+
2894
2934
poly = art3d .Poly3DCollection (faces ,
2895
2935
facecolors = facecolors [coord ],
2896
2936
edgecolors = edgecolors [coord ],
0 commit comments