Coverage for colour/plotting/section.py: 93%

162 statements  

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

1""" 

2Gamut Section Plotting 

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

4 

5Define the gamut section plotting objects. 

6 

7- :func:`colour.plotting.section.plot_hull_section_colours` 

8- :func:`colour.plotting.section.plot_hull_section_contour` 

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

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

11""" 

12 

13from __future__ import annotations 

14 

15import typing 

16 

17import numpy as np 

18 

19if typing.TYPE_CHECKING: 

20 from matplotlib.axes import Axes 

21 

22from matplotlib.collections import LineCollection 

23 

24if typing.TYPE_CHECKING: 

25 from matplotlib.figure import Figure 

26 

27from matplotlib.patches import Polygon 

28 

29from colour.colorimetry import ( 

30 MultiSpectralDistributions, 

31 SpectralDistribution, 

32 SpectralShape, 

33 reshape_msds, 

34) 

35from colour.geometry import hull_section, primitive_cube 

36from colour.graph import colourspace_model_to_reference, convert 

37 

38if typing.TYPE_CHECKING: 

39 from colour.hints import ( 

40 Any, 

41 ArrayLike, 

42 Dict, 

43 Literal, 

44 LiteralColourspaceModel, 

45 LiteralRGBColourspace, 

46 Sequence, 

47 Tuple, 

48 ) 

49 

50from colour.hints import Real, cast 

51from colour.models import ( 

52 COLOURSPACE_MODELS_AXIS_LABELS, 

53 RGB_Colourspace, 

54 RGB_to_XYZ, 

55) 

56from colour.notation import HEX_to_RGB 

57from colour.plotting import ( 

58 CONSTANTS_COLOUR_STYLE, 

59 XYZ_to_plotting_colourspace, 

60 artist, 

61 colourspace_model_axis_reorder, 

62 filter_cmfs, 

63 filter_illuminants, 

64 filter_RGB_colourspaces, 

65 override_style, 

66 render, 

67) 

68from colour.utilities import ( 

69 CanonicalMapping, 

70 as_int_array, 

71 first_item, 

72 full, 

73 ones, 

74 optional, 

75 required, 

76 suppress_warnings, 

77 tstack, 

78 validate_method, 

79) 

80from colour.volume import solid_RoschMacAdam 

81 

82__author__ = "Colour Developers" 

83__copyright__ = "Copyright 2013 Colour Developers" 

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

85__maintainer__ = "Colour Developers" 

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

87__status__ = "Production" 

88 

89__all__ = [ 

90 "MAPPING_AXIS_TO_PLANE", 

91 "plot_hull_section_colours", 

92 "plot_hull_section_contour", 

93 "plot_visible_spectrum_section", 

94 "plot_RGB_colourspace_section", 

95] 

96 

97MAPPING_AXIS_TO_PLANE: CanonicalMapping = CanonicalMapping( 

98 {"+x": (1, 2), "+y": (0, 2), "+z": (0, 1)} 

99) 

100MAPPING_AXIS_TO_PLANE.__doc__ = """ 

101Mapping from axes to their orthogonal planes. 

102 

103Maps each positive axis ('+x', '+y', '+z') to the indices of the two 

104dimensions that form the perpendicular plane in 3D space. 

105""" 

106 

107 

108@required("trimesh") 

109@override_style() 

110def plot_hull_section_colours( 

111 hull: trimesh.Trimesh, # pyright: ignore # noqa: F821 

112 model: LiteralColourspaceModel | str = "CIE xyY", 

113 axis: Literal["+z", "+x", "+y"] | str = "+z", 

114 origin: float = 0.5, 

115 normalise: bool = True, 

116 section_colours: ArrayLike | str | None = None, 

117 section_opacity: float = 1, 

118 convert_kwargs: dict | None = None, 

119 samples: int = 256, 

120 **kwargs: Any, 

121) -> Tuple[Figure, Axes]: 

