Exemplo n.º 1
0
def plot_multi_cctfs(cctfs=None, decoding_cctf=False, **kwargs):
    """
    Plots given colour component transfer functions.

    Parameters
    ----------
    cctfs : array_like, optional
        Colour component transfer function to plot.
    decoding_cctf : bool
        Plot the decoding colour component transfer function instead.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`,
        :func:`colour.plotting.plot_multi_functions`,
        :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    tuple
        Current figure and axes.

    Examples
    --------
    >>> plot_multi_cctfs(['ITU-R BT.709', 'sRGB'])  # doctest: +SKIP

    .. image:: ../_static/Plotting_Plot_Multi_CCTFs.png
        :align: center
        :alt: plot_multi_cctfs
    """

    if cctfs is None:
        cctfs = ('ITU-R BT.709', 'sRGB')

    cctfs = filter_passthrough(
        DECODING_CCTFS if decoding_cctf else ENCODING_CCTFS, cctfs)

    mode = 'Decoding' if decoding_cctf else 'Encoding'
    title = '{0} - {1} CCTFs'.format(', '.join([cctf for cctf in cctfs]), mode)

    settings = {
        'bounding_box': (0, 1, 0, 1),
        'legend': True,
        'title': title,
        'x_label': 'Signal Value' if decoding_cctf else 'Tristimulus Value',
        'y_label': 'Tristimulus Value' if decoding_cctf else 'Signal Value',
    }
    settings.update(kwargs)

    with domain_range_scale(1):
        return plot_multi_functions(cctfs, **settings)
Exemplo n.º 2
0
def plot_multi_munsell_value_functions(
    functions: Union[Callable, str, Sequence[Union[Callable, str]]],
    **kwargs: Any,
) -> Tuple[plt.Figure, plt.Axes]:
    """
    Plot given *Munsell* value functions.

    Parameters
    ----------
    functions
        *Munsell* value functions to plot. ``functions`` elements can be of any
        type or form supported by the
        :func:`colour.plotting.filter_passthrough` definition.

    Other Parameters
    ----------------
    kwargs
        {:func:`colour.plotting.artist`,
        :func:`colour.plotting.plot_multi_functions`,
        :func:`colour.plotting.render`},
        See the documentation of the previously listed definitions.

    Returns
    -------
    :class:`tuple`
        Current figure and axes.

    Examples
    --------
    >>> plot_multi_munsell_value_functions(['ASTM D1535', 'McCamy 1987'])
    ... # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, <...AxesSubplot...>)

    .. image:: ../_static/Plotting_Plot_Multi_Munsell_Value_Functions.png
        :align: center
        :alt: plot_multi_munsell_value_functions
    """

    functions_filtered = filter_passthrough(MUNSELL_VALUE_METHODS, functions)

    settings: Dict[str, Any] = {
        "bounding_box": (0, 100, 0, 10),
        "legend": True,
        "title": f"{', '.join(functions_filtered)} - Munsell Functions",
        "x_label": "Luminance Y",
        "y_label": "Munsell Value V",
    }
    settings.update(kwargs)

    return plot_multi_functions(functions_filtered,
                                samples=np.linspace(0, 100, 1000),
                                **settings)
Exemplo n.º 3
0
def plot_multi_luminance_functions(
    functions: Union[Callable, str, Sequence[Union[Callable, str]]],
    **kwargs: Any,
) -> Tuple[plt.Figure, plt.Axes]:
    """
    Plot given *Luminance* functions.

    Parameters
    ----------
    functions
        *Luminance* functions to plot. ``functions`` elements can be of any
        type or form supported by the
        :func:`colour.plotting.filter_passthrough` definition.

    Other Parameters
    ----------------
    kwargs
        {:func:`colour.plotting.artist`,
        :func:`colour.plotting.plot_multi_functions`,
        :func:`colour.plotting.render`},
        See the documentation of the previously listed definitions.

    Returns
    -------
    :class:`tuple`
        Current figure and axes.

    Examples
    --------
    >>> plot_multi_luminance_functions(['CIE 1976', 'Newhall 1943'])
    ... # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, <...AxesSubplot...>)

    .. image:: ../_static/Plotting_Plot_Multi_Luminance_Functions.png
        :align: center
        :alt: plot_multi_luminance_functions
    """

    functions_filtered = filter_passthrough(LUMINANCE_METHODS, functions)

    settings: Dict[str, Any] = {
        "bounding_box": (0, 1, 0, 1),
        "legend": True,
        "title": f"{', '.join(functions_filtered)} - Luminance Functions",
        "x_label": "Normalised Munsell Value / Lightness",
        "y_label": "Normalised Relative Luminance Y",
    }
    settings.update(kwargs)

    with domain_range_scale("1"):
        return plot_multi_functions(functions_filtered, **settings)
