Repository: pyvista/pyvista

Issue:
pyvista.Plane results in approximate coordinates, slightly off prescribed ones
### Describe the bug, what's wrong, and what you expected.

I want to mesh a rectangle in the cartesian y-z plane. Along the y-direction the rectangle has size `length` and along the z-direction it has size `height`. When I use `pyvista.Plane` to mesh the rectangle, the smallest y-coordinates of the points are not exactly what they are supposed to be, and they're slightly off. I believe this issue is similar to #4134 .

### Steps to reproduce the bug.

```python
import pyvista as pv
import numpy as np

y_0 = 0.  
length = 29380/18
height = 770.
no_elements = [2, 6]
plane1 = pv.Plane(center=[0, y_0 + length/2, 0], direction=[-1, 0, 0], i_size=height,
                  j_size=length, i_resolution=no_elements[0], j_resolution=no_elements[1])
print(np.sort(plane1.points[:, 1]))

y_0 = length
print(y_0)

plane2 = pv.Plane(center=[0, y_0 + length/2, 0], direction=[-1, 0, 0], i_size=height,
                  j_size=length, i_resolution=no_elements[0], j_resolution=no_elements[1])
print(np.sort(plane2.points[:, 1]))
```

This code returns the following:
```raw
[2.7126736e-05 2.7126736e-05 2.7126736e-05 2.7203702e+02 2.7203702e+02
 2.7203702e+02 5.4407404e+02 5.4407404e+02 5.4407404e+02 8.1611108e+02
 8.1611108e+02 8.1611108e+02 1.0881482e+03 1.0881482e+03 1.0881482e+03
 1.3601852e+03 1.3601852e+03 1.3601852e+03 1.6322222e+03 1.6322222e+03
 1.6322222e+03]
1632.2222222222222
[1632.2223 1632.2223 1632.2223 1904.2593 1904.2593 1904.2593 2176.2964
 2176.2964 2176.2964 2448.3333 2448.3333 2448.3333 2720.3704 2720.3704
 2720.3704 2992.4075 2992.4075 2992.4075 3264.4443 3264.4443 3264.4443]
```

The first elements of the first array should be exactly 0, and the first elements of the second array should be approximated to 1632.2222, NOT to 1632.2223.

What is happening here?

### System Information

```shell
--------------------------------------------------------------------------------
  Date: Mon Feb 12 14:35:13 2024 GMT Standard Time

                OS : Windows
            CPU(s) : 12
           Machine : AMD64
      Architecture : 64bit
               RAM : 31.8 GiB
       Environment : Python
        GPU Vendor : Intel
      GPU Renderer : Intel(R) UHD Graphics 630
       GPU Version : 4.5.0 - Build 30.0.101.1692
  MathText Support : False

  Python 3.9.17 (main, Jul  5 2023, 20:47:11) [MSC v.1916 64 bit (AMD64)]

           pyvista : 0.43.3
               vtk : 9.0.3
             numpy : 1.23.5
        matplotlib : 3.7.1
            scooby : 0.7.1
             pooch : Trouble importing
            pillow : 9.4.0
           imageio : 2.26.0
         pyvistaqt : 0.9.1
             PyQt5 : 5.15.7
           IPython : 8.10.0
        ipywidgets : 8.0.4
             scipy : 1.10.0
      nest_asyncio : 1.5.6

  Intel(R) oneAPI Math Kernel Library Version 2021.4-Product Build 20210904
  for Intel(R) 64 architecture applications
--------------------------------------------------------------------------------
```


### Screenshots

_No response_

Discussion:

Patch:
diff --git a/pyvista/core/filters/rectilinear_grid.py b/pyvista/core/filters/rectilinear_grid.py
index 2c996e08..f5650209 100644
--- a/pyvista/core/filters/rectilinear_grid.py
+++ b/pyvista/core/filters/rectilinear_grid.py
@@ -108,8 +108,8 @@ class RectilinearGridFilters:
             # See: https://gitlab.kitware.com/vtk/vtk/-/issues/18650
             if 1 in self.dimensions and tetra_per_cell == 12:  # type: ignore
                 raise RuntimeError(
-                    'Cannot split cells into 12 tetrahedrals when at least '  # type: ignore
-                    f'one dimension is 1. Dimensions are {self.dimensions}.'
+                    'Cannot split cells into 12 tetrahedrals when at least '
+                    f'one dimension is 1. Dimensions are {self.dimensions}.'  # type: ignore[attr-defined]
                 )
 
             alg.SetTetraPerCell(tetra_per_cell)
diff --git a/pyvista/core/utilities/geometric_sources.py b/pyvista/core/utilities/geometric_sources.py
index 344056ad..b5c36fe3 100644
--- a/pyvista/core/utilities/geometric_sources.py
+++ b/pyvista/core/utilities/geometric_sources.py
@@ -58,7 +58,7 @@ def translate(surf, center=(0.0, 0.0, 0.0), direction=(1.0, 0.0, 0.0)):
 
     surf.transform(trans)
     if not np.allclose(center, [0.0, 0.0, 0.0]):
-        surf.points += np.array(center)
+        surf.points += np.array(center, dtype=surf.points.dtype)
 
 
 @no_new_attr


Test patch:
diff --git a/tests/core/test_geometric_sources.py b/tests/core/test_geometric_sources.py
index f9509764..b8ce55a3 100644
--- a/tests/core/test_geometric_sources.py
+++ b/tests/core/test_geometric_sources.py
@@ -77,6 +77,26 @@ def test_translate_direction_collinear(is_negative, delta, bunny):
         assert np.allclose(points_in[:, 2], points_out[:, 2])
 
 
+def test_translate_precision():
+    """
+    Test that specifying a 64bit float as an arg, will not
+    introduce precision error for 32bit meshes.
+    """
+    val = np.float64(29380 / 18)
+
+    # test indirectly using Plane, which will yield a float32 mesh
+    mesh = pv.Plane(center=[0, val / 2, 0], j_size=val, i_resolution=1, j_resolution=1)
+    assert mesh.points.dtype == np.float32
+
+    expected = np.array(
+        [[-0.5, 0.0, 0.0], [0.5, 0.0, 0.0], [-0.5, 1632.2222, 0.0], [0.5, 1632.2222, 0.0]],
+        dtype=np.float32,
+    )
+
+    # DO NOT USE np.all_close. This should match exactly
+    assert np.array_equal(mesh.points, expected)
+
+
 def test_text3d_source_empty_string():
     # Test empty string is processed to have a single point
     src = pv.Text3DSource(process_empty_string=True)