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

colour-science / colour / 18370061804

09 Oct 2025 08:19AM UTC coverage: 76.753% (-22.6%) from 99.349%
18370061804

push

github

KelSolaar
Merge branch 'feature/v0.4.7' into develop

32663 of 42556 relevant lines covered (76.75%)

0.77 hits per line

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

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

5
Define the *ANSI/IES TM-30-18 Colour Rendition Report* components plotting
6
objects for comprehensive colour rendition evaluation and visualization.
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 typing
×
21

22
import numpy as np
×
23

24
if typing.TYPE_CHECKING:
25
    from matplotlib.axes import Axes
26
    from matplotlib.figure import Figure
27

28
from matplotlib.patches import Circle
×
29

30
from colour.algebra import sdiv, sdiv_mode
×
31
from colour.colorimetry import sd_to_XYZ
×
32

33
if typing.TYPE_CHECKING:
34
    from colour.hints import Any, ArrayLike, Dict, Literal, Tuple
35
    from colour.quality import ColourQuality_Specification_ANSIIESTM3018
36

37
from colour.io import read_image
×
38
from colour.plotting import (
×
39
    CONSTANTS_COLOUR_STYLE,
40
    artist,
41
    override_style,
42
    plot_image,
43
    render,
44
)
45
from colour.utilities import as_float_array, validate_method
×
46

47
__author__ = "Colour Developers"
×
48
__copyright__ = "Copyright 2013 Colour Developers"
×
49
__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
×
50
__maintainer__ = "Colour Developers"
×
51
__email__ = "colour-developers@colour-science.org"
×
52
__status__ = "Production"
×
53

54
__all__ = [
×
55
    "ROOT_RESOURCES_ANSIIESTM3018",
56
    "plot_spectra_ANSIIESTM3018",
57
    "plot_colour_vector_graphic",
58
    "plot_16_bin_bars",
59
    "plot_local_chroma_shifts",
60
    "plot_local_hue_shifts",
61
    "plot_local_colour_fidelities",
62
    "plot_colour_fidelity_indexes",
63
]
64

65
ROOT_RESOURCES_ANSIIESTM3018: str = os.path.join(os.path.dirname(__file__), "resources")
×
66
"""Resources directory."""
×
67

68
_COLOURS_BIN_BAR: list = [
×
69
    "#A35C60",
70
    "#CC765E",
71
    "#CC8145",
72
    "#D8AC62",
73
    "#AC9959",
74
    "#919E5D",
75
    "#668B5E",
76
    "#61B290",
77
    "#7BBAA6",
78
    "#297A7E",
79
    "#55788D",
80
    "#708AB2",
81
    "#988CAA",
82
    "#735877",
83
    "#8F6682",
84
    "#BA7A8E",
85
]
86

87
_COLOURS_BIN_ARROW: list = [
×
88
    "#E62828",
89
    "#E74B4B",
90
    "#FB812E",
91
    "#FFB529",
92
    "#CBCA46",
93
    "#7EB94C",
94
    "#41C06D",
95
    "#009C7C",
96
    "#16BCB0",
97
    "#00A4BF",
98
    "#0085C3",
99
    "#3B62AA",
100
    "#4568AE",
101
    "#6A4E85",
102
    "#9D69A1",
103
    "#A74F81",
104
]
105

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

208

209
@override_style()
×
210
def plot_spectra_ANSIIESTM3018(
×
211
    specification: ColourQuality_Specification_ANSIIESTM3018, **kwargs: Any
212
) -> Tuple[Figure, Axes]:
213
    """
214
    Plot the spectral distributions of a test emission source and reference
215
    illuminant for *ANSI/IES TM-30-18 Colour Rendition Report*.
216

217
    Parameters
218
    ----------
219
    specification
220
        *ANSI/IES TM-30-18 Colour Rendition Report* specification.
221

222
    Other Parameters
223
    ----------------
224
    kwargs
225
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
226
        See the documentation of the previously listed definitions.
227

228
    Returns
229
    -------
230
    :class:`tuple`
231
        Current figure and axes.
232

233
    Examples
234
    --------
235
    >>> from colour import SDS_ILLUMINANTS
236
    >>> from colour.quality import colour_fidelity_index_ANSIIESTM3018
237
    >>> sd = SDS_ILLUMINANTS["FL2"]
238
    >>> specification = colour_fidelity_index_ANSIIESTM3018(sd, True)
239
    >>> plot_spectra_ANSIIESTM3018(specification)
240
    ... # doctest: +ELLIPSIS
241
    (<Figure size ... with 1 Axes>, <...Axes...>)
242
    """
