• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

colour-science / colour / 4207404108

pending completion
4207404108

push

github-actions

Thomas Mansencal
Merge branch 'feature/v0.4.3' into develop

4 of 4 new or added lines in 2 files covered. (100.0%)

29816 of 38555 relevant lines covered (77.33%)

0.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.91
/colour/plotting/tm3018/components.py
1
"""
2
ANSI/IES TM-30-18 Colour Rendition Report Components
3
====================================================
4

5
Defines the *ANSI/IES TM-30-18 Colour Rendition Report* components plotting
6
objects:
7

8
-   :func:`colour.plotting.tm3018.components.plot_spectra_ANSIIESTM3018`
9
-   :func:`colour.plotting.tm3018.components.plot_colour_vector_graphic`
10
-   :func:`colour.plotting.tm3018.components.plot_16_bin_bars`
11
-   :func:`colour.plotting.tm3018.components.plot_local_chroma_shifts`
12
-   :func:`colour.plotting.tm3018.components.plot_local_hue_shifts`
13
-   :func:`colour.plotting.tm3018.components.plot_local_colour_fidelities`
14
-   :func:`colour.plotting.tm3018.components.plot_colour_fidelity_indexes`
15
"""
16

17
from __future__ import annotations
×
18

19
import os
×
20
import numpy as np
×
21
import matplotlib.pyplot as plt
×
22

23
from colour.algebra import sdiv, sdiv_mode
×
24
from colour.colorimetry import sd_to_XYZ
×
25
from colour.hints import Any, ArrayLike, Dict, Literal, Tuple, cast
×
26
from colour.io import read_image
×
27
from colour.plotting import (
×
28
    CONSTANTS_COLOUR_STYLE,
29
    artist,
30
    override_style,
31
    plot_image,
32
    render,
33
)
34
from colour.quality import ColourQuality_Specification_ANSIIESTM3018
×
35
from colour.utilities import as_float_array, validate_method
×
36

37
__author__ = "Colour Developers"
×
38
__copyright__ = "Copyright 2013 Colour Developers"
×
39
__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause"
×
40
__maintainer__ = "Colour Developers"
×
41
__email__ = "colour-developers@colour-science.org"
×
42
__status__ = "Production"
×
43

44
__all__ = [
×
45
    "ROOT_RESOURCES_ANSIIESTM3018",
46
    "plot_spectra_ANSIIESTM3018",
47
    "plot_colour_vector_graphic",
48
    "plot_16_bin_bars",
49
    "plot_local_chroma_shifts",
50
    "plot_local_hue_shifts",
51
    "plot_local_colour_fidelities",
52
    "plot_colour_fidelity_indexes",
53
]
54

55
ROOT_RESOURCES_ANSIIESTM3018: str = os.path.join(
×
56
    os.path.dirname(__file__), "resources"
57
)
58
"""Resources directory."""
×
59

60
_COLOURS_BIN_BAR: list = [
×
61
    "#A35C60",
62
    "#CC765E",
63
    "#CC8145",
64
    "#D8AC62",
65
    "#AC9959",
66
    "#919E5D",
67
    "#668B5E",
68
    "#61B290",
69
    "#7BBAA6",
70
    "#297A7E",
71
    "#55788D",
72
    "#708AB2",
73
    "#988CAA",
74
    "#735877",
75
    "#8F6682",
76
    "#BA7A8E",
77
]
78

79
_COLOURS_BIN_ARROW: list = [
×
80
    "#E62828",
81
    "#E74B4B",
82
    "#FB812E",
83
    "#FFB529",
84
    "#CBCA46",
85
    "#7EB94C",
86
    "#41C06D",
87
    "#009C7C",
88
    "#16BCB0",
89
    "#00A4BF",
90
    "#0085C3",
91
    "#3B62AA",
92
    "#4568AE",
93
    "#6A4E85",
94
    "#9D69A1",
95
    "#A74F81",
96
]
97

