maxplotlibx 0.1.1__tar.gz → 0.1.3__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.1 → maxplotlibx-0.1.3}/PKG-INFO +2 -1
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/pyproject.toml +2 -1
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/backends/matplotlib/utils.py +22 -14
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/canvas/canvas.py +212 -91
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/colors/colors.py +0 -1
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/subfigure/line_plot.py +16 -65
- maxplotlibx-0.1.3/src/maxplotlib/tests/test_canvas.py +6 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/tests/test_imports.py +1 -2
- maxplotlibx-0.1.3/src/maxplotlib/utils/options.py +3 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlibx.egg-info/PKG-INFO +2 -1
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlibx.egg-info/SOURCES.txt +0 -3
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlibx.egg-info/requires.txt +1 -0
- maxplotlibx-0.1.1/src/maxplotlib/backends/matplotlib/utils_old.py +0 -852
- maxplotlibx-0.1.1/src/maxplotlib/objects/layer.py +0 -20
- maxplotlibx-0.1.1/src/maxplotlib/subfigure/tikz_figure.py +0 -497
- maxplotlibx-0.1.1/src/maxplotlib/tests/test_canvas.py +0 -7
- maxplotlibx-0.1.1/src/maxplotlib/utils/options.py +0 -3
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/LICENSE +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/README.md +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/setup.cfg +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/__init__.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/backends/plotly/__init__.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/backends/plotly/utils.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/canvas/__init__.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/colors/__init__.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/linestyle/__init__.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/linestyle/linestyle.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/objects/__init__.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/objects/node.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/objects/path.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/subfigure/__init__.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/subfigure/subfigure.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/tests/test_plot.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/utils/__init__.py +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlibx.egg-info/dependency_links.txt +0 -0
- {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/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.3
|
|
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"
|
|
@@ -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.3"
|
|
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 = [
|
|
@@ -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):
|
|
@@ -65,19 +56,30 @@ def convert_to_inches(length_str):
|
|
|
65
56
|
return quantity.to("inch").magnitude # Convert to inches
|
|
66
57
|
|
|
67
58
|
|
|
68
|
-
def _2pt(width, dpi=300):
|
|
59
|
+
def _2pt(width, dpi=300, verbose: bool = False):
|
|
60
|
+
if verbose:
|
|
61
|
+
print(f"Converting width: {width} to points with dpi={dpi}")
|
|
62
|
+
|
|
69
63
|
if isinstance(width, (int, float)):
|
|
70
64
|
return width
|
|
71
65
|
elif isinstance(width, str):
|
|
72
66
|
length_in = convert_to_inches(width)
|
|
73
67
|
length_pt = length_in * dpi
|
|
74
|
-
|
|
68
|
+
if verbose:
|
|
69
|
+
print(f"Converted length: {length_in} inches = {length_pt} points")
|
|
75
70
|
return length_pt
|
|
76
71
|
else:
|
|
77
72
|
raise NotImplementedError
|
|
78
73
|
|
|
79
74
|
|
|
80
|
-
|
|
75
|
+
# TODO: Use literal types for width and ratio
|
|
76
|
+
def set_size(
|
|
77
|
+
width: str,
|
|
78
|
+
fraction: int | float = 1,
|
|
79
|
+
ratio: str | int | float = "golden",
|
|
80
|
+
dpi=300,
|
|
81
|
+
verbose: bool = False,
|
|
82
|
+
) -> tuple:
|
|
81
83
|
"""
|
|
82
84
|
Sets figure dimensions to avoid scaling in LaTeX.
|
|
83
85
|
"""
|
|
@@ -86,9 +88,11 @@ def set_size(width, fraction=1, ratio="golden", dpi=300):
|
|
|
86
88
|
elif width == "beamer":
|
|
87
89
|
width_pt = 307.28987
|
|
88
90
|
else:
|
|
89
|
-
width_pt = _2pt(width=width, dpi=dpi)
|
|
91
|
+
width_pt = _2pt(width=width, dpi=dpi, verbose=verbose)
|
|
90
92
|
|
|
91
93
|
fig_width_pt = width_pt * fraction
|
|
94
|
+
inches_per_pt = 1 / 72.27
|
|
95
|
+
# fig_width_pt = width_pt * fraction
|
|
92
96
|
# inches_per_pt = 1 / 72.27
|
|
93
97
|
|
|
94
98
|
# Calculate the figure height based on the desired ratio
|
|
@@ -101,7 +105,11 @@ def set_size(width, fraction=1, ratio="golden", dpi=300):
|
|
|
101
105
|
fig_height_pt = fig_width_pt * ratio
|
|
102
106
|
else:
|
|
103
107
|
raise ValueError("Invalid ratio specified.")
|
|
104
|
-
|
|
108
|
+
|
|
109
|
+
# Convert from points to inches for matplotlib
|
|
110
|
+
fig_width_in = fig_width_pt * inches_per_pt
|
|
111
|
+
fig_height_in = fig_height_pt * inches_per_pt
|
|
112
|
+
fig_dim = (fig_width_in, fig_height_in)
|
|
105
113
|
return fig_dim
|
|
106
114
|
|
|
107
115
|
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import re
|
|
2
3
|
from typing import Dict
|
|
3
4
|
|
|
5
|
+
import matplotlib.patches as patches
|
|
4
6
|
import matplotlib.pyplot as plt
|
|
5
|
-
import plotly.graph_objects as go
|
|
6
7
|
from plotly.subplots import make_subplots
|
|
8
|
+
from tikzpics import TikzFigure
|
|
7
9
|
|
|
8
10
|
from maxplotlib.backends.matplotlib.utils import (
|
|
9
11
|
set_size,
|
|
10
12
|
setup_plotstyle,
|
|
11
13
|
setup_tex_fonts,
|
|
12
14
|
)
|
|
15
|
+
from maxplotlib.colors.colors import Color
|
|
16
|
+
from maxplotlib.linestyle.linestyle import Linestyle
|
|
13
17
|
from maxplotlib.subfigure.line_plot import LinePlot
|
|
14
|
-
from maxplotlib.subfigure.tikz_figure import TikzFigure
|
|
15
18
|
from maxplotlib.utils.options import Backends
|
|
16
19
|
|
|
17
20
|
|
|
@@ -145,8 +148,6 @@ class Canvas:
|
|
|
145
148
|
|
|
146
149
|
# Initialize the LinePlot for the given subplot position
|
|
147
150
|
tikz_figure = TikzFigure(
|
|
148
|
-
col=col,
|
|
149
|
-
row=row,
|
|
150
151
|
label=label,
|
|
151
152
|
**kwargs,
|
|
152
153
|
)
|
|
@@ -225,7 +226,6 @@ class Canvas:
|
|
|
225
226
|
layers: list | None = None,
|
|
226
227
|
layer_by_layer: bool = False,
|
|
227
228
|
verbose: bool = False,
|
|
228
|
-
plot: bool = True,
|
|
229
229
|
):
|
|
230
230
|
filename_no_extension, extension = os.path.splitext(filename)
|
|
231
231
|
if backend == "matplotlib":
|
|
@@ -254,7 +254,6 @@ class Canvas:
|
|
|
254
254
|
else:
|
|
255
255
|
|
|
256
256
|
fig, axs = self.plot(
|
|
257
|
-
show=False,
|
|
258
257
|
backend="matplotlib",
|
|
259
258
|
savefig=True,
|
|
260
259
|
layers=layers,
|
|
@@ -263,30 +262,68 @@ class Canvas:
|
|
|
263
262
|
if verbose:
|
|
264
263
|
print(f"Saved {full_filepath}")
|
|
265
264
|
|
|
266
|
-
def plot(
|
|
265
|
+
def plot(
|
|
266
|
+
self,
|
|
267
|
+
backend: Backends = "matplotlib",
|
|
268
|
+
savefig=False,
|
|
269
|
+
layers=None,
|
|
270
|
+
verbose: bool = False,
|
|
271
|
+
):
|
|
272
|
+
if verbose:
|
|
273
|
+
print(f"Plotting figure using backend: {backend}")
|
|
274
|
+
|
|
267
275
|
if backend == "matplotlib":
|
|
268
|
-
return self.plot_matplotlib(
|
|
276
|
+
return self.plot_matplotlib(
|
|
277
|
+
savefig=savefig,
|
|
278
|
+
layers=layers,
|
|
279
|
+
verbose=verbose,
|
|
280
|
+
)
|
|
269
281
|
elif backend == "plotly":
|
|
270
282
|
return self.plot_plotly(savefig=savefig)
|
|
283
|
+
elif backend == "tikzpics":
|
|
284
|
+
return self.plot_tikzpics(savefig=savefig)
|
|
271
285
|
else:
|
|
272
286
|
raise ValueError(f"Invalid backend: {backend}")
|
|
273
287
|
|
|
274
|
-
def show(
|
|
288
|
+
def show(
|
|
289
|
+
self,
|
|
290
|
+
backend: Backends = "matplotlib",
|
|
291
|
+
verbose: bool = False,
|
|
292
|
+
):
|
|
293
|
+
if verbose:
|
|
294
|
+
print(f"Showing figure using backend: {backend}")
|
|
295
|
+
|
|
275
296
|
if backend == "matplotlib":
|
|
276
|
-
self.plot(
|
|
277
|
-
|
|
297
|
+
self.plot(
|
|
298
|
+
backend="matplotlib",
|
|
299
|
+
savefig=False,
|
|
300
|
+
layers=None,
|
|
301
|
+
verbose=verbose,
|
|
302
|
+
)
|
|
303
|
+
# self._matplotlib_fig.show()
|
|
278
304
|
elif backend == "plotly":
|
|
279
|
-
|
|
305
|
+
self.plot_plotly(savefig=False)
|
|
306
|
+
elif backend == "tikzpics":
|
|
307
|
+
fig = self.plot_tikzpics(savefig=False)
|
|
308
|
+
fig.show()
|
|
280
309
|
else:
|
|
281
310
|
raise ValueError("Invalid backend")
|
|
282
311
|
|
|
283
|
-
def plot_matplotlib(
|
|
312
|
+
def plot_matplotlib(
|
|
313
|
+
self,
|
|
314
|
+
savefig: bool = False,
|
|
315
|
+
layers: list | None = None,
|
|
316
|
+
usetex: bool = False,
|
|
317
|
+
verbose: bool = False,
|
|
318
|
+
):
|
|
284
319
|
"""
|
|
285
320
|
Generate and optionally display the subplots.
|
|
286
321
|
|
|
287
322
|
Parameters:
|
|
288
323
|
filename (str, optional): Filename to save the figure.
|
|
289
324
|
"""
|
|
325
|
+
if verbose:
|
|
326
|
+
print("Generating Matplotlib figure...")
|
|
290
327
|
|
|
291
328
|
tex_fonts = setup_tex_fonts(fontsize=self.fontsize, usetex=usetex)
|
|
292
329
|
|
|
@@ -297,7 +334,9 @@ class Canvas:
|
|
|
297
334
|
grid_alpha=1.0,
|
|
298
335
|
grid_linestyle="dotted",
|
|
299
336
|
)
|
|
300
|
-
|
|
337
|
+
if verbose:
|
|
338
|
+
print("Plot style set up.")
|
|
339
|
+
print(f"{self._figsize = } {self._width = } {self._ratio = }")
|
|
301
340
|
if self._figsize is not None:
|
|
302
341
|
fig_width, fig_height = self._figsize
|
|
303
342
|
else:
|
|
@@ -305,21 +344,25 @@ class Canvas:
|
|
|
305
344
|
width=self._width,
|
|
306
345
|
ratio=self._ratio,
|
|
307
346
|
dpi=self.dpi,
|
|
347
|
+
verbose=verbose,
|
|
308
348
|
)
|
|
309
|
-
|
|
310
|
-
|
|
349
|
+
if verbose:
|
|
350
|
+
print(f"Figure size: {fig_width} x {fig_height} points")
|
|
311
351
|
|
|
312
352
|
fig, axes = plt.subplots(
|
|
313
353
|
self.nrows,
|
|
314
354
|
self.ncols,
|
|
315
355
|
figsize=(fig_width, fig_height),
|
|
316
356
|
squeeze=False,
|
|
317
|
-
dpi=self.
|
|
357
|
+
dpi=self.dpi,
|
|
318
358
|
)
|
|
319
359
|
|
|
320
360
|
for (row, col), subplot in self.subplots.items():
|
|
321
361
|
ax = axes[row][col]
|
|
322
|
-
subplot
|
|
362
|
+
if isinstance(subplot, TikzFigure):
|
|
363
|
+
plot_matplotlib(subplot, ax, layers=layers)
|
|
364
|
+
else:
|
|
365
|
+
subplot.plot_matplotlib(ax, layers=layers)
|
|
323
366
|
# ax.set_title(f"Subplot ({row}, {col})")
|
|
324
367
|
ax.grid()
|
|
325
368
|
|
|
@@ -329,6 +372,22 @@ class Canvas:
|
|
|
329
372
|
self._matplotlib_axes = axes
|
|
330
373
|
return fig, axes
|
|
331
374
|
|
|
375
|
+
def plot_tikzpics(
|
|
376
|
+
self,
|
|
377
|
+
savefig=None,
|
|
378
|
+
verbose=False,
|
|
379
|
+
) -> TikzFigure:
|
|
380
|
+
if len(self.subplots) > 1:
|
|
381
|
+
raise NotImplementedError(
|
|
382
|
+
"Only one subplot is supported for tikzpics backend."
|
|
383
|
+
)
|
|
384
|
+
for (row, col), line_plot in self.subplots.items():
|
|
385
|
+
if verbose:
|
|
386
|
+
print(f"Plotting subplot at row {row}, col {col}")
|
|
387
|
+
print(f"{line_plot = }")
|
|
388
|
+
tikz_subplot = line_plot.plot_tikzpics(verbose=verbose)
|
|
389
|
+
return tikz_subplot
|
|
390
|
+
|
|
332
391
|
def plot_plotly(self, show=True, savefig=None, usetex=False):
|
|
333
392
|
"""
|
|
334
393
|
Generate and optionally display the subplots using Plotly.
|
|
@@ -338,7 +397,7 @@ class Canvas:
|
|
|
338
397
|
savefig (str, optional): Filename to save the figure if provided.
|
|
339
398
|
"""
|
|
340
399
|
|
|
341
|
-
|
|
400
|
+
setup_tex_fonts(
|
|
342
401
|
fontsize=self.fontsize,
|
|
343
402
|
usetex=usetex,
|
|
344
403
|
) # adjust or redefine for Plotly if needed
|
|
@@ -423,9 +482,6 @@ class Canvas:
|
|
|
423
482
|
return self._subplot_matrix
|
|
424
483
|
|
|
425
484
|
# Property setters
|
|
426
|
-
@nrows.setter
|
|
427
|
-
def dpi(self, value):
|
|
428
|
-
self._dpi = value
|
|
429
485
|
|
|
430
486
|
@nrows.setter
|
|
431
487
|
def nrows(self, value):
|
|
@@ -472,75 +528,140 @@ class Canvas:
|
|
|
472
528
|
raise IndexError("Subplot index out of range")
|
|
473
529
|
self._subplot_matrix[row][col] = value
|
|
474
530
|
|
|
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
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
#
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
531
|
+
|
|
532
|
+
def plot_matplotlib(tikzfigure: TikzFigure, ax, layers=None):
|
|
533
|
+
"""
|
|
534
|
+
Plot all nodes and paths on the provided axis using Matplotlib.
|
|
535
|
+
|
|
536
|
+
Parameters:
|
|
537
|
+
- ax (matplotlib.axes.Axes): Axis on which to plot the figure.
|
|
538
|
+
"""
|
|
539
|
+
|
|
540
|
+
# TODO: Specify which layers to retreive nodes from with layers=layers
|
|
541
|
+
nodes = tikzfigure.layers.get_nodes()
|
|
542
|
+
paths = tikzfigure.layers.get_paths()
|
|
543
|
+
|
|
544
|
+
for path in paths:
|
|
545
|
+
x_coords = [node.x for node in path.nodes]
|
|
546
|
+
y_coords = [node.y for node in path.nodes]
|
|
547
|
+
|
|
548
|
+
# Parse path color
|
|
549
|
+
path_color_spec = path.kwargs.get("color", "black")
|
|
550
|
+
try:
|
|
551
|
+
color = Color(path_color_spec).to_rgb()
|
|
552
|
+
except ValueError as e:
|
|
553
|
+
print(e)
|
|
554
|
+
color = "black"
|
|
555
|
+
|
|
556
|
+
# Parse line width
|
|
557
|
+
line_width_spec = path.kwargs.get("line_width", 1)
|
|
558
|
+
if isinstance(line_width_spec, str):
|
|
559
|
+
match = re.match(r"([\d.]+)(pt)?", line_width_spec)
|
|
560
|
+
if match:
|
|
561
|
+
line_width = float(match.group(1))
|
|
562
|
+
else:
|
|
563
|
+
print(
|
|
564
|
+
f"Invalid line width specification: '{line_width_spec}', defaulting to 1",
|
|
565
|
+
)
|
|
566
|
+
line_width = 1
|
|
567
|
+
else:
|
|
568
|
+
line_width = float(line_width_spec)
|
|
569
|
+
|
|
570
|
+
# Parse line style using Linestyle class
|
|
571
|
+
style_spec = path.kwargs.get("style", "solid")
|
|
572
|
+
linestyle = Linestyle(style_spec).to_matplotlib()
|
|
573
|
+
|
|
574
|
+
ax.plot(
|
|
575
|
+
x_coords,
|
|
576
|
+
y_coords,
|
|
577
|
+
color=color,
|
|
578
|
+
linewidth=line_width,
|
|
579
|
+
linestyle=linestyle,
|
|
580
|
+
zorder=1, # Lower z-order to place behind nodes
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
# Plot nodes after paths so they appear on top
|
|
584
|
+
for node in nodes:
|
|
585
|
+
# Determine shape and size
|
|
586
|
+
shape = node.kwargs.get("shape", "circle")
|
|
587
|
+
fill_color_spec = node.kwargs.get("fill", "white")
|
|
588
|
+
edge_color_spec = node.kwargs.get("draw", "black")
|
|
589
|
+
linewidth = float(node.kwargs.get("line_width", 1))
|
|
590
|
+
size = float(node.kwargs.get("size", 1))
|
|
591
|
+
|
|
592
|
+
# Parse colors using the Color class
|
|
593
|
+
try:
|
|
594
|
+
facecolor = Color(fill_color_spec).to_rgb()
|
|
595
|
+
except ValueError as e:
|
|
596
|
+
print(e)
|
|
597
|
+
facecolor = "white"
|
|
598
|
+
|
|
599
|
+
try:
|
|
600
|
+
edgecolor = Color(edge_color_spec).to_rgb()
|
|
601
|
+
except ValueError as e:
|
|
602
|
+
print(e)
|
|
603
|
+
edgecolor = "black"
|
|
604
|
+
|
|
605
|
+
# Plot shapes
|
|
606
|
+
if shape == "circle":
|
|
607
|
+
radius = size / 2
|
|
608
|
+
circle = patches.Circle(
|
|
609
|
+
(node.x, node.y),
|
|
610
|
+
radius,
|
|
611
|
+
facecolor=facecolor,
|
|
612
|
+
edgecolor=edgecolor,
|
|
613
|
+
linewidth=linewidth,
|
|
614
|
+
zorder=2, # Higher z-order to place on top of paths
|
|
615
|
+
)
|
|
616
|
+
ax.add_patch(circle)
|
|
617
|
+
elif shape == "rectangle":
|
|
618
|
+
width = height = size
|
|
619
|
+
rect = patches.Rectangle(
|
|
620
|
+
(node.x - width / 2, node.y - height / 2),
|
|
621
|
+
width,
|
|
622
|
+
height,
|
|
623
|
+
facecolor=facecolor,
|
|
624
|
+
edgecolor=edgecolor,
|
|
625
|
+
linewidth=linewidth,
|
|
626
|
+
zorder=2, # Higher z-order
|
|
627
|
+
)
|
|
628
|
+
ax.add_patch(rect)
|
|
629
|
+
else:
|
|
630
|
+
# Default to circle if shape is unknown
|
|
631
|
+
radius = size / 2
|
|
632
|
+
circle = patches.Circle(
|
|
633
|
+
(node.x, node.y),
|
|
634
|
+
radius,
|
|
635
|
+
facecolor=facecolor,
|
|
636
|
+
edgecolor=edgecolor,
|
|
637
|
+
linewidth=linewidth,
|
|
638
|
+
zorder=2,
|
|
639
|
+
)
|
|
640
|
+
ax.add_patch(circle)
|
|
641
|
+
|
|
642
|
+
# Add text inside the shape
|
|
643
|
+
if node.content:
|
|
644
|
+
ax.text(
|
|
645
|
+
node.x,
|
|
646
|
+
node.y,
|
|
647
|
+
node.content,
|
|
648
|
+
fontsize=10,
|
|
649
|
+
ha="center",
|
|
650
|
+
va="center",
|
|
651
|
+
wrap=True,
|
|
652
|
+
zorder=3, # Even higher z-order for text
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
# Remove axes, ticks, and legend
|
|
656
|
+
ax.axis("off")
|
|
657
|
+
|
|
658
|
+
# Adjust plot limits
|
|
659
|
+
all_x = [node.x for node in nodes]
|
|
660
|
+
all_y = [node.y for node in nodes]
|
|
661
|
+
padding = 1 # Adjust padding as needed
|
|
662
|
+
ax.set_xlim(min(all_x) - padding, max(all_x) + padding)
|
|
663
|
+
ax.set_ylim(min(all_y) - padding, max(all_y) + padding)
|
|
664
|
+
ax.set_aspect("equal", adjustable="datalim")
|
|
544
665
|
|
|
545
666
|
|
|
546
667
|
if __name__ == "__main__":
|
|
@@ -2,9 +2,7 @@ import matplotlib.pyplot as plt
|
|
|
2
2
|
import numpy as np
|
|
3
3
|
import plotly.graph_objects as go
|
|
4
4
|
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
5
|
-
|
|
6
|
-
import maxplotlib.subfigure.tikz_figure as tf
|
|
7
|
-
from maxplotlib.objects.layer import Tikzlayer
|
|
5
|
+
from tikzpics import TikzFigure
|
|
8
6
|
|
|
9
7
|
|
|
10
8
|
class Node:
|
|
@@ -224,6 +222,21 @@ class LinePlot:
|
|
|
224
222
|
if self.ymax is not None:
|
|
225
223
|
ax.axis(ymax=self.ymax)
|
|
226
224
|
|
|
225
|
+
def plot_tikzpics(self, layers=None, verbose: bool = False) -> TikzFigure:
|
|
226
|
+
|
|
227
|
+
tikz_figure = TikzFigure()
|
|
228
|
+
for layer_name, layer_lines in self.layered_line_data.items():
|
|
229
|
+
if layers and layer_name not in layers:
|
|
230
|
+
continue
|
|
231
|
+
for line in layer_lines:
|
|
232
|
+
if line["plot_type"] == "plot":
|
|
233
|
+
x = (line["x"] + self._xshift) * self._xscale
|
|
234
|
+
y = (line["y"] + self._yshift) * self._yscale
|
|
235
|
+
|
|
236
|
+
nodes = [[xi, yi] for xi, yi in zip(x, y)]
|
|
237
|
+
tikz_figure.draw(nodes=nodes, **line["kwargs"])
|
|
238
|
+
return tikz_figure
|
|
239
|
+
|
|
227
240
|
def plot_plotly(self):
|
|
228
241
|
"""
|
|
229
242
|
Plot all lines using Plotly and return a list of traces for each line.
|
|
@@ -255,68 +268,6 @@ class LinePlot:
|
|
|
255
268
|
|
|
256
269
|
return traces
|
|
257
270
|
|
|
258
|
-
def add_node(self, x, y, label=None, content="", layer=0, **kwargs):
|
|
259
|
-
"""
|
|
260
|
-
Add a node to the TikZ figure.
|
|
261
|
-
|
|
262
|
-
Parameters:
|
|
263
|
-
- x (float): X-coordinate of the node.
|
|
264
|
-
- y (float): Y-coordinate of the node.
|
|
265
|
-
- label (str, optional): Label of the node. If None, a default label will be assigned.
|
|
266
|
-
- **kwargs: Additional TikZ node options (e.g., shape, color).
|
|
267
|
-
|
|
268
|
-
Returns:
|
|
269
|
-
- node (Node): The Node object that was added.
|
|
270
|
-
"""
|
|
271
|
-
if label is None:
|
|
272
|
-
label = f"node{self._node_counter}"
|
|
273
|
-
node = Node(x=x, y=y, label=label, layer=layer, content=content, **kwargs)
|
|
274
|
-
self.nodes.append(node)
|
|
275
|
-
if layer in self.layers:
|
|
276
|
-
self.layers[layer].add(node)
|
|
277
|
-
else:
|
|
278
|
-
# print(f"{self.layers = } {layer = }")
|
|
279
|
-
self.layers[layer] = Tikzlayer(layer)
|
|
280
|
-
self.layers[layer].add(node)
|
|
281
|
-
self._node_counter += 1
|
|
282
|
-
return node
|
|
283
|
-
|
|
284
|
-
def add_path(self, nodes, layer=0, **kwargs):
|
|
285
|
-
"""
|
|
286
|
-
Add a line or path connecting multiple nodes.
|
|
287
|
-
|
|
288
|
-
Parameters:
|
|
289
|
-
- nodes (list of str): List of node names to connect.
|
|
290
|
-
- **kwargs: Additional TikZ path options (e.g., style, color).
|
|
291
|
-
|
|
292
|
-
Examples:
|
|
293
|
-
- add_path(['A', 'B', 'C'], color='blue')
|
|
294
|
-
Connects nodes A -> B -> C with a blue line.
|
|
295
|
-
"""
|
|
296
|
-
if not isinstance(nodes, list):
|
|
297
|
-
raise ValueError("nodes parameter must be a list of node names.")
|
|
298
|
-
|
|
299
|
-
nodes = [
|
|
300
|
-
(
|
|
301
|
-
node
|
|
302
|
-
if isinstance(node, Node)
|
|
303
|
-
else (
|
|
304
|
-
self.get_node(node)
|
|
305
|
-
if isinstance(node, str)
|
|
306
|
-
else ValueError(f"Invalid node type: {type(node)}")
|
|
307
|
-
)
|
|
308
|
-
)
|
|
309
|
-
for node in nodes
|
|
310
|
-
]
|
|
311
|
-
path = Path(nodes, **kwargs)
|
|
312
|
-
self.paths.append(path)
|
|
313
|
-
if layer in self.layers:
|
|
314
|
-
self.layers[layer].add(path)
|
|
315
|
-
else:
|
|
316
|
-
self.layers[layer] = Tikzlayer(layer)
|
|
317
|
-
self.layers[layer].add(path)
|
|
318
|
-
return path
|
|
319
|
-
|
|
320
271
|
@property
|
|
321
272
|
def xmin(self):
|
|
322
273
|
return self._xmin
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: maxplotlibx
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
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"
|