Exemplo n.º 4
0
def plot_multi_munsell_value_functions(functions=None, **kwargs):
    """
    Plots given *Munsell* value functions.

    Parameters
    ----------
    functions : array_like, optional
        *Munsell* value functions to plot.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`,
        :func:`colour.plotting.plot_multi_functions`,
        :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    tuple
        Current figure and axes.

    Examples
    --------
    >>> plot_multi_munsell_value_functions(['ASTM D1535', 'McCamy 1987'])
    ... # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, \
<matplotlib.axes._subplots.AxesSubplot object at 0x...>)

    .. image:: ../_static/Plotting_Plot_Multi_Munsell_Value_Functions.png
        :align: center
        :alt: plot_multi_munsell_value_functions
    """

    if functions is None:
        functions = ('ASTM D1535', 'McCamy 1987')

    functions = filter_passthrough(MUNSELL_VALUE_METHODS, functions)

    settings = {
        'bounding_box': (0, 100, 0, 10),
        'legend': True,
        'title': '{0} - Munsell Functions'.format(', '.join(functions)),
        'x_label': 'Luminance Y',
        'y_label': 'Munsell Value V',
    }
    settings.update(kwargs)

    return plot_multi_functions(functions,
                                samples=np.linspace(0, 100, 1000),
                                **settings)
Exemplo n.º 5
0
def plot_multi_luminance_functions(functions=None, **kwargs):
    """
    Plots given *Luminance* functions.

    Parameters
    ----------
    functions : array_like, optional
        *Luminance* functions to plot.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`,
        :func:`colour.plotting.plot_multi_functions`,
        :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    tuple
        Current figure and axes.

    Examples
    --------
    >>> plot_multi_luminance_functions(['CIE 1976', 'Newhall 1943'])
    ... # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, \
<matplotlib.axes._subplots.AxesSubplot object at 0x...>)

    .. image:: ../_static/Plotting_Plot_Multi_Luminance_Functions.png
        :align: center
        :alt: plot_multi_luminance_functions
    """

    if functions is None:
        functions = ('CIE 1976', 'Newhall 1943')

    functions = filter_passthrough(LUMINANCE_METHODS, functions)

    settings = {
        'bounding_box': (0, 1, 0, 1),
        'legend': True,
        'title': '{0} - Luminance Functions'.format(', '.join(functions)),
        'x_label': 'Normalised Munsell Value / Lightness',
        'y_label': 'Normalised Relative Luminance Y',
    }
    settings.update(kwargs)

    with domain_range_scale(1):
        return plot_multi_functions(functions, **settings)
Exemplo n.º 6
0
def plot_multi_luminance_functions(functions=None, **kwargs):
    """
    Plots given *Luminance* functions.

    Parameters
    ----------
    functions : array_like, optional
        *Luminance* functions to plot.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`,
        :func:`colour.plotting.plot_multi_functions`,
        :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    tuple
        Current figure and axes.

    Examples
    --------
    >>> plot_multi_luminance_functions(['CIE 1976', 'Newhall 1943'])
    ... # doctest: +SKIP

    .. image:: ../_static/Plotting_Plot_Multi_Luminance_Functions.png
        :align: center
        :alt: plot_multi_luminance_functions
    """

    if functions is None:
        functions = ('CIE 1976', 'Newhall 1943')

    functions = filter_passthrough(LUMINANCE_METHODS, functions)

    settings = {
        'bounding_box': (0, 1, 0, 1),
        'legend': True,
        'title': '{0} - Luminance Functions'.format(', '.join(functions)),
        'x_label': 'Normalised Munsell Value / Lightness',
        'y_label': 'Normalised Relative Luminance Y',
    }
    settings.update(kwargs)

    with domain_range_scale(1):
        return plot_multi_functions(functions, **settings)
Exemplo n.º 7
0
def plot_multi_lightness_functions(functions=None, **kwargs):
    """
    Plots given *Lightness* functions.

    Parameters
    ----------
    functions : array_like, optional
        *Lightness* functions to plot.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`,
        :func:`colour.plotting.plot_multi_functions`,
        :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    tuple
        Current figure and axes.

    Examples
    --------
    >>> plot_multi_lightness_functions(['CIE 1976', 'Wyszecki 1963'])
    ... # doctest: +SKIP

    .. image:: ../_static/Plotting_Plot_Multi_Lightness_Functions.png
        :align: center
        :alt: plot_multi_lightness_functions
    """

    if functions is None:
        functions = ('CIE 1976', 'Wyszecki 1963')

    functions = filter_passthrough(LIGHTNESS_METHODS, functions)

    settings = {
        'bounding_box': (0, 1, 0, 1),
        'legend': True,
        'title': '{0} - Lightness Functions'.format(', '.join(functions)),
        'x_label': 'Normalised Relative Luminance Y',
        'y_label': 'Normalised Lightness',
    }
    settings.update(kwargs)

    with domain_range_scale(1):
        return plot_multi_functions(functions, **settings)