98
_COLOURS_TCS_BAR: list = [
×
99
    "#F1BDCD",
100
    "#CA6183",
101
    "#573A40",
102
    "#CD8791",
103
    "#AD3F55",
104
    "#925F62",
105
    "#933440",
106
    "#8C3942",
107
    "#413D3E",
108
    "#FA8070",
109
    "#C35644",
110
    "#DA604A",
111
    "#824E39",
112
    "#BCA89F",
113
    "#C29A89",
114
    "#8D593C",
115
    "#915E3F",
116
    "#99745B",
117
    "#D39257",
118
    "#D07F2C",
119
    "#FEB45F",
120
    "#EFA248",
121
    "#F0DFBD",
122
    "#FED586",
123
    "#D0981E",
124
    "#FED06A",
125
    "#B5AC81",
126
    "#645D37",
127
    "#EAD163",
128
    "#9E9464",
129
    "#EBD969",
130
    "#C4B135",
131
    "#E6DE9C",
132
    "#99912C",
133
    "#61603A",
134
    "#C2C2AF",
135
    "#6D703B",
136
    "#D2D7A1",
137
    "#4B5040",
138
    "#6B7751",
139
    "#D3DCC3",
140
    "#88B33A",
141
    "#8EBF3E",
142
    "#3E3F3D",
143
    "#65984A",
144
    "#83A96E",
145
    "#92AE86",
146
    "#91CD8E",
147
    "#477746",
148
    "#568C6A",
149
    "#659477",
150
    "#276E49",
151
    "#008D62",
152
    "#B6E2D4",
153
    "#A5D9CD",
154
    "#39C4AD",
155
    "#00A18A",
156
    "#009786",
157
    "#B4E1D9",
158
    "#CDDDDC",
159
    "#99C1C0",
160
    "#909FA1",
161
    "#494D4E",
162
    "#009FA8",
163
    "#32636A",
164
    "#007788",
165
    "#007F95",
166
    "#66A0B2",
167
    "#687D88",
168
    "#75B6DB",
169
    "#1E5574",
170
    "#AAB9C3",
171
    "#3091C4",
172
    "#3B3E41",
173
    "#274D72",
174
    "#376FB8",
175
    "#496692",
176
    "#3B63AC",
177
    "#A0AED5",
178
    "#9293C8",
179
    "#61589D",
180
    "#D4D3E5",
181
    "#ACA6CA",
182
    "#3E3B45",
183
    "#5F5770",
184
    "#A08CC7",
185
    "#664782",
186
    "#A77AB5",
187
    "#6A4172",
188
    "#7D4983",
189
    "#C4BFC4",
190
    "#937391",
191
    "#AE91AA",
192
    "#764068",
193
    "#BF93B1",
194
    "#D7A9C5",
195
    "#9D587F",
196
    "#CE6997",
197
    "#AE4A79",
198
]
199

200

201
@override_style()
×
202
def plot_spectra_ANSIIESTM3018(
×
203
    specification: ColourQuality_Specification_ANSIIESTM3018, **kwargs: Any
204
) -> Tuple[plt.Figure, plt.Axes]:
205
    """
206
    Plot a comparison of the spectral distributions of a test emission source
207
    and a reference illuminant for *ANSI/IES TM-30-18 Colour Rendition Report*.
208

209
    Parameters
210
    ----------
211
    specification
212
        *ANSI/IES TM-30-18 Colour Rendition Report* specification.
213

214
    Other Parameters
215
    ----------------
216
    kwargs
217
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
218
        See the documentation of the previously listed definitions.
219

220
    Returns
221
    -------
222
    :class:`tuple`
223
        Current figure and axes
224

225
    Examples
226
    --------
227
    >>> from colour import SDS_ILLUMINANTS
228
    >>> from colour.quality import colour_fidelity_index_ANSIIESTM3018
229
    >>> sd = SDS_ILLUMINANTS["FL2"]
230
    >>> specification = colour_fidelity_index_ANSIIESTM3018(sd, True)
231
    >>> plot_spectra_ANSIIESTM3018(specification)
232
    ... # doctest: +ELLIPSIS
233
    (<Figure size ... with 1 Axes>, <...Axes...>)
234
    """
235

