babelplot 2025.11__tar.gz → 2025.13__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 (44) hide show
  1. {babelplot-2025.11 → babelplot-2025.13}/PKG-INFO +1 -1
  2. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/backend/catalog/bokeh_.py +71 -15
  3. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/backend/catalog/matplotlib_.py +57 -37
  4. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/backend/catalog/plotly_.py +46 -27
  5. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/backend/catalog/vedo_.py +105 -29
  6. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/task/plotting.py +3 -4
  7. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/type/backend.py +44 -21
  8. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/type/figure.py +2 -2
  9. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/type/frame.py +4 -0
  10. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/type/plot.py +24 -0
  11. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/version.py +1 -1
  12. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot.egg-info/PKG-INFO +1 -1
  13. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot.egg-info/SOURCES.txt +0 -1
  14. babelplot-2025.11/package/babelplot/task/color.py +0 -112
  15. {babelplot-2025.11 → babelplot-2025.13}/MANIFEST.in +0 -0
  16. {babelplot-2025.11 → babelplot-2025.13}/README-COPYRIGHT-utf8.txt +0 -0
  17. {babelplot-2025.11 → babelplot-2025.13}/README-LICENCE-utf8.txt +0 -0
  18. {babelplot-2025.11 → babelplot-2025.13}/README.rst +0 -0
  19. {babelplot-2025.11 → babelplot-2025.13}/documentation/wiki/description.asciidoc +0 -0
  20. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/__init__.py +0 -0
  21. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/api/type.py +0 -0
  22. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/backend/helper/creation.py +0 -0
  23. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/backend/helper/validation.py +0 -0
  24. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/constant/backend.py +0 -0
  25. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/constant/path.py +0 -0
  26. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/constant/project.py +0 -0
  27. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/extension/enum_.py +0 -0
  28. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/extension/function.py +0 -0
  29. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/runtime/backends.py +0 -0
  30. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/task/help.py +0 -0
  31. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/task/instantiation.py +0 -0
  32. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/task/showing.py +0 -0
  33. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/type/dimension.py +0 -0
  34. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/type/ffp_base.py +0 -0
  35. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/type/plot_function.py +0 -0
  36. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/type/plot_type.py +0 -0
  37. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot/type/translator.py +0 -0
  38. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot.egg-info/dependency_links.txt +0 -0
  39. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot.egg-info/requires.txt +0 -0
  40. {babelplot-2025.11 → babelplot-2025.13}/package/babelplot.egg-info/top_level.txt +0 -0
  41. {babelplot-2025.11 → babelplot-2025.13}/pyproject.toml +0 -0
  42. {babelplot-2025.11 → babelplot-2025.13}/requirements.txt +0 -0
  43. {babelplot-2025.11 → babelplot-2025.13}/setup.cfg +0 -0
  44. {babelplot-2025.11 → babelplot-2025.13}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: babelplot
3
- Version: 2025.11
3
+ Version: 2025.13
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
@@ -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
 
@@ -10,7 +10,7 @@ import typing as h
10
10
  import matplotlib.pyplot as pypl # noqa
11
11
  import numpy as nmpy
12
12
  import skimage.measure as msre
13
- from babelplot.task.color import NewConvertedColor
13
+ from color_spec_changer.translation import NewTranslatedColor
14
14
  from babelplot.task.plotting import NewPlotFunctionsTemplate
15
15
  from babelplot.type.dimension import dim_e
16
16
  from babelplot.type.ffp_base import backend_element_h
@@ -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 _: NewTranslatedColor(_, "hex", index_or_reduction=0)[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,8 @@ 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 color_spec_changer.analysis import NColorsAndFormat
17
+ from color_spec_changer.translation import NewTranslatedColor
16
18
  from babelplot.task.plotting import NewPlotFunctionsTemplate
17
19
  from babelplot.task.showing import ShowHTMLPlotWithPyQt
18
20
  from babelplot.type.dimension import dim_e
@@ -20,8 +22,8 @@ from babelplot.type.figure import figure_t as base_figure_t
20
22
  from babelplot.type.frame import frame_t as base_frame_t
21
23
  from babelplot.type.plot import plot_t as base_plot_t
22
24
  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
25
+ from babelplot.type.plot_type import plot_e, plot_type_h
26
+ from plotly.basedatatypes import BaseTraceType as backend_plot_t # noqa
25
27
  from plotly.graph_objects import Figure as backend_figure_t # noqa
26
28
  from plotly.subplots import make_subplots as NewMultiAxesFigure # noqa
27
29
 
@@ -53,6 +55,7 @@ class frame_t(base_frame_t):
53
55
  plot_function: plot_function_h,
54
56
  *args,
55
57
  title: str | None = None, # If _, then it is swallowed by kwargs!
58
+ type_: tuple[plot_type_h | plot_function_h, int],
56
59
  **kwargs,
57
60
  ) -> plot_t:
58
61
  """"""
@@ -94,7 +97,9 @@ class figure_t(base_figure_t):
94
97
  n_rows, n_cols = self.shape
95
98
  if (n_rows > 1) or (n_cols > 1):
96
99
  frame_titles = (n_rows * n_cols) * [""]
97
- arranged_plots = [n_cols * [None] for _ in range(n_rows)]
100
+ arranged_plots: list[list[list[backend_plot_t] | None]] = [
101
+ n_cols * [None] for _ in range(n_rows)
102
+ ]
98
103
  for frame, (row, col) in zip(self.frames, self.locations):
99
104
  if frame.title is not None:
100
105
  frame_titles[row * n_cols + col] = frame.title
@@ -106,6 +111,7 @@ class figure_t(base_figure_t):
106
111
  frame_types = [n_cols * [{}] for _ in range(n_rows)]
107
112
  for row, plot_row in enumerate(arranged_plots):
108
113
  for col, plot_cell in enumerate(plot_row):
114
+ # Note: Subclasses of backend_plot_t have a type property.
109
115
  frame_types[row][col] = {"type": plot_cell[0].type}
110
116
 
111
117
  raw = NewMultiAxesFigure(
@@ -334,7 +340,7 @@ def _Image(image: array_t, *_, **kwargs) -> backend_plot_t:
334
340
 
335
341
  def _IsoContour(*args, **kwargs) -> backend_plot_t:
336
342
  """"""
337
- parameters = {
343
+ parameters: dict[str, h.Any] = {
338
344
  "contours_coloring": "lines",
339
345
  "line_width": 2,
340
346
  }
@@ -388,7 +394,10 @@ def _IsoSurface(values: array_t, value: float, *args, **kwargs) -> backend_plot_
388
394
 
389
395
 
390
396
  def _Mesh(triangles: array_t, vertices: array_t, *_, **kwargs) -> backend_plot_t:
391
- """"""
397
+ """
398
+ Note: Mesh3d does not support setting edge colors. It can be done indirectly by
399
+ adding a Scatter3d plot.
400
+ """
392
401
  return plly.Mesh3d(
393
402
  x=vertices[:, 0],
394
403
  y=vertices[:, 1],
@@ -491,6 +500,17 @@ def _ApplyGlobalTranslation(
491
500
  if (value := kwargs.get(passed)) is not None:
492
501
  marker[wanted] = value
493
502
  del kwargs[passed]
503
+ if "color" in marker:
504
+ format_ = NColorsAndFormat(marker["color"])
505
+ n_colors = format_.n_colors
506
+ if n_colors > 1:
507
+ translated, _ = NewTranslatedColor(marker["color"], "function_rgb255")
508
+ colorscale = [
509
+ [_idx / (n_colors - 1), _clr]
510
+ for _idx, _clr in enumerate(translated)
511
+ ]
512
+ marker["colorscale"] = colorscale
513
+ marker["color"] = tuple(_[0] for _ in colorscale)
494
514
  if who_s_asking is _Scatter3:
495
515
  # /!\\ Passing one size per sample makes all of them disappear.
496
516
  if ("size" in marker) and not isinstance(marker["size"], int | float):
@@ -501,11 +521,11 @@ def _ApplyGlobalTranslation(
501
521
 
502
522
  kwargs["marker"] = marker
503
523
  #
504
- elif who_s_asking in (_Arrows2, _Polyline2, _Polyline3):
524
+ elif who_s_asking in (_Arrows2, _Polygon, _Polyline2, _Polyline3):
505
525
  line = {}
506
526
  for passed, wanted in zip(
507
- ("color", "opacity", "line_width", "line_style"),
508
- ("color", "opacity", "width", "symbol"),
527
+ ("color", "color_edge", "opacity", "line_width", "line_style"),
528
+ ("color", "color", "opacity", "width", "symbol"),
509
529
  ):
510
530
  if (value := kwargs.get(passed)) is not None:
511
531
  line[wanted] = value
@@ -524,25 +544,24 @@ def _ApplyGlobalTranslation(
524
544
 
525
545
 
526
546
  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,
547
+ None: {
548
+ "color_face": "surfacecolor",
549
+ None: _ApplyGlobalTranslation,
550
+ },
551
+ _Arrows3: {"color": None},
552
+ _BarH: {"color": None},
553
+ _BarV: {"color": None},
554
+ _ElevationSurface: {"color_edge": None, "surfacecolor": None, "width_edge": None},
555
+ _Image: {"colormap": None},
556
+ _IsoContour: {"color": "fillcolor"},
557
+ _IsoSurface: {
558
+ "color_edge": None,
559
+ "surfacecolor": None,
560
+ "step_size": None,
561
+ "width_edge": None,
562
+ },
563
+ _Mesh: {"color_edge": None, "surfacecolor": "facecolor", "width_edge": None},
564
+ _Polygon: {"surfacecolor": "fillcolor"},
546
565
  }
547
566
 
548
567
 
@@ -5,23 +5,30 @@ SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
7
  import dataclasses as d
8
+ import typing as h
8
9
  from multiprocessing import Process as process_t
9
10
 
10
- from babelplot.task.color import NewConvertedColor
11
- from babelplot.task.plotting import NewPlotFunctionsTemplate
11
+ from color_spec_changer.translation import NewTranslatedColor
12
+ from babelplot.task.plotting import NewPlotFunctionsTemplate, SetDefaultPlotFunction
12
13
  from babelplot.type.dimension import dim_e
13
14
  from babelplot.type.figure import figure_t as base_figure_t
14
15
  from babelplot.type.frame import frame_t as base_frame_t
15
16
  from babelplot.type.plot import plot_t as base_plot_t
16
17
  from babelplot.type.plot_function import plot_function_h
17
- from babelplot.type.plot_type import plot_e
18
+ from babelplot.type.plot_type import plot_e, plot_type_h
18
19
  from numpy import ndarray as array_t
19
- from vedo import Mesh as backend_plot_t # noqa
20
- from vedo import Plotter as backend_figure_t # noqa
20
+ from vedo import Mesh as backend_mesh_t # noqa
21
+ from vedo import Text2D as text_2d_t # noqa
21
22
  from vedo import Volume as volume_t # noqa
23
+ from vedo import show as BackendShow # noqa
24
+ from vedo.pyplot import plot as NewBackendPlot # noqa
22
25
 
23
26
  NAME = "vedo"
24
27
 
28
+ _MIN_HEIGHT_RATIO = 0.4
29
+
30
+ backend_plot_h = h.TypeVar("backend_plot_h")
31
+
25
32
 
26
33
  @d.dataclass(slots=True, repr=False, eq=False)
27
34
  class plot_t(base_plot_t): ...
@@ -30,33 +37,47 @@ class plot_t(base_plot_t): ...
30
37
  @d.dataclass(slots=True, repr=False, eq=False)
31
38
  class frame_t(base_frame_t):
32
39
 
40
+ @property
41
+ def backend_plots(self) -> list[backend_plot_h]:
42
+ """"""
43
+ output = []
44
+
45
+ for plot in self.plots:
46
+ if plot.title is None:
47
+ output.append(plot.raw)
48
+ else:
49
+ output.append((plot.raw, plot.title))
50
+
51
+ return output
52
+
33
53
  def _NewPlot(
34
54
  self,
35
55
  plot_function: plot_function_h,
36
56
  *args,
37
57
  title: str | None = None, # If _, then it is swallowed by kwargs!
58
+ type_: tuple[plot_type_h | plot_function_h, int],
38
59
  **kwargs,
39
60
  ) -> plot_t:
40
61
  """"""
41
- output = plot_t(
62
+ return plot_t(
42
63
  title=title,
43
64
  property=kwargs.copy(),
44
65
  backend_name=self.backend_name,
45
66
  raw=plot_function(*args, **kwargs),
46
67
  )
47
68
 
48
- if output.raw is not None:
49
- self.raw.__iadd__(output.raw)
50
-
51
- return output
52
-
53
69
 
54
70
  @d.dataclass(slots=True, repr=False, eq=False)
55
71
  class figure_t(base_figure_t):
56
72
 
57
- def _NewBackendFigure(self, *args, **kwargs) -> backend_figure_t:
73
+ @property
74
+ def backend_plots_s(self) -> list[list[backend_plot_h]]:
75
+ """"""
76
+ return [_.backend_plots for _ in self.frames]
77
+
78
+ def _NewBackendFigure(self, *args, **kwargs) -> None:
58
79
  """"""
59
- return backend_figure_t(*args, **kwargs)
80
+ return None
60
81
 
61
82
  def _NewFrame(
62
83
  self,
@@ -68,22 +89,20 @@ class figure_t(base_figure_t):
68
89
  **kwargs,
69
90
  ) -> frame_t:
70
91
  """"""
71
- output = frame_t(
92
+ return frame_t(
72
93
  title=title,
73
94
  dim=dim,
74
95
  backend_name=self.backend_name,
75
96
  )
76
97
 
77
- output.raw = self.raw
78
-
79
- return output
80
-
81
98
  def _BackendShow(self, modal: bool, /) -> None:
82
99
  """"""
83
- # Passing self.raw.show().close shows the figures one by one.
84
- BackendShow = lambda: self.raw.show().close()
100
+ _ShowAndClose = lambda: BackendShow(
101
+ self.backend_plots_s, shape=self.shape, sharecam=False
102
+ ).close()
85
103
 
86
- process = process_t(target=BackendShow)
104
+ # Passing [...].close shows the figures one by one. Using _ShowAndClose instead.
105
+ process = process_t(target=_ShowAndClose)
87
106
  process.start()
88
107
 
89
108
  if modal:
@@ -92,14 +111,62 @@ class figure_t(base_figure_t):
92
111
  self.showing_process = process
93
112
 
94
113
 
95
- def _IsoSurface(volume: array_t, iso_value: float, *_, **kwargs) -> backend_plot_t:
114
+ def _DefaultFunction(type_: plot_e, frame_dim: int, /) -> plot_function_h:
115
+ """"""
116
+
117
+ def Actual(*args, **kwargs) -> backend_plot_h:
118
+ #
119
+ return text_2d_t(
120
+ plot_t.UnhandledRequestMessage(type_, *args, frame_dim=frame_dim, **kwargs),
121
+ pos="middle-left",
122
+ s=1.0,
123
+ bold=True,
124
+ c="red",
125
+ )
126
+
127
+ return Actual
128
+
129
+
130
+ def _ElevationSurface(elevation: array_t, *args, **kwargs) -> backend_plot_h:
131
+ """"""
132
+ (n_rows, n_cols), min_z, max_z = elevation.shape, elevation.min(), elevation.max()
133
+ max_length = max(n_rows, n_cols)
134
+ height = max_z - min_z
135
+
136
+ if (height > 0) and (height / max_length < _MIN_HEIGHT_RATIO):
137
+ scaling = _MIN_HEIGHT_RATIO * max_length / height
138
+ if scaling.is_integer():
139
+ scaling = int(scaling)
140
+ equality = "="
141
+ else:
142
+ rounded = round(scaling)
143
+ rounded_length = str(rounded).__len__()
144
+ precision = max(3 - rounded_length, 0)
145
+ if precision > 0:
146
+ scaling = round(scaling, ndigits=precision)
147
+ else:
148
+ scaling = rounded
149
+ equality = "≈"
150
+ kwarg_axes = {"axes": {"ztitle": f"Scaling{equality}{scaling}"}}
151
+ else:
152
+ scaling = 1.0
153
+ kwarg_axes = {}
154
+
155
+ Elevation = lambda _row, _col: scaling * float(elevation[round(_row), round(_col)])
156
+
157
+ return NewBackendPlot(
158
+ Elevation, xlim=(0, n_rows), ylim=(0, n_cols), c="summer", **kwarg_axes
159
+ )
160
+
161
+
162
+ def _IsoSurface(volume: array_t, iso_value: float, *_, **kwargs) -> backend_mesh_t:
96
163
  """"""
97
164
  return volume_t(volume).isosurface(value=[iso_value], **kwargs)
98
165
 
99
166
 
100
- def _Mesh(triangles: array_t, vertices: array_t, *_, **kwargs) -> backend_plot_t:
167
+ def _Mesh(triangles: array_t, vertices: array_t, *_, **kwargs) -> backend_mesh_t:
101
168
  """"""
102
- output = backend_plot_t((vertices, triangles))
169
+ output = backend_mesh_t((vertices, triangles))
103
170
 
104
171
  if "width_edge" in kwargs:
105
172
  output.linewidth(kwargs["width_edge"])
@@ -112,16 +179,25 @@ def _Mesh(triangles: array_t, vertices: array_t, *_, **kwargs) -> backend_plot_t
112
179
 
113
180
 
114
181
  PLOTS = NewPlotFunctionsTemplate()
182
+ PLOTS[plot_e.ELEVATION][1] = _ElevationSurface
115
183
  PLOTS[plot_e.ISOSET][1] = _IsoSurface
116
184
  PLOTS[plot_e.MESH][1] = _Mesh
185
+ SetDefaultPlotFunction(PLOTS, _DefaultFunction)
117
186
 
118
187
 
119
188
  TRANSLATIONS = {
120
- (_IsoSurface, "color_edge"): None,
121
- (_IsoSurface, "color_face"): None,
122
- (_IsoSurface, "step_size"): None,
123
- (_IsoSurface, "width_edge"): None,
124
- (_Mesh, "color_face"): ("color_face", lambda _: NewConvertedColor(_, "hex", 0)),
189
+ _IsoSurface: {
190
+ "color_edge": None,
191
+ "color_face": None,
192
+ "step_size": None,
193
+ "width_edge": None,
194
+ },
195
+ _Mesh: {
196
+ "color_face": (
197
+ "color_face",
198
+ lambda _: NewTranslatedColor(_, "hex", index_or_reduction=0)[0],
199
+ )
200
+ },
125
201
  }
126
202
 
127
203
 
@@ -4,6 +4,7 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2022
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
+ from babelplot.type.plot import plot_t
7
8
  from babelplot.type.plot_function import plot_function_h, plot_functions_h
8
9
  from babelplot.type.plot_type import plot_e
9
10
  from logger_36 import L
@@ -29,11 +30,9 @@ def _FakePlotFunction(type_: plot_e, frame_dim: int, /) -> plot_function_h:
29
30
 
30
31
  def Actual(*args, **kwargs) -> None:
31
32
  #
32
- args = ", ".join(map(lambda _: type(_).__name__, args))
33
- kwargs = ", ".join(
34
- f"{_key}={type(_vle).__name__}" for _key, _vle in kwargs.items()
33
+ L.warning(
34
+ plot_t.UnhandledRequestMessage(type_, *args, frame_dim=frame_dim, **kwargs)
35
35
  )
36
- L.warning(f"Unhandled plot request: {type_.name}.{frame_dim}: {args}, {kwargs}")
37
36
 
38
37
  return Actual
39
38
 
@@ -63,38 +63,45 @@ class backend_t:
63
63
  if (translations is None) or (translations.__len__() == 0):
64
64
  return args, kwargs
65
65
 
66
+ if (generics := translations.get(None)) is None:
67
+ generics = {}
68
+ if (for_who_s_asking := translations.get(who_s_asking)) is None:
69
+ for_who_s_asking = {}
70
+
66
71
  out_args = []
67
72
  out_kwargs = {}
68
73
 
74
+ from_args_to_args = []
69
75
  for idx, value in enumerate(args):
70
- if (translation := translations.get((who_s_asking, idx), -1)) == -1:
76
+ if (translation := for_who_s_asking.get(idx, -1)) == -1:
71
77
  # No translation needed; Keeping the passed arg.
72
78
  out_args.append(value)
73
79
  elif translation is None:
74
80
  # The argument should be discarded.
75
81
  pass
76
- else: # isinstance(translation, str | tuple[str, h.Callable])
77
- # The passed arg should actually be a kwarg with name "translation".
82
+ else: # isinstance(translation, int|str | tuple[int|str, h.Callable])
83
+ # The passed arg should be placed at the position "translation", or
84
+ # should actually be a kwarg with name "translation".
78
85
  if isinstance(translation, list | tuple):
79
86
  translation, NewConvertedValue = translation
80
87
  value = NewConvertedValue(value)
81
- out_kwargs[translation] = value
88
+ if isinstance(translation, int):
89
+ from_args_to_args.append([translation, value])
90
+ else:
91
+ out_kwargs[translation] = value
92
+
93
+ out_args = [[_pos, _vle] for _pos, _vle in enumerate(out_args, start=1)]
94
+ _InsertArguments(out_args, from_args_to_args)
82
95
 
83
96
  from_kwargs_to_args = []
84
97
  for key, value in kwargs.items():
85
- # Search for specific translation (who_s_asking, key) before key alone.
86
- if (translation := translations.get((who_s_asking, key), -1)) == -1:
87
- if (translation := translations.get(key, -1)) == -1:
88
- # No translation needed; Keeping the passed arg.
89
- out_kwargs[key] = value
90
- elif translation is None:
91
- # The argument should be discarded.
92
- pass
93
- else: # isinstance(translation, str | tuple[str, h.Callable])
94
- if isinstance(translation, list | tuple):
95
- translation, NewConvertedValue = translation
96
- value = NewConvertedValue(value)
97
- out_kwargs[translation] = value
98
+ # Apply generic translation first, if any.
99
+ key = generics.get(key, key)
100
+
101
+ # Search for specific translation.
102
+ if (translation := for_who_s_asking.get(key, -1)) == -1:
103
+ # No translation needed; Keeping the passed kwarg.
104
+ out_kwargs[key] = value
98
105
  elif translation is None:
99
106
  # The argument should be discarded.
100
107
  pass
@@ -105,12 +112,12 @@ class backend_t:
105
112
  if isinstance(translation, str):
106
113
  out_kwargs[translation] = value
107
114
  else: # isinstance(translation, int)
108
- from_kwargs_to_args.append((translation, value))
115
+ from_kwargs_to_args.append([translation, value])
109
116
 
110
- for idx, value in sorted(from_kwargs_to_args, key=ItemAt(0)):
111
- out_args.insert(idx, value)
117
+ _InsertArguments(out_args, from_kwargs_to_args)
118
+ out_args = tuple(map(ItemAt(1), out_args))
112
119
 
113
- if (ApplyGlobalTranslation := translations.get(None)) is not None:
120
+ if (ApplyGlobalTranslation := generics.get(None)) is not None:
114
121
  ApplyGlobalTranslation(out_kwargs, who_s_asking)
115
122
 
116
123
  return out_args, out_kwargs
@@ -120,6 +127,22 @@ class backend_t:
120
127
  return self.py_path
121
128
 
122
129
 
130
+ def _InsertArguments(
131
+ current: list[list[int | h.Any]], new: list[list[int | h.Any]], /
132
+ ) -> None:
133
+ """
134
+ Note: list[int | h.Any] should actually be mutable_tuple[int, h.Any].
135
+ """
136
+ # Note: It is important to sort from_args_to_args so that a higher-positioned
137
+ # argument does not get shifted even higher by the insertion of a lower-positioned
138
+ # argument dealt with later.
139
+ for position, value in sorted(new, key=ItemAt(0)):
140
+ for argument in current:
141
+ if argument[0] >= position:
142
+ argument[0] += 1
143
+ current.append([position, value])
144
+
145
+
123
146
  @d.dataclass(slots=True, repr=False, eq=False)
124
147
  class backends_t(dict[str, backend_t]):
125
148
 
@@ -57,7 +57,7 @@ class figure_t(base_t):
57
57
 
58
58
  return output
59
59
 
60
- def _NewBackendFigure(self, *args, **kwargs) -> backend_figure_h:
60
+ def _NewBackendFigure(self, *args, **kwargs) -> backend_figure_h | None:
61
61
  """"""
62
62
  raise NotImplementedError
63
63
 
@@ -86,7 +86,7 @@ class figure_t(base_t):
86
86
  col = self.shape[1]
87
87
 
88
88
  args, kwargs = BACKENDS.TranslatedArguments(
89
- self.backend_name, args, kwargs, self.AddFrame.__name__
89
+ self.backend_name, args, kwargs, self._NewFrame.__name__
90
90
  )
91
91
  output = self._NewFrame(title, dim, row, col, *args, **kwargs)
92
92
 
@@ -33,6 +33,7 @@ class frame_t(base_t):
33
33
  **kwargs,
34
34
  ) -> plot_t:
35
35
  """"""
36
+ full_type = (type_, self.frame_dim)
36
37
  plot_function = BACKENDS.PlotFunction(self.backend_name, type_, self.frame_dim)
37
38
  args, kwargs = BACKENDS.TranslatedArguments(
38
39
  self.backend_name, args, kwargs, plot_function
@@ -41,8 +42,10 @@ class frame_t(base_t):
41
42
  plot_function,
42
43
  *args,
43
44
  title=title,
45
+ type_=full_type,
44
46
  **kwargs,
45
47
  )
48
+ plot.type_ = full_type
46
49
  # Note: plot.__class__ is not plot_t; It is the subclass defined by a backend.
47
50
  DefaultProperties = getattr(plot.__class__, "_BackendDefaultProperties", None)
48
51
  if DefaultProperties is not None:
@@ -59,6 +62,7 @@ class frame_t(base_t):
59
62
  plot_function: plot_function_h,
60
63
  *args,
61
64
  title: str | None = None, # /!\ If _, then it is swallowed by kwargs!
65
+ type_: tuple[plot_type_h | plot_function_h, int],
62
66
  **kwargs,
63
67
  ) -> plot_t:
64
68
  """"""
@@ -7,11 +7,35 @@ SEE COPYRIGHT NOTICE BELOW
7
7
  import dataclasses as d
8
8
 
9
9
  from babelplot.type.ffp_base import base_t
10
+ from babelplot.type.plot_function import plot_function_h
11
+ from babelplot.type.plot_type import plot_type_h
10
12
 
11
13
 
12
14
  @d.dataclass(slots=True, repr=False, eq=False)
13
15
  class plot_t(base_t):
14
16
  title: str | None = None
17
+ type_: tuple[plot_type_h | plot_function_h, int] | None = None
18
+
19
+ @staticmethod
20
+ def UnhandledRequestMessage(
21
+ type_: plot_type_h | plot_function_h, *args, frame_dim: int = 0, **kwargs
22
+ ) -> str:
23
+ """"""
24
+ if frame_dim > 0:
25
+ if isinstance(type_, str):
26
+ name = type_
27
+ else:
28
+ name = type_.name
29
+ request = f"{name}.{frame_dim}"
30
+ else:
31
+ request = str(type_)
32
+
33
+ args = ", ".join(map(lambda _: type(_).__name__, args))
34
+ kwargs = ", ".join(
35
+ f"{_key}={type(_vle).__name__}" for _key, _vle in kwargs.items()
36
+ )
37
+
38
+ return f"Unhandled Plot Request\n{request}\nargs: {args}\nkwargs: {kwargs}"
15
39
 
16
40
 
17
41
  """
@@ -4,7 +4,7 @@ Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2022
4
4
  SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
- __version__ = "2025.11"
7
+ __version__ = "2025.13"
8
8
 
9
9
  """
10
10
  COPYRIGHT NOTICE
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: babelplot
3
- Version: 2025.11
3
+ Version: 2025.13
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
@@ -26,7 +26,6 @@ package/babelplot/constant/project.py
26
26
  package/babelplot/extension/enum_.py
27
27
  package/babelplot/extension/function.py
28
28
  package/babelplot/runtime/backends.py
29
- package/babelplot/task/color.py
30
29
  package/babelplot/task/help.py
31
30
  package/babelplot/task/instantiation.py
32
31
  package/babelplot/task/plotting.py
@@ -1,112 +0,0 @@
1
- """
2
- Copyright CNRS/Inria/UniCA
3
- Contributor(s): Eric Debreuve (eric.debreuve@cnrs.fr) since 2022
4
- SEE COPYRIGHT NOTICE BELOW
5
- """
6
-
7
- import typing as h
8
-
9
- import numpy as nmpy
10
-
11
- try:
12
- import matplotlib.colors as colr
13
- except ModuleNotFoundError:
14
- colr = None
15
- else:
16
- AsHex = lambda _: colr.to_hex(_).upper()
17
- AsRGB1 = colr.to_rgb
18
- AsRGB255 = lambda _: nmpy.round(255.0 * nmpy.array(colr.to_rgb(_))).astype(
19
- nmpy.uint8
20
- )
21
-
22
-
23
- assert colr, "No module found for color conversion."
24
-
25
-
26
- array_t = nmpy.ndarray
27
-
28
-
29
- def NewConvertedColor(
30
- color: h.Any | h.Sequence[h.Any],
31
- format_color: h.Literal["hex", "rgb1", "rgb255"],
32
- sequence_or_reduction: (
33
- int | h.Literal["min", "mean", "median", "max"] | type | h.Callable
34
- ),
35
- /,
36
- ) -> h.Any:
37
- """"""
38
- if format_color == "rgb1":
39
- AsOutputFormat = AsRGB1
40
- elif format_color == "rgb255":
41
- AsOutputFormat = AsRGB255
42
- elif format_color == "hex":
43
- AsOutputFormat = AsHex
44
- else:
45
- raise ValueError(f"Unknown output format {format_color}.")
46
-
47
- if (
48
- (not isinstance(color, str))
49
- and (isinstance(color, h.Sequence) and isinstance(color[0], h.Sequence))
50
- or (isinstance(color, array_t) and (color.ndim == 2))
51
- ):
52
- # Notes: The first h.Sequence is implicitly tuple | list, or other "true" (i.e.
53
- # not str) sequence types, and color can be of any length.
54
- # The second h.Sequence is implicitly tuple | list, other "true" sequence types,
55
- # or str. If str, then color is in Hex format. Its length is either 4 (e.g.,
56
- # #ABC <=> #AABBCC), 5 (e.g., #ABCD <=> #AABBCCDD), 7 (e.g., #ABCDEF) or 9
57
- # (e.g., #ABCDEF01). If not str, then color is a sequence of 3 or 4 integers or
58
- # floats.
59
- if isinstance(sequence_or_reduction, int):
60
- return AsOutputFormat(color[sequence_or_reduction])
61
- elif isinstance(sequence_or_reduction, str):
62
- raise NotImplementedError
63
- return sequence_or_reduction(tuple(AsOutputFormat(_) for _ in color))
64
-
65
- return AsOutputFormat(color)
66
-
67
-
68
- """
69
- COPYRIGHT NOTICE
70
-
71
- This software is governed by the CeCILL license under French law and
72
- abiding by the rules of distribution of free software. You can use,
73
- modify and/ or redistribute the software under the terms of the CeCILL
74
- license as circulated by CEA, CNRS and INRIA at the following URL
75
- "http://www.cecill.info".
76
-
77
- As a counterpart to the access to the source code and rights to copy,
78
- modify and redistribute granted by the license, users are provided only
79
- with a limited warranty and the software's author, the holder of the
80
- economic rights, and the successive licensors have only limited
81
- liability.
82
-
83
- In this respect, the user's attention is drawn to the risks associated
84
- with loading, using, modifying and/or developing or reproducing the
85
- software by the user in light of its specific status of free software,
86
- that may mean that it is complicated to manipulate, and that also
87
- therefore means that it is reserved for developers and experienced
88
- professionals having in-depth computer knowledge. Users are therefore
89
- encouraged to load and test the software's suitability as regards their
90
- requirements in conditions enabling the security of their systems and/or
91
- data to be ensured and, more generally, to use and operate it in the
92
- same conditions as regards security.
93
-
94
- The fact that you are presently reading this means that you have had
95
- knowledge of the CeCILL license and that you accept its terms.
96
-
97
- SEE LICENCE NOTICE: file README-LICENCE-utf8.txt at project source root.
98
-
99
- This software is being developed by Eric Debreuve, a CNRS employee and
100
- member of team Morpheme.
101
- Team Morpheme is a joint team between Inria, CNRS, and UniCA.
102
- It is hosted by the Centre Inria d'Université Côte d'Azur, Laboratory
103
- I3S, and Laboratory iBV.
104
-
105
- CNRS: https://www.cnrs.fr/index.php/en
106
- Inria: https://www.inria.fr/en/
107
- UniCA: https://univ-cotedazur.eu/
108
- Centre Inria d'Université Côte d'Azur: https://www.inria.fr/en/centre/sophia/
109
- I3S: https://www.i3s.unice.fr/en/
110
- iBV: http://ibv.unice.fr/
111
- Team Morpheme: https://team.inria.fr/morpheme/
112
- """
File without changes
File without changes
File without changes
File without changes
File without changes