Coverage for plotting/temperature.py: 73%

1"""

2 Colour Temperature & Correlated Colour Temperature Plotting

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

4

5 Define the colour temperature and correlated colour temperature plotting

6 objects.

7

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

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

10- :func:`colour.plotting.\

11 plot_planckian_locus_in_chromaticity_diagram_CIE1931`

12- :func:`colour.plotting.\

13 plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS`

14- :func:`colour.plotting.\

15 plot_planckian_locus_in_chromaticity_diagram_CIE1976UCS`

16"""

17

18 from __future__ import annotations

19

20 import typing

21

22 import numpy as np

23

24 if typing.TYPE_CHECKING:

25 from matplotlib.axes import Axes

26

27 from matplotlib.collections import LineCollection

28

29 if typing.TYPE_CHECKING:

30 from matplotlib.figure import Figure

31

32 from colour.algebra import normalise_maximum, normalise_vector

33 from colour.colorimetry import CCS_ILLUMINANTS, MSDS_CMFS

34 from colour.constants import DTYPE_FLOAT_DEFAULT

35

36 if typing.TYPE_CHECKING:

37 from colour.hints import (

38 Any,

39 ArrayLike,

40 Callable,

41 Dict,

42 List,

43 Literal,

44 NDArray,

45 NDArrayFloat,

46 Sequence,

47 Tuple,

48 )

49

50 from colour.hints import cast

51 from colour.models import UCS_uv_to_xy, xy_to_XYZ

52 from colour.plotting import (

53 CONSTANTS_ARROW_STYLE,

54 CONSTANTS_COLOUR_STYLE,

55 METHODS_CHROMATICITY_DIAGRAM,

56 XYZ_to_plotting_colourspace,

57 artist,

58 filter_passthrough,

59 override_style,

60 plot_chromaticity_diagram_CIE1931,

61 plot_chromaticity_diagram_CIE1960UCS,

62 plot_chromaticity_diagram_CIE1976UCS,

63 render,

64 update_settings_collection,

65)

66 from colour.plotting.diagrams import plot_chromaticity_diagram

67 from colour.temperature import CCT_to_uv, CCT_to_xy_CIE_D, mired_to_CCT

68 from colour.utilities import (

69 CanonicalMapping,

70 as_float_array,

71 as_float_scalar,

72 as_int_scalar,

73 full,

74 optional,

75 tstack,

76 validate_method,

77 zeros,

78)

79

80 __author__ = "Colour Developers"

81 __copyright__ = "Copyright 2013 Colour Developers"

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

83 __maintainer__ = "Colour Developers"

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

85 __status__ = "Production"

86

87 __all__ = [

88 "lines_daylight_locus",

89 "plot_daylight_locus",

90 "LABELS_PLANCKIAN_LOCUS_DEFAULT",

91 "lines_planckian_locus",

92 "plot_planckian_locus",

93 "plot_planckian_locus_in_chromaticity_diagram",

94 "plot_planckian_locus_in_chromaticity_diagram_CIE1931",

95 "plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS",

96 "plot_planckian_locus_in_chromaticity_diagram_CIE1976UCS",

97]

98

99

100 def lines_daylight_locus(

101 mireds: bool = False,

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

103) -> Tuple[NDArray]:

104 """

105 Return the *Daylight Locus* line vertices including positions, normals,

106 and colours using the specified chromaticity diagram method.

107

108 Parameters

109 ----------

110 mireds

111 Whether to use micro reciprocal degrees for the iso-temperature

112 lines.

113 method

114 *Daylight Locus* chromaticity diagram method.

115

116 Returns

117 -------

118 :class:`tuple`

119 Tuple of *Daylight Locus* line vertices containing position,

120 normal, and colour data.

121

122 Examples

123 --------

124 >>> lines = lines_daylight_locus()

125 >>> len(lines)

126 1

127 >>> lines[0].dtype

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

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

130 """

131

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