236
    settings: Dict[str, Any] = dict(kwargs)
1✔
237

238
    _figure, axes = artist(**settings)
1✔
239

240
    Y_reference = sd_to_XYZ(specification.sd_reference)[1]
1✔
241
    Y_test = sd_to_XYZ(specification.sd_test)[1]
1✔
242

243
    with sdiv_mode():
1✔
244
        reference_values = sdiv(specification.sd_reference.values, Y_reference)
1✔
245
        test_values = sdiv(specification.sd_test.values, Y_test)
1✔
246

247
    axes.plot(
1✔
248
        specification.sd_reference.wavelengths,
249
        reference_values,
250
        "black",
251
        label="Reference",
252
        zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line,
253
    )
254
    axes.plot(
1✔
255
        specification.sd_test.wavelengths,
256
        test_values,
257
        "#F05046",
258
        label="Test",
259
        zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line,
260
    )
261
    axes.tick_params(axis="y", which="both", length=0)
1✔
262
    axes.set_yticklabels([])
1✔
263

264
    settings = {
1✔
265
        "axes": axes,
266
        "legend": True,
267
        "legend_columns": 2,
268
        "x_label": "Wavelength (nm)",
269
        "y_label": "Radiant Power\n(Equal Luminous Flux)",
270
    }
271
    settings.update(kwargs)
1✔
272

273
    return render(**settings)
1✔
274

275

276
def plot_colour_vector_graphic(
×
277
    specification: ColourQuality_Specification_ANSIIESTM3018, **kwargs: Any
278
) -> Tuple[plt.Figure, plt.Axes]:
279
    """
280
    Plot *Color Vector Graphic* according to
281
    *ANSI/IES TM-30-18 Colour Rendition Report*.
282

283
    Parameters
284
    ----------
285
    specification
286
        *ANSI/IES TM-30-18 Colour Rendition Report* specification.
287

288
    Other Parameters
289
    ----------------
290
    kwargs
291
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
292
        See the documentation of the previously listed definitions.
293

294
    Returns
295
    -------
296
    :class:`tuple`
297
        Current figure and axes
298

299
    Examples
300
    --------
301
    >>> from colour import SDS_ILLUMINANTS
302
    >>> from colour.quality import colour_fidelity_index_ANSIIESTM3018
303
    >>> sd = SDS_ILLUMINANTS["FL2"]
304
    >>> specification = colour_fidelity_index_ANSIIESTM3018(sd, True)
305
    >>> plot_colour_vector_graphic(specification)
306
    ... # doctest: +ELLIPSIS
307
    (<Figure size ... with 1 Axes>, <...Axes...>)
308
    """
309

310
    settings: Dict[str, Any] = dict(kwargs)
1✔
311
    settings["standalone"] = False
1✔
312

313
    # Background
314
    background_image = read_image(
1✔
315
        os.path.join(ROOT_RESOURCES_ANSIIESTM3018, "CVG_Background.jpg")
316
    )
317
    _figure, axes = plot_image(
1✔
318
        background_image,
319
        imshow_kwargs={"extent": [-1.5, 1.5, -1.5, 1.5]},
320
        **settings,
321
    )
322

323
    # Lines dividing the hues in 16 equal parts along with bin numbers.
324
    axes.plot(0, 0, "+", color="#A6A6A6")
1✔
325
    for i in range(16):
1✔
326
        angle = 2 * np.pi * i / 16
1✔
327
        dx = np.cos(angle)
1✔
328
        dy = np.sin(angle)
1✔
329
        axes.plot(
1✔
330
            (0.15 * dx, 1.5 * dx),
331
            (0.15 * dy, 1.5 * dy),
332
            "--",
333
            color="#A6A6A6",
334
            lw=0.75,
335
            zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line,
336
        )
337

338
        angle = 2 * np.pi * (i + 0.5) / 16
1✔
339
        axes.annotate(
1✔
340
            str(i + 1),
341
            color="#A6A6A6",
342
            ha="center",
343
            va="center",
344
            xy=(1.41 * np.cos(angle), 1.41 * np.sin(angle)),
345
            weight="bold",
346
            size=9,
347
            zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_annotation,
348
        )
