Coverage for plotting/diagrams.py: 79%

217 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-16 22:49 +1300

1""" 

2CIE Chromaticity Diagrams Plotting 

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

4 

5Define the *CIE* chromaticity diagrams plotting objects. 

6 

7- :func:`colour.plotting.lines_spectral_locus` 

8- :func:`colour.plotting.plot_chromaticity_diagram_CIE1931` 

9- :func:`colour.plotting.plot_chromaticity_diagram_CIE1960UCS` 

10- :func:`colour.plotting.plot_chromaticity_diagram_CIE1976UCS` 

11- :func:`colour.plotting.plot_sds_in_chromaticity_diagram_CIE1931` 

12- :func:`colour.plotting.plot_sds_in_chromaticity_diagram_CIE1960UCS` 

13- :func:`colour.plotting.plot_sds_in_chromaticity_diagram_CIE1976UCS` 

14""" 

15 

16from __future__ import annotations 

17 

18import bisect 

19import typing 

20 

21import numpy as np 

22 

23if typing.TYPE_CHECKING: 

24 from collections.abc import ValuesView 

25 

26if typing.TYPE_CHECKING: 

27 from matplotlib.axes import Axes 

28 

29from matplotlib.collections import LineCollection 

30 

31if typing.TYPE_CHECKING: 

32 from matplotlib.figure import Figure 

33 

34from matplotlib.patches import Polygon 

35 

36from colour.algebra import normalise_maximum, normalise_vector 

37from colour.colorimetry import ( 

38 SDS_ILLUMINANTS, 

39 MultiSpectralDistributions, 

40 SpectralDistribution, 

41 sd_to_XYZ, 

42 sds_and_msds_to_sds, 

43) 

44from colour.constants import DTYPE_FLOAT_DEFAULT 

45 

46if typing.TYPE_CHECKING: 

47 from colour.hints import ( 

48 Any, 

49 ArrayLike, 

50 Callable, 

51 Dict, 

52 List, 

53 Literal, 

54 NDArray, 

55 Sequence, 

56 ) 

57 

58from colour.hints import Tuple, cast 

59from colour.models import ( 

60 Luv_to_uv, 

61 Luv_uv_to_xy, 

62 UCS_to_uv, 

63 UCS_uv_to_xy, 

64 XYZ_to_Luv, 

65 XYZ_to_UCS, 

66 XYZ_to_xy, 

67 xy_to_Luv_uv, 

68 xy_to_UCS_uv, 

69 xy_to_XYZ, 

70) 

71from colour.notation import HEX_to_RGB 

72from colour.plotting import ( 

73 CONSTANTS_ARROW_STYLE, 

74 CONSTANTS_COLOUR_STYLE, 

75 XYZ_to_plotting_colourspace, 

76 artist, 

77 filter_cmfs, 

78 filter_illuminants, 

79 override_style, 

80 render, 

81 update_settings_collection, 

82) 

83from colour.utilities import ( 

84 CanonicalMapping, 

85 as_float_array, 

86 domain_range_scale, 

87 first_item, 

88 optional, 

89 tsplit, 

90 tstack, 

91 validate_method, 

92 zeros, 

93) 

94 

95__author__ = "Colour Developers" 

96__copyright__ = "Copyright 2013 Colour Developers" 

97__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause" 

98__maintainer__ = "Colour Developers" 

99__email__ = "colour-developers@colour-science.org" 

100__status__ = "Production" 

101 

102__all__ = [ 

103 "METHODS_CHROMATICITY_DIAGRAM", 

104 "LABELS_CHROMATICITY_DIAGRAM_DEFAULT", 

105 "lines_spectral_locus", 

106 "plot_spectral_locus", 

107 "plot_chromaticity_diagram_colours", 

108 "plot_chromaticity_diagram", 

109 "plot_chromaticity_diagram_CIE1931", 

110 "plot_chromaticity_diagram_CIE1960UCS", 

111 "plot_chromaticity_diagram_CIE1976UCS", 

112 "plot_sds_in_chromaticity_diagram", 

113 "plot_sds_in_chromaticity_diagram_CIE1931", 

114 "plot_sds_in_chromaticity_diagram_CIE1960UCS", 

115 "plot_sds_in_chromaticity_diagram_CIE1976UCS", 

116] 

117 

118METHODS_CHROMATICITY_DIAGRAM: CanonicalMapping = CanonicalMapping( 

119 { 

120 "CIE 1931": { 

121 "XYZ_to_ij": lambda a, *args: XYZ_to_xy(a), # noqa: ARG005 

122 "ij_to_XYZ": lambda a, *args: xy_to_XYZ(a), # noqa: ARG005 

123 "xy_to_ij": lambda a, *args: a, # noqa: ARG005 

124 "uv_to_ij": lambda a, *args: UCS_uv_to_xy(a), # noqa: ARG005 

125 }, 

126 "CIE 1960 UCS": { 

127 "XYZ_to_ij": lambda a, *args: UCS_to_uv( # noqa: ARG005 

128 XYZ_to_UCS(a) 

129 ), 

130 "ij_to_XYZ": lambda a, *args: xy_to_XYZ( # noqa: ARG005 

131 UCS_uv_to_xy(a) 

132 ), 

133 "xy_to_ij": lambda a, *args: xy_to_UCS_uv(a), # noqa: ARG005 

134 "uv_to_ij": lambda a, *args: a, # noqa: ARG005 

135 }, 

136 "CIE 1976 UCS": { 

137 "XYZ_to_ij": lambda a, *args: Luv_to_uv(XYZ_to_Luv(a, *args), *args), 

138 "ij_to_XYZ": lambda a, *args: xy_to_XYZ( # noqa: ARG005 

139 Luv_uv_to_xy(a) 

140 ), 

141 "xy_to_ij": lambda a, *args: xy_to_Luv_uv(a), # noqa: ARG005 

142 "uv_to_ij": lambda a, *args: xy_to_Luv_uv( # noqa: ARG005 

143 UCS_uv_to_xy(a) 

144 ), 

145 }, 

146 } 

147) 

148"""Chromaticity diagram conversion methods.""" 

149 

150LABELS_CHROMATICITY_DIAGRAM_DEFAULT: CanonicalMapping = CanonicalMapping( 

151 { 

152 "CIE 1931": ( 

153 390, 

154 460, 

155 470, 

156 480, 

157 490, 

158 500, 

159 510, 

160 520, 

161 540, 

162 560, 

163 580, 

164 600, 

165 620, 

166 700, 

167 ), 

168 "CIE 1960 UCS": ( 

169 420, 

170 440, 

171 450, 

172 460, 

173 470, 

174 480, 

175 490, 

176 500, 

177 510, 

178 520, 

179 530, 

180 540, 

181 550, 

182 560, 

183 570, 

184 580, 

185 590, 

186 600, 

187 610, 

188 620, 

189 630, 

190 645, 

191 680, 

192 ), 

193 "CIE 1976 UCS": ( 

194 420, 

195 440, 

196 450, 

197 460, 

198 470, 

199 480, 

200 490, 

201 500, 

202 510, 

203 520, 

204 530, 

205 540, 

206 550, 

207 560, 

208 570, 

209 580, 

210 590, 

211 600, 

212 610, 

213 620, 

214 630, 

215 645, 

216 680, 

217 ), 

218 } 

219) 