133

134 xy_to_ij = METHODS_CHROMATICITY_DIAGRAM[method]["xy_to_ij"]

135

136 def CCT_to_plotting_colourspace(CCT: ArrayLike) -> NDArrayFloat:

137 """

138 Convert specified correlated colour temperature :math:`T_{cp}` to the

139 default plotting colourspace.

140 """

141

142 return normalise_maximum(

143 XYZ_to_plotting_colourspace(xy_to_XYZ(CCT_to_xy_CIE_D(CCT))),

144 axis=-1,

145 )

146

147 start, end = (0, 1000) if mireds else (1e6 / 600, 1e6 / 10)

148

149 CCT = np.arange(start, end + 100, 10) * 1.4388 / 1.4380

150 CCT = mired_to_CCT(CCT) if mireds else CCT

151

152 ij_sl = np.reshape(xy_to_ij(CCT_to_xy_CIE_D(CCT)), (-1, 2))

153 colour_sl = np.reshape(CCT_to_plotting_colourspace(CCT), (-1, 3))

154

155 lines_sl = zeros(

156 ij_sl.shape[0],

157 [

158 ("position", DTYPE_FLOAT_DEFAULT, 2),

159 ("normal", DTYPE_FLOAT_DEFAULT, 2),

160 ("colour", DTYPE_FLOAT_DEFAULT, 3),

161 ], # pyright: ignore

162 )

163

164 lines_sl["position"] = ij_sl

165 lines_sl["colour"] = colour_sl

166

167 return (lines_sl,)

168

169

170@override_style()

171 def plot_daylight_locus(

172 daylight_locus_colours: ArrayLike | str | None = None,

173 daylight_locus_opacity: float = 1,

174 daylight_locus_mireds: bool = False,

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

176 **kwargs: Any,

177) -> Tuple[Figure, Axes]:

178 """

179 Plot the *Daylight Locus* using the specified chromaticity diagram

180 method.

181

182 Parameters

183 ----------

184 daylight_locus_colours

185 Colours of the *Daylight Locus*. If set to *RGB*, the colours will

186 be computed from the corresponding chromaticity coordinates.

187 daylight_locus_opacity

188 Opacity of the *Daylight Locus*.

189 daylight_locus_mireds

190 Whether to use micro reciprocal degrees for the iso-temperature

191 lines.

192 method

193 *Chromaticity Diagram* method.

194

195 Other Parameters

196 ----------------

197 kwargs

198 {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},

199 See the documentation of the previously listed definitions.

200

201 Returns

202 -------

203 :class:`tuple`

204 Current figure and axes.

205

206 Examples

207 --------

208 >>> plot_daylight_locus(daylight_locus_colours="RGB")

209 ... # doctest: +ELLIPSIS

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

211

212 .. image:: ../_static/Plotting_Plot_Daylight_Locus.png

213 :align: center

214 :alt: plot_daylight_locus

215 """

216

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

218

219 use_RGB_daylight_locus_colours = str(daylight_locus_colours).upper() == "RGB"

220

221 daylight_locus_colours = optional(

222 daylight_locus_colours, CONSTANTS_COLOUR_STYLE.colour.dark

223 )

224

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

226 settings.update(kwargs)

227

228 _figure, axes = artist(**settings)

229

230 lines_sl, *_ = lines_daylight_locus(daylight_locus_mireds, method)

231

232 line_collection = LineCollection(

233 np.reshape(

234 np.concatenate(

235 [lines_sl["position"][:-1], lines_sl["position"][1:]], axis=1

236 ),

237 (-1, 2, 2),

238 ), # pyright: ignore

239 colors=(

240 lines_sl["colour"]

241 if use_RGB_daylight_locus_colours

242 else daylight_locus_colours

243 ),

244 alpha=daylight_locus_opacity,

245 zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_line,

246 )

247 axes.add_collection(line_collection)

248

249 settings = {"axes": axes}

