Coverage for colour/plotting/tm3018/components.py: 100%

146 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-15 19:01 +1300

1""" 

2ANSI/IES TM-30-18 Colour Rendition Report Components 

3==================================================== 

4 

5Define the *ANSI/IES TM-30-18 Colour Rendition Report* components plotting 

6objects 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 

17from __future__ import annotations 

18 

19import os 

20import typing 

21 

22import numpy as np 

23 

24if typing.TYPE_CHECKING: 

25 from matplotlib.axes import Axes 

26 from matplotlib.figure import Figure 

27 

28from matplotlib.patches import Circle 

29 

30from colour.algebra import sdiv, sdiv_mode 

31from colour.colorimetry import sd_to_XYZ 

32 

33if typing.TYPE_CHECKING: 

34 from colour.hints import Any, ArrayLike, Dict, Literal, Tuple 

35 from colour.quality import ColourQuality_Specification_ANSIIESTM3018 

36 

37from colour.io import read_image 

38from colour.plotting import ( 

39 CONSTANTS_COLOUR_STYLE, 

40 artist, 

41 override_style, 

42 plot_image, 

43 render, 

44) 

45from 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 

65ROOT_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() 

210def 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) 

245 

246 _figure, axes = artist(**settings) 

247 

248 Y_reference = sd_to_XYZ(specification.sd_reference)[1] 

249 Y_test = sd_to_XYZ(specification.sd_test)[1] 

250 

251 with sdiv_mode(): 

252 reference_values = sdiv(specification.sd_reference.values, Y_reference) 

253 test_values = sdiv(specification.sd_test.values, Y_test) 

254 

255 axes.plot( 

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( 

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) 

270 axes.set_yticklabels([]) 

271 

272 settings = { 

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) 

280 

281 return render(**settings) 

282 

283 

284def 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) 

319 settings["show"] = False 

320 

321 # Background 

322 background_image = read_image( 

323 os.path.join(ROOT_RESOURCES_ANSIIESTM3018, "CVG_Background.jpg") 

324 ) 

325 _figure, axes = plot_image( 

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") 

333 for i in range(16): 

334 angle = 2 * np.pi * i / 16 

335 dx = np.cos(angle) 

336 dy = np.sin(angle) 

337 axes.plot( 

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 

347 axes.annotate( 

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( 

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) 

368 for radius in [0.8, 0.9, 1.1, 1.2]: 

369 circle = Circle( 

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) 

378 

379 # -/+20% marks near the white circles. 

380 props = {"ha": "right", "color": "white", "size": 7} 

381 axes.annotate( 

382 "-20%", 

383 xy=(0, -0.8), 

384 va="bottom", 

385 zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_annotation, 

386 **props, 

387 ) 

388 axes.annotate( 

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( 

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)])) 

406 

407 # Arrow offsets as defined by the standard. 

408 offsets = ( 

409 specification.averages_test - specification.averages_reference 

410 ) / specification.average_norms[:, None] 

411 xy_test = xy_reference + offsets 

412 

413 # Arrows. 

414 for i in range(16): 

415 axes.arrow( 

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) 

430 axes.plot( 

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: 

440 """Draw a label and text in specified corner.""" 

441 

442 x = -1.45 if ha == "left" else 1.45 

443 y = 1.45 if va == "top" else -1.45 

444 y_text = -15 if va == "top" else 15 

445 

446 axes.annotate( 

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( 

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") 

469 corner_label_and_text("$R_g$", f"{specification.R_g:.0f}", "right", "top") 

470 corner_label_and_text("CCT", f"{specification.CCT:.0f} K", "left", "bottom") 

471 corner_label_and_text("$D_{uv}$", f"{specification.D_uv:.4f}", "right", "bottom") 

472 

473 settings = {"show": True} 

474 settings.update(kwargs) 

475 

476 return render(**settings) 

477 

478 

479def 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) 

520 

521 label_orientation = validate_method(label_orientation, ("Horizontal", "Vertical")) 

522 

523 _figure, axes = artist(**kwargs) 

524 

525 bar_count = len(_COLOURS_BIN_BAR) 

526 axes.bar( 

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) 

536 if x_ticker: 

537 axes.set_xticks(np.arange(1, bar_count + 1)) 

538 axes.set_xlabel("Hue-Angle Bin (j)") 

539 else: 

540 axes.set_xticks([]) 

541 

542 label_orientation = label_orientation.lower() 

543 value_max = np.max(values) 

544 for i, value in enumerate(values): 

545 if label_orientation == "vertical": 

546 va, vo = ( 

547 ("bottom", value_max * 0.15) 

548 if value > 0 

549 else ("top", -value_max * 0.15) 

550 ) 

551 axes.annotate( 

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": 

561 va, vo = ( 

562 ("bottom", value_max * 0.025) 

563 if value < 90 

564 else ("top", -value_max * 0.025) 

565 ) 

566 axes.annotate( 

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) 

576 

577 

578def 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) 

617 settings["show"] = False 

618 

619 _figure, axes = plot_16_bin_bars( 

620 specification.R_cs, "{0:.0f}%", x_ticker, **settings 

621 ) 

622 

623 axes.set_ylim(-40, 40) 

624 axes.set_ylabel("Local Chroma Shift ($R_{cs,hj}$)") 

625 

626 ticks = np.arange(-40, 41, 10) 

627 axes.set_yticks(ticks) 

628 axes.set_yticklabels([f"{value}%" for value in ticks]) 

629 

630 settings = {"show": True} 

631 settings.update(kwargs) 

632 

633 return render(**settings) 

634 

635 

636def 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) 

675 settings["show"] = False 

676 

677 _figure, axes = plot_16_bin_bars( 

678 specification.R_hs, "{0:.2f}", x_ticker, **settings 

679 ) 

680 axes.set_ylim(-0.5, 0.5) 

681 axes.set_yticks(np.arange(-0.5, 0.51, 0.1)) 

682 axes.set_ylabel("Local Hue Shift ($R_{hs,hj}$)") 

683 

684 settings = {"show": True} 

685 settings.update(kwargs) 

686 

687 return render(**settings) 

688 

689 

690def 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) 

729 settings["show"] = False 

730 

731 _figure, axes = plot_16_bin_bars( 

732 specification.R_fs, "{0:.0f}", x_ticker, "Horizontal", **settings 

733 ) 

734 axes.set_ylim(0, 100) 

735 axes.set_yticks(np.arange(0, 101, 10)) 

736 axes.set_ylabel("Local Color Fidelity ($R_{f,hj}$)") 

737 

738 settings = {"show": True} 

739 settings.update(kwargs) 

740 

741 return render(**settings) 

742 

743 

744def 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) 

779 

780 bar_count = len(_COLOURS_TCS_BAR) 

781 axes.bar( 

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) 

791 axes.set_ylim(0, 100) 

792 axes.set_yticks(np.arange(0, 110, 10)) 

793 axes.set_ylabel("Color Sample Fidelity ($R_{f,CESi}$)") 

794 

795 ticks = list(range(1, bar_count + 1, 1)) 

796 axes.set_xticks(ticks) 

797 

798 labels = [f"CES{i:02d}" if i % 3 == 1 else "" for i in range(1, bar_count + 1)] 

799 axes.set_xticklabels(labels, rotation=90) 

800 

801 return render(**kwargs)