220"""*Chromaticity Diagram* default labels.""" 

221 

222 

223def lines_spectral_locus( 

224 cmfs: ( 

225 MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] 

226 ) = "CIE 1931 2 Degree Standard Observer", 

227 labels: Sequence | None = None, 

228 method: (Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] | str) = "CIE 1931", 

229) -> Tuple[NDArray, NDArray]: 

230 """ 

231 Return the *Spectral Locus* line vertices, i.e., positions, normals 

232 and colours, using the specified method. 

233 

234 Parameters 

235 ---------- 

236 cmfs 

237 Standard observer colour matching functions used for computing the 

238 spectral locus boundaries. ``cmfs`` can be of any type or form 

239 supported by the :func:`colour.plotting.common.filter_cmfs` 

240 definition. 

241 labels 

242 Array of wavelength labels used to customise which labels will be 

243 drawn around the spectral locus. Passing an empty array will 

244 result in no wavelength labels being drawn. 

245 method 

246 *Chromaticity Diagram* method. 

247 

248 Returns 

249 ------- 

250 :class:`tuple` 

251 Tuple of *Spectral Locus* vertices and wavelength labels vertices. 

252 

253 Examples 

254 -------- 

255 >>> lines = lines_spectral_locus() 

256 >>> len(lines) 

257 2 

258 >>> lines[0].dtype 

259 dtype([('position', '<f8', (2,)), ('normal', '<f8', (2,)), \ 

260('colour', '<f8', (3,))]) 

261 >>> lines[1].dtype 

262 dtype([('position', '<f8', (2,)), ('normal', '<f8', (2,)), \ 

263('colour', '<f8', (3,))]) 

264 """ 

265 

266 cmfs = cast("MultiSpectralDistributions", first_item(filter_cmfs(cmfs).values())) 

267 

268 labels = optional(labels, LABELS_CHROMATICITY_DIAGRAM_DEFAULT[method]) 

269 

270 method = validate_method(method, tuple(METHODS_CHROMATICITY_DIAGRAM)) 

271 

272 illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint 

273 

274 wavelengths = list(cmfs.wavelengths) 

275 equal_energy = np.array([1 / 3] * 2) 

276 

277 XYZ_to_ij = METHODS_CHROMATICITY_DIAGRAM[method]["XYZ_to_ij"] 

278 ij_to_XYZ = METHODS_CHROMATICITY_DIAGRAM[method]["ij_to_XYZ"] 

279 

280 # CMFS 

281 ij_cmfs = XYZ_to_ij(cmfs.values, illuminant) 

282 

283 # Line of Purples 

284 ij_pl = tstack( 

285 [ 

286 np.linspace(ij_cmfs[-1][0], ij_cmfs[0][0], 20), 

287 np.linspace(ij_cmfs[-1][1], ij_cmfs[0][1], 20), 

288 ] 

289 ) 

290 

291 ij_sl = np.vstack([ij_cmfs, ij_pl]) 

292 

293 colour_sl = normalise_maximum( 

294 XYZ_to_plotting_colourspace(np.reshape(ij_to_XYZ(ij_sl, illuminant), (-1, 3))), 

295 axis=-1, 

296 ) 

297 

298 lines_sl = zeros( 

299 ij_sl.shape[0], 

300 [ 

301 ("position", DTYPE_FLOAT_DEFAULT, 2), 

302 ("normal", DTYPE_FLOAT_DEFAULT, 2), 

303 ("colour", DTYPE_FLOAT_DEFAULT, 3), 

304 ], # pyright: ignore 

305 ) 

306 

307 lines_sl["position"] = ij_sl 

308 lines_sl["colour"] = colour_sl 

309 

310 # Labels Normals 

311 ij_n, colour_l, normal_l = [], [], [] 

312 wl_ij_cmfs = dict(zip(wavelengths, ij_cmfs, strict=True)) 

313 for label in cast("Tuple", labels): 

314 ij_l = wl_ij_cmfs.get(label) 

315 

316 if ij_l is None: 

317 continue 

318 

319 i, j = tsplit(ij_l) 

320 

321 index = bisect.bisect(wavelengths, label) 

322 left = wavelengths[index - 1] if index >= 0 else wavelengths[index] 

323 right = wavelengths[index] if index < len(wavelengths) else wavelengths[-1] 

324 

325 dx = wl_ij_cmfs[right][0] - wl_ij_cmfs[left][0] 

326 dy = wl_ij_cmfs[right][1] - wl_ij_cmfs[left][1] 

327 

328 direction = np.array([-dy, dx]) 

329 

330 normal = ( 

331 np.array([-dy, dx]) 

332 if np.dot( 

333 normalise_vector(ij_l - equal_energy), 

334 normalise_vector(direction), 

335 ) 

336 > 0 

337 else np.array([dy, -dx]) 

338 ) 

339 normal = normalise_vector(normal) 

340 

341 ij_n.append([[i, j], [i + normal[0] / 50, j + normal[1] / 50]]) 

342 normal_l.append([normal, normal]) 

343 colour_l.append([ij_l, ij_l]) 

344 

345 ij_w = np.reshape(as_float_array(ij_n), (-1, 2)) 

346 normal_w = np.reshape(as_float_array(normal_l), (-1, 2)) 

347 colours_w = np.reshape(as_float_array(colour_l), (-1, 2)) 

348 

349 colour_w = normalise_maximum( 

350 XYZ_to_plotting_colourspace( 

351 np.reshape(ij_to_XYZ(colours_w, illuminant), (-1, 3)) 

352 ), 

353 axis=-1, 

354 ) 

355 

356 lines_w = zeros( 

357 ij_w.shape[0], 

358 [ 

359 ("position", DTYPE_FLOAT_DEFAULT, 2), 

360 ("normal", DTYPE_FLOAT_DEFAULT, 2), 

361 ("colour", DTYPE_FLOAT_DEFAULT, 3), 

362 ], # pyright: ignore 

363 ) 

364 

365 lines_w["position"] = ij_w 

366 lines_w["normal"] = normal_w 

367 lines_w["colour"] = colour_w 

368 

369 return lines_sl, lines_w 

370 

371 

372@override_style() 

373def plot_spectral_locus( 

374 cmfs: ( 

375 MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] 

376 ) = "CIE 1931 2 Degree Standard Observer", 

377 spectral_locus_colours: ArrayLike | str | None = None, 

378 spectral_locus_opacity: float = 1, 

379 spectral_locus_labels: Sequence | None = None, 

380 method: (Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] | str) = "CIE 1931", 

381 **kwargs: Any, 

382) -> Tuple[Figure, Axes]: 

