Repository: pyvista/pyvista

Issue:
Icosphere Primitive
### Describe the feature you would like to be added.

I am new to pyvista and I am just exploring the different features.

It would be great if pyvista would support a quick way to create an icosphere (an icohedron projected into a sphere). See this image for an example.  The image is taken from https://en.wikipedia.org/wiki/Geodesic_polyhedron

![image](https://user-images.githubusercontent.com/10503349/233863624-cedfc26d-3e75-4597-a2c0-8b3703b485de.png)


Discussion:
codecov[bot]: ## [Codecov](https://codecov.io/gh/pyvista/pyvista/pull/4330?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista) Report
> Merging [#4330](https://codecov.io/gh/pyvista/pyvista/pull/4330?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista) (dc290b3) into [main](https://codecov.io/gh/pyvista/pyvista/commit/b9ee9b9c0625d737dfd4a60e9ed10609e7e0b7fd?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista) (b9ee9b9) will **decrease** coverage by `5.30%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main    #4330      +/-   ##
==========================================
- Coverage   95.78%   90.49%   -5.30%     
==========================================
  Files          97       97              
  Lines       20773    20781       +8     
==========================================
- Hits        19897    18805    -1092     
- Misses        876     1976    +1100     
```


Patch:
diff --git a/doc/source/api/utilities/geometric.rst b/doc/source/api/utilities/geometric.rst
index 567b36ef..63185562 100644
--- a/doc/source/api/utilities/geometric.rst
+++ b/doc/source/api/utilities/geometric.rst
@@ -21,9 +21,9 @@ additional details see :ref:`ref_geometric_example` example.
    Disc
    Dodecahedron
    Icosahedron
+   Icosphere
    Line
    MultipleLines
-   Tube
    Octahedron
    Plane
    PlatonicSolid
@@ -36,4 +36,5 @@ additional details see :ref:`ref_geometric_example` example.
    Tetrahedron
    Text3D
    Triangle
+   Tube
    Wavelet
diff --git a/pyvista/core/filters/poly_data.py b/pyvista/core/filters/poly_data.py
index d25aa8b3..6b28c8bf 100644
--- a/pyvista/core/filters/poly_data.py
+++ b/pyvista/core/filters/poly_data.py
@@ -1199,6 +1199,7 @@ class PolyDataFilters(DataSetFilters):
             )
 
         # Subdivide
+        sfilter.SetCheckForTriangles(False)  # we already check for this
         sfilter.SetNumberOfSubdivisions(nsub)
         sfilter.SetInputData(self)
         _update_alg(sfilter, progress_bar, 'Subdividing Mesh')
diff --git a/pyvista/utilities/geometric_objects.py b/pyvista/utilities/geometric_objects.py
index 61c997d1..5ce775f5 100644
--- a/pyvista/utilities/geometric_objects.py
+++ b/pyvista/utilities/geometric_objects.py
@@ -347,6 +347,10 @@ def Sphere(
     pyvista.PolyData
         Sphere mesh.
 
+    See Also
+    --------
+    pyvista.Icosphere
+
     Examples
     --------
     Create a sphere using default parameters.
@@ -1730,3 +1734,76 @@ def Icosahedron(radius=1.0, center=(0.0, 0.0, 0.0)):
 
     """
     return PlatonicSolid(kind='icosahedron', radius=radius, center=center)
+
+
+def Icosphere(radius=1.0, center=(0.0, 0.0, 0.0), nsub=3):
+    """Create an icosphere.
+
+    An icosphere is a `geodesic polyhedron
+    <https://en.wikipedia.org/wiki/Geodesic_polyhedron>`_, which is a
+    convex polyhedron made from triangles.
+
+    Geodesic polyhedra are constructed by subdividing faces of simpler
+    polyhedra, and then projecting the new vertices onto the surface of
+    a sphere. A geodesic polyhedron has straight edges and flat faces
+    that approximate a sphere,
+
+    Parameters
+    ----------
+    radius : float, default: 1.0
+        Radius of the icosphere.
+
+    center : sequence[float], default: (0.0, 0.0, 0.0)
+        Center of the icosphere.
+
+    nsub : int, default: 3
+        This is the number of times each triangle of the original
+        :func:`pyvista.Icosahedron` is subdivided.
+
+    Returns
+    -------
+    pyvista.PolyData
+        Mesh of the icosphere.
+
+    See Also
+    --------
+    pyvista.Sphere
+
+    Examples
+    --------
+    Create the icosphere and plot it with edges.
+
+    >>> import pyvista as pv
+    >>> icosphere = pv.Icosphere()
+    >>> icosphere.plot(show_edges=True)
+
+    Show how this icosphere was created.
+
+    >>> import numpy as np
+    >>> icosahedron = pv.Icosahedron()
+    >>> icosahedron.clear_data()  # remove extra scalars
+    >>> icosahedron_sub = icosahedron.subdivide(nsub=3)
+    >>> pl = pv.Plotter(shape=(1, 3))
+    >>> _ = pl.add_mesh(icosahedron, show_edges=True)
+    >>> pl.subplot(0, 1)
+    >>> _ = pl.add_mesh(icosahedron_sub, show_edges=True)
+    >>> pl.subplot(0, 2)
+    >>> _ = pl.add_mesh(icosphere, show_edges=True)
+    >>> pl.show()
+
+    Show how the triangles are not uniform in area. This is because the
+    ones farther from the edges from the original triangles have farther
+    to travel to the sphere.
+
+    >>> icosphere = pv.Icosphere(nsub=4)
+    >>> icosphere.compute_cell_sizes().plot(scalars='Area')
+
+    """
+    mesh = Icosahedron()
+    mesh.clear_data()
+    mesh = mesh.subdivide(nsub=nsub)
+
+    # scale to desired radius and translate origin
+    dist = np.linalg.norm(mesh.points, axis=1, keepdims=True)  # distance from origin
+    mesh.points = mesh.points * (radius / dist) + center
+    return mesh


Test patch:
diff --git a/tests/test_geometric_objects.py b/tests/test_geometric_objects.py
index 81b1df71..7fb4ee66 100644
--- a/tests/test_geometric_objects.py
+++ b/tests/test_geometric_objects.py
@@ -449,3 +449,15 @@ def test_icosahedron():
 
     doubled_solid = pyvista.Icosahedron(radius=2 * radius, center=center)
     assert np.isclose(doubled_solid.length, 2 * solid.length)
+
+
+def test_icosphere():
+    center = (1.0, 2.0, 3.0)
+    radius = 2.4
+    nsub = 2
+    icosphere = pyvista.Icosphere(radius=radius, center=center, nsub=nsub)
+    assert np.allclose(icosphere.center, center)
+    assert np.allclose(np.linalg.norm(icosphere.points - icosphere.center, axis=1), radius)
+
+    icosahedron = pyvista.Icosahedron()
+    assert icosahedron.n_faces * 4**nsub == icosphere.n_faces