243

244
    settings: Dict[str, Any] = dict(kwargs)
1✔
245

246
    _figure, axes = artist(**settings)
1✔
247

248
    Y_reference = sd_to_XYZ(specification.sd_reference)[1]
1✔
249
    Y_test = sd_to_XYZ(specification.sd_test)[1]
1✔
250

251
    with sdiv_mode():
1✔
252
        reference_values = sdiv(specification.sd_reference.values, Y_reference)
1✔
253
        test_values = sdiv(specification.sd_test.values, Y_test)
1✔
254

255
    axes.plot(
1✔
256
        specification.sd_reference.wavelengths,
257
        reference_values,
258
        "black",
259
        label="Reference",
260
        zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line,
261
    )
262
    axes.plot(
1✔
263
        specification.sd_test.wavelengths,
264
        test_values,
265
        "#F05046",
266
        label="Test",
267
        zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line,
268
    )
269
    axes.tick_params(axis="y", which="both", length=0)
1✔
270
    axes.set_yticklabels([])
1✔
271

272
    settings = {
1✔
273
        "axes": axes,
274
        "legend": True,
275
        "legend_columns": 2,
276
        "x_label": "Wavelength (nm)",
277
        "y_label": "Radiant Power\n(Equal Luminous Flux)",
278
    }
279
    settings.update(kwargs)
1✔
280

281
    return render(**settings)
1✔
282

283

284
def plot_colour_vector_graphic(
×
285
    specification: ColourQuality_Specification_ANSIIESTM3018, **kwargs: Any
286
) -> Tuple[Figure, Axes]:
287
    """
288
    Plot *Color Vector Graphic* using the *ANSI/IES TM-30-18 Colour
289
    Rendition Report*.
290

291
    Parameters
292
    ----------
293
    specification
294
        *ANSI/IES TM-30-18 Colour Rendition Report* specification.
295

296
    Other Parameters
297
    ----------------
298
    kwargs
299
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
300
        See the documentation of the previously listed definitions.
301

302
    Returns
303
    -------
304
    :class:`tuple`
305
        Current figure and axes.
306

307
    Examples
308
    --------
309
    >>> from colour import SDS_ILLUMINANTS
310
    >>> from colour.quality import colour_fidelity_index_ANSIIESTM3018
311
    >>> sd = SDS_ILLUMINANTS["FL2"]
312
    >>> specification = colour_fidelity_index_ANSIIESTM3018(sd, True)
313
    >>> plot_colour_vector_graphic(specification)
314
    ... # doctest: +ELLIPSIS
315
    (<Figure size ... with 1 Axes>, <...Axes...>)
316
    """
317

318
    settings: Dict[str, Any] = dict(kwargs)
1✔
319
    settings["show"] = False
1✔
320

321
    # Background
322
    background_image = read_image(
1✔
323
        os.path.join(ROOT_RESOURCES_ANSIIESTM3018, "CVG_Background.jpg")
324
    )
325
    _figure, axes = plot_image(
1✔
326
        background_image,
327
        imshow_kwargs={"extent": [-1.5, 1.5, -1.5, 1.5]},
328
        **settings,
329
    )
330

331
    # Lines dividing the hues in 16 equal parts along with bin numbers.
332
    axes.plot(0, 0, "+", color="#A6A6A6")
1✔
333
    for i in range(16):
1✔
334
        angle = 2 * np.pi * i / 16
1✔
335
        dx = np.cos(angle)
1✔
336
        dy = np.sin(angle)
1✔
337
        axes.plot(
1✔
338
            (0.15 * dx, 1.5 * dx),
339
            (0.15 * dy, 1.5 * dy),
340
            "--",
341
            color="#A6A6A6",
342
            lw=0.75,
343
            zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line,
344
        )
345

346
        angle = 2 * np.pi * (i + 0.5) / 16
1✔
347
        axes.annotate(
1✔
348
            str(i + 1),
349
            color="#A6A6A6",
350
            ha="center",
351
            va="center",
352
            xy=(1.41 * np.cos(angle), 1.41 * np.sin(angle)),
353
            weight="bold",
354
            size=9,
355
            zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_annotation,
356
        )
357

358
    # Circles.
359
    circle = Circle(
1✔
360
        (0, 0),
361
        1,
362
        color="black",
363
        lw=1.25,
364
        fill=False,
365
        zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_polygon,
366
    )