383 """ 

384 Plot the *Spectral Locus* using the specified method. 

385 

386 Parameters 

387 ---------- 

388 cmfs 

389 Standard observer colour matching functions used for computing 

390 the spectral locus boundaries. ``cmfs`` can be of any type or 

391 form supported by the :func:`colour.plotting.common.filter_cmfs` 

392 definition. 

393 spectral_locus_colours 

394 Colours of the *Spectral Locus*, if ``spectral_locus_colours`` 

395 is set to *RGB*, the colours will be computed using the 

396 corresponding chromaticity coordinates. 

397 spectral_locus_opacity 

398 Opacity of the *Spectral Locus*. 

399 spectral_locus_labels 

400 Array of wavelength labels used to customise which labels will 

401 be drawn around the spectral locus. Passing an empty array will 

402 result in no wavelength labels being drawn. 

403 method 

404 *Chromaticity Diagram* method. 

405 

406 Other Parameters 

407 ---------------- 

408 kwargs 

409 {:func:`colour.plotting.artist`, 

410 :func:`colour.plotting.render`}, 

411 See the documentation of the previously listed definitions. 

412 

413 Returns 

414 ------- 

415 :class:`tuple` 

416 Current figure and axes. 

417 

418 Examples 

419 -------- 

420 >>> plot_spectral_locus(spectral_locus_colours="RGB") # doctest: +ELLIPSIS 

421 (<Figure size ... with 1 Axes>, <...Axes...>) 

422 

423 .. image:: ../_static/Plotting_Plot_Spectral_Locus.png 

424 :align: center 

425 :alt: plot_spectral_locus 

426 """ 

427 

428 method = validate_method(method, tuple(METHODS_CHROMATICITY_DIAGRAM)) 

429 

430 spectral_locus_colours = optional( 

431 spectral_locus_colours, CONSTANTS_COLOUR_STYLE.colour.dark 

432 ) 

433 

434 labels = optional( 

435 spectral_locus_labels, LABELS_CHROMATICITY_DIAGRAM_DEFAULT[method] 

436 ) 

437 

438 use_RGB_colours = str(spectral_locus_colours).upper() == "RGB" 

439 

440 settings: Dict[str, Any] = {"uniform": True} 

441 settings.update(kwargs) 

442 

443 _figure, axes = artist(**settings) 

444 

445 cmfs = cast("MultiSpectralDistributions", first_item(filter_cmfs(cmfs).values())) 

446 

447 lines_sl, lines_w = lines_spectral_locus(cmfs, spectral_locus_labels, method) 

448 

449 axes.add_collection( 

450 LineCollection( 

451 np.reshape( 

452 np.concatenate( 

453 [lines_sl["position"][:-1], lines_sl["position"][1:]], 

454 axis=1, # pyright: ignore 

455 ), 

456 (-1, 2, 2), 

457 ), 

458 colors=(lines_sl["colour"] if use_RGB_colours else spectral_locus_colours), 

459 alpha=spectral_locus_opacity, 

460 zorder=CONSTANTS_COLOUR_STYLE.zorder.background_line, 

461 ) 

462 ) 

463 axes.add_collection( 

464 LineCollection( 

465 np.reshape(lines_w["position"], (-1, 2, 2)), # pyright: ignore 

466 colors=( 

467 lines_w["colour"][::2] if use_RGB_colours else spectral_locus_colours 

468 ), 

469 alpha=spectral_locus_opacity, 

470 zorder=CONSTANTS_COLOUR_STYLE.zorder.background_line, 

471 ) 

472 ) 

473 

474 for i, label in enumerate([label for label in labels if label in cmfs.wavelengths]): 

475 positions = lines_w["position"][::2] 

476 normals = lines_w["normal"][::2] 

477 colours = lines_w["colour"][::2] 

478 

479 axes.plot( 

480 positions[i, 0], 

481 positions[i, 1], 

482 "o", 

483 color=colours[i] if use_RGB_colours else spectral_locus_colours, 

484 alpha=spectral_locus_opacity, 

485 zorder=CONSTANTS_COLOUR_STYLE.zorder.background_line, 

486 ) 

487 

488 axes.text( 

489 positions[i, 0] + normals[i, 0] / 50 * 1.25, 

490 positions[i, 1] + normals[i, 1] / 50 * 1.25, 

491 label, 

492 clip_on=True, 

493 ha="left" if lines_w["normal"][::2][i, 0] >= 0 else "right", 

494 va="center", 

495 fontsize="x-small-colour-science", 

496 zorder=CONSTANTS_COLOUR_STYLE.zorder.background_label, 

497 ) 

498 

499 settings = {"axes": axes} 

500 settings.update(kwargs) 

501 

502 return render(**kwargs) 

503 

504 

505@override_style() 

506def plot_chromaticity_diagram_colours( 

507 samples: int = 256, 

508 diagram_colours: ArrayLike | str | None = None, 

509 diagram_opacity: float = 1, 

510 diagram_clipping_path: ArrayLike | None = None, 

511 cmfs: ( 

512 MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] 

513 ) = "CIE 1931 2 Degree Standard Observer", 

514 method: (Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] | str) = "CIE 1931", 

515 **kwargs: Any, 

516) -> Tuple[Figure, Axes]: 

517 """ 

518 Plot the *Chromaticity Diagram* colours using the specified method. 

519 

520 Parameters 

521 ---------- 

522 samples 

523 Sample count on one axis when computing the *Chromaticity 

524 Diagram* colours. 

525 diagram_colours 

526 Colours of the *Chromaticity Diagram*, if ``diagram_colours`` 

527 is set to *RGB*, the colours will be computed using the 

528 corresponding coordinates. 

529 diagram_opacity 

530 Opacity of the *Chromaticity Diagram*. 

531 diagram_clipping_path 

532 Path of points used to clip the *Chromaticity Diagram* colours. 

533 cmfs 

534 Standard observer colour matching functions used for computing the 

535 spectral locus boundaries. ``cmfs`` can be of any type or form 

536 supported by the :func:`colour.plotting.common.filter_cmfs` 

537 definition. 

538 method 

539 *Chromaticity Diagram* method. 

540 

541 Other Parameters 

542 ---------------- 

543 kwargs 

544 {:func:`colour.plotting.artist`, 

545 :func:`colour.plotting.render`}, 

546 See the documentation of the previously listed definitions. 

547 

548 Returns 

549 ------- 

550 :class:`tuple` 

551 Current figure and axes. 

552 

553 Examples 

554 -------- 

555 >>> plot_chromaticity_diagram_colours(diagram_colours="RGB") 

556 ... # doctest: +ELLIPSIS 

557 (<Figure size ... with 1 Axes>, <...Axes...>) 

558 

559 .. image:: ../_static/Plotting_Plot_Chromaticity_Diagram_Colours.png 

560 :align: center 

561 :alt: plot_chromaticity_diagram_colours 

562 """ 

563 

564 method = validate_method(method, ("CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS")) 

565 

566 settings: Dict[str, Any] = {"uniform": True} 

567 settings.update(kwargs) 

568 

569 _figure, axes = artist(**settings) 

570 

571 diagram_colours = optional( 

572 diagram_colours, HEX_to_RGB(CONSTANTS_COLOUR_STYLE.colour.average) 

573 ) 

574 

575 cmfs = cast("MultiSpectralDistributions", first_item(filter_cmfs(cmfs).values())) 

576 

577 illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint 

578 

579 XYZ_to_ij = METHODS_CHROMATICITY_DIAGRAM[method]["XYZ_to_ij"] 

580 

581 spectral_locus = XYZ_to_ij(cmfs.values, illuminant) 

582 

583 use_RGB_diagram_colours = str(diagram_colours).upper() == "RGB" 

584 if use_RGB_diagram_colours: 

585 ii, jj = np.meshgrid(np.linspace(0, 1, samples), np.linspace(1, 0, samples)) 

586 ij = tstack([ii, jj]) 

587 

