Source code for colour_visuals.diagrams

# !/usr/bin/env python
"""
Chromaticity Diagram Visuals
============================

Define the *Chromaticity Diagram* visuals:

-   :class:`colour_visuals.VisualSpectralLocus2D`
-   :class:`colour_visuals.VisualSpectralLocus3D`
-   :class:`colour_visuals.VisualChromaticityDiagram`
-   :class:`colour_visuals.VisualChromaticityDiagramCIE1931`
-   :class:`colour_visuals.VisualChromaticityDiagramCIE1960UCS`
-   :class:`colour_visuals.VisualChromaticityDiagramCIE1976UCS`
"""

from __future__ import annotations

import typing

import numpy as np
import pygfx as gfx
from colour.algebra import euclidean_distance, normalise_maximum
from colour.hints import (
    Any,
    ArrayLike,
    Literal,
    LiteralColourspaceModel,
    Sequence,
    Type,
    cast,
)
from colour.models import XYZ_to_RGB
from colour.plotting import (
    CONSTANTS_COLOUR_STYLE,
    LABELS_CHROMATICITY_DIAGRAM_DEFAULT,
    METHODS_CHROMATICITY_DIAGRAM,
    XYZ_to_plotting_colourspace,
    colourspace_model_axis_reorder,
)
from colour.plotting.diagrams import lines_spectral_locus
from colour.utilities import (
    full,
    optional,
    tstack,
)
from scipy.spatial import Delaunay

from colour_visuals.common import (
    DEFAULT_FLOAT_DTYPE_WGPU,
    DEFAULT_INT_DTYPE_WGPU,
    XYZ_to_colourspace_model,
    append_channel,
    as_contiguous_array,
)
from colour_visuals.visual import (
    MixinPropertyCMFS,
    MixinPropertyColour,
    MixinPropertyKwargs,
    MixinPropertyMethod,
    MixinPropertyModel,
    MixinPropertyOpacity,
    MixinPropertySamples,
    MixinPropertyThickness,
    MixinPropertyTypeMaterial,
    MixinPropertyWireframe,
    Visual,
    visual_property,
)

if typing.TYPE_CHECKING:
    from colour.colorimetry import MultiSpectralDistributions

__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__ = [
    "VisualSpectralLocus2D",
    "VisualSpectralLocus3D",
    "VisualChromaticityDiagram",
    "MixinPropertyKwargsVisualSpectralLocus",
    "MixinPropertyKwargsVisualChromaticityDiagram",
    "VisualChromaticityDiagramCIE1931",
    "VisualChromaticityDiagramCIE1960UCS",
    "VisualChromaticityDiagramCIE1976UCS",
]


