Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 61550af

Browse files
Add a ax.voxels(bool3d) function
See matplotlib#6404 for screenshots
1 parent 4e1792b commit 61550af

File tree

1 file changed

+97
-0
lines changed

1 file changed

+97
-0
lines changed

‎lib/mpl_toolkits/mplot3d/axes3d.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2649,6 +2649,103 @@ def calc_arrow(uvw, angle=15):
26492649

26502650
quiver3D = quiver
26512651

2652+
def voxels(self, filled, color=None):
2653+
"""
2654+
Plot a set of filled voxels
2655+
2656+
All voxels are plotted as 1x1x1 cubes on the axis, with filled[0,0,0]
2657+
placed with its lower corner at the origin. Occluded faces are not plotted
2658+
2659+
Parameters
2660+
----------
2661+
filled : np.array
2662+
A 3d array of values, with truthy values indicating which voxels
2663+
to fill
2664+
2665+
color : array_like
2666+
Either a single value or an array the same shape as filled,
2667+
indicating what color to draw the faces of the voxels. If None,
2668+
plot all voxels in the same color, the next in the color sequence
2669+
"""
2670+
# check dimensions, and deal with a single color
2671+
if filled.ndim != 3:
2672+
raise ValueError("Argument filled must be 3-dimensional")
2673+
2674+
if color is None:
2675+
color = next(self._get_patches_for_fill.prop_cycler)['color']
2676+
if np.ndim(color) <= 1:
2677+
color, _ = np.broadcast_arrays(
2678+
color,
2679+
filled[np.index_exp[...] + np.index_exp[np.newaxis] * np.ndim(color)]
2680+
)
2681+
elif np.ndim(color) < 3:
2682+
raise ValueError("Argument color must be at least 3-dimensional")
2683+
elif np.shape(color)[:3] != filled.shape:
2684+
raise ValueError("Argument color must match the shape of filled, if multidimensional")
2685+
2686+
self.auto_scale_xyz(
2687+
[0, filled.shape[0]],
2688+
[0, filled.shape[1]],
2689+
[0, filled.shape[2]]
2690+
)
2691+
2692+
2693+
# points lying on corners of a square
2694+
square = np.array([
2695+
[0, 0, 0],
2696+
[0, 1, 0],
2697+
[1, 1, 0],
2698+
[1, 0, 0]
2699+
])
2700+
2701+
def boundary_found(corners, color):
2702+
""" Plot a square at corners, with the specificed color """
2703+
poly = art3d.Poly3DCollection([corners])
2704+
poly.set_facecolor(color)
2705+
self.add_collection3d(poly)
2706+
2707+
def permutation_matrices(n):
2708+
""" Generator of cyclic permutation matices """
2709+
mat = np.eye(n)
2710+
for i in range(n):
2711+
yield mat
2712+
mat = np.roll(mat, 1, axis=0)
2713+
2714+
for permute in permutation_matrices(3):
2715+
# find the set of ranges to iterate over
2716+
pc, qc, rc = permute.T.dot(filled.shape[:3])
2717+
pinds = np.arange(pc)
2718+
qinds = np.arange(qc)
2719+
rinds = np.arange(rc)
2720+
2721+
for p in pinds:
2722+
for q in qinds:
2723+
# draw lower faces
2724+
p0 = permute.dot([p, q, 0])
2725+
i0 = tuple(p0)
2726+
if filled[i0]:
2727+
boundary_found(p0 + square.dot(permute.T), color[i0])
2728+
2729+
# draw middle faces
2730+
for r1, r2 in zip(rinds, rinds[1:]):
2731+
p1 = permute.dot([p, q, r1])
2732+
p2 = permute.dot([p, q, r2])
2733+
2734+
i1 = tuple(p1)
2735+
i2 = tuple(p2)
2736+
2737+
if filled[i1] and not filled[i2]:
2738+
boundary_found(p2 + square.dot(permute.T), color[i1])
2739+
elif not filled[i1] and filled[i2]:
2740+
boundary_found(p2 + square.dot(permute.T), color[i2])
2741+
2742+
# draw upper faces
2743+
pk = permute.dot([p, q, rc-1])
2744+
pk2 = permute.dot([p, q, rc])
2745+
ik = tuple(pk)
2746+
if filled[ik]:
2747+
boundary_found(pk2 + square.dot(permute.T), color[ik])
2748+
26522749

26532750
def get_test_data(delta=0.05):
26542751
'''

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /