maxplotlibx 0.1__tar.gz → 0.1.2__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 (35) hide show
  1. {maxplotlibx-0.1/src/maxplotlibx.egg-info → maxplotlibx-0.1.2}/PKG-INFO +2 -3
  2. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/README.md +0 -2
  3. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/pyproject.toml +11 -1
  4. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/backends/matplotlib/utils.py +0 -10
  5. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/canvas/canvas.py +226 -110
  6. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/colors/colors.py +0 -1
  7. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/subfigure/line_plot.py +26 -70
  8. maxplotlibx-0.1.2/src/maxplotlib/tests/test_canvas.py +6 -0
  9. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/tests/test_imports.py +1 -2
  10. maxplotlibx-0.1.2/src/maxplotlib/utils/__init__.py +0 -0
  11. maxplotlibx-0.1.2/src/maxplotlib/utils/options.py +3 -0
  12. {maxplotlibx-0.1 → maxplotlibx-0.1.2/src/maxplotlibx.egg-info}/PKG-INFO +2 -3
  13. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlibx.egg-info/SOURCES.txt +2 -3
  14. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlibx.egg-info/requires.txt +1 -0
  15. maxplotlibx-0.1/src/maxplotlib/backends/matplotlib/utils_old.py +0 -852
  16. maxplotlibx-0.1/src/maxplotlib/objects/layer.py +0 -20
  17. maxplotlibx-0.1/src/maxplotlib/subfigure/tikz_figure.py +0 -497
  18. maxplotlibx-0.1/src/maxplotlib/tests/test_canvas.py +0 -7
  19. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/LICENSE +0 -0
  20. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/setup.cfg +0 -0
  21. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/__init__.py +0 -0
  22. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/backends/plotly/__init__.py +0 -0
  23. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/backends/plotly/utils.py +0 -0
  24. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/canvas/__init__.py +0 -0
  25. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/colors/__init__.py +0 -0
  26. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/linestyle/__init__.py +0 -0
  27. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/linestyle/linestyle.py +0 -0
  28. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/objects/__init__.py +0 -0
  29. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/objects/node.py +0 -0
  30. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/objects/path.py +0 -0
  31. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/subfigure/__init__.py +0 -0
  32. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/subfigure/subfigure.py +0 -0
  33. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/tests/test_plot.py +0 -0
  34. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlibx.egg-info/dependency_links.txt +0 -0
  35. {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlibx.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maxplotlibx
3
- Version: 0.1
3
+ Version: 0.1.2
4
4
  Summary: A reproducible plotting module with various backends and export options.
5
5
  Author: Max
6
6
  Project-URL: Source, https://github.com/max-models/maxplotlib
@@ -12,6 +12,7 @@ License-File: LICENSE
12
12
  Requires-Dist: matplotlib
13
13
  Requires-Dist: pint
14
14
  Requires-Dist: plotly
15
+ Requires-Dist: tikzpics>=0.1.1
15
16
  Provides-Extra: test
16
17
  Requires-Dist: pytest; extra == "test"
17
18
  Requires-Dist: coverage; extra == "test"
@@ -37,8 +38,6 @@ Dynamic: license-file
37
38
 
38
39
  This is a wrapper for matplotlib so I can produce figures with consistent formatting. It also has some pretty nice additions such as using layers and exporting to tikz.
39
40
 
40
- Related packages: [maxtikzlib](https://github.com/max-models/maxtikzlib) and [maxtexlib](https://github.com/max-models/maxtexlib).
41
-
42
41
  ## Install
43
42
 
44
43
  Create and activate python environment
@@ -2,8 +2,6 @@
2
2
 
3
3
  This is a wrapper for matplotlib so I can produce figures with consistent formatting. It also has some pretty nice additions such as using layers and exporting to tikz.
4
4
 
5
- Related packages: [maxtikzlib](https://github.com/max-models/maxtikzlib) and [maxtexlib](https://github.com/max-models/maxtexlib).
6
-
7
5
  ## Install
8
6
 
9
7
  Create and activate python environment
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "maxplotlibx"
7
- version = "0.1"
7
+ version = "0.1.2"
8
8
  description = "A reproducible plotting module with various backends and export options."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -18,6 +18,7 @@ dependencies = [
18
18
  "matplotlib",
19
19
  "pint",
20
20
  "plotly",
21
+ "tikzpics>=0.1.1",
21
22
  ]
22
23
  [project.optional-dependencies]
23
24
  test = [
@@ -47,3 +48,12 @@ dev = [
47
48
 
48
49
  [tool.setuptools.packages.find]
49
50
  where = ["src"]
51
+
52
+ [tool.black]
53
+ line-length = 88
54
+
55
+ [tool.ruff]
56
+ line-length = 88
57
+
58
+ [tool.isort]
59
+ profile = "black"
@@ -1,18 +1,9 @@
1
1
  # import sys; from os.path import dirname; sys.path.append(f'{dirname(__file__)}/../../')
2
2
 
3
3
  # import matplotlib.pylab as pylab
4
- import math
5
- import pickle
6
- from pathlib import Path
7
4
 
8
- import _pickle as cPickle
9
- import matplotlib.colors as mcolors
10
5
  import matplotlib.pyplot as plt
11
- import numpy as np
12
6
  import pint
13
- from matplotlib.collections import PatchCollection
14
- from mpl_toolkits.mplot3d import Axes3D
15
- from mpl_toolkits.mplot3d.art3d import Line3DCollection, Poly3DCollection
16
7
 
17
8
 
18
9
  def setup_tex_fonts(fontsize=14, usetex=False):
@@ -71,7 +62,6 @@ def _2pt(width, dpi=300):
71
62
  elif isinstance(width, str):
72
63
  length_in = convert_to_inches(width)
73
64
  length_pt = length_in * dpi
74
- # print(f"{length_in = } {length_pt = }")
75
65
  return length_pt
76
66
  else:
77
67
  raise NotImplementedError
@@ -1,16 +1,38 @@
1
1
  import os
2
+ import re
3
+ from typing import Dict
2
4
 
5
+ import matplotlib.patches as patches
3
6
  import matplotlib.pyplot as plt
4
- import plotly.graph_objects as go
5
7
  from plotly.subplots import make_subplots
6
-
7
- import maxplotlib.backends.matplotlib.utils as plt_utils
8
+ from tikzpics import TikzFigure
9
+
10
+ from maxplotlib.backends.matplotlib.utils import (
11
+ set_size,
12
+ setup_plotstyle,
13
+ setup_tex_fonts,
14
+ )
15
+ from maxplotlib.colors.colors import Color
16
+ from maxplotlib.linestyle.linestyle import Linestyle
8
17
  from maxplotlib.subfigure.line_plot import LinePlot
9
- from maxplotlib.subfigure.tikz_figure import TikzFigure
18
+ from maxplotlib.utils.options import Backends
10
19
 
11
20
 
12
21
  class Canvas:
13
- def __init__(self, **kwargs):
22
+ def __init__(
23
+ self,
24
+ nrows: int = 1,
25
+ ncols: int = 1,
26
+ figsize: tuple | None = None,
27
+ caption: str | None = None,
28
+ description: str | None = None,
29
+ label: str | None = None,
30
+ fontsize: int = 14,
31
+ dpi: int = 300,
32
+ width: str = "17cm",
33
+ ratio: str = "golden", # TODO Add literal
34
+ gridspec_kw: Dict = {"wspace": 0.08, "hspace": 0.1},
35
+ ):
14
36
  """
15
37
  Initialize the Canvas class for multiple subplots.
16
38
 
@@ -18,21 +40,27 @@ class Canvas:
18
40
  nrows (int): Number of subplot rows. Default is 1.
19
41
  ncols (int): Number of subplot columns. Default is 1.
20
42
  figsize (tuple): Figure size.
43
+ caption (str): Caption for the figure.
44
+ description (str): Description for the figure.
45
+ label (str): Label for the figure.
46
+ fontsize (int): Font size. Default is 14.
47
+ dpi (int): DPI for the figure. Default is 300.
48
+ width (str): Width of the figure. Default is "17cm".
49
+ ratio (str): Aspect ratio. Default is "golden".
50
+ gridspec_kw (dict): Gridspec keyword arguments. Default is {"wspace": 0.08, "hspace": 0.1}.
21
51
  """
22
52
 
23
- # nrows=1, ncols=1, caption=None, description=None, label=None, figsize=None
24
- self._nrows = kwargs.get("nrows", 1)
25
- self._ncols = kwargs.get("ncols", 1)
26
- self._figsize = kwargs.get("figsize", None)
27
- self._caption = kwargs.get("caption", None)
28
- self._description = kwargs.get("description", None)
29
- self._label = kwargs.get("label", None)
30
- self._fontsize = kwargs.get("fontsize", 14)
31
- self._dpi = kwargs.get("dpi", 300)
32
- # self._width = kwargs.get("width", 426.79135)
33
- self._width = kwargs.get("width", "17cm")
34
- self._ratio = kwargs.get("ratio", "golden")
35
- self._gridspec_kw = kwargs.get("gridspec_kw", {"wspace": 0.08, "hspace": 0.1})
53
+ self._nrows = nrows
54
+ self._ncols = ncols
55
+ self._figsize = figsize
56
+ self._caption = caption
57
+ self._description = description
58
+ self._label = label
59
+ self._fontsize = fontsize
60
+ self._dpi = dpi
61
+ self._width = width
62
+ self._ratio = ratio
63
+ self._gridspec_kw = gridspec_kw
36
64
  self._plotted = False
37
65
 
38
66
  # Dictionary to store lines for each subplot
@@ -120,8 +148,6 @@ class Canvas:
120
148
 
121
149
  # Initialize the LinePlot for the given subplot position
122
150
  tikz_figure = TikzFigure(
123
- col=col,
124
- row=row,
125
151
  label=label,
126
152
  **kwargs,
127
153
  )
@@ -196,11 +222,10 @@ class Canvas:
196
222
  def savefig(
197
223
  self,
198
224
  filename,
199
- backend="matplotlib",
200
- layers=None,
201
- layer_by_layer=False,
202
- verbose=False,
203
- plot=True,
225
+ backend: Backends = "matplotlib",
226
+ layers: list | None = None,
227
+ layer_by_layer: bool = False,
228
+ verbose: bool = False,
204
229
  ):
205
230
  filename_no_extension, extension = os.path.splitext(filename)
206
231
  if backend == "matplotlib":
@@ -229,7 +254,6 @@ class Canvas:
229
254
  else:
230
255
 
231
256
  fig, axs = self.plot(
232
- show=False,
233
257
  backend="matplotlib",
234
258
  savefig=True,
235
259
  layers=layers,
@@ -238,20 +262,33 @@ class Canvas:
238
262
  if verbose:
239
263
  print(f"Saved {full_filepath}")
240
264
 
241
- def plot(self, backend="matplotlib", savefig=False, layers=None):
265
+ def plot(
266
+ self,
267
+ backend: Backends = "matplotlib",
268
+ savefig=False,
269
+ layers=None,
270
+ ):
242
271
  if backend == "matplotlib":
243
272
  return self.plot_matplotlib(savefig=savefig, layers=layers)
244
273
  elif backend == "plotly":
245
274
  return self.plot_plotly(savefig=savefig)
275
+ elif backend == "tikzpics":
276
+ return self.plot_tikzpics(savefig=savefig)
246
277
  else:
247
278
  raise ValueError(f"Invalid backend: {backend}")
248
279
 
249
- def show(self, backend="matplotlib"):
280
+ def show(
281
+ self,
282
+ backend: Backends = "matplotlib",
283
+ ):
250
284
  if backend == "matplotlib":
251
285
  self.plot(backend="matplotlib", savefig=False, layers=None)
252
286
  self._matplotlib_fig.show()
253
287
  elif backend == "plotly":
254
- plot = self.plot_plotly(savefig=False)
288
+ self.plot_plotly(savefig=False)
289
+ elif backend == "tikzpics":
290
+ fig = self.plot_tikzpics(savefig=False)
291
+ fig.show()
255
292
  else:
256
293
  raise ValueError("Invalid backend")
257
294
 
@@ -263,9 +300,9 @@ class Canvas:
263
300
  filename (str, optional): Filename to save the figure.
264
301
  """
265
302
 
266
- tex_fonts = plt_utils.setup_tex_fonts(fontsize=self.fontsize, usetex=usetex)
303
+ tex_fonts = setup_tex_fonts(fontsize=self.fontsize, usetex=usetex)
267
304
 
268
- plt_utils.setup_plotstyle(
305
+ setup_plotstyle(
269
306
  tex_fonts=tex_fonts,
270
307
  axes_grid=True,
271
308
  axes_grid_which="major",
@@ -276,25 +313,26 @@ class Canvas:
276
313
  if self._figsize is not None:
277
314
  fig_width, fig_height = self._figsize
278
315
  else:
279
- fig_width, fig_height = plt_utils.set_size(
316
+ fig_width, fig_height = set_size(
280
317
  width=self._width,
281
318
  ratio=self._ratio,
282
319
  dpi=self.dpi,
283
320
  )
284
321
 
285
- # print(f"{(fig_width / self._dpi, fig_height / self._dpi) = }")
286
-
287
322
  fig, axes = plt.subplots(
288
323
  self.nrows,
289
324
  self.ncols,
290
325
  figsize=(fig_width, fig_height),
291
326
  squeeze=False,
292
- dpi=self._dpi,
327
+ dpi=self.dpi,
293
328
  )
294
329
 
295
330
  for (row, col), subplot in self.subplots.items():
296
331
  ax = axes[row][col]
297
- subplot.plot_matplotlib(ax, layers=layers)
332
+ if isinstance(subplot, TikzFigure):
333
+ plot_matplotlib(subplot, ax, layers=layers)
334
+ else:
335
+ subplot.plot_matplotlib(ax, layers=layers)
298
336
  # ax.set_title(f"Subplot ({row}, {col})")
299
337
  ax.grid()
300
338
 
@@ -304,6 +342,22 @@ class Canvas:
304
342
  self._matplotlib_axes = axes
305
343
  return fig, axes
306
344
 
345
+ def plot_tikzpics(
346
+ self,
347
+ savefig=None,
348
+ verbose=False,
349
+ ) -> TikzFigure:
350
+ if len(self.subplots) > 1:
351
+ raise NotImplementedError(
352
+ "Only one subplot is supported for tikzpics backend."
353
+ )
354
+ for (row, col), line_plot in self.subplots.items():
355
+ if verbose:
356
+ print(f"Plotting subplot at row {row}, col {col}")
357
+ print(f"{line_plot = }")
358
+ tikz_subplot = line_plot.plot_tikzpics(verbose=verbose)
359
+ return tikz_subplot
360
+
307
361
  def plot_plotly(self, show=True, savefig=None, usetex=False):
308
362
  """
309
363
  Generate and optionally display the subplots using Plotly.
@@ -313,7 +367,7 @@ class Canvas:
313
367
  savefig (str, optional): Filename to save the figure if provided.
314
368
  """
315
369
 
316
- tex_fonts = plt_utils.setup_tex_fonts(
370
+ setup_tex_fonts(
317
371
  fontsize=self.fontsize,
318
372
  usetex=usetex,
319
373
  ) # adjust or redefine for Plotly if needed
@@ -322,7 +376,7 @@ class Canvas:
322
376
  if self._figsize is not None:
323
377
  fig_width, fig_height = self._figsize
324
378
  else:
325
- fig_width, fig_height = plt_utils.set_size(
379
+ fig_width, fig_height = set_size(
326
380
  width=self._width,
327
381
  ratio=self._ratio,
328
382
  )
@@ -398,9 +452,6 @@ class Canvas:
398
452
  return self._subplot_matrix
399
453
 
400
454
  # Property setters
401
- @nrows.setter
402
- def dpi(self, value):
403
- self._dpi = value
404
455
 
405
456
  @nrows.setter
406
457
  def nrows(self, value):
@@ -447,75 +498,140 @@ class Canvas:
447
498
  raise IndexError("Subplot index out of range")
448
499
  self._subplot_matrix[row][col] = value
449
500
 
450
- # def generate_matplotlib_code(self):
451
- # """Generate code for plotting the data using matplotlib."""
452
- # code = "import matplotlib.pyplot as plt\n\n"
453
- # code += f"fig, axes = plt.subplots({self.nrows}, {self.ncols}, figsize={self.figsize})\n\n"
454
- # if self.nrows == 1 and self.ncols == 1:
455
- # code += "axes = [axes] # Single subplot\n\n"
456
- # else:
457
- # code += "axes = axes.flatten()\n\n"
458
- # for idx, (subplot_idx, lines) in enumerate(self.subplots.items()):
459
- # code += f"# Subplot {subplot_idx}\n"
460
- # code += f"ax = axes[{idx}]\n"
461
- # for line in lines:
462
- # x_data = line['x']
463
- # y_data = line['y']
464
- # label = line['label']
465
- # kwargs = line.get('kwargs', {})
466
- # kwargs_str = ', '.join(f"{k}={repr(v)}" for k, v in kwargs.items())
467
- # code += f"ax.plot({x_data}, {y_data}, label={repr(label)}"
468
- # if kwargs_str:
469
- # code += f", {kwargs_str}"
470
- # code += ")\n"
471
- # code += "ax.set_xlabel('X-axis')\n"
472
- # code += "ax.set_ylabel('Y-axis')\n"
473
- # if self.nrows * self.ncols > 1:
474
- # code += f"ax.set_title('Subplot {subplot_idx}')\n"
475
- # code += "ax.legend()\n\n"
476
- # code += "plt.tight_layout()\nplt.show()\n"
477
- # return code
478
-
479
- # def generate_latex_plot(self):
480
- # """Generate LaTeX code for plotting the data using pgfplots in subplots."""
481
- # latex_code = "\\begin{figure}[h!]\n\\centering\n"
482
- # total_subplots = self.nrows * self.ncols
483
- # for idx in range(total_subplots):
484
- # subplot_idx = divmod(idx, self.ncols)
485
- # lines = self.subplots.get(subplot_idx, [])
486
- # if not lines:
487
- # continue # Skip empty subplots
488
- # latex_code += "\\begin{subfigure}[b]{0.45\\textwidth}\n"
489
- # latex_code += " \\begin{tikzpicture}\n"
490
- # latex_code += " \\begin{axis}[\n"
491
- # latex_code += " xlabel={X-axis},\n"
492
- # latex_code += " ylabel={Y-axis},\n"
493
- # if self.nrows * self.ncols > 1:
494
- # latex_code += f" title={{Subplot {subplot_idx}}},\n"
495
- # latex_code += " legend style={at={(1.05,1)}, anchor=north west},\n"
496
- # latex_code += " legend entries={" + ", ".join(f"{{{line['label']}}}" for line in lines) + "}\n"
497
- # latex_code += " ]\n"
498
- # for line in lines:
499
- # options = []
500
- # kwargs = line.get('kwargs', {})
501
- # if 'color' in kwargs:
502
- # options.append(f"color={kwargs['color']}")
503
- # if 'linestyle' in kwargs:
504
- # linestyle_map = {'-': 'solid', '--': 'dashed', '-.': 'dash dot', ':': 'dotted'}
505
- # linestyle = linestyle_map.get(kwargs['linestyle'], kwargs['linestyle'])
506
- # options.append(f"style={linestyle}")
507
- # options_str = f"[{', '.join(options)}]" if options else ""
508
- # latex_code += f" \\addplot {options_str} coordinates {{\n"
509
- # for x, y in zip(line['x'], line['y']):
510
- # latex_code += f" ({x}, {y})\n"
511
- # latex_code += " };\n"
512
- # latex_code += " \\end{axis}\n"
513
- # latex_code += " \\end{tikzpicture}\n"
514
- # latex_code += "\\end{subfigure}\n"
515
- # latex_code += "\\hfill\n" if (idx + 1) % self.ncols != 0 else "\n"
516
- # latex_code += "\\caption{Multiple Subplots}\n"
517
- # latex_code += "\\end{figure}\n"
518
- # return latex_code
501
+
502
+ def plot_matplotlib(tikzfigure: TikzFigure, ax, layers=None):
503
+ """
504
+ Plot all nodes and paths on the provided axis using Matplotlib.
505
+
506
+ Parameters:
507
+ - ax (matplotlib.axes.Axes): Axis on which to plot the figure.
508
+ """
509
+
510
+ # TODO: Specify which layers to retreive nodes from with layers=layers
511
+ nodes = tikzfigure.layers.get_nodes()
512
+ paths = tikzfigure.layers.get_paths()
513
+
514
+ for path in paths:
515
+ x_coords = [node.x for node in path.nodes]
516
+ y_coords = [node.y for node in path.nodes]
517
+
518
+ # Parse path color
519
+ path_color_spec = path.kwargs.get("color", "black")
520
+ try:
521
+ color = Color(path_color_spec).to_rgb()
522
+ except ValueError as e:
523
+ print(e)
524
+ color = "black"
525
+
526
+ # Parse line width
527
+ line_width_spec = path.kwargs.get("line_width", 1)
528
+ if isinstance(line_width_spec, str):
529
+ match = re.match(r"([\d.]+)(pt)?", line_width_spec)
530
+ if match:
531
+ line_width = float(match.group(1))
532
+ else:
533
+ print(
534
+ f"Invalid line width specification: '{line_width_spec}', defaulting to 1",
535
+ )
536
+ line_width = 1
537
+ else:
538
+ line_width = float(line_width_spec)
539
+
540
+ # Parse line style using Linestyle class
541
+ style_spec = path.kwargs.get("style", "solid")
542
+ linestyle = Linestyle(style_spec).to_matplotlib()
543
+
544
+ ax.plot(
545
+ x_coords,
546
+ y_coords,
547
+ color=color,
548
+ linewidth=line_width,
549
+ linestyle=linestyle,
550
+ zorder=1, # Lower z-order to place behind nodes
551
+ )
552
+
553
+ # Plot nodes after paths so they appear on top
554
+ for node in nodes:
555
+ # Determine shape and size
556
+ shape = node.kwargs.get("shape", "circle")
557
+ fill_color_spec = node.kwargs.get("fill", "white")
558
+ edge_color_spec = node.kwargs.get("draw", "black")
559
+ linewidth = float(node.kwargs.get("line_width", 1))
560
+ size = float(node.kwargs.get("size", 1))
561
+
562
+ # Parse colors using the Color class
563
+ try:
564
+ facecolor = Color(fill_color_spec).to_rgb()
565
+ except ValueError as e:
566
+ print(e)
567
+ facecolor = "white"
568
+
569
+ try:
570
+ edgecolor = Color(edge_color_spec).to_rgb()
571
+ except ValueError as e:
572
+ print(e)
573
+ edgecolor = "black"
574
+
575
+ # Plot shapes
576
+ if shape == "circle":
577
+ radius = size / 2
578
+ circle = patches.Circle(
579
+ (node.x, node.y),
580
+ radius,
581
+ facecolor=facecolor,
582
+ edgecolor=edgecolor,
583
+ linewidth=linewidth,
584
+ zorder=2, # Higher z-order to place on top of paths
585
+ )
586
+ ax.add_patch(circle)
587
+ elif shape == "rectangle":
588
+ width = height = size
589
+ rect = patches.Rectangle(
590
+ (node.x - width / 2, node.y - height / 2),
591
+ width,
592
+ height,
593
+ facecolor=facecolor,
594
+ edgecolor=edgecolor,
595
+ linewidth=linewidth,
596
+ zorder=2, # Higher z-order
597
+ )
598
+ ax.add_patch(rect)
599
+ else:
600
+ # Default to circle if shape is unknown
601
+ radius = size / 2
602
+ circle = patches.Circle(
603
+ (node.x, node.y),
604
+ radius,
605
+ facecolor=facecolor,
606
+ edgecolor=edgecolor,
607
+ linewidth=linewidth,
608
+ zorder=2,
609
+ )
610
+ ax.add_patch(circle)
611
+
612
+ # Add text inside the shape
613
+ if node.content:
614
+ ax.text(
615
+ node.x,
616
+ node.y,
617
+ node.content,
618
+ fontsize=10,
619
+ ha="center",
620
+ va="center",
621
+ wrap=True,
622
+ zorder=3, # Even higher z-order for text
623
+ )
624
+
625
+ # Remove axes, ticks, and legend
626
+ ax.axis("off")
627
+
628
+ # Adjust plot limits
629
+ all_x = [node.x for node in nodes]
630
+ all_y = [node.y for node in nodes]
631
+ padding = 1 # Adjust padding as needed
632
+ ax.set_xlim(min(all_x) - padding, max(all_x) + padding)
633
+ ax.set_ylim(min(all_y) - padding, max(all_y) + padding)
634
+ ax.set_aspect("equal", adjustable="datalim")
519
635
 
520
636
 
521
637
  if __name__ == "__main__":
@@ -1,7 +1,6 @@
1
1
  import re
2
2
 
3
3
  import matplotlib.colors as mcolors
4
- import matplotlib.patches as patches
5
4
  import numpy as np
6
5
 
7
6