maxplotlibx 0.1__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.
File without changes
File without changes
File without changes
@@ -0,0 +1,526 @@
1
+ import os
2
+
3
+ import matplotlib.pyplot as plt
4
+ import plotly.graph_objects as go
5
+ from plotly.subplots import make_subplots
6
+
7
+ import maxplotlib.backends.matplotlib.utils as plt_utils
8
+ from maxplotlib.subfigure.line_plot import LinePlot
9
+ from maxplotlib.subfigure.tikz_figure import TikzFigure
10
+
11
+
12
+ class Canvas:
13
+ def __init__(self, **kwargs):
14
+ """
15
+ Initialize the Canvas class for multiple subplots.
16
+
17
+ Parameters:
18
+ nrows (int): Number of subplot rows. Default is 1.
19
+ ncols (int): Number of subplot columns. Default is 1.
20
+ figsize (tuple): Figure size.
21
+ """
22
+
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})
36
+ self._plotted = False
37
+
38
+ # Dictionary to store lines for each subplot
39
+ # Key: (row, col), Value: list of lines with their data and kwargs
40
+ self._subplots = {}
41
+ self._num_subplots = 0
42
+
43
+ self._subplot_matrix = [[None] * self.ncols for _ in range(self.nrows)]
44
+
45
+ @property
46
+ def subplots(self):
47
+ return self._subplots
48
+
49
+ @property
50
+ def layers(self):
51
+ layers = []
52
+ for (row, col), subplot in self.subplots.items():
53
+ layers.extend(subplot.layers)
54
+ return list(set(layers))
55
+
56
+ def generate_new_rowcol(self, row, col):
57
+ if row is None:
58
+ for irow in range(self.nrows):
59
+ has_none = any(item is None for item in self._subplot_matrix[irow])
60
+ if has_none:
61
+ row = irow
62
+ break
63
+ assert row is not None, "Not enough rows!"
64
+
65
+ if col is None:
66
+ for icol in range(self.ncols):
67
+ if self._subplot_matrix[row][icol] is None:
68
+ col = icol
69
+ break
70
+ assert col is not None, "Not enough columns!"
71
+ return row, col
72
+
73
+ def add_line(
74
+ self,
75
+ x_data,
76
+ y_data,
77
+ layer=0,
78
+ subplot: LinePlot | None = None,
79
+ row: int | None = None,
80
+ col: int | None = None,
81
+ plot_type="plot",
82
+ **kwargs,
83
+ ):
84
+ if row is not None and col is not None:
85
+ try:
86
+ subplot = self._subplot_matrix[row][col]
87
+ except KeyError:
88
+ raise ValueError("Invalid subplot position.")
89
+ else:
90
+ row, col = 0, 0
91
+ subplot = self._subplot_matrix[row][col]
92
+
93
+ if subplot is None:
94
+ row, col = self.generate_new_rowcol(row, col)
95
+ subplot = self.add_subplot(col=col, row=row)
96
+
97
+ subplot.add_line(
98
+ x_data=x_data,
99
+ y_data=y_data,
100
+ layer=layer,
101
+ plot_type=plot_type,
102
+ **kwargs,
103
+ )
104
+
105
+ def add_tikzfigure(
106
+ self,
107
+ col=None,
108
+ row=None,
109
+ label=None,
110
+ **kwargs,
111
+ ):
112
+ """
113
+ Adds a subplot to the figure.
114
+
115
+ Parameters:
116
+ **kwargs: Arbitrary keyword arguments.
117
+ """
118
+
119
+ row, col = self.generate_new_rowcol(row, col)
120
+
121
+ # Initialize the LinePlot for the given subplot position
122
+ tikz_figure = TikzFigure(
123
+ col=col,
124
+ row=row,
125
+ label=label,
126
+ **kwargs,
127
+ )
128
+ self._subplot_matrix[row][col] = tikz_figure
129
+
130
+ # Store the LinePlot instance by its position for easy access
131
+ if label is None:
132
+ self._subplots[(row, col)] = tikz_figure
133
+ else:
134
+ self._subplots[label] = tikz_figure
135
+ return tikz_figure
136
+
137
+ def add_subplot(
138
+ self,
139
+ col: int | None = None,
140
+ row: int | None = None,
141
+ figsize: tuple = (10, 6),
142
+ title: str | None = None,
143
+ caption: str | None = None,
144
+ description: str | None = None,
145
+ label: str | None = None,
146
+ grid: bool = False,
147
+ legend: bool = False,
148
+ xmin: float | int | None = None,
149
+ xmax: float | int | None = None,
150
+ ymin: float | int | None = None,
151
+ ymax: float | int | None = None,
152
+ xlabel: str | None = None,
153
+ ylabel: str | None = None,
154
+ xscale: float | int = 1.0,
155
+ yscale: float | int = 1.0,
156
+ xshift: float | int = 0.0,
157
+ yshift: float | int = 0.0,
158
+ ):
159
+ """
160
+ Adds a subplot to the figure.
161
+
162
+ Parameters:
163
+ **kwargs: Arbitrary keyword arguments.
164
+ - col (int): Column index for the subplot.
165
+ - row (int): Row index for the subplot.
166
+ - label (str): Label to identify the subplot.
167
+ """
168
+
169
+ row, col = self.generate_new_rowcol(row, col)
170
+
171
+ # Initialize the LinePlot for the given subplot position
172
+ line_plot = LinePlot(
173
+ title=title,
174
+ grid=grid,
175
+ legend=legend,
176
+ xmin=xmin,
177
+ xmax=xmax,
178
+ ymin=ymin,
179
+ ymax=ymax,
180
+ xlabel=xlabel,
181
+ ylabel=ylabel,
182
+ xscale=xscale,
183
+ yscale=yscale,
184
+ xshift=xshift,
185
+ yshift=yshift,
186
+ )
187
+ self._subplot_matrix[row][col] = line_plot
188
+
189
+ # Store the LinePlot instance by its position for easy access
190
+ if label is None:
191
+ self._subplots[(row, col)] = line_plot
192
+ else:
193
+ self._subplots[label] = line_plot
194
+ return line_plot
195
+
196
+ def savefig(
197
+ self,
198
+ filename,
199
+ backend="matplotlib",
200
+ layers=None,
201
+ layer_by_layer=False,
202
+ verbose=False,
203
+ plot=True,
204
+ ):
205
+ filename_no_extension, extension = os.path.splitext(filename)
206
+ if backend == "matplotlib":
207
+ if layer_by_layer:
208
+ layers = []
209
+ for layer in self.layers:
210
+ layers.append(layer)
211
+ fig, axs = self.plot(
212
+ show=False,
213
+ backend="matplotlib",
214
+ savefig=True,
215
+ layers=layers,
216
+ )
217
+ _fn = f"{filename_no_extension}_{layers}.{extension}"
218
+ fig.savefig(_fn)
219
+ print(f"Saved {_fn}")
220
+ else:
221
+ if layers is None:
222
+ layers = self.layers
223
+ full_filepath = filename
224
+ else:
225
+ full_filepath = f"{filename_no_extension}_{layers}.{extension}"
226
+
227
+ if self._plotted:
228
+ self._matplotlib_fig.savefig(full_filepath)
229
+ else:
230
+
231
+ fig, axs = self.plot(
232
+ show=False,
233
+ backend="matplotlib",
234
+ savefig=True,
235
+ layers=layers,
236
+ )
237
+ fig.savefig(full_filepath)
238
+ if verbose:
239
+ print(f"Saved {full_filepath}")
240
+
241
+ def plot(self, backend="matplotlib", savefig=False, layers=None):
242
+ if backend == "matplotlib":
243
+ return self.plot_matplotlib(savefig=savefig, layers=layers)
244
+ elif backend == "plotly":
245
+ return self.plot_plotly(savefig=savefig)
246
+ else:
247
+ raise ValueError(f"Invalid backend: {backend}")
248
+
249
+ def show(self, backend="matplotlib"):
250
+ if backend == "matplotlib":
251
+ self.plot(backend="matplotlib", savefig=False, layers=None)
252
+ self._matplotlib_fig.show()
253
+ elif backend == "plotly":
254
+ plot = self.plot_plotly(savefig=False)
255
+ else:
256
+ raise ValueError("Invalid backend")
257
+
258
+ def plot_matplotlib(self, savefig=False, layers=None, usetex=False):
259
+ """
260
+ Generate and optionally display the subplots.
261
+
262
+ Parameters:
263
+ filename (str, optional): Filename to save the figure.
264
+ """
265
+
266
+ tex_fonts = plt_utils.setup_tex_fonts(fontsize=self.fontsize, usetex=usetex)
267
+
268
+ plt_utils.setup_plotstyle(
269
+ tex_fonts=tex_fonts,
270
+ axes_grid=True,
271
+ axes_grid_which="major",
272
+ grid_alpha=1.0,
273
+ grid_linestyle="dotted",
274
+ )
275
+
276
+ if self._figsize is not None:
277
+ fig_width, fig_height = self._figsize
278
+ else:
279
+ fig_width, fig_height = plt_utils.set_size(
280
+ width=self._width,
281
+ ratio=self._ratio,
282
+ dpi=self.dpi,
283
+ )
284
+
285
+ # print(f"{(fig_width / self._dpi, fig_height / self._dpi) = }")
286
+
287
+ fig, axes = plt.subplots(
288
+ self.nrows,
289
+ self.ncols,
290
+ figsize=(fig_width, fig_height),
291
+ squeeze=False,
292
+ dpi=self._dpi,
293
+ )
294
+
295
+ for (row, col), subplot in self.subplots.items():
296
+ ax = axes[row][col]
297
+ subplot.plot_matplotlib(ax, layers=layers)
298
+ # ax.set_title(f"Subplot ({row}, {col})")
299
+ ax.grid()
300
+
301
+ # Set caption, labels, etc., if needed
302
+ self._plotted = True
303
+ self._matplotlib_fig = fig
304
+ self._matplotlib_axes = axes
305
+ return fig, axes
306
+
307
+ def plot_plotly(self, show=True, savefig=None, usetex=False):
308
+ """
309
+ Generate and optionally display the subplots using Plotly.
310
+
311
+ Parameters:
312
+ show (bool): Whether to display the plot.
313
+ savefig (str, optional): Filename to save the figure if provided.
314
+ """
315
+
316
+ tex_fonts = plt_utils.setup_tex_fonts(
317
+ fontsize=self.fontsize,
318
+ usetex=usetex,
319
+ ) # adjust or redefine for Plotly if needed
320
+
321
+ # Set default width and height if not specified
322
+ if self._figsize is not None:
323
+ fig_width, fig_height = self._figsize
324
+ else:
325
+ fig_width, fig_height = plt_utils.set_size(
326
+ width=self._width,
327
+ ratio=self._ratio,
328
+ )
329
+ # print(self._width, fig_width, fig_height)
330
+ # Create subplots
331
+ fig = make_subplots(
332
+ rows=self.nrows,
333
+ cols=self.ncols,
334
+ subplot_titles=[
335
+ f"Subplot ({row}, {col})" for (row, col) in self.subplots.keys()
336
+ ],
337
+ )
338
+
339
+ # Plot each subplot
340
+ for (row, col), line_plot in self.subplots.items():
341
+ traces = line_plot.plot_plotly() # Generate Plotly traces for the line_plot
342
+ for trace in traces:
343
+ fig.add_trace(trace, row=row + 1, col=col + 1)
344
+
345
+ # Update layout settings
346
+ fig.update_layout(
347
+ # width=fig_width,
348
+ # height=fig_height,
349
+ font=dict(size=self.fontsize),
350
+ margin=dict(l=10, r=10, t=40, b=10), # Adjust margins if needed
351
+ )
352
+
353
+ # Optionally save the figure
354
+ if savefig:
355
+ fig.write_image(savefig)
356
+
357
+ # Show or return the figure
358
+ # if show:
359
+ # fig.show()
360
+ return fig
361
+
362
+ # Property getters
363
+
364
+ @property
365
+ def dpi(self):
366
+ return self._dpi
367
+
368
+ @property
369
+ def fontsize(self):
370
+ return self._fontsize
371
+
372
+ @property
373
+ def nrows(self):
374
+ return self._nrows
375
+
376
+ @property
377
+ def ncols(self):
378
+ return self._ncols
379
+
380
+ @property
381
+ def caption(self):
382
+ return self._caption
383
+
384
+ @property
385
+ def description(self):
386
+ return self._description
387
+
388
+ @property
389
+ def label(self):
390
+ return self._label
391
+
392
+ @property
393
+ def figsize(self):
394
+ return self._figsize
395
+
396
+ @property
397
+ def subplot_matrix(self):
398
+ return self._subplot_matrix
399
+
400
+ # Property setters
401
+ @nrows.setter
402
+ def dpi(self, value):
403
+ self._dpi = value
404
+
405
+ @nrows.setter
406
+ def nrows(self, value):
407
+ self._nrows = value
408
+
409
+ @ncols.setter
410
+ def ncols(self, value):
411
+ self._ncols = value
412
+
413
+ @caption.setter
414
+ def caption(self, value):
415
+ self._caption = value
416
+
417
+ @description.setter
418
+ def description(self, value):
419
+ self._description = value
420
+
421
+ @label.setter
422
+ def label(self, value):
423
+ self._label = value
424
+
425
+ @figsize.setter
426
+ def figsize(self, value):
427
+ self._figsize = value
428
+
429
+ # Magic methods
430
+ def __str__(self):
431
+ return f"Canvas(nrows={self.nrows}, ncols={self.ncols}, figsize={self.figsize})"
432
+
433
+ def __repr__(self):
434
+ return f"Canvas(nrows={self.nrows}, ncols={self.ncols}, caption={self.caption}, label={self.label})"
435
+
436
+ def __getitem__(self, key):
437
+ """Allows accessing subplots by tuple index."""
438
+ row, col = key
439
+ if row >= self.nrows or col >= self.ncols:
440
+ raise IndexError("Subplot index out of range")
441
+ return self._subplot_matrix[row][col]
442
+
443
+ def __setitem__(self, key, value):
444
+ """Allows setting a subplot by tuple index."""
445
+ row, col = key
446
+ if row >= self.nrows or col >= self.ncols:
447
+ raise IndexError("Subplot index out of range")
448
+ self._subplot_matrix[row][col] = value
449
+
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
519
+
520
+
521
+ if __name__ == "__main__":
522
+ c = Canvas(ncols=2, nrows=2)
523
+ sp = c.add_subplot()
524
+ sp.add_line("Line 1", [0, 1, 2, 3], [0, 1, 4, 9])
525
+ c.plot()
526
+ print("done")
File without changes
@@ -0,0 +1,85 @@
1
+ import re
2
+
3
+ import matplotlib.colors as mcolors
4
+ import matplotlib.patches as patches
5
+ import numpy as np
6
+
7
+
8
+ class Color:
9
+ def __init__(self, color_spec):
10
+ """
11
+ Initialize the Color object by parsing the color specification.
12
+
13
+ Parameters:
14
+ - color_spec: Can be a TikZ color string (e.g., 'blue!20'), a standard color name,
15
+ an RGB tuple, a hex code, etc.
16
+ """
17
+ self.color_spec = color_spec
18
+ self.rgb = self._parse_color(color_spec)
19
+
20
+ def _parse_color(self, color_spec):
21
+ """
22
+ Internal method to parse the color specification and convert it to an RGB tuple.
23
+
24
+ Parameters:
25
+ - color_spec: The color specification.
26
+
27
+ Returns:
28
+ - rgb: A tuple of (r, g, b) values, each between 0 and 1.
29
+ """
30
+ # If it's already an RGB tuple or list
31
+ if isinstance(color_spec, (list, tuple)) and len(color_spec) == 3:
32
+ # Normalize values if necessary
33
+ rgb = tuple(float(c) / 255 if c > 1 else float(c) for c in color_spec)
34
+ return rgb
35
+
36
+ # If it's a hex code
37
+ if isinstance(color_spec, str) and color_spec.startswith("#"):
38
+ return mcolors.hex2color(color_spec)
39
+
40
+ # If it's a TikZ color string
41
+ match = re.match(r"(\w+)!([\d.]+)", color_spec)
42
+ if match:
43
+ base_color_name, percentage = match.groups()
44
+ percentage = float(percentage)
45
+ base_color = mcolors.to_rgb(base_color_name)
46
+ white = np.array([1.0, 1.0, 1.0])
47
+ mix = percentage / 100.0
48
+ color = mix * np.array(base_color) + (1 - mix) * white
49
+ return tuple(color)
50
+
51
+ # Else, try to parse as a standard color name
52
+ try:
53
+ return mcolors.to_rgb(color_spec)
54
+ except ValueError:
55
+ raise ValueError(f"Invalid color specification: '{color_spec}'")
56
+
57
+ def to_rgb(self):
58
+ """
59
+ Return the color as an RGB tuple.
60
+
61
+ Returns:
62
+ - rgb: A tuple of (r, g, b) values, each between 0 and 1.
63
+ """
64
+ return self.rgb
65
+
66
+ def to_hex(self):
67
+ """
68
+ Return the color as a hex code.
69
+
70
+ Returns:
71
+ - hex_code: A string representing the color in hex format.
72
+ """
73
+ return mcolors.to_hex(self.rgb)
74
+
75
+ def to_rgba(self, alpha=1.0):
76
+ """
77
+ Return the color as an RGBA tuple.
78
+
79
+ Parameters:
80
+ - alpha (float): The alpha (opacity) value between 0 and 1.
81
+
82
+ Returns:
83
+ - rgba: A tuple of (r, g, b, a) values.
84
+ """
85
+ return (*self.rgb, alpha)
File without changes
@@ -0,0 +1,58 @@
1
+ import re
2
+
3
+
4
+ class Linestyle:
5
+ def __init__(self, style_spec):
6
+ """
7
+ Initialize the Linestyle object by parsing the style specification.
8
+
9
+ Parameters:
10
+ - style_spec: Can be a TikZ-style line style string (e.g., 'dashed', 'dotted', 'solid', 'dashdot'),
11
+ or a custom dash pattern.
12
+ """
13
+ self.style_spec = style_spec
14
+ self.matplotlib_style = self._parse_style(style_spec)
15
+
16
+ def _parse_style(self, style_spec):
17
+ """
18
+ Internal method to parse the style specification and convert it to a Matplotlib linestyle.
19
+
20
+ Parameters:
21
+ - style_spec: The style specification.
22
+
23
+ Returns:
24
+ - linestyle: A Matplotlib linestyle string or dash pattern.
25
+ """
26
+ # Predefined mappings from TikZ to Matplotlib
27
+ linestyle_mapping = {
28
+ "solid": "solid",
29
+ "dashed": "dashed",
30
+ "dotted": "dotted",
31
+ "dashdot": "dashdot",
32
+ # You can add more styles or custom dash patterns
33
+ }
34
+
35
+ # Check for predefined styles
36
+ if style_spec in linestyle_mapping:
37
+ return linestyle_mapping[style_spec]
38
+ else:
39
+ # Check if it's a custom dash pattern, e.g., 'dash pattern=on 5pt off 2pt'
40
+ match = re.match(r"dash pattern=on ([\d.]+)pt off ([\d.]+)pt", style_spec)
41
+ if match:
42
+ on_length = float(match.group(1))
43
+ off_length = float(match.group(2))
44
+ # Matplotlib dash pattern is specified in points
45
+ return (0, (on_length, off_length))
46
+ else:
47
+ # Default to solid if style is unknown
48
+ print(f"Unknown line style: '{style_spec}', defaulting to 'solid'")
49
+ return "solid"
50
+
51
+ def to_matplotlib(self):
52
+ """
53
+ Return the line style in Matplotlib format.
54
+
55
+ Returns:
56
+ - linestyle: A Matplotlib linestyle string or dash sequence.
57
+ """
58
+ return self.matplotlib_style
File without changes
@@ -0,0 +1,20 @@
1
+ from abc import ABCMeta, abstractmethod
2
+
3
+
4
+ class Layer(metaclass=ABCMeta):
5
+ def __init__(self, label):
6
+ self.label = label
7
+ self.items = []
8
+
9
+
10
+ class Tikzlayer(Layer):
11
+ def __init__(self, label):
12
+ super().__init__(label)
13
+
14
+ def generate_tikz(self):
15
+ tikz_script = f"\n% Layer {self.label}\n"
16
+ tikz_script += f"\\begin{{pgfonlayer}}{{{self.label}}}\n"
17
+ for item in self.items:
18
+ tikz_script += item.to_tikz()
19
+ tikz_script += f"\\end{{pgfonlayer}}{{{self.label}}}\n"
20
+ return tikz_script