bsplot 0.0.2__py3-none-any.whl

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 (89) hide show
  1. bsplot/__init__.py +23 -0
  2. bsplot/anat.py +55 -0
  3. bsplot/animate.py +194 -0
  4. bsplot/bioicons.py +777 -0
  5. bsplot/brain.py +188 -0
  6. bsplot/colors.py +1935 -0
  7. bsplot/data/HCP_avg-SC.txt +379 -0
  8. bsplot/data/MNI152.rh.pial +0 -0
  9. bsplot/data/STN_lh.nii.gz +0 -0
  10. bsplot/data/STN_rh.nii.gz +0 -0
  11. bsplot/data/__init__.py +22 -0
  12. bsplot/data/left_electrode.ply +257270 -0
  13. bsplot/data/parcellations/HCP-MMP1.L.label.gii +442 -0
  14. bsplot/data/parcellations/HCP-MMP1.R.label.gii +442 -0
  15. bsplot/data/parcellations/code/split_hcpmmp1.sh +4 -0
  16. bsplot/data/right_electrode.ply +257270 -0
  17. bsplot/data/surface.py +112 -0
  18. bsplot/data/tpl-MNI152NLin2009b_atlas-hcpmmp1_desc-ordered_dseg.nii.gz +0 -0
  19. bsplot/figure.py +177 -0
  20. bsplot/graph/__init__.py +31 -0
  21. bsplot/graph/edges.py +312 -0
  22. bsplot/graph/flowchart.py +762 -0
  23. bsplot/graph/layout.py +201 -0
  24. bsplot/graph/network.py +974 -0
  25. bsplot/graph/nodes.py +452 -0
  26. bsplot/panels.py +110 -0
  27. bsplot/scientific_color_maps/__init__.py +0 -0
  28. bsplot/scientific_color_maps/_sync_from_zenodo_download.sh +1 -0
  29. bsplot/scientific_color_maps/batlowK.txt +256 -0
  30. bsplot/scientific_color_maps/batlowW.txt +256 -0
  31. bsplot/scientific_color_maps/cyclic/bamO.txt +256 -0
  32. bsplot/scientific_color_maps/cyclic/brocO.txt +256 -0
  33. bsplot/scientific_color_maps/cyclic/corkO.txt +256 -0
  34. bsplot/scientific_color_maps/cyclic/romaO.txt +256 -0
  35. bsplot/scientific_color_maps/cyclic/vikO.txt +256 -0
  36. bsplot/scientific_color_maps/diverging/bam.txt +256 -0
  37. bsplot/scientific_color_maps/diverging/berlin.txt +256 -0
  38. bsplot/scientific_color_maps/diverging/broc.txt +256 -0
  39. bsplot/scientific_color_maps/diverging/cork.txt +256 -0
  40. bsplot/scientific_color_maps/diverging/lisbon.txt +256 -0
  41. bsplot/scientific_color_maps/diverging/managua.txt +256 -0
  42. bsplot/scientific_color_maps/diverging/roma.txt +256 -0
  43. bsplot/scientific_color_maps/diverging/tofino.txt +256 -0
  44. bsplot/scientific_color_maps/diverging/vanimo.txt +256 -0
  45. bsplot/scientific_color_maps/diverging/vik.txt +256 -0
  46. bsplot/scientific_color_maps/multisequential/bukavu.txt +256 -0
  47. bsplot/scientific_color_maps/multisequential/fes.txt +256 -0
  48. bsplot/scientific_color_maps/multisequential/oleron.txt +256 -0
  49. bsplot/scientific_color_maps/naviaW.txt +256 -0
  50. bsplot/scientific_color_maps/sequential/acton.txt +256 -0
  51. bsplot/scientific_color_maps/sequential/bamako.txt +256 -0
  52. bsplot/scientific_color_maps/sequential/batlow.txt +256 -0
  53. bsplot/scientific_color_maps/sequential/bilbao.txt +256 -0
  54. bsplot/scientific_color_maps/sequential/buda.txt +256 -0
  55. bsplot/scientific_color_maps/sequential/davos.txt +256 -0
  56. bsplot/scientific_color_maps/sequential/devon.txt +256 -0
  57. bsplot/scientific_color_maps/sequential/glasgow.txt +256 -0
  58. bsplot/scientific_color_maps/sequential/grayC.txt +256 -0
  59. bsplot/scientific_color_maps/sequential/hawaii.txt +256 -0
  60. bsplot/scientific_color_maps/sequential/imola.txt +256 -0
  61. bsplot/scientific_color_maps/sequential/lajolla.txt +256 -0
  62. bsplot/scientific_color_maps/sequential/lapaz.txt +256 -0
  63. bsplot/scientific_color_maps/sequential/lipari.txt +256 -0
  64. bsplot/scientific_color_maps/sequential/navia.txt +256 -0
  65. bsplot/scientific_color_maps/sequential/nuuk.txt +256 -0
  66. bsplot/scientific_color_maps/sequential/oslo.txt +256 -0
  67. bsplot/scientific_color_maps/sequential/tokyo.txt +256 -0
  68. bsplot/scientific_color_maps/sequential/turku.txt +256 -0
  69. bsplot/streamlines.py +314 -0
  70. bsplot/style.py +791 -0
  71. bsplot/styles/black.mplstyle +78 -0
  72. bsplot/styles/bss.mplstyle +50 -0
  73. bsplot/styles/bwcomp.mplstyle +30 -0
  74. bsplot/styles/nature.mplstyle +57 -0
  75. bsplot/styles/transparent.mplstyle +16 -0
  76. bsplot/styles/tvbo.mplstyle +57 -0
  77. bsplot/surface.py +2370 -0
  78. bsplot/templates.py +270 -0
  79. bsplot/text.py +199 -0
  80. bsplot/text2obj.py +59 -0
  81. bsplot/timeseries.py +10 -0
  82. bsplot/utils.py +13 -0
  83. bsplot/volume.py +958 -0
  84. bsplot-0.0.2.dist-info/METADATA +60 -0
  85. bsplot-0.0.2.dist-info/RECORD +89 -0
  86. bsplot-0.0.2.dist-info/WHEEL +5 -0
  87. bsplot-0.0.2.dist-info/entry_points.txt +2 -0
  88. bsplot-0.0.2.dist-info/licenses/LICENSE +193 -0
  89. bsplot-0.0.2.dist-info/top_level.txt +1 -0