250 settings.update(kwargs)

251

252 return render(**settings)

253

254

255 LABELS_PLANCKIAN_LOCUS_DEFAULT: CanonicalMapping = CanonicalMapping(

256 {

257 "Default": (1e6 / 600, 2000, 2500, 3000, 4000, 6000, 1e6 / 100),

258 "Mireds": (0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000),

259 }

260)

261"""*Planckian Locus* default labels."""

262

263

264 def lines_planckian_locus(

265 labels: Sequence | None = None,

266 mireds: bool = False,

267 iso_temperature_lines_D_uv: float = 0.05,

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

269) -> Tuple[NDArray, NDArray]:

270 """

271 Return the *Planckian Locus* line vertices with positions, normals, and

272 colours using the specified chromaticity diagram method.

273

274 Parameters

275 ----------

276 labels

277 Array of labels used to customise which iso-temperature lines will

278 be drawn along the *Planckian Locus*. Passing an empty array will

279 result in no iso-temperature lines being drawn.

280 mireds

281 Whether to use micro reciprocal degrees for the iso-temperature

282 lines.

283 iso_temperature_lines_D_uv

284 Iso-temperature lines :math:`\\Delta_{uv}` length on each side of

285 the *Planckian Locus*.

286 method

287 *Planckian Locus* method.

288

289 Returns

290 -------

291 :class:`tuple`

292 Tuple of *Planckian Locus* vertices and wavelength labels vertices.

293

294 Examples

295 --------

296 >>> lines = lines_planckian_locus()

297 >>> len(lines)

298 2

299 >>> lines[0].dtype

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

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

302 >>> lines[1].dtype

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

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

305 """

306

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

308

309 labels = cast(

310 "tuple",

311 optional(

312 labels,

313 LABELS_PLANCKIAN_LOCUS_DEFAULT["Mireds" if mireds else "Default"],

314 ),

315 )

316 D_uv = iso_temperature_lines_D_uv

317

318 uv_to_ij = METHODS_CHROMATICITY_DIAGRAM[method]["uv_to_ij"]

319

320 def CCT_D_uv_to_plotting_colourspace(CCT_D_uv: ArrayLike) -> NDArrayFloat:

321 """

322 Convert specified correlated colour temperature :math:`T_{cp}` and

323 :math:`\\Delta_{uv}` to the default plotting colourspace.

324 """

325

326 return normalise_maximum(

327 XYZ_to_plotting_colourspace(

328 xy_to_XYZ(UCS_uv_to_xy(CCT_to_uv(CCT_D_uv, "Robertson 1968")))

329 ),

330 axis=-1,

331 )

332

333 # Planckian Locus

334 start, end = (0, 1000) if mireds else (1e6 / 600, 1e6 / 10)

335

336 CCT = np.arange(start, end + 100, 100)

337 CCT = mired_to_CCT(CCT) if mireds else CCT

338 CCT_D_uv = tstack([CCT, zeros(CCT.shape)])

339

340 ij_pl = uv_to_ij(CCT_to_uv(CCT_D_uv, "Robertson 1968"))

341 colour_pl = CCT_D_uv_to_plotting_colourspace(CCT_D_uv)

342

343 lines_pl = zeros(

344 ij_pl.shape[0],

345 [

346 ("position", DTYPE_FLOAT_DEFAULT, 2),

347 ("normal", DTYPE_FLOAT_DEFAULT, 2),

348 ("colour", DTYPE_FLOAT_DEFAULT, 3),

349 ], # pyright: ignore

350 )

351

352 lines_pl["position"] = ij_pl

353 lines_pl["colour"] = colour_pl

354

355 # Labels

356 ij_itl, normal_itl, colour_itl = [], [], []

357 for label in labels:

358 CCT_D_uv = tstack(

359 [

360 full(

361 20,

362 as_float_scalar(mired_to_CCT(label)) if mireds else label,

363 ),

364 np.linspace(-D_uv, D_uv, 20),

365 ]

366 )

