babelplot 2025.10__tar.gz → 2025.12__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {babelplot-2025.10 → babelplot-2025.12}/PKG-INFO +2 -3
  2. {babelplot-2025.10 → babelplot-2025.12}/README.rst +1 -1
  3. babelplot-2025.10/package/babelplot/api/definition.py → babelplot-2025.12/package/babelplot/api/type.py +3 -2
  4. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/backend/catalog/bokeh_.py +71 -15
  5. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/backend/catalog/matplotlib_.py +56 -36
  6. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/backend/catalog/plotly_.py +47 -27
  7. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/backend/catalog/vedo_.py +104 -28
  8. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/backend/helper/creation.py +5 -0
  9. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/backend/helper/validation.py +5 -0
  10. babelplot-2025.10/package/babelplot/api/help.py → babelplot-2025.12/package/babelplot/constant/backend.py +6 -2
  11. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/task/color.py +53 -9
  12. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/task/help.py +5 -0
  13. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/task/plotting.py +3 -4
  14. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/task/showing.py +4 -4
  15. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/type/backend.py +45 -22
  16. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/type/ffp_base.py +2 -3
  17. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/type/figure.py +2 -2
  18. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/type/frame.py +4 -0
  19. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/type/plot.py +24 -0
  20. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/version.py +1 -1
  21. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot.egg-info/PKG-INFO +2 -3
  22. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot.egg-info/SOURCES.txt +1 -3
  23. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot.egg-info/requires.txt +0 -1
  24. {babelplot-2025.10 → babelplot-2025.12}/requirements.txt +0 -1
  25. babelplot-2025.10/package/babelplot/api/backend.py +0 -54
  26. babelplot-2025.10/package/babelplot/constant/backend.py +0 -65
  27. {babelplot-2025.10 → babelplot-2025.12}/MANIFEST.in +0 -0
  28. {babelplot-2025.10 → babelplot-2025.12}/README-COPYRIGHT-utf8.txt +0 -0
  29. {babelplot-2025.10 → babelplot-2025.12}/README-LICENCE-utf8.txt +0 -0
  30. {babelplot-2025.10 → babelplot-2025.12}/documentation/wiki/description.asciidoc +0 -0
  31. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/__init__.py +0 -0
  32. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/constant/path.py +0 -0
  33. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/constant/project.py +0 -0
  34. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/extension/enum_.py +0 -0
  35. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/extension/function.py +0 -0
  36. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/runtime/backends.py +0 -0
  37. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/task/instantiation.py +0 -0
  38. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/type/dimension.py +0 -0
  39. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/type/plot_function.py +0 -0
  40. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/type/plot_type.py +0 -0
  41. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot/type/translator.py +0 -0
  42. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot.egg-info/dependency_links.txt +0 -0
  43. {babelplot-2025.10 → babelplot-2025.12}/package/babelplot.egg-info/top_level.txt +0 -0
  44. {babelplot-2025.10 → babelplot-2025.12}/pyproject.toml +0 -0
  45. {babelplot-2025.10 → babelplot-2025.12}/setup.cfg +0 -0
  46. {babelplot-2025.10 → babelplot-2025.12}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: babelplot
3
- Version: 2025.10
3
+ Version: 2025.12
4
4
  Summary: A Meta Plotting Library That Speaks Several Backends
5
5
  Home-page: https://src.koda.cnrs.fr/eric.debreuve/babelplot/
6
6
  Author: Eric Debreuve
@@ -17,7 +17,6 @@ Classifier: Development Status :: 3 - Alpha
17
17
  Requires-Python: >=3.11
18
18
  Description-Content-Type: text/x-rst
19
19
  Requires-Dist: numpy
20
- Requires-Dist: PyQt6
21
20
  Requires-Dist: scikit-image
22
21
  Dynamic: author
23
22
  Dynamic: author-email