bsplot/graph/nodes.py ADDED
@@ -0,0 +1,452 @@
1
+ import matplotlib
2
+ import matplotlib.colors as mcolors
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
+ from typing import Dict, Optional, Union, Tuple, Any
6
+ from pathlib import Path
7
+
8
+
9
+ def get_actual_bounds(ax, axis="x"):
10
+ renderer = ax.figure.canvas.get_renderer()
11
+ xcoords = []
12
+ ycoords = []
13
+
14
+ for artist in ax.get_children():
15
+ if not isinstance(artist, matplotlib.text.Text):
16
+ continue # Only process text elements
17
+ elif artist.get_text() == "":
18
+ continue
19
+ if hasattr(artist, "get_window_extent"):
20
+ try:
21
+ # Get the bounding box in display coordinates
22
+ bbox = artist.get_window_extent(renderer=renderer)
23
+ # print(f"Bbox for '{artist.get_text()}': {bbox}")
24
+
25
+ # Transform the bounding box corners to data coordinates
26
+ bbox_data = ax.transData.inverted().transform(
27
+ [
28
+ [bbox.x0, bbox.y0], # Bottom-left
29
+ [bbox.x1, bbox.y1], # Top-right
30
+ ]
31
+ )
32
+ xcoords.extend([bbox_data[0, 0], bbox_data[1, 0]])
33
+ ycoords.extend([bbox_data[0, 1], bbox_data[1, 1]])
34
+ except Exception as e:
35
+ print(f"Error processing '{artist.get_text()}': {e}")
36
+
37
+ if axis == "x":
38
+ return np.min(xcoords), np.max(xcoords)
39
+ elif axis == "y":
40
+ return np.min(ycoords), np.max(ycoords)
41
+
42
+
43
+ def draw_custom_nodes(
44
+ G,
45
+ pos,
46
+ labels=None,
47
+ font_size=10,
48
+ ax=None,
49
+ node_colors=None,
50
+ alpha=0.8,
51
+ facecolor=None,
52
+ edgecolor=None,
53
+ ):
54
+ """
55
+ Custom function to draw nodes as text in a network graph.
56
+
57
+ Parameters:
58
+ ----------
59
+ G : networkx.Graph
60
+ The graph on which nodes will be drawn.
61
+ pos : dict
62
+ A dictionary with nodes as keys and positions as values. Positions should be tuples of (x, y) coordinates.
63
+ labels : dict, optional
64
+ A dictionary with node labels. If None, nodes are labeled with node names.
65
+ font_size : int, optional
66
+ The font size of the node labels (default is 10).
67
+ ax : matplotlib.axes.Axes, optional
68
+ Matplotlib axes object to draw the nodes on. If None, the current axes will be used (default is None).
69
+ node_colors : dict or str, optional
70
+ A dictionary specifying the color for each node or a single color for all nodes (default is None).
71
+
72
+ Returns:
73
+ -------
74
+ list
75
+ A list of text objects for the node labels.
76
+ """
77
+ x = [v[0] for v in pos.values()]
78
+ y = [v[1] for v in pos.values()]
79
+
80
+ if ax is None:
81
+ ax = plt.gca()
82
+
83
+ if min(x) != max(x):
84
+ ax.set_xlim([min(x), max(x)])
85
+ if min(y) != max(y):
86
+ ax.set_ylim([min(y), max(y)])
87
+
88
+ if labels is None:
89
+ labels = {node: node for node in G.nodes()}
90
+
91
+ if node_colors is None:
92
+ node_colors = ["grey" for node in G.nodes()]
93
+ elif isinstance(node_colors, str):
94
+ if node_colors.startswith("#"):
95
+ node_colors = mcolors.to_rgba(node_colors)
96
+ node_colors = [node_colors for node in G.nodes()]
97
+ elif isinstance(node_colors, dict):
98
+ node_colors = [
99
+ (
100
+ mcolors.to_rgba(node_colors[node])
101
+ if isinstance(node_colors[node], str)
102
+ else node_colors[node]
103
+ )
104
+ for node in G.nodes()
105
+ ]
106
+
107
+ texts = {} # To store the text objects
108
+ bbox_pad = 0.3 # Padding for the bounding box
109
+ for i, (node, position) in enumerate(pos.items()):
110
+ text = labels[node]
111
+ x, y = position
112
+ txt_obj = ax.text(
113
+ x,
114
+ y,
115
+ text,
116
+ bbox=dict(
117
+ facecolor=node_colors[i] if facecolor is None else facecolor,
118
+ edgecolor=node_colors[i] if edgecolor is None else edgecolor,
119
+ alpha=alpha,
120
+ boxstyle=f"round,pad={bbox_pad}",
121
+ ),
122
+ ha="center",
123
+ va="top",
124
+ fontsize=font_size,
125
+ )
126
+ texts[node] = txt_obj
127
+ ax.figure.canvas.draw()
128
+ # Force rendering to ensure bounding boxes are accurate
129
+ bbox_positions = {
130
+ node: txt.get_window_extent(
131
+ renderer=ax.figure.canvas.get_renderer()
132
+ ).transformed(ax.transData.inverted())
133
+ for node, txt in texts.items()
134
+ }
135
+
136
+ xmin, xmax = get_actual_bounds(ax, axis="x")
137
+ ymin, ymax = get_actual_bounds(ax, axis="y")
138
+ ax.set_xlim([xmin - bbox_pad, xmax + bbox_pad])
139
+ ax.set_ylim([ymin - bbox_pad, ymax + bbox_pad])
140
+
141
+ return texts, bbox_positions
142
+
143
+ # return texts
144
+
145
+
146
+ def draw_nodes_with_icons(
147
+ G,
148
+ pos,
149
+ labels: Optional[Dict[str, str]] = None,
150
+ icons: Optional[Dict[str, str]] = None,
151
+ font_size: int = 10,
152
+ ax=None,
153
+ node_colors: Optional[Union[Dict, str, list]] = None,
154
+ alpha: float = 0.8,
155
+ facecolor: Optional[str] = None,
156
+ edgecolor: Optional[str] = None,
157
+ icon_size: float = 0.15,
158
+ icon_position: str = "above",
159
+ local_icons_dir: Optional[Union[str, Path]] = None,
160
+ ) -> Tuple[Dict, Dict, Dict]:
161
+ """
162
+ Draw nodes with bioicons in a network graph.
163
+
164
+ This function extends draw_custom_nodes to support icons from
165
+ bioicons.com or local image files.
166
+
167
+ Parameters
168
+ ----------
169
+ G : networkx.Graph
170
+ The graph on which nodes will be drawn.
171
+ pos : dict
172
+ A dictionary with nodes as keys and positions as values.
173
+ labels : dict, optional
174
+ Node labels. If None, nodes are labeled with node names.
175
+ icons : dict, optional
176
+ Dictionary mapping node names to icon IDs or image paths.
177
+ Example: {"node1": "neuron", "node2": "/path/to/icon.png"}
178
+ font_size : int
179
+ Font size for node labels.
180
+ ax : matplotlib.axes.Axes, optional
181
+ Axes to draw on. If None, uses current axes.
182
+ node_colors : dict, str, or list, optional
183
+ Colors for each node.
184
+ alpha : float
185
+ Transparency of node boxes.
186
+ facecolor : str, optional
187
+ Background color for all nodes.
188
+ edgecolor : str, optional
189
+ Border color for all nodes.
190
+ icon_size : float
191
+ Size of icons relative to the plot.
192
+ icon_position : str
193
+ Where to place icons: "above", "below", "left", "right", or "center".
194
+ local_icons_dir : str or Path, optional
195
+ Directory containing local icon files.
196
+
197
+ Returns
198
+ -------
199
+ texts : dict
200
+ Dictionary of text objects for each node.
201
+ bbox_positions : dict
202
+ Dictionary of bounding box positions for each node.
203
+ icon_annotations : dict
204
+ Dictionary of icon AnnotationBbox objects for each node.
205
+
206
+ Examples
207
+ --------
208
+ >>> import networkx as nx
209
+ >>> import matplotlib.pyplot as plt
210
+ >>> from bsplot.graph.nodes import draw_nodes_with_icons
211
+ >>>
212
+ >>> G = nx.Graph()
213
+ >>> G.add_nodes_from(["A", "B", "C"])
214
+ >>> G.add_edges_from([("A", "B"), ("B", "C")])
215
+ >>> pos = {"A": (0, 0), "B": (1, 0), "C": (2, 0)}
216
+ >>> icons = {"A": "neuron", "B": "brain", "C": "synapse"}
217
+ >>>
218
+ >>> fig, ax = plt.subplots()
219
+ >>> draw_nodes_with_icons(G, pos, icons=icons, ax=ax)
220
+ >>> plt.show()
221
+ """
222
+ from ..bioicons import BioIcons
223
+
224
+ x_coords = [v[0] for v in pos.values()]
225
+ y_coords = [v[1] for v in pos.values()]
226
+
227
+ if ax is None:
228
+ ax = plt.gca()
229
+
230
+ # Set axis limits
231
+ x_margin = (max(x_coords) - min(x_coords)) * 0.2 if max(x_coords) != min(x_coords) else 1
232
+ y_margin = (max(y_coords) - min(y_coords)) * 0.2 if max(y_coords) != min(y_coords) else 1
233
+ ax.set_xlim([min(x_coords) - x_margin, max(x_coords) + x_margin])
234
+ ax.set_ylim([min(y_coords) - y_margin, max(y_coords) + y_margin])
235
+
236
+ if labels is None:
237
+ labels = {node: node for node in G.nodes()}
238
+
239
+ if icons is None:
240
+ icons = {}
241
+
242
+ # Process node colors
243
+ if node_colors is None:
244
+ node_colors_list = ["grey" for _ in G.nodes()]
245
+ elif isinstance(node_colors, str):
246
+ if node_colors.startswith("#"):
247
+ node_colors = mcolors.to_rgba(node_colors)
248
+ node_colors_list = [node_colors for _ in G.nodes()]
249
+ elif isinstance(node_colors, dict):
250
+ node_colors_list = [
251
+ (
252
+ mcolors.to_rgba(node_colors[node])
253
+ if isinstance(node_colors[node], str)
254
+ else node_colors[node]
255
+ )
256
+ for node in G.nodes()
257
+ ]
258
+ else:
259
+ node_colors_list = list(node_colors)
260
+
261
+ # Initialize bioicons interface
262
+ bio_icons = BioIcons(
263
+ local_icons_dir=local_icons_dir,
264
+ )
265
+
266
+ texts = {}
267
+ icon_annotations = {}
268
+ bbox_pad = 0.3
269
+
270
+ # Calculate offset based on icon position
271
+ def get_icon_offset(position: str, size: float) -> Tuple[float, float]:
272
+ offsets = {
273
+ "above": (0, size * 2),
274
+ "below": (0, -size * 2),
275
+ "left": (-size * 2, 0),
276
+ "right": (size * 2, 0),
277
+ "center": (0, 0),
278
+ }
279
+ return offsets.get(position, (0, size * 2))
280
+
281
+ for i, (node, position) in enumerate(pos.items()):
282
+ text = labels.get(node, str(node))
283
+ x, y = position
284
+
285
+ # Draw the text label with box
286
+ txt_obj = ax.text(
287
+ x,
288
+ y,
289
+ text,
290
+ bbox=dict(
291
+ facecolor=node_colors_list[i] if facecolor is None else facecolor,
292
+ edgecolor=node_colors_list[i] if edgecolor is None else edgecolor,
293
+ alpha=alpha,
294
+ boxstyle=f"round,pad={bbox_pad}",
295
+ ),
296
+ ha="center",
297
+ va="center",
298
+ fontsize=font_size,
299
+ )
300
+ texts[node] = txt_obj
301
+
302
+ # Draw icon if specified for this node
303
+ if node in icons:
304
+ icon_id = icons[node]
305
+ dx, dy = get_icon_offset(icon_position, icon_size)
306
+
307
+ ab = bio_icons.plot(
308
+ icon_id,
309
+ ax=ax,
310
+ x=x + dx,
311
+ y=y + dy,
312
+ size=icon_size,
313
+ )
314
+ icon_annotations[node] = ab
315
+
316
+ ax.figure.canvas.draw()
317
+
318
+ # Get bounding boxes
319
+ bbox_positions = {}
320
+ try:
321
+ renderer = ax.figure.canvas.get_renderer()
322
+ bbox_positions = {
323
+ node: txt.get_window_extent(renderer=renderer).transformed(
324
+ ax.transData.inverted()
325
+ )
326
+ for node, txt in texts.items()
327
+ }
328
+ except Exception:
329
+ # Fallback if renderer is not available
330
+ pass
331
+
332
+ # Adjust limits to fit everything
333
+ try:
334
+ xmin, xmax = get_actual_bounds(ax, axis="x")
335
+ ymin, ymax = get_actual_bounds(ax, axis="y")
336
+ if xmin is not None and xmax is not None:
337
+ ax.set_xlim([xmin - bbox_pad - icon_size, xmax + bbox_pad + icon_size])
338
+ if ymin is not None and ymax is not None:
339
+ ax.set_ylim([ymin - bbox_pad - icon_size, ymax + bbox_pad + icon_size])
340
+ except Exception:
341
+ pass
342
+
343
+ return texts, bbox_positions, icon_annotations
344
+
345
+
346
+ def draw_icon_nodes(
347
+ G,
348
+ pos,
349
+ icons: Dict[str, str],
350
+ labels: Optional[Dict[str, str]] = None,
351
+ font_size: int = 10,
352
+ ax=None,
353
+ icon_size: float = 0.2,
354
+ show_labels: bool = True,
355
+ label_offset: float = 0.15,
356
+ local_icons_dir: Optional[Union[str, Path]] = None,
357
+ ) -> Tuple[Dict, Dict]:
358
+ """
359
+ Draw network nodes using only bioicons (no text boxes).
360
+
361
+ This creates a cleaner visualization where nodes are represented
362
+ purely by icons with optional labels below.
363
+
364
+ Parameters
365
+ ----------
366
+ G : networkx.Graph
367
+ The graph.
368
+ pos : dict
369
+ Node positions.
370
+ icons : dict
371
+ Dictionary mapping node names to icon IDs.
372
+ labels : dict, optional
373
+ Node labels. If None, uses node names.
374
+ font_size : int
375
+ Font size for labels.
376
+ ax : Axes, optional
377
+ Matplotlib axes.
378
+ icon_size : float
379
+ Size of icons.
380
+ show_labels : bool
381
+ Whether to show text labels below icons.
382
+ label_offset : float
383
+ Vertical offset for labels.
384
+ local_icons_dir : str or Path, optional
385
+ Local icons directory.
386
+
387
+ Returns
388
+ -------
389
+ icon_annotations : dict
390
+ Dictionary of icon AnnotationBbox objects.
391
+ texts : dict
392
+ Dictionary of text objects (empty if show_labels=False).
393
+
394
+ Examples
395
+ --------
396
+ >>> import networkx as nx
397
+ >>> G = nx.path_graph(3)
398
+ >>> pos = nx.spring_layout(G)
399
+ >>> icons = {0: "neuron", 1: "synapse", 2: "brain"}
400
+ >>> draw_icon_nodes(G, pos, icons)
401
+ """
402
+ from ..bioicons import BioIcons
403
+
404
+ if ax is None:
405
+ ax = plt.gca()
406
+
407
+ if labels is None:
408
+ labels = {node: str(node) for node in G.nodes()}
409
+
410
+ # Initialize icons interface
411
+ bio_icons = BioIcons(
412
+ local_icons_dir=local_icons_dir,
413
+ )
414
+
415
+ icon_annotations = {}
416
+ texts = {}
417
+
418
+ for node, position in pos.items():
419
+ x, y = position
420
+
421
+ # Draw icon
422
+ icon_id = icons.get(node, str(node))
423
+ ab = bio_icons.plot(
424
+ icon_id,
425
+ ax=ax,
426
+ x=x,
427
+ y=y,
428
+ size=icon_size,
429
+ )
430
+ icon_annotations[node] = ab
431
+
432
+ # Draw label if enabled
433
+ if show_labels:
434
+ txt = ax.text(
435
+ x,
436
+ y - label_offset,
437
+ labels.get(node, str(node)),
438
+ ha="center",
439
+ va="top",
440
+ fontsize=font_size,
441
+ color="#333333",
442
+ )
443
+ texts[node] = txt
444
+
445
+ # Set axis limits
446
+ x_coords = [v[0] for v in pos.values()]
447
+ y_coords = [v[1] for v in pos.values()]
448
+ margin = icon_size + label_offset + 0.1
449
+ ax.set_xlim([min(x_coords) - margin, max(x_coords) + margin])
450
+ ax.set_ylim([min(y_coords) - margin, max(y_coords) + margin])
451
+
452
+ return icon_annotations, texts
bsplot/panels.py ADDED
@@ -0,0 +1,110 @@
1
+ import string
2
+
3
+
4
+ def get_alphabet(index, uppercase=False):
5
+ """
6
+ Get the alphabet letter(s) corresponding to an index (1-based).
7
+ Extends beyond 'Z' to 'AA', 'AB', etc., if needed.
8
+
9
+ Parameters:
10
+ index (int): The 1-based index (1 = A, 2 = B, ..., 27 = AA).
11
+ uppercase (bool): If True, returns uppercase letters; otherwise, lowercase.
12
+
13
+ Returns:
14
+ str: The corresponding letter(s).
15
+ """
16
+ alphabet = string.ascii_uppercase if uppercase else string.ascii_lowercase
17
+ result = ""
18
+ while index > 0:
19
+ index, remainder = divmod(index - 1, 26)
20
+ result = alphabet[remainder] + result
21
+ return result
22
+
23
+
24
+ def add_panel_number(
25
+ ax,
26
+ label,
27
+ option="letters",
28
+ background=False,
29
+ loc="upper left",
30
+ coord="axes",
31
+ x_scale=1,
32
+ y_scale=1.01,
33
+ x_shift=0,
34
+ y_shift=0,
35
+ **kwargs,
36
+ ):
37
+ """
38
+ Adds a label to the given axis near the upper-left corner.
39
+ If option is 'letters' and the label is a number, it converts it to the
40
+ corresponding alphabetical letter(s).
41
+
42
+ Placement coordinates:
43
+ - coord='axes' (default): positions are specified in axes-fraction coordinates
44
+ with (0,0) bottom-left and (1,1) top-right. x_shift/y_shift are interpreted
45
+ as fractions of the axes size (so they are data-independent).
46
+ - coord='data': positions are specified in data coordinates for backward
47
+ compatibility. In this mode x_scale/y_scale and x_shift/y_shift behave like
48
+ before, but are now robust to inverted axes.
49
+
50
+ Parameters:
51
+ ax (matplotlib.axes.Axes): The axis to add the label to.
52
+ label (int or str): The label to place in the upper left corner.
53
+ option (str): 'letters' to convert numbers to letters, 'numbers' to use label as-is.
54
+ background (bool): If True, adds a white rounded box behind the label.
55
+ loc (str): Currently unused; reserved for future placement options.
56
+ text_kwargs (dict): Additional keyword args forwarded to ax.text.
57
+ x_scale (float): Scale factor applied to the left x-limit before placement.
58
+ y_scale (float): Scale factor applied to the top y-limit before placement.
59
+ x_shift (float): Fraction of the x-axis span to shift after scaling
60
+ (+ moves right, - moves left), independent of data range.
61
+ y_shift (float): Fraction of the y-axis span to shift after scaling
62
+ (+ moves up, - moves down), independent of data range.
63
+ """
64
+ kwargs = {
65
+ "fontsize": 16,
66
+ "fontweight": "bold",
67
+ "va": "bottom",
68
+ "ha": "center",
69
+ **kwargs,
70
+ }
71
+ if option == "letters" and isinstance(label, int):
72
+ label = get_alphabet(label)
73
+
74
+ if background:
75
+ kwargs["bbox"] = {
76
+ "boxstyle": "round,pad=0.1",
77
+ "facecolor": "white",
78
+ "edgecolor": "none",
79
+ "alpha": 0.8,
80
+ }
81
+ else:
82
+ kwargs["bbox"] = None
83
+
84
+ if coord == "axes":
85
+ # Axes-fraction coordinates: robust to inverted/scaled axes
86
+ ax.text(
87
+ 0.0 + x_shift,
88
+ 1.0 + y_shift,
89
+ str(label),
90
+ transform=ax.transAxes,
91
+ **kwargs,
92
+ )
93
+ else:
94
+ # Data-coordinate placement (legacy). Robust to inverted axes.
95
+ x0, x1 = ax.get_xlim()
96
+ y0, y1 = ax.get_ylim()
97
+ x_left = min(x0, x1)
98
+ y_top = max(y0, y1)
99
+
100
+ x = x_left * x_scale
101
+ y = y_top * y_scale
102
+ x += abs(x1 - x0) * x_shift
103
+ y += abs(y1 - y0) * y_shift
104
+
105
+ ax.text(
106
+ x,
107
+ y,
108
+ str(label),
109
+ **kwargs,
110
+ )
File without changes
@@ -0,0 +1 @@
1
+ find "/Users/leonmartin_bih/Downloads/ScientificColourMaps8" -type f -name "*.txt" ! -name "*10*" ! -name "*25*" ! -name "*50*" ! -name "*HEX*" ! -name "*S*" -exec cp {} "/Users/leonmartin_bih/tools/bsplot/bsplot/scientific_color_maps" \;