122 """ 

123 Plot the section colours of the specified *trimesh* hull along the 

124 specified axis and origin. 

125 

126 Parameters 

127 ---------- 

128 hull 

129 *Trimesh* hull. 

130 model 

131 Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute 

132 for the list of supported colourspace models. 

133 axis 

134 Axis the hull section will be normal to. 

135 origin 

136 Coordinate along ``axis`` at which to plot the hull section. 

137 normalise 

138 Whether to normalise ``axis`` to the extent of the hull along it. 

139 section_colours 

140 Colours of the hull section, if ``section_colours`` is set to *RGB*, 

141 the colours will be computed using the corresponding coordinates. 

142 section_opacity 

143 Opacity of the hull section colours. 

144 convert_kwargs 

145 Keyword arguments for the :func:`colour.convert` definition. 

146 samples 

147 Sample count on one axis when computing the hull section colours. 

148 

149 Other Parameters 

150 ---------------- 

151 kwargs 

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

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

154 See the documentation of the previously listed definitions. 

155 

156 Returns 

157 ------- 

158 :class:`tuple` 

159 Current figure and axes. 

160 

161 Examples 

162 -------- 

163 >>> from colour.models import RGB_COLOURSPACE_sRGB 

164 >>> from colour.utilities import is_trimesh_installed 

165 >>> vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64) 

166 >>> XYZ_vertices = RGB_to_XYZ(vertices["position"] + 0.5, RGB_COLOURSPACE_sRGB) 

167 >>> if is_trimesh_installed: 

168 ... from trimesh import Trimesh 

169 ... 

170 ... hull = Trimesh(XYZ_vertices, faces, process=False) 

171 ... plot_hull_section_colours(hull, section_colours="RGB") 

172 ... # doctest: +ELLIPSIS 

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

174 

175 .. image:: ../_static/Plotting_Plot_Hull_Section_Colours.png 

176 :align: center 

177 :alt: plot_hull_section_colours 

178 """ 

179 

180 axis = validate_method( 

181 axis, 

182 ("+z", "+x", "+y"), 

183 '"{0}" axis is invalid, it must be one of {1}!', 

184 ) 

185 

186 hull = hull.copy() 

187 

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

189 settings.update(kwargs) 

190 

191 _figure, axes = artist(**settings) 

192 

193 section_colours = optional( 

194 section_colours, HEX_to_RGB(CONSTANTS_COLOUR_STYLE.colour.average) 

195 ) 

196 

197 convert_kwargs = optional(convert_kwargs, {}) 

198 

199 # Luminance / Lightness reordered along "z" axis. 

200 with suppress_warnings(python_warnings=True): 

201 ijk_vertices = colourspace_model_axis_reorder( 

202 convert(hull.vertices, "CIE XYZ", model, **convert_kwargs), model 

203 ) 

204 ijk_vertices = np.nan_to_num(ijk_vertices) 

205 ijk_vertices = colourspace_model_to_reference(ijk_vertices, model) 

206 

207 hull.vertices = ijk_vertices 

208 

209 if axis == "+x": 

210 index_origin = 0 

211 elif axis == "+y": 

212 index_origin = 1 

213 elif axis == "+z": 

214 index_origin = 2 

215 plane = MAPPING_AXIS_TO_PLANE[axis] 

216 

217 section = hull_section(hull, axis, origin, normalise) 

218 

219 padding = 0.1 * np.mean(colourspace_model_to_reference(ones(3), model)) 

220 min_x = np.min(ijk_vertices[..., plane[0]]) - padding 

221 max_x = np.max(ijk_vertices[..., plane[0]]) + padding 

222 min_y = np.min(ijk_vertices[..., plane[1]]) - padding 

223 max_y = np.max(ijk_vertices[..., plane[1]]) + padding 

224 extent = (min_x, max_x, min_y, max_y) 

225 

226 use_RGB_section_colours = str(section_colours).upper() == "RGB" 

227 if use_RGB_section_colours: 

228 ii, jj = np.meshgrid( 

229 np.linspace(min_x, max_x, samples), 

230 np.linspace(max_y, min_y, samples), 

231 ) 

232 ij = tstack([ii, jj]) 

