code: def plot_surface(
      pos_history,
      canvas=None,
      title="Trajectory",
      designer=None,
      mesher=None,
      animator=None,
      mark=None,
      n_processes=None,
      **kwargs,
  ):
      """Plot a swarm's trajectory in 3D
  
      This is useful for plotting the swarm's 2-dimensional position with
      respect to the objective function. The value in the z-axis is the fitness
      of the 2D particle when passed to the objective function. When preparing the
      position history, make sure that the:
  
      * first column is the position in the x-axis,
      * second column is the position in the y-axis; and
      * third column is the fitness of the 2D particle
  
      The :class:`pyswarms.utils.plotters.formatters.Mesher` class provides a
      method that prepares this history given a 2D pos history from any
      optimizer.
  
      .. code-block:: python
  
          import pyswarms as ps
          from pyswarms.utils.functions.single_obj import sphere
          from pyswarms.utils.plotters import plot_surface
          from pyswarms.utils.plotters.formatters import Mesher
  
          # Run optimizer
          options = {'c1':0.5, 'c2':0.3, 'w':0.9}
          optimizer = ps.single.GlobalBestPSO(n_particles=10, dimensions=2, options)
  
          # Prepare position history
          m = Mesher(func=sphere)
          pos_history_3d = m.compute_history_3d(optimizer.pos_history)
  
          # Plot!
          plot_surface(pos_history_3d)
  
      Parameters
      ----------
      pos_history : numpy.ndarray
          Position history of the swarm with shape
          :code:`(iteration, n_particles, 3)`
      objective_func : callable
          The objective function that takes a swarm of shape
          :code:`(n_particles, 2)` and returns a fitness array
          of :code:`(n_particles, )`
      canvas : (:obj:`matplotlib.figure.Figure`, :obj:`matplotlib.axes.Axes`),
          The (figure, axis) where all the events will be draw. If :code:`None`
          is supplied, then plot will be drawn to a fresh set of canvas.
      title : str, optional
          The title of the plotted graph. Default is `Trajectory`
      mark : tuple, optional
          Marks a particular point with a red crossmark. Useful for marking the
          optima.
      designer : :obj:`pyswarms.utils.formatters.Designer`, optional
          Designer class for custom attributes
      mesher : :obj:`pyswarms.utils.formatters.Mesher`, optional
          Mesher class for mesh plots
      animator : :obj:`pyswarms.utils.formatters.Animator`, optional
          Animator class for custom animation
      n_processes : int
          number of processes to use for parallel mesh point calculation (default: None = no parallelization)
      **kwargs : dict
          Keyword arguments that are passed as a keyword argument to
          :class:`matplotlib.axes.Axes` plotting function
  
      Returns
      -------
      :class:`matplotlib.animation.FuncAnimation`
          The drawn animation that can be saved to mp4 or other
          third-party tools
      """
      try:
          # If no Designer class supplied, use defaults
          if designer is None:
              designer = Designer(
                  limits=[(-1, 1), (-1, 1), (-1, 1)],
                  label=["x-axis", "y-axis", "z-axis"],
                  colormap=cm.viridis,
              )
  
          # If no Animator class supplied, use defaults
          if animator is None:
              animator = Animator()
  
          # Get number of iterations
          # If ax is default, then create new plot. Set-up the figure, the
          # axis, and the plot element that we want to animate
          if canvas is None:
              fig, ax = plt.subplots(1, 1, figsize=designer.figsize)
          else:
              fig, ax = canvas
  
          # Initialize 3D-axis
          ax = Axes3D(fig)
  
          n_iters = len(pos_history)
  
          # Customize plot
          ax.set_title(title, fontsize=designer.title_fontsize)
          ax.set_xlabel(
              designer.label[0], fontsize=designer.text_fontsize
          )
          ax.set_ylabel(
              designer.label[1], fontsize=designer.text_fontsize
          )
          ax.set_zlabel(
              designer.label[2], fontsize=designer.text_fontsize
          )
          ax.set_xlim(designer.limits[0])
          ax.set_ylim(designer.limits[1])
          ax.set_zlim(designer.limits[2])
  
          # Make a contour map if possible
          if mesher is not None:
              (xx, yy, zz) = _mesh(mesher, n_processes=n_processes)
              ax.plot_surface(
                  xx, yy, zz, cmap=designer.colormap, alpha=mesher.alpha
              )
  
          # Mark global best if possible
          if mark is not None:
              ax.scatter(
                  mark[0], mark[1], mark[2], color="red", marker="x"
              )
  
          # Put scatter skeleton
          plot = ax.scatter(
              xs=[], ys=[], zs=[], c="black", alpha=0.6, **kwargs
          )
  
          # Do animation
          anim = animation.FuncAnimation(
              fig=fig,
              func=_animate,
              frames=range(n_iters),
              fargs=(pos_history, plot),
              interval=animator.interval,
              repeat=animator.repeat,
              repeat_delay=animator.repeat_delay,
          )
      except TypeError:
          rep.logger.exception("Please check your input type")
          raise
      else:
          return anim
  
file_path: pyswarms/pyswarms/utils/plotters/plotters.py
imports: import logging
  import matplotlib.pyplot
  import numpy
  import multiprocessing
  from matplotlib import animation, cm
  from mpl_toolkits.mplot3d import Axes3D
  from ..reporter import Reporter
  from .formatters import Animator, Designer
label_desc: Use image node_id 3 for calling a global function with example usage: plot_surface(pos_history, canvas, title, designer, mesher, animator, mark, n_processes) and returns: anim