Repository: pyvista/pyvista

Issue:
`cast_to_unstructured_grid` not working for `PointSet`
### Describe the bug, what's wrong, and what you expected.

See code below. The output type is the same as the input type. The output type is expected to be an unstructured grid.

### Steps to reproduce the bug.

``` python
import pyvista as pv
from pyvista import examples
mesh = examples.download_cloud_dark_matter()
print(type(mesh))  # <class 'pyvista.core.pointset.PointSet'>
grid = mesh.cast_to_unstructured_grid()
print(type(grid))  # <class 'pyvista.core.pointset.PointSet'>
```

### System Information

```shell
pyvista : 0.44.dev0
               vtk : 9.3.0
             numpy : 1.26.4
        matplotlib : 3.8.2
            scooby : 0.9.2
             pooch : 1.8.0
            pillow : 10.2.0
           imageio : 2.34.0
           IPython : 8.21.0
          colorcet : 3.1.0
           cmocean : 3.1.3
        ipywidgets : 8.1.2
             scipy : 1.12.0
              tqdm : 4.66.2
            meshio : 5.3.5
        jupyterlab : 4.1.4
    pytest_pyvista : 0.1.8
             trame : 3.5.3
      trame_client : 2.16.1
      trame_server : 2.17.2
         trame_vtk : 2.8.5
     trame_vuetify : 2.4.3
      nest_asyncio : 1.6.0
```


### Screenshots

_No response_

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pyvista/pyvista/pull/6127?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista) Report
Attention: Patch coverage is `50.00000%` with `1 lines` in your changes are missing coverage. Please review.
> Project coverage is 90.58%. Comparing base [(`4d4d896`)](https://app.codecov.io/gh/pyvista/pyvista/commit/4d4d89645a8af8367b0833675bf98219d59169ec?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista) to head [(`0c4a6e4`)](https://app.codecov.io/gh/pyvista/pyvista/commit/0c4a6e45cecaa95303d635c59a253267ee391e57?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pyvista).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #6127      +/-   ##
==========================================
- Coverage   96.99%   90.58%   -6.42%     
==========================================
  Files         141      141              
  Lines       24667    24669       +2     
==========================================
- Hits        23926    22346    -1580     
- Misses        741     2323    +1582     
```

</details>
tkoyama010: @pyvista-bot preview
pyvista-bot: <!-- NETLIFY DEPLOY COMMENT GENERATED BY ACTIONS_NETLIFY - APP ID SHA256: a13cb84fb117b1fbb38c5e9d3853a95b804288edd848742e6f4a409c37f2648c -->
🚀 Deployed on https://6655b96f3247fcfea2200a3c--meek-duckanoo-d2f5ec.netlify.app

Patch:
diff --git a/pyvista/core/pointset.py b/pyvista/core/pointset.py
index 994a42cf..a3375c5c 100644
--- a/pyvista/core/pointset.py
+++ b/pyvista/core/pointset.py
@@ -347,6 +347,33 @@ class PointSet(_vtk.vtkPointSet, _PointSet):
                 pdata.point_data[key] = value
         return pdata
 
+    def cast_to_unstructured_grid(self) -> pyvista.UnstructuredGrid:
+        """Cast this dataset to :class:`pyvista.UnstructuredGrid`.
+
+        A deep copy of the points and point data is made.
+
+        Returns
+        -------
+        pyvista.UnstructuredGrid
+            Dataset cast to a :class:`pyvista.UnstructuredGrid`.
+
+        Examples
+        --------
+        Cast a :class:`pyvista.PointSet` to a
+        :class:`pyvista.UnstructuredGrid`.
+
+        >>> import pyvista as pv
+        >>> from pyvista import examples
+        >>> mesh = examples.download_cloud_dark_matter()
+        >>> type(mesh)
+        <class 'pyvista.core.pointset.PointSet'>
+        >>> grid = mesh.cast_to_unstructured_grid()
+        >>> type(grid)
+        <class 'pyvista.core.pointset.UnstructuredGrid'>
+
+        """
+        return self.cast_to_polydata(deep=False).cast_to_unstructured_grid()
+
     @wraps(DataSet.plot)
     def plot(self, *args, **kwargs):
         """Cast to PolyData and plot."""


Test patch:
diff --git a/tests/core/test_pointset.py b/tests/core/test_pointset.py
index 4402d7aa..373318d0 100644
--- a/tests/core/test_pointset.py
+++ b/tests/core/test_pointset.py
@@ -111,6 +111,19 @@ def test_cast_to_polydata(pointset, deep):
         assert np.allclose(pdata.point_data[key], pointset.point_data[key])
 
 
+def test_cast_to_unstructured_grid(pointset):
+    data = np.linspace(0, 1, pointset.n_points)
+    key = 'key'
+    pointset.point_data[key] = data
+
+    pdata = pointset.cast_to_unstructured_grid()
+    assert isinstance(pdata, pv.UnstructuredGrid)
+    assert key in pdata.point_data
+    assert np.allclose(pdata.point_data[key], pointset.point_data[key])
+    pdata.point_data[key][:] = 0
+    assert not np.allclose(pdata.point_data[key], pointset.point_data[key])
+
+
 def test_filters_return_pointset(sphere):
     pointset = sphere.cast_to_pointset()
     clipped = pointset.clip()