233 ijk_section = full( 

234 (samples, samples, 3), 

235 cast("Real", np.median(section[..., index_origin])), 

236 ) 

237 ijk_section[..., plane] = ij 

238 ijk_section = ijk_section / colourspace_model_to_reference(ones(3), model) 

239 XYZ_section = convert( 

240 colourspace_model_axis_reorder(ijk_section, model, "Inverse"), 

241 model, 

242 "CIE XYZ", 

243 **convert_kwargs, 

244 ) 

245 RGB_section = XYZ_to_plotting_colourspace(XYZ_section) 

246 else: 

247 section_colours = np.hstack([section_colours, section_opacity]) 

248 

249 facecolor = "none" if use_RGB_section_colours else section_colours 

250 polygon = Polygon( 

251 section[..., plane], 

252 facecolor=facecolor, 

253 edgecolor="none", 

254 zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, 

255 ) 

256 axes.add_patch(polygon) 

257 

258 if use_RGB_section_colours: 

259 image = axes.imshow( 

260 np.clip(RGB_section, 0, 1), 

261 interpolation="bilinear", 

262 extent=extent, # type: ignore 

263 clip_path=None, 

264 alpha=section_opacity, 

265 zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, 

266 ) 

267 image.set_clip_path(polygon) 

268 

269 settings = { 

270 "axes": axes, 

271 "bounding_box": extent, 

272 } 

273 settings.update(kwargs) 

274 

275 return render(**settings) 

276 

277 

278@required("trimesh") 

279@override_style() 

280def plot_hull_section_contour( 

281 hull: trimesh.Trimesh, # pyright: ignore # noqa: F821 

282 model: LiteralColourspaceModel | str = "CIE xyY", 

283 axis: Literal["+z", "+x", "+y"] | str = "+z", 

284 origin: float = 0.5, 

285 normalise: bool = True, 

286 contour_colours: ArrayLike | str | None = None, 

287 contour_opacity: float = 1, 

288 convert_kwargs: dict | None = None, 

289 **kwargs: Any, 

290) -> Tuple[Figure, Axes]: 

291 """ 

292 Plot the section contour of the specified *trimesh* hull along the 

293 specified axis and origin. 

294 

295 Parameters 

296 ---------- 

297 hull 

298 *Trimesh* hull. 

299 model 

300 Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute 

301 for the list of supported colourspace models. 

302 axis 

303 Axis the hull section will be normal to. 

304 origin 

305 Coordinate along ``axis`` at which to plot the hull section. 

306 normalise 

307 Whether to normalise ``axis`` to the extent of the hull along it. 

308 contour_colours 

309 Colours of the hull section contour, if ``contour_colours`` is set 

310 to *RGB*, the colours will be computed using the corresponding 

311 coordinates. 

312 contour_opacity 

313 Opacity of the hull section contour. 

314 convert_kwargs 

315 Keyword arguments for the :func:`colour.convert` definition. 

316 

317 Other Parameters 

318 ---------------- 

319 kwargs 

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

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

322 See the documentation of the previously listed definitions. 

323 

324 Returns 

325 ------- 

326 :class:`tuple` 

327 Current figure and axes. 

328 

329 Examples 

330 -------- 

331 >>> from colour.models import RGB_COLOURSPACE_sRGB 

332 >>> from colour.utilities import is_trimesh_installed 

333 >>> vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64) 

334 >>> XYZ_vertices = RGB_to_XYZ(vertices["position"] + 0.5, RGB_COLOURSPACE_sRGB) 

335 >>> if is_trimesh_installed: 

336 ... from trimesh import Trimesh 

337 ... 

338 ... hull = Trimesh(XYZ_vertices, faces, process=False) 

339 ... plot_hull_section_contour(hull, contour_colours="RGB") 

340 ... # doctest: +ELLIPSIS 

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

342 

343 .. image:: ../_static/Plotting_Plot_Hull_Section_Contour.png 

344 :align: center 

345 :alt: plot_hull_section_contour 

346 """ 

347 

348 hull = hull.copy() 