349

350
    # Circles.
351
    circle = plt.Circle(
1✔
352
        (0, 0),
353
        1,
354
        color="black",
355
        lw=1.25,
356
        fill=False,
357
        zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_polygon,
358
    )
359
    axes.add_artist(circle)
1✔
360
    for radius in [0.8, 0.9, 1.1, 1.2]:
1✔
361
        circle = plt.Circle(
1✔
362
            (0, 0),
363
            radius,
364
            color="white",
365
            lw=0.75,
366
            fill=False,
367
            zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_polygon,
368
        )
369
        axes.add_artist(circle)
1✔
370

371
    # -/+20% marks near the white circles.
372
    props = {"ha": "right", "color": "white", "size": 7}
1✔
373
    axes.annotate(
1✔
374
        "-20%",
375
        xy=(0, -0.8),
376
        va="bottom",
377
        zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_annotation,
378
        **props,
379
    )
380
    axes.annotate(
1✔
381
        "+20%",
382
        xy=(0, -1.2),
383
        va="top",
384
        zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_annotation,
385
        **props,
386
    )
387

388
    # Average "CAM02" h correlate for each bin, in radians.
389
    average_hues = np.radians(
1✔
390
        [
391
            np.mean(
392
                [
393
                    cast(float, specification.colorimetry_data[1][i].CAM.h)
394
                    for i in specification.bins[j]
395
                ]
396
            )
397
            for j in range(16)
398
        ]
399
    )
400
    xy_reference = np.transpose(
1✔
401
        np.vstack([np.cos(average_hues), np.sin(average_hues)])
402
    )
403

404
    # Arrow offsets as defined by the standard.
405
    offsets = (
1✔
406
        specification.averages_test - specification.averages_reference
407
    ) / specification.average_norms[:, None]
408
    xy_test = xy_reference + offsets
1✔
409

410
    # Arrows.
411
    for i in range(16):
1✔
412
        axes.arrow(
1✔
413
            xy_reference[i, 0],
414
            xy_reference[i, 1],
415
            offsets[i, 0],
416
            offsets[i, 1],
417
            length_includes_head=True,
418
            width=0.005,
419
            head_width=0.04,
420
            linewidth=None,
421
            color=_COLOURS_BIN_ARROW[i],
422
            zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_annotation,
423
        )
424

425
    # Red (test) gamut shape.
426
    loop = np.append(xy_test, xy_test[0, None], axis=0)
1✔
427
    axes.plot(
1✔
428
        loop[:, 0],
429
        loop[:, 1],
430
        "-",
431
        color="#F05046",
432
        lw=2,
433
        zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line,
434
    )
435

436
    def corner_label_and_text(label: str, text: str, ha: str, va: str):
1✔
437
        """Draw a label and text in given corner."""
438

439
        x = -1.45 if ha == "left" else 1.45
1✔
440
        y = 1.45 if va == "top" else -1.45
1✔
441
        y_text = -15 if va == "top" else 15
1✔
442

443
        axes.annotate(
1✔
444
            text,
445
            xy=(x, y),
446
            color="black",
447
            ha=ha,
448
            va=va,
449
            weight="bold",
450
            size="larger",
451
            zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_label,
452
        )
453
        axes.annotate(
1✔
454
            label,
455
            xy=(x, y),
456
            color="black",
457
            xytext=(0, y_text),
458
            textcoords="offset points",
459
            ha=ha,
460
            va=va,
461
            size="small",
462
            zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_label,
463
        )
464

465
    corner_label_and_text("$R_f$", f"{specification.R_f:.0f}", "left", "top")
1✔
466
    corner_label_and_text("$R_g$", f"{specification.R_g:.0f}", "right", "top")
1✔
467
    corner_label_and_text(
1✔
468
        "CCT", f"{specification.CCT:.0f} K", "left", "bottom"
469
    )
470
    corner_label_and_text(
1✔
471
        "$D_{uv}$", f"{specification.D_uv:.4f}", "right", "bottom"
472
    )
473

