code: def SolidSphereGeneric(
      radius=None,
      theta=None,
      phi=None,
      center=(0.0, 0.0, 0.0),
      direction=(0.0, 0.0, 1.0),
      radians=False,
      tol_radius=1.0e-8,
      tol_angle=None,
  ):
      """Create a solid sphere with flexible sampling.
  
      A solid sphere fills space in 3D in comparison to
      :func:`pyvista.Sphere`, which is a 2D surface.
  
      This function allows user defined sampling of each spherical
      coordinate, whereas :func:`pyvista.SolidSphere`
      only allows linear sampling.   Angles are by default
      specified in degrees.
  
      PyVista uses a convention where ``theta`` represents the azimuthal
      angle (similar to degrees longitude on the globe) and ``phi``
      represents the polar angle (similar to degrees latitude on the
      globe). In contrast to latitude on the globe, here
      ``phi`` is 0 degrees at the North Pole and 180 degrees at the South
      Pole. ``phi=0`` is on the positive z-axis by default.
      ``theta=0`` is on the positive x-axis by default.
  
      Parameters
      ----------
      radius : sequence[float], optional
          A monotonically increasing sequence of values specifying radial
          points. Must have at least two points and be non-negative.
  
      theta : sequence[float], optional
          A monotonically increasing sequence of values specifying ``theta``
          points. Must have at least two points.  Can have any value as long
          as range is within 360 degrees. Large magnitudes may result in
          problems with endpoint overlap detection.
  
      phi : sequence[float], optional
          A monotonically increasing sequence of values specifying ``phi``
          points. Must have at least two points.  Must be between
          0 and 180 degrees.
  
      center : sequence[float], default: (0.0, 0.0, 0.0)
          Center coordinate vector in ``[x, y, z]``.
  
      direction : sequence[float], default: (0.0, 0.0, 1.0)
          Direction coordinate vector in ``[x, y, z]`` pointing from ``center`` to
          the sphere's north pole at zero degrees ``phi``.
  
      radians : bool, default: False
          Whether to use radians for ``theta`` and ``phi``. Default is degrees.
  
      tol_radius : float, default: 1.0e-8
          Absolute tolerance for endpoint detection for ``radius``.
  
      tol_angle : float, optional
          Absolute tolerance for endpoint detection
          for ``phi`` and ``theta``. Unit is determined by choice
          of ``radians`` parameter.  Default is 1.0e-8 degrees or
          1.0e-8 degrees converted to radians.
  
      Returns
      -------
      pyvista.UnstructuredGrid
          Solid sphere mesh.
  
      See Also
      --------
      pyvista.SolidSphere: Sphere creation using linear sampling.
      pyvista.Sphere: Sphere that describes outer 2D surface.
  
      Examples
      --------
      Linearly sampling spherical coordinates does not lead to
      cells of all the same size at each radial position.
      Cells near the poles have smaller sizes.
  
      >>> import pyvista as pv
      >>> import numpy as np
      >>> solid_sphere = pv.SolidSphereGeneric(
      ...     radius=np.linspace(0, 0.5, 2),
      ...     theta=np.linspace(180, 360, 30),
      ...     phi=np.linspace(0, 180, 30),
      ... )
      >>> solid_sphere = solid_sphere.compute_cell_sizes()
      >>> solid_sphere.plot(
      ...     scalars="Volume", show_edges=True, clim=[3e-5, 5e-4]
      ... )
  
      Sampling the polar angle in a nonlinear manner allows for consistent cell volumes.  See
      `Sphere Point Picking <https://mathworld.wolfram.com/SpherePointPicking.html>`_.
  
      >>> phi = np.rad2deg(np.arccos(np.linspace(1, -1, 30)))
      >>> solid_sphere = pv.SolidSphereGeneric(
      ...     radius=np.linspace(0, 0.5, 2),
      ...     theta=np.linspace(180, 360, 30),
      ...     phi=phi,
      ... )
      >>> solid_sphere = solid_sphere.compute_cell_sizes()
      >>> solid_sphere.plot(
      ...     scalars="Volume", show_edges=True, clim=[3e-5, 5e-4]
      ... )
  
      """
      if radius is None:
          radius = np.linspace(0, 0.5, 5)
      radius = np.asanyarray(radius)
  
      # Default tolerance from user is set in degrees
      # But code is in radians.
      if tol_angle is None:
          tol_angle = np.deg2rad(1e-8)
      elif not radians:
          tol_angle = np.deg2rad(tol_angle)
  
      if theta is None:
          theta = np.linspace(0, 2 * np.pi, 30)
      else:
          theta = np.asanyarray(theta) if radians else np.deg2rad(theta)
  
      if phi is None:
          phi = np.linspace(0, np.pi, 30)
      else:
          phi = np.asanyarray(phi) if radians else np.deg2rad(phi)
  
      # Hereafter all degrees are in radians
      # radius, phi, theta are now np.ndarrays
  
      nr = len(radius)
      ntheta = len(theta)
      nphi = len(phi)
  
      if nr < 2:
          raise ValueError("radius resolution must be 2 or more")
      if ntheta < 2:
          raise ValueError("theta resolution must be 2 or more")
      if nphi < 2:
          raise ValueError("phi resolution must be 2 or more")
  
      def _is_sorted(a):
          return np.all(a[:-1] < a[1:])
  
      if not _is_sorted(radius):
          raise ValueError("radius is not monotonically increasing")
      if not _is_sorted(theta):
          raise ValueError("theta is not monotonically increasing")
      if not _is_sorted(phi):
          raise ValueError("phi is not monotonically increasing")
  
      def _greater_than_equal_or_close(value1, value2, atol):
          return value1 >= value2 or np.isclose(
              value1, value2, rtol=0.0, atol=atol
          )
  
      def _less_than_equal_or_close(value1, value2, atol):
          return value1 <= value2 or np.isclose(
              value1, value2, rtol=0.0, atol=atol
          )
  
      if not _greater_than_equal_or_close(radius[0], 0.0, tol_radius):
          raise ValueError("minimum radius cannot be negative")
  
      # range of theta cannot be greater than 360 degrees
      if not _less_than_equal_or_close(
          theta[-1] - theta[0], 2 * np.pi, tol_angle
      ):
          max_angle = "2 * np.pi" if radians else "360 degrees"
          raise ValueError(
              f"max theta and min theta must be within {max_angle}"
          )
  
      if not _greater_than_equal_or_close(phi[0], 0.0, tol_angle):
          raise ValueError("minimum phi cannot be negative")
      if not _less_than_equal_or_close(phi[-1], np.pi, tol_angle):
          max_angle = "np.pi" if radians else "180 degrees"
          raise ValueError(f"maximum phi cannot be > {max_angle}")
  
      def _spherical_to_cartesian(r, phi, theta):
          """Convert spherical coordinate sequences to a ``(n,3)`` Cartesian coordinate array.
  
          Parameters
          ----------
          r : sequence[float]
              Ordered sequence of floats of radii.
          phi : sequence[float]
              Ordered sequence of floats for phi direction.
          theta : sequence[float]
              Ordered sequence of floats for theta direction.
  
          Returns
          -------
          np.ndarray
              ``(n, 3)`` Cartesian coordinate array.
  
          """
          r, phi, theta = np.meshgrid(r, phi, theta, indexing="ij")
          x, y, z = pyvista.spherical_to_cartesian(r, phi, theta)
          return np.vstack(
              (x.ravel(), y.ravel(), z.ravel())
          ).transpose()
  
      points = []
  
      npoints_on_axis = 0
  
      if np.isclose(radius[0], 0.0, rtol=0.0, atol=tol_radius):
          points.append([0.0, 0.0, 0.0])
          include_origin = True
          nr = nr - 1
          radius = radius[1:]
          npoints_on_axis += 1
      else:
          include_origin = False
  
      if np.isclose(
          theta[-1] - theta[0], 2 * np.pi, rtol=0.0, atol=tol_angle
      ):
          duplicate_theta = True
          theta = theta[:-1]
      else:
          duplicate_theta = False
  
      if np.isclose(phi[0], 0.0, rtol=0.0, atol=tol_angle):
          points.extend(_spherical_to_cartesian(radius, 0.0, theta[0]))
          positive_axis = True
          phi = phi[1:]
          nphi = nphi - 1
          npoints_on_axis += nr
      else:
          positive_axis = False
      npoints_on_pos_axis = npoints_on_axis
  
      if np.isclose(phi[-1], np.pi, rtol=0.0, atol=tol_angle):
          points.extend(
              _spherical_to_cartesian(radius, np.pi, theta[0])
          )
          negative_axis = True
          phi = phi[:-1]
          nphi = nphi - 1
          npoints_on_axis += nr
      else:
          negative_axis = False
  
      # rest of points with theta changing quickest
      for ir, iphi in product(radius, phi):
          points.extend(_spherical_to_cartesian(ir, iphi, theta))
  
      cells = []
      celltypes = []
  
      def _index(ir, iphi, itheta):
          """Index for points not on axis.
  
          Values of ir and phi here are relative to the first nonaxis values.
          """
          if duplicate_theta:
              ntheta_ = ntheta - 1
              itheta = itheta % ntheta_
          else:
              ntheta_ = ntheta
  
          return (
              npoints_on_axis
              + ir * nphi * ntheta_
              + iphi * ntheta_
              + itheta
          )
  
      if include_origin:
          # First make the tetras that form with origin and axis point
          #   origin is 0
          #   first axis point is 1
          #   other points at first phi position off axis
          if positive_axis:
              for itheta in range(ntheta - 1):
                  cells.append(4)
                  cells.extend(
                      [
                          0,
                          1,
                          _index(0, 0, itheta),
                          _index(0, 0, itheta + 1),
                      ]
                  )
                  celltypes.append(pyvista.CellType.TETRA)
  
          # Next tetras that form with origin and bottom axis point
          #   origin is 0
          #   axis point is first in negative dir
          #   other points at last phi position off axis
          if negative_axis:
              for itheta in range(ntheta - 1):
                  cells.append(4)
                  cells.extend(
                      [
                          0,
                          npoints_on_pos_axis,
                          _index(0, nphi - 1, itheta + 1),
                          _index(0, nphi - 1, itheta),
                      ]
                  )
                  celltypes.append(pyvista.CellType.TETRA)
  
          # Pyramids that form to origin but without an axis point
          for iphi, itheta in product(
              range(nphi - 1), range(ntheta - 1)
          ):
              cells.append(5)
              cells.extend(
                  [
                      _index(0, iphi, itheta),
                      _index(0, iphi, itheta + 1),
                      _index(0, iphi + 1, itheta + 1),
                      _index(0, iphi + 1, itheta),
                      0,
                  ]
              )
              celltypes.append(pyvista.CellType.PYRAMID)
  
      # Wedges form between two r levels at first and last phi position
      #   At each r level, the triangle is formed with axis point,  two theta positions
      # First go upwards
      if positive_axis:
          for ir, itheta in product(range(nr - 1), range(ntheta - 1)):
              axis0 = ir + 1 if include_origin else ir
              axis1 = ir + 2 if include_origin else ir + 1
              cells.append(6)
              cells.extend(
                  [
                      axis0,
                      _index(ir, 0, itheta + 1),
                      _index(ir, 0, itheta),
                      axis1,
                      _index(ir + 1, 0, itheta + 1),
                      _index(ir + 1, 0, itheta),
                  ]
              )
              celltypes.append(pyvista.CellType.WEDGE)
  
      # now go downwards
      if negative_axis:
          for ir, itheta in product(range(nr - 1), range(ntheta - 1)):
              axis0 = npoints_on_pos_axis + ir
              axis1 = npoints_on_pos_axis + ir + 1
              cells.append(6)
              cells.extend(
                  [
                      axis0,
                      _index(ir, nphi - 1, itheta),
                      _index(ir, nphi - 1, itheta + 1),
                      axis1,
                      _index(ir + 1, nphi - 1, itheta),
                      _index(ir + 1, nphi - 1, itheta + 1),
                  ]
              )
              celltypes.append(pyvista.CellType.WEDGE)
  
      # Form Hexahedra
      # Hexahedra form between two r levels and two phi levels and two theta levels
      #   Order by r levels
      for ir, iphi, itheta in product(
          range(nr - 1), range(nphi - 1), range(ntheta - 1)
      ):
          cells.append(8)
          cells.extend(
              [
                  _index(ir, iphi, itheta),
                  _index(ir, iphi + 1, itheta),
                  _index(ir, iphi + 1, itheta + 1),
                  _index(ir, iphi, itheta + 1),
                  _index(ir + 1, iphi, itheta),
                  _index(ir + 1, iphi + 1, itheta),
                  _index(ir + 1, iphi + 1, itheta + 1),
                  _index(ir + 1, iphi, itheta + 1),
              ]
          )
          celltypes.append(pyvista.CellType.HEXAHEDRON)
  
      mesh = pyvista.UnstructuredGrid(cells, celltypes, points)
      mesh.rotate_y(90, inplace=True)
      translate(mesh, center, direction)
      return mesh
  
imports: from itertools import product
  import numpy
  import pyvista
  from pyvista.core import _vtk_core
  from .arrays import _coerce_pointslike_arg
  from .geometric_sources import ConeSource, CylinderSource, MultipleLinesSource, Text3DSource, translate
  from .helpers import wrap
  from .misc import check_valid_vector
label_desc: Use image node_id 6 for calling a global function with example usage: SolidSphereGeneric(radius, theta, phi, center, direction, radians, tol_radius, tol_angle) and returns: mesh, np, unknown, unknown, np, unknown