349 

350 contour_colours = optional(contour_colours, CONSTANTS_COLOUR_STYLE.colour.dark) 

351 

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

353 settings.update(kwargs) 

354 

355 _figure, axes = artist(**settings) 

356 

357 convert_kwargs = optional(convert_kwargs, {}) 

358 

359 # Luminance / Lightness is re-ordered along "z-up" axis. 

360 with suppress_warnings(python_warnings=True): 

361 ijk_vertices = colourspace_model_axis_reorder( 

362 convert(hull.vertices, "CIE XYZ", model, **convert_kwargs), model 

363 ) 

364 ijk_vertices = np.nan_to_num(ijk_vertices) 

365 ijk_vertices = colourspace_model_to_reference(ijk_vertices, model) 

366 

367 hull.vertices = ijk_vertices 

368 

369 plane = MAPPING_AXIS_TO_PLANE[axis] 

370 

371 padding = 0.1 * np.mean(colourspace_model_to_reference(np.ones(3), model)) 

372 min_x = np.min(ijk_vertices[..., plane[0]]) - padding 

373 max_x = np.max(ijk_vertices[..., plane[0]]) + padding 

374 min_y = np.min(ijk_vertices[..., plane[1]]) - padding 

375 max_y = np.max(ijk_vertices[..., plane[1]]) + padding 

376 extent = (min_x, max_x, min_y, max_y) 

377 

378 use_RGB_contour_colours = str(contour_colours).upper() == "RGB" 

379 section = hull_section(hull, axis, origin, normalise) 

380 if use_RGB_contour_colours: 

381 ijk_section = section / colourspace_model_to_reference(np.ones(3), model) 

382 XYZ_section = convert( 

383 colourspace_model_axis_reorder(ijk_section, model, "Inverse"), 

384 model, 

385 "CIE XYZ", 

386 **convert_kwargs, 

387 ) 

388 contour_colours = np.clip(XYZ_to_plotting_colourspace(XYZ_section), 0, 1) 

389 

390 section = np.reshape(section[..., plane], (-1, 1, 2)) 

391 line_collection = LineCollection( 

392 np.concatenate([section[:-1], section[1:]], axis=1), # pyright: ignore 

393 colors=contour_colours, 

394 alpha=contour_opacity, 

395 zorder=CONSTANTS_COLOUR_STYLE.zorder.background_line, 

396 ) 

397 axes.add_collection(line_collection) 

398 

399 settings = { 

400 "axes": axes, 

401 "bounding_box": extent, 

402 } 

403 settings.update(kwargs) 

404 

405 return render(**settings) 

406 

407 

408@required("trimesh") 

409@override_style() 

410def plot_visible_spectrum_section( 

411 cmfs: ( 

412 MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] 

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

414 illuminant: SpectralDistribution | str = "D65", 

415 model: LiteralColourspaceModel | str = "CIE xyY", 

416 axis: Literal["+z", "+x", "+y"] | str = "+z", 

417 origin: float = 0.5, 

418 normalise: bool = True, 

419 show_section_colours: bool = True, 

420 show_section_contour: bool = True, 

421 **kwargs: Any, 

422) -> Tuple[Figure, Axes]: 