588 ij_to_XYZ = METHODS_CHROMATICITY_DIAGRAM[method]["ij_to_XYZ"] 

589 

590 diagram_colours = normalise_maximum( 

591 XYZ_to_plotting_colourspace(ij_to_XYZ(ij), illuminant), axis=-1 

592 ) 

593 

594 polygon = Polygon( 

595 (spectral_locus if diagram_clipping_path is None else diagram_clipping_path), 

596 facecolor=( 

597 "none" 

598 if use_RGB_diagram_colours 

599 else np.hstack([diagram_colours, diagram_opacity]) 

600 ), 

601 edgecolor=( 

602 "none" 

603 if use_RGB_diagram_colours 

604 else np.hstack([diagram_colours, diagram_opacity]) 

605 ), 

606 zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, 

607 ) 

608 axes.add_patch(polygon) 

609 

610 if use_RGB_diagram_colours: 

611 # Preventing bounding box related issues as per 

612 # https://github.com/matplotlib/matplotlib/issues/10529 

613 image = axes.imshow( 

614 diagram_colours, 

615 interpolation="bilinear", 

616 extent=(0, 1, 0, 1), 

617 clip_path=None, 

618 alpha=diagram_opacity, 

619 zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, 

620 ) 

621 image.set_clip_path(polygon) 

622 

623 settings = {"axes": axes} 

624 settings.update(kwargs) 

625 

626 return render(**kwargs) 

627 

628 

629@override_style() 

630def plot_chromaticity_diagram( 

631 cmfs: ( 

632 MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] 

633 ) = "CIE 1931 2 Degree Standard Observer", 

634 show_diagram_colours: bool = True, 

635 show_spectral_locus: bool = True, 

636 method: (Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] | str) = "CIE 1931", 

637 **kwargs: Any, 

638) -> Tuple[Figure, Axes]: 

639 """ 

640 Plot the *Chromaticity Diagram* using the specified method. 

641 

642 Parameters 

643 ---------- 

644 cmfs 

645 Standard observer colour matching functions used for computing the 

646 spectral locus boundaries. ``cmfs`` can be of any type or form 

647 supported by the :func:`colour.plotting.common.filter_cmfs` 

648 definition. 

649 show_diagram_colours 

650 Whether to display the *Chromaticity Diagram* background colours. 

651 show_spectral_locus 

652 Whether to display the *Spectral Locus*. 

653 method 

654 *Chromaticity Diagram* method. 

655 

656 Other Parameters 

657 ---------------- 

658 kwargs 

659 {:func:`colour.plotting.artist`, 

660 :func:`colour.plotting.diagrams.plot_spectral_locus`, 

661 :func:`colour.plotting.diagrams.plot_chromaticity_diagram_colours`, 

662 :func:`colour.plotting.render`}, 

663 See the documentation of the previously listed definitions. 

664 

665 Returns 

666 ------- 

667 :class:`tuple` 

668 Current figure and axes. 

669 

670 Examples 

671 -------- 

672 >>> plot_chromaticity_diagram() # doctest: +ELLIPSIS 

673 (<Figure size ... with 1 Axes>, <...Axes...>) 

674 

675 .. image:: ../_static/Plotting_Plot_Chromaticity_Diagram.png 

676 :align: center 

677 :alt: plot_chromaticity_diagram 

678 """ 

679 

680 method = validate_method(method, ("CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS")) 

681 

682 settings: Dict[str, Any] = {"uniform": True} 

683 settings.update(kwargs) 

684 

685 _figure, axes = artist(**settings) 

686 

687 cmfs = cast("MultiSpectralDistributions", first_item(filter_cmfs(cmfs).values())) 

688 

689 if show_diagram_colours: 

690 settings = {"axes": axes, "method": method, "diagram_colours": "RGB"} 

691 settings.update(kwargs) 

692 settings["show"] = False 

693 settings["cmfs"] = cmfs 

694 

695 plot_chromaticity_diagram_colours(**settings) 

696 

697 if show_spectral_locus: 

698 settings = {"axes": axes, "method": method} 

699 settings.update(kwargs) 

700 settings["show"] = False 

701 settings["cmfs"] = cmfs 

702 

703 plot_spectral_locus(**settings) 

704 

705 if method == "cie 1931": 

706 x_label, y_label = "CIE x", "CIE y" 

707 

708 elif method == "cie 1960 ucs": 

709 x_label, y_label = "CIE u", "CIE v" 

710 

711 elif method == "cie 1976 ucs": 

712 x_label, y_label = ( 

713 "CIE u'", 

714 "CIE v'", 

715 ) 

716 

717 title = f"{method.upper()} Chromaticity Diagram - {cmfs.display_name}" 

718 

719 settings.update( 

720 { 

721 "axes": axes, 

722 "show": True, 

723 "bounding_box": (0, 1, 0, 1), 

724 "title": title, 

725 "x_label": x_label, 

726 "y_label": y_label, 

727 } 

728 ) 

729 settings.update(kwargs) 

730 

731 return render(**settings) 

732 

733 

734@override_style() 

735def plot_chromaticity_diagram_CIE1931( 

736 cmfs: ( 

737 MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] 

738 ) = "CIE 1931 2 Degree Standard Observer", 

739 show_diagram_colours: bool = True, 

740 show_spectral_locus: bool = True, 

741 **kwargs: Any, 

742) -> Tuple[Figure, Axes]: 

743 """ 

744 Plot the *CIE 1931 Chromaticity Diagram*. 

745 

746 Parameters 

747 ---------- 

748 cmfs 

749 Standard observer colour matching functions used for computing the 

750 spectral locus boundaries. ``cmfs`` can be of any type or form 

751 supported by the :func:`colour.plotting.common.filter_cmfs` 

752 definition. 

753 show_diagram_colours 

754 Whether to display the *Chromaticity Diagram* background colours. 

755 show_spectral_locus 

756 Whether to display the *Spectral Locus*. 

757 

758 Other Parameters 

759 ---------------- 

760 kwargs 

761 {:func:`colour.plotting.artist`, 

762 :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, 

763 :func:`colour.plotting.render`}, 

764 See the documentation of the previously listed definitions. 

765 

766 Returns 

767 ------- 

768 :class:`tuple` 

769 Current figure and axes. 

770 

771 Examples 

772 -------- 

773 >>> plot_chromaticity_diagram_CIE1931() # doctest: +ELLIPSIS 

774 (<Figure size ... with 1 Axes>, <...Axes...>) 

775 

776 .. image:: ../_static/Plotting_Plot_Chromaticity_Diagram_CIE1931.png 

777 :align: center 

778 :alt: plot_chromaticity_diagram_CIE1931 

779 """ 

780 

781 settings = dict(kwargs) 

782 settings.update({"method": "CIE 1931"}) 

783 

784 return plot_chromaticity_diagram( 

785 cmfs, show_diagram_colours, show_spectral_locus, **settings 

786 ) 

787 

788 

789@override_style() 

790def plot_chromaticity_diagram_CIE1960UCS( 

791 cmfs: ( 

792 MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] 

793 ) = "CIE 1931 2 Degree Standard Observer", 

794 show_diagram_colours: bool = True, 

795 show_spectral_locus: bool = True, 

796 **kwargs: Any, 

797) -> Tuple[Figure, Axes]: 