367

368 ij = uv_to_ij(CCT_to_uv(CCT_D_uv, "Robertson 1968"))

369 ij_itl.append(ij)

370 normal_itl.append(np.tile(normalise_vector(ij[-1, ...] - ij[0, ...]), (20, 1)))

371 colour_itl.append(CCT_D_uv_to_plotting_colourspace(CCT_D_uv))

372

373 ij_l = np.reshape(as_float_array(ij_itl), (-1, 2))

374 normal_l = np.reshape(as_float_array(normal_itl), (-1, 2))

375 colour_l = np.reshape(as_float_array(colour_itl), (-1, 3))

376

377 lines_l = zeros(

378 ij_l.shape[0],

379 [

380 ("position", DTYPE_FLOAT_DEFAULT, 2),

381 ("normal", DTYPE_FLOAT_DEFAULT, 2),

382 ("colour", DTYPE_FLOAT_DEFAULT, 3),

383 ], # pyright: ignore

384 )

385

386 lines_l["position"] = ij_l

387 lines_l["normal"] = normal_l

388 lines_l["colour"] = colour_l

389

390 return lines_pl, lines_l

391

392

393@override_style()

394 def plot_planckian_locus(

395 planckian_locus_colours: ArrayLike | str | None = None,

396 planckian_locus_opacity: float = 1,

397 planckian_locus_labels: Sequence | None = None,

398 planckian_locus_mireds: bool = False,

399 planckian_locus_iso_temperature_lines_D_uv: float = 0.05,

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

401 **kwargs: Any,

402) -> Tuple[Figure, Axes]:

403 """

404 Plot the *Planckian Locus* using the specified method.

405

406 Parameters

407 ----------

408 planckian_locus_colours

409 Colours of the *Planckian Locus*, if ``planckian_locus_colours`` is

410 set to *RGB*, the colours will be computed using the corresponding

411 chromaticity coordinates.

412 planckian_locus_opacity

413 Opacity of the *Planckian Locus*.

414 planckian_locus_labels

415 Array of labels used to customise which iso-temperature lines will

416 be drawn along the *Planckian Locus*. Passing an empty array will

417 result in no iso-temperature lines being drawn.

418 planckian_locus_mireds

419 Whether to use micro reciprocal degrees for the iso-temperature

420 lines.

421 planckian_locus_iso_temperature_lines_D_uv

422 Iso-temperature lines :math:`\\Delta_{uv}` length on each side of

423 the *Planckian Locus*.

424 method

425 *Chromaticity Diagram* method.

426

427 Other Parameters

428 ----------------

429 kwargs

430 {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},

431 See the documentation of the previously listed definitions.

432

433 Returns

434 -------

435 :class:`tuple`

436 Current figure and axes.

437

438 Examples

439 --------

440 >>> plot_planckian_locus(planckian_locus_colours="RGB")

441 ... # doctest: +ELLIPSIS

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

443

444 .. image:: ../_static/Plotting_Plot_Planckian_Locus.png

445 :align: center

446 :alt: plot_planckian_locus

447 """

448

449 planckian_locus_colours = optional(

450 planckian_locus_colours, CONSTANTS_COLOUR_STYLE.colour.dark

451 )

452

453 use_RGB_planckian_locus_colours = str(planckian_locus_colours).upper() == "RGB"

454

455 labels = cast(

456 "tuple",

457 optional(

458 planckian_locus_labels,

459 LABELS_PLANCKIAN_LOCUS_DEFAULT[

460 "Mireds" if planckian_locus_mireds else "Default"

461 ],

462 ),

463 )

464

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

466 settings.update(kwargs)

467

468 _figure, axes = artist(**settings)

469

470 lines_pl, lines_l = lines_planckian_locus(

471 labels,

472 planckian_locus_mireds,

473 planckian_locus_iso_temperature_lines_D_uv,

474 method,

475 )