@@ -47,7 +46,7 @@ Dynamic: summary
47
46
  .. |DOCUMENTATION_URL| replace:: https://src.koda.cnrs.fr/eric.debreuve/babelplot/-/wikis/home
48
47
  .. _DOCUMENTATION_URL: https://src.koda.cnrs.fr/eric.debreuve/babelplot/-/wikis/home
49
48
 
50
- .. |DEPENDENCIES_MANDATORY| replace:: numpy, PyQt6, scikit-image
49
+ .. |DEPENDENCIES_MANDATORY| replace:: numpy, scikit-image
51
50
  .. |DEPENDENCIES_OPTIONAL| replace:: bokeh, matplotlib, plotly, vedo
52
51
 
53
52
 
@@ -13,7 +13,7 @@
13
13
  .. |DOCUMENTATION_URL| replace:: https://src.koda.cnrs.fr/eric.debreuve/babelplot/-/wikis/home
14
14
  .. _DOCUMENTATION_URL: https://src.koda.cnrs.fr/eric.debreuve/babelplot/-/wikis/home
15
15
 
16
- .. |DEPENDENCIES_MANDATORY| replace:: numpy, PyQt6, scikit-image
16
+ .. |DEPENDENCIES_MANDATORY| replace:: numpy, scikit-image
17
17
  .. |DEPENDENCIES_OPTIONAL| replace:: bokeh, matplotlib, plotly, vedo
18
18
 
19
19
 
@@ -4,8 +4,9 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2022
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- from babelplot.type.dimension import dim_e # noqa
8
- from babelplot.type.plot_type import plot_e # noqa
7
+ from babelplot.type.figure import figure_t # noqa
8
+ from babelplot.type.frame import frame_t # noqa
9
+ from babelplot.type.plot import plot_t # noqa
9
10
 