798 """ 

799 Plot the *CIE 1960 UCS Chromaticity Diagram*. 

800 

801 Parameters 

802 ---------- 

803 cmfs 

804 Standard observer colour matching functions used for computing the 

805 spectral locus boundaries. ``cmfs`` can be of any type or form 

806 supported by the :func:`colour.plotting.common.filter_cmfs` 

807 definition. 

808 show_diagram_colours 

809 Whether to display the *Chromaticity Diagram* background colours. 

810 show_spectral_locus 

811 Whether to display the *Spectral Locus*. 

812 

813 Other Parameters 

814 ---------------- 

815 kwargs 

816 {:func:`colour.plotting.artist`, 

817 :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, 

818 :func:`colour.plotting.render`}, 

819 See the documentation of the previously listed definitions. 

820 

821 Returns 

822 ------- 

823 :class:`tuple` 

824 Current figure and axes. 

825 

826 Examples 

827 -------- 

828 >>> plot_chromaticity_diagram_CIE1960UCS() # doctest: +ELLIPSIS 

829 (<Figure size ... with 1 Axes>, <...Axes...>) 

830 

831 .. image:: ../_static/Plotting_Plot_Chromaticity_Diagram_CIE1960UCS.png 

832 :align: center 

833 :alt: plot_chromaticity_diagram_CIE1960UCS 

834 """ 

835 

836 settings = dict(kwargs) 

837 settings.update({"method": "CIE 1960 UCS"}) 

838 

839 return plot_chromaticity_diagram( 

840 cmfs, show_diagram_colours, show_spectral_locus, **settings 

841 ) 

842 

843 

844@override_style() 

845def plot_chromaticity_diagram_CIE1976UCS( 

846 cmfs: ( 

847 MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] 

848 ) = "CIE 1931 2 Degree Standard Observer", 

849 show_diagram_colours: bool = True, 

850 show_spectral_locus: bool = True, 

851 **kwargs: Any, 

852) -> Tuple[Figure, Axes]: 

853 """ 

854 Plot the *CIE 1976 UCS Chromaticity Diagram*. 

855 

856 Parameters 

857 ---------- 

858 cmfs 

859 Standard observer colour matching functions used for computing the 

860 spectral locus boundaries. ``cmfs`` can be of any type or form 

861 supported by the :func:`colour.plotting.common.filter_cmfs` 

862 definition. 

863 show_diagram_colours 

864 Whether to display the *Chromaticity Diagram* background colours. 

865 show_spectral_locus 

866 Whether to display the *Spectral Locus*. 

867 

868 Other Parameters 

869 ---------------- 

870 kwargs 

871 {:func:`colour.plotting.artist`, 

872 :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, 

873 :func:`colour.plotting.render`}, 

874 See the documentation of the previously listed definitions. 

875 

876 Returns 

877 ------- 

878 :class:`tuple` 

879 Current figure and axes. 

880 

881 Examples 

882 -------- 

883 >>> plot_chromaticity_diagram_CIE1976UCS() # doctest: +ELLIPSIS 

884 (<Figure size ... with 1 Axes>, <...Axes...>) 

885 

886 .. image:: ../_static/Plotting_Plot_Chromaticity_Diagram_CIE1976UCS.png 

887 :align: center 

888 :alt: plot_chromaticity_diagram_CIE1976UCS 

889 """ 

890 

891 settings = dict(kwargs) 

892 settings.update({"method": "CIE 1976 UCS"}) 

893 

894 return plot_chromaticity_diagram( 

895 cmfs, show_diagram_colours, show_spectral_locus, **settings 

896 ) 

897 

898 

899@override_style() 

900def plot_sds_in_chromaticity_diagram( 

901 sds: ( 

902 Sequence[SpectralDistribution | MultiSpectralDistributions] 

903 | SpectralDistribution 

904 | MultiSpectralDistributions 

905 | ValuesView 

906 ), 

907 cmfs: ( 

908 MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] 

909 ) = "CIE 1931 2 Degree Standard Observer", 

910 chromaticity_diagram_callable: Callable = plot_chromaticity_diagram, 

911 method: (Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] | str) = "CIE 1931", 

912 annotate_kwargs: dict | List[dict] | None = None, 

913 plot_kwargs: dict | List[dict] | None = None, 

914 **kwargs: Any, 

915) -> Tuple[Figure, Axes]: 

916 """ 

917 Plot specified spectral distribution chromaticity coordinates into the 

918 *Chromaticity Diagram* using the specified method. 

919 

920 Parameters 

921 ---------- 

922 sds 

923 Spectral distributions or multi-spectral distributions to plot. 

924 ``sds`` can be a single :class:`colour.MultiSpectralDistributions` 

925 class instance, a list of :class:`colour.MultiSpectralDistributions` 

926 class instances or a list of :class:`colour.SpectralDistribution` class 

927 instances. 

928 cmfs 

929 Standard observer colour matching functions used for computing the 

930 spectral locus boundaries. ``cmfs`` can be of any type or form 

931 supported by the :func:`colour.plotting.common.filter_cmfs` 

932 definition. 

933 chromaticity_diagram_callable 

934 Callable responsible for drawing the *Chromaticity Diagram*. 

935 method 

936 *Chromaticity Diagram* method. 

937 annotate_kwargs 

938 Keyword arguments for the :func:`matplotlib.pyplot.annotate` 

939 definition, used to annotate the resulting chromaticity coordinates 

940 with their respective spectral distribution names. 

941 ``annotate_kwargs`` can be either a single dictionary applied to 

942 all the arrows with same settings or a sequence of dictionaries 

943 with different settings for each spectral distribution. The 

944 following special keyword arguments can also be used: 

945 

946 - ``annotate`` : Whether to annotate the spectral distributions. 

947 plot_kwargs 

948 Keyword arguments for the :func:`matplotlib.pyplot.plot` 

949 definition, used to control the style of the plotted spectral 

950 distributions. ``plot_kwargs`` can be either a single dictionary 

951 applied to all the plotted spectral distributions with the same 

952 settings or a sequence of dictionaries with different settings for 

953 each plotted spectral distribution. The following special keyword 

954 arguments can also be used: 

955 

956 - ``illuminant`` : The illuminant used to compute the spectral 

957 distributions colours. The default is the illuminant 

958 associated with the whitepoint of the default plotting 

959 colourspace. ``illuminant`` can be of any type or form 

960 supported by the :func:`colour.plotting.common.filter_cmfs` 

961 definition. 

962 - ``cmfs`` : The standard observer colour matching functions 

963 used for computing the spectral distributions colours. 

964 ``cmfs`` can be of any type or form supported by the 

965 :func:`colour.plotting.common.filter_cmfs` definition. 

966 - ``normalise_sd_colours`` : Whether to normalise the computed 

967 spectral distributions colours. The default is *True*. 

968 - ``use_sd_colours`` : Whether to use the computed spectral 

969 distributions colours under the plotting colourspace 

970 illuminant. Alternatively, it is possible to use the 

971 :func:`matplotlib.pyplot.plot` definition ``color`` argument 

972 with pre-computed values. The default is *True*. 

973 

974 Other Parameters 

975 ---------------- 

976 kwargs 

977 {:func:`colour.plotting.artist`, 

978 :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, 

979 :func:`colour.plotting.render`}, 

980 See the documentation of the previously listed definitions. 

981 

982 Returns 

983 ------- 

984 :class:`tuple` 

985 Current figure and axes. 

986 

987 Examples 

988 -------- 

989 >>> A = SDS_ILLUMINANTS["A"] 

990 >>> D65 = SDS_ILLUMINANTS["D65"] 

991 >>> annotate_kwargs = [ 

992 ... {"xytext": (-25, 15), "arrowprops": {"arrowstyle": "-"}}, 

993 ... {}, 

994 ... ] 

995 >>> plot_kwargs = [ 

996 ... { 

997 ... "illuminant": SDS_ILLUMINANTS["E"], 

998 ... "markersize": 15, 

999 ... "normalise_sd_colours": True, 

1000 ... "use_sd_colours": True, 

1001 ... }, 

1002 ... {"illuminant": SDS_ILLUMINANTS["E"]}, 

1003 ... ] 

1004 >>> plot_sds_in_chromaticity_diagram( 

1005 ... [A, D65], annotate_kwargs=annotate_kwargs, plot_kwargs=plot_kwargs 

1006 ... ) 

1007 ... # doctest: +ELLIPSIS 

1008 (<Figure size ... with 1 Axes>, <...Axes...>) 

1009 

1010 .. image:: ../_static/Plotting_Plot_SDS_In_Chromaticity_Diagram.png 

1011 :align: center 

1012 :alt: plot_sds_in_chromaticity_diagram 

1013 """ 

