tikzplot42 0.2.8__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.
- tikzplot/__init__.py +5 -0
- tikzplot/__init__.pyi +5 -0
- tikzplot/axes.py +717 -0
- tikzplot/axes.pyi +445 -0
- tikzplot/axes3d.py +566 -0
- tikzplot/colorbar.py +261 -0
- tikzplot/colorbar.pyi +128 -0
- tikzplot/colors.py +58 -0
- tikzplot/colors.pyi +1 -0
- tikzplot/config.py +101 -0
- tikzplot/config.pyi +95 -0
- tikzplot/elements.py +641 -0
- tikzplot/elements.pyi +11 -0
- tikzplot/figure.py +285 -0
- tikzplot/figure.pyi +27 -0
- tikzplot/latex_special.py +62 -0
- tikzplot/plots.py +175 -0
- tikzplot/plots.pyi +314 -0
- tikzplot/py.typed +0 -0
- tikzplot/state.py +24 -0
- tikzplot/state.pyi +3 -0
- tikzplot42-0.2.8.dist-info/METADATA +780 -0
- tikzplot42-0.2.8.dist-info/RECORD +26 -0
- tikzplot42-0.2.8.dist-info/WHEEL +5 -0
- tikzplot42-0.2.8.dist-info/licenses/LICENSE +674 -0
- tikzplot42-0.2.8.dist-info/top_level.txt +1 -0
tikzplot/axes.py
ADDED
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
import numpy as _np
|
|
2
|
+
import matplotlib.pyplot as _plt
|
|
3
|
+
|
|
4
|
+
from .elements import Graph
|
|
5
|
+
from .config import TikzConfig
|
|
6
|
+
from .state import _next_imshow_num, main_name
|
|
7
|
+
from .latex_special import tex_text
|
|
8
|
+
|
|
9
|
+
class BaseAxes:
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self._elements = []
|
|
12
|
+
self._axis_options = {}
|
|
13
|
+
self._axis_args = set()
|
|
14
|
+
self._legend_on = False
|
|
15
|
+
self._yticks = True
|
|
16
|
+
self._fig = None
|
|
17
|
+
if TikzConfig.USE_DECIMAL_COMMA:
|
|
18
|
+
self._axis_args.add("/pgf/number format/use comma")
|
|
19
|
+
|
|
20
|
+
self._add_legend = ""
|
|
21
|
+
|
|
22
|
+
def _plot(self, x, y, settings=None, xerr=None, yerr=None, **style):
|
|
23
|
+
e = Graph(self, (x, y), settings, xerr=xerr, yerr=yerr, **style)
|
|
24
|
+
self._elements.append(e)
|
|
25
|
+
return e
|
|
26
|
+
|
|
27
|
+
def _check_kwargs(self, func, allowed, **kwargs):
|
|
28
|
+
blacklist = set(kwargs) - allowed
|
|
29
|
+
for b in blacklist:
|
|
30
|
+
raise Warning(f"Ignoring unknown kwarg for {func}: {b}")
|
|
31
|
+
return {k: v for k, v in kwargs.items() if k in allowed}
|
|
32
|
+
|
|
33
|
+
def plot(self, *args, **kwargs):
|
|
34
|
+
kws = {"fmt", "alpha", "color", "c", "linestyle", "ls", "linewidth", "lw", "marker", "markersize", "ms", "label"}
|
|
35
|
+
kwargs = self._check_kwargs("plot", kws, **kwargs)
|
|
36
|
+
if len(args) == 1:
|
|
37
|
+
y = args[0]
|
|
38
|
+
return self._plot(range(len(y)), y, **kwargs)
|
|
39
|
+
elif len(args) == 2:
|
|
40
|
+
x, y = args
|
|
41
|
+
return self._plot(x, y, **kwargs)
|
|
42
|
+
else:
|
|
43
|
+
x,y,fmt = args[:3]
|
|
44
|
+
return self._plot(x,y,fmt=fmt, **kwargs)
|
|
45
|
+
|
|
46
|
+
def scatter(self, x, y, *args, **kwargs):
|
|
47
|
+
kws = {"fmt", "alpha", "color", "c", "marker", "markersize", "s", "label"}
|
|
48
|
+
kwargs = self._check_kwargs("scatter", kws, **kwargs)
|
|
49
|
+
|
|
50
|
+
if "s" in kwargs:
|
|
51
|
+
kwargs["ms"] = kwargs.pop("s")
|
|
52
|
+
|
|
53
|
+
return self._plot(x, y, **kwargs, ls="")
|
|
54
|
+
|
|
55
|
+
def semilogy(self, x, y, *args, **kwargs):
|
|
56
|
+
kws = {"fmt", "base", "alpha", "color", "c", "linestyle", "ls", "linewidth", "lw", "marker", "markersize", "ms", "label"}
|
|
57
|
+
kwargs = self._check_kwargs("semilogy", kws, **kwargs)
|
|
58
|
+
self._axis_options["ymode"] = "log"
|
|
59
|
+
if "base" in kwargs:
|
|
60
|
+
self._axis_options["log basis y"] = kwargs["base"]
|
|
61
|
+
return self._plot(x, y, **kwargs)
|
|
62
|
+
|
|
63
|
+
def errorbar(self, x, y, *args, **kwargs):
|
|
64
|
+
kws = {"xerr", "yerr", "fmt", "alpha", "color", "c", "linestyle", "ls", "linewidth", "lw", "marker", "markersize", "ms", "label"}
|
|
65
|
+
kwargs = self._check_kwargs("errorbar", kws, **kwargs)
|
|
66
|
+
if len(args) == 1:
|
|
67
|
+
return self._plot(x, y, **kwargs, yerr=args[0])
|
|
68
|
+
elif len(args) == 2:
|
|
69
|
+
return self._plot(x, y, **kwargs, yerr=args[0], fmt=args[1])
|
|
70
|
+
else:
|
|
71
|
+
return self._plot(x, y, **kwargs)
|
|
72
|
+
|
|
73
|
+
def stem(self, *args, **kwargs):
|
|
74
|
+
kws = {"orientation", "linefmt", "markerfmt", "alpha", "color", "c", "linestyle", "ls", "linewidth", "lw", "marker", "markersize", "ms", "label"}
|
|
75
|
+
kwargs = self._check_kwargs("stem", kws, **kwargs)
|
|
76
|
+
if "linefmt" in kwargs:
|
|
77
|
+
kwargs["fmt"] = kwargs.pop("linefmt")
|
|
78
|
+
vert = True
|
|
79
|
+
if len(args) == 1:
|
|
80
|
+
y = args[0]
|
|
81
|
+
x = range(len(y))
|
|
82
|
+
elif len(args) == 2:
|
|
83
|
+
x,y=args
|
|
84
|
+
elif len(args) == 3:
|
|
85
|
+
x,y,fmt=args
|
|
86
|
+
kwargs["fmt"]=fmt
|
|
87
|
+
if "orientation" in kwargs:
|
|
88
|
+
o = kwargs.pop("orientation")
|
|
89
|
+
if o == "horizontal":
|
|
90
|
+
vert = False
|
|
91
|
+
if vert:
|
|
92
|
+
return self._plot(x,y,settings=["ycomb"], **kwargs)
|
|
93
|
+
else:
|
|
94
|
+
return self._plot(y,x,settings=["xcomb"], **kwargs)
|
|
95
|
+
|
|
96
|
+
def fill_between(self, x, y1, y2=None, **kwargs):
|
|
97
|
+
kws = {"fmt", "alpha", "color", "c", "label"}
|
|
98
|
+
kwargs = self._check_kwargs("fill_between", kws, **kwargs)
|
|
99
|
+
def _check_instance(xs, ys, pname):
|
|
100
|
+
for el in self._elements:
|
|
101
|
+
if el._check_equal(xs,ys):
|
|
102
|
+
return el._try_set_pname(pname)
|
|
103
|
+
else:
|
|
104
|
+
return None
|
|
105
|
+
name1 = self._fig._get_free_path_name()
|
|
106
|
+
name2 = self._fig._get_free_path_name()
|
|
107
|
+
if isinstance(y1, (int, float)):
|
|
108
|
+
y1 = _np.asarray([y1] * len(x))
|
|
109
|
+
inst = _check_instance(x,y1,name1)
|
|
110
|
+
if inst is None:
|
|
111
|
+
self._plot(x,y1,path_name=name1, alpha=0)
|
|
112
|
+
else:
|
|
113
|
+
name1 = inst
|
|
114
|
+
|
|
115
|
+
if y2 is not None:
|
|
116
|
+
if isinstance(y1, (int, float)):
|
|
117
|
+
y2 = _np.asarray([y2] * len(x))
|
|
118
|
+
inst = _check_instance(x,y2,name2)
|
|
119
|
+
if inst is None:
|
|
120
|
+
self._plot(x,y2,path_name=name2, alpha=0)
|
|
121
|
+
else:
|
|
122
|
+
name2 = inst
|
|
123
|
+
else:
|
|
124
|
+
xs = [min(x), max(x)]
|
|
125
|
+
ys = [0,0]
|
|
126
|
+
inst = _check_instance(xs,ys,name2)
|
|
127
|
+
if inst is None:
|
|
128
|
+
self._plot(xs,ys,path_name=name2, alpha=0)
|
|
129
|
+
else:
|
|
130
|
+
name2 = inst
|
|
131
|
+
e = Graph(self, f"fill between [of={name1} and {name2}]",settings=None, xerr=None, yerr=None, **kwargs)
|
|
132
|
+
self._elements.append(e)
|
|
133
|
+
return e
|
|
134
|
+
|
|
135
|
+
def hlines(self, y, xmin, xmax, colors="k", linestyles="solid", **kwargs):
|
|
136
|
+
kws = {"label"}
|
|
137
|
+
kwargs = self._check_kwargs("hlines", kws, **kwargs)
|
|
138
|
+
def _pad_or_truncate(some_list, target_len):
|
|
139
|
+
return some_list[:target_len] + [some_list[-1]]*(target_len - len(some_list))
|
|
140
|
+
def _to_list(x):
|
|
141
|
+
if x is None:
|
|
142
|
+
return []
|
|
143
|
+
if isinstance(x, (int, float, str)):
|
|
144
|
+
return [x]
|
|
145
|
+
return list(x)
|
|
146
|
+
ys = _to_list(y)
|
|
147
|
+
xmins = _pad_or_truncate(_to_list(xmin), len(ys))
|
|
148
|
+
xmaxs = _pad_or_truncate(_to_list(xmax), len(ys))
|
|
149
|
+
colorss = _pad_or_truncate(_to_list(colors), len(ys))
|
|
150
|
+
lss = _pad_or_truncate(_to_list(linestyles), len(ys))
|
|
151
|
+
for i in range(len(ys)):
|
|
152
|
+
if i == 0 and "label" in kwargs:
|
|
153
|
+
return self._plot([xmins[i], xmaxs[i]], [ys[i]]*2, None, None, None, c=colorss[i], ls=lss[i], label=kwargs["label"])
|
|
154
|
+
else:
|
|
155
|
+
return self._plot([xmins[i], xmaxs[i]], [ys[i]]*2, None, None, None, c=colorss[i], ls=lss[i])
|
|
156
|
+
|
|
157
|
+
def hist(self, x, bins=10, density=False,**kwargs):
|
|
158
|
+
#kws = {"alpha", "color", "c", "label"}
|
|
159
|
+
#kwargs = self._check_kwargs("hist", kws, **kwargs)
|
|
160
|
+
try:
|
|
161
|
+
iter(x)
|
|
162
|
+
iter(x[0])
|
|
163
|
+
datasets = x
|
|
164
|
+
except:
|
|
165
|
+
datasets = [x]
|
|
166
|
+
all_data = _np.concatenate(datasets)
|
|
167
|
+
edges = _np.histogram_bin_edges(all_data, bins=bins)
|
|
168
|
+
for data in datasets:
|
|
169
|
+
counts, _ = _np.histogram(data, edges, density=density)
|
|
170
|
+
centers = (edges[:-1] + edges[1:]) / 2
|
|
171
|
+
widths = edges[1:] - edges[:-1]
|
|
172
|
+
settings = []
|
|
173
|
+
if "orientation" in kwargs and kwargs["orientation"] == "horizontal":
|
|
174
|
+
settings.append("xbar")
|
|
175
|
+
else:
|
|
176
|
+
settings.append("ybar")
|
|
177
|
+
settings.append("fill")
|
|
178
|
+
if "rwidth" in kwargs:
|
|
179
|
+
settings.append(f"bar width={widths.mean()*kwargs['rwidth']}")
|
|
180
|
+
else:
|
|
181
|
+
settings[0] += " interval"
|
|
182
|
+
if "range" in kwargs:
|
|
183
|
+
if settings[0] == "xbar":
|
|
184
|
+
self.set_ylim(kwargs["range"])
|
|
185
|
+
else:
|
|
186
|
+
self.set_xlim(kwargs["range"])
|
|
187
|
+
if "cumulative" in kwargs and kwargs["cumulative"]:
|
|
188
|
+
counts = _np.cumsum(counts)
|
|
189
|
+
|
|
190
|
+
return self._plot(centers, counts, settings=settings, **kwargs)
|
|
191
|
+
|
|
192
|
+
def set_ylabel(self, label):
|
|
193
|
+
self._axis_options["ylabel"] = f"{{{tex_text(label)}}}"
|
|
194
|
+
|
|
195
|
+
def set_ylim(self, *args, **kwargs):
|
|
196
|
+
bottom = None
|
|
197
|
+
top = None
|
|
198
|
+
for k in kwargs:
|
|
199
|
+
if k not in ["bottom", "top"]:
|
|
200
|
+
print(f"Invalid argument {kwargs.pop(k)} in ylim")
|
|
201
|
+
|
|
202
|
+
if len(args) == 1:
|
|
203
|
+
bottom, top = args[0]
|
|
204
|
+
elif len(args) == 2:
|
|
205
|
+
bottom, top = args
|
|
206
|
+
elif len(args) > 2:
|
|
207
|
+
raise ValueError("set_ylim accepts at most 2 positional arguments")
|
|
208
|
+
|
|
209
|
+
if "bottom" in kwargs:
|
|
210
|
+
bottom = kwargs["bottom"]
|
|
211
|
+
if "top" in kwargs:
|
|
212
|
+
top = kwargs["top"]
|
|
213
|
+
|
|
214
|
+
if bottom is not None:
|
|
215
|
+
self._axis_options["ymin"] = bottom
|
|
216
|
+
if top is not None:
|
|
217
|
+
self._axis_options["ymax"] = top
|
|
218
|
+
|
|
219
|
+
def set_yscale(self, *args, **kwargs):
|
|
220
|
+
kws = {"base"}
|
|
221
|
+
kwargs = self._check_kwargs("set_yscale", kws, **kwargs)
|
|
222
|
+
if "log" in args:
|
|
223
|
+
self._axis_options["ymode"] = "log"
|
|
224
|
+
if "base" in kwargs:
|
|
225
|
+
self._axis_options["log basis y"] = kwargs["base"]
|
|
226
|
+
|
|
227
|
+
def set_yticks(self, ticks, labels=None):
|
|
228
|
+
if ticks:
|
|
229
|
+
s_ticks = map(str, ticks)
|
|
230
|
+
self._axis_options["ytick"]=f"{{{','.join(s_ticks)}}}"
|
|
231
|
+
if labels and len(labels)==len(ticks):
|
|
232
|
+
self._axis_options["yticklabels"]=f"{{{tex_text(','.join(labels))}}}"
|
|
233
|
+
elif labels is not None and len(labels) == 0:
|
|
234
|
+
self._axis_options["yticklabels"]=r"{}"
|
|
235
|
+
self._yticks = False
|
|
236
|
+
else:
|
|
237
|
+
self._axis_options["yticks"]=r"{}"
|
|
238
|
+
self._yticks = False
|
|
239
|
+
|
|
240
|
+
def set_yticklabels(self, labels):
|
|
241
|
+
if labels:
|
|
242
|
+
self._axis_options["yticklabels"]=f"{{{tex_text(','.join(labels))}}}"
|
|
243
|
+
else:
|
|
244
|
+
self._axis_options["yticklabels"]=r"{}"
|
|
245
|
+
self._yticks = False
|
|
246
|
+
|
|
247
|
+
_LEGEND_LOC_MAP = ["best", "upper right", "upper left", "lower_left", "lower right", "right", "center left", "center right", "lower center", "upper center", "center"]
|
|
248
|
+
_ANCHOR_MAP = {"top": "north", "bottom": "south", "upper": "north", "lower": "south", "left": "west", "right": "east", "center": "center"}
|
|
249
|
+
|
|
250
|
+
def legend(self, *args, **kwargs):
|
|
251
|
+
if "loc" in kwargs:
|
|
252
|
+
loc = kwargs["loc"]
|
|
253
|
+
lx = ly = posit = None
|
|
254
|
+
if isinstance(loc, tuple):
|
|
255
|
+
try:
|
|
256
|
+
lx,ly=float(loc[0]), float(loc[1])
|
|
257
|
+
posit = "south west"
|
|
258
|
+
except: print(f"Error parsing legend location: {loc}")
|
|
259
|
+
else:
|
|
260
|
+
if isinstance(loc, int):
|
|
261
|
+
loc = self._LEGEND_LOC_MAP[loc]
|
|
262
|
+
posit = " ".join([self._ANCHOR_MAP[k] for k in self._ANCHOR_MAP if k in str(loc)])
|
|
263
|
+
if "center" in posit:
|
|
264
|
+
if "north" in posit or "south" in posit or "west" in posit or "east" in posit:
|
|
265
|
+
posit = posit.replace("center", "")
|
|
266
|
+
lx, ly = 0.5, 0.5
|
|
267
|
+
if "north" in posit:
|
|
268
|
+
ly = 1 - TikzConfig.LEGEND_REL_Y
|
|
269
|
+
elif "south" in posit:
|
|
270
|
+
ly = TikzConfig.LEGEND_REL_Y
|
|
271
|
+
if "west" in posit:
|
|
272
|
+
lx = TikzConfig.LEGEND_REL_X
|
|
273
|
+
elif "east" in posit:
|
|
274
|
+
lx = 1 - TikzConfig.LEGEND_REL_X
|
|
275
|
+
|
|
276
|
+
legend_string = []
|
|
277
|
+
if lx is not None and ly is not None:
|
|
278
|
+
legend_string.append(r"at={(" + f"{lx},{ly}" + r")}")
|
|
279
|
+
if len(posit):
|
|
280
|
+
legend_string.append(r"anchor=" + posit)
|
|
281
|
+
self._axis_options["legend style"] = f"{{{','.join(legend_string)}}}"
|
|
282
|
+
self._legend_on = True
|
|
283
|
+
if "ncols" in kwargs:
|
|
284
|
+
self._axis_options["legend columns"] = kwargs["ncols"]
|
|
285
|
+
if len(args) == 2:
|
|
286
|
+
self._add_legend = args
|
|
287
|
+
elif len(args) == 1:
|
|
288
|
+
labs = args[0]
|
|
289
|
+
if len(labs) > len(self._elements):
|
|
290
|
+
print("Legend: more labels than elements")
|
|
291
|
+
else:
|
|
292
|
+
for i in range(len(labs)):
|
|
293
|
+
self._elements[i]._set_label(tex_text(labs[i]))
|
|
294
|
+
|
|
295
|
+
def _add_legend_entries(self):
|
|
296
|
+
if self._add_legend == "": return ""
|
|
297
|
+
axs, labs = self._add_legend
|
|
298
|
+
output = ""
|
|
299
|
+
if len(axs) != len(labs):
|
|
300
|
+
print("Legend: different number of plots and labels, ignoring.")
|
|
301
|
+
return ""
|
|
302
|
+
for i in range(len(axs)):
|
|
303
|
+
output += f"\n\\addlegendimage{{{axs[i]._style_string()}}}"
|
|
304
|
+
output += f"\n\\addlegendentry{{{tex_text(labs[i])}}}"
|
|
305
|
+
return output
|
|
306
|
+
|
|
307
|
+
def _content_tex(self, filename):
|
|
308
|
+
ouptut = "\n".join(e._to_tex(filename) for e in self._elements)
|
|
309
|
+
ouptut += self._add_legend_entries()
|
|
310
|
+
return ouptut
|
|
311
|
+
|
|
312
|
+
def _get_hard_range(self,which):
|
|
313
|
+
arg = f"{which[0]}mode"
|
|
314
|
+
mode = "lin"
|
|
315
|
+
if arg in self._axis_options:
|
|
316
|
+
mode = self._axis_options[arg]
|
|
317
|
+
if which in self._axis_options:
|
|
318
|
+
for e in self._elements:
|
|
319
|
+
e._filter(which, self._axis_options[which])
|
|
320
|
+
return (self._axis_options[which], mode)
|
|
321
|
+
return None, mode
|
|
322
|
+
|
|
323
|
+
def _get_range(self, which):
|
|
324
|
+
arg = f"{which[0]}mode"
|
|
325
|
+
mode = "lin"
|
|
326
|
+
if arg in self._axis_options:
|
|
327
|
+
mode = self._axis_options[arg]
|
|
328
|
+
if which in self._axis_options:
|
|
329
|
+
for e in self._elements:
|
|
330
|
+
e._filter(which, self._axis_options[which])
|
|
331
|
+
return (self._axis_options[which], True, mode)
|
|
332
|
+
if "min" in which:
|
|
333
|
+
return (min([e._get_erange(which) for e in self._elements]), False, mode)
|
|
334
|
+
return (max([e._get_erange(which) for e in self._elements]), False, mode)
|
|
335
|
+
|
|
336
|
+
def _get_limit(self, which):
|
|
337
|
+
arg = f"{which[0]}mode"
|
|
338
|
+
mode = "lin"
|
|
339
|
+
base = 10
|
|
340
|
+
if arg in self._axis_options:
|
|
341
|
+
mode = self._axis_options[arg]
|
|
342
|
+
arg = f"log basis {which[0]}"
|
|
343
|
+
if arg in self._axis_options:
|
|
344
|
+
base = self._axis_options[arg]
|
|
345
|
+
if which in self._axis_options:
|
|
346
|
+
return self._axis_options[which], mode, base
|
|
347
|
+
else: return None, mode, base
|
|
348
|
+
|
|
349
|
+
def _set_range(self, which, value):
|
|
350
|
+
self._axis_options[which] = value
|
|
351
|
+
for e in self._elements:
|
|
352
|
+
e._filter(which, value)
|
|
353
|
+
|
|
354
|
+
def _num_points(self):
|
|
355
|
+
return [e._num_points() for e in self._elements]
|
|
356
|
+
|
|
357
|
+
def _reduce_points(self, limit):
|
|
358
|
+
for e in self._elements:
|
|
359
|
+
e._reduce_points(limit)
|
|
360
|
+
|
|
361
|
+
def _add_col(self, r,g,b):
|
|
362
|
+
self._fig._add_col(r,g,b)
|
|
363
|
+
|
|
364
|
+
def set(self, **kwargs):
|
|
365
|
+
defined = {"ylim": self.set_ylim, "ylabel": self.set_ylabel, "yscale": self.set_yscale, "yticklabels": self.set_yticklabels, "yticks": self.set_yticks}
|
|
366
|
+
for attr in defined:
|
|
367
|
+
if attr in kwargs:
|
|
368
|
+
defined[attr](kwargs.pop(attr))
|
|
369
|
+
|
|
370
|
+
class Axes(BaseAxes):
|
|
371
|
+
|
|
372
|
+
def __init__(self, nrows, ncols, index, fig, polar):
|
|
373
|
+
super().__init__()
|
|
374
|
+
self._left = False
|
|
375
|
+
self._neigh = None
|
|
376
|
+
|
|
377
|
+
self._nrows = nrows
|
|
378
|
+
self._ncols = ncols
|
|
379
|
+
self._index = index - 1
|
|
380
|
+
self._row = self._index // self._ncols
|
|
381
|
+
self._col = self._index - self._row * self._ncols
|
|
382
|
+
|
|
383
|
+
self._fig = fig
|
|
384
|
+
self._imshow = None
|
|
385
|
+
|
|
386
|
+
self._defcol_counter = 0
|
|
387
|
+
self._colorbar = ""
|
|
388
|
+
self._cbar_h = False
|
|
389
|
+
self._polar = polar
|
|
390
|
+
|
|
391
|
+
def _posit_string(): # returns neighbour, neighbour corner, anchor
|
|
392
|
+
i = self._index
|
|
393
|
+
if i == 0:
|
|
394
|
+
return None
|
|
395
|
+
if self._col == 0:
|
|
396
|
+
self._neigh = i - self._ncols
|
|
397
|
+
self._left = True
|
|
398
|
+
return self._neigh, "south", "north"
|
|
399
|
+
self._neigh = i - 1
|
|
400
|
+
return self._neigh, "east", "west"
|
|
401
|
+
|
|
402
|
+
self._axis_options["alias" if TikzConfig.USE_GROUPPLOTS else "name"] = f"p{index-1}"
|
|
403
|
+
pos = _posit_string()
|
|
404
|
+
if pos is not None and not TikzConfig.USE_GROUPPLOTS:
|
|
405
|
+
self._axis_options["at"] = f"{{(p{self._neigh}.{pos[1]})}}"
|
|
406
|
+
self._axis_options["anchor"] = pos[2]
|
|
407
|
+
|
|
408
|
+
self._secondary_y = None
|
|
409
|
+
|
|
410
|
+
self._width = None
|
|
411
|
+
self._height = None
|
|
412
|
+
if self._fig._get_width():
|
|
413
|
+
self._width= f"{self._fig._get_width() / ncols}cm"
|
|
414
|
+
if self._fig._get_height():
|
|
415
|
+
self._height = f"{self._fig._get_height() / nrows}cm"
|
|
416
|
+
|
|
417
|
+
self._xticks = True
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def _update_size(self):
|
|
421
|
+
if self._fig._get_width():
|
|
422
|
+
self._width= f"{self._fig._get_width() / self._ncols}cm"
|
|
423
|
+
if self._fig._get_height():
|
|
424
|
+
self._height = f"{self._fig._get_height() / self._nrows}cm"
|
|
425
|
+
|
|
426
|
+
def loglog(self, x, y, *args, **kwargs):
|
|
427
|
+
kws = {"base", "fmt", "alpha", "color", "c", "linestyle", "ls", "linewidth", "lw", "marker", "markersize", "ms", "label"}
|
|
428
|
+
kwargs = self._check_kwargs("loglog", kws, **kwargs)
|
|
429
|
+
self._axis_options["xmode"] = "log"
|
|
430
|
+
self._axis_options["ymode"] = "log"
|
|
431
|
+
if "base" in kwargs:
|
|
432
|
+
base = kwargs["base"]
|
|
433
|
+
self._axis_options["log basis x"] = base
|
|
434
|
+
self._axis_options["log basis y"] = base
|
|
435
|
+
return self._plot(x, y, **kwargs)
|
|
436
|
+
|
|
437
|
+
def semilogx(self, x, y, *args, **kwargs):
|
|
438
|
+
kws = {"base", "fmt", "alpha", "color", "c", "linestyle", "ls", "linewidth", "lw", "marker", "markersize", "ms", "label"}
|
|
439
|
+
kwargs = self._check_kwargs("semilogx", kws, **kwargs)
|
|
440
|
+
self._axis_options["xmode"] = "log"
|
|
441
|
+
if "base" in kwargs:
|
|
442
|
+
self._axis_options["log basis x"] = kwargs["base"]
|
|
443
|
+
return self._plot(x, y, **kwargs)
|
|
444
|
+
|
|
445
|
+
def vlines(self, x, ymin, ymax, colors="k", linestyles="solid", **kwargs):
|
|
446
|
+
kws = {"label"}
|
|
447
|
+
kwargs = self._check_kwargs("vlines", kws, **kwargs)
|
|
448
|
+
def _pad_or_truncate(some_list, target_len):
|
|
449
|
+
return some_list[:target_len] + [some_list[-1]]*(target_len - len(some_list))
|
|
450
|
+
def _to_list(x):
|
|
451
|
+
if x is None:
|
|
452
|
+
return []
|
|
453
|
+
if isinstance(x, (int, float, str)):
|
|
454
|
+
return [x]
|
|
455
|
+
return list(x)
|
|
456
|
+
xs = _to_list(x)
|
|
457
|
+
ymins = _pad_or_truncate(_to_list(ymin), len(xs))
|
|
458
|
+
ymaxs = _pad_or_truncate(_to_list(ymax), len(xs))
|
|
459
|
+
colorss = _pad_or_truncate(_to_list(colors), len(xs))
|
|
460
|
+
lss = _pad_or_truncate(_to_list(linestyles), len(xs))
|
|
461
|
+
for i in range(len(xs)):
|
|
462
|
+
if i == 0 and "label" in kwargs:
|
|
463
|
+
self._plot([xs[i]]*2, [ymins[i], ymaxs[i]], None, None, None, c=colorss[i], ls=lss[i], label=kwargs["label"])
|
|
464
|
+
else:
|
|
465
|
+
self._plot([xs[i]]*2, [ymins[i], ymaxs[i]], None, None, None, c=colorss[i], ls=lss[i])
|
|
466
|
+
|
|
467
|
+
def imshow(self, *args, **kwargs):
|
|
468
|
+
#kws = {"fmt", "alpha", "color", "c", "linestyle", "ls", "linewidth", "lw", "marker", "markersize", "ms", "label"}
|
|
469
|
+
#kwargs = self._check_kwargs("imshow", kws, **kwargs)
|
|
470
|
+
self._imshow = (args, kwargs)
|
|
471
|
+
self._axis_args.add("axis on top")
|
|
472
|
+
self._axis_options["enlargelimits"] = "false"
|
|
473
|
+
#self._fig._add_global("\\pgfplotsset{set layers}")
|
|
474
|
+
data = args[0]
|
|
475
|
+
m, M = _np.min(data), _np.max(data)
|
|
476
|
+
if "cmap" in kwargs:
|
|
477
|
+
cmap = kwargs["cmap"]
|
|
478
|
+
else:
|
|
479
|
+
cmap = "viridis"
|
|
480
|
+
return (self, cmap, m, M)
|
|
481
|
+
|
|
482
|
+
def set_xlabel(self, label):
|
|
483
|
+
self._axis_options["xlabel"] = f"{{{tex_text(label)}}}"
|
|
484
|
+
|
|
485
|
+
def set_title(self, title):
|
|
486
|
+
self._axis_options["title"] = f"{{{tex_text(title)}}}"
|
|
487
|
+
|
|
488
|
+
def grid(self, visible=True, which="major", **kwargs):
|
|
489
|
+
|
|
490
|
+
if not visible:
|
|
491
|
+
self._axis_options["grid"] = "none"
|
|
492
|
+
return
|
|
493
|
+
selector = which + " "
|
|
494
|
+
if which == "major":
|
|
495
|
+
if "grid" in self._axis_options and self._axis_options["grid"] == "minor":
|
|
496
|
+
self._axis_options["grid"] = "both"
|
|
497
|
+
else:
|
|
498
|
+
self._axis_options["grid"] = "major"
|
|
499
|
+
|
|
500
|
+
elif which == "minor":
|
|
501
|
+
if "grid" in self._axis_options and self._axis_options["grid"] == "major":
|
|
502
|
+
self._axis_options["grid"] = "both"
|
|
503
|
+
else:
|
|
504
|
+
self._axis_options["grid"] = "minor"
|
|
505
|
+
|
|
506
|
+
elif which == "both":
|
|
507
|
+
self._axis_options["grid"] = "both"
|
|
508
|
+
selector = ""
|
|
509
|
+
|
|
510
|
+
if kwargs:
|
|
511
|
+
accepted_kwargs = {"color", "c", "linestyle", "ls", "linewidth", "lw", "alpha"}
|
|
512
|
+
kwargs = self._check_kwargs("grid", accepted_kwargs, **kwargs)
|
|
513
|
+
g = Graph(self, None, None, None, None, **kwargs)._style_string()
|
|
514
|
+
self._axis_options[f"{selector}grid style"] = f"{{{g}}}"
|
|
515
|
+
|
|
516
|
+
def set_minorticks_num(self, num):
|
|
517
|
+
self._axis_options["minor tick num"] = num
|
|
518
|
+
|
|
519
|
+
def set_xlim(self, *args, **kwargs):
|
|
520
|
+
left = None
|
|
521
|
+
right = None
|
|
522
|
+
for k in kwargs:
|
|
523
|
+
if k not in ["left", "right"]:
|
|
524
|
+
print(f"Invalid argument {kwargs.pop(k)} in ylim")
|
|
525
|
+
|
|
526
|
+
if len(args) == 1:
|
|
527
|
+
left, right = args[0]
|
|
528
|
+
elif len(args) == 2:
|
|
529
|
+
left, right = args
|
|
530
|
+
elif len(args) > 2:
|
|
531
|
+
raise ValueError("set_xlim accepts at most 2 positional arguments")
|
|
532
|
+
|
|
533
|
+
if "left" in kwargs:
|
|
534
|
+
left = kwargs["left"]
|
|
535
|
+
if "right" in kwargs:
|
|
536
|
+
right = kwargs["right"]
|
|
537
|
+
|
|
538
|
+
if left is not None:
|
|
539
|
+
self._axis_options["xmin"] = left
|
|
540
|
+
if right is not None:
|
|
541
|
+
self._axis_options["xmax"] = right
|
|
542
|
+
|
|
543
|
+
def set_xscale(self, *args, **kwargs):
|
|
544
|
+
kws = {"base"}
|
|
545
|
+
kwargs = self._check_kwargs("set_xscale", kws, **kwargs)
|
|
546
|
+
if "log" in args:
|
|
547
|
+
self._axis_options["xmode"] = "log"
|
|
548
|
+
if "base" in kwargs:
|
|
549
|
+
self._axis_options["log basis x"] = kwargs["base"]
|
|
550
|
+
|
|
551
|
+
def set_xticks(self, ticks, labels=None):
|
|
552
|
+
if ticks:
|
|
553
|
+
s_ticks = map(str, ticks)
|
|
554
|
+
self._axis_options["xtick"]=f"{{{','.join(s_ticks)}}}"
|
|
555
|
+
if labels and len(labels)==len(ticks):
|
|
556
|
+
self._axis_options["xticklabels"]=f"{{{tex_text(','.join(labels))}}}"
|
|
557
|
+
elif labels is not None and len(labels) == 0:
|
|
558
|
+
self._axis_options["xticklabels"]=r"{}"
|
|
559
|
+
self._xticks = False
|
|
560
|
+
else:
|
|
561
|
+
self._axis_options["xticks"]=r"{}"
|
|
562
|
+
self._xticks = False
|
|
563
|
+
|
|
564
|
+
def set_xticklabels(self, labels):
|
|
565
|
+
if labels:
|
|
566
|
+
self._axis_options["xticklabels"]=f"{{{tex_text(','.join(labels))}}}"
|
|
567
|
+
else:
|
|
568
|
+
self._axis_options["xticklabels"]=r"{}"
|
|
569
|
+
self._xticks = False
|
|
570
|
+
|
|
571
|
+
def twinx(self):
|
|
572
|
+
if self._polar:
|
|
573
|
+
raise Exception("Cannot create twinx() on polar plot.")
|
|
574
|
+
self._secondary_y = Secondary(self)
|
|
575
|
+
return self._secondary_y
|
|
576
|
+
|
|
577
|
+
def _export_imshow(self, *args, **kwargs):
|
|
578
|
+
if "xmode" in self._axis_options and self._axis_options["xmode"] == "log":
|
|
579
|
+
base = 10
|
|
580
|
+
if "log basis x" in self._axis_options:
|
|
581
|
+
base = self._axis_options["log basis x"]
|
|
582
|
+
_plt.xscale("log", base=base)
|
|
583
|
+
if "ymode" in self._axis_options and self._axis_options["ymode"] == "log":
|
|
584
|
+
base = 10
|
|
585
|
+
if "log basis y" in self._axis_options:
|
|
586
|
+
base = self._axis_options["log basis y"]
|
|
587
|
+
_plt.yscale("log", base=base)
|
|
588
|
+
_plt.axis("off")
|
|
589
|
+
_plt.imshow(*args, **kwargs)
|
|
590
|
+
im_name = f"{str(main_name()[1]).removesuffix('.py')}_{TikzConfig.IMSHOW_SAVENAME}{_next_imshow_num()}.pdf"
|
|
591
|
+
_plt.savefig(im_name,bbox_inches='tight', pad_inches=0)
|
|
592
|
+
return im_name
|
|
593
|
+
|
|
594
|
+
def _axis_option_string(self):
|
|
595
|
+
self._update_size()
|
|
596
|
+
if self._width:
|
|
597
|
+
self._axis_options["width"] = self._width
|
|
598
|
+
if self._height:
|
|
599
|
+
self._axis_options["height"] = self._height
|
|
600
|
+
if not TikzConfig.USE_GROUPPLOTS:
|
|
601
|
+
if self._left:
|
|
602
|
+
self._axis_options["yshift"] = f"-{self._fig._get_spacing(self._row, self._col)}cm"
|
|
603
|
+
else:
|
|
604
|
+
self._axis_options["xshift"] = f"{self._fig._get_spacing(self._row, self._col)}cm"
|
|
605
|
+
if self._imshow:
|
|
606
|
+
im_name = self._export_imshow(*self._imshow[0], **self._imshow[1]).replace(r"\\", r"/")
|
|
607
|
+
dims = _np.shape(self._imshow[0][0])
|
|
608
|
+
bounds = [0, dims[1], 0, dims[0]]
|
|
609
|
+
if "extent" in self._imshow[1]:
|
|
610
|
+
bounds = self._imshow[1]["extent"]
|
|
611
|
+
xm, xM, ym, yM = bounds
|
|
612
|
+
self._elements.insert(0, Graph(self, f"graphics [xmin={xm}, xmax={xM}, ymin={ym}, ymax={yM}] {{{im_name}}}",settings=None, xerr=None, yerr=None, onlayer="axis background"))
|
|
613
|
+
axis_opt_str = ""
|
|
614
|
+
if self._axis_args:
|
|
615
|
+
axis_opt_str += ",\n".join(self._axis_args)
|
|
616
|
+
if TikzConfig.SCHOOL_AXIS:
|
|
617
|
+
axis_opt_str += f",\n axis lines=middle,\n xlabel style={{at={{(ticklabel* cs:{1+TikzConfig.SCHOOL_AXIS_LABEL_MARGIN})}},anchor=north}},\n ylabel style={{at={{(ticklabel* cs:{1+TikzConfig.SCHOOL_AXIS_LABEL_MARGIN})}},anchor=east}}"
|
|
618
|
+
if TikzConfig.USE_GROUPPLOTS:
|
|
619
|
+
axis_opt_str += f",\n set layers,\n axis line style={{on layer=axis foreground}}"
|
|
620
|
+
if self._axis_options:
|
|
621
|
+
if axis_opt_str: axis_opt_str += ",\n"
|
|
622
|
+
axis_opt_str += ",\n".join(f"{k}={v}" for k, v in self._axis_options.items())
|
|
623
|
+
axis_opt_str += self._colorbar
|
|
624
|
+
return axis_opt_str
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
def _margins(self):
|
|
628
|
+
left = TikzConfig.LEFT_PADDING + TikzConfig.YTICK_PADDING * self._yticks + TikzConfig.Y_LABEL_PADDING * ("ylabel" in self._axis_options)
|
|
629
|
+
right = TikzConfig.RIGHT_PADDING + TikzConfig.CBAR_X_MARGIN * (self._colorbar != "" and not self._cbar_h)
|
|
630
|
+
top = TikzConfig.TOP_PADDING + TikzConfig.TITLE_PADDING * ("title" in self._axis_options)
|
|
631
|
+
bottom = TikzConfig.BOTTOM_PADDING + TikzConfig.XTICK_PADDING * self._xticks + TikzConfig.X_LABEL_PADDING * ("xlabel" in self._axis_options) + TikzConfig.CBAR_Y_MARGIN * (self._colorbar != "" and self._cbar_h)
|
|
632
|
+
if self._secondary_y is not None:
|
|
633
|
+
right += self._secondary_y._padding()
|
|
634
|
+
|
|
635
|
+
return left, right, top, bottom
|
|
636
|
+
|
|
637
|
+
def _get_row(self):
|
|
638
|
+
return self._row
|
|
639
|
+
def _get_col(self):
|
|
640
|
+
return self._col
|
|
641
|
+
def _get_nrows(self):
|
|
642
|
+
return self._nrows
|
|
643
|
+
def _get_ncols(self):
|
|
644
|
+
return self._ncols
|
|
645
|
+
def _get_defcol(self):
|
|
646
|
+
self._defcol_counter += 1
|
|
647
|
+
return self._defcol_counter - 1
|
|
648
|
+
def _show_colorbar(self, cbar, horizontal=False):
|
|
649
|
+
self._colorbar = ",\n" + cbar
|
|
650
|
+
self._cbar_h = horizontal
|
|
651
|
+
def _get_index(self):
|
|
652
|
+
return self._index
|
|
653
|
+
|
|
654
|
+
def _to_tex(self, filename):
|
|
655
|
+
lines = []
|
|
656
|
+
lines2 = []
|
|
657
|
+
if TikzConfig.USE_GROUPPLOTS:
|
|
658
|
+
lines.append("\\nextgroupplot")
|
|
659
|
+
if self._polar:
|
|
660
|
+
lines.append("\\begin{polaraxis}")
|
|
661
|
+
elif not TikzConfig.USE_GROUPPLOTS:
|
|
662
|
+
lines.append("\\begin{axis}")
|
|
663
|
+
lines.append(f"[{self._axis_option_string()}]")
|
|
664
|
+
lines.append(self._content_tex(filename))
|
|
665
|
+
if self._polar:
|
|
666
|
+
lines.append("\\begin{polaraxis}")
|
|
667
|
+
elif not TikzConfig.USE_GROUPPLOTS:
|
|
668
|
+
lines.append("\\end{axis}")
|
|
669
|
+
if self._secondary_y is not None:
|
|
670
|
+
lines2.append("\\begin{axis}")
|
|
671
|
+
lines2.append(f"[{self._secondary_y._axis_option_string()}]")
|
|
672
|
+
lines2.append(self._secondary_y._content_tex(filename))
|
|
673
|
+
lines2.append("\\end{axis}")
|
|
674
|
+
return lines, lines2
|
|
675
|
+
|
|
676
|
+
def set(self, **kwargs):
|
|
677
|
+
defined = {"title": self.set_title, "xlim": self.set_xlim, "xlabel": self.set_xlabel, "xscale": self.set_xscale, "xticklabels": self.set_xticklabels, "xticks": self.set_xticks}
|
|
678
|
+
for attr in defined:
|
|
679
|
+
if attr in kwargs:
|
|
680
|
+
defined[attr](kwargs.pop(attr))
|
|
681
|
+
|
|
682
|
+
super().set(**kwargs)
|
|
683
|
+
|
|
684
|
+
class Secondary(BaseAxes):
|
|
685
|
+
def __init__(self, primary):
|
|
686
|
+
super().__init__()
|
|
687
|
+
self._primary = primary
|
|
688
|
+
|
|
689
|
+
self._axis_options["axis y line*"] = "right"
|
|
690
|
+
self._axis_options["axis x line"] = "none"
|
|
691
|
+
self._axis_options["at"] = f"{{({primary._axis_options['alias' if 'alias' in primary._axis_options else 'name']}.south west)}}"
|
|
692
|
+
self._axis_options["anchor"] = "south west"
|
|
693
|
+
self._axis_options["y label style"] = r"{at={(1.1,0.5)}, rotate=180}"
|
|
694
|
+
|
|
695
|
+
self._fig = primary._fig
|
|
696
|
+
|
|
697
|
+
def _axis_option_string(self):
|
|
698
|
+
if self._primary._width:
|
|
699
|
+
self._axis_options["width"] = self._primary._width
|
|
700
|
+
if self._primary._height:
|
|
701
|
+
self._axis_options["height"] = self._primary._height
|
|
702
|
+
axis_opt_str = ""
|
|
703
|
+
if self._axis_args:
|
|
704
|
+
axis_opt_str += ",\n".join(self._axis_args)
|
|
705
|
+
if self._axis_options:
|
|
706
|
+
if axis_opt_str: axis_opt_str += ",\n"
|
|
707
|
+
axis_opt_str += ",\n".join(f"{k}={v}" for k, v in self._axis_options.items())
|
|
708
|
+
return axis_opt_str
|
|
709
|
+
|
|
710
|
+
def _padding(self):
|
|
711
|
+
return TikzConfig.SEC_Y_PADDING + TikzConfig.YTICK_PADDING * self._yticks + TikzConfig.SEC_Y_LABEL_PADDING * ("ylabel" in self._axis_options)
|
|
712
|
+
|
|
713
|
+
def _get_defcol(self):
|
|
714
|
+
return self._primary._get_defcol()
|
|
715
|
+
|
|
716
|
+
def _get_index(self):
|
|
717
|
+
return self._primary._get_index()
|