423 """ 

424 Plot the visible spectrum volume section colours along the specified axis 

425 and origin. 

426 

427 The visible spectrum volume represents the *Rösch-MacAdam* colour solid. 

428 

429 Parameters 

430 ---------- 

431 cmfs 

432 Standard observer colour matching functions, default to the 

433 *CIE 1931 2 Degree Standard Observer*. ``cmfs`` can be of any type 

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

435 definition. 

436 illuminant 

437 Illuminant spectral distribution, default to *CIE Illuminant D65*. 

438 ``illuminant`` can be of any type or form supported by the 

439 :func:`colour.plotting.common.filter_illuminants` definition. 

440 model 

441 Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute 

442 for the list of supported colourspace models. 

443 axis 

444 Axis the hull section will be normal to. 

445 origin 

446 Coordinate along ``axis`` at which to plot the hull section. 

447 normalise 

448 Whether to normalise ``axis`` to the extent of the hull along it. 

449 show_section_colours 

450 Whether to show the hull section colours. 

451 show_section_contour 

452 Whether to show the hull section contour. 

453 

454 Other Parameters 

455 ---------------- 

456 kwargs 

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

458 :func:`colour.plotting.render`, 

459 :func:`colour.plotting.section.plot_hull_section_colours` 

460 :func:`colour.plotting.section.plot_hull_section_contour`}, 

461 See the documentation of the previously listed definitions. 

462 

463 Returns 

464 ------- 

465 :class:`tuple` 

466 Current figure and axes. 

467 

468 Examples 

469 -------- 

470 >>> from colour.utilities import is_trimesh_installed 

471 >>> if is_trimesh_installed: 

472 ... plot_visible_spectrum_section(section_colours="RGB", section_opacity=0.15) 

473 ... # doctest: +ELLIPSIS 

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

475 

476 .. image:: ../_static/Plotting_Plot_Visible_Spectrum_Section.png 

477 :align: center 

478 :alt: plot_visible_spectrum_section 

479 """ 

480 

481 import trimesh.convex # noqa: PLC0415 

482 from trimesh import Trimesh # noqa: PLC0415 

483 

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

485 settings.update(kwargs) 

486 

487 _figure, axes = artist(**settings) 

488 

489 cmfs = cast( 

490 "MultiSpectralDistributions", 

491 reshape_msds( 

492 first_item(filter_cmfs(cmfs).values()), 

493 SpectralShape(360, 780, 1), 

494 copy=False, 

495 ), 

496 ) 

497 illuminant = cast( 

498 "SpectralDistribution", 

499 first_item(filter_illuminants(illuminant).values()), 

500 ) 

501 

502 vertices = solid_RoschMacAdam( 

503 cmfs, 

504 illuminant, 

505 point_order="Pulse Wave Width", 

506 filter_jagged_points=True, 

507 ) 

508 mesh = Trimesh(vertices) 

509 hull = trimesh.convex.convex_hull(mesh) 

510 

511 if show_section_colours: 

512 settings = {"axes": axes} 

513 settings.update(kwargs) 

514 settings["show"] = False 

515 

516 plot_hull_section_colours(hull, model, axis, origin, normalise, **settings) 

517 

518 if show_section_contour: 

519 settings = {"axes": axes} 

520 settings.update(kwargs) 

521 settings["show"] = False 

522 

523 plot_hull_section_contour(hull, model, axis, origin, normalise, **settings) 

524 

525 title = ( 

526 f"Visible Spectrum Section - " 

527 f"{f'{origin * 100}%' if normalise else origin} - " 

528 f"{model} - " 

529 f"{cmfs.display_name}" 

530 ) 

531 

532 plane = MAPPING_AXIS_TO_PLANE[axis] 

533 

534 labels = np.array(COLOURSPACE_MODELS_AXIS_LABELS[model])[ 

535 as_int_array(colourspace_model_axis_reorder([0, 1, 2], model)) 

536 ] 

537 x_label, y_label = labels[plane[0]], labels[plane[1]] 

538 

539 settings.update( 

540 { 

541 "axes": axes, 

542 "show": True, 

543 "title": title, 

544 "x_label": x_label, 

545 "y_label": y_label, 

546 } 

547 ) 

548 settings.update(kwargs) 

549 

550 return render(**settings) 

551 

552 

553@required("trimesh") 

554@override_style() 

555def plot_RGB_colourspace_section( 

556 colourspace: ( 

557 RGB_Colourspace 

558 | LiteralRGBColourspace 

559 | str 

560 | Sequence[RGB_Colourspace | LiteralRGBColourspace | str] 

561 ), 

562 model: LiteralColourspaceModel | str = "CIE xyY", 

563 axis: Literal["+z", "+x", "+y"] | str = "+z", 

564 origin: float = 0.5, 

565 normalise: bool = True, 

566 size: float = 1.0, 

567 show_section_colours: bool = True, 

568 show_section_contour: bool = True, 

569 segments: int = 64, 

570 **kwargs: Any, 

571) -> Tuple[Figure, Axes]: 