Exemplo n.º 8
0
def plot_multi_munsell_value_functions(functions=None, **kwargs):
    """
    Plots given *Munsell* value functions.

    Parameters
    ----------
    functions : array_like, optional
        *Munsell* value functions to plot.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`,
        :func:`colour.plotting.plot_multi_functions`,
        :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    tuple
        Current figure and axes.

    Examples
    --------
    >>> plot_multi_munsell_value_functions(['ASTM D1535-08', 'McCamy 1987'])
    ... # doctest: +SKIP

    .. image:: ../_static/Plotting_Plot_Multi_Munsell_Value_Functions.png
        :align: center
        :alt: plot_multi_munsell_value_functions
    """

    if functions is None:
        functions = ('ASTM D1535-08', 'McCamy 1987')

    functions = filter_passthrough(MUNSELL_VALUE_METHODS, functions)

    settings = {
        'bounding_box': (0, 100, 0, 10),
        'legend': True,
        'title': '{0} - Munsell Functions'.format(', '.join(functions)),
        'x_label': 'Luminance Y',
        'y_label': 'Munsell Value V',
    }
    settings.update(kwargs)

    return plot_multi_functions(
        functions, samples=np.linspace(0, 100, 1000), **settings)
Exemplo n.º 9
0
def plot_multi_lightness_functions(functions, **kwargs):
    """
    Plots given *Lightness* functions.

    Parameters
    ----------
    functions : unicode or object or array_like
        *Lightness* functions to plot. ``functions`` elements can be of any
        type or form supported by the
        :func:`colour.plotting.filter_passthrough` definition.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`,
        :func:`colour.plotting.plot_multi_functions`,
        :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    tuple
        Current figure and axes.

    Examples
    --------
    >>> plot_multi_lightness_functions(['CIE 1976', 'Wyszecki 1963'])
    ... # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, <...AxesSubplot...>)

    .. image:: ../_static/Plotting_Plot_Multi_Lightness_Functions.png
        :align: center
        :alt: plot_multi_lightness_functions
    """

    functions = filter_passthrough(LIGHTNESS_METHODS, functions)

    settings = {
        'bounding_box': (0, 1, 0, 1),
        'legend': True,
        'title': '{0} - Lightness Functions'.format(', '.join(functions)),
        'x_label': 'Normalised Relative Luminance Y',
        'y_label': 'Normalised Lightness',
    }
    settings.update(kwargs)

    with domain_range_scale(1):
        return plot_multi_functions(functions, **settings)
Exemplo n.º 10
0
    def test_filter_passthrough(self):
        """Test :func:`colour.plotting.common.filter_passthrough` definition."""

        self.assertListEqual(
            sorted(colourspace.name for colourspace in filter_passthrough(
                RGB_COLOURSPACES, ["^ACES.*"]).values()),
            ["ACES2065-1", "ACEScc", "ACEScct", "ACEScg", "ACESproxy"],
        )

        self.assertListEqual(
            sorted(filter_passthrough(RGB_COLOURSPACES, ["^ACEScc$"]).keys()),
            ["ACEScc"],
        )

        self.assertListEqual(
            sorted(filter_passthrough(RGB_COLOURSPACES, ["^acescc$"]).keys()),
            ["ACEScc"],
        )

        self.assertDictEqual(
            filter_passthrough(
                SDS_ILLUMINANTS,
                [SDS_ILLUMINANTS["D65"], {
                    "Is": "Excluded"
                }],
                allow_non_siblings=False,
            ),
            {"D65": SDS_ILLUMINANTS["D65"]},
        )

        self.assertDictEqual(
            filter_passthrough(
                SDS_ILLUMINANTS,
                [SDS_ILLUMINANTS["D65"], {
                    "Is": "Included"
                }],
                allow_non_siblings=True,
            ),
            {
                "D65": SDS_ILLUMINANTS["D65"],
                "Is": "Included"
            },
        )

        self.assertListEqual(
            sorted(element for element in filter_passthrough(
                {
                    "John": "Doe",
                    "Luke": "Skywalker"
                }, ["John"]).values()),
            ["Doe", "John"],
        )