10
11
  """
11
12
  COPYRIGHT NOTICE
@@ -9,6 +9,7 @@ import gzip
9
9
  import typing as h
10
10
  from multiprocessing import Process as process_t
11
11
 
12
+ import numpy as nmpy
12
13
  from babelplot.task.plotting import NewPlotFunctionsTemplate, SetDefaultPlotFunction
13
14
  from babelplot.task.showing import ShowHTMLPlotWithPyQt
14
15
  from babelplot.type.dimension import dim_e
@@ -16,7 +17,7 @@ from babelplot.type.figure import figure_t as base_figure_t
16
17
  from babelplot.type.frame import frame_t as base_frame_t
17
18
  from babelplot.type.plot import plot_t as base_plot_t
18
19
  from babelplot.type.plot_function import plot_function_h
19
- from babelplot.type.plot_type import plot_e
20
+ from babelplot.type.plot_type import plot_e, plot_type_h
20
21
  from bokeh.embed import file_html as HTMLofBackendContent # noqa
21
22
  from bokeh.layouts import column as NewBackendColLayout # noqa
22
23
  from bokeh.layouts import grid as NewBackendGridLayout # noqa
@@ -27,6 +28,8 @@ from bokeh.resources import INLINE # noqa
27
28
 
28
29
  NAME = "bokeh"
29
30
 
31
+ array_t = nmpy.ndarray
32
+
30
33
 
31
34
  class backend_figure_t: ...
32
35
 
@@ -43,6 +46,7 @@ class frame_t(base_frame_t):
43
46
  plot_function: plot_function_h,
44
47
  *args,
45
48
  title: str | None = None, # If _, then it is swallowed by kwargs!
49
+ type_: tuple[plot_type_h | plot_function_h, int],
46
50
  **kwargs,
47
51
  ) -> plot_t:
48
52
  """"""
@@ -59,9 +63,9 @@ class figure_t(base_figure_t):
59
63
 
60
64
  layout: h.Any = d.field(init=False, default=None)
61
65
 
62
- def _NewBackendFigure(self, *args, **kwargs) -> backend_figure_t:
66
+ def _NewBackendFigure(self, *args, **kwargs) -> None:
63
67
  """"""
64
- return backend_figure_t() # self.raw is never used.
68
+ return None
65
69
 
66
70
  def _NewFrame(
67
71
  self,
@@ -85,7 +89,7 @@ class figure_t(base_figure_t):
85
89
  # arranged_frames must be composed of list since Bokeh does not support tuples
86
90
  # here!
87
91
  n_rows, n_cols = self.shape
88
- arranged_frames = [n_cols * [None] for _ in range(n_rows)]
92
+ arranged_frames: list[list[h.Any]] = [n_cols * [None] for _ in range(n_rows)]
89
93
  for frame, (row, col) in zip(self.frames, self.locations):
90
94
  raw = frame.raw
91
95
  if raw.renderers.__len__() == 0:
@@ -153,18 +157,15 @@ def _DefaultFunction(type_: plot_e, frame_dim: int, /) -> plot_function_h:
153
157
  """"""
154
158
 
155
159
  def Actual(frame: backend_frame_t, *args, **kwargs) -> backend_plot_t:
156
- """"""
157
- args = ", ".join(map(lambda _: type(_).__name__, args))
158
- kwargs = ", ".join(
159
- f"{_key}={type(_vle).__name__}" for _key, _vle in kwargs.items()
160
- )
161
-
160
+ #
162
161
  return backend_frame_t.text(
163
162
  frame,
164
163
  x=(0,),
165
164
  y=(0,),
166
165
  text=(
167
- f"Unhandled Plot Request\n{type_.name}.{frame_dim}\nargs: {args}\nkwargs: {kwargs}",
166
+ plot_t.UnhandledRequestMessage(
167
+ type_, *args, frame_dim=frame_dim, **kwargs
168
+ ),
168
169
  ),
169
170
  text_font_size="30px",
170
171
  text_align="center",
@@ -174,16 +175,71 @@ def _DefaultFunction(type_: plot_e, frame_dim: int, /) -> plot_function_h:
174
175
  return Actual
175
176
 
176
177
 
178
+ def _BarH(frame: backend_frame_t, *args, **kwargs) -> backend_plot_t:
179
+ """"""
180
+ if args.__len__() == 1:
181
+ counts = args[0]
182
+ positions = tuple(range(counts.__len__()))
183
+ else:
184
+ positions, counts = args
185
+
186
+ return frame.hbar(y=positions, right=counts, **kwargs)
187
+
188
+
189
+ def _BarV(frame: backend_frame_t, *args, **kwargs) -> backend_plot_t:
190
+ """"""
191
+ if args.__len__() == 1:
192
+ counts = args[0]
193
+ positions = tuple(range(counts.__len__()))
194
+ else:
195
+ positions, counts = args
196
+
197
+ return frame.vbar(x=positions, top=counts, **kwargs)
198
+
199
+
200
+ def _Image(frame: backend_frame_t, image: array_t, *args, **kwargs) -> backend_plot_t:
201
+ """"""
202
+ if nmpy.issubdtype(image.dtype, nmpy.floating):
203
+ image = nmpy.round(255.0 * (image / image.max())).astype(nmpy.uint8)
204
+
205
+ for_bokeh = nmpy.empty(image.shape[:2], dtype=nmpy.uint32)
206
+ view = for_bokeh.view(dtype=nmpy.uint8).reshape(image.shape[:2] + (4,))
207
+
208
+ if (image.ndim == 2) or (image.shape[2] != 4):
209
+ if (image.ndim == 2) or (image.shape[2] == 1):
210
+ planes = (image, image, image)
211
+ else:
212
+ planes = (image[..., 0], image[..., 1], image[..., 2])
213
+ for d_idx in range(3):
214
+ view[..., d_idx] = planes[d_idx]
215
+ view[..., 3] = nmpy.full_like(image, 255)
216
+ else:
217
+ view[...] = image[...]
218
+
219
+ frame.x_range.range_padding = frame.y_range.range_padding = 0
220
+
221
+ return frame.image_rgba(
222
+ image=(for_bokeh,), x=0, y=0, dw=image.shape[1], dh=image.shape[0]
223
+ )
224
+
225
+
177
226
  PLOTS = NewPlotFunctionsTemplate()
227
+ PLOTS[plot_e.BARH][0] = _BarH
228
+ PLOTS[plot_e.BARV][0] = _BarV
229
+ PLOTS[plot_e.IMAGE][0] = _Image
178
230
  PLOTS[plot_e.SCATTER][0] = backend_frame_t.scatter
179
231
  SetDefaultPlotFunction(PLOTS, _DefaultFunction)
180
232
 
181
233
 
182
234
  TRANSLATIONS = {
183
- "color_face": "fill_color",
184
- "opacity": "alpha",
185
- ("AddFrame", "azimuth"): None,
186
- ("AddFrame", "elevation"): None,
235
+ None: {
236
+ "color_face": "fill_color",
237
+ "opacity": "alpha",
238
+ },
239
+ "_NewFrame": {
240
+ "azimuth": None,
241
+ "elevation": None,
242
+ },
187
243
  }
188
244
 
189
245
 
@@ -108,6 +108,7 @@ class frame_t(base_frame_t):
108
108
  plot_function: plot_function_h,
109
109
  *args,
110
110
  title: str | None = None, # /!\ If _, then it is swallowed by kwargs!
111
+ type_: tuple[plot_type_h | plot_function_h, int],
111
112
  **kwargs,
112
113
  ) -> plot_t:
113
114
  """"""
@@ -271,7 +272,7 @@ def _Arrows3(frame: backend_frame_3d_t, *args, **kwargs) -> backend_plot_t:
271
272
  return frame.quiver(x, y, z, u, v, w, **kwargs)
272
273
 
273
274
 
274
- def _Bar3(frame: backend_frame_2d_t, *args, **kwargs) -> backend_plot_t:
275
+ def _Bar3(frame: backend_frame_3d_t, *args, **kwargs) -> backend_plot_t:
275
276
  """"""
276
277
  if args.__len__() == 1:
277
278
  counts = nmpy.asarray(args[0])
@@ -319,7 +320,7 @@ def _BarV(frame: backend_frame_2d_t, *args, **kwargs) -> backend_plots_t:
319
320
  return frame.bar(positions, counts, **kwargs)
320
321
 
321
322
 
322
- def _ElevationSurface(frame: backend_frame_2d_t, *args, **kwargs) -> backend_plot_t:
323
+ def _ElevationSurface(frame: backend_frame_3d_t, *args, **kwargs) -> backend_plot_t:
323
324
  """"""
324
325
  if args.__len__() == 1:
325
326
  elevation = args[0]
@@ -347,7 +348,7 @@ def _IsoContour(frame: backend_frame_2d_t, *args, **kwargs) -> backend_plot_t:
347
348
  return output
348
349
 
349
350
 
350
- def _IsoSurface(frame: backend_frame_2d_t, *args, **kwargs) -> backend_plot_t:
351
+ def _IsoSurface(frame: backend_frame_3d_t, *args, **kwargs) -> backend_plot_t:
351
352
  """"""
352
353
  if "step_size" in kwargs:
353
354
  mc_kwargs = {"step_size": kwargs["step_size"]}
@@ -362,9 +363,13 @@ def _IsoSurface(frame: backend_frame_2d_t, *args, **kwargs) -> backend_plot_t:
362
363
 
363
364
 
364
365
  def _Mesh(
365
- frame: backend_frame_2d_t, triangles: array_t, vertices: array_t, *_, **kwargs
366
+ frame: backend_frame_3d_t, triangles: array_t, vertices: array_t, *_, **kwargs
366
367
  ) -> backend_plot_t:
367
- """"""
368
+ """
369
+ Note: As of 3.9.2, plot_trisurf does not support custom triangle colors yet (see
370
+ https://github.com/matplotlib/matplotlib/blob/v3.9.2/lib/mpl_toolkits/mplot3d/axes3d.py,
371
+ line 2291).
372
+ """
368
373
  return frame.plot_trisurf(
369
374
  vertices[:, 0], vertices[:, 1], triangles, vertices[:, 2], **kwargs
370
375
  )
@@ -413,37 +418,52 @@ PLOTS[plot_e.TEXT][1] = _Text3
413
418
 
414
419
 
415
420
  TRANSLATIONS = {
416
- "color": "c",
417
- "color_edge": "edgecolors",
418
- "color_face": "facecolors",
419
- "color_max": "vmax",
420
- "color_min": "vmin",
421
- "color_scaling": "norm",
422
- "colormap": "cmap",
423
- "depth_shade": "depthshade",
424
- "opacity": "alpha",
425
- "plot_non_finite": "plotnonfinite",
426
- "size": "s",
427
- "width_edge": "linewidths",
428
- ("AddFrame", "azimuth"): "azim",
429
- ("AddFrame", "elevation"): "elev",
430
- (_Arrows2, "color"): "color",
431
- (_Arrows3, "color"): "color",
432
- (_Bar3, "color"): "color",
433
- (_BarH, "color"): "color",
434
- (_BarH, "offset"): "left",
435
- (_BarV, "color"): "color",
436
- (_BarV, "offset"): "bottom",
437
- (_ElevationSurface, "color_face"): "color",
438
- (_IsoContour, "color"): "colors",
439
- (_IsoSurface, "color_face"): "color",
440
- (_Mesh, "color_face"): ("color", lambda _: NewConvertedColor(_, "hex", 0)),
441
- (_Polygon, "color_edge"): "edgecolor",
442
- (_Polygon, "color_face"): "facecolor",
443
- (backend_frame_2d_t.pie, "color"): "colors",
444
- (backend_frame_2d_t.quiver, "color"): "color",
445
- (backend_frame_3d_t.quiver, "color"): "colors",
446
- (backend_frame_3d_t.scatter, 2): "zs",
421
+ None: {
422
+ "color": "c",
423
+ "color_edge": "edgecolors",
424
+ "color_face": "facecolors",
425
+ "color_max": "vmax",
426
+ "color_min": "vmin",
427
+ "color_scaling": "norm",
428
+ "colormap": "cmap",
429
+ "depth_shade": "depthshade",
430
+ "opacity": "alpha",
431
+ "plot_non_finite": "plotnonfinite",
432
+ "size": "s",
433
+ "width_edge": "linewidths",
434
+ },
435
+ "_NewFrame": {
436
+ "azimuth": "azim",
437
+ "elevation": "elev",
438
+ },
439
+ _Arrows2: {"c": "color"},
440
+ _Arrows3: {"c": "color"},
441
+ _Bar3: {"c": "color"},
442
+ _BarH: {
443
+ "c": "color",
444
+ "offset": "left",
445
+ },
446
+ _BarV: {
447
+ "c": "color",
448
+ "offset": "bottom",
449
+ },
450
+ _ElevationSurface: {"facecolors": "color"},
451
+ _IsoContour: {"c": "colors"},
452
+ _IsoSurface: {"facecolors": "color"},
453
+ _Mesh: {
454
+ "facecolors": (
455
+ "color",
456
+ lambda _: NewConvertedColor(_, "hex", sequence_or_reduction=0),
457
+ )
458
+ },
459
+ _Polygon: {
460
+ "edgecolors": "edgecolor",
461
+ "facecolors": "facecolor",
462
+ },
463
+ backend_frame_2d_t.pie: {"c": "colors"},
464
+ backend_frame_2d_t.quiver: {"c": "color"},
465
+ backend_frame_3d_t.quiver: {"c": "colors"},
466
+ backend_frame_3d_t.scatter: {2: "zs"},
447
467
  }
448
468
 
449
469
  """
