697 lines
21 KiB
Python
697 lines
21 KiB
Python
|
|
"""Wrapper for :vtk:`vtkTexture`."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from collections.abc import Sequence
|
||
|
|
from typing import TYPE_CHECKING
|
||
|
|
import warnings
|
||
|
|
|
||
|
|
import numpy as np
|
||
|
|
|
||
|
|
import pyvista
|
||
|
|
from pyvista.core.dataobject import DataObject
|
||
|
|
from pyvista.core.utilities.fileio import _try_imageio_imread
|
||
|
|
from pyvista.core.utilities.misc import AnnotatedIntEnum
|
||
|
|
|
||
|
|
from . import _vtk
|
||
|
|
|
||
|
|
if TYPE_CHECKING:
|
||
|
|
from pyvista.core._typing_core import NumpyArray
|
||
|
|
|
||
|
|
|
||
|
|
class Texture(DataObject, _vtk.vtkTexture):
|
||
|
|
"""Wrap :vtk:`vtkTexture`.
|
||
|
|
|
||
|
|
Textures can be used to apply images to surfaces, as in the case of
|
||
|
|
:ref:`texture_example`.
|
||
|
|
|
||
|
|
They can also be used for environment textures to affect the lighting of
|
||
|
|
the scene, or even as a environment cubemap as in the case of
|
||
|
|
:ref:`pbr_example` and :ref:`planets_example`.
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
uinput : str, :vtk:`vtkImageData`, :vtk:`vtkTexture`, sequence[ImageData], optional
|
||
|
|
Filename, :vtk:`vtkImageData`, :vtk:`vtkTexture`, :class:`numpy.ndarray` or a
|
||
|
|
sequence of images to create a cubemap. If a sequence of images, must
|
||
|
|
be of the same size and in the following order:
|
||
|
|
|
||
|
|
* +X
|
||
|
|
* -X
|
||
|
|
* +Y
|
||
|
|
* -Y
|
||
|
|
* +Z
|
||
|
|
* -Z
|
||
|
|
|
||
|
|
**kwargs : dict, optional
|
||
|
|
Optional arguments when reading from a file. Generally unused.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Load a texture from file. File should be a "image" or "image-like" file.
|
||
|
|
|
||
|
|
>>> from pathlib import Path
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> from pyvista import examples
|
||
|
|
>>> path = examples.download_masonry_texture(load=False)
|
||
|
|
>>> Path(path).name
|
||
|
|
'masonry.bmp'
|
||
|
|
>>> texture = pv.Texture(path)
|
||
|
|
>>> texture
|
||
|
|
Texture (...)
|
||
|
|
Components: 3
|
||
|
|
Cube Map: False
|
||
|
|
Dimensions: 256, 256
|
||
|
|
|
||
|
|
Create a texture from an RGB array. Note how this is colored per "point"
|
||
|
|
rather than per "pixel".
|
||
|
|
|
||
|
|
>>> import numpy as np
|
||
|
|
>>> arr = np.array(
|
||
|
|
... [
|
||
|
|
... [255, 255, 255],
|
||
|
|
... [255, 0, 0],
|
||
|
|
... [0, 255, 0],
|
||
|
|
... [0, 0, 255],
|
||
|
|
... ],
|
||
|
|
... dtype=np.uint8,
|
||
|
|
... )
|
||
|
|
>>> arr = arr.reshape((2, 2, 3))
|
||
|
|
>>> texture = pv.Texture(arr)
|
||
|
|
>>> texture.plot()
|
||
|
|
|
||
|
|
Create a cubemap from 6 images.
|
||
|
|
|
||
|
|
>>> px = examples.download_sky(direction='posx') # doctest:+SKIP
|
||
|
|
>>> nx = examples.download_sky(direction='negx') # doctest:+SKIP
|
||
|
|
>>> py = examples.download_sky(direction='posy') # doctest:+SKIP
|
||
|
|
>>> ny = examples.download_sky(direction='negy') # doctest:+SKIP
|
||
|
|
>>> pz = examples.download_sky(direction='posz') # doctest:+SKIP
|
||
|
|
>>> nz = examples.download_sky(direction='negz') # doctest:+SKIP
|
||
|
|
>>> texture = pv.Texture([px, nx, py, ny, pz, nz]) # doctest:+SKIP
|
||
|
|
>>> texture.cube_map # doctest:+SKIP
|
||
|
|
True
|
||
|
|
|
||
|
|
"""
|
||
|
|
|
||
|
|
class WrapType(AnnotatedIntEnum):
|
||
|
|
"""Types of wrapping a texture can support.
|
||
|
|
|
||
|
|
Wrap mode for the texture coordinates valid values are:
|
||
|
|
|
||
|
|
* CLAMP_TO_EDGE
|
||
|
|
* REPEAT (Default in :class:`pyvista.Texture`)
|
||
|
|
* MIRRORED_REPEAT
|
||
|
|
* CLAMP_TO_BORDER
|
||
|
|
|
||
|
|
See :attr:`Texture.wrap` for usage.
|
||
|
|
|
||
|
|
"""
|
||
|
|
|
||
|
|
CLAMP_TO_EDGE = (0, 'Clamp to edge')
|
||
|
|
REPEAT = (1, 'Repeat')
|
||
|
|
MIRRORED_REPEAT = (2, 'Mirrored repeat')
|
||
|
|
CLAMP_TO_BORDER = (3, 'Clamp to border')
|
||
|
|
|
||
|
|
def __init__(self, uinput=None, **kwargs):
|
||
|
|
"""Initialize the texture."""
|
||
|
|
super().__init__(uinput)
|
||
|
|
|
||
|
|
if isinstance(uinput, _vtk.vtkTexture):
|
||
|
|
self._from_texture(uinput)
|
||
|
|
elif isinstance(uinput, np.ndarray):
|
||
|
|
self._from_array(uinput)
|
||
|
|
elif isinstance(uinput, _vtk.vtkImageData):
|
||
|
|
self._from_image_data(uinput)
|
||
|
|
elif isinstance(uinput, str):
|
||
|
|
self._from_file(filename=uinput, **kwargs)
|
||
|
|
elif isinstance(uinput, Sequence) and len(uinput) == 6:
|
||
|
|
# Create a cubemap
|
||
|
|
self.mipmap = True
|
||
|
|
self.interpolate = True
|
||
|
|
self.cube_map = True # Must be set prior to setting images
|
||
|
|
|
||
|
|
# add each image to the cubemap
|
||
|
|
for i, image in enumerate(uinput):
|
||
|
|
if not isinstance(image, pyvista.ImageData):
|
||
|
|
msg = (
|
||
|
|
'If a sequence, the each item in the first argument must be a '
|
||
|
|
'pyvista.ImageData'
|
||
|
|
)
|
||
|
|
raise TypeError(msg)
|
||
|
|
# must flip y for cubemap to display properly
|
||
|
|
self.SetInputDataObject(i, image._flip_uniform(1))
|
||
|
|
elif uinput is None:
|
||
|
|
pass
|
||
|
|
else:
|
||
|
|
msg = f'Cannot create a pyvista.Texture from ({type(uinput)})'
|
||
|
|
raise TypeError(msg)
|
||
|
|
|
||
|
|
def _from_file(self, filename, **kwargs):
|
||
|
|
try:
|
||
|
|
image = pyvista.read(filename, **kwargs)
|
||
|
|
if image.n_points < 2: # pragma: no cover
|
||
|
|
msg = 'Problem reading the image with VTK.'
|
||
|
|
raise RuntimeError(msg)
|
||
|
|
self._from_image_data(image)
|
||
|
|
except (KeyError, ValueError, OSError):
|
||
|
|
self._from_array(_try_imageio_imread(filename)) # pragma: no cover
|
||
|
|
|
||
|
|
def _from_texture(self, texture):
|
||
|
|
image = texture.GetInput()
|
||
|
|
self._from_image_data(image)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def interpolate(self) -> bool: # numpydoc ignore=RT01
|
||
|
|
"""Return if interpolate is enabled or disabled.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Show the masonry texture without interpolation. Here, we zoom to show
|
||
|
|
the individual pixels.
|
||
|
|
|
||
|
|
>>> from pyvista import examples
|
||
|
|
>>> texture = examples.download_masonry_texture()
|
||
|
|
>>> texture.interpolate = False
|
||
|
|
>>> texture.plot(cpos='xy', zoom=3)
|
||
|
|
|
||
|
|
Plot the same texture with interpolation.
|
||
|
|
|
||
|
|
>>> texture.interpolate = True
|
||
|
|
>>> texture.plot(cpos='xy', zoom=3)
|
||
|
|
|
||
|
|
"""
|
||
|
|
return bool(self.GetInterpolate())
|
||
|
|
|
||
|
|
@interpolate.setter
|
||
|
|
def interpolate(self, value: bool):
|
||
|
|
self.SetInterpolate(value)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def mipmap(self) -> bool: # numpydoc ignore=RT01
|
||
|
|
"""Return if mipmap is enabled or disabled."""
|
||
|
|
return bool(self.GetMipmap())
|
||
|
|
|
||
|
|
@mipmap.setter
|
||
|
|
def mipmap(self, value: bool):
|
||
|
|
self.SetMipmap(value)
|
||
|
|
|
||
|
|
def _from_image_data(self, image):
|
||
|
|
if not isinstance(image, pyvista.ImageData):
|
||
|
|
image = pyvista.ImageData(image)
|
||
|
|
self.SetInputDataObject(image)
|
||
|
|
self.Update()
|
||
|
|
|
||
|
|
def _from_array(self, image):
|
||
|
|
"""Create a texture from a np.ndarray."""
|
||
|
|
if image.ndim not in [2, 3]:
|
||
|
|
# we support 2 [single component image] or 3 [e.g. rgb or rgba] dims
|
||
|
|
msg = 'Input image must be nn by nm by RGB[A]'
|
||
|
|
raise ValueError(msg)
|
||
|
|
|
||
|
|
if image.ndim == 3:
|
||
|
|
if image.shape[2] not in [1, 3, 4]:
|
||
|
|
msg = 'Third dimension of the array must be of size 3 (RGB) or 4 (RGBA)'
|
||
|
|
raise ValueError(msg)
|
||
|
|
n_components = image.shape[2]
|
||
|
|
elif image.ndim == 2:
|
||
|
|
n_components = 1
|
||
|
|
|
||
|
|
grid = pyvista.ImageData(dimensions=(image.shape[1], image.shape[0], 1))
|
||
|
|
grid.point_data['Image'] = np.flip(image.swapaxes(0, 1), axis=1).reshape(
|
||
|
|
(-1, n_components),
|
||
|
|
order='F',
|
||
|
|
)
|
||
|
|
grid.set_active_scalars('Image')
|
||
|
|
self._from_image_data(grid)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def repeat(self) -> bool: # numpydoc ignore=RT01
|
||
|
|
"""Repeat the texture.
|
||
|
|
|
||
|
|
This is provided for convenience and backwards compatibility.
|
||
|
|
|
||
|
|
For new code, use :func:`Texture.wrap`.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Load the masonry texture and create a simple :class:`pyvista.PolyData`
|
||
|
|
with texture coordinates using :func:`pyvista.Plane`. By default the
|
||
|
|
texture coordinates are between 0 and 1. Let's raise these values over
|
||
|
|
1 by multiplying them in place. This will allow us to wrap the texture.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> from pyvista import examples
|
||
|
|
>>> texture = examples.download_masonry_texture()
|
||
|
|
>>> plane = pv.Plane()
|
||
|
|
>>> plane.active_texture_coordinates *= 2
|
||
|
|
|
||
|
|
This is the texture plotted with repeat set to ``False``.
|
||
|
|
|
||
|
|
>>> texture.repeat = False
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor = pl.add_mesh(plane, texture=texture)
|
||
|
|
>>> pl.camera.zoom('tight')
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
This is the texture plotted with repeat set to ``True``.
|
||
|
|
|
||
|
|
>>> texture.repeat = True
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor = pl.add_mesh(plane, texture=texture)
|
||
|
|
>>> pl.camera.zoom('tight')
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
"""
|
||
|
|
return bool(self.GetRepeat())
|
||
|
|
|
||
|
|
@repeat.setter
|
||
|
|
def repeat(self, flag: bool):
|
||
|
|
self.SetRepeat(flag)
|
||
|
|
|
||
|
|
def flip_x(self) -> Texture:
|
||
|
|
"""Flip the texture in the x direction.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
pyvista.Texture
|
||
|
|
Flipped texture.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
>>> from pyvista import examples
|
||
|
|
>>> texture = examples.download_puppy_texture()
|
||
|
|
>>> flipped = texture.flip_x()
|
||
|
|
>>> flipped.plot()
|
||
|
|
|
||
|
|
"""
|
||
|
|
return Texture(self.to_image()._flip_uniform(0)) # type: ignore[abstract]
|
||
|
|
|
||
|
|
def flip_y(self) -> Texture:
|
||
|
|
"""Flip the texture in the y direction.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
pyvista.Texture
|
||
|
|
Flipped texture.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
>>> from pyvista import examples
|
||
|
|
>>> texture = examples.download_puppy_texture()
|
||
|
|
>>> flipped = texture.flip_y()
|
||
|
|
>>> flipped.plot()
|
||
|
|
|
||
|
|
"""
|
||
|
|
return Texture(self.to_image()._flip_uniform(1)) # type: ignore[abstract]
|
||
|
|
|
||
|
|
def to_image(self):
|
||
|
|
"""Return the texture as an image.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
pyvista.ImageData
|
||
|
|
Texture represented as a uniform grid.
|
||
|
|
|
||
|
|
"""
|
||
|
|
return self.GetInput()
|
||
|
|
|
||
|
|
def to_array(self) -> NumpyArray[float]:
|
||
|
|
"""Return the texture as an array.
|
||
|
|
|
||
|
|
Notes
|
||
|
|
-----
|
||
|
|
The shape of the array's first two dimensions will be swapped. For
|
||
|
|
example, a ``(300, 200)`` image will return an array of ``(200, 300)``.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
numpy.ndarray
|
||
|
|
Texture as a numpy array.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
>>> from pyvista import examples
|
||
|
|
>>> texture = examples.download_puppy_texture()
|
||
|
|
>>> texture
|
||
|
|
Texture (...)
|
||
|
|
Components: 3
|
||
|
|
Cube Map: False
|
||
|
|
Dimensions: 1600, 1200
|
||
|
|
>>> texture.to_array().shape
|
||
|
|
(1200, 1600, 3)
|
||
|
|
>>> texture.to_array().dtype
|
||
|
|
dtype('uint8')
|
||
|
|
|
||
|
|
"""
|
||
|
|
return self.to_image().active_scalars.reshape(
|
||
|
|
[*list(self.dimensions)[::-1], self.n_components]
|
||
|
|
)[::-1]
|
||
|
|
|
||
|
|
def rotate_cw(self) -> Texture:
|
||
|
|
"""Rotate this texture 90 degrees clockwise.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
pyvista.Texture
|
||
|
|
Rotated texture.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
>>> from pyvista import examples
|
||
|
|
>>> texture = examples.download_puppy_texture()
|
||
|
|
>>> rotated = texture.rotate_cw()
|
||
|
|
>>> rotated.plot()
|
||
|
|
|
||
|
|
"""
|
||
|
|
return Texture(np.rot90(self.to_array())) # type: ignore[abstract]
|
||
|
|
|
||
|
|
def rotate_ccw(self) -> Texture:
|
||
|
|
"""Rotate this texture 90 degrees counter-clockwise.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
pyvista.Texture
|
||
|
|
Rotated texture.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
>>> from pyvista import examples
|
||
|
|
>>> texture = examples.download_puppy_texture()
|
||
|
|
>>> rotated = texture.rotate_ccw()
|
||
|
|
>>> rotated.plot()
|
||
|
|
|
||
|
|
"""
|
||
|
|
return Texture(np.rot90(self.to_array(), k=3)) # type: ignore[abstract]
|
||
|
|
|
||
|
|
@property
|
||
|
|
def cube_map(self) -> bool: # numpydoc ignore=RT01
|
||
|
|
"""Return ``True`` if cube mapping is enabled and ``False`` otherwise."""
|
||
|
|
return self.GetCubeMap()
|
||
|
|
|
||
|
|
@cube_map.setter
|
||
|
|
def cube_map(self, flag: bool):
|
||
|
|
self.SetCubeMap(flag)
|
||
|
|
|
||
|
|
def copy(self): # type: ignore[override]
|
||
|
|
"""Make a copy of this texture.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
pyvista.Texture
|
||
|
|
Copied texture.
|
||
|
|
|
||
|
|
"""
|
||
|
|
return Texture(self.to_image().copy()) # type: ignore[abstract]
|
||
|
|
|
||
|
|
def to_skybox(self):
|
||
|
|
"""Return the texture as a :vtk:`vtkSkybox` if cube mapping is enabled.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
:vtk:`vtkSkybox`
|
||
|
|
Skybox if cube mapping is enabled. Otherwise, ``None``.
|
||
|
|
|
||
|
|
"""
|
||
|
|
if self.cube_map:
|
||
|
|
skybox = _vtk.vtkSkybox()
|
||
|
|
skybox.SetTexture(self)
|
||
|
|
return skybox
|
||
|
|
return None
|
||
|
|
|
||
|
|
def __repr__(self):
|
||
|
|
"""Return the object representation."""
|
||
|
|
return pyvista.DataSet.__repr__(self) # type: ignore[type-var]
|
||
|
|
|
||
|
|
def _get_attrs(self):
|
||
|
|
"""Return the representation methods (internal helper)."""
|
||
|
|
attrs = []
|
||
|
|
attrs.append(('Components', self.n_components, '{:d}'))
|
||
|
|
attrs.append(('Cube Map', self.cube_map, '{:}'))
|
||
|
|
attrs.append(('Dimensions', self.dimensions, '{:d}, {:d}')) # type: ignore[arg-type]
|
||
|
|
return attrs
|
||
|
|
|
||
|
|
@property
|
||
|
|
def n_components(self) -> int: # numpydoc ignore=RT01
|
||
|
|
"""Return the number of components in the image.
|
||
|
|
|
||
|
|
In textures, 3 or 4 components are used for representing RGB and RGBA
|
||
|
|
images.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Show the number of components in the example masonry texture.
|
||
|
|
|
||
|
|
>>> from pyvista import examples
|
||
|
|
>>> texture = examples.download_masonry_texture()
|
||
|
|
>>> texture.n_components
|
||
|
|
3
|
||
|
|
|
||
|
|
"""
|
||
|
|
input_data = self.GetInput()
|
||
|
|
if input_data is None:
|
||
|
|
return 0
|
||
|
|
return input_data.GetPointData().GetScalars().GetNumberOfComponents()
|
||
|
|
|
||
|
|
@property
|
||
|
|
def dimensions(self) -> tuple[int, int]: # numpydoc ignore=RT01
|
||
|
|
"""Dimensions of the texture.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
>>> from pyvista import examples
|
||
|
|
>>> texture = examples.download_masonry_texture()
|
||
|
|
>>> texture.dimensions
|
||
|
|
(256, 256)
|
||
|
|
|
||
|
|
"""
|
||
|
|
input_data = self.GetInput()
|
||
|
|
if input_data is None:
|
||
|
|
return (0, 0)
|
||
|
|
return input_data.GetDimensions()[:2]
|
||
|
|
|
||
|
|
def plot(self, **kwargs):
|
||
|
|
"""Plot the texture as an image.
|
||
|
|
|
||
|
|
If the texture is a cubemap, it will be displayed as a skybox with a
|
||
|
|
sphere in the center reflecting the environment.
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
**kwargs : dict, optional
|
||
|
|
Optional keyworld arguments. See :func:`pyvista.plot`.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
pyvista.Actor | None
|
||
|
|
See the returns section of :func:`pyvista.plot`.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Plot a simple texture.
|
||
|
|
|
||
|
|
>>> from pyvista import examples
|
||
|
|
>>> texture = examples.download_masonry_texture()
|
||
|
|
>>> texture.plot()
|
||
|
|
|
||
|
|
Plot a cubemap as a skybox.
|
||
|
|
|
||
|
|
>>> cube_map = examples.download_sky_box_cube_map()
|
||
|
|
>>> cube_map.plot()
|
||
|
|
|
||
|
|
"""
|
||
|
|
if self.cube_map:
|
||
|
|
return self._plot_skybox(**kwargs)
|
||
|
|
kwargs.setdefault('zoom', 'tight')
|
||
|
|
kwargs.setdefault('lighting', False)
|
||
|
|
kwargs.setdefault('show_axes', False)
|
||
|
|
kwargs.setdefault('show_scalar_bar', False)
|
||
|
|
mesh = pyvista.Plane(i_size=self.dimensions[0], j_size=self.dimensions[1])
|
||
|
|
return mesh.plot(texture=self, **kwargs)
|
||
|
|
|
||
|
|
def _plot_skybox(self, **kwargs):
|
||
|
|
"""Plot this texture as a skybox."""
|
||
|
|
cpos = kwargs.pop('cpos', 'xy')
|
||
|
|
zoom = kwargs.pop('zoom', 0.5)
|
||
|
|
show_axes = kwargs.pop('show_axes', True)
|
||
|
|
lighting = kwargs.pop('lighting', None)
|
||
|
|
pl = pyvista.Plotter(lighting=lighting)
|
||
|
|
pl.add_actor(self.to_skybox())
|
||
|
|
pl.set_environment_texture(self, is_srgb=True)
|
||
|
|
pl.add_mesh(pyvista.Sphere(), pbr=True, roughness=0.5, metallic=1.0)
|
||
|
|
pl.camera_position = cpos
|
||
|
|
pl.camera.zoom(zoom)
|
||
|
|
if show_axes:
|
||
|
|
pl.show_axes()
|
||
|
|
pl.show(**kwargs)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def wrap(self) -> Texture.WrapType: # numpydoc ignore=RT01
|
||
|
|
"""Return or set the Wrap mode for the texture coordinates.
|
||
|
|
|
||
|
|
Wrap mode for the texture coordinates valid values are:
|
||
|
|
|
||
|
|
* ``0`` - CLAMP_TO_EDGE
|
||
|
|
* ``1`` - REPEAT
|
||
|
|
* ``2`` - MIRRORED_REPEAT
|
||
|
|
* ``3`` - CLAMP_TO_BORDER
|
||
|
|
|
||
|
|
Notes
|
||
|
|
-----
|
||
|
|
CLAMP_TO_BORDER is not supported with OpenGL ES <= 3.2. Wrap will
|
||
|
|
default to CLAMP_TO_EDGE if it is set to CLAMP_TO_BORDER in this case.
|
||
|
|
|
||
|
|
Requires ``vtk`` v9.1.0 or newer.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Load the masonry texture and create a simple :class:`pyvista.PolyData`
|
||
|
|
with texture coordinates using :func:`pyvista.Plane`. By default the
|
||
|
|
texture coordinates are between 0 and 1. Let's raise these values over
|
||
|
|
1 by multiplying them in place. This will allow us to wrap the texture.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> from pyvista import examples
|
||
|
|
>>> texture = examples.download_masonry_texture()
|
||
|
|
>>> plane = pv.Plane()
|
||
|
|
>>> plane.active_texture_coordinates *= 2
|
||
|
|
|
||
|
|
Let's now set the texture wrap to clamp to edge and visualize it.
|
||
|
|
|
||
|
|
>>> texture.wrap = pv.Texture.WrapType.CLAMP_TO_EDGE
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor = pl.add_mesh(plane, texture=texture)
|
||
|
|
>>> pl.camera.zoom('tight')
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
Here is the default repeat:
|
||
|
|
|
||
|
|
>>> texture.wrap = pv.Texture.WrapType.REPEAT
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor = pl.add_mesh(plane, texture=texture)
|
||
|
|
>>> pl.camera.zoom('tight')
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
And here is mirrored repeat:
|
||
|
|
|
||
|
|
>>> texture.wrap = pv.Texture.WrapType.MIRRORED_REPEAT
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor = pl.add_mesh(plane, texture=texture)
|
||
|
|
>>> pl.camera.zoom('tight')
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
Finally, this is clamp to border:
|
||
|
|
|
||
|
|
>>> texture.wrap = pv.Texture.WrapType.CLAMP_TO_BORDER
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor = pl.add_mesh(plane, texture=texture)
|
||
|
|
>>> pl.camera.zoom('tight')
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
"""
|
||
|
|
if not hasattr(self, 'GetWrap'): # pragma: no cover
|
||
|
|
from pyvista.core.errors import VTKVersionError # noqa: PLC0415
|
||
|
|
|
||
|
|
msg = '`wrap` requires VTK v9.1.0 or newer.'
|
||
|
|
raise VTKVersionError(msg)
|
||
|
|
|
||
|
|
return Texture.WrapType(self.GetWrap()) # type: ignore[call-arg]
|
||
|
|
|
||
|
|
@wrap.setter
|
||
|
|
def wrap(self, value: Texture.WrapType | int):
|
||
|
|
if not hasattr(self, 'SetWrap'): # pragma: no cover
|
||
|
|
from pyvista.core.errors import VTKVersionError # noqa: PLC0415
|
||
|
|
|
||
|
|
msg = '`wrap` requires VTK v9.1.0 or newer.'
|
||
|
|
raise VTKVersionError(msg)
|
||
|
|
|
||
|
|
self.SetWrap(value)
|
||
|
|
|
||
|
|
def to_grayscale(self) -> Texture:
|
||
|
|
"""Convert this texture as a single component (grayscale) texture.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
pyvista.Texture
|
||
|
|
Texture converted to grayscale. If already grayscale, the original
|
||
|
|
texture itself is returned.
|
||
|
|
|
||
|
|
Notes
|
||
|
|
-----
|
||
|
|
The transparency channel (if available) will be dropped.
|
||
|
|
|
||
|
|
Follows the `CCIR 601 <https://en.wikipedia.org/wiki/Rec._601>`_ luma
|
||
|
|
calculation equation of ``Y = 0.299*R + 0.587*G + 0.114*B``.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
>>> from pyvista import examples
|
||
|
|
>>> texture = examples.download_masonry_texture()
|
||
|
|
>>> bw_texture = texture.to_grayscale()
|
||
|
|
>>> bw_texture
|
||
|
|
Texture (...)
|
||
|
|
Components: 1
|
||
|
|
Cube Map: False
|
||
|
|
Dimensions: 256, 256
|
||
|
|
>>> bw_texture.plot()
|
||
|
|
|
||
|
|
"""
|
||
|
|
if self.n_components == 1:
|
||
|
|
return self.copy()
|
||
|
|
|
||
|
|
data = self.to_array()
|
||
|
|
r, g, b = data[..., 0], data[..., 1], data[..., 2]
|
||
|
|
data = (0.299 * r + 0.587 * g + 0.114 * b).round().astype(np.uint8)
|
||
|
|
return Texture(data) # type: ignore[abstract]
|
||
|
|
|
||
|
|
|
||
|
|
def image_to_texture(image):
|
||
|
|
"""Convert :class:`pyvista.ImageData` to a :class:`pyvista.Texture`.
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
image : pyvista.ImageData | :vtk:`vtkImageData`
|
||
|
|
Image to convert.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
pyvista.Texture
|
||
|
|
The texture.
|
||
|
|
|
||
|
|
"""
|
||
|
|
return Texture(image) # type: ignore[abstract]
|
||
|
|
|
||
|
|
|
||
|
|
def numpy_to_texture(image):
|
||
|
|
"""Convert a NumPy image array to a :class:`pyvista.Texture`.
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
image : numpy.ndarray
|
||
|
|
Numpy image array. Texture datatype expected to be ``np.uint8``.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
pyvista.Texture
|
||
|
|
PyVista texture.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Create an all white texture.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> import numpy as np
|
||
|
|
>>> tex_arr = np.ones((1024, 1024, 3), dtype=np.uint8) * 255
|
||
|
|
>>> tex = pv.numpy_to_texture(tex_arr)
|
||
|
|
|
||
|
|
"""
|
||
|
|
if image.dtype != np.uint8:
|
||
|
|
image = image.astype(np.uint8)
|
||
|
|
warnings.warn(
|
||
|
|
'Expected `image` dtype to be ``np.uint8``. `image` has been copied '
|
||
|
|
'and converted to np.uint8.',
|
||
|
|
UserWarning,
|
||
|
|
)
|
||
|
|
|
||
|
|
return Texture(image) # type: ignore[abstract]
|