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/elements.py ADDED
@@ -0,0 +1,641 @@
1
+ import numpy as np
2
+ from pathlib import Path
3
+
4
+ from .config import TikzConfig
5
+ from .state import next_export_num, main_name
6
+ from .colors import _tex_color
7
+
8
+ class BaseGraph:
9
+ _COLOR_MAP = {'b':'blue', 'g':'teal', 'r':'red', 'c':'cyan', 'm':'magenta', 'y':'yellow', 'k':'black', 'w':'white', "orange":"orange", "green": "green", "cyan":"cyan", "peru": "brown", "lime": "lime", "gray": "gray", "magenta": "magetna", "purple": "violet"}
10
+ _LINE_MAP = {"--": "dashed", ":": "dotted", "-.": "dashdotted", "-":"solid"}
11
+ _MARKER_MAP = {'o':'*', ".": "*", 's':'square*', '^':'triangle', 'v':'triangle*', 'd':'diamond', '+':'+', 'x':'x', '*':'star'}
12
+
13
+ def __init__(self):
14
+ self._axes = None
15
+ self._classic = False
16
+ self._style = None
17
+ self._label = None
18
+ self._settings = None
19
+ if self._settings == None: self._settings = []
20
+
21
+ self._opacity = 1
22
+ self._path_name = None
23
+ self._has_color = False
24
+ self._style_str = None
25
+
26
+ def _normalize_error(self, err, n):
27
+ if err is None:
28
+ return None, False
29
+ if isinstance(err, (int, float)):
30
+ return np.asarray([err] * n), False
31
+ if len(err) == n:
32
+ if hasattr(err[0], "__len__") and len(err[0]) == 2:
33
+ return np.asarray(err), True
34
+ return np.asarray(err), False
35
+ raise ValueError("Invalid errorbar specification")
36
+
37
+ def _style_string(self):
38
+ if self._style_str != None:
39
+ return self._style_str
40
+ opts = []
41
+
42
+ def match_ls(input):
43
+ if input in self._LINE_MAP.keys():
44
+ return self._LINE_MAP[input]
45
+ if input in self._LINE_MAP.values():
46
+ return input
47
+ print(f"Unrecognized linestyle {input}")
48
+ return None
49
+
50
+ def match_mark(input):
51
+ if input in self._MARKER_MAP.keys():
52
+ return self._MARKER_MAP[input]
53
+ if input in self._MARKER_MAP.values():
54
+ return input
55
+ print(f"Unrecognized marker {input}")
56
+ return None
57
+
58
+ def match_color(input):
59
+ self._has_color = True
60
+ ccode, op = _tex_color(input)
61
+ if not isinstance(op, bool):
62
+ self._opacity = op
63
+ if isinstance(ccode, str):
64
+ return ccode
65
+ r,g,b=ccode
66
+ self._axes._add_col(r,g,b)
67
+ return f"c{r:.3f}{g:.3f}{b:.3f}".replace(".", "")
68
+
69
+ if "fmt" in self._style:
70
+ fmt = self._style["fmt"]
71
+ col = list(set(self._COLOR_MAP.keys()) & set(fmt))
72
+ if col:
73
+ opts.append(f"color={self._COLOR_MAP[col[0]]}")
74
+ fmt = fmt.replace(col[0], "")
75
+ self._has_color = True
76
+ mark = list(set(self._MARKER_MAP.keys()) & set(fmt))
77
+ if mark:
78
+ opts.append(f"mark={self._MARKER_MAP[mark[0]]}")
79
+ fmt = fmt.replace(mark[0], "")
80
+ ls = None
81
+ if fmt:
82
+ ls = match_ls(fmt)
83
+ if ls:
84
+ opts.append(ls)
85
+
86
+ if "c" in self._style:
87
+ sel_col = match_color(self._style['c'])
88
+ if sel_col:
89
+ opts.append(f"color={{{sel_col}}}")
90
+ if "color" in self._style:
91
+ sel_col = match_color(self._style['color'])
92
+ if sel_col:
93
+ opts.append(f"color={{{sel_col}}}")
94
+ if "ls" in self._style:
95
+ ls = self._style["ls"]
96
+ if ls == "":
97
+ opts.append("only marks")
98
+ else:
99
+ sel_ls = match_ls(ls)
100
+ if sel_ls:
101
+ opts.append(sel_ls)
102
+ if "linestyle" in self._style:
103
+ ls = self._style["linestyle"]
104
+ if ls == "":
105
+ opts.append("only marks")
106
+ else:
107
+ sel_ls = match_ls(ls)
108
+ if sel_ls:
109
+ opts.append(sel_ls)
110
+ if "lw" in self._style:
111
+ opts.append(f"line width={self._style['lw']}pt")
112
+ if "linewidth" in self._style:
113
+ opts.append(f"line width={self._style['linewidth']}pt")
114
+ if "marker" in self._style:
115
+ sel_mark = match_mark(self._style['marker'])
116
+ if sel_mark:
117
+ opts.append(f"mark={sel_mark}")
118
+ if "ms" in self._style:
119
+ opts.append(f"mark size={self._style['ms']}pt")
120
+ if "marksize" in self._style:
121
+ opts.append(f"mark size={self._style['marksize']}pt")
122
+ if "markerfmt" in self._style:
123
+ col = list(set(self._COLOR_MAP.keys()) & set(fmt))
124
+ if col:
125
+ opts.append(f"mark options={{{self._COLOR_MAP[col[0]]}}}")
126
+ fmt = fmt.replace(col[0], "")
127
+ self._has_color = True
128
+ mark = list(set(self._MARKER_MAP.keys()) & set(fmt))
129
+ if mark:
130
+ opts.append(f"mark={self._MARKER_MAP[mark[0]]}")
131
+ fmt = fmt.replace(mark[0], "")
132
+ if "label" in self._style:
133
+ self._label = self._style["label"]
134
+
135
+ if "alpha" in self._style:
136
+ self._opacity = self._style["alpha"]
137
+
138
+ #if "onlayer" in self._style:
139
+ # opts.append(f"on layer={self._style["onlayer"]}")
140
+
141
+ if self._opacity < 1:
142
+ opts.append(f"opacity={self._opacity}")
143
+ if not self._has_color and self._classic:
144
+ opts.append(f"color={{{match_color(f'C{self._axes._get_defcol()}')}}}")
145
+ if self._classic:
146
+ if self._xerr is not None or self._yerr is not None or (isinstance(self, Graph3) and self._zerr is not None):
147
+ opts.append("error bars/.cd")
148
+ if self._xerr is not None:
149
+ opts.append("x dir=both")
150
+ opts.append("x explicit")
151
+ if self._yerr is not None:
152
+ opts.append("y dir=both")
153
+ opts.append("y explicit")
154
+ if isinstance(self, Graph3) and self._zerr is not None:
155
+ opts.append("z dir=both")
156
+ opts.append("z explicit")
157
+ keys = {}
158
+ for i in reversed(range(len(opts))):
159
+ key = str(opts[i]).split("=")
160
+ if key[0] in keys:
161
+ del opts[i]
162
+ if self._path_name: self._settings.append(f"name path={self._path_name}")
163
+ if self._settings:
164
+ opts = self._settings + opts
165
+ self._style_str = ",\n".join(str(o) for o in opts)
166
+ return self._style_str
167
+
168
+ def _save_data(self, points, filename):
169
+ path = Path(filename)
170
+ file_number = next_export_num()
171
+ current = path.parent
172
+ dir = current / TikzConfig.DATAPOINTS_DIR
173
+ dir.mkdir(parents=True, exist_ok=True)
174
+ file_name = dir / f"{str(path.stem)}_{file_number}.dat"
175
+ if not TikzConfig.UPDATE_STYLE_ONLY:
176
+ with file_name.open("w", encoding="utf-8") as f:
177
+ f.write(points)
178
+ return str(Path(TikzConfig.DATAPOINTS_DIR) / f"{str(path.stem)}_{file_number}.dat")
179
+
180
+ def _try_set_pname(self, pname):
181
+ if self._path_name:
182
+ return self._path_name
183
+ self._path_name = pname
184
+ return pname
185
+
186
+ def _num_points(self):
187
+ if self._classic:
188
+ return len(self._x)
189
+ return 0
190
+
191
+ def _set_label(self, lab):
192
+ self._style["label"] = lab
193
+
194
+ class Graph(BaseGraph):
195
+ def __init__(self, axes, coordinates, settings=None, xerr=None, yerr=None, path_name=None, **style):
196
+ super().__init__()
197
+ self._axes = axes
198
+ self._classic = False
199
+ if isinstance(coordinates, tuple):
200
+ self._classic = True
201
+ x,y=coordinates
202
+ self._x = np.asarray(x)
203
+ self._y = np.asarray(y)
204
+ mask = np.isfinite(self._x) & np.isfinite(self._y)
205
+ self._x = self._x[mask]
206
+ self._y = self._y[mask]
207
+ n = len(self._x)
208
+ self._xerr, self._x_asym = self._normalize_error(xerr, n)
209
+ self._yerr, self._y_asym = self._normalize_error(yerr, n)
210
+ if self._xerr is not None:
211
+ self._xerr = np.asarray(self._xerr)[mask]
212
+
213
+ if self._yerr is not None:
214
+ self._yerr = np.asarray(self._yerr)[mask]
215
+ else:
216
+ self._special = coordinates
217
+ self._style = style
218
+ self._label = None
219
+ self._settings = settings
220
+ if self._settings == None: self._settings = []
221
+
222
+ self._opacity = 1
223
+ self._path_name = path_name
224
+ self._has_color = False
225
+ self._style_str = None
226
+
227
+ def _header(self):
228
+ cols = ["x", "y"]
229
+ if self._xerr is not None:
230
+ if self._x_asym:
231
+ cols += ["xerrminus", "xerrplus"]
232
+ else:
233
+ cols.append("xerror")
234
+ if self._yerr is not None:
235
+ if self._y_asym:
236
+ cols += ["yerrminus", "yerrplus"]
237
+ else:
238
+ cols.append("yerror")
239
+ return " ".join(cols)
240
+
241
+ def _rows(self):
242
+ rows = []
243
+ for i in range(len(self._x)):
244
+ line = [self._x[i], self._y[i]]
245
+ if self._xerr is not None:
246
+ if self._x_asym:
247
+ line += list(self._xerr[i])
248
+ else:
249
+ line.append(self._xerr[i])
250
+ if self._yerr is not None:
251
+ if self._y_asym:
252
+ line += list(self._yerr[i])
253
+ else:
254
+ line.append(self._yerr[i])
255
+ rows.append(" ".join(str(v) for v in line))
256
+ return "\n".join(rows)
257
+
258
+ def _to_tex(self, filename):
259
+ style = self._style_string()
260
+
261
+ if self._classic:
262
+ header = self._header()
263
+ rows = self._rows()
264
+ table_opts = "x=x,y=y"
265
+ if self._xerr is not None:
266
+ table_opts += ",x error=xerror"
267
+ if self._yerr is not None:
268
+ table_opts += ",y error=yerror"
269
+ datapoints = f"{header}\n{rows}\n"
270
+ if TikzConfig.SAVE_DATAPOINTS:
271
+ datapoints = self._save_data(datapoints, filename)
272
+ if not TikzConfig.SAVE_DATAPOINTS or (TikzConfig.SAVE_DATAPOINTS and not TikzConfig.UPDATE_DATA_ONLY):
273
+ if self._label and self._axes._legend_on:
274
+ return f"\\addplot [{style}] table [{table_opts}] {{{datapoints}}};\\addlegendentry{{{self._label}}}"
275
+ return f"\\addplot [forget plot,\n{style}] table [{table_opts}] {{{datapoints}}};"
276
+ return ""
277
+ elif TikzConfig.SAVE_DATAPOINTS or not (TikzConfig.SAVE_DATAPOINTS and not TikzConfig.UPDATE_STYLE_ONLY):
278
+ return f"\\addplot [forget plot,\n{style}] {self._special};"
279
+ else:
280
+ return ""
281
+
282
+ def _data_range(self):
283
+ xmin, xmax = min(self._x), max(self._x)
284
+ ymin, ymax = min(self._y), max(self._y)
285
+ return xmin, xmax, ymin, ymax
286
+
287
+ def _get_erange(self, which):
288
+ if which == "xmin":
289
+ return min(self._x)
290
+ if which == "xmax":
291
+ return max(self._x)
292
+ if which == "ymin":
293
+ return min(self._y)
294
+ if which == "ymax":
295
+ return max(self._y)
296
+
297
+ def _filter(self, which, value):
298
+ if which == "xmin":
299
+ mask = self._x >= value
300
+ idx_keep = np.where(self._x < value)[0]
301
+ if len(idx_keep) > 0:
302
+ idx_keep = idx_keep[-1]
303
+ elif which == "xmax":
304
+ mask = self._x <= value
305
+ idx_keep = np.where(self._x > value)[0]
306
+ if len(idx_keep) > 0:
307
+ idx_keep = idx_keep[0]
308
+ elif which == "ymin":
309
+ mask = self._y >= value
310
+ idx_keep = np.where(self._y < value)[0]
311
+ if len(idx_keep) > 0:
312
+ idx_keep = idx_keep[-1]
313
+ elif which == "ymax":
314
+ mask = self._y <= value
315
+ idx_keep = np.where(self._y > value)[0]
316
+ if len(idx_keep) > 0:
317
+ idx_keep = idx_keep[0]
318
+
319
+ else:
320
+ raise ValueError("Invalid filter type")
321
+
322
+ mask[idx_keep] = True
323
+
324
+ self._x = self._x[mask]
325
+ self._y = self._y[mask]
326
+
327
+ if self._xerr is not None:
328
+ self._xerr = self._xerr[mask]
329
+
330
+ if self._yerr is not None:
331
+ self._yerr = self._yerr[mask]
332
+
333
+ def _check_equal(self, x,y):
334
+ if self._classic:
335
+ return np.array_equal(np.asarray(x),self._x) and np.array_equal(np.asarray(y),self._y)
336
+ return False
337
+
338
+ def _reduce_points(self, limit):
339
+ if self._classic:
340
+ xm, xmode, xbase = self._axes._get_limit("xmin")
341
+ xM, _, _ = self._axes._get_limit("xmax")
342
+ ym, ymode, ybase = self._axes._get_limit("ymin")
343
+ yM, _, _ = self._axes._get_limit("ymax")
344
+ if xm == None: xm = self._get_erange("xmin")
345
+ if xM == None: xM = self._get_erange("xmax")
346
+ if ym == None: ym = self._get_erange("ymin")
347
+ if yM == None: yM = self._get_erange("ymax")
348
+ l = len(self._x)
349
+ if l > limit:
350
+ if TikzConfig.REDUCE_METHOD == 0:
351
+ idx_keep = np.linspace(0, l-1, limit, dtype=int)
352
+ self._x = self._x[idx_keep]
353
+ self._y = self._y[idx_keep]
354
+ if self._xerr is not None:
355
+ self._xerr = self._xerr[idx_keep]
356
+ if self._yerr is not None:
357
+ self._yerr = self._yerr[idx_keep]
358
+ elif TikzConfig.REDUCE_METHOD in [1,2]:
359
+ if xmode == "log":
360
+ fac = xM / xm
361
+ if fac > 0: fac = np.log(fac) / np.log(xbase)
362
+ else: fac = 1
363
+ vis_x = np.log(self._x) / fac
364
+ else:
365
+ fac = xM - xm
366
+ if fac == 0: fac = 1
367
+ vis_x = self._x / fac
368
+ if ymode == "log":
369
+ fac = yM / ym
370
+ if fac > 0: fac = np.log(fac) / np.log(ybase)
371
+ else: fac = 1
372
+ vis_y = np.log(self._y) / fac
373
+ else:
374
+ fac = yM - ym
375
+ if fac == 0: fac = 1
376
+ vis_y = self._y / fac
377
+ while len(self._x) > limit:
378
+ if TikzConfig.REDUCE_METHOD == 1:
379
+ dx1 = vis_x[1:-1] - vis_x[:-2]
380
+ dy1 = vis_y[1:-1] - vis_y[:-2]
381
+ dx2 = vis_x[2:] - vis_x[1:-1]
382
+ dy2 = vis_y[2:] - vis_y[1:-1]
383
+ crit = np.hypot(dx1, dy1) + np.hypot(dx2, dy2)
384
+ elif TikzConfig.REDUCE_METHOD == 2:
385
+ x0, x1, x2 = vis_x[:-2], vis_x[1:-1], vis_x[2:]
386
+ y0, y1, y2 = vis_y[:-2], vis_y[1:-1], vis_y[2:]
387
+ crit = np.abs((x1 - x0)*(y2 - y0) - (y1 - y0)*(x2 - x0))
388
+ idx_remove = np.argmin(crit)+1
389
+ if idx_remove == len(crit): idx_remove -= 1
390
+ mask = np.ones(len(self._x), dtype=bool)
391
+ mask[idx_remove] = False
392
+ self._x = self._x[mask]
393
+ self._y = self._y[mask]
394
+ vis_x = vis_x[mask]
395
+ vis_y = vis_y[mask]
396
+ if self._xerr is not None:
397
+ self._xerr = self._xerr[mask]
398
+ if self._yerr is not None:
399
+ self._yerr = self._yerr[mask]
400
+
401
+ class Graph3(BaseGraph):
402
+ def __init__(self, axes, coordinates, settings=None, xerr=None, yerr=None, zerr=None, path_name=None, **style):
403
+ super().__init__()
404
+ self._axes = axes
405
+ self._classic = False
406
+ if isinstance(coordinates, tuple):
407
+ self._classic = True
408
+ x,y,z=coordinates
409
+ self._x = np.asarray(x)
410
+ self._y = np.asarray(y)
411
+ self._z = np.asarray(z)
412
+ mask = np.isfinite(self._x) & np.isfinite(self._y) & np.isfinite(self._z)
413
+ self._x = self._x[mask]
414
+ self._y = self._y[mask]
415
+ self._z = self._z[mask]
416
+ n = len(self._x)
417
+ self._xerr, self._x_asym = self._normalize_error(xerr, n)
418
+ self._yerr, self._y_asym = self._normalize_error(yerr, n)
419
+ self._zerr, self._z_asym = self._normalize_error(zerr, n)
420
+ if self._xerr is not None:
421
+ self._xerr = np.asarray(self._xerr)[mask]
422
+ if self._yerr is not None:
423
+ self._yerr = np.asarray(self._yerr)[mask]
424
+ if self._zerr is not None:
425
+ self._zerr = np.asarray(self._zerr)[mask]
426
+ else:
427
+ self._special = coordinates
428
+ self._style = style
429
+ self._label = None
430
+ self._settings = settings
431
+ if self._settings == None: self._settings = []
432
+
433
+ self._opacity = 1
434
+ self._path_name = path_name
435
+ self._has_color = False
436
+ self._style_str = None
437
+
438
+ def _header(self):
439
+ cols = ["x", "y", "z"]
440
+ if self._xerr is not None:
441
+ if self._x_asym:
442
+ cols += ["xerrminus", "xerrplus"]
443
+ else:
444
+ cols.append("xerror")
445
+ if self._yerr is not None:
446
+ if self._y_asym:
447
+ cols += ["yerrminus", "yerrplus"]
448
+ else:
449
+ cols.append("yerror")
450
+ if self._zerr is not None:
451
+ if self._z_asym:
452
+ cols += ["zerrminus", "zerrplus"]
453
+ else:
454
+ cols.append("zerror")
455
+ return " ".join(cols)
456
+
457
+ def _rows(self):
458
+ rows = []
459
+ for i in range(len(self._x)):
460
+ line = [self._x[i], self._y[i], self._z[i]]
461
+ if self._xerr is not None:
462
+ if self._x_asym:
463
+ line += list(self._xerr[i])
464
+ else:
465
+ line.append(self._xerr[i])
466
+ if self._yerr is not None:
467
+ if self._y_asym:
468
+ line += list(self._yerr[i])
469
+ else:
470
+ line.append(self._yerr[i])
471
+ if self._zerr is not None:
472
+ if self._z_asym:
473
+ line += list(self._zerr[i])
474
+ else:
475
+ line.append(self._zerr[i])
476
+ rows.append(" ".join(str(v) for v in line))
477
+ return "\n".join(rows)
478
+
479
+ def _to_tex(self, filename):
480
+ style = self._style_string()
481
+
482
+ if self._classic:
483
+ header = self._header()
484
+ rows = self._rows()
485
+ table_opts = "x=x,y=y,z=z"
486
+ if self._xerr is not None:
487
+ table_opts += ",x error=xerror"
488
+ if self._yerr is not None:
489
+ table_opts += ",y error=yerror"
490
+ if self._zerr is not None:
491
+ table_opts += ",z error=zerror"
492
+ datapoints = f"{header}\n{rows}\n"
493
+ if TikzConfig.SAVE_DATAPOINTS:
494
+ datapoints = self._save_data(datapoints, filename).replace(r"\\", r"/")
495
+ if not TikzConfig.SAVE_DATAPOINTS or (TikzConfig.SAVE_DATAPOINTS and not TikzConfig.UPDATE_DATA_ONLY):
496
+ if self._label and self._axes._legend_on:
497
+ return f"\\addplot3 [{style}] table [{table_opts}] {{{datapoints}}};\\addlegendentry{{{self._label}}}"
498
+ return f"\\addplot3 [forget plot,\n{style}] table [{table_opts}] {{{datapoints}}};"
499
+ return ""
500
+ elif TikzConfig.SAVE_DATAPOINTS or not (TikzConfig.SAVE_DATAPOINTS and not TikzConfig.UPDATE_STYLE_ONLY):
501
+ return f"""\\addplot3 [forget plot,\n{style}] {self._special};"""
502
+ else:
503
+ return ""
504
+
505
+ def _data_range(self):
506
+ xmin, xmax = min(self._x), max(self._x)
507
+ ymin, ymax = min(self._y), max(self._y)
508
+ zmin, zmax = min(self._z), max(self._z)
509
+ return xmin, xmax, ymin, ymax, zmin, zmax
510
+
511
+ def _get_erange(self, which):
512
+ if which == "xmin":
513
+ return min(self._x)
514
+ if which == "xmax":
515
+ return max(self._x)
516
+ if which == "ymin":
517
+ return min(self._y)
518
+ if which == "ymax":
519
+ return max(self._y)
520
+ if which == "zmin":
521
+ return min(self._z)
522
+ if which == "zmax":
523
+ return max(self._z)
524
+
525
+
526
+ def _filter(self, which, value):
527
+ if which == "xmin":
528
+ mask = self._x >= value
529
+ idx_keep = np.where(self._x < value)[0]
530
+ if len(idx_keep) > 0:
531
+ idx_keep = idx_keep[-1]
532
+ elif which == "xmax":
533
+ mask = self._x <= value
534
+ idx_keep = np.where(self._x > value)[0]
535
+ if len(idx_keep) > 0:
536
+ idx_keep = idx_keep[0]
537
+ elif which == "ymin":
538
+ mask = self._y >= value
539
+ idx_keep = np.where(self._y < value)[0]
540
+ if len(idx_keep) > 0:
541
+ idx_keep = idx_keep[-1]
542
+ elif which == "ymax":
543
+ mask = self._y <= value
544
+ idx_keep = np.where(self._y > value)[0]
545
+ if len(idx_keep) > 0:
546
+ idx_keep = idx_keep[0]
547
+ elif which == "zmin":
548
+ mask = self._z >= value
549
+ idx_keep = np.where(self._z < value)[0]
550
+ if len(idx_keep) > 0:
551
+ idx_keep = idx_keep[-1]
552
+ elif which == "zmax":
553
+ mask = self._z <= value
554
+ idx_keep = np.where(self._z > value)[0]
555
+ if len(idx_keep) > 0:
556
+ idx_keep = idx_keep[0]
557
+
558
+ else:
559
+ raise ValueError("Invalid filter type")
560
+
561
+ mask[idx_keep] = True
562
+
563
+ self._x = self._x[mask]
564
+ self._y = self._y[mask]
565
+ self._z = self._z[mask]
566
+
567
+ if self._xerr is not None:
568
+ self._xerr = self._xerr[mask]
569
+
570
+ if self._yerr is not None:
571
+ self._yerr = self._yerr[mask]
572
+
573
+ if self._zerr is not None:
574
+ self._zerr = self._zerr[mask]
575
+
576
+ def _check_equal(self, x,y,z):
577
+ if self._classic:
578
+ return np.array_equal(np.asarray(x),self._x) and np.array_equal(np.asarray(y),self._y) and np.array_equal(np.asarray(z),self._z)
579
+ return False
580
+
581
+ def _reduce_points(self, limit, logx=False, logy=False, logz=False):
582
+ if self._classic:
583
+ l = len(self._x)
584
+ if l > limit:
585
+ if TikzConfig.REDUCE_METHOD == 0:
586
+ idx_keep = np.linspace(0, l-1, limit, dtype=int)
587
+ self._x = self._x[idx_keep]
588
+ self._y = self._y[idx_keep]
589
+ self._z = self._z[idx_keep]
590
+ if self._xerr is not None:
591
+ self._xerr = self._xerr[idx_keep]
592
+ if self._yerr is not None:
593
+ self._yerr = self._yerr[idx_keep]
594
+ if self._zerr is not None:
595
+ self._zerr = self._zerr[idx_keep]
596
+ elif TikzConfig.REDUCE_METHOD in [1,2]:
597
+ if logx:
598
+ vis_x = np.log(self._x)
599
+ else:
600
+ vis_x = self._x
601
+ if logy:
602
+ vis_y = np.log(self._y)
603
+ else:
604
+ vis_y = self._y
605
+ if logz:
606
+ vis_z = np.log(self._z)
607
+ else:
608
+ vis_z = self._z
609
+ while len(self._x) > limit:
610
+ if TikzConfig.REDUCE_METHOD == 1:
611
+ dx1 = vis_x[1:-1] - vis_x[:-2]
612
+ dy1 = vis_y[1:-1] - vis_y[:-2]
613
+ dz1 = vis_z[1:-1] - vis_z[:-2]
614
+ dx2 = vis_x[2:] - vis_x[1:-1]
615
+ dy2 = vis_y[2:] - vis_y[1:-1]
616
+ dz2 = vis_z[2:] - vis_z[1:-1]
617
+ crit = np.hypot(np.hypot(dx1, dy1), dz1) + np.hypot(np.hypot(dx2, dy2), dz2)
618
+ elif TikzConfig.REDUCE_METHOD == 2:
619
+ x0, x1, x2 = vis_x[:-2], vis_x[1:-1], vis_x[2:]
620
+ y0, y1, y2 = vis_y[:-2], vis_y[1:-1], vis_y[2:]
621
+ z0, z1, z2 = vis_z[:-2], vis_z[1:-1], vis_z[2:]
622
+ v1 = np.stack([x1 - x0, y1 - y0, z1 - z0], axis=-1)
623
+ v2 = np.stack([x2 - x1, y2 - y1, z2 - z1], axis=-1)
624
+ cross = np.cross(v1, v2)
625
+ crit = np.linalg.norm(cross, axis=-1)
626
+ idx_remove = np.argmin(crit)+1
627
+ if idx_remove == len(crit): idx_remove -= 1
628
+ mask = np.ones(len(self._x), dtype=bool)
629
+ mask[idx_remove] = False
630
+ self._x = self._x[mask]
631
+ self._y = self._y[mask]
632
+ self._z = self._z[mask]
633
+ vis_x = vis_x[mask]
634
+ vis_y = vis_y[mask]
635
+ vis_z = vis_z[mask]
636
+ if self._xerr is not None:
637
+ self._xerr = self._xerr[mask]
638
+ if self._yerr is not None:
639
+ self._yerr = self._yerr[mask]
640
+ if self._zerr is not None:
641
+ self._zerr = self._zerr[mask]
tikzplot/elements.pyi ADDED
@@ -0,0 +1,11 @@
1
+ from .config import TikzConfig as TikzConfig
2
+ from .state import main_name as main_name, next_export_num as next_export_num
3
+
4
+ class Graph:
5
+ pass
6
+ """ def __init__(self, axes, coordinates, settings=None, xerr=None, yerr=None, path_name=None, **style) -> None: ...
7
+ def save_data(self, points, filename): ...
8
+ def to_tex(self, filename): ...
9
+ def data_range(self, which): ...
10
+ def get_erange(self, which): ...
11
+ def filter(self, which, value) -> None: ..."""