@@ -13,6 +13,7 @@ from pathlib import Path as path_t
13
13
  import numpy as nmpy
14
14
  import plotly.figure_factory as fcry # noqa
15
15
  import plotly.graph_objects as plly # noqa
16
+ from babelplot.task.color import NColorsAndFormat
16
17
  from babelplot.task.plotting import NewPlotFunctionsTemplate
17
18
  from babelplot.task.showing import ShowHTMLPlotWithPyQt
18
19
  from babelplot.type.dimension import dim_e
@@ -20,8 +21,8 @@ from babelplot.type.figure import figure_t as base_figure_t
20
21
  from babelplot.type.frame import frame_t as base_frame_t
21
22
  from babelplot.type.plot import plot_t as base_plot_t
22
23
  from babelplot.type.plot_function import plot_function_h
23
- from babelplot.type.plot_type import plot_e
24
- from plotly.basedatatypes import BasePlotlyType as backend_plot_t # noqa
24
+ from babelplot.type.plot_type import plot_e, plot_type_h
25
+ from plotly.basedatatypes import BaseTraceType as backend_plot_t # noqa
25
26
  from plotly.graph_objects import Figure as backend_figure_t # noqa
26
27
  from plotly.subplots import make_subplots as NewMultiAxesFigure # noqa
