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.
Files changed (36) hide show
  1. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/PKG-INFO +2 -1
  2. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/pyproject.toml +2 -1
  3. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/backends/matplotlib/utils.py +22 -14
  4. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/canvas/canvas.py +212 -91
  5. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/colors/colors.py +0 -1
  6. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/subfigure/line_plot.py +16 -65
  7. maxplotlibx-0.1.3/src/maxplotlib/tests/test_canvas.py +6 -0
  8. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/tests/test_imports.py +1 -2
  9. maxplotlibx-0.1.3/src/maxplotlib/utils/options.py +3 -0
  10. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlibx.egg-info/PKG-INFO +2 -1
  11. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlibx.egg-info/SOURCES.txt +0 -3
  12. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlibx.egg-info/requires.txt +1 -0
  13. maxplotlibx-0.1.1/src/maxplotlib/backends/matplotlib/utils_old.py +0 -852
  14. maxplotlibx-0.1.1/src/maxplotlib/objects/layer.py +0 -20
  15. maxplotlibx-0.1.1/src/maxplotlib/subfigure/tikz_figure.py +0 -497
  16. maxplotlibx-0.1.1/src/maxplotlib/tests/test_canvas.py +0 -7
  17. maxplotlibx-0.1.1/src/maxplotlib/utils/options.py +0 -3
  18. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/LICENSE +0 -0
  19. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/README.md +0 -0
  20. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/setup.cfg +0 -0
  21. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/__init__.py +0 -0
  22. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/backends/plotly/__init__.py +0 -0
  23. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/backends/plotly/utils.py +0 -0
  24. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/canvas/__init__.py +0 -0
  25. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/colors/__init__.py +0 -0
  26. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/linestyle/__init__.py +0 -0
  27. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/linestyle/linestyle.py +0 -0
  28. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/objects/__init__.py +0 -0
  29. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/objects/node.py +0 -0
  30. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/objects/path.py +0 -0
  31. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/subfigure/__init__.py +0 -0
  32. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/subfigure/subfigure.py +0 -0
  33. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/tests/test_plot.py +0 -0
  34. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlib/utils/__init__.py +0 -0
  35. {maxplotlibx-0.1.1 → maxplotlibx-0.1.3}/src/maxplotlibx.egg-info/dependency_links.txt +0 -0
  36. {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.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.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
- # print(f"{length_in = } {length_pt = }")
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
- def set_size(width, fraction=1, ratio="golden", dpi=300):
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
- fig_dim = (fig_width_pt, fig_height_pt)
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(self, backend: Backends = "matplotlib", savefig=False, layers=None):
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(savefig=savefig, layers=layers)
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(self, backend="matplotlib"):
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(backend="matplotlib", savefig=False, layers=None)
277
- self._matplotlib_fig.show()
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
- plot = self.plot_plotly(savefig=False)
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(self, savefig=False, layers=None, usetex=False):
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
- # print(f"{(fig_width / self._dpi, fig_height / self._dpi) = }")
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._dpi,
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.plot_matplotlib(ax, layers=layers)
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
- tex_fonts = setup_tex_fonts(
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
- # def generate_matplotlib_code(self):
476
- # """Generate code for plotting the data using matplotlib."""
477
- # code = "import matplotlib.pyplot as plt\n\n"
478
- # code += f"fig, axes = plt.subplots({self.nrows}, {self.ncols}, figsize={self.figsize})\n\n"
479
- # if self.nrows == 1 and self.ncols == 1:
480
- # code += "axes = [axes] # Single subplot\n\n"
481
- # else:
482
- # code += "axes = axes.flatten()\n\n"
483
- # for idx, (subplot_idx, lines) in enumerate(self.subplots.items()):
484
- # code += f"# Subplot {subplot_idx}\n"
485
- # code += f"ax = axes[{idx}]\n"
486
- # for line in lines:
487
- # x_data = line['x']
488
- # y_data = line['y']
489
- # label = line['label']
490
- # kwargs = line.get('kwargs', {})
491
- # kwargs_str = ', '.join(f"{k}={repr(v)}" for k, v in kwargs.items())
492
- # code += f"ax.plot({x_data}, {y_data}, label={repr(label)}"
493
- # if kwargs_str:
494
- # code += f", {kwargs_str}"
495
- # code += ")\n"
496
- # code += "ax.set_xlabel('X-axis')\n"
497
- # code += "ax.set_ylabel('Y-axis')\n"
498
- # if self.nrows * self.ncols > 1:
499
- # code += f"ax.set_title('Subplot {subplot_idx}')\n"
500
- # code += "ax.legend()\n\n"
501
- # code += "plt.tight_layout()\nplt.show()\n"
502
- # return code
503
-
504
- # def generate_latex_plot(self):
505
- # """Generate LaTeX code for plotting the data using pgfplots in subplots."""
506
- # latex_code = "\\begin{figure}[h!]\n\\centering\n"
507
- # total_subplots = self.nrows * self.ncols
508
- # for idx in range(total_subplots):
509
- # subplot_idx = divmod(idx, self.ncols)
510
- # lines = self.subplots.get(subplot_idx, [])
511
- # if not lines:
512
- # continue # Skip empty subplots
513
- # latex_code += "\\begin{subfigure}[b]{0.45\\textwidth}\n"
514
- # latex_code += " \\begin{tikzpicture}\n"
515
- # latex_code += " \\begin{axis}[\n"
516
- # latex_code += " xlabel={X-axis},\n"
517
- # latex_code += " ylabel={Y-axis},\n"
518
- # if self.nrows * self.ncols > 1:
519
- # latex_code += f" title={{Subplot {subplot_idx}}},\n"
520
- # latex_code += " legend style={at={(1.05,1)}, anchor=north west},\n"
521
- # latex_code += " legend entries={" + ", ".join(f"{{{line['label']}}}" for line in lines) + "}\n"
522
- # latex_code += " ]\n"
523
- # for line in lines:
524
- # options = []
525
- # kwargs = line.get('kwargs', {})
526
- # if 'color' in kwargs:
527
- # options.append(f"color={kwargs['color']}")
528
- # if 'linestyle' in kwargs:
529
- # linestyle_map = {'-': 'solid', '--': 'dashed', '-.': 'dash dot', ':': 'dotted'}
530
- # linestyle = linestyle_map.get(kwargs['linestyle'], kwargs['linestyle'])
531
- # options.append(f"style={linestyle}")
532
- # options_str = f"[{', '.join(options)}]" if options else ""
533
- # latex_code += f" \\addplot {options_str} coordinates {{\n"
534
- # for x, y in zip(line['x'], line['y']):
535
- # latex_code += f" ({x}, {y})\n"
536
- # latex_code += " };\n"
537
- # latex_code += " \\end{axis}\n"
538
- # latex_code += " \\end{tikzpicture}\n"
539
- # latex_code += "\\end{subfigure}\n"
540
- # latex_code += "\\hfill\n" if (idx + 1) % self.ncols != 0 else "\n"
541
- # latex_code += "\\caption{Multiple Subplots}\n"
542
- # latex_code += "\\end{figure}\n"
543
- # return latex_code
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__":
@@ -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
 
@@ -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
@@ -0,0 +1,6 @@
1
+ def test():
2
+ pass
3
+
4
+
5
+ if __name__ == "__main__":
6
+ test()
@@ -3,9 +3,8 @@ import pytest
3
3
 
4
4
  @pytest.mark.parametrize("x", [0])
5
5
  def import_modules(x):
6
- import matplotlib
7
6
 
8
- import maxplotlib
7
+ pass
9
8
 
10
9
 
11
10
  if __name__ == "__main__":
@@ -0,0 +1,3 @@
1
+ from typing import Literal
2
+
3
+ Backends = Literal["matplotlib", "plotly", "tikzpics"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maxplotlibx
3
- Version: 0.1.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"