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,357 @@
1
+ import matplotlib.pyplot as plt
2
+ import numpy as np
3
+ import plotly.graph_objects as go
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
8
+
9
+
10
+ class Node:
11
+ def __init__(self, x, y, label="", content="", layer=0, **kwargs):
12
+ self.x = x
13
+ self.y = y
14
+ self.label = label
15
+ self.content = content
16
+ self.layer = layer
17
+ self.options = kwargs
18
+
19
+
20
+ class Path:
21
+ def __init__(
22
+ self,
23
+ nodes,
24
+ path_actions=[],
25
+ cycle=False,
26
+ label="",
27
+ layer=0,
28
+ **kwargs,
29
+ ):
30
+ self.nodes = nodes
31
+ self.path_actions = path_actions
32
+ self.cycle = cycle
33
+ self.layer = layer
34
+ self.label = label
35
+ self.options = kwargs
36
+
37
+
38
+ class LinePlot:
39
+ def __init__(
40
+ self,
41
+ title: str | None = None,
42
+ grid: bool = False,
43
+ legend: bool = False,
44
+ xmin: float | int | None = None,
45
+ xmax: float | int | None = None,
46
+ ymin: float | int | None = None,
47
+ ymax: float | int | None = None,
48
+ xlabel: str | None = None,
49
+ ylabel: str | None = None,
50
+ xscale: float | int = 1.0,
51
+ yscale: float | int = 1.0,
52
+ xshift: float | int = 0.0,
53
+ yshift: float | int = 0.0,
54
+ ):
55
+ """
56
+ Initialize the LinePlot class for a subplot.
57
+
58
+ Parameters:
59
+ title (str): Title of the plot.
60
+ caption (str): Caption for the plot.
61
+ description (str): Description of the plot.
62
+ label (str): Label for the plot.
63
+ grid (bool): Whether to display grid lines (default is False).
64
+ legend (bool): Whether to display legend (default is False).
65
+ xmin, xmax, ymin, ymax (float): Axis limits.
66
+ xlabel, ylabel (str): Axis labels.
67
+ xscale, yscale (float): Scaling factors for axes.
68
+ xshift, yshift (float): Shifts for axes.
69
+ """
70
+
71
+ self._title = title
72
+ self._grid = grid
73
+ self._legend = legend
74
+ self._xmin = xmin
75
+ self._xmax = xmax
76
+ self._ymin = ymin
77
+ self._ymax = ymax
78
+ self._xlabel = xlabel
79
+ self._ylabel = ylabel
80
+ self._xscale = xscale
81
+ self._yscale = yscale
82
+ self._xshift = xshift
83
+ self._yshift = yshift
84
+
85
+ # List to store line data, each entry contains x and y data, label, and plot kwargs
86
+ self.line_data = []
87
+ self.layered_line_data = {}
88
+
89
+ # Initialize lists to hold Node and Path objects
90
+ self.nodes = []
91
+ self.paths = []
92
+
93
+ # Counter for unnamed nodes
94
+ self._node_counter = 0
95
+
96
+ def add_caption(self, caption):
97
+ self._caption = caption
98
+
99
+ def _add(self, obj, layer):
100
+ self.line_data.append(obj)
101
+ if layer in self.layered_line_data:
102
+ self.layered_line_data[layer].append(obj)
103
+ else:
104
+ self.layered_line_data[layer] = [obj]
105
+
106
+ def add_line(
107
+ self,
108
+ x_data,
109
+ y_data,
110
+ layer=0,
111
+ plot_type="plot",
112
+ **kwargs,
113
+ ):
114
+ """
115
+ Add a line to the plot.
116
+
117
+ Parameters:
118
+ label (str): Label for the line.
119
+ x_data (list): X-axis data.
120
+ y_data (list): Y-axis data.
121
+ **kwargs: Additional keyword arguments for the plot (e.g., color, linestyle).
122
+ """
123
+ ld = {
124
+ "x": np.array(x_data),
125
+ "y": np.array(y_data),
126
+ "layer": layer,
127
+ "plot_type": plot_type,
128
+ "kwargs": kwargs,
129
+ }
130
+ self._add(ld, layer)
131
+
132
+ def add_imshow(self, data, layer=0, plot_type="imshow", **kwargs):
133
+ ld = {
134
+ "data": np.array(data),
135
+ "layer": layer,
136
+ "plot_type": plot_type,
137
+ "kwargs": kwargs,
138
+ }
139
+ self._add(ld, layer)
140
+
141
+ def add_patch(self, patch, layer=0, plot_type="patch", **kwargs):
142
+ ld = {
143
+ "patch": patch,
144
+ "layer": layer,
145
+ "plot_type": plot_type,
146
+ "kwargs": kwargs,
147
+ }
148
+ self._add(ld, layer)
149
+
150
+ def add_colorbar(self, label="", layer=0, plot_type="colorbar", **kwargs):
151
+ cb = {
152
+ "label": label,
153
+ "layer": layer,
154
+ "plot_type": plot_type,
155
+ "kwargs": kwargs,
156
+ }
157
+ self._add(cb, layer)
158
+
159
+ @property
160
+ def layers(self):
161
+ layers = []
162
+ for layer_name, layer_lines in self.layered_line_data.items():
163
+ layers.append(layer_name)
164
+ return layers
165
+
166
+ def plot_matplotlib(self, ax, layers=None):
167
+ """
168
+ Plot all lines on the provided axis.
169
+
170
+ Parameters:
171
+ ax (matplotlib.axes.Axes): Axis on which to plot the lines.
172
+ """
173
+ for layer_name, layer_lines in self.layered_line_data.items():
174
+ if layers and layer_name not in layers:
175
+ continue
176
+ for line in layer_lines:
177
+ if line["plot_type"] == "plot":
178
+ ax.plot(
179
+ (line["x"] + self._xshift) * self._xscale,
180
+ (line["y"] + self._yshift) * self._yscale,
181
+ **line["kwargs"],
182
+ )
183
+ elif line["plot_type"] == "scatter":
184
+ ax.scatter(
185
+ (line["x"] + self._xshift) * self._xscale,
186
+ (line["y"] + self._yshift) * self._yscale,
187
+ **line["kwargs"],
188
+ )
189
+ elif line["plot_type"] == "imshow":
190
+ im = ax.imshow(
191
+ line["data"],
192
+ **line["kwargs"],
193
+ )
194
+ elif line["plot_type"] == "patch":
195
+ ax.add_patch(
196
+ line["patch"],
197
+ **line["kwargs"],
198
+ )
199
+ elif line["plot_type"] == "colorbar":
200
+ divider = make_axes_locatable(ax)
201
+ cax = divider.append_axes("right", size="5%", pad=0.05)
202
+ plt.colorbar(im, cax=cax, label="Potential (V)")
203
+ if self._title:
204
+ ax.set_title(self._title)
205
+ if self._xlabel:
206
+ ax.set_xlabel(self._xlabel)
207
+ if self._ylabel:
208
+ ax.set_ylabel(self._ylabel)
209
+ if self._legend and len(self.line_data) > 0:
210
+ ax.legend()
211
+ if self._grid:
212
+ ax.grid()
213
+ if self.xmin:
214
+ ax.axis(xmin=self.xmin)
215
+ if self.xmax:
216
+ ax.axis(xmax=self.xmax)
217
+ if self.ymin:
218
+ ax.axis(ymin=self.ymin)
219
+ if self.ymax:
220
+ ax.axis(ymax=self.ymax)
221
+
222
+ def plot_plotly(self):
223
+ """
224
+ Plot all lines using Plotly and return a list of traces for each line.
225
+ """
226
+ # Mapping Matplotlib linestyles to Plotly dash styles
227
+ linestyle_map = {
228
+ "solid": "solid",
229
+ "dashed": "dash",
230
+ "dotted": "dot",
231
+ "dashdot": "dashdot",
232
+ }
233
+
234
+ traces = []
235
+ for line in self.line_data:
236
+ trace = go.Scatter(
237
+ x=(line["x"] + self._xshift) * self._xscale,
238
+ y=(line["y"] + self._yshift) * self._yscale,
239
+ mode="lines+markers" if "marker" in line["kwargs"] else "lines",
240
+ name=line["kwargs"].get("label", ""),
241
+ line=dict(
242
+ color=line["kwargs"].get("color", None),
243
+ dash=linestyle_map.get(
244
+ line["kwargs"].get("linestyle", "solid"),
245
+ "solid",
246
+ ),
247
+ ),
248
+ )
249
+ traces.append(trace)
250
+
251
+ return traces
252
+
253
+ def add_node(self, x, y, label=None, content="", layer=0, **kwargs):
254
+ """
255
+ Add a node to the TikZ figure.
256
+
257
+ Parameters:
258
+ - x (float): X-coordinate of the node.
259
+ - y (float): Y-coordinate of the node.
260
+ - label (str, optional): Label of the node. If None, a default label will be assigned.
261
+ - **kwargs: Additional TikZ node options (e.g., shape, color).
262
+
263
+ Returns:
264
+ - node (Node): The Node object that was added.
265
+ """
266
+ if label is None:
267
+ label = f"node{self._node_counter}"
268
+ node = Node(x=x, y=y, label=label, layer=layer, content=content, **kwargs)
269
+ self.nodes.append(node)
270
+ if layer in self.layers:
271
+ self.layers[layer].add(node)
272
+ else:
273
+ # print(f"{self.layers = } {layer = }")
274
+ self.layers[layer] = Tikzlayer(layer)
275
+ self.layers[layer].add(node)
276
+ self._node_counter += 1
277
+ return node
278
+
279
+ def add_path(self, nodes, layer=0, **kwargs):
280
+ """
281
+ Add a line or path connecting multiple nodes.
282
+
283
+ Parameters:
284
+ - nodes (list of str): List of node names to connect.
285
+ - **kwargs: Additional TikZ path options (e.g., style, color).
286
+
287
+ Examples:
288
+ - add_path(['A', 'B', 'C'], color='blue')
289
+ Connects nodes A -> B -> C with a blue line.
290
+ """
291
+ if not isinstance(nodes, list):
292
+ raise ValueError("nodes parameter must be a list of node names.")
293
+
294
+ nodes = [
295
+ (
296
+ node
297
+ if isinstance(node, Node)
298
+ else (
299
+ self.get_node(node)
300
+ if isinstance(node, str)
301
+ else ValueError(f"Invalid node type: {type(node)}")
302
+ )
303
+ )
304
+ for node in nodes
305
+ ]
306
+ path = Path(nodes, **kwargs)
307
+ self.paths.append(path)
308
+ if layer in self.layers:
309
+ self.layers[layer].add(path)
310
+ else:
311
+ self.layers[layer] = Tikzlayer(layer)
312
+ self.layers[layer].add(path)
313
+ return path
314
+
315
+ @property
316
+ def xmin(self):
317
+ return self._xmin
318
+
319
+ @property
320
+ def xmax(self):
321
+ return self._xmax
322
+
323
+ @property
324
+ def ymin(self):
325
+ return self._ymin
326
+
327
+ @property
328
+ def ymax(self):
329
+ return self._ymax
330
+
331
+ # Getter and Setter for grid
332
+ @property
333
+ def grid(self):
334
+ return self._grid
335
+
336
+ @grid.setter
337
+ def grid(self, value):
338
+ self._grid = value
339
+
340
+ # Getter and Setter for legend
341
+ @property
342
+ def legend(self):
343
+ return self._legend
344
+
345
+ @legend.setter
346
+ def legend(self, value):
347
+ self._legend = value
348
+
349
+
350
+ if __name__ == "__main__":
351
+ plotter = LinePlot()
352
+ plotter.add_line("Line 1", [0, 1, 2, 3], [0, 1, 4, 9])
353
+ plotter.add_line("Line 2", [0, 1, 2, 3], [0, 2, 3, 6])
354
+ latex_code = plotter.generate_latex_plot()
355
+ with open("figures/latex_code.tex", "w") as f:
356
+ f.write(latex_code)
357
+ print(latex_code)
@@ -0,0 +1,3 @@
1
+ class Subfigure:
2
+ def __init__(self, **kwargs):
3
+ self.kwargs = kwargs