474
    settings = {"standalone": True}
1✔
475
    settings.update(kwargs)
1✔
476

477
    return render(**settings)
1✔
478

479

480
def plot_16_bin_bars(
×
481
    values: ArrayLike,
482
    label_template: str,
483
    x_ticker: bool = False,
484
    label_orientation: Literal["Horizontal", "Vertical"] | str = "Vertical",
485
    **kwargs: Any,
486
) -> Tuple[plt.Figure, plt.Axes]:
487
    """
488
    Plot the 16 bin bars for given values according to
489
    *ANSI/IES TM-30-18 Colour Rendition Report*.
490

491
    Parameters
492
    ----------
493
    values
494
        Values to generate the bin bars for.
495
    label_template
496
        Template to format the labels.
497
    x_ticker
498
        Whether to show the *X* axis ticker and the associated label.
499
    label_orientation
500
        Orientation of the labels.
501

502
    Other Parameters
503
    ----------------
504
    kwargs
505
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
506
        See the documentation of the previously listed definitions.
507

508
    Returns
509
    -------
510
    :class:`tuple`
511
        Current figure and axes
512

513
    Examples
514
    --------
515
    >>> plot_16_bin_bars(np.arange(16), "{0}")
516
    ... # doctest: +ELLIPSIS
517
    (<Figure size ... with 1 Axes>, <...Axes...>)
518
    """
519

520
    values = as_float_array(values)
1✔
521

522
    label_orientation = validate_method(
1✔
523
        label_orientation, ["Horizontal", "Vertical"]
524
    )
525

526
    _figure, axes = artist(**kwargs)
1✔
527

528
    bar_count = len(_COLOURS_BIN_BAR)
1✔
529
    axes.bar(
1✔
530
        np.arange(bar_count) + 1,
531
        values,
532
        color=_COLOURS_BIN_BAR,
533
        width=1,
534
        edgecolor="black",
535
        linewidth=CONSTANTS_COLOUR_STYLE.geometry.short / 3,
536
        zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon,
537
    )
538
    axes.set_xlim(0.5, bar_count + 0.5)
1✔
539
    if x_ticker:
1✔
540
        axes.set_xticks(np.arange(1, bar_count + 1))
1✔
541
        axes.set_xlabel("Hue-Angle Bin (j)")
1✔
542
    else:
543
        axes.set_xticks([])
1✔
544

545
    label_orientation = label_orientation.lower()
1✔
546
    value_max = np.max(values)
1✔
547
    for i, value in enumerate(values):
1✔
548
        if label_orientation == "vertical":
1✔
549
            va, vo = (
1✔
550
                ("bottom", value_max * 0.15)
551
                if value > 0
552
                else ("top", -value_max * 0.15)
553
            )
554
            axes.annotate(
1✔
555
                label_template.format(value),
556
                xy=(i + 1, value + vo),
557
                rotation=90,
558
                fontsize="xx-small",
559
                ha="center",
560
                va=va,
561
                zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_label,
562
            )
563
        elif label_orientation == "horizontal":
1✔
564
            va, vo = (
1✔
565
                ("bottom", value_max * 0.025)
566
                if value < 90
567
                else ("top", -value_max * 0.025)
568
            )
569
            axes.annotate(
1✔
570
                label_template.format(value),
571
                xy=(i + 1, value + vo),
572
                fontsize="xx-small",
573
                ha="center",
574
                va=va,
575
                zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_label,
576
            )
577

578
    return render(**kwargs)
1✔
579

580

