Incorrect drawing order when plotting polygons in separate Poly3DCollections

There is a bug when using multiple Poly3DCollections which can result in incorrect drawing order so that a polygon that should be on top of another one is actually displayed underneath. The script below shows a minimal example.

The example script creates two figure windows. In the first one three triangles are drawn using a single Poly3DCollection. This works as expected. In the second one the triangles are drawn using two separate Poly3DCollections. In this case the green triangle (from the second collection) is displayed underneath the red one (from the first collection) even though it should be on top of it. Interestingly, the bug disappears if the blue triangle (which does not overlap with the green one) is removed from the first collection.

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection


def create_figure():
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    ax.set_xlim(-130, 130)
    ax.set_ylim(-130, 130)
    ax.set_zlim(0, 100)
    return fig


# Red triangle
T1 = [[-55.9, 47.0, 50],
      [23.2, 52.5, 50],
      [-7.3, -55.4, 50]]

# Blue triangle
T2 = [[81.3, -54.1, 50],
      [87.7, -96.6, 50],
      [60.6, -96.9, 50]]

# Green triangle
T3 = [[68.6, -64.2, 70],
      [31.3, -64.8, 70],
      [51.8, -23.3, 70]]


# Add all triangles in a single collection. This works as expected.
fig = create_figure()
coll = Poly3DCollection([T1, T2, T3], facecolors=['r', 'b', 'g'], edgecolors=['r', 'b', 'g'])
fig.gca().add_collection(coll)
fig.gca().set_title("Correct behaviour")


# Add triangles in two separate collections. This exposes a bug where
# the green triangle is displayed underneath the red one even though
# it should be on top of it. Note, however, that if we omit the blue
# triangle (T2) from the first collection then the bug disappears.
fig = create_figure()
coll1 = Poly3DCollection([T1, T2], facecolors=['r', 'b'], edgecolors=['r', 'b'])
coll2 = Poly3DCollection([T3], facecolors=['g'], edgecolors=['g'])
fig.gca().add_collection(coll1)
fig.gca().add_collection(coll2)
fig.gca().set_title("Buggy behaviour (but works if blue triangle is removed)")

plt.show()

Author: Fantashit

1 thought on “Incorrect drawing order when plotting polygons in separate Poly3DCollections

  1. This is a separate issue, and it is a fundamental limitation of what I call the 2.1D layering engine that matplotlib uses. The layering engine needs to sort the collections and independent artists according to a single z-order value (the “.1” of 2.1D layering). So, while some of the 3D collections are implemented to layer themselves properly (for the most part) by specially ordering the draws of their own elements on the fly (at a huge performance hit), they are completely unaware of any other artists and their spatial relationship to their own drawing elements. This leads to “Escher-esque” visual effects (they don’t make spatial sense).

    A necessary, but not sufficient condition for this effect is that the 3D bounding boxes of artists intersect. There are some controls to help alleviate this. A collection object can either report a minimum, a maximum, or an average zsort value for itself. It defaults to minimum. This is why removing the one triangle “fixed” it.

    This is a known limitation, and addressed in the documentation. Don’t use mplot3d for complex scenes. There are better tools for that such as mayavi. So, I will close this issue.

Comments are closed.