1014 

1015 method = validate_method(method, ("CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS")) 

1016 

1017 sds_converted = sds_and_msds_to_sds(sds) 

1018 

1019 settings: Dict[str, Any] = {"uniform": True} 

1020 settings.update(kwargs) 

1021 

1022 _figure, axes = artist(**settings) 

1023 

1024 settings.update( 

1025 { 

1026 "axes": axes, 

1027 "show": False, 

1028 "method": method, 

1029 "cmfs": cmfs, 

1030 } 

1031 ) 

1032 

1033 chromaticity_diagram_callable(**settings) 

1034 

1035 XYZ_to_ij = METHODS_CHROMATICITY_DIAGRAM[method]["XYZ_to_ij"] 

1036 

1037 if method == "cie 1931": 

1038 bounding_box = (-0.1, 0.9, -0.1, 0.9) 

1039 

1040 elif method == "cie 1960 ucs": 

1041 bounding_box = (-0.1, 0.7, -0.2, 0.6) 

1042 

1043 elif method == "cie 1976 ucs": 

1044 bounding_box = (-0.1, 0.7, -0.1, 0.7) 

1045 

1046 annotate_settings_collection = [ 

1047 { 

1048 "annotate": True, 

1049 "xytext": (-50, 30), 

1050 "textcoords": "offset points", 

1051 "arrowprops": CONSTANTS_ARROW_STYLE, 

1052 "zorder": CONSTANTS_COLOUR_STYLE.zorder.midground_annotation, 

1053 } 

1054 for _ in range(len(sds_converted)) 

1055 ] 

1056 

1057 if annotate_kwargs is not None: 

1058 update_settings_collection( 

1059 annotate_settings_collection, annotate_kwargs, len(sds_converted) 

1060 ) 

1061 

1062 plot_settings_collection = [ 

1063 { 

1064 "color": CONSTANTS_COLOUR_STYLE.colour.brightest, 

1065 "label": f"{sd.display_name}", 

1066 "marker": "o", 

1067 "markeredgecolor": CONSTANTS_COLOUR_STYLE.colour.dark, 

1068 "markeredgewidth": CONSTANTS_COLOUR_STYLE.geometry.short * 0.75, 

1069 "markersize": ( 

1070 CONSTANTS_COLOUR_STYLE.geometry.short * 6 

1071 + CONSTANTS_COLOUR_STYLE.geometry.short * 0.75 

1072 ), 

1073 "zorder": CONSTANTS_COLOUR_STYLE.zorder.midground_line, 

1074 "cmfs": cmfs, 

1075 "illuminant": SDS_ILLUMINANTS["E"], 

1076 "use_sd_colours": False, 

1077 "normalise_sd_colours": False, 

1078 } 

1079 for sd in sds_converted 

1080 ] 

1081 

1082 if plot_kwargs is not None: 

1083 update_settings_collection( 

1084 plot_settings_collection, plot_kwargs, len(sds_converted) 

1085 ) 

1086 

1087 for i, sd in enumerate(sds_converted): 

1088 plot_settings = plot_settings_collection[i] 

1089 

1090 cmfs = cast( 

1091 "MultiSpectralDistributions", 

1092 first_item(filter_cmfs(plot_settings.pop("cmfs")).values()), 

1093 ) 

1094 illuminant = cast( 

1095 "SpectralDistribution", 

1096 first_item(filter_illuminants(plot_settings.pop("illuminant")).values()), 

1097 ) 

1098 normalise_sd_colours = plot_settings.pop("normalise_sd_colours") 

1099 use_sd_colours = plot_settings.pop("use_sd_colours") 

1100 

1101 with domain_range_scale("1"): 

1102 XYZ = sd_to_XYZ(sd, cmfs, illuminant) 

1103 

1104 if use_sd_colours: 

1105 if normalise_sd_colours: 

1106 XYZ /= XYZ[..., 1] 

1107 

1108 plot_settings["color"] = np.clip(XYZ_to_plotting_colourspace(XYZ), 0, 1) 

1109 

1110 ij = cast("tuple[float, float]", XYZ_to_ij(XYZ)) 

1111 

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

1113 

1114 if sd.name is not None and annotate_settings_collection[i]["annotate"]: 

1115 annotate_settings = annotate_settings_collection[i] 

1116 annotate_settings.pop("annotate") 

1117 

1118 axes.annotate(sd.name, xy=ij, **annotate_settings) 

1119 

1120 settings.update({"show": True, "bounding_box": bounding_box}) 

1121 settings.update(kwargs) 

1122 

1123 return render(**settings) 

1124 

1125 

1126@override_style() 

1127def plot_sds_in_chromaticity_diagram_CIE1931( 

1128 sds: ( 

1129 Sequence[SpectralDistribution | MultiSpectralDistributions] 

1130 | SpectralDistribution 

1131 | MultiSpectralDistributions 

1132 | ValuesView 

1133 ), 

1134 cmfs: ( 

1135 MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] 

1136 ) = "CIE 1931 2 Degree Standard Observer", 

1137 chromaticity_diagram_callable_CIE1931: Callable = ( 

1138 plot_chromaticity_diagram_CIE1931 

1139 ), 

1140 annotate_kwargs: dict | List[dict] | None = None, 

1141 plot_kwargs: dict | List[dict] | None = None, 

1142 **kwargs: Any, 

1143) -> Tuple[Figure, Axes]: 

