The purpose of the code is to use arrow3D to initiate X, Y, Z axis on a 3D plane. The parameters of arrow3D are x, y, z, which default to zero; and dx, dy, dz, which is the length of X, Y, Z axis. That way, I could setup my window on 3D plane. So in the code below, arrow3D inherits FancyArrowPatch and has its attributes _xyz and _dxdydz.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.proj3d import proj_transform
from mpl_toolkits.mplot3d.axes3d import Axes3D
from matplotlib.text import Annotation
from matplotlib.patches import FancyArrowPatch
# X axis, Y axis, and Z axis should be added into axes like other artists
# class Arrow3D is created for them
# Arrow3D inherits FancyArrowPatch, since those axes are like arrows
class Arrow3D(FancyArrowPatch):
def __init__(self, x, y, z, dx, dy, dz, *args, **kwargs):
super().__init__((0,0), (0,0), *args, **kwargs)
self._xyz = (x,y,z)
self._dxdydz = (dx,dy,dz)
def draw(self, renderer):
x1,y1,z1 = self._xyz
dx,dy,dz = self._dxdydz
x2,y2,z2 = (x1+dx,y1+dy,z1+dz)
xs, ys, zs = proj_transform((x1,x2),(y1,y2),(z1,z2), renderer.M)
self.set_positions((xs[0],ys[0]),(xs[1],ys[1]))
super().draw(renderer)
def _arrow3D(ax, x, y, z, dx, dy, dz, *args, **kwargs):
'''Add an 3d arrow to an `Axes3D` instance.'''
arrow = Arrow3D(x, y, z, dx, dy, dz, *args, **kwargs)
ax.add_artist(arrow)
# an object of class Axes3D will have an attribute-'arrow3D', which is the function _arrow3D
# this object could be axes, when it is created by passing the projection='3d' keyword argument to Figure.add_subplot
setattr(Axes3D,'arrow3D',_arrow3D)
# an object of class Axes3D will have an attribute-'arrow3D', which is the function _arrow3D
# this object could be axes, when it is created by passing the projection='3d' keyword argument to Figure.add_subplot
setattr(Axes3D,'arrow3D',_arrow3D)
class Annotation3D(Annotation):
def __init__(self, text, xyz, *args, **kwargs):
super().__init__(text, xy=(0,0), *args, **kwargs)
self._xyz = xyz
def draw(self, renderer):
x2, y2, z2 = proj_transform(*self._xyz, renderer.M)
self.xy=(x2,y2)
super().draw(renderer)
def _annotate3D(ax,text, xyz, *args, **kwargs):
'''Add anotation `text` to an `Axes3d` instance.'''
annotation= Annotation3D(text, xyz, *args, **kwargs)
ax.add_artist(annotation)
setattr(Axes3D,'annotate3D',_annotate3D)
With arrow3D defined, I then define the _axesSetup so that my 3D background could be set.
import arrow3d
from mpl_toolkits.mplot3d.axes3d import Axes3D
from matplotlib.text import Annotation
from matplotlib.patches import FancyArrowPatch
import numpy as np
def _axesSetup(ax, lX, uX, lY, uY, lZ, uZ):
# set xlimit, draw x axis, annotate x axis
# x axis
ax.set_xlim(lX, uX)
ax.arrow3D(lX, 0, 0, uX - lX, 0, 0, mutation_scale=15, arrowstyle="-|>", color='red')
ax.annotate3D('X', (uX, 0, 0), xytext=(0, 0), textcoords=('offset points'), color = 'xkcd:eggplant', weight="bold")
# y axis
ax.set_ylim(lY, uY)
ax.arrow3D(0, lY, 0, 0, uY - lY, 0, mutation_scale=15, arrowstyle="-|>", color='red')
ax.annotate3D('Y', (0, uY, 0), xytext=(0, 0), textcoords=('offset points'), color = 'xkcd:eggplant', weight="bold")
# z axis
ax.set_zlim(lZ, uZ)
ax.arrow3D(0, 0, lZ, 0, 0, uZ - lZ, mutation_scale=15, arrowstyle="-|>", color='red')
ax.annotate3D('Z', (0, 0, uZ), xytext=(0, 0), textcoords=('offset points'), color = 'xkcd:eggplant', weight="bold")
setattr(Axes3D, 'axesSetup', _axesSetup )
# setattr create 'axesSetup' attribute for class Axes3D if 'axesSetup' is not found
# an object of class Axes3D is the axes projected in 3D
It used to work, but now it shows nothing. Some other post already figured out it's due to Matplotlib's upgrade. Could someone help me to modify my code to make it work?