476

477 axes.add_collection(

478 LineCollection(

479 np.reshape(

480 np.concatenate(

481 [lines_pl["position"][:-1], lines_pl["position"][1:]], axis=1

482 ),

483 (-1, 2, 2),

484 ), # pyright: ignore

485 colors=(

486 lines_pl["colour"]

487 if use_RGB_planckian_locus_colours

488 else planckian_locus_colours

489 ),

490 alpha=planckian_locus_opacity,

491 zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_line,

492 )

493 )

494

495 lines_itl = np.reshape(lines_l["position"], (len(labels), 20, 2))

496 colours_itl = np.reshape(lines_l["colour"], (len(labels), 20, 3))

497 for i, label in enumerate(labels):

498 axes.add_collection(

499 LineCollection(

500 np.reshape(

501 np.concatenate(

502 [lines_itl[i][:-1], lines_itl[i][1:]], # pyright: ignore

503 axis=1,

504 ),

505 (-1, 2, 2),

506 ),

507 colors=(

508 colours_itl[i]

509 if use_RGB_planckian_locus_colours

510 else planckian_locus_colours

511 ),

512 alpha=planckian_locus_opacity,

513 zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_line,

514 )

515 )

516

517 axes.text(

518 lines_itl[i][-1, 0],

519 lines_itl[i][-1, 1],

520 f"{as_int_scalar(label)}{'M' if planckian_locus_mireds else 'K'}",

521 clip_on=True,

522 ha="left",

523 va="bottom",

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

525 zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_label,

526 )

527

528 settings = {"axes": axes}

529 settings.update(kwargs)

530

531 return render(**settings)

532

533

534@override_style()

535 def plot_planckian_locus_in_chromaticity_diagram(

536 illuminants: str | Sequence[str] | None = None,

537 chromaticity_diagram_callable: Callable = plot_chromaticity_diagram,

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

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

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

541 **kwargs: Any,

542) -> Tuple[Figure, Axes]:

543 """

544 Plot the *Planckian Locus* and specified illuminants in the

545 *Chromaticity Diagram* using the specified method.

546

547 Parameters

548 ----------

549 illuminants

550 Illuminants to plot. ``illuminants`` elements can be of any

551 type or form supported by the

552 :func:`colour.plotting.common.filter_passthrough` definition.

553 chromaticity_diagram_callable

554 Callable responsible for drawing the *Chromaticity Diagram*.

555 method

556 *Chromaticity Diagram* method.

557 annotate_kwargs

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

559 definition, used to annotate the resulting chromaticity

560 coordinates with their respective spectral distribution names.

561 ``annotate_kwargs`` can be either a single dictionary applied

562 to all the arrows with same settings or a sequence of

563 dictionaries with different settings for each spectral

564 distribution. The following special keyword arguments can also

565 be used:

566

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

568 plot_kwargs

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

570 definition, used to control the style of the plotted

571 illuminants. ``plot_kwargs`` can be either a single dictionary

572 applied to all the plotted illuminants with the same settings

573 or a sequence of dictionaries with different settings for each

574 plotted illuminant.

575

576 Other Parameters

577 ----------------

578 kwargs

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

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

581 :func:`colour.plotting.temperature.plot_planckian_locus`,

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

583 See the documentation of the previously listed definitions.

584

585 Returns

586 -------

587 :class:`tuple`

588 Current figure and axes.

589

590 Examples

591 --------

592 >>> annotate_kwargs = [

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

594 ... {"arrowprops": {"arrowstyle": "-["}},

595 ... {},

596 ... ]

597 >>> plot_kwargs = [

598 ... {

599 ... "markersize": 15,

600 ... },

601 ... {"color": "r"},

602 ... {},

603 ... ]

604 >>> plot_planckian_locus_in_chromaticity_diagram(

605 ... ["A", "B", "C"],

606 ... annotate_kwargs=annotate_kwargs,

607 ... plot_kwargs=plot_kwargs,

608 ... ) # doctest: +ELLIPSIS

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

610

611 .. image:: ../_static/Plotting_\

612 Plot_Planckian_Locus_In_Chromaticity_Diagram.png

613 :align: center

614 :alt: plot_planckian_locus_in_chromaticity_diagram

615 """