1144 """ 

1145 Plot specified spectral distribution chromaticity coordinates in the 

1146 *CIE 1931 Chromaticity Diagram*. 

1147 

1148 Parameters 

1149 ---------- 

1150 sds 

1151 Spectral distributions or multi-spectral distributions to plot. 

1152 ``sds`` can be a single :class:`colour.MultiSpectralDistributions` 

1153 class instance, a list of :class:`colour.MultiSpectralDistributions` 

1154 class instances or a list of :class:`colour.SpectralDistribution` class 

1155 instances. 

1156 cmfs 

1157 Standard observer colour matching functions used for computing the 

1158 spectral locus boundaries. ``cmfs`` can be of any type or form 

1159 supported by the :func:`colour.plotting.common.filter_cmfs` 

1160 definition. 

1161 chromaticity_diagram_callable_CIE1931 

1162 Callable responsible for drawing the *CIE 1931 Chromaticity 

1163 Diagram*. 

1164 annotate_kwargs 

1165 Keyword arguments for the :func:`matplotlib.pyplot.annotate` 

1166 definition, used to annotate the resulting chromaticity 

1167 coordinates with their respective spectral distribution names. 

1168 ``annotate_kwargs`` can be either a single dictionary applied to 

1169 all the arrows with same settings or a sequence of dictionaries 

1170 with different settings for each spectral distribution. The 

1171 following special keyword arguments can also be used: 

1172 

1173 - ``annotate`` : Whether to annotate the spectral 

1174 distributions. 

1175 plot_kwargs 

1176 Keyword arguments for the :func:`matplotlib.pyplot.plot` 

1177 definition, used to control the style of the plotted spectral 

1178 distributions. ``plot_kwargs`` can be either a single dictionary 

1179 applied to all the plotted spectral distributions with the same 

1180 settings or a sequence of dictionaries with different settings for 

1181 each plotted spectral distribution. The following special keyword 

1182 arguments can also be used: 

1183 

1184 - ``illuminant`` : The illuminant used to compute the spectral 

1185 distributions colours. The default is the illuminant 

1186 associated with the whitepoint of the default plotting 

1187 colourspace. ``illuminant`` can be of any type or form 

1188 supported by the :func:`colour.plotting.common.filter_cmfs` 

1189 definition. 

1190 - ``cmfs`` : The standard observer colour matching functions 

1191 used for computing the spectral distributions colours. 

1192 ``cmfs`` can be of any type or form supported by the 

1193 :func:`colour.plotting.common.filter_cmfs` definition. 

1194 - ``normalise_sd_colours`` : Whether to normalise the computed 

1195 spectral distributions colours. The default is *True*. 

1196 - ``use_sd_colours`` : Whether to use the computed spectral 

1197 distributions colours under the plotting colourspace 

1198 illuminant. Alternatively, it is possible to use the 

1199 :func:`matplotlib.pyplot.plot` definition ``color`` argument 

1200 with pre-computed values. The default is *True*. 

1201 

1202 Other Parameters 

1203 ---------------- 

1204 kwargs 

1205 {:func:`colour.plotting.artist`, 

1206 :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, 

1207 :func:`colour.plotting.render`}, 

1208 See the documentation of the previously listed definitions. 

1209 

1210 Returns 

1211 ------- 

1212 :class:`tuple` 

1213 Current figure and axes. 

1214 

1215 Examples 

1216 -------- 

1217 >>> A = SDS_ILLUMINANTS["A"] 

1218 >>> D65 = SDS_ILLUMINANTS["D65"] 

1219 >>> plot_sds_in_chromaticity_diagram_CIE1931([A, D65]) 

1220 ... # doctest: +ELLIPSIS 

1221 (<Figure size ... with 1 Axes>, <...Axes...>) 

1222 

1223 .. image:: ../_static/Plotting_\ 

1224Plot_SDS_In_Chromaticity_Diagram_CIE1931.png 

1225 :align: center 

1226 :alt: plot_sds_in_chromaticity_diagram_CIE1931 

1227 """ 

1228 

1229 settings = dict(kwargs) 

1230 settings.update({"method": "CIE 1931"}) 

1231 

1232 return plot_sds_in_chromaticity_diagram( 

1233 sds, 

1234 cmfs, 

1235 chromaticity_diagram_callable_CIE1931, 

1236 annotate_kwargs=annotate_kwargs, 

1237 plot_kwargs=plot_kwargs, 

1238 **settings, 

1239 ) 

1240 

1241 

1242@override_style() 

1243def plot_sds_in_chromaticity_diagram_CIE1960UCS( 

1244 sds: ( 

1245 Sequence[SpectralDistribution | MultiSpectralDistributions] 

1246 | SpectralDistribution 

1247 | MultiSpectralDistributions 

1248 | ValuesView 

1249 ), 

1250 cmfs: ( 

1251 MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] 

1252 ) = "CIE 1931 2 Degree Standard Observer", 

1253 chromaticity_diagram_callable_CIE1960UCS: Callable = ( 

1254 plot_chromaticity_diagram_CIE1960UCS 

1255 ), 

1256 annotate_kwargs: dict | List[dict] | None = None, 

1257 plot_kwargs: dict | List[dict] | None = None, 

1258 **kwargs: Any, 

1259) -> Tuple[Figure, Axes]: 

1260 """ 

1261 Plot spectral distribution chromaticity coordinates in the 

1262 *CIE 1960 UCS Chromaticity Diagram*. 

1263 

1264 Parameters 

1265 ---------- 

1266 sds 

1267 Spectral distributions or multi-spectral distributions to plot. 

1268 ``sds`` can be a single :class:`colour.MultiSpectralDistributions` 

1269 class instance, a list of :class:`colour.MultiSpectralDistributions` 

1270 class instances or a list of :class:`colour.SpectralDistribution` class 

1271 instances. 

1272 cmfs 

1273 Standard observer colour matching functions used for computing the 

1274 spectral locus boundaries. ``cmfs`` can be of any type or form 

1275 supported by the :func:`colour.plotting.common.filter_cmfs` 

1276 definition. 

1277 chromaticity_diagram_callable_CIE1960UCS 

1278 Callable responsible for drawing the 

1279 *CIE 1960 UCS Chromaticity Diagram*. 

1280 annotate_kwargs 

1281 Keyword arguments for the :func:`matplotlib.pyplot.annotate` 

1282 definition, used to annotate the resulting chromaticity 

1283 coordinates with their respective spectral distribution names. 

1284 ``annotate_kwargs`` can be either a single dictionary applied to 

1285 all the arrows with same settings or a sequence of dictionaries 

1286 with different settings for each spectral distribution. The 

1287 following special keyword arguments can also be used: 

1288 

1289 - ``annotate`` : Whether to annotate the spectral 

1290 distributions. 

1291 plot_kwargs 

1292 Keyword arguments for the :func:`matplotlib.pyplot.plot` 

1293 definition, used to control the style of the plotted spectral 

1294 distributions. ``plot_kwargs`` can be either a single dictionary 

1295 applied to all the plotted spectral distributions with the same 

1296 settings or a sequence of dictionaries with different settings for 

1297 each plotted spectral distribution. The following special keyword 

1298 arguments can also be used: 

1299 

1300 - ``illuminant`` : The illuminant used to compute the spectral 

1301 distributions colours. The default is the illuminant 

1302 associated with the whitepoint of the default plotting 

1303 colourspace. ``illuminant`` can be of any type or form 

1304 supported by the :func:`colour.plotting.common.filter_cmfs` 

1305 definition. 

1306 - ``cmfs`` : The standard observer colour matching functions 

1307 used for computing the spectral distributions colours. 

1308 ``cmfs`` can be of any type or form supported by the 

1309 :func:`colour.plotting.common.filter_cmfs` definition. 

1310 - ``normalise_sd_colours`` : Whether to normalise the computed 

1311 spectral distributions colours. The default is *True*. 

1312 - ``use_sd_colours`` : Whether to use the computed spectral 

1313 distributions colours under the plotting colourspace 

1314 illuminant. Alternatively, it is possible to use the 

1315 :func:`matplotlib.pyplot.plot` definition ``color`` argument 

1316 with pre-computed values. The default is *True*. 

1317 

1318 Other Parameters 

1319 ---------------- 

1320 kwargs 

1321 {:func:`colour.plotting.artist`, 

1322 :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, 

1323 :func:`colour.plotting.render`}, 

1324 See the documentation of the previously listed definitions. 

1325 

1326 Returns 

1327 ------- 

1328 :class:`tuple` 

1329 Current figure and axes. 

1330 

1331 Examples 

1332 -------- 

1333 >>> A = SDS_ILLUMINANTS["A"] 

1334 >>> D65 = SDS_ILLUMINANTS["D65"] 

1335 >>> plot_sds_in_chromaticity_diagram_CIE1960UCS([A, D65]) 

1336 ... # doctest: +ELLIPSIS 

1337 (<Figure size ... with 1 Axes>, <...Axes...>) 

1338 

1339 .. image:: ../_static/Plotting_\ 

1340Plot_SDS_In_Chromaticity_Diagram_CIE1960UCS.png 

1341 :align: center 

1342 :alt: plot_sds_in_chromaticity_diagram_CIE1960UCS 

1343 """ 

