Repository: pyvista/pyvista

Issue:
Add `spherical_transform` function
### Describe the feature you would like to be added.

`SphericalTransform` will convert (r,phi,theta) coordinates to (x,y,z) coordinates and back again. The equation that is used is $x = r \sin(phi) \cos(theta)$ , $y = r \sin(phi) \sin(theta)$ , $z = r \cos(phi)$ .

_Originally posted by @tkoyama010 in https://github.com/pyvista/pyvista/pull/4965#discussion_r1341733386_

### Links to VTK Documentation, Examples, or Class Definitions.

https://vtk.org/doc/nightly/html/classvtkSphericalTransform.html#details


### Pseudocode or Screenshots

_No response_

Discussion:
tkoyama010: pre-commit.ci autofix
tkoyama010: @denizkirbiyik 
Thank you for the PR. This is a very nice addition. We need to add testing to this. Mention me if you need help.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pyvista/pyvista/pull/5003?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista) Report
> Merging [#5003](https://app.codecov.io/gh/pyvista/pyvista/pull/5003?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista) (fea3a10) into [main](https://app.codecov.io/gh/pyvista/pyvista/commit/46a2d12051cf1fd6923477526ec98aed0bdde6b6?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista) (46a2d12) will **decrease** coverage by `3.13%`.
> The diff coverage is `16.66%`.

```diff
@@            Coverage Diff             @@
##             main    #5003      +/-   ##
==========================================
- Coverage   95.99%   92.87%   -3.13%     
==========================================
  Files         131      131              
  Lines       21624    21630       +6     
==========================================
- Hits        20758    20088     -670     
- Misses        866     1542     +676     
```

tkoyama010: pre-commit.ci autofix

Patch:
diff --git a/pyvista/core/utilities/__init__.py b/pyvista/core/utilities/__init__.py
index 4bdc8add..5af82b09 100644
--- a/pyvista/core/utilities/__init__.py
+++ b/pyvista/core/utilities/__init__.py
@@ -30,6 +30,7 @@ from .features import (
     merge,
     perlin_noise,
     sample_function,
+    spherical_to_cartesian,
     transform_vectors_sph_to_cart,
     voxelize,
 )
diff --git a/pyvista/core/utilities/features.py b/pyvista/core/utilities/features.py
index 59fa1c0a..f8b1856d 100644
--- a/pyvista/core/utilities/features.py
+++ b/pyvista/core/utilities/features.py
@@ -247,6 +247,34 @@ def cartesian_to_spherical(x, y, z):
     return r, phi, theta
 
 
+def spherical_to_cartesian(r, phi, theta):
+    """Convert Spherical coordinates to 3D Cartesian coordinates.
+
+    Parameters
+    ----------
+    r : numpy.ndarray
+        Radial distance.
+
+    phi : numpy.ndarray
+        Angle (radians) with respect to the polar axis. Also known
+        as polar angle.
+
+    theta : numpy.ndarray
+        Angle (radians) of rotation from the initial meridian plane.
+        Also known as azimuthal angle.
+
+    Returns
+    -------
+    numpy.ndarray, numpy.ndarray, numpy.ndarray
+        Cartesian coordinates.
+    """
+    s = np.sin(phi)
+    x = r * s * np.cos(theta)
+    y = r * s * np.sin(theta)
+    z = r * np.cos(phi)
+    return x, y, z
+
+
 def merge(
     datasets,
     merge_points=True,


Test patch:
diff --git a/tests/core/test_utilities.py b/tests/core/test_utilities.py
index 2798c283..06ee808f 100644
--- a/tests/core/test_utilities.py
+++ b/tests/core/test_utilities.py
@@ -758,6 +758,13 @@ def test_cartesian_to_spherical():
     assert np.allclose(polar2cart(r, phi, theta), points)
 
 
+def test_spherical_to_cartesian():
+    points = np.random.random((1000, 3))
+    r, phi, theta = points.T
+    x, y, z = pv.spherical_to_cartesian(r, phi, theta)
+    assert np.allclose(pv.cartesian_to_spherical(x, y, z), points.T)
+
+
 def test_set_pickle_format():
     pv.set_pickle_format('legacy')
     assert pv.PICKLE_FORMAT == 'legacy'