Source code for colour.volume.macadam_limits

"""
Optimal Colour Stimuli - MacAdam Limits
=======================================

Defines the objects related to *Optimal Colour Stimuli* computations.
"""

from __future__ import annotations

import numpy as np
from scipy.spatial import Delaunay

from colour.hints import (
    ArrayLike,
    Dict,
    Floating,
    Literal,
    NDArray,
    Optional,
    Union,
)
from colour.models import xyY_to_XYZ
from colour.volume import OPTIMAL_COLOUR_STIMULI_ILLUMINANTS
from colour.utilities import CACHE_REGISTRY, validate_method

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

__all__ = [
    "is_within_macadam_limits",
]

_CACHE_OPTIMAL_COLOUR_STIMULI_XYZ: Dict = CACHE_REGISTRY.register_cache(
    f"{__name__}._CACHE_OPTIMAL_COLOUR_STIMULI_XYZ"
)

_CACHE_OPTIMAL_COLOUR_STIMULI_XYZ_TRIANGULATIONS: Dict = (
    CACHE_REGISTRY.register_cache(
        f"{__name__}._CACHE_OPTIMAL_COLOUR_STIMULI_XYZ_TRIANGULATIONS"
    )
)


def _XYZ_optimal_colour_stimuli(
    illuminant: Union[Literal["A", "C", "D65"], str] = "D65"
) -> NDArray:
    """
    Return given illuminant *Optimal Colour Stimuli* in *CIE XYZ* tristimulus
    values and caches it if not existing.

    Parameters
    ----------
    illuminant
        Illuminant name.

    Returns
    -------
    :class:`numpy.ndarray`
        Illuminant *Optimal Colour Stimuli*.
    """

    illuminant = validate_method(
        illuminant,
        list(OPTIMAL_COLOUR_STIMULI_ILLUMINANTS.keys()),
        '"{0}" illuminant is invalid, it must be one of {1}!',
    )

    optimal_colour_stimuli = OPTIMAL_COLOUR_STIMULI_ILLUMINANTS.get(illuminant)

    if optimal_colour_stimuli is None:
        raise KeyError(
            f'"{illuminant}" not found in factory "Optimal Colour Stimuli": '
            f'"{sorted(OPTIMAL_COLOUR_STIMULI_ILLUMINANTS.keys())}".'
        )

    vertices = _CACHE_OPTIMAL_COLOUR_STIMULI_XYZ.get(illuminant)

    if vertices is None:
        _CACHE_OPTIMAL_COLOUR_STIMULI_XYZ[illuminant] = vertices = (
            xyY_to_XYZ(optimal_colour_stimuli) / 100
        )

    return vertices


[docs]def is_within_macadam_limits( xyY: ArrayLike, illuminant: Union[Literal["A", "C", "D65"], str] = "D65", tolerance: Optional[Floating] = None, ) -> NDArray: """ Return whether given *CIE xyY* colourspace array is within MacAdam limits of given illuminant. Parameters ---------- xyY *CIE xyY* colourspace array. illuminant Illuminant name. tolerance Tolerance allowed in the inside-triangle check. Returns ------- :class:`numpy.ndarray` Whether given *CIE xyY* colourspace array is within MacAdam limits. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``xyY`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ Examples -------- >>> is_within_macadam_limits(np.array([0.3205, 0.4131, 0.51]), 'A') array(True, dtype=bool) >>> a = np.array([[0.3205, 0.4131, 0.51], ... [0.0005, 0.0031, 0.001]]) >>> is_within_macadam_limits(a, 'A') array([ True, False], dtype=bool) """ optimal_colour_stimuli = _XYZ_optimal_colour_stimuli(illuminant) triangulation = _CACHE_OPTIMAL_COLOUR_STIMULI_XYZ_TRIANGULATIONS.get( illuminant ) if triangulation is None: _CACHE_OPTIMAL_COLOUR_STIMULI_XYZ_TRIANGULATIONS[ illuminant ] = triangulation = Delaunay(optimal_colour_stimuli) simplex = triangulation.find_simplex(xyY_to_XYZ(xyY), tol=tolerance) simplex = np.where(simplex >= 0, True, False) return simplex