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.
- {maxplotlibx-0.1/src/maxplotlibx.egg-info → maxplotlibx-0.1.2}/PKG-INFO +2 -3
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/README.md +0 -2
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/pyproject.toml +11 -1
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/backends/matplotlib/utils.py +0 -10
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/canvas/canvas.py +226 -110
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/colors/colors.py +0 -1
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/subfigure/line_plot.py +26 -70
- maxplotlibx-0.1.2/src/maxplotlib/tests/test_canvas.py +6 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/tests/test_imports.py +1 -2
- maxplotlibx-0.1.2/src/maxplotlib/utils/__init__.py +0 -0
- maxplotlibx-0.1.2/src/maxplotlib/utils/options.py +3 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2/src/maxplotlibx.egg-info}/PKG-INFO +2 -3
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlibx.egg-info/SOURCES.txt +2 -3
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlibx.egg-info/requires.txt +1 -0
- maxplotlibx-0.1/src/maxplotlib/backends/matplotlib/utils_old.py +0 -852
- maxplotlibx-0.1/src/maxplotlib/objects/layer.py +0 -20
- maxplotlibx-0.1/src/maxplotlib/subfigure/tikz_figure.py +0 -497
- maxplotlibx-0.1/src/maxplotlib/tests/test_canvas.py +0 -7
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/LICENSE +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/setup.cfg +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/__init__.py +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/backends/plotly/__init__.py +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/backends/plotly/utils.py +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/canvas/__init__.py +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/colors/__init__.py +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/linestyle/__init__.py +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/linestyle/linestyle.py +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/objects/__init__.py +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/objects/node.py +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/objects/path.py +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/subfigure/__init__.py +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/subfigure/subfigure.py +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlib/tests/test_plot.py +0 -0
- {maxplotlibx-0.1 → maxplotlibx-0.1.2}/src/maxplotlibx.egg-info/dependency_links.txt +0 -0
- {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
|
-
|
|
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.
|
|
18
|
+
from maxplotlib.utils.options import Backends
|
|
10
19
|
|
|
11
20
|
|
|
12
21
|
class Canvas:
|
|
13
|
-
def __init__(
|
|
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
|
-
|
|
24
|
-
self.
|
|
25
|
-
self.
|
|
26
|
-
self.
|
|
27
|
-
self.
|
|
28
|
-
self.
|
|
29
|
-
self.
|
|
30
|
-
self.
|
|
31
|
-
self.
|
|
32
|
-
|
|
33
|
-
self.
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
303
|
+
tex_fonts = setup_tex_fonts(fontsize=self.fontsize, usetex=usetex)
|
|
267
304
|
|
|
268
|
-
|
|
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 =
|
|
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.
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
#
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
#
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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__":
|