616

617 illuminants = optional(illuminants, [])

618

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

620

621 cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]

622

623 illuminants_filtered = filter_passthrough(CCS_ILLUMINANTS[cmfs.name], illuminants)

624

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

626 settings.update(kwargs)

627

628 _figure, axes = artist(**settings)

629

630 method = method.upper()

631

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

633 settings.update(kwargs)

634 settings["show"] = False

635

636 chromaticity_diagram_callable(**settings)

637

638 plot_planckian_locus(**settings)

639

640 xy_to_ij = METHODS_CHROMATICITY_DIAGRAM[method]["xy_to_ij"]

641

642 if method == "CIE 1931":

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

644

645 elif method == "CIE 1960 UCS":

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

647

648 elif method == "CIE 1976 UCS":

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

650

651 annotate_settings_collection = [

652 {

653 "annotate": True,

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

655 "textcoords": "offset points",

656 "arrowprops": CONSTANTS_ARROW_STYLE,

657 "zorder": CONSTANTS_COLOUR_STYLE.zorder.foreground_annotation,

658 }

659 for _ in range(len(illuminants_filtered))

660 ]

661

662 if annotate_kwargs is not None:

663 update_settings_collection(

664 annotate_settings_collection,

665 annotate_kwargs,

666 len(illuminants_filtered),

667 )

668

669 plot_settings_collection = [

670 {

671 "color": CONSTANTS_COLOUR_STYLE.colour.brightest,

672 "label": f"{illuminant}",

673 "marker": "o",

674 "markeredgecolor": CONSTANTS_COLOUR_STYLE.colour.dark,

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

676 "markersize": (

677 CONSTANTS_COLOUR_STYLE.geometry.short * 6

678 + CONSTANTS_COLOUR_STYLE.geometry.short * 0.75

679 ),

680 "zorder": CONSTANTS_COLOUR_STYLE.zorder.foreground_line,

681 }

682 for illuminant in illuminants_filtered

683 ]

684

685 if plot_kwargs is not None:

686 update_settings_collection(

687 plot_settings_collection, plot_kwargs, len(illuminants_filtered)

688 )

689

690 for i, (illuminant, xy) in enumerate(illuminants_filtered.items()):

691 plot_settings = plot_settings_collection[i]

692

693 ij = cast("tuple[float, float]", xy_to_ij(xy))

694

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

696

697 if annotate_settings_collection[i]["annotate"]:

698 annotate_settings = annotate_settings_collection[i]

699 annotate_settings.pop("annotate")

700

701 axes.annotate(illuminant, xy=ij, **annotate_settings)

702

703 title = (

704 (

705 f"{', '.join(illuminants_filtered)} Illuminants - Planckian Locus\n"

706 f"{method.upper()} Chromaticity Diagram - "

707 "CIE 1931 2 Degree Standard Observer"

708 )

709 if illuminants_filtered

710 else (

711 f"Planckian Locus\n{method.upper()} Chromaticity Diagram - "

712 f"CIE 1931 2 Degree Standard Observer"

713 )

714 )

715

716 settings.update(

717 {

718 "axes": axes,

719 "show": True,

720 "bounding_box": bounding_box,

721 "title": title,

722 }

723 )

724 settings.update(kwargs)

725

726 return render(**settings)

727

728

729@override_style()

730 def plot_planckian_locus_in_chromaticity_diagram_CIE1931(

731 illuminants: str | Sequence[str] | None = None,

732 chromaticity_diagram_callable_CIE1931: Callable = (

733 plot_chromaticity_diagram_CIE1931

734 ),

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

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

737 **kwargs: Any,

738) -> Tuple[Figure, Axes]:

