'Create a surface plot.
By default it will be colored in shades of a solid color,
but it also supports color mapping by supplying the *cmap*
argument.
The `rstride` and `cstride` kwargs set the stride used to
sample the input data to generate the graph.  If 1k by 1k
arrays are passed in, the default values for the strides will
result in a 100x100 grid being plotted. Defaults to 10.
Raises a ValueError if both stride and count kwargs
(see next section) are provided.
The `rcount` and `ccount` kwargs supersedes `rstride` and
`cstride` for default sampling method for surface plotting.
These arguments will determine at most how many evenly spaced
samples will be taken from the input data to generate the graph.
This is the default sampling method unless using the \'classic\'
style. Will raise ValueError if both stride and count are
specified.
Added in v2.0.0.
Argument      Description
*X*, *Y*, *Z* Data values as 2D arrays
*rstride*     Array row stride (step size)
*cstride*     Array column stride (step size)
*rcount*      Use at most this many rows, defaults to 50
*ccount*      Use at most this many columns, defaults to 50
*color*       Color of the surface patches
*cmap*        A colormap for the surface patches.
*facecolors*  Face colors for the individual patches
*norm*        An instance of Normalize to map values to colors
*vmin*        Minimum value to map
*vmax*        Maximum value to map
*shade*       Whether to shade the facecolors
Other arguments are passed on to
:class:`~mpl_toolkits.mplot3d.art3d.Poly3DCollection`'

def plot_surface(self, X, Y, Z, *args, **kwargs):

    had_data = self.has_data()
    if (Z.ndim != 2):
        raise ValueError(u'Argument    Z    must    be    2-dimensional.')
    (X, Y, Z) = np.broadcast_arrays(X, Y, Z)
    (rows, cols) = Z.shape
    has_stride = ((u'rstride' in kwargs) or (u'cstride' in kwargs))
    has_count = ((u'rcount' in kwargs) or (u'ccount' in kwargs))
    if (has_stride and has_count):
        raise ValueError(u'Cannot    specify    both    stride    and    count    arguments')
    rstride = kwargs.pop(u'rstride', 10)
    cstride = kwargs.pop(u'cstride', 10)
    rcount = kwargs.pop(u'rcount', 50)
    ccount = kwargs.pop(u'ccount', 50)
    if rcParams[u'_internal.classic_mode']:
        if has_count:
            rstride = int(np.ceil((rows / rcount)))
            cstride = int(np.ceil((cols / ccount)))
    elif (not has_stride):
        rstride = int(np.ceil((rows / rcount)))
        cstride = int(np.ceil((cols / ccount)))
    if (u'facecolors' in kwargs):
        fcolors = kwargs.pop(u'facecolors')
    else:
        color = kwargs.pop(u'color', None)
        if (color is None):
            color = self._get_lines.get_next_color()
        color = np.array(mcolors.to_rgba(color))
        fcolors = None
    cmap = kwargs.get(u'cmap', None)
    norm = kwargs.pop(u'norm', None)
    vmin = kwargs.pop(u'vmin', None)
    vmax = kwargs.pop(u'vmax', None)
    linewidth = kwargs.get(u'linewidth', None)
    shade = kwargs.pop(u'shade', (cmap is None))
    lightsource = kwargs.pop(u'lightsource', None)
    if (shade and (cmap is not None) and (fcolors is not None)):
        fcolors = self._shade_colors_lightsource(Z, cmap, lightsource)
    polys = []
    if ((cmap is None) and shade):
        totpts = int((np.ceil((float((rows - 1)) / rstride)) * np.ceil((float((cols - 1)) / cstride))))
        v1 = np.empty((totpts, 3))
        v2 = np.empty((totpts, 3))
        which_pt = 0
    colset = []
    for rs in xrange(0, (rows - 1), rstride):
        for cs in xrange(0, (cols - 1), cstride):
            ps = []
            for a in (X, Y, Z):
                ztop = a[rs, cs:min(cols, ((cs + cstride) + 1))]
                zleft = a[(rs + 1):min(rows, ((rs + rstride) + 1)), min((cols - 1), (cs + cstride))]
                zbase = a[min((rows - 1), (rs + rstride)), cs:min(cols, ((cs + cstride) + 1)):None][::(-1)]
                zright = a[rs:min((rows - 1), (rs + rstride)):None, cs][::(-1)]
                z = np.concatenate((ztop, zleft, zbase, zright))
                ps.append(z)
            ps = list(zip(*ps))
            lastp = np.array([])
            ps2 = ([ps[0]] + [ps[i] for i in xrange(1, len(ps)) if (ps[i] != ps[(i - 1)])])
            avgzsum = sum((p[2] for p in ps2))
            polys.append(ps2)
            if (fcolors is not None):
                colset.append(fcolors[rs][cs])
            else:
                colset.append((avgzsum / len(ps2)))
            if ((cmap is None) and shade):
                (i1, i2, i3) = (0, int((len(ps2) / 3)), int(((2 * len(ps2)) / 3)))
                v1[which_pt] = (np.array(ps2[i1]) - np.array(ps2[i2]))
                v2[which_pt] = (np.array(ps2[i2]) - np.array(ps2[i3]))
                which_pt += 1
    if ((cmap is None) and shade):
        normals = np.cross(v1, v2)
    else:
        normals = []
    polyc = art3d.Poly3DCollection(polys, *args, **kwargs)
    if (fcolors is not None):
        if shade:
            colset = self._shade_colors(colset, normals)
        polyc.set_facecolors(colset)
        polyc.set_edgecolors(colset)
    elif cmap:
        colset = np.array(colset)
        polyc.set_array(colset)
        if ((vmin is not None) or (vmax is not None)):
            polyc.set_clim(vmin, vmax)
        if (norm is not None):
            polyc.set_norm(norm)
    else:
        if shade:
            colset = self._shade_colors(color, normals)
        else:
            colset = color
        polyc.set_facecolors(colset)
    self.add_collection(polyc)
    self.auto_scale_xyz(X, Y, Z, had_data)
    return polyc