Exemplo n.º 11
0
    def test_filter_passthrough(self):
        """
        Tests :func:`colour.plotting.common.filter_passthrough` definition.
        """

        self.assertListEqual(
            sorted([
                colourspace.name for colourspace in filter_passthrough(
                    RGB_COLOURSPACES, ['^ACES.*']).values()
            ]), ['ACES2065-1', 'ACEScc', 'ACEScct', 'ACEScg', 'ACESproxy'])

        self.assertListEqual(
            sorted(filter_passthrough(RGB_COLOURSPACES, ['^ACEScc$']).keys()),
            ['ACEScc'])

        self.assertListEqual(
            sorted(filter_passthrough(RGB_COLOURSPACES, ['^acescc$']).keys()),
            ['ACEScc'])

        self.assertDictEqual(
            filter_passthrough(SDS_ILLUMINANTS,
                               [SDS_ILLUMINANTS['D65'], {
                                   'Is': 'Excluded'
                               }],
                               allow_non_siblings=False),
            {'D65': SDS_ILLUMINANTS['D65']})

        self.assertDictEqual(
            filter_passthrough(SDS_ILLUMINANTS,
                               [SDS_ILLUMINANTS['D65'], {
                                   'Is': 'Included'
                               }],
                               allow_non_siblings=True), {
                                   'D65': SDS_ILLUMINANTS['D65'],
                                   'Is': 'Included'
                               })

        self.assertListEqual(
            sorted([
                element for element in filter_passthrough(
                    {
                        'John': 'Doe',
                        'Luke': 'Skywalker'
                    }, ['John']).values()
            ]), ['Doe', 'John'])
Exemplo n.º 12
0
def plot_planckian_locus_in_chromaticity_diagram(
        illuminants=None,
        annotate_parameters=None,
        chromaticity_diagram_callable=plot_chromaticity_diagram,
        planckian_locus_callable=plot_planckian_locus,
        method='CIE 1931',
        **kwargs):
    """
    Plots the *Planckian Locus* and given illuminants in the
    *Chromaticity Diagram* according to given method.

    Parameters
    ----------
    illuminants : array_like, optional
        Factory illuminants to plot.
    annotate_parameters : dict or array_like, optional
        Parameters for the :func:`plt.annotate` definition, used to annotate
        the resulting chromaticity coordinates with their respective illuminant
        names if ``annotate`` is set to *True*. ``annotate_parameters`` can be
        either a single dictionary applied to all the arrows with same settings
        or a sequence of dictionaries with different settings for each
        illuminant.
    chromaticity_diagram_callable : callable, optional
        Callable responsible for drawing the *Chromaticity Diagram*.
    planckian_locus_callable : callable, optional
        Callable responsible for drawing the *Planckian Locus*.
    method : unicode, optional
        **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**,
        *Chromaticity Diagram* method.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`,
        :func:`colour.plotting.diagrams.plot_chromaticity_diagram`,
        :func:`colour.plotting.temperature.plot_planckian_locus`,
        :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    tuple
        Current figure and axes.

    Examples
    --------
    >>> plot_planckian_locus_in_chromaticity_diagram(['A', 'B', 'C'])
    ... # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, \
<matplotlib.axes._subplots.AxesSubplot object at 0x...>)

    .. image:: ../_static/Plotting_\
Plot_Planckian_Locus_In_Chromaticity_Diagram.png
        :align: center
        :alt: plot_planckian_locus_in_chromaticity_diagram
    """

    cmfs = CMFS['CIE 1931 2 Degree Standard Observer']

    if illuminants is None:
        illuminants = ('A', 'B', 'C')

    illuminants = filter_passthrough(ILLUMINANTS.get(cmfs.name), illuminants)

    settings = {'uniform': True}
    settings.update(kwargs)

    _figure, axes = artist(**settings)

    method = method.upper()

    settings = {'axes': axes, 'method': method}
    settings.update(kwargs)
    settings['standalone'] = False

    chromaticity_diagram_callable(**settings)

    planckian_locus_callable(**settings)

    if method == 'CIE 1931':

        def xy_to_ij(xy):
            """
            Converts given *CIE xy* chromaticity coordinates to *ij*
            chromaticity coordinates.
            """

            return xy

        bounding_box = (-0.1, 0.9, -0.1, 0.9)
    elif method == 'CIE 1960 UCS':

        def xy_to_ij(xy):
            """
            Converts given *CIE xy* chromaticity coordinates to *ij*
            chromaticity coordinates.
            """

            return UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(xy)))

        bounding_box = (-0.1, 0.7, -0.2, 0.6)
    else:
        raise ValueError('Invalid method: "{0}", must be one of '
                         '{{\'CIE 1931\', \'CIE 1960 UCS\'}}'.format(method))

    annotate_settings_collection = [{
        'annotate': True,
        'xytext': (-50, 30),
        'textcoords': 'offset points',
        'arrowprops': COLOUR_ARROW_STYLE,
    } for _ in range(len(illuminants))]

    if annotate_parameters is not None:
        if not isinstance(annotate_parameters, dict):
            assert len(annotate_parameters) == len(illuminants), (
                'Multiple annotate parameters defined, but they do not match '
                'the illuminants count!')

        for i, annotate_settings in enumerate(annotate_settings_collection):
            if isinstance(annotate_parameters, dict):
                annotate_settings.update(annotate_parameters)
            else:
                annotate_settings.update(annotate_parameters[i])

    for i, (illuminant, xy) in enumerate(illuminants.items()):
        ij = xy_to_ij(xy)

        axes.plot(ij[0],
                  ij[1],
                  'o',
                  color=COLOUR_STYLE_CONSTANTS.colour.brightest,
                  markeredgecolor=COLOUR_STYLE_CONSTANTS.colour.dark,
                  markersize=(COLOUR_STYLE_CONSTANTS.geometry.short * 6 +
                              COLOUR_STYLE_CONSTANTS.geometry.short * 0.75),
                  markeredgewidth=COLOUR_STYLE_CONSTANTS.geometry.short * 0.75,
                  label=illuminant)

        if annotate_settings_collection[i]['annotate']:
            annotate_settings = annotate_settings_collection[i]
            annotate_settings.pop('annotate')

            axes.annotate(illuminant, xy=ij, **annotate_settings)

    title = (('{0} Illuminants - Planckian Locus\n'
              '{1} Chromaticity Diagram - '
              'CIE 1931 2 Degree Standard Observer').format(
                  ', '.join(illuminants), method) if illuminants else
             ('Planckian Locus\n{0} Chromaticity Diagram - '
              'CIE 1931 2 Degree Standard Observer'.format(method)))

    settings.update({
        'axes': axes,
        'standalone': True,
        'bounding_box': bounding_box,
        'title': title,
    })
    settings.update(kwargs)

    return render(**settings)
