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/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()