367
    axes.add_artist(circle)
1✔
368
    for radius in [0.8, 0.9, 1.1, 1.2]:
1✔
369
        circle = Circle(
1✔
370
            (0, 0),
371
            radius,
372
            color="white",
373
            lw=0.75,
374
            fill=False,
375
            zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_polygon,
376
        )
377
        axes.add_artist(circle)
1✔
378

379
    # -/+20% marks near the white circles.
380
    props = {"ha": "right", "color": "white", "size": 7}
1✔
381
    axes.annotate(
1✔
382
        "-20%",
383
        xy=(0, -0.8),
384
        va="bottom",
385
        zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_annotation,
386
        **props,
387
    )
388
    axes.annotate(
1✔
389
        "+20%",
390
        xy=(0, -1.2),
391
        va="top",
392
        zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_annotation,
393
        **props,
394
    )
395

396
    # Average "CAM02" h correlate for each bin, in radians.
397
    average_hues = np.radians(
1✔
398
        [
399
            np.mean(
400
                specification.colorimetry_data[1].JMh[specification.bins == j, 2],
401
            )
402
            for j in range(16)
403
        ]
404
    )
405
    xy_reference = np.transpose(np.vstack([np.cos(average_hues), np.sin(average_hues)]))
1✔
406

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

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

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

439
    def corner_label_and_text(label: str, text: str, ha: str, va: str) -> None:
1✔
440
        """Draw a label and text in specified corner."""
441

442
        x = -1.45 if ha == "left" else 1.45
1✔
443
        y = 1.45 if va == "top" else -1.45
1✔
444
        y_text = -15 if va == "top" else 15
1✔
445

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

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

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

476
    return render(**settings)
1✔
477

478

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

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

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

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

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

519
    values = as_float_array(values)
1✔
520

521
    label_orientation = validate_method(label_orientation, ("Horizontal", "Vertical"))
1✔
522

523
    _figure, axes = artist(**kwargs)
1✔
524

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

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

575
    return render(**kwargs)
1✔
576

577

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

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

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

600
    Returns
601
    -------
602
    :class:`tuple`
603
        Current figure and axes.
604

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

616
    settings: Dict[str, Any] = dict(kwargs)
1✔
617
    settings["show"] = False
1✔
618

619
    _figure, axes = plot_16_bin_bars(
1✔
620
        specification.R_cs, "{0:.0f}%", x_ticker, **settings
621
    )
622

623
    axes.set_ylim(-40, 40)
1✔
624
    axes.set_ylabel("Local Chroma Shift ($R_{cs,hj}$)")
1✔
625

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

630
    settings = {"show": True}
1✔
631
    settings.update(kwargs)
1✔
632

633
    return render(**settings)
1✔
634

635

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

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

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

658
    Returns
659
    -------
660
    :class:`tuple`
661
        Current figure and axes.
662

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

674
    settings: Dict[str, Any] = dict(kwargs)
1✔
675
    settings["show"] = False
1✔
676

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

684
    settings = {"show": True}
1✔
685
    settings.update(kwargs)
1✔
686

687
    return render(**settings)
1✔
688

689

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

699
    Parameters
700
    ----------
701
    specification
702
        *ANSI/IES TM-30-18 Colour Rendition Report* specification.
703
    x_ticker
704
        Whether to display the *X* axis ticker and its associated label.
705

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

712
    Returns
713
    -------
714
    :class:`tuple`
715
        Current figure and axes.
716

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

728
    settings: Dict[str, Any] = dict(kwargs)
1✔
729
    settings["show"] = False
1✔
730

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

738
    settings = {"show": True}
1✔
739
    settings.update(kwargs)
1✔
740

741
    return render(**settings)
1✔
742

743

744
def plot_colour_fidelity_indexes(
×
745
    specification: ColourQuality_Specification_ANSIIESTM3018, **kwargs: Any
746
) -> Tuple[Figure, Axes]:
747
    """
748
    Plot colour fidelity indexes using *ANSI/IES TM-30-18 Colour Rendition
749
    Report*.
750

751
    Parameters
752
    ----------
753
    specification
754
        *ANSI/IES TM-30-18 Colour Rendition Report* specification.
755

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

762
    Returns
763
    -------
764
    :class:`tuple`
765
        Current figure and axes.
766

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

778
    _figure, axes = artist(**kwargs)
1✔
779

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

795
    ticks = list(range(1, bar_count + 1, 1))
1✔
796
    axes.set_xticks(ticks)
1✔
797

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

801
    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