27
28
 
@@ -53,6 +54,7 @@ class frame_t(base_frame_t):
53
54
  plot_function: plot_function_h,
54
55
  *args,
55
56
  title: str | None = None, # If _, then it is swallowed by kwargs!
57
+ type_: tuple[plot_type_h | plot_function_h, int],
56
58
  **kwargs,
57
59
  ) -> plot_t:
58
60
  """"""
@@ -94,7 +96,9 @@ class figure_t(base_figure_t):
94
96
  n_rows, n_cols = self.shape
95
97
  if (n_rows > 1) or (n_cols > 1):
96
98
  frame_titles = (n_rows * n_cols) * [""]
97
- arranged_plots = [n_cols * [None] for _ in range(n_rows)]
99
+ arranged_plots: list[list[list[backend_plot_t] | None]] = [
100
+ n_cols * [None] for _ in range(n_rows)
101
+ ]
98
102
  for frame, (row, col) in zip(self.frames, self.locations):
99
103
  if frame.title is not None:
100
104
  frame_titles[row * n_cols + col] = frame.title
@@ -106,6 +110,7 @@ class figure_t(base_figure_t):
106
110
  frame_types = [n_cols * [{}] for _ in range(n_rows)]
107
111
  for row, plot_row in enumerate(arranged_plots):