581
def plot_local_chroma_shifts(
×
582
    specification: ColourQuality_Specification_ANSIIESTM3018,
583
    x_ticker: bool = False,
584
    **kwargs: Any,
585
) -> Tuple[plt.Figure, plt.Axes]:
586
    """
587
    Plot the local chroma shifts according to
588
    *ANSI/IES TM-30-18 Colour Rendition Report*.
589

590
    Parameters
591
    ----------
592
    specification
593
        *ANSI/IES TM-30-18 Colour Rendition Report* specification.
594
    x_ticker
595
        Whether to show the *X* axis ticker and the associated label.
596

597
    Other Parameters
598
    ----------------
599
    kwargs
600
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
601
        See the documentation of the previously listed definitions.
602

603
    Returns
604
    -------
605
    :class:`tuple`
606
        Current figure and axes
607

608
    Examples
609
    --------
610
    >>> from colour import SDS_ILLUMINANTS
611
    >>> from colour.quality import colour_fidelity_index_ANSIIESTM3018
612
    >>> sd = SDS_ILLUMINANTS["FL2"]
613
    >>> specification = colour_fidelity_index_ANSIIESTM3018(sd, True)
614
    >>> plot_local_chroma_shifts(specification)
615
    ... # doctest: +ELLIPSIS
616
    (<Figure size ... with 1 Axes>, <...Axes...>)
617
    """
618

619
    settings: Dict[str, Any] = dict(kwargs)
1✔
620
    settings["standalone"] = False
1✔
621

622
    _figure, axes = plot_16_bin_bars(
1✔
623
        specification.R_cs, "{0:.0f}%", x_ticker, **settings
624
    )
625

626
    axes.set_ylim(-40, 40)
1✔
627
    axes.set_ylabel("Local Chroma Shift ($R_{cs,hj}$)")
1✔
628

629
    ticks = np.arange(-40, 41, 10)
1✔
630
    axes.set_yticks(ticks)
1✔
631
    axes.set_yticklabels([f"{value}%" for value in ticks])
1✔
632

633
    settings = {"standalone": True}
1✔
634
    settings.update(kwargs)
1✔
635

636
    return render(**settings)
1✔
637

638

639
def plot_local_hue_shifts(
×
640
    specification: ColourQuality_Specification_ANSIIESTM3018,
641
    x_ticker: bool = False,
642
    **kwargs: Any,
643
) -> Tuple[plt.Figure, plt.Axes]:
644
    """
645
    Plot the local hue shifts according to
646
    *ANSI/IES TM-30-18 Colour Rendition Report*.
647

648
    Parameters
649
    ----------
650
    specification
651
        *ANSI/IES TM-30-18 Colour Rendition Report* specification.
652
    x_ticker
653
        Whether to show the *X* axis ticker and the associated label.
654

655
    Other Parameters
656
    ----------------
657
    kwargs
658
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
659
        See the documentation of the previously listed definitions.
660

661
    Returns
662
    -------
663
    :class:`tuple`
664
        Current figure and axes
665

666
    Examples
667
    --------
668
    >>> from colour import SDS_ILLUMINANTS
669
    >>> from colour.quality import colour_fidelity_index_ANSIIESTM3018
670
    >>> sd = SDS_ILLUMINANTS["FL2"]
671
    >>> specification = colour_fidelity_index_ANSIIESTM3018(sd, True)
672
    >>> plot_local_hue_shifts(specification)
673
    ... # doctest: +ELLIPSIS
674
    (<Figure size ... with 1 Axes>, <...Axes...>)
675
    """
676

677
    settings: Dict[str, Any] = dict(kwargs)
1✔
678
    settings["standalone"] = False
1✔
679

680
    _figure, axes = plot_16_bin_bars(
1✔
681
        specification.R_hs, "{0:.2f}", x_ticker, **settings
682
    )
683
    axes.set_ylim(-0.5, 0.5)
1✔
684
    axes.set_yticks(np.arange(-0.5, 0.51, 0.1))
1✔
685
    axes.set_ylabel("Local Hue Shift ($R_{hs,hj}$)")
1✔
686

687
    settings = {"standalone": True}
1✔
688
    settings.update(kwargs)
1✔
689

690
    return render(**settings)
1✔
691

692

