Repository: pyvista/pyvista

Issue:
Some texture mapping examples are broken in the documentation
The examples starting from the first puppy example [here](https://docs.pyvista.org/examples/02-plot/texture.html#applying-textures) are missing the actual texture. The only texture that's visible on the rest of the page is the last one with a collection of puppies.

Current puppy figure:

![sphx_glr_texture_004](https://user-images.githubusercontent.com/17914410/96180931-7a079c80-0f33-11eb-9c00-2469564a16ba.png)

An older version of the same figure from a local doc build of mine:

![sphx_glr_texture_003](https://user-images.githubusercontent.com/17914410/96181156-d8cd1600-0f33-11eb-8641-54ac93df9e75.png)

---

I've now found https://github.com/pyvista/pyvista/pull/853 so I guess it's a known bug, but I couldn't find a duplicate issue so I'll leave this here for the time being.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/pyvista/pyvista/pull/973?src=pr&el=h1) Report
> Merging [#973](https://codecov.io/gh/pyvista/pyvista/pull/973?src=pr&el=desc) into [master](https://codecov.io/gh/pyvista/pyvista/commit/071fa97770a6b07575f4e7a3cc6fd164f7a6213e?el=desc) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master     #973   +/-   ##
=======================================
  Coverage   86.80%   86.80%           
=======================================
  Files          36       36           
  Lines        9144     9145    +1     
=======================================
+ Hits         7937     7938    +1     
  Misses       1207     1207           
```


Patch:
diff --git a/examples/00-load/wrap-trimesh.py b/examples/00-load/wrap-trimesh.py
new file mode 100644
index 00000000..e166ca2f
--- /dev/null
+++ b/examples/00-load/wrap-trimesh.py
@@ -0,0 +1,51 @@
+"""
+.. _ref_wrap_trimesh:
+
+Wrapping Other Objects
+~~~~~~~~~~~~~~~~~~~~~~
+You can wrap several other object types using pyvista including:
+
+- `numpy` arrays
+- `trimesh.Trimesh` meshes
+- VTK objects
+
+This allows for the "best of both worlds" programming special to
+Python due to its modularity.  If there's some limitation of pyvista
+(or trimesh), then you can adapt your scripts to use the best features
+of more than one module.
+
+"""
+import pyvista as pv
+
+###############################################################################
+# Wrap a point cloud composed of random points from numpy
+import numpy as np
+points = np.random.random((30, 3))
+cloud = pv.wrap(points)
+pv.plot(cloud, scalars=points[:, 2], render_points_as_spheres=True, point_size=50,
+        opacity=points[:, 0], cpos='xz')
+
+###############################################################################
+# Wrap an instance of Trimesh 
+import trimesh
+points = [[0, 0, 0], [0, 0, 1], [0, 1, 0]]
+faces = [[0, 1, 2]]
+tmesh = trimesh.Trimesh(points, faces=faces, process=False)
+mesh = pv.wrap(tmesh)
+print(mesh)
+
+###############################################################################
+# Wrap an instance of vtk.vtkPolyData
+
+import vtk
+points = vtk.vtkPoints()
+p = [1.0, 2.0, 3.0]
+vertices = vtk.vtkCellArray()
+pid = points.InsertNextPoint(p)
+vertices.InsertNextCell(1)
+vertices.InsertCellPoint(pid)
+point = vtk.vtkPolyData()
+point.SetPoints(points)
+point.SetVerts(vertices)
+mesh = pv.wrap(point)
+print(mesh)
diff --git a/examples/01-filter/distance-between-surfaces.py b/examples/01-filter/distance-between-surfaces.py
index b35e9d65..25edeeae 100644
--- a/examples/01-filter/distance-between-surfaces.py
+++ b/examples/01-filter/distance-between-surfaces.py
@@ -20,9 +20,10 @@ import numpy as np
 
 # A helper to make a random surface
 def hill(seed):
-    mesh = pv.ParametricRandomHills(randomseed=seed,
+    mesh = pv.ParametricRandomHills(randomseed=seed, u_res=50, v_res=50,
                                     hillamplitude=0.5)
-    mesh.rotate_y(80) # give the surfaces some tilt
+    mesh.rotate_y(-10) # give the surfaces some tilt
+    
     return mesh
 
 h0 = hill(1).elevation()
@@ -34,8 +35,8 @@ h1 = h1.elevation()
 ###############################################################################
 
 p = pv.Plotter()
-p.add_mesh(h0)
-p.add_mesh(h1)
+p.add_mesh(h0, smooth_shading=True)
+p.add_mesh(h1, smooth_shading=True)
 p.show_grid()
 p.show()
 
@@ -45,7 +46,7 @@ p.show()
 #
 # Compute normals of lower surface
 h0n = h0.compute_normals(point_normals=True, cell_normals=False,
-                         auto_orient_normals=True, )
+                         auto_orient_normals=True)
 
 ###############################################################################
 # Travel along noramals to the other surface and compute the thickness on each
@@ -68,8 +69,8 @@ np.nanmean(h0n["distances"])
 
 ###############################################################################
 p = pv.Plotter()
-p.add_mesh(h0n, scalars="distances")
-p.add_mesh(h1, color=True, opacity=0.75)
+p.add_mesh(h0n, scalars="distances", smooth_shading=True)
+p.add_mesh(h1, color=True, opacity=0.75, smooth_shading=True)
 p.show()
 
 
@@ -90,6 +91,6 @@ np.mean(d)
 
 ###############################################################################
 p = pv.Plotter()
-p.add_mesh(h0, scalars="distances")
-p.add_mesh(h1, color=True, opacity=0.75)
+p.add_mesh(h0, scalars="distances", smooth_shading=True)
+p.add_mesh(h1, color=True, opacity=0.75, smooth_shading=True)
 p.show()
diff --git a/pyvista/core/common.py b/pyvista/core/common.py
index 4f9ff825..81710611 100644
--- a/pyvista/core/common.py
+++ b/pyvista/core/common.py
@@ -77,7 +77,10 @@ class DataObject:
         reader = self._READERS[file_ext]()
         reader.SetFileName(str(file_path))
         reader.Update()
-        self.shallow_copy(reader.GetOutput())
+        return reader.GetOutputDataObject(0)
+
+    def _from_file(self, filename):
+        self.shallow_copy(self._load_file(filename))
 
     def save(self, filename, binary=True):
         """Save this vtk object to file.
@@ -182,7 +185,7 @@ class DataObject:
         This includes header details and information about all arrays.
 
         """
-        raise NotImplemented('Called only by the inherited class')
+        raise NotImplementedError('Called only by the inherited class')
 
     def copy_meta_from(self, ido):  # pragma: no cover
         """Copy pyvista meta data onto this object from another object."""
diff --git a/pyvista/core/composite.py b/pyvista/core/composite.py
index 20720610..96e18b30 100644
--- a/pyvista/core/composite.py
+++ b/pyvista/core/composite.py
@@ -80,7 +80,7 @@ class MultiBlock(vtkMultiBlockDataSet, CompositeFilters, DataObject):
                 for block in args[0]:
                     self.append(block)
             elif isinstance(args[0], (str, pathlib.Path)):
-                self._load_file(args[0])
+                self._from_file(args[0])
             elif isinstance(args[0], dict):
                 idx = 0
                 for key, block in args[0].items():
diff --git a/pyvista/core/grid.py b/pyvista/core/grid.py
index 7ff8c766..8c231baa 100644
--- a/pyvista/core/grid.py
+++ b/pyvista/core/grid.py
@@ -92,7 +92,7 @@ class RectilinearGrid(vtkRectilinearGrid, Grid):
             if isinstance(args[0], vtk.vtkRectilinearGrid):
                 self.deep_copy(args[0])
             elif isinstance(args[0], (str, pathlib.Path)):
-                self._load_file(args[0])
+                self._from_file(args[0])
             elif isinstance(args[0], np.ndarray):
                 self._from_arrays(args[0], None, None)
             else:
@@ -288,7 +288,7 @@ class UniformGrid(vtkImageData, Grid, UniformGridFilters):
             if isinstance(args[0], vtk.vtkImageData):
                 self.deep_copy(args[0])
             elif isinstance(args[0], (str, pathlib.Path)):
-                self._load_file(args[0])
+                self._from_file(args[0])
             else:
                 arg0_is_valid = len(args[0]) == 3
                 self._from_specs(args[0])
diff --git a/pyvista/core/objects.py b/pyvista/core/objects.py
index 987b2d33..de21c27d 100644
--- a/pyvista/core/objects.py
+++ b/pyvista/core/objects.py
@@ -3,7 +3,7 @@
 The data objects does not have any sort of spatial reference.
 
 """
-
+import imageio
 import numpy as np
 import vtk
 
@@ -283,11 +283,18 @@ class Table(vtk.vtkTable, DataObject):
         return np.nanmin(arr), np.nanmax(arr)
 
 
-class Texture(vtk.vtkTexture):
+class Texture(vtk.vtkTexture, DataObject):
     """A helper class for vtkTextures."""
 
+    _READERS = {'.bmp': vtk.vtkBMPReader, '.dem': vtk.vtkDEMReader, '.dcm': vtk.vtkDICOMImageReader,
+                '.img': vtk.vtkDICOMImageReader, '.jpeg': vtk.vtkJPEGReader, '.jpg': vtk.vtkJPEGReader,
+                '.mhd': vtk.vtkMetaImageReader, '.nrrd': vtk.vtkNrrdReader, '.nhdr': vtk.vtkNrrdReader,
+                '.png': vtk.vtkPNGReader, '.pnm': vtk.vtkPNMReader, '.slc': vtk.vtkSLCReader,
+                '.tiff': vtk.vtkTIFFReader, '.tif': vtk.vtkTIFFReader}
+
     def __init__(self, *args, **kwargs):
         """Initialize the texture."""
+        super().__init__(*args, **kwargs)
         assert_empty_kwargs(**kwargs)
 
         if len(args) == 1:
@@ -298,10 +305,19 @@ class Texture(vtk.vtkTexture):
             elif isinstance(args[0], vtk.vtkImageData):
                 self._from_image_data(args[0])
             elif isinstance(args[0], str):
-                self._from_texture(pyvista.read_texture(args[0]))
+                self._from_file(filename=args[0])
             else:
                 raise TypeError(f'Table unable to be made from ({type(args[0])})')
 
+    def _from_file(self, filename):
+        try:
+            image = self._load_file(filename)
+            if image.GetNumberOfPoints() < 2:
+                raise ValueError("Problem reading the image with VTK.")
+            self._from_image_data(image)
+        except (KeyError, ValueError):
+            self._from_array(imageio.imread(filename))
+
     def _from_texture(self, texture):
         image = texture.GetInput()
         self._from_image_data(image)
@@ -313,46 +329,52 @@ class Texture(vtk.vtkTexture):
         return self.Update()
 
     def _from_array(self, image):
-        if image.ndim not in [2,3]:
+        """Create a texture from a np.ndarray."""
+        if not 2 <= image.ndim <= 3:
             # we support 2 [single component image] or 3 [e.g. rgb or rgba] dims
             raise ValueError('Input image must be nn by nm by RGB[A]')
 
         if image.ndim == 3:
-            if image.shape[2] != 3 and image.shape[2] != 4:
+            if not 3 <= image.shape[2] <= 4:
                 raise ValueError('Third dimension of the array must be of size 3 (RGB) or 4 (RGBA)')
-
             n_components = image.shape[2]
-
         elif image.ndim == 2:
             n_components = 1
 
         grid = pyvista.UniformGrid((image.shape[1], image.shape[0], 1))
         grid.point_arrays['Image'] = np.flip(image.swapaxes(0, 1), axis=1).reshape((-1, n_components), order='F')
         grid.set_active_scalars('Image')
-
         return self._from_image_data(grid)
 
+    @property
+    def repeat(self):
+        """Repeat the texture."""
+        return self.GetRepeat()
+
+    @repeat.setter
+    def repeat(self, flag):
+        self.SetRepeat(flag)
+
+    @property
+    def n_components(self):
+        """Components in the image (e.g. 3 [or 4] for RGB[A])."""
+        image = self.to_image()
+        return image.active_scalars.shape[1]
+
     def flip(self, axis):
         """Flip this texture inplace along the specified axis. 0 for X and 1 for Y."""
-        if axis < 0 or axis > 1:
+        if not 0 <= axis <= 1:
             raise ValueError(f"Axis {axis} out of bounds")
-        ax = [1, 0]
         array = self.to_array()
-        array = np.flip(array, axis=ax[axis])
+        array = np.flip(array, axis=1 - axis)
         return self._from_array(array)
 
     def to_image(self):
         """Return the texture as an image."""
         return self.GetInput()
 
-    @property
-    def n_components(self):
-        """Components in the image (e.g. 3 [or 4] for RGB[A])."""
-        image = self.to_image()
-        return image.active_scalars.shape[1]
-
     def to_array(self):
-        """Return the texture as an array."""
+        """Return the texture as a np.ndarray."""
         image = self.to_image()
 
         if image.active_scalars.ndim > 1:
@@ -366,15 +388,6 @@ class Texture(vtk.vtkTexture):
         """Plot the texture as image data by itself."""
         return self.to_image().plot(*args, **kwargs)
 
-    @property
-    def repeat(self):
-        """Repeat the texture."""
-        return self.GetRepeat()
-
-    @repeat.setter
-    def repeat(self, flag):
-        self.SetRepeat(flag)
-
     def copy(self):
-        """Make a copy of this textrue."""
+        """Make a copy of this texture."""
         return Texture(self.to_image().copy())
diff --git a/pyvista/core/pointset.py b/pyvista/core/pointset.py
index 92a7efb0..b3180630 100644
--- a/pyvista/core/pointset.py
+++ b/pyvista/core/pointset.py
@@ -157,7 +157,7 @@ class PolyData(vtkPolyData, PointSet, PolyDataFilters):
                 else:
                     self.shallow_copy(args[0])
             elif isinstance(args[0], (str, pathlib.Path)):
-                self._load_file(args[0])
+                self._from_file(args[0])
             elif isinstance(args[0], (np.ndarray, list)):
                 if isinstance(args[0], list):
                     points = np.asarray(args[0])
@@ -506,7 +506,7 @@ class UnstructuredGrid(vtkUnstructuredGrid, PointGrid, UnstructuredGridFilters):
                     self.shallow_copy(args[0])
 
             elif isinstance(args[0], (str, pathlib.Path)):
-                self._load_file(args[0])
+                self._from_file(args[0])
 
             elif isinstance(args[0], vtk.vtkStructuredGrid):
                 vtkappend = vtk.vtkAppendFilter()
@@ -801,7 +801,7 @@ class StructuredGrid(vtkStructuredGrid, PointGrid):
             if isinstance(args[0], vtk.vtkStructuredGrid):
                 self.deep_copy(args[0])
             elif isinstance(args[0], str):
-                self._load_file(args[0])
+                self._from_file(args[0])
 
         elif len(args) == 3:
             arg0_is_arr = isinstance(args[0], np.ndarray)
diff --git a/pyvista/core/pyvista_ndarray.py b/pyvista/core/pyvista_ndarray.py
index 3a76df29..c68cd7ea 100644
--- a/pyvista/core/pyvista_ndarray.py
+++ b/pyvista/core/pyvista_ndarray.py
@@ -1,5 +1,5 @@
 """Contains pyvista_ndarray a numpy ndarray type used in pyvista."""
-from collections import Iterable
+from collections.abc import Iterable
 
 import numpy as np
 from vtk.numpy_interface.dataset_adapter import VTKObjectWrapper, VTKArray
diff --git a/pyvista/examples/examples.py b/pyvista/examples/examples.py
index 3fce697d..06dba921 100755
--- a/pyvista/examples/examples.py
+++ b/pyvista/examples/examples.py
@@ -246,13 +246,11 @@ def load_spline():
 
 def load_random_hills():
     """Create random hills toy example.
-    
-    Uses the parametric random hill function to create hills oriented like
-    topography and add's an elevation array.
 
+    Uses the parametric random hill function to create hills oriented
+    like topography and adds an elevation array.
     """
     mesh = pyvista.ParametricRandomHills()
-    mesh.rotate_y(90)
     return mesh.elevation()
 
 
diff --git a/pyvista/plotting/plotting.py b/pyvista/plotting/plotting.py
index 65e05c8f..cd8842e1 100644
--- a/pyvista/plotting/plotting.py
+++ b/pyvista/plotting/plotting.py
@@ -19,9 +19,8 @@ from vtk.util import numpy_support as VN
 from vtk.util.numpy_support import numpy_to_vtk, vtk_to_numpy
 
 import pyvista
-from pyvista.utilities import (assert_empty_kwargs,
-                               convert_array, convert_string_array, get_array,
-                               is_pyvista_dataset, numpy_to_texture, abstract_class,
+from pyvista.utilities import (assert_empty_kwargs, convert_array, convert_string_array,
+                               get_array, is_pyvista_dataset, abstract_class,
                                raise_not_matching, try_callback, wrap)
 from .background_renderer import BackgroundRenderer
 from .colors import get_cmap_safe
@@ -1525,6 +1524,7 @@ class BasePlotter(PickingHelper, WidgetHelper):
             texture = mesh._activate_texture(texture)
 
         if texture:
+
             if isinstance(texture, np.ndarray):
                 texture = numpy_to_texture(texture)
             if not isinstance(texture, (vtk.vtkTexture, vtk.vtkOpenGLTexture)):
@@ -1539,6 +1539,9 @@ class BasePlotter(PickingHelper, WidgetHelper):
                 show_scalar_bar = False
             self.mapper.SetScalarModeToUsePointFieldData()
 
+            # see https://github.com/pyvista/pyvista/issues/950
+            mesh.set_active_scalars(None)
+
         # Handle making opacity array =========================================
 
         _custom_opac = False
diff --git a/pyvista/utilities/fileio.py b/pyvista/utilities/fileio.py
index ace01b6f..2df12095 100644
--- a/pyvista/utilities/fileio.py
+++ b/pyvista/utilities/fileio.py
@@ -238,7 +238,7 @@ def read(filename, attrs=None, file_format=None):
         # Attempt to use the legacy reader...
         return read_legacy(filename)
     elif ext in ['.jpeg', '.jpg']:
-        return read_texture(filename).to_image()
+        return pyvista.Texture(filename).to_image()
     else:
         # Attempt find a reader in the readers mapping
         try:
@@ -267,13 +267,13 @@ def read_texture(filename, attrs=None):
         reader = get_reader(filename)
         image = standard_reader_routine(reader, filename, attrs=attrs)
         if image.n_points < 2:
-            raise RuntimeError("Problem reading the image with VTK.")
-        return pyvista.image_to_texture(image)
-    except (KeyError, RuntimeError):
+            raise ValueError("Problem reading the image with VTK.")
+        return pyvista.Texture(image)
+    except (KeyError, ValueError):
         # Otherwise, use the imageio reader
         pass
     import imageio
-    return pyvista.numpy_to_texture(imageio.imread(filename))
+    return pyvista.Texture(imageio.imread(filename))
 
 
 def read_exodus(filename,
diff --git a/pyvista/utilities/helpers.py b/pyvista/utilities/helpers.py
index cd0d1b94..820a7bc4 100644
--- a/pyvista/utilities/helpers.py
+++ b/pyvista/utilities/helpers.py
@@ -1,7 +1,6 @@
 """Supporting functions for polydata and grid objects."""
 
 import collections.abc
-import ctypes
 import enum
 import logging
 import signal
@@ -434,12 +433,80 @@ def is_meshio_mesh(mesh):
         return False
 
 
-def wrap(vtkdataset):
+def wrap(dataset):
     """Wrap any given VTK data object to its appropriate PyVista data object.
 
     Other formats that are supported include:
     * 2D :class:`numpy.ndarray` of XYZ vertices
     * 3D :class:`numpy.ndarray` representing a volume. Values will be scalars.
+    * 3d :class:`trimesh.Trimesh` mesh.
+
+    Parameters
+    ----------
+    dataset : :class:`numpy.ndarray`, :class:`trimesh.Trimesh`, or VTK object
+        Dataset to wrap.
+
+    Returns
+    -------
+    wrapped_dataset : pyvista class
+        The `pyvista` wrapped dataset.
+
+    Examples
+    --------
+    Wrap a numpy array representing a random point cloud
+
+    >>> import numpy as np
+    >>> import pyvista
+    >>> points = np.random.random((10, 3))
+    >>> cloud = pyvista.wrap(points)
+    >>> cloud  # doctest:+SKIP
+    PolyData (0x7fc52db83d70)
+      N Cells:  10
+      N Points: 10
+      X Bounds: 1.123e-01, 7.457e-01
+      Y Bounds: 1.009e-01, 9.877e-01
+      Z Bounds: 2.346e-03, 9.640e-01
+      N Arrays: 0
+
+    Wrap a Trimesh object
+
+    >>> import trimesh
+    >>> import pyvista
+    >>> points = [[0, 0, 0], [0, 0, 1], [0, 1, 0]]
+    >>> faces = [[0, 1, 2]]
+    >>> tmesh = trimesh.Trimesh(points, faces=faces, process=False)
+    >>> mesh = pyvista.wrap(tmesh)
+    >>> mesh  # doctest:+SKIP
+    PolyData (0x7fc55ff27ad0)
+      N Cells:  1
+      N Points: 3
+      X Bounds: 0.000e+00, 0.000e+00
+      Y Bounds: 0.000e+00, 1.000e+00
+      Z Bounds: 0.000e+00, 1.000e+00
+      N Arrays: 0
+
+    Wrap a VTK object
+
+    >>> import pyvista
+    >>> import vtk
+    >>> points = vtk.vtkPoints()
+    >>> p = [1.0, 2.0, 3.0]
+    >>> vertices = vtk.vtkCellArray()
+    >>> pid = points.InsertNextPoint(p)
+    >>> _ = vertices.InsertNextCell(1)
+    >>> _ = vertices.InsertCellPoint(pid)
+    >>> point = vtk.vtkPolyData()
+    >>> _ = point.SetPoints(points)
+    >>> _ = point.SetVerts(vertices)
+    >>> mesh = pyvista.wrap(point)
+    >>> mesh  # doctest:+SKIP
+    PolyData (0x7fc55ff27ad0)
+      N Cells:  1
+      N Points: 3
+      X Bounds: 0.000e+00, 0.000e+00
+      Y Bounds: 0.000e+00, 1.000e+00
+      Z Bounds: 0.000e+00, 1.000e+00
+      N Arrays: 0
 
     """
     wrappers = {
@@ -454,32 +521,39 @@ def wrap(vtkdataset):
         # 'vtkParametricSpline': pyvista.Spline,
     }
     # Otherwise, we assume a VTK data object was passed
-    if hasattr(vtkdataset, 'GetClassName'):
-        key = vtkdataset.GetClassName()
-    elif vtkdataset is None:
+    if hasattr(dataset, 'GetClassName'):
+        key = dataset.GetClassName()
+    elif dataset is None:
         return None
-    elif isinstance(vtkdataset, np.ndarray):
-        if vtkdataset.ndim == 1 and vtkdataset.shape[0] == 3:
-            return pyvista.PolyData(vtkdataset)
-        if vtkdataset.ndim > 1 and vtkdataset.ndim < 3 and vtkdataset.shape[1] == 3:
-            return pyvista.PolyData(vtkdataset)
-        elif vtkdataset.ndim == 3:
-            mesh = pyvista.UniformGrid(vtkdataset.shape)
-            mesh['values'] = vtkdataset.ravel(order='F')
+    elif isinstance(dataset, np.ndarray):
+        if dataset.ndim == 1 and dataset.shape[0] == 3:
+            return pyvista.PolyData(dataset)
+        if dataset.ndim > 1 and dataset.ndim < 3 and dataset.shape[1] == 3:
+            return pyvista.PolyData(dataset)
+        elif dataset.ndim == 3:
+            mesh = pyvista.UniformGrid(dataset.shape)
+            mesh['values'] = dataset.ravel(order='F')
             mesh.active_scalars_name = 'values'
             return mesh
         else:
-            print(vtkdataset.shape, vtkdataset)
+            print(dataset.shape, dataset)
             raise NotImplementedError('NumPy array could not be converted to PyVista.')
-    elif is_meshio_mesh(vtkdataset):
-        return from_meshio(vtkdataset)
+    elif is_meshio_mesh(dataset):
+        return from_meshio(dataset)
+    elif dataset.__class__.__name__ == 'Trimesh':
+        # trimesh doesn't pad faces
+        n_face = dataset.faces.shape[0]
+        faces = np.empty((n_face, 4), dataset.faces.dtype)
+        faces[:, 1:] = dataset.faces
+        faces[:, 0] = 3
+        return pyvista.PolyData(np.asarray(dataset.vertices), faces)
     else:
-        raise NotImplementedError(f'Type ({type(vtkdataset)}) not able to be wrapped into a PyVista mesh.')
+        raise NotImplementedError(f'Type ({type(dataset)}) not able to be wrapped into a PyVista mesh.')
     try:
-        wrapped = wrappers[key](vtkdataset)
+        wrapped = wrappers[key](dataset)
     except KeyError:
         logging.warning(f'VTK data type ({key}) is not currently supported by pyvista.')
-        return vtkdataset # if not supported just passes the VTK data object
+        return dataset  # if not supported just passes the VTK data object
     return wrapped
 
 
@@ -490,8 +564,6 @@ def image_to_texture(image):
 
 def numpy_to_texture(image):
     """Convert a NumPy image array to a vtk.vtkTexture."""
-    if not isinstance(image, np.ndarray):
-        raise TypeError(f'Unknown input type ({type(image)})')
     return pyvista.Texture(image)
 
 
diff --git a/requirements_docs.txt b/requirements_docs.txt
index 83571a2f..a38ef4d9 100644
--- a/requirements_docs.txt
+++ b/requirements_docs.txt
@@ -14,3 +14,4 @@ sphinx-notfound-page>=0.3.0
 sphinx-copybutton
 sphinx-gallery>=0.4.0
 lxml
+trimesh
diff --git a/requirements_style.txt b/requirements_style.txt
index 987f7f8c..06fb0b5d 100644
--- a/requirements_style.txt
+++ b/requirements_style.txt
@@ -1,2 +1,2 @@
-codespell==1.16.0
-pydocstyle==5.0.2
+codespell==1.17.1
+pydocstyle==5.1.1


Test patch:
diff --git a/requirements_test.txt b/requirements_test.txt
index ebfdfdef..2f11247a 100644
--- a/requirements_test.txt
+++ b/requirements_test.txt
@@ -11,3 +11,4 @@ itkwidgets>=0.25.2
 tqdm
 hypothesis>=5.8.0
 sphinx_gallery
+trimesh
diff --git a/tests/test_common.py b/tests/test_common.py
index 705fb6de..78308eb8 100644
--- a/tests/test_common.py
+++ b/tests/test_common.py
@@ -7,7 +7,7 @@ from hypothesis.strategies import composite, integers, floats, one_of
 from vtk.util.numpy_support import vtk_to_numpy
 
 import pyvista
-from pyvista import examples
+from pyvista import examples, Texture
 
 
 @pytest.fixture()
@@ -386,7 +386,7 @@ def test_texture():
     # now grab the texture coordinates
     foo = mesh.t_coords
     assert np.allclose(foo, t_coords)
-    texture = pyvista.read_texture(examples.mapfile)
+    texture = Texture(examples.mapfile)
     mesh.textures['map'] = texture
     assert mesh.textures['map'] is not None
     mesh.clear_textures()
@@ -398,7 +398,7 @@ def test_texture_airplane():
     mesh.texture_map_to_plane(inplace=True, name="tex_a", use_bounds=False)
     mesh.texture_map_to_plane(inplace=True, name="tex_b", use_bounds=True)
     assert not np.allclose(mesh["tex_a"], mesh["tex_b"])
-    texture = pyvista.read_texture(examples.mapfile)
+    texture = Texture(examples.mapfile)
     mesh.textures["tex_a"] = texture.copy()
     mesh.textures["tex_b"] = texture.copy()
     mesh._activate_texture("tex_a")
diff --git a/tests/test_filters.py b/tests/test_filters.py
index 83812198..4d67e8ec 100644
--- a/tests/test_filters.py
+++ b/tests/test_filters.py
@@ -1,5 +1,6 @@
 import os
 import sys
+import platform
 
 import numpy as np
 import pytest
@@ -22,6 +23,7 @@ skip_py2_nobind = pytest.mark.skipif(int(sys.version[0]) < 3,
                                      reason="Python 2 doesn't support binding methods")
 
 skip_windows = pytest.mark.skipif(os.name == 'nt', reason="Flaky Windows tests")
+skip_mac = pytest.mark.skipif(platform.system() == 'Darwin', reason="Flaky Mac tests")
 
 
 @pytest.fixture(scope='module')
@@ -51,6 +53,7 @@ def test_clip_filter():
 
 
 @skip_windows
+@skip_mac
 def test_clip_by_scalars_filter():
     """This tests the clip filter on all datatypes available filters"""
     for i, dataset_in in enumerate(DATASETS):
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
new file mode 100644
index 00000000..1533a0e3
--- /dev/null
+++ b/tests/test_helpers.py
@@ -0,0 +1,14 @@
+import pyvista
+import trimesh
+import numpy as np
+
+
+def test_wrap_trimesh():
+    points = [[0, 0, 0], [0, 0, 1], [0, 1, 0]]
+    faces = [[0, 1, 2]]
+    tmesh = trimesh.Trimesh(points, faces=faces, process=False)
+    mesh = pyvista.wrap(tmesh)
+    assert isinstance(mesh, pyvista.PolyData)
+
+    assert np.allclose(tmesh.vertices, mesh.points)
+    assert np.allclose(tmesh.faces, mesh.faces[1:])