739 """

740 Plot the *Planckian Locus* and specified illuminants in the

741 *CIE 1931 Chromaticity Diagram*.

742

743 Parameters

744 ----------

745 illuminants

746 Illuminants to plot. ``illuminants`` elements can be of any type or

747 form supported by the

748 :func:`colour.plotting.common.filter_passthrough` definition.

749 chromaticity_diagram_callable_CIE1931

750 Callable responsible for drawing the *CIE 1931 Chromaticity Diagram*.

751 annotate_kwargs

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

753 definition, used to annotate the resulting chromaticity

754 coordinates with their respective spectral distribution names.

755 ``annotate_kwargs`` can be either a single dictionary applied

756 to all the arrows with same settings or a sequence of

757 dictionaries with different settings for each spectral

758 distribution. The following special keyword arguments can also

759 be used:

760

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

762 plot_kwargs

763 Keyword arguments for the :func:`matplotlib.pyplot.plot` definition,

764 used to control the style of the plotted illuminants. ``plot_kwargs``

765 can be either a single dictionary applied to all the plotted

766 illuminants with the same settings or a sequence of dictionaries with

767 different settings for each plotted illuminant.

768

769 Other Parameters

770 ----------------

771 kwargs

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

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

774 :func:`colour.plotting.temperature.plot_planckian_locus`,

775 :func:`colour.plotting.temperature.\

776 plot_planckian_locus_in_chromaticity_diagram`,

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

778 See the documentation of the previously listed definitions.

779

780 Returns

781 -------

782 :class:`tuple`

783 Current figure and axes.

784

785 Examples

786 --------

787 >>> plot_planckian_locus_in_chromaticity_diagram_CIE1931(["A", "B", "C"])

788 ... # doctest: +ELLIPSIS

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

790

791 .. image:: ../_static/Plotting_\

792 Plot_Planckian_Locus_In_Chromaticity_Diagram_CIE1931.png

793 :align: center

794 :alt: plot_planckian_locus_in_chromaticity_diagram_CIE1931

795 """

796

797 settings = dict(kwargs)

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

799

800 return plot_planckian_locus_in_chromaticity_diagram(

801 illuminants,

802 chromaticity_diagram_callable_CIE1931,

803 annotate_kwargs=annotate_kwargs,

804 plot_kwargs=plot_kwargs,

805 **settings,

806 )

807

808

809@override_style()

810 def plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS(

811 illuminants: str | Sequence[str] | None = None,

812 chromaticity_diagram_callable_CIE1960UCS: Callable = (

813 plot_chromaticity_diagram_CIE1960UCS

814 ),

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

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

817 **kwargs: Any,

818) -> Tuple[Figure, Axes]:

819 """

820 Plot the *Planckian Locus* and specified illuminants in the

821 *CIE 1960 UCS Chromaticity Diagram*.

822

823 Parameters

824 ----------

825 illuminants

826 Illuminants to plot. ``illuminants`` elements can be of any type or

827 form supported by the

828 :func:`colour.plotting.common.filter_passthrough` definition.

829 chromaticity_diagram_callable_CIE1960UCS

830 Callable responsible for drawing the

831 *CIE 1960 UCS Chromaticity Diagram*.

832 annotate_kwargs

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

834 definition, used to annotate the resulting chromaticity

835 coordinates with their respective spectral distribution names.

836 ``annotate_kwargs`` can be either a single dictionary applied

837 to all the arrows with same settings or a sequence of

838 dictionaries with different settings for each spectral

839 distribution. The following special keyword arguments can also

840 be used:

841

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

843 plot_kwargs

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

845 definition, used to control the style of the plotted illuminants.

846 ``plot_kwargs`` can be either a single dictionary applied to all

847 the plotted illuminants with the same settings or a sequence of

848 dictionaries with different settings for each plotted illuminant.

849

850 Other Parameters

851 ----------------

852 kwargs

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

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

855 :func:`colour.plotting.temperature.plot_planckian_locus`,

856 :func:`colour.plotting.temperature.\

857 plot_planckian_locus_in_chromaticity_diagram`,

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

859 See the documentation of the previously listed definitions.

860

861 Returns

862 -------

863 :class:`tuple`

864 Current figure and axes.

865

866 Examples

867 --------

868 >>> plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS(

869 ... ["A", "C", "E"]

870 ... ) # doctest: +ELLIPSIS

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

872

873 .. image:: ../_static/Plotting_\

874 Plot_Planckian_Locus_In_Chromaticity_Diagram_CIE1960UCS.png

875 :align: center

876 :alt: plot_planckian_locus_in_chromaticity_diagram_CIE1960UCS

877 """