[docs] class VisualSpectralLocus2D( MixinPropertyCMFS, MixinPropertyColour, MixinPropertyMethod, MixinPropertyOpacity, MixinPropertyThickness, Visual, ): """ Create a 2D *Spectral Locus* visual. Parameters ---------- cmfs Standard observer colour matching functions used for computing the spectrum domain and colours. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.common.filter_cmfs` definition. method *Chromaticity Diagram* method. labels Array of wavelength labels used to customise which labels will be drawn around the spectral locus. Passing an empty array will result in no wavelength labels being drawn. colour Colour of the visual, if *None*, the colour is computed from the visual geometry. opacity Opacity of the visual. thickness Thickness of the visual lines. Attributes ---------- - :attr:`~colour_visuals.VisualSpectralLocus2D.cmfs` - :attr:`~colour_visuals.VisualSpectralLocus2D.method` - :attr:`~colour_visuals.VisualSpectralLocus2D.labels` - :attr:`~colour_visuals.VisualSpectralLocus2D.colour` - :attr:`~colour_visuals.VisualSpectralLocus2D.opacity` - :attr:`~colour_visuals.VisualSpectralLocus2D.thickness` Methods ------- - :meth:`~colour_visuals.VisualSpectralLocus2D.__init__` - :meth:`~colour_visuals.VisualSpectralLocus2D.update` Examples -------- >>> import os >>> 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 = VisualSpectralLocus2D() ... 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_VisualSpectralLocus2D.png :align: center :alt: visual-spectral-locus-2d """
[docs] def __init__( self, cmfs: ( MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] ) = "CIE 1931 2 Degree Standard Observer", method: ( Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] | str ) = "CIE 1931", labels: Sequence | None = None, colour: ArrayLike | None = None, opacity: float = 1, thickness: float = 1, ) -> None: super().__init__() self._spectral_locus = None self._wavelengths = None self._texts = None self._points = None self._labels = None with self.block_update(): self.cmfs = cmfs self.method = method self.labels = labels self.colour = colour self.opacity = opacity self.thickness = thickness self.update()
@visual_property def labels( self, ) -> Sequence | None: """ Getter and setter property for the labels. Parameters ---------- value Value to set the labels with. Returns ------- :class:`str` Labels. """ return self._labels @labels.setter def labels(self, value: Sequence | None) -> None: """Setter for the **self.labels** property.""" self._labels = cast( "Sequence", optional(value, LABELS_CHROMATICITY_DIAGRAM_DEFAULT[self._method]), )
[docs] def update(self) -> None: """Update the visual.""" if self._is_update_blocked: return self.clear() lines_sl, lines_w = lines_spectral_locus(self._cmfs, self._labels, self._method) # Spectral Locus positions = np.reshape( np.concatenate( [lines_sl["position"][:-1], lines_sl["position"][1:]], axis=1 ), (-1, 2), ) positions = np.hstack( [ positions, np.full((positions.shape[0], 1), 0, DEFAULT_FLOAT_DTYPE_WGPU), ] ) if self._colour is None: colour_sl = np.reshape( np.concatenate( [lines_sl["colour"][:-1], lines_sl["colour"][1:]], axis=1 ), (-1, 3), ) else: colour_sl = np.tile(self._colour, (positions.shape[0], 1)) self._spectral_locus = gfx.Line( gfx.Geometry( positions=as_contiguous_array(positions), colors=as_contiguous_array(append_channel(colour_sl, self._opacity)), ), gfx.LineSegmentMaterial(thickness=self._thickness, color_mode="vertex"), ) self.add(self._spectral_locus) if not self._labels: return # Wavelengths positions = lines_w["position"] positions = np.hstack( [ positions, np.full((positions.shape[0], 1), 0, DEFAULT_FLOAT_DTYPE_WGPU), ] ) if self._colour is None: colour_w = lines_w["colour"] else: colour_w = np.tile(self._colour, (positions.shape[0], 1)) self._wavelengths = gfx.Line( gfx.Geometry( positions=as_contiguous_array(positions), colors=as_contiguous_array(append_channel(colour_w, self._opacity)), ), gfx.LineSegmentMaterial(thickness=self._thickness, color_mode="vertex"), ) self.add(self._wavelengths) # Labels self._texts = [] for i, label in enumerate( [label for label in self._labels if label in self._cmfs.wavelengths] ): positions = lines_w["position"][::2] normals = lines_w["normal"][::2] text = gfx.Text( gfx.TextGeometry( str(label), font_size=CONSTANTS_COLOUR_STYLE.font.size, screen_space=True, anchor=( "Center-Left" if lines_w["normal"][::2][i, 0] >= 0 else "Center-Right" ), ), gfx.TextMaterial(color=CONSTANTS_COLOUR_STYLE.colour.light), ) text.local.position = np.array( [ positions[i, 0] + normals[i, 0] / 50 * 1.25, positions[i, 1] + normals[i, 1] / 50 * 1.25, 0, ] ) self._texts.append(text) self.add(text) positions = np.hstack( [ lines_w["position"][::2], np.full( (lines_w["position"][::2].shape[0], 1), 0, DEFAULT_FLOAT_DTYPE_WGPU, ), ] ) if self._colour is None: colour_lp = lines_w["colour"][::2] else: colour_lp = np.tile(self._colour, (positions.shape[0], 1)) self._points = gfx.Points( gfx.Geometry( positions=as_contiguous_array(positions), sizes=as_contiguous_array( full(lines_w["position"][::2].shape[0], self._thickness * 5) ), colors=as_contiguous_array(append_channel(colour_lp, self._opacity)), ), gfx.PointsMaterial( color_mode="vertex", size_mode="vertex", size_space="screen" ), ) self.add(self._points)
[docs] class VisualSpectralLocus3D( MixinPropertyCMFS, MixinPropertyColour, MixinPropertyKwargs, MixinPropertyModel, MixinPropertyOpacity, MixinPropertyThickness, Visual, ): """ Create a 3D *Spectral Locus* visual. Parameters ---------- cmfs Standard observer colour matching functions used for computing the spectrum domain and colours. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.common.filter_cmfs` definition. model Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute for the list of supported colourspace models. labels Array of wavelength labels used to customise which labels will be drawn around the spectral locus. Passing an empty array will result in no wavelength labels being drawn. colour Colour of the visual, if *None*, the colour is computed from the visual geometry. opacity Opacity of the visual. thickness Thickness of the visual lines. Other Parameters ---------------- kwargs See the documentation of the supported conversion definitions. Attributes ---------- - :attr:`~colour_visuals.VisualSpectralLocus3D.cmfs` - :attr:`~colour_visuals.VisualSpectralLocus3D.model` - :attr:`~colour_visuals.VisualSpectralLocus3D.labels` - :attr:`~colour_visuals.VisualSpectralLocus3D.colour` - :attr:`~colour_visuals.VisualSpectralLocus3D.opacity` - :attr:`~colour_visuals.VisualSpectralLocus3D.thickness` Methods ------- - :meth:`~colour_visuals.VisualSpectralLocus3D.__init__` - :meth:`~colour_visuals.VisualSpectralLocus3D.update` Examples -------- >>> import os >>> 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 = VisualSpectralLocus3D(model="CIE XYZ") ... 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_VisualSpectralLocus3D.png :align: center :alt: visual-spectral-locus-3d """
[docs] def __init__( self, cmfs: ( MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] ) = "CIE 1931 2 Degree Standard Observer", model: LiteralColourspaceModel | str = "CIE xyY", colour: ArrayLike | None = None, opacity: float = 1, thickness: float = 1, **kwargs: Any, ) -> None: super().__init__() self._spectral_locus = None with self.block_update(): self.cmfs = cmfs self.model = model self.colour = colour self.opacity = opacity self.thickness = thickness self.kwargs = kwargs self.update()
[docs] def update(self) -> None: """Update the visual.""" if self._is_update_blocked: return self.clear() colourspace = CONSTANTS_COLOUR_STYLE.colour.colourspace positions = colourspace_model_axis_reorder( XYZ_to_colourspace_model( self._cmfs.values, colourspace.whitepoint, self._model, **self._kwargs, ), self._model, ) positions = np.reshape( np.concatenate([positions[:-1], positions[1:]], axis=1), (-1, 3) ) if self._colour is None: colour = XYZ_to_RGB(self._cmfs.values, colourspace) colour = np.reshape( np.concatenate([colour[:-1], colour[1:]], axis=1), (-1, 3) ) else: colour = np.tile(self._colour, (positions.shape[0], 1)) self._spectral_locus = gfx.Line( gfx.Geometry( positions=as_contiguous_array(positions), colors=as_contiguous_array(append_channel(colour, self._opacity)), ), gfx.LineSegmentMaterial(thickness=self._thickness, color_mode="vertex"), ) self.add(self._spectral_locus)
[docs] class VisualChromaticityDiagram( MixinPropertyCMFS, MixinPropertyColour, MixinPropertyTypeMaterial, MixinPropertyMethod, MixinPropertyOpacity, MixinPropertySamples, MixinPropertyWireframe, Visual, ): """ Create a *Chromaticity Diagram* visual. Parameters ---------- cmfs Standard observer colour matching functions used for computing the spectrum domain and colours. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.common.filter_cmfs` definition. method *Chromaticity Diagram* method. colours Colour of the visual, if *None*, the colours are computed from the visual geometry. opacity Opacity of the visual. material Material used to surface the visual geometry. wireframe Whether to render the visual as a wireframe, i.e., only render edges. samples Sample count used for generating the *Chromaticity Diagram* Delaunay tessellation. Attributes ---------- - :attr:`~colour_visuals.VisualChromaticityDiagram.cmfs` - :attr:`~colour_visuals.VisualChromaticityDiagram.method` - :attr:`~colour_visuals.VisualChromaticityDiagram.colours` - :attr:`~colour_visuals.VisualChromaticityDiagram.opacity` - :attr:`~colour_visuals.VisualChromaticityDiagram.type_material` - :attr:`~colour_visuals.VisualChromaticityDiagram.wireframe` - :attr:`~colour_visuals.VisualChromaticityDiagram.samples` Methods ------- - :meth:`~colour_visuals.VisualChromaticityDiagram.__init__` - :meth:`~colour_visuals.VisualChromaticityDiagram.update` Examples -------- >>> import os >>> 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 = VisualChromaticityDiagram() ... 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_VisualChromaticityDiagram.png :align: center :alt: visual-chromaticity-diagram """
[docs] def __init__( self, cmfs: ( MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] ) = "CIE 1931 2 Degree Standard Observer", method: ( Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] | str ) = "CIE 1931", colours: ArrayLike | None = None, opacity: float = 1, material: Type[gfx.MeshAbstractMaterial] = gfx.MeshBasicMaterial, wireframe: bool = False, samples: int = 64, ) -> None: super().__init__() self._chromaticity_diagram = None with self.block_update(): self.cmfs = cmfs self.method = method self.colours = colours self.opacity = opacity self.type_material = material self.wireframe = wireframe self.samples = samples self.update()
[docs] def update(self) -> None: """Update the visual.""" if self._is_update_blocked: return self.clear() illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint XYZ_to_ij = METHODS_CHROMATICITY_DIAGRAM[self._method]["XYZ_to_ij"] ij_to_XYZ = METHODS_CHROMATICITY_DIAGRAM[self._method]["ij_to_XYZ"] # CMFS ij_l = XYZ_to_ij(self._cmfs.values, illuminant) # Line of Purples d = euclidean_distance(ij_l[0], ij_l[-1]) ij_p = tstack( [ np.linspace(ij_l[0][0], ij_l[-1][0], int(d * self._samples)), np.linspace(ij_l[0][1], ij_l[-1][1], int(d * self._samples)), ] ) # Grid triangulation = Delaunay(ij_l, qhull_options="QJ") xi = np.linspace(0, 1, self._samples) ii_g, jj_g = np.meshgrid(xi, xi) ij_g = tstack([ii_g, jj_g]) ij_g = ij_g[triangulation.find_simplex(ij_g) > 0] ij = np.vstack([ij_l, illuminant, ij_p, ij_g]) triangulation = Delaunay(ij, qhull_options="QJ") positions = np.hstack( [ij, np.full((ij.shape[0], 1), 0, DEFAULT_FLOAT_DTYPE_WGPU)] ) if self._colour is None: colours = normalise_maximum( XYZ_to_plotting_colourspace( ij_to_XYZ(positions[..., :2], illuminant), illuminant ), axis=-1, ) else: colours = np.tile(self._colour, (positions.shape[0], 1)) geometry = gfx.Geometry( positions=as_contiguous_array(positions), indices=as_contiguous_array( triangulation.simplices, DEFAULT_INT_DTYPE_WGPU ), colors=as_contiguous_array(append_channel(colours, self._opacity)), ) self._chromaticity_diagram = gfx.Mesh( geometry, ( self._type_material(color_mode="vertex", wireframe=self._wireframe) if self._wireframe else self._type_material(color_mode="vertex") ), ) self.add(self._chromaticity_diagram)
class MixinPropertyKwargsVisualSpectralLocus: """ Define a mixin for keyword arguments for the :class:`colour_visuals.VisualSpectralLocus2D` class. Attributes ---------- - :attr:`~colour_visuals.diagrams.MixinPropertyKwargsVisualSpectralLocus.\ kwargs_visual_spectral_locus` """ def __init__(self) -> None: self._spectral_locus = None self._kwargs_visual_spectral_locus = {} super().__init__() @property def kwargs_visual_spectral_locus(self) -> dict: """ Getter and setter property for the visual kwargs for the *Spectral Locus*. Parameters ---------- value Value to set visual kwargs for the *Spectral Locus* with. Returns ------- :class:`dict` Visual kwargs for the *Spectral Locus*. """ return self._kwargs_visual_spectral_locus @kwargs_visual_spectral_locus.setter def kwargs_visual_spectral_locus(self, value: dict) -> None: """ Setter for the **self.kwargs_visual_spectral_locus** property. """ self._kwargs_visual_spectral_locus = value for key, item in self._kwargs_visual_spectral_locus.items(): setattr(self._spectral_locus, key, item) class MixinPropertyKwargsVisualChromaticityDiagram: """ Define a mixin for keyword arguments for the :class:`colour_visuals.VisualChromaticityDiagram` class. Attributes ---------- - :attr:`~colour_visuals.diagrams.\ MixinPropertyKwargsVisualChromaticityDiagram.kwargs_visual_chromaticity_diagram` """ def __init__(self) -> None: self._chromaticity_diagram = None self._kwargs_visual_chromaticity_diagram = {} super().__init__() @property def kwargs_visual_chromaticity_diagram(self) -> dict: """ Getter and setter property for the visual kwargs for the *Chromaticity Diagram*. Parameters ---------- value Value to set visual kwargs for the *Chromaticity Diagram* with. Returns ------- :class:`dict` Visual kwargs for the *Chromaticity Diagram*. """ return self._kwargs_visual_chromaticity_diagram @kwargs_visual_chromaticity_diagram.setter def kwargs_visual_chromaticity_diagram(self, value: dict) -> None: """ Setter for the **self.kwargs_visual_chromaticity_diagram** property. """ self._kwargs_visual_chromaticity_diagram = value for key, item in self._kwargs_visual_chromaticity_diagram.items(): setattr(self._chromaticity_diagram, key, item)
[docs] class VisualChromaticityDiagramCIE1931( MixinPropertyKwargsVisualSpectralLocus, MixinPropertyKwargsVisualChromaticityDiagram, Visual, ): """ Create the *CIE 1931* *Chromaticity Diagram* visual. Parameters ---------- kwargs_visual_spectral_locus Keyword arguments for the underlying :class:`colour_visuals.VisualSpectralLocus2D` class. kwargs_visual_chromaticity_diagram Keyword arguments for the underlying :class:`colour_visuals.VisualChromaticityDiagram` class. Attributes ---------- - :attr:`~colour_visuals.VisualChromaticityDiagramCIE1931.\ kwargs_visual_spectral_locus` - :attr:`~colour_visuals.VisualChromaticityDiagramCIE1931.\ kwargs_visual_chromaticity_diagram` Methods ------- - :meth:`~colour_visuals.VisualChromaticityDiagramCIE1931.__init__` - :meth:`~colour_visuals.VisualChromaticityDiagramCIE1931.update` Examples -------- >>> import os >>> 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 = VisualChromaticityDiagramCIE1931( ... kwargs_visual_chromaticity_diagram={"opacity": 0.25} ... ) ... 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_VisualChromaticityDiagramCIE1931.png :align: center :alt: visual-chromaticity-diagram-cie-1931 """
[docs] def __init__( self, kwargs_visual_spectral_locus: dict | None = None, kwargs_visual_chromaticity_diagram: dict | None = None, ) -> None: super().__init__() if kwargs_visual_spectral_locus is None: kwargs_visual_spectral_locus = {} if kwargs_visual_chromaticity_diagram is None: kwargs_visual_chromaticity_diagram = {} self._spectral_locus = VisualSpectralLocus2D(method="CIE 1931") self.add(self._spectral_locus) self._chromaticity_diagram = VisualChromaticityDiagram(method="CIE 1931") self.add(self._chromaticity_diagram) self.kwargs_visual_spectral_locus = kwargs_visual_spectral_locus self.kwargs_visual_chromaticity_diagram = kwargs_visual_chromaticity_diagram
[docs] def update(self) -> None: """Update the visual."""
[docs] class VisualChromaticityDiagramCIE1960UCS( MixinPropertyKwargsVisualSpectralLocus, MixinPropertyKwargsVisualChromaticityDiagram, Visual, ): """ Create the *CIE 1960 UCS* *Chromaticity Diagram* visual. Parameters ---------- kwargs_visual_spectral_locus Keyword arguments for the underlying :class:`colour_visuals.VisualSpectralLocus2D` class. kwargs_visual_chromaticity_diagram Keyword arguments for the underlying :class:`colour_visuals.VisualChromaticityDiagram` class. Attributes ---------- - :attr:`~colour_visuals.VisualChromaticityDiagramCIE1960UCS.\ kwargs_visual_spectral_locus` - :attr:`~colour_visuals.VisualChromaticityDiagramCIE1960UCS.\ kwargs_visual_chromaticity_diagram` Methods ------- - :meth:`~colour_visuals.VisualChromaticityDiagramCIE1960UCS.__init__` - :meth:`~colour_visuals.VisualChromaticityDiagramCIE1960UCS.update` Examples -------- >>> import os >>> 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 = VisualChromaticityDiagramCIE1960UCS( ... kwargs_visual_chromaticity_diagram={"opacity": 0.25} ... ) ... 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_VisualChromaticityDiagramCIE1960UCS.png :align: center :alt: visual-chromaticity-diagram-cie-1960-ucs """
[docs] def __init__( self, kwargs_visual_spectral_locus: dict | None = None, kwargs_visual_chromaticity_diagram: dict | None = None, ) -> None: super().__init__() if kwargs_visual_spectral_locus is None: kwargs_visual_spectral_locus = {} if kwargs_visual_chromaticity_diagram is None: kwargs_visual_chromaticity_diagram = {} self._spectral_locus = VisualSpectralLocus2D( method="CIE 1960 UCS", **(optional(kwargs_visual_spectral_locus, {})), ) self.add(self._spectral_locus) self._chromaticity_diagram = VisualChromaticityDiagram( method="CIE 1960 UCS", **(optional(kwargs_visual_chromaticity_diagram, {})), ) self.add(self._chromaticity_diagram) self.kwargs_visual_spectral_locus = kwargs_visual_spectral_locus self.kwargs_visual_chromaticity_diagram = kwargs_visual_chromaticity_diagram
[docs] def update(self) -> None: """Update the visual."""
[docs] class VisualChromaticityDiagramCIE1976UCS( MixinPropertyKwargsVisualSpectralLocus, MixinPropertyKwargsVisualChromaticityDiagram, Visual, ): """ Create the *CIE 1976 UCS* *Chromaticity Diagram* visual. Parameters ---------- kwargs_visual_spectral_locus Keyword arguments for the underlying :class:`colour_visuals.VisualSpectralLocus2D` class. kwargs_visual_chromaticity_diagram Keyword arguments for the underlying :class:`colour_visuals.VisualChromaticityDiagram` class. Attributes ---------- - :attr:`~colour_visuals.VisualChromaticityDiagramCIE1976UCS.\ kwargs_visual_spectral_locus` - :attr:`~colour_visuals.VisualChromaticityDiagramCIE1976UCS.\ kwargs_visual_chromaticity_diagram` Methods ------- - :meth:`~colour_visuals.VisualChromaticityDiagramCIE1976UCS.__init__` - :meth:`~colour_visuals.VisualChromaticityDiagramCIE1976UCS.update` Examples -------- >>> import os >>> 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 = VisualChromaticityDiagramCIE1976UCS( ... kwargs_visual_chromaticity_diagram={"opacity": 0.25} ... ) ... 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_VisualChromaticityDiagramCIE1976UCS.png :align: center :alt: visual-chromaticity-diagram-cie-1976-ucs """
[docs] def __init__( self, kwargs_visual_spectral_locus: dict | None = None, kwargs_visual_chromaticity_diagram: dict | None = None, ) -> None: super().__init__() if kwargs_visual_spectral_locus is None: kwargs_visual_spectral_locus = {} if kwargs_visual_chromaticity_diagram is None: kwargs_visual_chromaticity_diagram = {} self._spectral_locus = VisualSpectralLocus2D( method="CIE 1976 UCS", **(optional(kwargs_visual_spectral_locus, {})), ) self.add(self._spectral_locus) self._chromaticity_diagram = VisualChromaticityDiagram( method="CIE 1976 UCS", **(optional(kwargs_visual_chromaticity_diagram, {})), ) self.add(self._chromaticity_diagram) self.kwargs_visual_spectral_locus = kwargs_visual_spectral_locus self.kwargs_visual_chromaticity_diagram = kwargs_visual_chromaticity_diagram
[docs] def update(self) -> None: """Update the visual."""
if __name__ == "__main__": scene = gfx.Scene() scene.add( gfx.Background(None, gfx.BackgroundMaterial(np.array([0.18, 0.18, 0.18]))) ) visual_1 = VisualChromaticityDiagramCIE1931() scene.add(visual_1) visual_2 = VisualChromaticityDiagramCIE1931( kwargs_visual_chromaticity_diagram={"wireframe": True, "opacity": 0.5} ) visual_2.local.position = np.array([1, 0, 0]) scene.add(visual_2) visual_3 = VisualChromaticityDiagramCIE1931( kwargs_visual_chromaticity_diagram={"colours": [0.5, 0.5, 0.5]} ) visual_3.local.position = np.array([2, 0, 0]) scene.add(visual_3) visual_4 = VisualChromaticityDiagramCIE1960UCS() visual_4.local.position = np.array([3, 0, 0]) scene.add(visual_4) visual_5 = VisualChromaticityDiagramCIE1976UCS() visual_5.local.position = np.array([4, 0, 0]) scene.add(visual_5) visual_6 = VisualSpectralLocus2D(colour=[0.5, 0.5, 0.5]) visual_6.local.position = np.array([5, 0, 0]) scene.add(visual_6) visual_7 = VisualSpectralLocus3D() scene.add(visual_7) visual_8 = VisualSpectralLocus3D(colours=[0.5, 0.5, 0.5]) visual_8.local.position = np.array([5, 0, 0]) scene.add(visual_8) visual_9 = VisualSpectralLocus3D(model="CIE XYZ") visual_9.local.position = np.array([6, 0, 0]) scene.add(visual_9) gfx.show(scene, up=np.array([0, 0, 1]))