Coverage for plotting/diagrams.py: 79%

1"""

2 CIE Chromaticity Diagrams Plotting

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

4

5 Define 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

16 from __future__ import annotations

17

18 import bisect

19 import typing

20

21 import numpy as np

22

23 if typing.TYPE_CHECKING:

24 from collections.abc import ValuesView

25

26 if typing.TYPE_CHECKING:

27 from matplotlib.axes import Axes

28

29 from matplotlib.collections import LineCollection

30

31 if typing.TYPE_CHECKING:

32 from matplotlib.figure import Figure

33

34 from matplotlib.patches import Polygon

35

36 from colour.algebra import normalise_maximum, normalise_vector

37 from colour.colorimetry import (

38 SDS_ILLUMINANTS,

39 MultiSpectralDistributions,

40 SpectralDistribution,

41 sd_to_XYZ,

42 sds_and_msds_to_sds,

43)

44 from colour.constants import DTYPE_FLOAT_DEFAULT

45

46 if 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

58 from colour.hints import Tuple, cast

59 from 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)

71 from colour.notation import HEX_to_RGB

72 from 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)

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

118 METHODS_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

150 LABELS_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

223 def 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()

373 def 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()

506 def 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()

630 def 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()

735 def 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()

790 def 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()

845 def 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()

900 def 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()

1127 def 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_\

1224 Plot_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()

1243 def 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_\

1340 Plot_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()

1359 def 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_\

1456 Plot_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 )