881 lines
29 KiB
Python
881 lines
29 KiB
Python
|
|
"""Module containing composite data mapper."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from itertools import cycle
|
||
|
|
import sys
|
||
|
|
from typing import TYPE_CHECKING
|
||
|
|
import weakref
|
||
|
|
|
||
|
|
import numpy as np
|
||
|
|
|
||
|
|
import pyvista
|
||
|
|
from pyvista import vtk_version_info
|
||
|
|
from pyvista._deprecate_positional_args import _deprecate_positional_args
|
||
|
|
from pyvista.core.utilities.arrays import convert_array
|
||
|
|
from pyvista.core.utilities.arrays import convert_string_array
|
||
|
|
from pyvista.core.utilities.misc import _check_range
|
||
|
|
from pyvista.core.utilities.misc import _NoNewAttrMixin
|
||
|
|
|
||
|
|
from . import _vtk
|
||
|
|
from .colors import Color
|
||
|
|
from .colors import get_cycler
|
||
|
|
from .mapper import _BaseMapper
|
||
|
|
|
||
|
|
if TYPE_CHECKING:
|
||
|
|
from collections.abc import Sequence
|
||
|
|
|
||
|
|
import cycler
|
||
|
|
|
||
|
|
from ._typing import ColorLike
|
||
|
|
|
||
|
|
|
||
|
|
class BlockAttributes(_NoNewAttrMixin):
|
||
|
|
"""Block attributes used to set the attributes of a block.
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
block : pyvista.DataObject
|
||
|
|
PyVista data object.
|
||
|
|
|
||
|
|
attr : pyvista.plotting.composite_mapper.CompositeAttributes
|
||
|
|
Parent attributes.
|
||
|
|
|
||
|
|
Notes
|
||
|
|
-----
|
||
|
|
This class employs VTK's flat indexing and allows for accessing both
|
||
|
|
the blocks of a composite dataset as well as the entire composite
|
||
|
|
dataset. If there is only one composite dataset, ``A``, which contains
|
||
|
|
datasets ``[b, c]``, the indexing would be ``[A, b, c]``.
|
||
|
|
|
||
|
|
If there are two composite datasets ``[B, C]`` in one composite
|
||
|
|
dataset, ``A``, each of which containing three additional datasets
|
||
|
|
``[d, e, f]``, and ``[g, h, i]``, respectively, then the head node,
|
||
|
|
``A``, would be the zero index, followed by the first child, ``B``,
|
||
|
|
followed by all the children of ``B``, ``[d, e, f]``. In data
|
||
|
|
structures, this flat indexing would be known as "Depth-first search"
|
||
|
|
and the entire indexing would be::
|
||
|
|
|
||
|
|
[A, B, d, e, f, C, g, h, i]
|
||
|
|
|
||
|
|
Note how the composite datasets themselves are capitalized and are
|
||
|
|
accessible in the flat indexing, and not just the datasets.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Add a sphere and a cube as a multiblock dataset to a plotter and then
|
||
|
|
change the visibility and color of the blocks. Note how the index of the
|
||
|
|
cube is ``1`` as the index of the entire multiblock is ``0``.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset)
|
||
|
|
>>> mapper.block_attr[1].color = 'b'
|
||
|
|
>>> mapper.block_attr[1].opacity = 0.1
|
||
|
|
>>> mapper.block_attr[1]
|
||
|
|
Composite Block Addr=... Attributes
|
||
|
|
Visible: None
|
||
|
|
Opacity: 0.1
|
||
|
|
Color: Color(name='blue', hex='#0000ffff', opacity=255)
|
||
|
|
Pickable None
|
||
|
|
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
"""
|
||
|
|
|
||
|
|
def __init__(self, block, attr):
|
||
|
|
"""Initialize the block attributes class."""
|
||
|
|
self._block = block
|
||
|
|
self.__attr = weakref.ref(attr)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def _attr(self):
|
||
|
|
"""Return the CompositeAttributes."""
|
||
|
|
return self.__attr()
|
||
|
|
|
||
|
|
@property
|
||
|
|
def _has_color(self):
|
||
|
|
"""Return if a block has its color set."""
|
||
|
|
return self._attr.HasBlockColor(self._block)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def _has_visibility(self):
|
||
|
|
"""Return if a block has its visibility set."""
|
||
|
|
return self._attr.HasBlockVisibility(self._block)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def _has_opacity(self):
|
||
|
|
"""Return if a block has its opacity set."""
|
||
|
|
return self._attr.HasBlockOpacity(self._block)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def _has_pickable(self):
|
||
|
|
"""Return if a block has its pickability set."""
|
||
|
|
return self._attr.HasBlockPickability(self._block)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def color(self): # numpydoc ignore=RT01
|
||
|
|
"""Get or set the color of a block.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Set the colors of a composite dataset to red and blue.
|
||
|
|
|
||
|
|
Note how the zero index is the entire multiblock, so we have to add 1
|
||
|
|
to our indexing to access the right block.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset)
|
||
|
|
>>> mapper.block_attr[1].color = 'r'
|
||
|
|
>>> mapper.block_attr[2].color = 'b'
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
"""
|
||
|
|
if not self._has_color:
|
||
|
|
return None
|
||
|
|
return Color(tuple(self._attr.GetBlockColor(self._block)))
|
||
|
|
|
||
|
|
@color.setter
|
||
|
|
def color(self, new_color):
|
||
|
|
if new_color is None:
|
||
|
|
self._attr.RemoveBlockColor(self._block)
|
||
|
|
self._attr.Modified()
|
||
|
|
return
|
||
|
|
self._attr.SetBlockColor(self._block, Color(new_color).float_rgb)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def visible(self) -> bool | None: # numpydoc ignore=RT01
|
||
|
|
"""Get or set the visibility of a block.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Hide the first block of a composite dataset.
|
||
|
|
|
||
|
|
Note how the zero index is the entire multiblock, so we have to add 1
|
||
|
|
to our indexing to access the right block.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset)
|
||
|
|
>>> mapper.block_attr[1].visible = False
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
"""
|
||
|
|
if not self._has_visibility:
|
||
|
|
return None
|
||
|
|
return self._attr.GetBlockVisibility(self._block)
|
||
|
|
|
||
|
|
@visible.setter
|
||
|
|
def visible(self, new_visible: bool | None):
|
||
|
|
if new_visible is None:
|
||
|
|
self._attr.RemoveBlockVisibility(self._block)
|
||
|
|
self._attr.Modified()
|
||
|
|
return
|
||
|
|
self._attr.SetBlockVisibility(self._block, new_visible)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def opacity(self) -> float | None: # numpydoc ignore=RT01
|
||
|
|
"""Get or set the opacity of a block.
|
||
|
|
|
||
|
|
If opacity has not been set this will be ``None``.
|
||
|
|
|
||
|
|
Warnings
|
||
|
|
--------
|
||
|
|
VTK 9.0.3 has a bug where changing the opacity to less than 1.0 also
|
||
|
|
changes the edge visibility on the block that is partially transparent.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Change the opacity of the second block of the dataset.
|
||
|
|
|
||
|
|
Note how the zero index is the entire multiblock, so we have to add 1
|
||
|
|
to our indexing to access the right block.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset)
|
||
|
|
>>> mapper.block_attr[2].opacity = 0.5
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
"""
|
||
|
|
if not self._has_opacity:
|
||
|
|
return None
|
||
|
|
return self._attr.GetBlockOpacity(self._block)
|
||
|
|
|
||
|
|
@opacity.setter
|
||
|
|
def opacity(self, new_opacity: float | None):
|
||
|
|
if new_opacity is None:
|
||
|
|
self._attr.RemoveBlockOpacity(self._block)
|
||
|
|
self._attr.Modified()
|
||
|
|
return
|
||
|
|
|
||
|
|
_check_range(new_opacity, (0, 1), 'opacity')
|
||
|
|
self._attr.SetBlockOpacity(self._block, new_opacity)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def pickable(self) -> bool | None: # numpydoc ignore=RT01
|
||
|
|
"""Get or set the pickability of a block.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Make the cube of a multiblock dataset pickable and the sphere unpickable.
|
||
|
|
|
||
|
|
Note how the zero index is the entire multiblock, so we have to add 1
|
||
|
|
to our indexing to access the right block.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset)
|
||
|
|
>>> mapper.block_attr[1].pickable = True
|
||
|
|
>>> mapper.block_attr[2].pickable = False
|
||
|
|
>>> pl.close()
|
||
|
|
|
||
|
|
See :ref:`composite_picking_example` for a full example using block
|
||
|
|
picking.
|
||
|
|
|
||
|
|
"""
|
||
|
|
if not self._has_pickable:
|
||
|
|
return None
|
||
|
|
return self._attr.GetBlockPickability(self._block)
|
||
|
|
|
||
|
|
@pickable.setter
|
||
|
|
def pickable(self, new_pickable: bool | None):
|
||
|
|
if new_pickable is None:
|
||
|
|
self._attr.RemoveBlockPickability(self._block)
|
||
|
|
self._attr.Modified()
|
||
|
|
return
|
||
|
|
self._attr.SetBlockPickability(self._block, new_pickable)
|
||
|
|
|
||
|
|
def __repr__(self):
|
||
|
|
"""Representation of block properties."""
|
||
|
|
return '\n'.join(
|
||
|
|
[
|
||
|
|
f'Composite Block {self._block.memory_address} Attributes',
|
||
|
|
f'Visible: {self.visible}',
|
||
|
|
f'Opacity: {self.opacity}',
|
||
|
|
f'Color: {self.color}',
|
||
|
|
f'Pickable {self.pickable}',
|
||
|
|
],
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class CompositeAttributes(
|
||
|
|
_NoNewAttrMixin,
|
||
|
|
_vtk.DisableVtkSnakeCase,
|
||
|
|
_vtk.vtkCompositeDataDisplayAttributes,
|
||
|
|
):
|
||
|
|
"""Block attributes.
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
mapper : pyvista.plotting.composite_mapper.CompositePolyDataMapper
|
||
|
|
Parent mapper.
|
||
|
|
|
||
|
|
dataset : pyvista.MultiBlock
|
||
|
|
Multiblock dataset.
|
||
|
|
|
||
|
|
Notes
|
||
|
|
-----
|
||
|
|
This class employs VTK's flat indexing and allows for accessing both
|
||
|
|
the blocks of a composite dataset as well as the entire composite
|
||
|
|
dataset. If there is only one composite dataset, ``A``, which contains
|
||
|
|
datasets ``[b, c]``, the indexing would be ``[A, b, c]``.
|
||
|
|
|
||
|
|
If there are two composite datasets ``[B, C]`` in one composite
|
||
|
|
dataset, ``A``, each of which containing three additional datasets
|
||
|
|
``[d, e, f]``, and ``[g, h, i]``, respectively, then the head node,
|
||
|
|
``A``, would be the zero index, followed by the first child, ``B``,
|
||
|
|
followed by all the children of ``B``, ``[d, e, f]``. In data
|
||
|
|
structures, this flat indexing would be known as "Depth-first search"
|
||
|
|
and the entire indexing would be::
|
||
|
|
|
||
|
|
[A, B, d, e, f, C, g, h, i]
|
||
|
|
|
||
|
|
Note how the composite datasets themselves are capitalized and are
|
||
|
|
accessible in the flat indexing, and not just the datasets.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Add a sphere and a cube as a multiblock dataset to a plotter and then
|
||
|
|
change the visibility and color of the blocks. Note how the index of the
|
||
|
|
cube is ``1`` as the index of the entire multiblock is ``0``.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset)
|
||
|
|
>>> mapper.block_attr[1].color = 'b'
|
||
|
|
>>> mapper.block_attr[1].opacity = 0.1
|
||
|
|
>>> mapper.block_attr[1]
|
||
|
|
Composite Block Addr=... Attributes
|
||
|
|
Visible: None
|
||
|
|
Opacity: 0.1
|
||
|
|
Color: Color(name='blue', hex='#0000ffff', opacity=255)
|
||
|
|
Pickable None
|
||
|
|
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
"""
|
||
|
|
|
||
|
|
def __init__(self, mapper, dataset):
|
||
|
|
"""Initialize CompositeAttributes."""
|
||
|
|
super().__init__()
|
||
|
|
mapper.SetCompositeDataDisplayAttributes(self)
|
||
|
|
self._dataset = dataset
|
||
|
|
|
||
|
|
def reset_visibilities(self):
|
||
|
|
"""Reset the visibility of all blocks.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Hide the first block of a composite dataset and then show all by
|
||
|
|
resetting visibilities.
|
||
|
|
|
||
|
|
Note how the zero index is the entire multiblock, so we have to add 1
|
||
|
|
to our indexing to access the right block.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset)
|
||
|
|
>>> mapper.block_attr[1].visible = False
|
||
|
|
>>> mapper.block_attr.reset_visibilities()
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
"""
|
||
|
|
self.RemoveBlockVisibilities()
|
||
|
|
|
||
|
|
def reset_pickabilities(self):
|
||
|
|
"""Reset the pickability of all blocks.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Make the cube of a multiblock dataset pickable and the sphere
|
||
|
|
unpickable, then reset it.
|
||
|
|
|
||
|
|
Note how the zero index is the entire multiblock, so we have to add 1
|
||
|
|
to our indexing to access the right block.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset)
|
||
|
|
>>> mapper.block_attr[1].pickable = True
|
||
|
|
>>> mapper.block_attr[2].pickable = False
|
||
|
|
>>> mapper.block_attr.reset_pickabilities()
|
||
|
|
>>> [
|
||
|
|
... mapper.block_attr[1].pickable,
|
||
|
|
... mapper.block_attr[2].pickable,
|
||
|
|
... ]
|
||
|
|
[None, None]
|
||
|
|
>>> pl.close()
|
||
|
|
|
||
|
|
"""
|
||
|
|
self.RemoveBlockPickabilities()
|
||
|
|
|
||
|
|
def reset_colors(self):
|
||
|
|
"""Reset the color of all blocks.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Set individual block colors and then reset them.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset, color='w')
|
||
|
|
>>> mapper.block_attr[1].color = 'r'
|
||
|
|
>>> mapper.block_attr[2].color = 'b'
|
||
|
|
>>> mapper.block_attr.reset_colors()
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
"""
|
||
|
|
self.RemoveBlockColors()
|
||
|
|
|
||
|
|
def reset_opacities(self):
|
||
|
|
"""Reset the opacities of all blocks.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Change the opacity of the second block of the dataset then reset all
|
||
|
|
opacities.
|
||
|
|
|
||
|
|
Note how the zero index is the entire multiblock, so we have to add 1
|
||
|
|
to our indexing to access the right block.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset)
|
||
|
|
>>> mapper.block_attr[2].opacity = 0.5
|
||
|
|
>>> mapper.block_attr.reset_opacities()
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
"""
|
||
|
|
self.RemoveBlockOpacities()
|
||
|
|
|
||
|
|
def get_block(self, index):
|
||
|
|
"""Return a block by its flat index.
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
index : int
|
||
|
|
Flat index of the block to retrieve.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
pyvista.DataObject
|
||
|
|
PyVista data object.
|
||
|
|
|
||
|
|
Notes
|
||
|
|
-----
|
||
|
|
This method employs VTK's flat indexing and allows for accessing both
|
||
|
|
the blocks of a composite dataset as well as the entire composite
|
||
|
|
dataset. If there is only one composite dataset, ``A``, which contains
|
||
|
|
datasets ``[b, c]``, the indexing would be ``[A, b, c]``.
|
||
|
|
|
||
|
|
If there are two composite datasets ``[B, C]`` in one composite
|
||
|
|
dataset, ``A``, each of which containing three additional datasets
|
||
|
|
``[d, e, f]``, and ``[g, h, i]``, respectively, then the head node,
|
||
|
|
``A``, would be the zero index, followed by the first child, ``B``,
|
||
|
|
followed by all the children of ``B``, ``[d, e, f]``. In data
|
||
|
|
structures, this flat indexing would be known as "Depth-first search"
|
||
|
|
and the entire indexing would be::
|
||
|
|
|
||
|
|
[A, B, d, e, f, C, g, h, i]
|
||
|
|
|
||
|
|
Note how the composite datasets themselves are capitalized and are
|
||
|
|
accessible in the flat indexing, and not just the datasets.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Add a composite dataset to a plotter and access its block attributes.
|
||
|
|
Note how the zero index is the entire multiblock and you can use ``1``
|
||
|
|
and ``2`` to access the individual sub-blocks.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset)
|
||
|
|
>>> mapper.block_attr.get_block(0)
|
||
|
|
MultiBlock (...)
|
||
|
|
N Blocks: 2
|
||
|
|
X Bounds: -5.000e-01, 5.000e-01
|
||
|
|
Y Bounds: -5.000e-01, 5.000e-01
|
||
|
|
Z Bounds: -5.000e-01, 1.500e+00
|
||
|
|
|
||
|
|
Note this is the same as using ``__getitem__``
|
||
|
|
|
||
|
|
>>> mapper.block_attr[0]
|
||
|
|
Composite Block Addr=... Attributes
|
||
|
|
Visible: None
|
||
|
|
Opacity: None
|
||
|
|
Color: None
|
||
|
|
Pickable None
|
||
|
|
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
if vtk_version_info <= (9, 0, 3): # pragma: no cover
|
||
|
|
vtk_ref = _vtk.reference(0) # needed for <=9.0.3
|
||
|
|
block = self.DataObjectFromIndex(index, self._dataset, vtk_ref) # type: ignore[arg-type]
|
||
|
|
else:
|
||
|
|
block = self.DataObjectFromIndex(index, self._dataset)
|
||
|
|
except OverflowError:
|
||
|
|
msg = f'Invalid block key: {index}'
|
||
|
|
raise KeyError(msg) from None
|
||
|
|
if block is None and index > len(self) - 1:
|
||
|
|
msg = f'index {index} is out of bounds. There are only {len(self)} blocks.'
|
||
|
|
raise KeyError(msg) from None
|
||
|
|
return block
|
||
|
|
|
||
|
|
def __getitem__(self, index):
|
||
|
|
"""Return a block attribute by its flat index."""
|
||
|
|
return BlockAttributes(self.get_block(index), self)
|
||
|
|
|
||
|
|
def __len__(self):
|
||
|
|
"""Return the number of blocks in this dataset."""
|
||
|
|
from pyvista import MultiBlock # avoid circular # noqa: PLC0415
|
||
|
|
|
||
|
|
# start with 1 as there is always a composite dataset and this is the
|
||
|
|
# root of the tree
|
||
|
|
cc = 1
|
||
|
|
for dataset in self._dataset:
|
||
|
|
if isinstance(dataset, MultiBlock):
|
||
|
|
cc += len(dataset) + 1 # include the block itself
|
||
|
|
else:
|
||
|
|
cc += 1
|
||
|
|
return cc
|
||
|
|
|
||
|
|
def __iter__(self):
|
||
|
|
"""Return an iterator of all the block attributes."""
|
||
|
|
for ii in range(len(self)):
|
||
|
|
yield self[ii]
|
||
|
|
|
||
|
|
|
||
|
|
class CompositePolyDataMapper(
|
||
|
|
_BaseMapper,
|
||
|
|
(
|
||
|
|
_vtk.vtkCompositePolyDataMapper # type: ignore[misc]
|
||
|
|
if vtk_version_info >= (9, 3)
|
||
|
|
else _vtk.vtkCompositePolyDataMapper2
|
||
|
|
),
|
||
|
|
):
|
||
|
|
"""Composite PolyData mapper.
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
dataset : pyvista.MultiBlock, optional
|
||
|
|
Multiblock dataset.
|
||
|
|
|
||
|
|
theme : pyvista.plotting.themes.Theme, optional
|
||
|
|
Plot-specific theme.
|
||
|
|
|
||
|
|
color_missing_with_nan : bool, optional
|
||
|
|
Color any missing values with the ``nan_color``. This is useful
|
||
|
|
when not all blocks of the composite dataset have the specified
|
||
|
|
``scalars``.
|
||
|
|
|
||
|
|
interpolate_before_map : bool, optional
|
||
|
|
Enabling makes for a smoother scalars display. Default is
|
||
|
|
``True``. When ``False``, OpenGL will interpolate the
|
||
|
|
mapped colors which can result is showing colors that are
|
||
|
|
not present in the color map.
|
||
|
|
|
||
|
|
"""
|
||
|
|
|
||
|
|
@_deprecate_positional_args(allowed=['dataset'])
|
||
|
|
def __init__( # noqa: PLR0917
|
||
|
|
self,
|
||
|
|
dataset=None,
|
||
|
|
theme=None,
|
||
|
|
color_missing_with_nan=None,
|
||
|
|
interpolate_before_map=None,
|
||
|
|
):
|
||
|
|
"""Initialize this composite mapper."""
|
||
|
|
super().__init__(theme=theme)
|
||
|
|
# this must be added to set the color, opacity, and visibility of
|
||
|
|
# individual blocks
|
||
|
|
self._attr = CompositeAttributes(self, dataset)
|
||
|
|
self.dataset = dataset
|
||
|
|
|
||
|
|
if color_missing_with_nan is not None:
|
||
|
|
self.color_missing_with_nan = color_missing_with_nan
|
||
|
|
if interpolate_before_map is not None:
|
||
|
|
self.interpolate_before_map = interpolate_before_map
|
||
|
|
|
||
|
|
self._orig_scalars_name: str | None = None
|
||
|
|
|
||
|
|
@property
|
||
|
|
def dataset(self) -> pyvista.MultiBlock: # numpydoc ignore=RT01
|
||
|
|
"""Return the composite dataset assigned to this mapper.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset)
|
||
|
|
>>> mapper.dataset
|
||
|
|
MultiBlock (...)
|
||
|
|
N Blocks: 2
|
||
|
|
X Bounds: -5.000e-01, 5.000e-01
|
||
|
|
Y Bounds: -5.000e-01, 5.000e-01
|
||
|
|
Z Bounds: -5.000e-01, 1.500e+00
|
||
|
|
|
||
|
|
"""
|
||
|
|
return self._dataset
|
||
|
|
|
||
|
|
@dataset.setter
|
||
|
|
def dataset(self, obj: pyvista.MultiBlock):
|
||
|
|
self.SetInputDataObject(obj)
|
||
|
|
self._dataset = obj
|
||
|
|
self._attr._dataset = obj
|
||
|
|
|
||
|
|
@property
|
||
|
|
def block_attr(self) -> CompositeAttributes: # numpydoc ignore=RT01
|
||
|
|
"""Return the block attributes.
|
||
|
|
|
||
|
|
Notes
|
||
|
|
-----
|
||
|
|
``block_attr`` employs VTK's flat indexing and allows for accessing
|
||
|
|
both the blocks of a composite dataset as well as the entire composite
|
||
|
|
dataset. If there is only one composite dataset, ``A``, which contains
|
||
|
|
datasets ``[b, c]``, the indexing would be ``[A, b, c]``.
|
||
|
|
|
||
|
|
If there are two composite datasets ``[B, C]`` in one composite
|
||
|
|
dataset, ``A``, each of which containing three additional datasets
|
||
|
|
``[d, e, f]``, and ``[g, h, i]``, respectively, then the head node,
|
||
|
|
``A``, would be the zero index, followed by the first child, ``B``,
|
||
|
|
followed by all the children of ``B``, ``[d, e, f]``. In data
|
||
|
|
structures, this flat indexing would be known as "Depth-first search"
|
||
|
|
and the entire indexing would be::
|
||
|
|
|
||
|
|
[A, B, d, e, f, C, g, h, i]
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Add a sphere and a cube as a multiblock dataset to a plotter and then
|
||
|
|
change the visibility and color of the blocks.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset)
|
||
|
|
>>> mapper.block_attr[1].color = 'b'
|
||
|
|
>>> mapper.block_attr[1].opacity = 0.1
|
||
|
|
>>> mapper.block_attr[1]
|
||
|
|
Composite Block Addr=... Attributes
|
||
|
|
Visible: None
|
||
|
|
Opacity: 0.1
|
||
|
|
Color: Color(name='blue', hex='#0000ffff', opacity=255)
|
||
|
|
Pickable None
|
||
|
|
|
||
|
|
"""
|
||
|
|
return self._attr
|
||
|
|
|
||
|
|
@property
|
||
|
|
def color_missing_with_nan(self) -> bool: # numpydoc ignore=RT01
|
||
|
|
"""Color missing arrays with the NaN color.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Enable coloring missing values with NaN.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> dataset[0].point_data['data'] = dataset[0].points[:, 2]
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(
|
||
|
|
... dataset, scalars='data', show_scalar_bar=False
|
||
|
|
... )
|
||
|
|
>>> pv.global_theme.nan_color = 'r'
|
||
|
|
>>> mapper.color_missing_with_nan = True
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
"""
|
||
|
|
return self.GetColorMissingArraysWithNanColor()
|
||
|
|
|
||
|
|
@color_missing_with_nan.setter
|
||
|
|
def color_missing_with_nan(self, value: bool):
|
||
|
|
self.SetColorMissingArraysWithNanColor(value)
|
||
|
|
|
||
|
|
def set_unique_colors(
|
||
|
|
self,
|
||
|
|
color_cycler: bool | str | cycler.Cycler[str, ColorLike] | Sequence[ColorLike] = True, # noqa: FBT001, FBT002
|
||
|
|
):
|
||
|
|
"""Set each block of the dataset to a unique color.
|
||
|
|
|
||
|
|
This uses ``matplotlib``'s color cycler by default.
|
||
|
|
|
||
|
|
When a custom color cycler, or a sequence of
|
||
|
|
color-like objects, is passed it sets the blocks
|
||
|
|
to the corresponding colors.
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
color_cycler : bool | str | cycler.Cycler | sequence[ColorLike]
|
||
|
|
The sequence of colors to cycle through,
|
||
|
|
if ``True``, uses matplotlib cycler.
|
||
|
|
|
||
|
|
Examples
|
||
|
|
--------
|
||
|
|
Set each block of the composite dataset to a unique color.
|
||
|
|
|
||
|
|
>>> import pyvista as pv
|
||
|
|
>>> dataset = pv.MultiBlock([pv.Cube(), pv.Sphere(center=(0, 0, 1))])
|
||
|
|
>>> pl = pv.Plotter()
|
||
|
|
>>> actor, mapper = pl.add_composite(dataset)
|
||
|
|
>>> mapper.set_unique_colors()
|
||
|
|
>>> mapper.block_attr[1].color
|
||
|
|
Color(name='tab:orange', hex='#ff7f0eff', opacity=255)
|
||
|
|
>>> mapper.block_attr[2].color
|
||
|
|
Color(name='tab:green', hex='#2ca02cff', opacity=255)
|
||
|
|
|
||
|
|
>>> pl.show()
|
||
|
|
|
||
|
|
"""
|
||
|
|
self.scalar_visibility = False
|
||
|
|
|
||
|
|
if isinstance(color_cycler, bool):
|
||
|
|
colors = cycle(get_cycler('matplotlib'))
|
||
|
|
else:
|
||
|
|
colors = cycle(get_cycler(color_cycler))
|
||
|
|
|
||
|
|
for attr in self.block_attr:
|
||
|
|
attr.color = next(colors)['color']
|
||
|
|
|
||
|
|
@_deprecate_positional_args(allowed=['scalars_name'])
|
||
|
|
def set_scalars( # noqa: PLR0917
|
||
|
|
self,
|
||
|
|
scalars_name,
|
||
|
|
preference,
|
||
|
|
component,
|
||
|
|
annotations,
|
||
|
|
rgb,
|
||
|
|
scalar_bar_args,
|
||
|
|
n_colors,
|
||
|
|
nan_color,
|
||
|
|
above_color,
|
||
|
|
below_color,
|
||
|
|
clim,
|
||
|
|
cmap,
|
||
|
|
flip_scalars,
|
||
|
|
log_scale,
|
||
|
|
):
|
||
|
|
"""Set the scalars of the mapper.
|
||
|
|
|
||
|
|
Parameters
|
||
|
|
----------
|
||
|
|
scalars_name : str
|
||
|
|
Name of the scalars in the dataset. Must already exist in at least
|
||
|
|
of the blocks.
|
||
|
|
|
||
|
|
preference : str
|
||
|
|
For each block, when ``block.n_points == block.n_cells`` and
|
||
|
|
setting scalars, this parameter sets how the scalars will be mapped
|
||
|
|
to the mesh. Default ``'point'``, causes the scalars will be
|
||
|
|
associated with the mesh points. Can be either ``'point'`` or
|
||
|
|
``'cell'``.
|
||
|
|
|
||
|
|
component : int
|
||
|
|
Set component of vector valued scalars to plot. Must be
|
||
|
|
nonnegative, if supplied. If ``None``, the magnitude of
|
||
|
|
the vector is plotted.
|
||
|
|
|
||
|
|
annotations : dict
|
||
|
|
Pass a dictionary of annotations. Keys are the float
|
||
|
|
values in the scalars range to annotate on the scalar bar
|
||
|
|
and the values are the string annotations.
|
||
|
|
|
||
|
|
rgb : bool
|
||
|
|
If the ``scalars_name`` corresponds to a 2 dimensional array, plot
|
||
|
|
those values as RGB(A) colors.
|
||
|
|
|
||
|
|
scalar_bar_args : dict
|
||
|
|
Dictionary of keyword arguments to pass when adding the
|
||
|
|
scalar bar to the scene. For options, see
|
||
|
|
:func:`pyvista.Plotter.add_scalar_bar`.
|
||
|
|
|
||
|
|
n_colors : int
|
||
|
|
Number of colors to use when displaying scalars.
|
||
|
|
|
||
|
|
nan_color : ColorLike
|
||
|
|
The color to use for all ``NaN`` values in the plotted
|
||
|
|
scalar array.
|
||
|
|
|
||
|
|
above_color : ColorLike
|
||
|
|
Solid color for values below the scalars range
|
||
|
|
(``clim``). This will automatically set the scalar bar
|
||
|
|
``above_label`` to ``'above'``.
|
||
|
|
|
||
|
|
below_color : ColorLike
|
||
|
|
Solid color for values below the scalars range
|
||
|
|
(``clim``). This will automatically set the scalar bar
|
||
|
|
``below_label`` to ``'below'``.
|
||
|
|
|
||
|
|
clim : Sequence
|
||
|
|
Color bar range for scalars. Defaults to minimum and
|
||
|
|
maximum of scalars array. Example: ``[-1, 2]``. ``rng``
|
||
|
|
is also an accepted alias for this.
|
||
|
|
|
||
|
|
cmap : str | list | pyvista.LookupTable
|
||
|
|
Name of the Matplotlib colormap to use when mapping the
|
||
|
|
``scalars``. See available Matplotlib colormaps. Only applicable
|
||
|
|
for when displaying ``scalars``.
|
||
|
|
``colormap`` is also an accepted alias for this. If
|
||
|
|
``colorcet`` or ``cmocean`` are installed, their colormaps can be
|
||
|
|
specified by name.
|
||
|
|
|
||
|
|
You can also specify a list of colors to override an existing
|
||
|
|
colormap with a custom one. For example, to create a three color
|
||
|
|
colormap you might specify ``['green', 'red', 'blue']``.
|
||
|
|
|
||
|
|
This parameter also accepts a :class:`pyvista.LookupTable`. If this
|
||
|
|
is set, all parameters controlling the color map like ``n_colors``
|
||
|
|
will be ignored.
|
||
|
|
are installed, their colormaps can be specified by name.
|
||
|
|
|
||
|
|
flip_scalars : bool
|
||
|
|
Flip direction of cmap. Most colormaps allow ``*_r``
|
||
|
|
suffix to do this as well.
|
||
|
|
|
||
|
|
log_scale : bool
|
||
|
|
Use log scale when mapping data to colors. Scalars less
|
||
|
|
than zero are mapped to the smallest representable
|
||
|
|
positive float.
|
||
|
|
|
||
|
|
Returns
|
||
|
|
-------
|
||
|
|
dict
|
||
|
|
Dictionary of scalar bar arguments.
|
||
|
|
|
||
|
|
"""
|
||
|
|
self._orig_scalars_name = scalars_name
|
||
|
|
|
||
|
|
field, scalars_name, dtype = self._dataset._activate_plotting_scalars(
|
||
|
|
scalars_name=scalars_name,
|
||
|
|
preference=preference,
|
||
|
|
component=component,
|
||
|
|
rgb=rgb,
|
||
|
|
)
|
||
|
|
|
||
|
|
self.scalar_visibility = True
|
||
|
|
if rgb:
|
||
|
|
self.color_mode = 'direct'
|
||
|
|
return scalar_bar_args
|
||
|
|
else:
|
||
|
|
self.scalar_map_mode = field.name.lower()
|
||
|
|
|
||
|
|
scalar_bar_args.setdefault('title', scalars_name)
|
||
|
|
|
||
|
|
if clim is None:
|
||
|
|
clim = self._dataset.get_data_range(scalars_name, allow_missing=True)
|
||
|
|
self.scalar_range = clim
|
||
|
|
|
||
|
|
if log_scale and clim[0] <= 0:
|
||
|
|
clim = [sys.float_info.min, clim[1]]
|
||
|
|
|
||
|
|
if isinstance(cmap, pyvista.LookupTable):
|
||
|
|
self.lookup_table = cmap
|
||
|
|
else:
|
||
|
|
if dtype == np.bool_:
|
||
|
|
cats = np.array([b'False', b'True'], dtype='|S5')
|
||
|
|
values = np.array([0, 1])
|
||
|
|
n_colors = 2
|
||
|
|
scalar_bar_args.setdefault('n_labels', 0)
|
||
|
|
self.lookup_table.SetAnnotations(convert_array(values), convert_string_array(cats))
|
||
|
|
clim = [-0.5, 1.5]
|
||
|
|
|
||
|
|
self.lookup_table.log_scale = log_scale
|
||
|
|
|
||
|
|
if isinstance(annotations, dict):
|
||
|
|
self.lookup_table.annotations = annotations
|
||
|
|
|
||
|
|
# self.lookup_table.SetNumberOfTableValues(n_colors)
|
||
|
|
if nan_color:
|
||
|
|
self.lookup_table.nan_color = nan_color
|
||
|
|
if above_color:
|
||
|
|
self.lookup_table.above_range_color = above_color
|
||
|
|
scalar_bar_args.setdefault('above_label', 'above')
|
||
|
|
if below_color:
|
||
|
|
self.lookup_table.below_range_color = below_color
|
||
|
|
scalar_bar_args.setdefault('below_label', 'below')
|
||
|
|
|
||
|
|
if cmap is None:
|
||
|
|
cmap = pyvista.global_theme.cmap if self._theme is None else self._theme.cmap
|
||
|
|
|
||
|
|
if cmap is not None:
|
||
|
|
self.lookup_table.apply_cmap(cmap, n_colors, flip=flip_scalars)
|
||
|
|
elif flip_scalars:
|
||
|
|
self.lookup_table.SetHueRange(0.0, 0.66667)
|
||
|
|
else:
|
||
|
|
self.lookup_table.SetHueRange(0.66667, 0.0)
|
||
|
|
|
||
|
|
return scalar_bar_args
|