572 """ 

573 Plot the specified *RGB* colourspace section colours along the 

574 specified axis and origin. 

575 

576 Parameters 

577 ---------- 

578 colourspace 

579 *RGB* colourspace of the *RGB* array. ``colourspace`` can be of 

580 any type or form supported by the 

581 :func:`colour.plotting.common.filter_RGB_colourspaces` 

582 definition. 

583 model 

584 Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` 

585 attribute for the list of supported colourspace models. 

586 axis 

587 Axis the hull section will be normal to. 

588 origin 

589 Coordinate along ``axis`` at which to plot the hull section. 

590 normalise 

591 Whether to normalise ``axis`` to the extent of the hull along 

592 it. 

593 size: 

594 Size of the underlying *RGB* colourspace cube; used for plotting 

595 HDR related sections. 

596 show_section_colours 

597 Whether to show the hull section colours. 

598 show_section_contour 

599 Whether to show the hull section contour. 

600 segments 

601 Edge segments count for the *RGB* colourspace cube. 

602 

603 Other Parameters 

604 ---------------- 

605 kwargs 

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

607 :func:`colour.plotting.render`, 

608 :func:`colour.plotting.section.plot_hull_section_colours` 

609 :func:`colour.plotting.section.plot_hull_section_contour`}, 

610 See the documentation of the previously listed definitions. 

611 

612 Returns 

613 ------- 

614 :class:`tuple` 

615 Current figure and axes. 

616 

617 Examples 

618 -------- 

619 >>> from colour.utilities import is_trimesh_installed 

620 >>> if is_trimesh_installed: 

621 ... plot_RGB_colourspace_section( 

622 ... "sRGB", section_colours="RGB", section_opacity=0.15 

623 ... ) 

624 ... # doctest: +ELLIPSIS 

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

626 

627 .. image:: ../_static/Plotting_Plot_RGB_Colourspace_Section.png 

628 :align: center 

629 :alt: plot_RGB_colourspace_section 

630 """ 

631 

632 from trimesh import Trimesh # noqa: PLC0415 

633 

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

635 settings.update(kwargs) 

636 

637 _figure, axes = artist(**settings) 

638 

639 colourspace = cast( 

640 "RGB_Colourspace", 

641 first_item(filter_RGB_colourspaces(colourspace).values()), 

642 ) 

643 

644 vertices, faces, _outline = primitive_cube(1, 1, 1, segments, segments, segments) 

645 XYZ_vertices = RGB_to_XYZ((vertices["position"] + 0.5) * size, colourspace) 

646 hull = Trimesh(XYZ_vertices, faces, process=False) 

647 

648 if show_section_colours: 

649 settings = {"axes": axes} 

650 settings.update(kwargs) 

651 settings["show"] = False 

652 

653 plot_hull_section_colours(hull, model, axis, origin, normalise, **settings) 

654 

655 if show_section_contour: 

656 settings = {"axes": axes} 

657 settings.update(kwargs) 

658 settings["show"] = False 

659 

660 plot_hull_section_contour(hull, model, axis, origin, normalise, **settings) 

661 

662 title = ( 

663 f"{colourspace.name} Section - " 

664 f"{f'{origin * 100}%' if normalise else origin} - " 

665 f"{model}" 

666 ) 

667 

668 plane = MAPPING_AXIS_TO_PLANE[axis] 

669 

670 labels = np.array(COLOURSPACE_MODELS_AXIS_LABELS[model])[ 

671 as_int_array(colourspace_model_axis_reorder([0, 1, 2], model)) 

672 ] 

673 x_label, y_label = labels[plane[0]], labels[plane[1]] 

674 

675 settings.update( 

676 { 

677 "axes": axes, 

678 "show": True, 

679 "title": title, 

680 "x_label": x_label, 

681 "y_label": y_label, 

682 } 

683 ) 

684 settings.update(kwargs) 

685 

686 return render(**settings)