1344 

1345 settings = dict(kwargs) 

1346 settings.update({"method": "CIE 1960 UCS"}) 

1347 

1348 return plot_sds_in_chromaticity_diagram( 

1349 sds, 

1350 cmfs, 

1351 chromaticity_diagram_callable_CIE1960UCS, 

1352 annotate_kwargs=annotate_kwargs, 

1353 plot_kwargs=plot_kwargs, 

1354 **settings, 

1355 ) 

1356 

1357 

1358@override_style() 

1359def plot_sds_in_chromaticity_diagram_CIE1976UCS( 

1360 sds: ( 

1361 Sequence[SpectralDistribution | MultiSpectralDistributions] 

1362 | SpectralDistribution 

1363 | MultiSpectralDistributions 

1364 | ValuesView 

1365 ), 

1366 cmfs: ( 

1367 MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] 

1368 ) = "CIE 1931 2 Degree Standard Observer", 

1369 chromaticity_diagram_callable_CIE1976UCS: Callable = ( 

1370 plot_chromaticity_diagram_CIE1976UCS 

1371 ), 

1372 annotate_kwargs: dict | List[dict] | None = None, 

1373 plot_kwargs: dict | List[dict] | None = None, 

1374 **kwargs: Any, 

1375) -> Tuple[Figure, Axes]: 

1376 """ 

1377 Plot specified spectral distribution chromaticity coordinates in the 

1378 *CIE 1976 UCS Chromaticity Diagram*. 

1379 

1380 Parameters 

1381 ---------- 

1382 sds 

1383 Spectral distributions or multi-spectral distributions to plot. 

1384 ``sds`` can be a single :class:`colour.MultiSpectralDistributions` 

1385 class instance, a list of :class:`colour.MultiSpectralDistributions` 

1386 class instances or a list of :class:`colour.SpectralDistribution` class 

1387 instances. 

1388 cmfs 

1389 Standard observer colour matching functions used for computing the 

1390 spectral locus boundaries. ``cmfs`` can be of any type or form 

1391 supported by the :func:`colour.plotting.common.filter_cmfs` 

1392 definition. 

1393 chromaticity_diagram_callable_CIE1976UCS 

1394 Callable responsible for drawing the 

1395 *CIE 1976 UCS Chromaticity Diagram*. 

1396 annotate_kwargs 

1397 Keyword arguments for the :func:`matplotlib.pyplot.annotate` 

1398 definition, used to annotate the resulting chromaticity 

1399 coordinates with their respective spectral distribution names. 

1400 ``annotate_kwargs`` can be either a single dictionary applied to 

1401 all the arrows with same settings or a sequence of dictionaries 

1402 with different settings for each spectral distribution. The 

1403 following special keyword arguments can also be used: 

1404 

1405 - ``annotate`` : Whether to annotate the spectral 

1406 distributions. 

1407 plot_kwargs 

1408 Keyword arguments for the :func:`matplotlib.pyplot.plot` 

1409 definition, used to control the style of the plotted spectral 

1410 distributions. ``plot_kwargs`` can be either a single dictionary 

1411 applied to all the plotted spectral distributions with the same 

1412 settings or a sequence of dictionaries with different settings for 

1413 each plotted spectral distribution. The following special keyword 

1414 arguments can also be used: 

1415 

1416 - ``illuminant`` : The illuminant used to compute the spectral 

1417 distributions colours. The default is the illuminant 

1418 associated with the whitepoint of the default plotting 

1419 colourspace. ``illuminant`` can be of any type or form 

1420 supported by the :func:`colour.plotting.common.filter_cmfs` 

1421 definition. 

1422 - ``cmfs`` : The standard observer colour matching functions 

1423 used for computing the spectral distributions colours. 

1424 ``cmfs`` can be of any type or form supported by the 

1425 :func:`colour.plotting.common.filter_cmfs` definition. 

1426 - ``normalise_sd_colours`` : Whether to normalise the computed 

1427 spectral distributions colours. The default is *True*. 

1428 - ``use_sd_colours`` : Whether to use the computed spectral 

1429 distributions colours under the plotting colourspace 

1430 illuminant. Alternatively, it is possible to use the 

1431 :func:`matplotlib.pyplot.plot` definition ``color`` argument 

1432 with pre-computed values. The default is *True*. 

1433 

1434 Other Parameters 

1435 ---------------- 

1436 kwargs 

1437 {:func:`colour.plotting.artist`, 

1438 :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, 

1439 :func:`colour.plotting.render`}, 

1440 See the documentation of the previously listed definitions. 

1441 

1442 Returns 

1443 ------- 

1444 :class:`tuple` 

1445 Current figure and axes. 

1446 

1447 Examples 

1448 -------- 

1449 >>> A = SDS_ILLUMINANTS["A"] 

1450 >>> D65 = SDS_ILLUMINANTS["D65"] 

1451 >>> plot_sds_in_chromaticity_diagram_CIE1976UCS([A, D65]) 

1452 ... # doctest: +ELLIPSIS 

1453 (<Figure size ... with 1 Axes>, <...Axes...>) 

1454 

1455 .. image:: ../_static/Plotting_\ 

1456Plot_SDS_In_Chromaticity_Diagram_CIE1976UCS.png 

1457 :align: center 

1458 :alt: plot_sds_in_chromaticity_diagram_CIE1976UCS 

1459 """ 

1460 

1461 settings = dict(kwargs) 

1462 settings.update({"method": "CIE 1976 UCS"}) 

1463 

1464 return plot_sds_in_chromaticity_diagram( 

1465 sds, 

1466 cmfs, 

1467 chromaticity_diagram_callable_CIE1976UCS, 

1468 annotate_kwargs=annotate_kwargs, 

1469 plot_kwargs=plot_kwargs, 

1470 **settings, 

1471 )