878

879 settings = dict(kwargs)

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

881

882 return plot_planckian_locus_in_chromaticity_diagram(

883 illuminants,

884 chromaticity_diagram_callable_CIE1960UCS,

885 annotate_kwargs=annotate_kwargs,

886 plot_kwargs=plot_kwargs,

887 **settings,

888 )

889

890

891@override_style()

892 def plot_planckian_locus_in_chromaticity_diagram_CIE1976UCS(

893 illuminants: str | Sequence[str] | None = None,

894 chromaticity_diagram_callable_CIE1976UCS: Callable = (

895 plot_chromaticity_diagram_CIE1976UCS

896 ),

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

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

899 **kwargs: Any,

900) -> Tuple[Figure, Axes]:

901 """

902 Plot the *Planckian Locus* and specified illuminants in the

903 *CIE 1976 UCS Chromaticity Diagram*.

904

905 Parameters

906 ----------

907 illuminants

908 Illuminants to plot. ``illuminants`` elements can be of any type

909 or form supported by the

910 :func:`colour.plotting.common.filter_passthrough` definition.

911 chromaticity_diagram_callable_CIE1976UCS

912 Callable responsible for drawing the

913 *CIE 1976 UCS Chromaticity Diagram*.

914 annotate_kwargs

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

916 definition, used to annotate the resulting chromaticity

917 coordinates with their respective spectral distribution names.

918 ``annotate_kwargs`` can be either a single dictionary applied

919 to all the arrows with same settings or a sequence of

920 dictionaries with different settings for each spectral

921 distribution. The following special keyword arguments can also

922 be used:

923

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

925 plot_kwargs

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

927 definition, used to control the style of the plotted illuminants.

928 ``plot_kwargs`` can be either a single dictionary applied to all

929 the plotted illuminants with the same settings or a sequence of

930 dictionaries with different settings for each plotted illuminant.

931

932 Other Parameters

933 ----------------

934 kwargs

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

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

937 :func:`colour.plotting.temperature.plot_planckian_locus`,

938 :func:`colour.plotting.temperature.\

939 plot_planckian_locus_in_chromaticity_diagram`,

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

941 See the documentation of the previously listed definitions.

942

943 Returns

944 -------

945 :class:`tuple`

946 Current figure and axes.

947

948 Examples

949 --------

950 >>> plot_planckian_locus_in_chromaticity_diagram_CIE1976UCS(

951 ... ["A", "C", "E"]

952 ... ) # doctest: +ELLIPSIS

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

954

955 .. image:: ../_static/Plotting_\

956 Plot_Planckian_Locus_In_Chromaticity_Diagram_CIE1976UCS.png

957 :align: center

958 :alt: plot_planckian_locus_in_chromaticity_diagram_CIE1976UCS

959 """

960

961 settings = dict(kwargs)

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

963

964 return plot_planckian_locus_in_chromaticity_diagram(

965 illuminants,

966 chromaticity_diagram_callable_CIE1976UCS,

967 annotate_kwargs=annotate_kwargs,

968 plot_kwargs=plot_kwargs,

969 **settings,

970 )