File: volume_property.py

package info (click to toggle)
python-pyvista 0.44.1-11
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 159,804 kB
  • sloc: python: 72,164; sh: 118; makefile: 68
file content (368 lines) | stat: -rw-r--r-- 13,104 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
"""This module contains the VolumeProperty class."""

from __future__ import annotations

import pyvista
from pyvista.core.utilities.misc import no_new_attr

from . import _vtk


@no_new_attr
class VolumeProperty(_vtk.vtkVolumeProperty):
    """Wrap the VTK class vtkVolumeProperty.

    This class is used to represent common properties associated with volume
    rendering. This includes properties for determining the type of
    interpolation to use when sampling a volume, the color of a volume, the
    scalar opacity of a volume, the gradient opacity of a volume, and the
    shading parameters of a volume.

    Parameters
    ----------
    lookup_table : pyvista.LookupTable, optional
        Lookup table to set the color and opacity transfer functions.

    interpolation_type : str, optional
        Value must be either ``'linear'`` or ``'nearest'``.

    ambient : float, optional
        When lighting is enabled, this is the amount of light in
        the range of 0 to 1 (default 0.0) that reaches the actor
        when not directed at the light source emitted from the
        viewer.

    diffuse : float, optional
        The diffuse lighting coefficient.

    specular : float, optional
        The specular lighting coefficient.

    specular_power : float, optional
        The specular power. Between 0.0 and 128.0.

    shade : bool, optional
        Enable or disable volume shading.  If shading is turned off, then the
        mapper for the volume will not perform shading calculations. If shading
        is turned on, the mapper may perform shading calculations - in some
        cases shading does not apply (for example, in a maximum intensity
        projection) and therefore shading will not be performed even if this
        flag is on. For a compositing type of mapper, turning shading off is
        generally the same as setting ``ambient=1``, ``diffuse=0``,
        ``specular=0``. Shading can be independently turned on/off per
        component.

    opacity_unit_distance : float, optional
        This is the unit distance on which the scalar opacity transfer function
        is defined. By default this is 1.0, meaning that over a distance of 1.0
        units, a given opacity (from the transfer function) is
        accumulated. This is adjusted for the actual sampling distance during
        rendering.

    Examples
    --------
    Create a sample dataset from perlin noise and apply a lookup table to the
    :class:`VolumeProperty`.

    >>> import pyvista as pv
    >>> noise = pv.perlin_noise(1, (1, 3, 5), (0, 0, 0))
    >>> grid = pv.sample_function(
    ...     noise, [0, 3.0, -0, 1.0, 0, 1.0], dim=(40, 40, 40)
    ... )
    >>> grid['scalars'] -= grid['scalars'].min()
    >>> grid['scalars'] *= 255 / grid['scalars'].max()
    >>> pl = pv.Plotter()
    >>> actor = pl.add_volume(grid, show_scalar_bar=False)
    >>> lut = pv.LookupTable(cmap='bwr')
    >>> lut.apply_opacity([1.0, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 0.3])
    >>> actor.prop.apply_lookup_table(lut)
    >>> pl.show()


    """

    def __init__(
        self,
        lookup_table=None,
        interpolation_type=None,
        ambient=None,
        diffuse=None,
        specular=None,
        specular_power=None,
        shade=None,
        opacity_unit_distance=None,
    ):
        """Initialize the vtkVolumeProperty class."""
        super().__init__()
        if lookup_table is not None:
            self.apply_lookup_table(lookup_table)
        if interpolation_type is not None:
            self.interpolation_type = interpolation_type
        if ambient is not None:
            self.ambient = ambient
        if diffuse is not None:
            self.diffuse = diffuse
        if specular is not None:
            self.specular = specular
        if specular_power is not None:
            self.specular_power = specular_power
        if shade is not None:
            self.shade = shade
        if opacity_unit_distance is not None:
            self.opacity_unit_distance = opacity_unit_distance

    def apply_lookup_table(self, lookup_table: pyvista.LookupTable):
        """Apply a lookup table to the volume property.

        Applies both the color and opacity of the lookup table as transfer
        functions.

        Parameters
        ----------
        lookup_table : pyvista.LookupTable, optional
            Lookup table to set the color and opacity transfer functions.

        Examples
        --------
        Plot perlin noise volumetrically using a custom lookup table.

        >>> import pyvista as pv
        >>> noise = pv.perlin_noise(1, (1, 3, 5), (0, 0, 0))
        >>> grid = pv.sample_function(
        ...     noise, [0, 3.0, -0, 1.0, 0, 1.0], dim=(40, 40, 40)
        ... )
        >>> grid['scalars'] -= grid['scalars'].min()
        >>> grid['scalars'] *= 255 / grid['scalars'].max()
        >>> pl = pv.Plotter()
        >>> actor = pl.add_volume(grid, show_scalar_bar=False)
        >>> lut = pv.LookupTable(cmap='bwr')
        >>> lut.apply_opacity([1.0, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 0.3])
        >>> actor.prop.apply_lookup_table(lut)
        >>> pl.show()

        """
        if not isinstance(lookup_table, pyvista.LookupTable):
            raise TypeError('`lookup_table` must be a `pyvista.LookupTable`')

        self.SetColor(lookup_table.to_color_tf())
        self.SetScalarOpacity(lookup_table.to_opacity_tf())

    @property
    def interpolation_type(self) -> str:  # numpydoc ignore=RT01
        """Return or set the interpolation type.

        Value must be either ``'linear'`` or ``'nearest'``.

        Examples
        --------
        Create a sample :class:`pyvista.ImageData` dataset.

        >>> import numpy as np
        >>> import pyvista as pv
        >>> n = 21
        >>> c = -(n - 1) / 2
        >>> vol = pv.ImageData(dimensions=(n, n, n), origin=(c, c, c))
        >>> scalars = np.linalg.norm(vol.points, axis=1)
        >>> scalars *= 255 / scalars.max()
        >>> vol['scalars'] = scalars

        Demonstrate nearest (default) interpolation.

        >>> pl = pv.Plotter()
        >>> actor = pl.add_volume(
        ...     vol,
        ...     show_scalar_bar=False,
        ...     opacity=[0.3, 0.0, 0.05, 0.0, 0.0, 0.0, 1.0, 0.0],
        ...     cmap='plasma',
        ... )
        >>> actor.prop.interpolation_type = 'nearest'
        >>> pl.show()

        Demonstrate linear interpolation.

        >>> pl = pv.Plotter()
        >>> actor = pl.add_volume(
        ...     vol,
        ...     show_scalar_bar=False,
        ...     opacity=[0.3, 0.0, 0.05, 0.0, 0.0, 0.0, 1.0, 0.0],
        ...     cmap='plasma',
        ... )
        >>> actor.prop.interpolation_type = 'linear'
        >>> pl.show()

        """
        return self.GetInterpolationTypeAsString().split()[0].lower()

    @interpolation_type.setter
    def interpolation_type(self, value: str):  # numpydoc ignore=GL08
        if value == 'linear':
            self.SetInterpolationTypeToLinear()
        elif value == 'nearest':
            self.SetInterpolationTypeToNearest()
        else:
            raise ValueError('`interpolation_type` must be either "linear" or "nearest"')

    @property
    def opacity_unit_distance(self) -> float:  # numpydoc ignore=RT01
        """Return or set the opacity unit distance.

        This is the unit distance on which the scalar opacity transfer function
        is defined.

        By default this is 1.0, meaning that over a distance of 1.0 units, a
        given opacity (from the transfer function) is accumulated. This is
        adjusted for the actual sampling distance during rendering.
        """
        return self.GetScalarOpacityUnitDistance()

    @opacity_unit_distance.setter
    def opacity_unit_distance(self, value: float):  # numpydoc ignore=GL08
        self.SetScalarOpacityUnitDistance(value)

    @property
    def shade(self) -> bool:  # numpydoc ignore=RT01
        """Return or set shading of a volume.

        If shading is turned off, then the mapper for the volume will not
        perform shading calculations. If shading is turned on, the mapper may
        perform shading calculations - in some cases shading does not apply
        (for example, in a maximum intensity projection) and therefore shading
        will not be performed even if this flag is on. For a compositing type
        of mapper, turning shading off is generally the same as setting
        ``ambient=1``, ``diffuse=0``, ``specular=0``. Shading can be
        independently turned on/off per component.

        """
        return bool(self.GetShade())

    @shade.setter
    def shade(self, value: bool):  # numpydoc ignore=GL08
        self.SetShade(value)

    @property
    def independent_components(self) -> bool:  # numpydoc ignore=RT01
        """Return or set independent components.

        If ``False``, then you must have either 2 or 4 component data.
        For 2 component data, the first is passed through the
        first color transfer function and the second component is passed
        through the first scalar opacity (and gradient opacity) transfer
        function. Normals will be generated off of the second component. When
        using gradient based opacity modulation, the gradients are computed off
        of the second component.

        For 4 component data, the first three will directly represent RGB (no
        lookup table). The fourth component will be passed through the first
        scalar opacity transfer function for opacity and first gradient opacity
        transfer function for gradient based opacity modulation. Normals will
        be generated from the fourth component. When using gradient based
        opacity modulation, the gradients are computed off of the fourth
        component.
        """
        return bool(self.GetIndependentComponents())

    @independent_components.setter
    def independent_components(self, value: bool):  # numpydoc ignore=GL08
        self.SetIndependentComponents(value)

    @property
    def ambient(self) -> float:  # numpydoc ignore=RT01
        """Return or set ambient lighting coefficient.

        This is the amount of light in the range of 0 to 1 (default 0.0) that
        reaches the actor when not directed at the light source emitted from
        the viewer.

        Changing attribute has no effect unless :attr:`VolumeProperty.shade` is
        set to ``True``.

        """
        return self.GetAmbient()

    @ambient.setter
    def ambient(self, value: float):  # numpydoc ignore=GL08
        self.SetAmbient(value)

    @property
    def diffuse(self) -> float:  # numpydoc ignore=RT01
        """Return or set the diffuse lighting coefficient.

        This is the scattering of light by reflection or transmission. Diffuse
        reflection results when light strikes an irregular surface such as a
        frosted window or the surface of a frosted or coated light bulb.

        Changing attribute has no effect unless :attr:`VolumeProperty.shade` is
        set to ``True``.

        """
        return self.GetDiffuse()

    @diffuse.setter
    def diffuse(self, value: float):  # numpydoc ignore=GL08
        self.SetDiffuse(value)

    @property
    def specular(self) -> float:  # numpydoc ignore=RT01
        """Return or set specular.

        Default 0.0

        Specular lighting simulates the bright spot of a light that appears on
        shiny objects.

        Changing attribute has no effect unless :attr:`VolumeProperty.shade` is
        set to ``True``.

        """
        return self.GetSpecular()

    @specular.setter
    def specular(self, value: float):  # numpydoc ignore=GL08
        self.SetSpecular(value)

    @property
    def specular_power(self) -> float:  # numpydoc ignore=RT01
        """Return or set specular power.

        The specular power. Between 0.0 and 128.0. Default 10.0

        """
        return self.GetSpecularPower()

    @specular_power.setter
    def specular_power(self, value: float):  # numpydoc ignore=GL08
        self.SetSpecularPower(value)

    def copy(self) -> VolumeProperty:
        """Create a deep copy of this property.

        Returns
        -------
        pyvista.plotting.volume_property.VolumeProperty
            Deep copy of this property.

        """
        new_prop = VolumeProperty()
        new_prop.DeepCopy(self)
        return new_prop

    def __repr__(self):
        """Representation of this property."""
        props = [
            f'{type(self).__name__} ({hex(id(self))})',
        ]

        for attr in dir(self):
            if not attr.startswith('_') and attr[0].islower():
                name = ' '.join(attr.split('_')).capitalize() + ':'
                try:
                    value = getattr(self, attr)
                except AttributeError:  # pragma:no cover
                    continue
                if callable(value):
                    continue
                if isinstance(value, str):
                    value = f'"{value}"'
                props.append(f'  {name:28s} {value}')

        return '\n'.join(props)