Exemplo n.º 13
0
def plot_planckian_locus_in_chromaticity_diagram(
    illuminants: Union[str, Sequence[str]],
    chromaticity_diagram_callable: Callable = (
        plot_chromaticity_diagram  # type: ignore[has-type]
    ),
    method: Union[Literal["CIE 1931", "CIE 1960 UCS"], str] = "CIE 1931",
    annotate_kwargs: Optional[Union[Dict, List[Dict]]] = None,
    plot_kwargs: Optional[Union[Dict, List[Dict]]] = None,
    **kwargs: Any,
) -> Tuple[plt.Figure, plt.Axes]:
    """
    Plot the *Planckian Locus* and given illuminants in the
    *Chromaticity Diagram* according to given method.

    Parameters
    ----------
    illuminants
        Illuminants to plot. ``illuminants`` elements can be of any
        type or form supported by the
        :func:`colour.plotting.filter_passthrough` definition.
    chromaticity_diagram_callable
        Callable responsible for drawing the *Chromaticity Diagram*.
    method
        *Chromaticity Diagram* method.
    annotate_kwargs
        Keyword arguments for the :func:`matplotlib.pyplot.annotate`
        definition, used to annotate the resulting chromaticity coordinates
        with their respective spectral distribution names. ``annotate_kwargs``
        can be either a single dictionary applied to all the arrows with same
        settings or a sequence of dictionaries with different settings for each
        spectral distribution. The following special keyword arguments can also
        be used:

        -   ``annotate`` : Whether to annotate the spectral distributions.
    plot_kwargs
        Keyword arguments for the :func:`matplotlib.pyplot.plot` definition,
        used to control the style of the plotted illuminants. ``plot_kwargs``
        can be either a single dictionary applied to all the plotted
        illuminants with the same settings or a sequence of dictionaries with
        different settings for eachplotted illuminant.

    Other Parameters
    ----------------
    kwargs
        {:func:`colour.plotting.artist`,
        :func:`colour.plotting.diagrams.plot_chromaticity_diagram`,
        :func:`colour.plotting.temperature.plot_planckian_locus`,
        :func:`colour.plotting.render`},
        See the documentation of the previously listed definitions.

    Returns
    -------
    :class:`tuple`
        Current figure and axes.

    Examples
    --------
    >>> annotate_kwargs = [
    ...     {'xytext': (-25, 15), 'arrowprops':{'arrowstyle':'-'}},
    ...     {'arrowprops':{'arrowstyle':'-['}},
    ...     {},
    ... ]
    >>> plot_kwargs = [
    ...     {
    ...         'markersize' : 15,
    ...     },
    ...     {   'color': 'r'},
    ...     {},
    ... ]
    >>> plot_planckian_locus_in_chromaticity_diagram(
    ...     ['A', 'B', 'C'],
    ...     annotate_kwargs=annotate_kwargs,
    ...     plot_kwargs=plot_kwargs
    ... )  # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, <...AxesSubplot...>)

    .. image:: ../_static/Plotting_\
Plot_Planckian_Locus_In_Chromaticity_Diagram.png
        :align: center
        :alt: plot_planckian_locus_in_chromaticity_diagram
    """

    cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]

    illuminants_filtered = filter_passthrough(
        CCS_ILLUMINANTS.get(cmfs.name),
        illuminants  # type: ignore[arg-type]
    )

    settings: Dict[str, Any] = {"uniform": True}
    settings.update(kwargs)

    _figure, axes = artist(**settings)

    method = method.upper()

    settings = {"axes": axes, "method": method}
    settings.update(kwargs)
    settings["standalone"] = False

    chromaticity_diagram_callable(**settings)

    plot_planckian_locus(**settings)

    if method == "CIE 1931":

        def xy_to_ij(xy: NDArray) -> NDArray:
            """
            Convert given *CIE xy* chromaticity coordinates to *ij*
            chromaticity coordinates.
            """

            return xy

        bounding_box = (-0.1, 0.9, -0.1, 0.9)
    elif method == "CIE 1960 UCS":

        def xy_to_ij(xy: NDArray) -> NDArray:
            """
            Convert given *CIE xy* chromaticity coordinates to *ij*
            chromaticity coordinates.
            """

            return UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(xy)))

        bounding_box = (-0.1, 0.7, -0.2, 0.6)
    else:
        raise ValueError(f'Invalid method: "{method}", must be one of '
                         f'["CIE 1931", "CIE 1960 UCS"]')

    annotate_settings_collection = [{
        "annotate":
        True,
        "xytext": (-50, 30),
        "textcoords":
        "offset points",
        "arrowprops":
        CONSTANTS_ARROW_STYLE,
        "zorder":
        CONSTANTS_COLOUR_STYLE.zorder.foreground_annotation,
    } for _ in range(len(illuminants_filtered))]

    if annotate_kwargs is not None:
        update_settings_collection(
            annotate_settings_collection,
            annotate_kwargs,
            len(illuminants_filtered),
        )

    plot_settings_collection = [{
        "color":
        CONSTANTS_COLOUR_STYLE.colour.brightest,
        "label":
        f"{illuminant}",
        "marker":
        "o",
        "markeredgecolor":
        CONSTANTS_COLOUR_STYLE.colour.dark,
        "markeredgewidth":
        CONSTANTS_COLOUR_STYLE.geometry.short * 0.75,
        "markersize": (CONSTANTS_COLOUR_STYLE.geometry.short * 6 +
                       CONSTANTS_COLOUR_STYLE.geometry.short * 0.75),
        "zorder":
        CONSTANTS_COLOUR_STYLE.zorder.foreground_line,
    } for illuminant in illuminants_filtered]

    if plot_kwargs is not None:
        update_settings_collection(plot_settings_collection, plot_kwargs,
                                   len(illuminants_filtered))

    for i, (illuminant, xy) in enumerate(illuminants_filtered.items()):
        plot_settings = plot_settings_collection[i]

        ij = xy_to_ij(xy)

        axes.plot(ij[0], ij[1], **plot_settings)

        if annotate_settings_collection[i]["annotate"]:
            annotate_settings = annotate_settings_collection[i]
            annotate_settings.pop("annotate")

            axes.annotate(illuminant, xy=ij, **annotate_settings)

    title = ((
        f"{', '.join(illuminants_filtered)} Illuminants - Planckian Locus\n"
        f"{method.upper()} Chromaticity Diagram - "
        "CIE 1931 2 Degree Standard Observer") if illuminants_filtered else
             (f"Planckian Locus\n{method.upper()} Chromaticity Diagram - "
              f"CIE 1931 2 Degree Standard Observer"))

    settings.update({
        "axes": axes,
        "standalone": True,
        "bounding_box": bounding_box,
        "title": title,
    })
    settings.update(kwargs)

    return render(**settings)