108
112
  for col, plot_cell in enumerate(plot_row):
113
+ # Note: Subclasses of backend_plot_t have a type property.
109
114
  frame_types[row][col] = {"type": plot_cell[0].type}
110
115
 
111
116
  raw = NewMultiAxesFigure(
@@ -334,7 +339,7 @@ def _Image(image: array_t, *_, **kwargs) -> backend_plot_t:
334
339
 
335
340
  def _IsoContour(*args, **kwargs) -> backend_plot_t:
336
341
  """"""
337
- parameters = {
342
+ parameters: dict[str, h.Any] = {
338
343
  "contours_coloring": "lines",
339
344
  "line_width": 2,
340
345
  }
@@ -388,7 +393,10 @@ def _IsoSurface(values: array_t, value: float, *args, **kwargs) -> backend_plot_
388
393
 
389
394
 
390
395
  def _Mesh(triangles: array_t, vertices: array_t, *_, **kwargs) -> backend_plot_t:
391
- """"""
396
+ """
397
+ Note: Mesh3d does not support setting edge colors. It can be done indirectly by
398
+ adding a Scatter3d plot.
399
+ """
392
400
  return plly.Mesh3d(
393
401
  x=vertices[:, 0],
394
402
  y=vertices[:, 1],
@@ -491,6 +499,19 @@ def _ApplyGlobalTranslation(
491
499
  if (value := kwargs.get(passed)) is not None:
492
500
  marker[wanted] = value
493
501
  del kwargs[passed]
502
+ if "color" in marker:
503
+ n_colors, _ = NColorsAndFormat(marker["color"])
504
+ if n_colors > 1:
505
+ # TODO: Currently, works only for RGB color format.
506
+ colorscale = [
507
+ [
508
+ _idx / (n_colors - 1),
509
+ f"rgb({round(255.0*_red)}, {round(255.0*_grn)}, {round(255.0*_blu)})",
510
+ ]
511
+ for _idx, (_red, _grn, _blu) in enumerate(marker["color"])
512
+ ]
513
+ marker["colorscale"] = colorscale
514
+ marker["color"] = tuple(_[0] for _ in colorscale)
494
515
  if who_s_asking is _Scatter3:
495
516
  # /!\\ Passing one size per sample makes all of them disappear.
496
517
  if ("size" in marker) and not isinstance(marker["size"], int | float):
@@ -501,11 +522,11 @@ def _ApplyGlobalTranslation(
501
522
 
502
523
  kwargs["marker"] = marker
503
524
  #
504
- elif who_s_asking in (_Arrows2, _Polyline2, _Polyline3):
525
+ elif who_s_asking in (_Arrows2, _Polygon, _Polyline2, _Polyline3):
505
526
  line = {}
506
527
  for passed, wanted in zip(
507
- ("color", "opacity", "line_width", "line_style"),
508
- ("color", "opacity", "width", "symbol"),
528
+ ("color", "color_edge", "opacity", "line_width", "line_style"),
529
+ ("color", "color", "opacity", "width", "symbol"),
509
530
  ):
510
531
  if (value := kwargs.get(passed)) is not None:
511
532
  line[wanted] = value
@@ -524,25 +545,24 @@ def _ApplyGlobalTranslation(
524
545
 
525
546
 
526
547
  TRANSLATIONS = {
527
- "color_face": "surfacecolor",
528
- (_Arrows3, "color"): None,
529
- (_BarH, "color"): None,
530
- (_BarV, "color"): None,
531
- (_ElevationSurface, "color_edge"): None,
532
- (_ElevationSurface, "color_face"): None,
533
- (_ElevationSurface, "width_edge"): None,
534
- (_Image, "colormap"): None,
535
- (_IsoContour, "color"): "fillcolor",
536
- (_IsoSurface, "color_edge"): None,
537
- (_IsoSurface, "color_face"): None,
538
- (_IsoSurface, "step_size"): None,
539
- (_IsoSurface, "width_edge"): None,
540
- (_Mesh, "color_edge"): None,
541
- (_Mesh, "color_face"): "facecolor",
542
- (_Mesh, "width_edge"): None,
543
- (_Polygon, "color_edge"): None,
544
- (_Polygon, "color_face"): "fillcolor",
545
- None: _ApplyGlobalTranslation,
548
+ None: {
549
+ "color_face": "surfacecolor",
550
+ None: _ApplyGlobalTranslation,
551
+ },
552
+ _Arrows3: {"color": None},
553
+ _BarH: {"color": None},
554
+ _BarV: {"color": None},
555
+ _ElevationSurface: {"color_edge": None, "surfacecolor": None, "width_edge": None},
556
+ _Image: {"colormap": None},
557
+ _IsoContour: {"color": "fillcolor"},
558
+ _IsoSurface: {
559
+ "color_edge": None,
560
+ "surfacecolor": None,
561
+ "step_size": None,
562
+ "width_edge": None,
563
+ },
564
+ _Mesh: {"color_edge": None, "surfacecolor": "facecolor", "width_edge": None},
565
+ _Polygon: {"surfacecolor": "fillcolor"},
546
566
  }
547
567
 
548
568