Source code for colour_visuals.rgb_scatter

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

Defines the *RGB* scatter visuals:

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

from __future__ import annotations

import numpy as np
import pygfx as gfx
from colour import RGB_to_XYZ
from colour.constants import EPSILON
from colour.hints import (
    ArrayLike,
    LiteralColourspaceModel,
    LiteralRGBColourspace,
    Sequence,
    cast,
)
from colour.models import RGB_Colourspace
from colour.plotting import (
    colourspace_model_axis_reorder,
    filter_RGB_colourspaces,
)
from colour.utilities import as_float_array, first_item

from colour_visuals.common import (
    XYZ_to_colourspace_model,
    append_channel,
    as_contiguous_array,
)

__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(gfx.Points): """ 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. colours Colours of the visual, if *None*, the colours are computed from the visual geometry. opacity Opacity of the visual. size Size of the visual points 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", colours: ArrayLike | None = None, opacity: float = 1, size: float = 2, ): colourspace = cast( RGB_Colourspace, first_item(filter_RGB_colourspaces(colourspace).values()), ) RGB = as_float_array(RGB).reshape(-1, 3) RGB[RGB == 0] = EPSILON XYZ = RGB_to_XYZ(RGB, colourspace) positions = colourspace_model_axis_reorder( XYZ_to_colourspace_model(XYZ, colourspace.whitepoint, model), model, ) if colours is None: # noqa: SIM108 colours = RGB else: colours = np.tile(colours, (RGB.shape[0], 1)) super().__init__( gfx.Geometry( positions=as_contiguous_array(positions), sizes=as_contiguous_array(np.full(positions.shape[0], size)), colors=as_contiguous_array(append_channel(colours, opacity)), ), gfx.PointsMaterial(color_mode="vertex", vertex_sizes=True), )
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)), colours=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]))