Exemplo n.º 14
0
def plot_planckian_locus_in_chromaticity_diagram(
        illuminants,
        chromaticity_diagram_callable=plot_chromaticity_diagram,
        planckian_locus_callable=plot_planckian_locus,
        method='CIE 1931',
        annotate_kwargs=None,
        plot_kwargs=None,
        **kwargs):
    """
    Plots the *Planckian Locus* and given illuminants in the
    *Chromaticity Diagram* according to given method.

    Parameters
    ----------
    illuminants : unicode or object or array_like
        Illuminants to plot. ``illuminants`` elements can be of any
        type or form supported by the
        :func:`colour.plotting.filter_passthrough` definition.
    chromaticity_diagram_callable : callable, optional
        Callable responsible for drawing the *Chromaticity Diagram*.
    planckian_locus_callable : callable, optional
        Callable responsible for drawing the *Planckian Locus*.
    method : unicode, optional
        **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**,
        *Chromaticity Diagram* method.
    annotate_kwargs : dict or array_like, optional
        Keyword arguments for the :func:`plt.annotate` definition, used to
        annotate the resulting chromaticity coordinates with their respective
        illuminant names. ``annotate_kwargs`` can be either a single dictionary
        applied to all the arrows with same settings or a sequence of
        dictionaries with different settings for each illuminant.
        The following special keyword arguments can also be used:

        -   *annotate* : bool, whether to annotate the illuminants.
    plot_kwargs : dict or array_like, optional
        Keyword arguments for the :func:`plt.plot` definition, used to control
        the style of the plotted illuminants. ``plot_kwargs`` can be either a
        single dictionary applied to all the plotted illuminants with same
        settings or a sequence of dictionaries with different settings for each
        plotted illuminant.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`,
        :func:`colour.plotting.diagrams.plot_chromaticity_diagram`,
        :func:`colour.plotting.temperature.plot_planckian_locus`,
        :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.
        Also handles keywords arguments for deprecation management.

    Returns
    -------
    tuple
        Current figure and axes.

    Examples
    --------
    >>> annotate_kwargs = [
    ...     {'xytext': (-25, 15), 'arrowprops':{'arrowstyle':'-'}},
    ...     {'arrowprops':{'arrowstyle':'-['}},
    ...     {},
    ... ]
    >>> plot_kwargs = [
    ...     {
    ...         'markersize' : 15,
    ...     },
    ...     {   'color': 'r'},
    ...     {},
    ... ]
    >>> plot_planckian_locus_in_chromaticity_diagram(
    ...     ['A', 'B', 'C'],
    ...     annotate_kwargs=annotate_kwargs,
    ...     plot_kwargs=plot_kwargs
    ... )  # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, <...AxesSubplot...>)

    .. image:: ../_static/Plotting_\
Plot_Planckian_Locus_In_Chromaticity_Diagram.png
        :align: center
        :alt: plot_planckian_locus_in_chromaticity_diagram
    """

    annotate_kwargs = handle_arguments_deprecation(
        {
            'ArgumentRenamed': [['annotate_parameters', 'annotate_kwargs']],
        }, **kwargs).get('annotate_kwargs', annotate_kwargs)

    cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer']

    illuminants = filter_passthrough(CCS_ILLUMINANTS.get(cmfs.name),
                                     illuminants)

    settings = {'uniform': True}
    settings.update(kwargs)

    _figure, axes = artist(**settings)

    method = method.upper()

    settings = {'axes': axes, 'method': method}
    settings.update(kwargs)
    settings['standalone'] = False

    chromaticity_diagram_callable(**settings)

    planckian_locus_callable(**settings)

    if method == 'CIE 1931':

        def xy_to_ij(xy):
            """
            Converts given *CIE xy* chromaticity coordinates to *ij*
            chromaticity coordinates.
            """

            return xy

        bounding_box = (-0.1, 0.9, -0.1, 0.9)
    elif method == 'CIE 1960 UCS':

        def xy_to_ij(xy):
            """
            Converts given *CIE xy* chromaticity coordinates to *ij*
            chromaticity coordinates.
            """

            return UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(xy)))

        bounding_box = (-0.1, 0.7, -0.2, 0.6)
    else:
        raise ValueError('Invalid method: "{0}", must be one of '
                         '[\'CIE 1931\', \'CIE 1960 UCS\']'.format(method))

    annotate_settings_collection = [{
        'annotate': True,
        'xytext': (-50, 30),
        'textcoords': 'offset points',
        'arrowprops': CONSTANTS_ARROW_STYLE,
    } for _ in range(len(illuminants))]

    if annotate_kwargs is not None:
        update_settings_collection(annotate_settings_collection,
                                   annotate_kwargs, len(illuminants))

    plot_settings_collection = [{
        'color':
        CONSTANTS_COLOUR_STYLE.colour.brightest,
        'label':
        '{0}'.format(illuminant),
        'marker':
        'o',
        'markeredgecolor':
        CONSTANTS_COLOUR_STYLE.colour.dark,
        'markeredgewidth':
        CONSTANTS_COLOUR_STYLE.geometry.short * 0.75,
        'markersize': (CONSTANTS_COLOUR_STYLE.geometry.short * 6 +
                       CONSTANTS_COLOUR_STYLE.geometry.short * 0.75),
    } for illuminant in illuminants]

    if plot_kwargs is not None:
        update_settings_collection(plot_settings_collection, plot_kwargs,
                                   len(illuminants))

    for i, (illuminant, xy) in enumerate(illuminants.items()):
        plot_settings = plot_settings_collection[i]

        ij = xy_to_ij(xy)

        axes.plot(ij[0], ij[1], **plot_settings)

        if annotate_settings_collection[i]['annotate']:
            annotate_settings = annotate_settings_collection[i]
            annotate_settings.pop('annotate')

            axes.annotate(illuminant, xy=ij, **annotate_settings)

    title = (('{0} Illuminants - Planckian Locus\n'
              '{1} Chromaticity Diagram - '
              'CIE 1931 2 Degree Standard Observer').format(
                  ', '.join(illuminants), method) if illuminants else
             ('Planckian Locus\n{0} Chromaticity Diagram - '
              'CIE 1931 2 Degree Standard Observer'.format(method)))

    settings.update({
        'axes': axes,
        'standalone': True,
        'bounding_box': bounding_box,
        'title': title,
    })
    settings.update(kwargs)

    return render(**settings)