693
def plot_local_colour_fidelities(
×
694
    specification: ColourQuality_Specification_ANSIIESTM3018,
695
    x_ticker: bool = False,
696
    **kwargs: Any,
697
) -> Tuple[plt.Figure, plt.Axes]:
698
    """
699
    Plot the local colour fidelities according to
700
    *ANSI/IES TM-30-18 Colour Rendition Report*.
701

702
    Parameters
703
    ----------
704
    specification
705
        *ANSI/IES TM-30-18 Colour Rendition Report* specification.
706
    x_ticker
707
        Whether to show the *X* axis ticker and the associated label.
708

709
    Other Parameters
710
    ----------------
711
    kwargs
712
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
713
        See the documentation of the previously listed definitions.
714

715
    Returns
716
    -------
717
    :class:`tuple`
718
        Current figure and axes
719

720
    Examples
721
    --------
722
    >>> from colour import SDS_ILLUMINANTS
723
    >>> from colour.quality import colour_fidelity_index_ANSIIESTM3018
724
    >>> sd = SDS_ILLUMINANTS["FL2"]
725
    >>> specification = colour_fidelity_index_ANSIIESTM3018(sd, True)
726
    >>> plot_local_colour_fidelities(specification)
727
    ... # doctest: +ELLIPSIS
728
    (<Figure size ... with 1 Axes>, <...Axes...>)
729
    """
730

731
    settings: Dict[str, Any] = dict(kwargs)
1✔
732
    settings["standalone"] = False
1✔
733

734
    _figure, axes = plot_16_bin_bars(
1✔
735
        specification.R_fs, "{0:.0f}", x_ticker, "Horizontal", **settings
736
    )
737
    axes.set_ylim(0, 100)
1✔
738
    axes.set_yticks(np.arange(0, 101, 10))
1✔
739
    axes.set_ylabel("Local Color Fidelity ($R_{f,hj}$)")
1✔
740

741
    settings = {"standalone": True}
1✔
742
    settings.update(kwargs)
1✔
743

744
    return render(**settings)
1✔
745

746

747
def plot_colour_fidelity_indexes(
×
748
    specification: ColourQuality_Specification_ANSIIESTM3018, **kwargs: Any
749
) -> Tuple[plt.Figure, plt.Axes]:
750
    """
751
    Plot the local chroma shifts according to
752
    *ANSI/IES TM-30-18 Colour Rendition Report*.
753

754
    Parameters
755
    ----------
756
    specification
757
        *ANSI/IES TM-30-18 Colour Rendition Report* specification.
758

759
    Other Parameters
760
    ----------------
761
    kwargs
762
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
763
        See the documentation of the previously listed definitions.
764

765
    Returns
766
    -------
767
    :class:`tuple`
768
        Current figure and axes
769

770
    Examples
771
    --------
772
    >>> from colour import SDS_ILLUMINANTS
773
    >>> from colour.quality import colour_fidelity_index_ANSIIESTM3018
774
    >>> sd = SDS_ILLUMINANTS["FL2"]
775
    >>> specification = colour_fidelity_index_ANSIIESTM3018(sd, True)
776
    >>> plot_colour_fidelity_indexes(specification)
777
    ... # doctest: +ELLIPSIS
778
    (<Figure size ... with 1 Axes>, <...Axes...>)
779
    """
780

781
    _figure, axes = artist(**kwargs)
1✔
782

783
    bar_count = len(_COLOURS_TCS_BAR)
1✔
784
    axes.bar(
1✔
785
        np.arange(bar_count) + 1,
786
        specification.R_s,
787
        color=_COLOURS_TCS_BAR,
788
        width=1,
789
        edgecolor="black",
790
        linewidth=CONSTANTS_COLOUR_STYLE.geometry.short / 3,
791
        zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon,
792
    )
793
    axes.set_xlim(0.5, bar_count + 0.5)
1✔
794
    axes.set_ylim(0, 100)
1✔
795
    axes.set_yticks(np.arange(0, 110, 10))
1✔
796
    axes.set_ylabel("Color Sample Fidelity ($R_{f,CESi}$)")
1✔
797

798
    ticks = list(range(1, bar_count + 1, 1))
1✔
799
    axes.set_xticks(ticks)
1✔
800

801
    labels = [
1✔
802
        f"CES{i:02d}" if i % 3 == 1 else "" for i in range(1, bar_count + 1)
803
    ]
804
    axes.set_xticklabels(labels, rotation=90)
1✔
805

806
    return render(**kwargs)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc