Source code for colour_visuals.rgb_scatter

# !/usr/bin/env python
"""
RGB Scatter Visuals
===================

Define the *RGB* scatter visuals:

-   :class:`colour_visuals.VisualRGBScatter3D`
"""

from __future__ import annotations

import typing

import numpy as np
import pygfx as gfx
from colour import RGB_to_XYZ
from colour.constants import EPSILON

if typing.TYPE_CHECKING:
    from colour.hints import (
        Any,
        ArrayLike,
        LiteralColourspaceModel,
        LiteralRGBColourspace,
        NDArray,
        Sequence,
    )
    from colour.models import RGB_Colourspace

from colour.plotting import (
    colourspace_model_axis_reorder,
)
from colour.utilities import as_float_array

from colour_visuals.common import (
    XYZ_to_colourspace_model,
    append_channel,
    as_contiguous_array,
)
from colour_visuals.visual import (
    MixinPropertyColour,
    MixinPropertyColourspace,
    MixinPropertyKwargs,
    MixinPropertyModel,
    MixinPropertyOpacity,
    MixinPropertySize,
    Visual,
    visual_property,
)

__author__ = "Colour Developers"
__copyright__ = "Copyright 2023 Colour Developers"
__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
__maintainer__ = "Colour Developers"
__email__ = "colour-developers@colour-science.org"
__status__ = "Production"

__all__ = ["VisualRGBScatter3D"]


[docs] class VisualRGBScatter3D( MixinPropertyColour, MixinPropertyColourspace, MixinPropertyKwargs, MixinPropertyModel, MixinPropertyOpacity, MixinPropertySize, Visual, ): """ Create a 3D *RGB* scatter visual. Parameters ---------- RGB *RGB* colourspace array. colourspace *RGB* colourspace of the *RGB* array. ``colourspace`` can be of any type or form supported by the :func:`colour.plotting.common.filter_RGB_colourspaces` definition. model Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute for the list of supported colourspace models. colour Colour of the visual, if *None*, the colour is computed from the visual geometry. opacity Opacity of the visual. size Size of the visual points. Other Parameters ---------------- kwargs See the documentation of the supported conversion definitions. Attributes ---------- - :attr:`~colour_visuals.VisualRGBScatter3D.RGB` - :attr:`~colour_visuals.VisualRGBScatter3D.colourspace` - :attr:`~colour_visuals.VisualRGBScatter3D.model` - :attr:`~colour_visuals.VisualRGBScatter3D.colour` - :attr:`~colour_visuals.VisualRGBScatter3D.opacity` - :attr:`~colour_visuals.VisualRGBScatter3D.size` - :attr:`~colour_visuals.VisualRGBScatter3D.kwargs` Methods ------- - :meth:`~colour_visuals.VisualRGBScatter3D.__init__` - :meth:`~colour_visuals.VisualRGBScatter3D.update` Examples -------- >>> import os >>> import pylinalg as la >>> from colour.utilities import suppress_stdout >>> from wgpu.gui.auto import WgpuCanvas >>> with suppress_stdout(): ... canvas = WgpuCanvas(size=(960, 540)) ... scene = gfx.Scene() ... scene.add( ... gfx.Background( ... None, gfx.BackgroundMaterial(np.array([0.18, 0.18, 0.18])) ... ) ... ) ... visual = VisualRGBScatter3D(np.random.random([24, 32, 3])) ... visual.local.rotation = la.quat_from_euler((-np.pi / 4, 0), order="XY") ... camera = gfx.PerspectiveCamera(50, 16 / 9) ... camera.show_object(visual, up=np.array([0, 0, 1]), scale=1.25) ... scene.add(visual) ... if os.environ.get("CI") is None: ... gfx.show(scene, camera=camera, canvas=canvas) .. image:: ../_static/Plotting_VisualRGBScatter3D.png :align: center :alt: visual-rgbscatter-3-d """
[docs] def __init__( self, RGB: ArrayLike, colourspace: ( RGB_Colourspace | str | Sequence[RGB_Colourspace | LiteralRGBColourspace | str] ) = "sRGB", model: LiteralColourspaceModel | str = "CIE xyY", colour: ArrayLike | None = None, opacity: float = 1, size: float = 2, **kwargs: Any, ) -> None: super().__init__() self._RGB = np.array([]) self._scatter = None with self.block_update(): self.RGB = RGB self.colourspace = colourspace self.model = model self.colour = colour self.opacity = opacity self.size = size self.kwargs = kwargs self.update()
@visual_property def RGB(self) -> NDArray: """ Getter and setter property for the *RGB* colourspace array. Parameters ---------- value Value to set the *RGB* colourspace array with. Returns ------- :class:`numpy.ndarray` *RGB* colourspace array. """ return self._RGB @RGB.setter def RGB(self, value: ArrayLike) -> None: """Setter for the **self.RGB** property.""" self._RGB = np.reshape(as_float_array(value), (-1, 3)) self._RGB[self._RGB == 0] = EPSILON
[docs] def update(self) -> None: """Update the visual.""" if self._is_update_blocked: return self.clear() XYZ = RGB_to_XYZ(self._RGB, self._colourspace) positions = colourspace_model_axis_reorder( XYZ_to_colourspace_model( XYZ, self._colourspace.whitepoint, self._model, **self._kwargs, ), self._model, ) if self._colour is None: colour = self._RGB else: colour = np.tile(self._colour, (self._RGB.shape[0], 1)) self._scatter = gfx.Points( gfx.Geometry( positions=as_contiguous_array(positions), sizes=as_contiguous_array(np.full(positions.shape[0], self._size)), colors=as_contiguous_array(append_channel(colour, self._opacity)), ), gfx.PointsMaterial( color_mode="vertex", size_mode="vertex", size_space="screen" ), ) self.add(self._scatter)
if __name__ == "__main__": from colour_visuals.rgb_colourspace import VisualRGBColourspace3D scene = gfx.Scene() scene.add( gfx.Background(None, gfx.BackgroundMaterial(np.array([0.18, 0.18, 0.18]))) ) visual_1 = VisualRGBScatter3D(np.random.random((64, 64, 3))) scene.add(visual_1) visual_2 = VisualRGBColourspace3D(segments=8, wireframe=True) scene.add(visual_2) visual_3 = VisualRGBScatter3D( np.random.random((64, 64, 3)), colour=np.array([0.5, 0.5, 0.5]) ) visual_3.local.position = np.array([0.5, 0, 0]) scene.add(visual_3) gfx.show(scene, up=np.array([0, 0, 1]))