Exemplo n.º 15
0
def plot_planckian_locus_in_chromaticity_diagram(
        illuminants=None,
        annotate_parameters=None,
        chromaticity_diagram_callable=plot_chromaticity_diagram,
        method='CIE 1931',
        **kwargs):
    """
    Plots the *Planckian Locus* and given illuminants in the
    *Chromaticity Diagram* according to given method.

    Parameters
    ----------
    illuminants : array_like, optional
        Factory illuminants to plot.
    annotate_parameters : dict or array_like, optional
        Parameters for the :func:`plt.annotate` definition, used to annotate
        the resulting chromaticity coordinates with their respective illuminant
        names if ``annotate`` is set to *True*. ``annotate_parameters`` can be
        either a single dictionary applied to all the arrows with same settings
        or a sequence of dictionaries with different settings for each
        illuminant.
    chromaticity_diagram_callable : callable, optional
        Callable responsible for drawing the *Chromaticity Diagram*.
    method : unicode, optional
        **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**,
        *Chromaticity Diagram* method.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`,
        :func:`colour.plotting.diagrams.plot_chromaticity_diagram`,
        :func:`colour.plotting.temperature.plot_planckian_locus`,
        :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    tuple
        Current figure and axes.

    Examples
    --------
    >>> plot_planckian_locus_in_chromaticity_diagram(['A', 'B', 'C'])
    ... # doctest: +SKIP

    .. image:: ../_static/Plotting_\
Plot_Planckian_Locus_In_Chromaticity_Diagram.png
        :align: center
        :alt: plot_planckian_locus_in_chromaticity_diagram
    """

    cmfs = CMFS['CIE 1931 2 Degree Standard Observer']

    if illuminants is None:
        illuminants = ('A', 'B', 'C')

    illuminants = filter_passthrough(ILLUMINANTS.get(cmfs.name), illuminants)

    settings = {'uniform': True}
    settings.update(kwargs)

    _figure, axes = artist(**settings)

    method = method.upper()

    settings = {'axes': axes, 'method': method}
    settings.update(kwargs)
    settings['standalone'] = False

    chromaticity_diagram_callable(**settings)

    plot_planckian_locus(**settings)

    if method == 'CIE 1931':

        def xy_to_ij(xy):
            """
            Converts given *xy* chromaticity coordinates to *ij* chromaticity
            coordinates.
            """

            return xy

        bounding_box = (-0.1, 0.9, -0.1, 0.9)
    elif method == 'CIE 1960 UCS':

        def xy_to_ij(xy):
            """
            Converts given *xy* chromaticity coordinates to *ij* chromaticity
            coordinates.
            """

            return UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(xy)))

        bounding_box = (-0.1, 0.7, -0.2, 0.6)
    else:
        raise ValueError('Invalid method: "{0}", must be one of '
                         '{{\'CIE 1931\', \'CIE 1960 UCS\'}}'.format(method))

    annotate_settings_collection = [{
        'annotate': True,
        'xytext': (-50, 30),
        'textcoords': 'offset points',
        'arrowprops': COLOUR_ARROW_STYLE,
    } for _ in range(len(illuminants))]

    if annotate_parameters is not None:
        if not isinstance(annotate_parameters, dict):
            assert len(annotate_parameters) == len(illuminants), (
                'Multiple annotate parameters defined, but they do not match '
                'the illuminants count!')

        for i, annotate_settings in enumerate(annotate_settings_collection):
            if isinstance(annotate_parameters, dict):
                annotate_settings.update(annotate_parameters)
            else:
                annotate_settings.update(annotate_parameters[i])

    for i, (illuminant, xy) in enumerate(illuminants.items()):
        ij = xy_to_ij(xy)

        axes.plot(
            ij[0],
            ij[1],
            'o',
            color=COLOUR_STYLE_CONSTANTS.colour.brightest,
            markeredgecolor=COLOUR_STYLE_CONSTANTS.colour.dark,
            markersize=(COLOUR_STYLE_CONSTANTS.geometry.short * 6 +
                        COLOUR_STYLE_CONSTANTS.geometry.short * 0.75),
            markeredgewidth=COLOUR_STYLE_CONSTANTS.geometry.short * 0.75,
            label=illuminant)

        if annotate_settings_collection[i]['annotate']:
            annotate_settings = annotate_settings_collection[i]
            annotate_settings.pop('annotate')

            plt.annotate(illuminant, xy=ij, **annotate_settings)

    title = (('{0} Illuminants - Planckian Locus\n'
              '{1} Chromaticity Diagram - '
              'CIE 1931 2 Degree Standard Observer').format(
                  ', '.join(illuminants), method) if illuminants else
             ('Planckian Locus\n{0} Chromaticity Diagram - '
              'CIE 1931 2 Degree Standard Observer'.format(method)))

    settings.update({
        'axes': axes,
        'standalone': True,
        'bounding_box': bounding_box,
        'title': title,
    })
    settings.update(kwargs)

    return render(**settings)